Merge lp:~aacid/unity8/select_first_submenu_when_it_appears into lp:unity8
- select_first_submenu_when_it_appears
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Lukáš Tinkl |
Approved revision: | 2841 |
Merged at revision: | 2889 |
Proposed branch: | lp:~aacid/unity8/select_first_submenu_when_it_appears |
Merge into: | lp:unity8 |
Prerequisite: | lp:~aacid/unity8/menutool |
Diff against target: |
289 lines (+94/-28) 6 files modified
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/-0) tests/qmltests/ApplicationMenus/tst_MenuBar.qml (+34/-0) tests/qmltests/ApplicationMenus/tst_MenuPopup.qml (+17/-14) |
To merge this branch: | bzr merge lp:~aacid/unity8/select_first_submenu_when_it_appears |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Lukáš Tinkl (community) | Approve | ||
Unity8 CI Bot | continuous-integration | Approve | |
Michael Zanetti | Pending | ||
Review via email: mp+318615@code.launchpad.net |
This proposal supersedes a proposal from 2017-02-22.
Commit message
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)
Description of the change
* Are there any related MPs required for this MP to build/function as expected?
https:/
* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A
* If you changed the UI, has there been a design review?
N/A
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal | # |
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:2835
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2839
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 2840. By Albert Astals Cid
-
only submenus select first, not first level menus
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2840
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2840
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:2840
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 2841. By Albert Astals Cid
-
Merge
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2841
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:2841
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Lukáš Tinkl (lukas-kde) wrote : | # |
This works fine for first level submenus; however, very often I found that on second and subsequent levels, most of the time it's the last item that gets preselected. It seems very random, sometimes it's the first one, sometimes the last one.
Albert Astals Cid (aacid) wrote : | # |
> This works fine for first level submenus; however, very often I found that on
> second and subsequent levels, most of the time it's the last item that gets
> preselected. It seems very random, sometimes it's the first one, sometimes the
> last one.
Are you sure you're using this with https:/
Lukáš Tinkl (lukas-kde) wrote : | # |
> > This works fine for first level submenus; however, very often I found that
> on
> > second and subsequent levels, most of the time it's the last item that gets
> > preselected. It seems very random, sometimes it's the first one, sometimes
> the
> > last one.
>
> Are you sure you're using this with
> https:/
> Because what you describe sounds like what happens if you do not.
Yup, that might explain it (added the MP to the silo as well)
Lukáš Tinkl (lukas-kde) wrote : | # |
Works fine now, thanks!
* Did you perform an exploratory manual test run of the code change and any related functionality?
Yes
* Did CI run pass? If not, please explain why.
Yes
Preview Diff
1 | === modified file 'qml/ApplicationMenus/MenuBar.qml' |
2 | --- qml/ApplicationMenus/MenuBar.qml 2017-03-08 09:53:21 +0000 |
3 | +++ qml/ApplicationMenus/MenuBar.qml 2017-03-13 14:11:08 +0000 |
4 | @@ -44,10 +44,6 @@ |
5 | implicitWidth: row.width |
6 | height: parent.height |
7 | |
8 | - function select(index) { |
9 | - d.select(index); |
10 | - } |
11 | - |
12 | function dismiss() { |
13 | d.dismissAll(); |
14 | } |
15 | @@ -137,7 +133,8 @@ |
16 | objectName: visualItem.objectName + "-menu", |
17 | desiredX: Qt.binding(function() { return visualItem.x - units.gu(1); }), |
18 | desiredY: Qt.binding(function() { return root.height; }), |
19 | - unityMenuModel: Qt.binding(function() { return root.unityMenuModel.submenu(visualItem.__ownIndex); }) |
20 | + unityMenuModel: Qt.binding(function() { return root.unityMenuModel.submenu(visualItem.__ownIndex); }), |
21 | + selectFirstOnCountChange: false |
22 | }); |
23 | __popup.reset(); |
24 | __popup.childActivated.connect(dismiss); |
25 | |
26 | === modified file 'qml/ApplicationMenus/MenuPopup.qml' |
27 | --- qml/ApplicationMenus/MenuPopup.qml 2017-02-22 09:43:50 +0000 |
28 | +++ qml/ApplicationMenus/MenuPopup.qml 2017-03-13 14:11:08 +0000 |
29 | @@ -33,6 +33,8 @@ |
30 | // if they don't fit when growing right |
31 | property bool substractWidth: false |
32 | |
33 | + property bool selectFirstOnCountChange: true |
34 | + |
35 | property real desiredX |
36 | x: { |
37 | var dummy = visible; // force recalc when shown/hidden |
38 | @@ -73,8 +75,8 @@ |
39 | d.currentItem = null; |
40 | } |
41 | |
42 | - function select(index) { |
43 | - d.select(index) |
44 | + function selectFirstIndex() { |
45 | + d.selectNext(-1); |
46 | } |
47 | |
48 | function reset() { |
49 | @@ -325,6 +327,7 @@ |
50 | }); |
51 | } else if (popup) { |
52 | popup.visible = true; |
53 | + popup.item.selectFirstIndex(); |
54 | } |
55 | } else { |
56 | root.unityMenuModel.activate(__ownIndex); |
57 | @@ -358,6 +361,12 @@ |
58 | Repeater { |
59 | id: repeater |
60 | |
61 | + onCountChanged: { |
62 | + if (root.selectFirstOnCountChange && !d.currentItem && count > 0) { |
63 | + root.selectFirstIndex(); |
64 | + } |
65 | + } |
66 | + |
67 | Loader { |
68 | id: loader |
69 | objectName: root.objectName + "-item" + __ownIndex |
70 | @@ -484,9 +493,6 @@ |
71 | target: item |
72 | onChildActivated: childActivated(); |
73 | } |
74 | - |
75 | - Component.onCompleted: item.select(0); |
76 | - onVisibleChanged: if (visible) { item.select(0); } |
77 | } |
78 | } |
79 | } |
80 | |
81 | === modified file 'tests/mocks/QMenuModel/unitymenumodel.cpp' |
82 | --- tests/mocks/QMenuModel/unitymenumodel.cpp 2017-01-10 14:46:09 +0000 |
83 | +++ tests/mocks/QMenuModel/unitymenumodel.cpp 2017-03-13 14:11:08 +0000 |
84 | @@ -18,6 +18,8 @@ |
85 | |
86 | #include "unitymenumodel.h" |
87 | |
88 | +#include <QTimer> |
89 | + |
90 | enum MenuRoles { |
91 | LabelRole = Qt::DisplayRole + 1, |
92 | SensitiveRole, |
93 | @@ -35,7 +37,8 @@ |
94 | }; |
95 | |
96 | UnityMenuModel::UnityMenuModel(QObject *parent) |
97 | -: QAbstractListModel(parent) |
98 | + : QAbstractListModel(parent) |
99 | + , m_rowCountStatus(NoRequestMade) |
100 | { |
101 | } |
102 | |
103 | @@ -60,7 +63,7 @@ |
104 | |
105 | void UnityMenuModel::insertRow(int row, const QVariant& data) |
106 | { |
107 | - row = qMin(row, rowCount()); |
108 | + row = qMin(row, m_modelData.count()); |
109 | |
110 | beginInsertRows(QModelIndex(), row, row); |
111 | |
112 | @@ -71,12 +74,12 @@ |
113 | |
114 | void UnityMenuModel::appendRow(const QVariant& data) |
115 | { |
116 | - insertRow(rowCount(), data); |
117 | + insertRow(m_modelData.count(), data); |
118 | } |
119 | |
120 | void UnityMenuModel::removeRow(int row) |
121 | { |
122 | - if (row < 0 || rowCount() <= row) { |
123 | + if (row < 0 || m_modelData.count() <= row) { |
124 | return; |
125 | } |
126 | |
127 | @@ -133,6 +136,22 @@ |
128 | |
129 | int UnityMenuModel::rowCount(const QModelIndex&) const |
130 | { |
131 | + // Fake the rowCount to be 0 for a while (100ms) |
132 | + // This emulates menus in real world that don't load immediately |
133 | + if (m_rowCountStatus == TimerRunning) |
134 | + return 0; |
135 | + |
136 | + if (m_rowCountStatus == NoRequestMade) { |
137 | + UnityMenuModel *that = const_cast<UnityMenuModel*>(this); |
138 | + that->m_rowCountStatus = TimerRunning; |
139 | + QTimer::singleShot(100, that, [that] { |
140 | + that->beginInsertRows(QModelIndex(), 0, that->m_modelData.count() - 1); |
141 | + that->m_rowCountStatus = TimerFinished; |
142 | + that->endInsertRows(); |
143 | + }); |
144 | + return 0; |
145 | + } |
146 | + |
147 | return m_modelData.count(); |
148 | } |
149 | |
150 | |
151 | === modified file 'tests/mocks/QMenuModel/unitymenumodel.h' |
152 | --- tests/mocks/QMenuModel/unitymenumodel.h 2016-09-29 09:15:04 +0000 |
153 | +++ tests/mocks/QMenuModel/unitymenumodel.h 2017-03-13 14:11:08 +0000 |
154 | @@ -101,6 +101,13 @@ |
155 | QByteArray m_busName; |
156 | QVariantMap m_actions; |
157 | QByteArray m_menuObjectPath; |
158 | + |
159 | + enum RowCountStatus { |
160 | + NoRequestMade, |
161 | + TimerRunning, |
162 | + TimerFinished |
163 | + }; |
164 | + RowCountStatus m_rowCountStatus; |
165 | }; |
166 | |
167 | #endif // MOCK_UNITYMENUMODEL_H |
168 | |
169 | === modified file 'tests/qmltests/ApplicationMenus/tst_MenuBar.qml' |
170 | --- tests/qmltests/ApplicationMenus/tst_MenuBar.qml 2017-03-03 10:21:54 +0000 |
171 | +++ tests/qmltests/ApplicationMenus/tst_MenuBar.qml 2017-03-13 14:11:08 +0000 |
172 | @@ -271,5 +271,39 @@ |
173 | return false; |
174 | }, true); |
175 | } |
176 | + |
177 | + function test_firstDisabled() { |
178 | + var data = appMenuData.generateTestData(10,5,2,3); |
179 | + data[0].submenu[1].submenu[0].rowData.sensitive = false; |
180 | + menuBackend.modelData = data; |
181 | + |
182 | + var menuItem = findChild(menuBar, "menuBar-item0"); |
183 | + menuItem.show(); |
184 | + |
185 | + // waits for item to be created so the keyclick actually works |
186 | + findChild(menuBar, "menuBar-item0-menu-item1-actionItem"); |
187 | + |
188 | + keyClick(Qt.Key_Down); |
189 | + keyClick(Qt.Key_Down); |
190 | + keyClick(Qt.Key_Right); |
191 | + |
192 | + var submenu = findChild(menuBar, "menuBar-item0-menu-item1-menu"); |
193 | + var priv = findInvisibleChild(submenu, "d"); |
194 | + var subActionItem1 = findChild(submenu, "menuBar-item0-menu-item1-menu-item1-actionItem"); |
195 | + compare(priv.currentItem.item, subActionItem1); |
196 | + |
197 | + keyClick(Qt.Key_Down); |
198 | + var subActionItem3 = findChild(submenu, "menuBar-item0-menu-item1-menu-item3-actionItem"); |
199 | + compare(priv.currentItem.item, subActionItem3); |
200 | + |
201 | + // now move mouse over to a different item and back to exercise a different codepath |
202 | + var actionItem0 = findChild(menuBar, "menuBar-item0-menu-item0-actionItem"); |
203 | + mouseMove(actionItem0, actionItem0.width/2, actionItem0.height/2); |
204 | + |
205 | + var actionItem1 = findChild(menuBar, "menuBar-item0-menu-item1-actionItem"); |
206 | + mouseMove(actionItem1, actionItem1.width/2, actionItem1.height/2); |
207 | + |
208 | + tryCompareFunction(function() { return priv.currentItem.item == subActionItem1; }, true); |
209 | + } |
210 | } |
211 | } |
212 | |
213 | === modified file 'tests/qmltests/ApplicationMenus/tst_MenuPopup.qml' |
214 | --- tests/qmltests/ApplicationMenus/tst_MenuPopup.qml 2017-01-25 14:10:44 +0000 |
215 | +++ tests/qmltests/ApplicationMenus/tst_MenuPopup.qml 2017-03-13 14:11:08 +0000 |
216 | @@ -160,9 +160,6 @@ |
217 | var priv = findInvisibleChild(menu, "d"); |
218 | |
219 | keyClick(Qt.Key_Down, Qt.NoModifier); |
220 | - compare(priv.currentItem, item0, "CurrentItem should have moved to item 0"); |
221 | - |
222 | - keyClick(Qt.Key_Down, Qt.NoModifier); |
223 | compare(priv.currentItem, item1, "CurrentItem should have moved to item 1"); |
224 | |
225 | keyClick(Qt.Key_Down, Qt.NoModifier); |
226 | @@ -170,6 +167,9 @@ |
227 | |
228 | keyClick(Qt.Key_Down, Qt.NoModifier); |
229 | compare(priv.currentItem, item0, "CurrentItem should have moved to item 0"); |
230 | + |
231 | + keyClick(Qt.Key_Down, Qt.NoModifier); |
232 | + compare(priv.currentItem, item1, "CurrentItem should have moved to item 1"); |
233 | } |
234 | |
235 | function test_keyboardNavigation_UpKeySelectsAndOpensPreviousMenuItemAndRotates() { |
236 | @@ -181,17 +181,17 @@ |
237 | |
238 | var priv = findInvisibleChild(menu, "d"); |
239 | |
240 | - keyClick(Qt.Key_Down, Qt.NoModifier); |
241 | - compare(priv.currentItem, item0, "CurrentItem should have moved to item 2"); |
242 | + keyClick(Qt.Key_Up, Qt.NoModifier); |
243 | + compare(priv.currentItem, item2, "CurrentItem should have moved to item 2"); |
244 | |
245 | - keyClick(Qt.Key_Down, Qt.NoModifier); |
246 | + keyClick(Qt.Key_Up, Qt.NoModifier); |
247 | compare(priv.currentItem, item1, "CurrentItem should have moved to item 1"); |
248 | |
249 | - keyClick(Qt.Key_Down, Qt.NoModifier); |
250 | - compare(priv.currentItem, item2, "CurrentItem should have moved to item 0"); |
251 | + keyClick(Qt.Key_Up, Qt.NoModifier); |
252 | + compare(priv.currentItem, item0, "CurrentItem should have moved to item 0"); |
253 | |
254 | - keyClick(Qt.Key_Down, Qt.NoModifier); |
255 | - compare(priv.currentItem, item0, "CurrentItem should have moved to item 2"); |
256 | + keyClick(Qt.Key_Up, Qt.NoModifier); |
257 | + compare(priv.currentItem, item2, "CurrentItem should have moved to item 2"); |
258 | } |
259 | |
260 | function test_keyboardNavigation_RightKeyEntersSubMenu() { |
261 | @@ -217,7 +217,7 @@ |
262 | |
263 | var menuItem = findChild(menu, "menu-item0"); verify(menuItem); |
264 | mouseClick(menuItem, menuItem.width/2, menuItem.height/2); |
265 | - tryCompareFunction(function() { return menuItem.popup !== null && menuItem.popup.visible }, true); |
266 | + tryCompareFunction(function() { return menuItem.popup !== null && findInvisibleChild(menuItem.popup, "d").currentItem !== null }, true); |
267 | |
268 | keyClick(Qt.Key_Left, Qt.NoModifier); |
269 | tryCompareFunction(function() { return menuItem.popup !== null && menuItem.popup.visible }, false); |
270 | @@ -245,13 +245,16 @@ |
271 | |
272 | menu.unityMenuModel.modelData = differentSizesMenu; |
273 | |
274 | - waitForRendering(menu); |
275 | + // Wait for the two items to be there |
276 | + tryCompareFunction(function() { return findChild(menu, "menu-item1") !== null; }, true); |
277 | var longWidth = menu.width; |
278 | + |
279 | + // Now pop one item and make sure it's smaller |
280 | differentSizesMenu.pop(); |
281 | menu.unityMenuModel.modelData = differentSizesMenu; |
282 | |
283 | - waitForRendering(menu); |
284 | - verify(menu.width < longWidth); |
285 | + tryCompareFunction(function() { return findChild(menu, "menu-item0") !== null; }, true); |
286 | + tryCompareFunction(function() { return menu.width < longWidth; }, true); |
287 | } |
288 | |
289 | function test_minimumWidth() { |
It does improve the situation but there is something odd:
again, using kate as an example, opening the Tools menu, navigate down to a submenu, enter it, navigate down to the third entry, press left, go to another submenu, enter it. Now the highlight will be at the third entry instead of starting at the first again.
also I managed to get into the situation where I lost keyboard focus altogether in the menu by navigating through the main menu entries. While I'm not 100% sure that is introduced by this branch, I haven't seen it in trunk so far.