on xenial help sign in
Fix real world submenus (e.g. kate) not getting their first item selected on open
daef790Albert Astals Cid
qml/ApplicationMenus/MenuBar.qml+2−5
@@ -44,10 +44,6 @@ Item {
44 implicitWidth: row.width
44 implicitWidth: row.width
45 height: parent.height
45 height: parent.height
46
46
47− function select(index) {
48− d.select(index);
49− }
50−
51 function dismiss() {
47 function dismiss() {
52 d.dismissAll();
48 d.dismissAll();
53 }
49 }
@@ -142,7 +138,8 @@ Item {
142 objectName: visualItem.objectName + "-menu",
138 objectName: visualItem.objectName + "-menu",
143 desiredX: Qt.binding(function() { return visualItem.x - units.gu(1); }),
139 desiredX: Qt.binding(function() { return visualItem.x - units.gu(1); }),
144 desiredY: Qt.binding(function() { return root.height; }),
140 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
146 });
143 });
147 __popup.reset();
144 __popup.reset();
148 __popup.childActivated.connect(dismiss);
145 __popup.childActivated.connect(dismiss);
qml/ApplicationMenus/MenuPopup.qml+11−5
@@ -33,6 +33,8 @@ UbuntuShape {
33 // if they don't fit when growing right
33 // if they don't fit when growing right
34 property bool substractWidth: false
34 property bool substractWidth: false
35
35
36+ property bool selectFirstOnCountChange: true
37+
36 property real desiredX
38 property real desiredX
37 x: {
39 x: {
38 var dummy = visible; // force recalc when shown/hidden
40 var dummy = visible; // force recalc when shown/hidden
@@ -73,8 +75,8 @@ UbuntuShape {
73 d.currentItem = null;
75 d.currentItem = null;
74 }
76 }
75
77
76− function select(index) {
78+ function selectFirstIndex() {
77− d.select(index)
79+ d.selectNext(-1);
78 }
80 }
79
81
80 function reset() {
82 function reset() {
@@ -327,6 +329,7 @@ UbuntuShape {
327 } else if (!popup.visible) {
329 } else if (!popup.visible) {
328 root.unityMenuModel.aboutToShow(__ownIndex);
330 root.unityMenuModel.aboutToShow(__ownIndex);
329 popup.visible = true;
331 popup.visible = true;
332+ popup.item.selectFirstIndex();
330 }
333 }
331 } else {
334 } else {
332 root.unityMenuModel.activate(__ownIndex);
335 root.unityMenuModel.activate(__ownIndex);
@@ -360,6 +363,12 @@ UbuntuShape {
360 Repeater {
363 Repeater {
361 id: repeater
364 id: repeater
362
365
366+ onCountChanged: {
367+ if (root.selectFirstOnCountChange && !d.currentItem && count > 0) {
368+ root.selectFirstIndex();
369+ }
370+ }
371+
363 Loader {
372 Loader {
364 id: loader
373 id: loader
365 objectName: root.objectName + "-item" + __ownIndex
374 objectName: root.objectName + "-item" + __ownIndex
@@ -486,9 +495,6 @@ UbuntuShape {
486 target: item
495 target: item
487 onChildActivated: childActivated();
496 onChildActivated: childActivated();
488 }
497 }
489−
490− Component.onCompleted: item.select(0);
491− onVisibleChanged: if (visible) { item.select(0); }
492 }
498 }
493 }
499 }
494 }
500 }
tests/mocks/QMenuModel/unitymenumodel.cpp+23−4
@@ -18,6 +18,8 @@
18
18
19#include "unitymenumodel.h"
19#include "unitymenumodel.h"
20
20
21+#include <QTimer>
22+
21enum MenuRoles {
23enum MenuRoles {
22 LabelRole = Qt::DisplayRole + 1,
24 LabelRole = Qt::DisplayRole + 1,
23 SensitiveRole,
25 SensitiveRole,
@@ -35,7 +37,8 @@ enum MenuRoles {
35};
37};
36
38
37UnityMenuModel::UnityMenuModel(QObject *parent)
39UnityMenuModel::UnityMenuModel(QObject *parent)
38−: QAbstractListModel(parent)
40+ : QAbstractListModel(parent)
41+ , m_rowCountStatus(NoRequestMade)
39{
42{
40}
43}
41
44
@@ -60,7 +63,7 @@ void UnityMenuModel::setModelData(const QVariant& data)
60
63
61void UnityMenuModel::insertRow(int row, const QVariant& data)
64void UnityMenuModel::insertRow(int row, const QVariant& data)
62{
65{
63− row = qMin(row, rowCount());
66+ row = qMin(row, m_modelData.count());
64
67
65 beginInsertRows(QModelIndex(), row, row);
68 beginInsertRows(QModelIndex(), row, row);
66
69
@@ -71,12 +74,12 @@ void UnityMenuModel::insertRow(int row, const QVariant& data)
71
74
72void UnityMenuModel::appendRow(const QVariant& data)
75void UnityMenuModel::appendRow(const QVariant& data)
73{
76{
74− insertRow(rowCount(), data);
77+ insertRow(m_modelData.count(), data);
75}
78}
76
79
77void UnityMenuModel::removeRow(int row)
80void UnityMenuModel::removeRow(int row)
78{
81{
79− if (row < 0 || rowCount() <= row) {
82+ if (row < 0 || m_modelData.count() <= row) {
80 return;
83 return;
81 }
84 }
82
85
@@ -133,6 +136,22 @@ QString UnityMenuModel::nameOwner() const
133
136
134int UnityMenuModel::rowCount(const QModelIndex&) const
137int UnityMenuModel::rowCount(const QModelIndex&) const
135{
138{
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+
136 return m_modelData.count();
155 return m_modelData.count();
137}
156}
138
157
tests/mocks/QMenuModel/unitymenumodel.h+7
@@ -103,6 +103,13 @@ private:
103 QByteArray m_busName;
103 QByteArray m_busName;
104 QVariantMap m_actions;
104 QVariantMap m_actions;
105 QByteArray m_menuObjectPath;
105 QByteArray m_menuObjectPath;
106+
107+ enum RowCountStatus {
108+ NoRequestMade,
109+ TimerRunning,
110+ TimerFinished
111+ };
112+ RowCountStatus m_rowCountStatus;
106};
113};
107
114
108#endif // MOCK_UNITYMENUMODEL_H
115#endif // MOCK_UNITYMENUMODEL_H
tests/qmltests/ApplicationMenus/tst_MenuBar.qml+34
@@ -338,5 +338,39 @@ Item {
338 return false;
338 return false;
339 }, true);
339 }, true);
340 }
340 }
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+ }
341 }
375 }
342}
376}
tests/qmltests/ApplicationMenus/tst_MenuPopup.qml+17−14
@@ -153,9 +153,6 @@ Item {
153
153
154 var priv = findInvisibleChild(menu, "d");
154 var priv = findInvisibleChild(menu, "d");
155
155
156− keyClick(Qt.Key_Down, Qt.NoModifier);
157− compare(priv.currentItem, item0, "CurrentItem should have moved to item 0");
158−
159 keyClick(Qt.Key_Down, Qt.NoModifier);
156 keyClick(Qt.Key_Down, Qt.NoModifier);
160 compare(priv.currentItem, item1, "CurrentItem should have moved to item 1");
157 compare(priv.currentItem, item1, "CurrentItem should have moved to item 1");
161
158
@@ -164,6 +161,9 @@ Item {
164
161
165 keyClick(Qt.Key_Down, Qt.NoModifier);
162 keyClick(Qt.Key_Down, Qt.NoModifier);
166 compare(priv.currentItem, item0, "CurrentItem should have moved to item 0");
163 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");
167 }
167 }
168
168
169 function test_keyboardNavigation_UpKeySelectsAndOpensPreviousMenuItemAndRotates() {
169 function test_keyboardNavigation_UpKeySelectsAndOpensPreviousMenuItemAndRotates() {
@@ -175,17 +175,17 @@ Item {
175
175
176 var priv = findInvisibleChild(menu, "d");
176 var priv = findInvisibleChild(menu, "d");
177
177
178− keyClick(Qt.Key_Down, Qt.NoModifier);
178+ keyClick(Qt.Key_Up, Qt.NoModifier);
179− compare(priv.currentItem, item0, "CurrentItem should have moved to item 2");
179+ compare(priv.currentItem, item2, "CurrentItem should have moved to item 2");
180
180
181− keyClick(Qt.Key_Down, Qt.NoModifier);
181+ keyClick(Qt.Key_Up, Qt.NoModifier);
182 compare(priv.currentItem, item1, "CurrentItem should have moved to item 1");
182 compare(priv.currentItem, item1, "CurrentItem should have moved to item 1");
183
183
184− keyClick(Qt.Key_Down, Qt.NoModifier);
184+ keyClick(Qt.Key_Up, Qt.NoModifier);
185− compare(priv.currentItem, item2, "CurrentItem should have moved to item 0");
185+ compare(priv.currentItem, item0, "CurrentItem should have moved to item 0");
186
186
187− keyClick(Qt.Key_Down, Qt.NoModifier);
187+ keyClick(Qt.Key_Up, Qt.NoModifier);
188− compare(priv.currentItem, item0, "CurrentItem should have moved to item 2");
188+ compare(priv.currentItem, item2, "CurrentItem should have moved to item 2");
189 }
189 }
190
190
191 function test_aboutToShow() {
191 function test_aboutToShow() {
@@ -242,7 +242,7 @@ Item {
242
242
243 var menuItem = findChild(menu, "menu-item0"); verify(menuItem);
243 var menuItem = findChild(menu, "menu-item0"); verify(menuItem);
244 mouseClick(menuItem, menuItem.width/2, menuItem.height/2);
244 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);
246
246
247 keyClick(Qt.Key_Left, Qt.NoModifier);
247 keyClick(Qt.Key_Left, Qt.NoModifier);
248 tryCompareFunction(function() { return menuItem.popup !== null && menuItem.popup.visible }, false);
248 tryCompareFunction(function() { return menuItem.popup !== null && menuItem.popup.visible }, false);
@@ -270,13 +270,16 @@ Item {
270
270
271 menu.unityMenuModel.modelData = differentSizesMenu;
271 menu.unityMenuModel.modelData = differentSizesMenu;
272
272
273− waitForRendering(menu);
273+ // Wait for the two items to be there
274+ tryCompareFunction(function() { return findChild(menu, "menu-item1") !== null; }, true);
274 var longWidth = menu.width;
275 var longWidth = menu.width;
276+
277+ // Now pop one item and make sure it's smaller
275 differentSizesMenu.pop();
278 differentSizesMenu.pop();
276 menu.unityMenuModel.modelData = differentSizesMenu;
279 menu.unityMenuModel.modelData = differentSizesMenu;
277
280
278− waitForRendering(menu);
281+ tryCompareFunction(function() { return findChild(menu, "menu-item0") !== null; }, true);
279− verify(menu.width < longWidth);
282+ tryCompareFunction(function() { return menu.width < longWidth; }, true);
280 }
283 }
281
284
282 function test_minimumWidth() {
285 function test_minimumWidth() {