Merge lp:~aacid/unity8/makeSureMenuPositionOnScreen into lp:unity8
- makeSureMenuPositionOnScreen
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Michael Zanetti |
Approved revision: | 2776 |
Merged at revision: | 2811 |
Proposed branch: | lp:~aacid/unity8/makeSureMenuPositionOnScreen |
Merge into: | lp:unity8 |
Prerequisite: | lp:~aacid/unity8/clickOpenMenuClosesIt |
Diff against target: |
282 lines (+174/-17) 5 files modified
qml/ApplicationMenus/ApplicationMenusLimits.qml (+24/-0) qml/ApplicationMenus/MenuBar.qml (+2/-2) qml/ApplicationMenus/MenuPopup.qml (+47/-15) qml/ApplicationMenus/qmldir (+1/-0) tests/qmltests/Stage/tst_DesktopStage.qml (+100/-0) |
To merge this branch: | bzr merge lp:~aacid/unity8/makeSureMenuPositionOnScreen |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Zanetti (community) | Approve | ||
Unity8 CI Bot | continuous-integration | Approve | |
Review via email: mp+315655@code.launchpad.net |
This proposal supersedes a proposal from 2017-01-24.
Commit message
Make the menus and submenus do not go outside the screen when popping out
Description of the change
* Are there any related MPs required for this MP to build/function as expected?
No
* 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
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2773
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:/
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:2774
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 : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:2775
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:2776
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:2776
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:/
Michael Zanetti (mzanetti) wrote : | # |
code looks fine, it also works great in my testing.
Visually it allows getting in rather unpleasant situations like for example this: http://
should we report a bug for design for this? wdyt?
Albert Astals Cid (aacid) wrote : | # |
> code looks fine, it also works great in my testing.
>
> Visually it allows getting in rather unpleasant situations like for example
> this: http://
>
> should we report a bug for design for this? wdyt?
Yes, i agree we should open a bug for design about this. Will you or want me to?
Meanwhile if possible I'd like to get this landed as defenitely improves the situation.
Michael Zanetti (mzanetti) wrote : | # |
Reported the bug: https:/
apart from this, it works fine. code looks good
- 2777. By Albert Astals Cid
-
Merge
Preview Diff
1 | === added file 'qml/ApplicationMenus/ApplicationMenusLimits.qml' |
2 | --- qml/ApplicationMenus/ApplicationMenusLimits.qml 1970-01-01 00:00:00 +0000 |
3 | +++ qml/ApplicationMenus/ApplicationMenusLimits.qml 2017-02-07 15:45:21 +0000 |
4 | @@ -0,0 +1,24 @@ |
5 | +/* |
6 | + * Copyright (C) 2017 Canonical, Ltd. |
7 | + * |
8 | + * This program is free software; you can redistribute it and/or modify |
9 | + * it under the terms of the GNU General Public License as published by |
10 | + * the Free Software Foundation; version 3. |
11 | + * |
12 | + * This program is distributed in the hope that it will be useful, |
13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | + * GNU General Public License for more details. |
16 | + * |
17 | + * You should have received a copy of the GNU General Public License |
18 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | + */ |
20 | + |
21 | +pragma Singleton |
22 | +import QtQuick 2.4 |
23 | +import QtQuick.Window 2.2 |
24 | + |
25 | +QtObject { |
26 | + property real screenWidth: Screen.width |
27 | + property real screenHeight: Screen.height |
28 | +} |
29 | |
30 | === modified file 'qml/ApplicationMenus/MenuBar.qml' |
31 | --- qml/ApplicationMenus/MenuBar.qml 2017-02-07 15:45:21 +0000 |
32 | +++ qml/ApplicationMenus/MenuBar.qml 2017-02-07 15:45:21 +0000 |
33 | @@ -149,8 +149,8 @@ |
34 | Component { |
35 | id: menuComponent |
36 | MenuPopup { |
37 | - x: visualItem.x - units.gu(1) |
38 | - anchors.top: parent.bottom |
39 | + desiredX: visualItem.x - units.gu(1) |
40 | + desiredY: parent.height |
41 | unityMenuModel: root.unityMenuModel.submenu(visualItem.__ownIndex) |
42 | |
43 | Component.onCompleted: reset(); |
44 | |
45 | === modified file 'qml/ApplicationMenus/MenuPopup.qml' |
46 | --- qml/ApplicationMenus/MenuPopup.qml 2017-02-07 15:45:21 +0000 |
47 | +++ qml/ApplicationMenus/MenuPopup.qml 2017-02-07 15:45:21 +0000 |
48 | @@ -16,10 +16,10 @@ |
49 | |
50 | import QtQuick 2.4 |
51 | import QtQuick.Layouts 1.1 |
52 | -import QtQuick.Window 2.2 |
53 | import Ubuntu.Components 1.3 |
54 | import Ubuntu.Components.ListItems 1.3 as ListItems |
55 | import "../Components" |
56 | +import "." |
57 | |
58 | UbuntuShape { |
59 | id: root |
60 | @@ -28,6 +28,38 @@ |
61 | |
62 | signal childActivated() |
63 | |
64 | + // true for submenus that need to show on the other side of their parent |
65 | + // if they don't fit when growing right |
66 | + property bool substractWidth: false |
67 | + |
68 | + property real desiredX |
69 | + x: { |
70 | + var dummy = visible; // force recalc when shown/hidden |
71 | + var parentTopLeft = parent.mapToItem(null, 0, 0); |
72 | + var farX = ApplicationMenusLimits.screenWidth; |
73 | + if (parentTopLeft.x + width + desiredX <= farX) { |
74 | + return desiredX; |
75 | + } else { |
76 | + if (substractWidth) { |
77 | + return -width; |
78 | + } else { |
79 | + return farX - parentTopLeft.x - width; |
80 | + } |
81 | + } |
82 | + } |
83 | + |
84 | + property real desiredY |
85 | + y: { |
86 | + var dummy = visible; // force recalc when shown/hidden |
87 | + var parentTopLeft = parent.mapToItem(null, 0, 0); |
88 | + var bottomY = ApplicationMenusLimits.screenHeight; |
89 | + if (parentTopLeft.y + height + desiredY <= bottomY) { |
90 | + return desiredY; |
91 | + } else { |
92 | + return bottomY - parentTopLeft.y - height; |
93 | + } |
94 | + } |
95 | + |
96 | property alias unityMenuModel: repeater.model |
97 | |
98 | function show() { |
99 | @@ -66,9 +98,9 @@ |
100 | readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1 |
101 | |
102 | property real __minimumWidth: units.gu(20) |
103 | - property real __maximumWidth: Screen.width * 0.7 |
104 | + property real __maximumWidth: ApplicationMenusLimits.screenWidth * 0.7 |
105 | property real __minimumHeight: units.gu(2) |
106 | - property real __maximumHeight: Screen.height * 0.7 |
107 | + property real __maximumHeight: ApplicationMenusLimits.screenHeight * 0.7 |
108 | |
109 | signal dismissAll() |
110 | |
111 | @@ -259,8 +291,9 @@ |
112 | popup = submenuComponent.createObject(focusScope, { |
113 | objectName: loader.objectName + "-", |
114 | unityMenuModel: model, |
115 | - x: Qt.binding(function() { return root.width }), |
116 | - y: Qt.binding(function() { |
117 | + substractWidth: true, |
118 | + desiredX: Qt.binding(function() { return root.width }), |
119 | + desiredY: Qt.binding(function() { |
120 | var dummy = listView.contentY; // force a recalc on contentY change. |
121 | return mapToItem(container, 0, y).y; |
122 | }) |
123 | @@ -383,20 +416,19 @@ |
124 | id: submenuLoader |
125 | source: "MenuPopup.qml" |
126 | |
127 | + property real desiredX |
128 | + property real desiredY |
129 | + property bool substractWidth |
130 | property var unityMenuModel: null |
131 | signal retreat() |
132 | signal childActivated() |
133 | |
134 | - Binding { |
135 | - target: item |
136 | - property: "unityMenuModel" |
137 | - value: submenuLoader.unityMenuModel |
138 | - } |
139 | - |
140 | - Binding { |
141 | - target: item |
142 | - property: "objectName" |
143 | - value: submenuLoader.objectName + "menu" |
144 | + onLoaded: { |
145 | + item.unityMenuModel = Qt.binding(function() { return submenuLoader.unityMenuModel; }); |
146 | + item.objectName = Qt.binding(function() { return submenuLoader.objectName + "menu"; }); |
147 | + item.desiredX = Qt.binding(function() { return submenuLoader.desiredX; }); |
148 | + item.desiredY = Qt.binding(function() { return submenuLoader.desiredY; }); |
149 | + item.substractWidth = Qt.binding(function() { return submenuLoader.substractWidth; }); |
150 | } |
151 | |
152 | Keys.onLeftPressed: retreat() |
153 | |
154 | === added file 'qml/ApplicationMenus/qmldir' |
155 | --- qml/ApplicationMenus/qmldir 1970-01-01 00:00:00 +0000 |
156 | +++ qml/ApplicationMenus/qmldir 2017-02-07 15:45:21 +0000 |
157 | @@ -0,0 +1,1 @@ |
158 | +singleton ApplicationMenusLimits 0.1 ApplicationMenusLimits.qml |
159 | |
160 | === modified file 'tests/qmltests/Stage/tst_DesktopStage.qml' |
161 | --- tests/qmltests/Stage/tst_DesktopStage.qml 2017-01-10 14:44:29 +0000 |
162 | +++ tests/qmltests/Stage/tst_DesktopStage.qml 2017-02-07 15:45:21 +0000 |
163 | @@ -29,6 +29,7 @@ |
164 | import "../../../qml/Stage" |
165 | import "../../../qml/Components" |
166 | import "../../../qml/Components/PanelState" |
167 | +import "../../../qml/ApplicationMenus" |
168 | |
169 | Item { |
170 | id: root |
171 | @@ -44,6 +45,8 @@ |
172 | } |
173 | |
174 | Component.onCompleted: { |
175 | + ApplicationMenusLimits.screenWidth = Qt.binding( function() { return stageLoader.width; } ); |
176 | + ApplicationMenusLimits.screenHeight = Qt.binding( function() { return stageLoader.height; } ); |
177 | QuickUtils.keyboardAttached = true; |
178 | theme.name = "Ubuntu.Components.Themes.SuruDark"; |
179 | resetGeometry(); |
180 | @@ -830,5 +833,102 @@ |
181 | mouseRelease(decoration); |
182 | tryCompare(Mir, "cursorName", ""); |
183 | } |
184 | + |
185 | + function test_menuPositioning_data() { |
186 | + return [ |
187 | + {tag: "good", |
188 | + windowPosition: Qt.point(units.gu(10), units.gu(10)) |
189 | + }, |
190 | + {tag: "collides right", |
191 | + windowPosition: Qt.point(units.gu(100), units.gu(10)), |
192 | + minimumXDifference: units.gu(8) |
193 | + }, |
194 | + {tag: "collides bottom", |
195 | + windowPosition: Qt.point(units.gu(10), units.gu(80)), |
196 | + minimumYDifference: units.gu(7) |
197 | + }, |
198 | + ] |
199 | + } |
200 | + |
201 | + function test_menuPositioning(data) { |
202 | + var appDelegate = startApplication("dialer-app"); |
203 | + appDelegate.windowedX = data.windowPosition.x; |
204 | + appDelegate.windowedY = data.windowPosition.y; |
205 | + |
206 | + var menuItem = findChild(appDelegate, "menuBar-item3"); |
207 | + menuItem.show(); |
208 | + |
209 | + var menu = findChild(appDelegate, "menuBar-item3-menu"); |
210 | + tryCompare(menu, "visible", true); |
211 | + |
212 | + var normalPositioningX = menuItem.x - units.gu(1); |
213 | + var normalPositioningY = menuItem.height; |
214 | + |
215 | + // We do this fuzzy checking because otherwise we would be duplicating the code |
216 | + // that calculates the coordinates and any bug it may have, what we want is really |
217 | + // to check that on collision with the border the menu is shifted substantially |
218 | + if (data.minimumXDifference) { |
219 | + verify(menu.x < normalPositioningX - data.minimumXDifference); |
220 | + } else { |
221 | + compare(menu.x, normalPositioningX); |
222 | + } |
223 | + |
224 | + if (data.minimumYDifference) { |
225 | + verify(menu.y < normalPositioningY - data.minimumYDifference); |
226 | + } else { |
227 | + compare(menu.y, normalPositioningY); |
228 | + } |
229 | + } |
230 | + |
231 | + function test_submenuPositioning_data() { |
232 | + return [ |
233 | + {tag: "good", |
234 | + windowPosition: Qt.point(units.gu(10), units.gu(10)) |
235 | + }, |
236 | + {tag: "collides right", |
237 | + windowPosition: Qt.point(units.gu(100), units.gu(10)), |
238 | + minimumXDifference: units.gu(35) |
239 | + }, |
240 | + {tag: "collides bottom", |
241 | + windowPosition: Qt.point(units.gu(10), units.gu(80)), |
242 | + minimumYDifference: units.gu(8) |
243 | + }, |
244 | + ] |
245 | + } |
246 | + |
247 | + function test_submenuPositioning(data) { |
248 | + var appDelegate = startApplication("dialer-app"); |
249 | + appDelegate.windowedX = data.windowPosition.x; |
250 | + appDelegate.windowedY = data.windowPosition.y; |
251 | + |
252 | + var menuItem = findChild(appDelegate, "menuBar-item3"); |
253 | + menuItem.show(); |
254 | + |
255 | + var menu = findChild(appDelegate, "menuBar-item3-menu"); |
256 | + menuItem = findChild(menu, "menuBar-item3-menu-item3-actionItem"); |
257 | + tryCompare(menuItem, "visible", true); |
258 | + mouseMove(menuItem); |
259 | + mouseClick(menuItem); |
260 | + |
261 | + menu = findChild(appDelegate, "menuBar-item3-menu-item3-menu"); |
262 | + |
263 | + var normalPositioningX = menuItem.width; |
264 | + var normalPositioningY = menuItem.parent.y; |
265 | + |
266 | + // We do this fuzzy checking because otherwise we would be duplicating the code |
267 | + // that calculates the coordinates and any bug it may have, what we want is really |
268 | + // to check that on collision with the border the menu is shifted substantially |
269 | + if (data.minimumXDifference) { |
270 | + verify(menu.x < normalPositioningX - data.minimumXDifference); |
271 | + } else { |
272 | + compare(menu.x, normalPositioningX); |
273 | + } |
274 | + |
275 | + if (data.minimumYDifference) { |
276 | + verify(menu.y < normalPositioningY - data.minimumYDifference); |
277 | + } else { |
278 | + compare(menu.y, normalPositioningY); |
279 | + } |
280 | + } |
281 | } |
282 | } |
FAILED: Continuous integration, rev:2772 /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/3011/ /unity8- jenkins. ubuntu. com/job/ build/3916 /unity8- jenkins. ubuntu. com/job/ test-0- autopkgtest/ label=amd64, release= xenial+ overlay, testname= qmluitests. sh/2294 /unity8- jenkins. ubuntu. com/job/ test-0- autopkgtest/ label=amd64, release= zesty,testname= qmluitests. sh/2294 /unity8- jenkins. ubuntu. com/job/ build-0- fetch/3944 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 3789 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 3789/artifact/ output/ *zip*/output. zip /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= zesty/3789 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= zesty/3789/ artifact/ output/ *zip*/output. zip /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 3789 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 3789/artifact/ output/ *zip*/output. zip /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= zesty/3789 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= zesty/3789/ artifact/ output/ *zip*/output. zip /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 3789 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 3789/artifact/ output/ *zip*/output. zip /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= zesty/3789 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= zesty/3789/ artifact/ output/ *zip*/output. zip
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: /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/3011/ rebuild
https:/