on xenial help sign in
Fix real world submenus (e.g. kate) not getting their first item selected on open

MenuBar.qml: remove unused function MenuPopup.qml: Select first item when one arrives if there's no currentItem instead of on completion unitymenumodel.cpp: trick rowCount() to simulate "real world late" menus tst_MenuPopup.qml: adapt to unitymenumodel.cpp changes (and make the Up test actually press Up and not Down) (LP: #1666859) Approved by: Lukáš Tinkl, Unity8 CI Bot

commitdaef7906524d811b0f143078a459c3c6b9d06670 authorAlbert Astals Cid <albert.astals@canonical.com> dateTue 21 Mar 2017, 10:56 (10 years ago) parente1dd3c7554b9fe
6 files changed+94−28
qml/ApplicationMenus/MenuBar.qml+2−5 qml/ApplicationMenus/MenuPopup.qml+11−5 tests/mocks/QMenuModel/unitymenumodel.cpp+23−4 tests/mocks/QMenuModel/unitymenumodel.h+7 tests/qmltests/ApplicationMenus/tst_MenuBar.qml+34 tests/qmltests/ApplicationMenus/tst_MenuPopup.qml+17−14
qml/ApplicationMenus/MenuBar.qml+2−5
@@ -44,10 +44,6 @@ Item {
4444 implicitWidth: row.width
4545 height: parent.height
4646
47− function select(index) {
48− d.select(index);
49− }
50−
5147 function dismiss() {
5248 d.dismissAll();
5349 }
@@ -142,7 +138,8 @@ Item {
142138 objectName: visualItem.objectName + "-menu",
143139 desiredX: Qt.binding(function() { return visualItem.x - units.gu(1); }),
144140 desiredY: Qt.binding(function() { return root.height; }),
145− unityMenuModel: Qt.binding(function() { return root.unityMenuModel.submenu(visualItem.__ownIndex); })
141+ unityMenuModel: Qt.binding(function() { return root.unityMenuModel.submenu(visualItem.__ownIndex); }),
142+ selectFirstOnCountChange: false
146143 });
147144 __popup.reset();
148145 __popup.childActivated.connect(dismiss);
qml/ApplicationMenus/MenuPopup.qml+11−5
@@ -33,6 +33,8 @@ UbuntuShape {
3333 // if they don't fit when growing right
3434 property bool substractWidth: false
3535
36+ property bool selectFirstOnCountChange: true
37+
3638 property real desiredX
3739 x: {
3840 var dummy = visible; // force recalc when shown/hidden
@@ -73,8 +75,8 @@ UbuntuShape {
7375 d.currentItem = null;
7476 }
7577
76− function select(index) {
77− d.select(index)
78+ function selectFirstIndex() {
79+ d.selectNext(-1);
7880 }
7981
8082 function reset() {
@@ -327,6 +329,7 @@ UbuntuShape {
327329 } else if (!popup.visible) {
328330 root.unityMenuModel.aboutToShow(__ownIndex);
329331 popup.visible = true;
332+ popup.item.selectFirstIndex();
330333 }
331334 } else {
332335 root.unityMenuModel.activate(__ownIndex);
@@ -360,6 +363,12 @@ UbuntuShape {
360363 Repeater {
361364 id: repeater
362365
366+ onCountChanged: {
367+ if (root.selectFirstOnCountChange && !d.currentItem && count > 0) {
368+ root.selectFirstIndex();
369+ }
370+ }
371+
363372 Loader {
364373 id: loader
365374 objectName: root.objectName + "-item" + __ownIndex
@@ -486,9 +495,6 @@ UbuntuShape {
486495 target: item
487496 onChildActivated: childActivated();
488497 }
489−
490− Component.onCompleted: item.select(0);
491− onVisibleChanged: if (visible) { item.select(0); }
492498 }
493499 }
494500 }
tests/mocks/QMenuModel/unitymenumodel.cpp+23−4
@@ -18,6 +18,8 @@
1818
1919#include "unitymenumodel.h"
2020
21+#include <QTimer>
22+
2123enum MenuRoles {
2224 LabelRole = Qt::DisplayRole + 1,
2325 SensitiveRole,
@@ -35,7 +37,8 @@ enum MenuRoles {
3537};
3638
3739UnityMenuModel::UnityMenuModel(QObject *parent)
38−: QAbstractListModel(parent)
40+ : QAbstractListModel(parent)
41+ , m_rowCountStatus(NoRequestMade)
3942{
4043}
4144
@@ -60,7 +63,7 @@ void UnityMenuModel::setModelData(const QVariant& data)
6063
6164void UnityMenuModel::insertRow(int row, const QVariant& data)
6265{
63− row = qMin(row, rowCount());
66+ row = qMin(row, m_modelData.count());
6467
6568 beginInsertRows(QModelIndex(), row, row);
6669
@@ -71,12 +74,12 @@ void UnityMenuModel::insertRow(int row, const QVariant& data)
7174
7275void UnityMenuModel::appendRow(const QVariant& data)
7376{
74− insertRow(rowCount(), data);
77+ insertRow(m_modelData.count(), data);
7578}
7679
7780void UnityMenuModel::removeRow(int row)
7881{
79− if (row < 0 || rowCount() <= row) {
82+ if (row < 0 || m_modelData.count() <= row) {
8083 return;
8184 }
8285
@@ -133,6 +136,22 @@ QString UnityMenuModel::nameOwner() const
133136
134137int UnityMenuModel::rowCount(const QModelIndex&) const
135138{
139+ // Fake the rowCount to be 0 for a while (100ms)
140+ // This emulates menus in real world that don't load immediately
141+ if (m_rowCountStatus == TimerRunning)
142+ return 0;
143+
144+ if (m_rowCountStatus == NoRequestMade) {
145+ UnityMenuModel *that = const_cast<UnityMenuModel*>(this);
146+ that->m_rowCountStatus = TimerRunning;
147+ QTimer::singleShot(100, that, [that] {
148+ that->beginInsertRows(QModelIndex(), 0, that->m_modelData.count() - 1);
149+ that->m_rowCountStatus = TimerFinished;
150+ that->endInsertRows();
151+ });
152+ return 0;
153+ }
154+
136155 return m_modelData.count();
137156}
138157
tests/mocks/QMenuModel/unitymenumodel.h+7
@@ -103,6 +103,13 @@ private:
103103 QByteArray m_busName;
104104 QVariantMap m_actions;
105105 QByteArray m_menuObjectPath;
106+
107+ enum RowCountStatus {
108+ NoRequestMade,
109+ TimerRunning,
110+ TimerFinished
111+ };
112+ RowCountStatus m_rowCountStatus;
106113};
107114
108115#endif // MOCK_UNITYMENUMODEL_H
tests/qmltests/ApplicationMenus/tst_MenuBar.qml+34
@@ -338,5 +338,39 @@ Item {
338338 return false;
339339 }, true);
340340 }
341+
342+ function test_firstDisabled() {
343+ var data = appMenuData.generateTestData(10,5,2,3);
344+ data[0].submenu[1].submenu[0].rowData.sensitive = false;
345+ menuBackend.modelData = data;
346+
347+ var menuItem = findChild(menuBar, "menuBar-item0");
348+ menuItem.show();
349+
350+ // waits for item to be created so the keyclick actually works
351+ findChild(menuBar, "menuBar-item0-menu-item1-actionItem");
352+
353+ keyClick(Qt.Key_Down);
354+ keyClick(Qt.Key_Down);
355+ keyClick(Qt.Key_Right);
356+
357+ var submenu = findChild(menuBar, "menuBar-item0-menu-item1-menu");
358+ var priv = findInvisibleChild(submenu, "d");
359+ var subActionItem1 = findChild(submenu, "menuBar-item0-menu-item1-menu-item1-actionItem");
360+ compare(priv.currentItem.item, subActionItem1);
361+
362+ keyClick(Qt.Key_Down);
363+ var subActionItem3 = findChild(submenu, "menuBar-item0-menu-item1-menu-item3-actionItem");
364+ compare(priv.currentItem.item, subActionItem3);
365+
366+ // now move mouse over to a different item and back to exercise a different codepath
367+ var actionItem0 = findChild(menuBar, "menuBar-item0-menu-item0-actionItem");
368+ mouseMove(actionItem0, actionItem0.width/2, actionItem0.height/2);
369+
370+ var actionItem1 = findChild(menuBar, "menuBar-item0-menu-item1-actionItem");
371+ mouseMove(actionItem1, actionItem1.width/2, actionItem1.height/2);
372+
373+ tryCompareFunction(function() { return priv.currentItem.item == subActionItem1; }, true);
374+ }
341375 }
342376}
tests/qmltests/ApplicationMenus/tst_MenuPopup.qml+17−14
@@ -153,9 +153,6 @@ Item {
153153
154154 var priv = findInvisibleChild(menu, "d");
155155
156− keyClick(Qt.Key_Down, Qt.NoModifier);
157− compare(priv.currentItem, item0, "CurrentItem should have moved to item 0");
158−
159156 keyClick(Qt.Key_Down, Qt.NoModifier);
160157 compare(priv.currentItem, item1, "CurrentItem should have moved to item 1");
161158
@@ -164,6 +161,9 @@ Item {
164161
165162 keyClick(Qt.Key_Down, Qt.NoModifier);
166163 compare(priv.currentItem, item0, "CurrentItem should have moved to item 0");
164+
165+ keyClick(Qt.Key_Down, Qt.NoModifier);
166+ compare(priv.currentItem, item1, "CurrentItem should have moved to item 1");
167167 }
168168
169169 function test_keyboardNavigation_UpKeySelectsAndOpensPreviousMenuItemAndRotates() {
@@ -175,17 +175,17 @@ Item {
175175
176176 var priv = findInvisibleChild(menu, "d");
177177
178− keyClick(Qt.Key_Down, Qt.NoModifier);
179− compare(priv.currentItem, item0, "CurrentItem should have moved to item 2");
178+ keyClick(Qt.Key_Up, Qt.NoModifier);
179+ compare(priv.currentItem, item2, "CurrentItem should have moved to item 2");
180180
181− keyClick(Qt.Key_Down, Qt.NoModifier);
181+ keyClick(Qt.Key_Up, Qt.NoModifier);
182182 compare(priv.currentItem, item1, "CurrentItem should have moved to item 1");
183183
184− keyClick(Qt.Key_Down, Qt.NoModifier);
185− compare(priv.currentItem, item2, "CurrentItem should have moved to item 0");
184+ keyClick(Qt.Key_Up, Qt.NoModifier);
185+ compare(priv.currentItem, item0, "CurrentItem should have moved to item 0");
186186
187− keyClick(Qt.Key_Down, Qt.NoModifier);
188− compare(priv.currentItem, item0, "CurrentItem should have moved to item 2");
187+ keyClick(Qt.Key_Up, Qt.NoModifier);
188+ compare(priv.currentItem, item2, "CurrentItem should have moved to item 2");
189189 }
190190
191191 function test_aboutToShow() {
@@ -242,7 +242,7 @@ Item {
242242
243243 var menuItem = findChild(menu, "menu-item0"); verify(menuItem);
244244 mouseClick(menuItem, menuItem.width/2, menuItem.height/2);
245− tryCompareFunction(function() { return menuItem.popup !== null && menuItem.popup.visible }, true);
245+ tryCompareFunction(function() { return menuItem.popup !== null && findInvisibleChild(menuItem.popup, "d").currentItem !== null }, true);
246246
247247 keyClick(Qt.Key_Left, Qt.NoModifier);
248248 tryCompareFunction(function() { return menuItem.popup !== null && menuItem.popup.visible }, false);
@@ -270,13 +270,16 @@ Item {
270270
271271 menu.unityMenuModel.modelData = differentSizesMenu;
272272
273− waitForRendering(menu);
273+ // Wait for the two items to be there
274+ tryCompareFunction(function() { return findChild(menu, "menu-item1") !== null; }, true);
274275 var longWidth = menu.width;
276+
277+ // Now pop one item and make sure it's smaller
275278 differentSizesMenu.pop();
276279 menu.unityMenuModel.modelData = differentSizesMenu;
277280
278− waitForRendering(menu);
279− verify(menu.width < longWidth);
281+ tryCompareFunction(function() { return findChild(menu, "menu-item0") !== null; }, true);
282+ tryCompareFunction(function() { return menu.width < longWidth; }, true);
280283 }
281284
282285 function test_minimumWidth() {