Merge lp:~aacid/unity8/makeSureMenuPositionOnScreen into lp:unity8
- makeSureMenuPositionOnScreen
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~aacid/unity8/makeSureMenuPositionOnScreen |
Merge into: | lp:unity8 |
Prerequisite: | lp:~nick-dedekind/unity8/menu.width.fix |
Diff against target: |
408 lines (+252/-24) 6 files modified
qml/ApplicationMenus/ApplicationMenusLimits.qml (+24/-0) qml/ApplicationMenus/MenuBar.qml (+10/-3) qml/ApplicationMenus/MenuPopup.qml (+67/-21) qml/ApplicationMenus/qmldir (+1/-0) tests/qmltests/ApplicationMenus/tst_MenuBar.qml (+50/-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 |
---|---|---|---|
Unity8 CI Bot | continuous-integration | Approve | |
Unity Team | Pending | ||
Review via email: mp+315470@code.launchpad.net |
This proposal has been superseded by a proposal from 2017-01-26.
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
- 2773. By Albert Astals Cid
-
Now with better files!
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
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:/
- 2774. By Albert Astals Cid
-
Do not use exact gu values for the compares
Since there's text involved the gu values are not the same depending your px per gu, so do it in a bit of more fuzzy way
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
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:/
- 2775. By Albert Astals Cid
-
Merge
- 2776. By Albert Astals Cid
-
Merge clickOpenMenuCl
osesIt
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
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:/
- 2777. By Albert Astals Cid
-
Merge
Unmerged revisions
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-01-26 09:29:42 +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-01-17 09:45:22 +0000 |
32 | +++ qml/ApplicationMenus/MenuBar.qml 2017-01-26 09:29:42 +0000 |
33 | @@ -114,6 +114,7 @@ |
34 | function show() { |
35 | if (!__popup) { |
36 | __popup = menuComponent.createObject(root, { objectName: visualItem.objectName + "-menu" }); |
37 | + __popup.childActivated.connect(dismiss); |
38 | // force the current item to be the newly popped up menu |
39 | } else { |
40 | __popup.show(); |
41 | @@ -148,8 +149,8 @@ |
42 | Component { |
43 | id: menuComponent |
44 | MenuPopup { |
45 | - x: visualItem.x - units.gu(1) |
46 | - anchors.top: parent.bottom |
47 | + desiredX: visualItem.x - units.gu(1) |
48 | + desiredY: parent.height |
49 | unityMenuModel: root.unityMenuModel.submenu(visualItem.__ownIndex) |
50 | |
51 | Component.onCompleted: reset(); |
52 | @@ -212,7 +213,13 @@ |
53 | updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y)) |
54 | } |
55 | } |
56 | - onClicked: updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y)) |
57 | + onClicked: { |
58 | + var prevItem = d.currentItem; |
59 | + updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y)) |
60 | + if (prevItem && d.currentItem == prevItem) { |
61 | + prevItem.hide(); |
62 | + } |
63 | + } |
64 | |
65 | function updateCurrentItemFromPosition(point) { |
66 | var pos = mapToItem(row, point.x, point.y); |
67 | |
68 | === modified file 'qml/ApplicationMenus/MenuPopup.qml' |
69 | --- qml/ApplicationMenus/MenuPopup.qml 2017-01-18 12:08:05 +0000 |
70 | +++ qml/ApplicationMenus/MenuPopup.qml 2017-01-26 09:29:42 +0000 |
71 | @@ -16,16 +16,50 @@ |
72 | |
73 | import QtQuick 2.4 |
74 | import QtQuick.Layouts 1.1 |
75 | -import QtQuick.Window 2.2 |
76 | import Ubuntu.Components 1.3 |
77 | import Ubuntu.Components.ListItems 1.3 as ListItems |
78 | import "../Components" |
79 | +import "." |
80 | |
81 | UbuntuShape { |
82 | id: root |
83 | objectName: "menu" |
84 | backgroundColor: theme.palette.normal.overlay |
85 | |
86 | + signal childActivated() |
87 | + |
88 | + // true for submenus that need to show on the other side of their parent |
89 | + // if they don't fit when growing right |
90 | + property bool substractWidth: false |
91 | + |
92 | + property real desiredX |
93 | + x: { |
94 | + var dummy = visible; // force recalc when shown/hidden |
95 | + var parentTopLeft = parent.mapToItem(null, 0, 0); |
96 | + var farX = ApplicationMenusLimits.screenWidth; |
97 | + if (parentTopLeft.x + width + desiredX <= farX) { |
98 | + return desiredX; |
99 | + } else { |
100 | + if (substractWidth) { |
101 | + return -width; |
102 | + } else { |
103 | + return farX - parentTopLeft.x - width; |
104 | + } |
105 | + } |
106 | + } |
107 | + |
108 | + property real desiredY |
109 | + y: { |
110 | + var dummy = visible; // force recalc when shown/hidden |
111 | + var parentTopLeft = parent.mapToItem(null, 0, 0); |
112 | + var bottomY = ApplicationMenusLimits.screenHeight; |
113 | + if (parentTopLeft.y + height + desiredY <= bottomY) { |
114 | + return desiredY; |
115 | + } else { |
116 | + return bottomY - parentTopLeft.y - height; |
117 | + } |
118 | + } |
119 | + |
120 | property alias unityMenuModel: repeater.model |
121 | |
122 | function show() { |
123 | @@ -64,9 +98,9 @@ |
124 | readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1 |
125 | |
126 | property real __minimumWidth: units.gu(20) |
127 | - property real __maximumWidth: Screen.width * 0.7 |
128 | + property real __maximumWidth: ApplicationMenusLimits.screenWidth * 0.7 |
129 | property real __minimumHeight: units.gu(2) |
130 | - property real __maximumHeight: Screen.height * 0.7 |
131 | + property real __maximumHeight: ApplicationMenusLimits.screenHeight * 0.7 |
132 | |
133 | signal dismissAll() |
134 | |
135 | @@ -257,22 +291,29 @@ |
136 | popup = submenuComponent.createObject(focusScope, { |
137 | objectName: loader.objectName + "-", |
138 | unityMenuModel: model, |
139 | - x: Qt.binding(function() { return root.width }), |
140 | - y: Qt.binding(function() { |
141 | + substractWidth: true, |
142 | + desiredX: Qt.binding(function() { return root.width }), |
143 | + desiredY: Qt.binding(function() { |
144 | var dummy = listView.contentY; // force a recalc on contentY change. |
145 | return mapToItem(container, 0, y).y; |
146 | }) |
147 | }); |
148 | + popup.retreat.connect(function() { |
149 | + popup.destroy(); |
150 | + popup = null; |
151 | + menuItem.forceActiveFocus(); |
152 | + }); |
153 | + popup.childActivated.connect(function() { |
154 | + popup.destroy(); |
155 | + popup = null; |
156 | + root.childActivated(); |
157 | + }); |
158 | } else if (popup) { |
159 | popup.visible = true; |
160 | } |
161 | - popup.retreat.connect(function() { |
162 | - popup.destroy(); |
163 | - popup = null; |
164 | - menuItem.forceActiveFocus(); |
165 | - }) |
166 | } else { |
167 | root.unityMenuModel.activate(__ownIndex); |
168 | + root.childActivated(); |
169 | } |
170 | } |
171 | |
172 | @@ -375,23 +416,28 @@ |
173 | id: submenuLoader |
174 | source: "MenuPopup.qml" |
175 | |
176 | + property real desiredX |
177 | + property real desiredY |
178 | + property bool substractWidth |
179 | property var unityMenuModel: null |
180 | signal retreat() |
181 | - |
182 | - Binding { |
183 | - target: item |
184 | - property: "unityMenuModel" |
185 | - value: submenuLoader.unityMenuModel |
186 | - } |
187 | - |
188 | - Binding { |
189 | - target: item |
190 | - property: "objectName" |
191 | - value: submenuLoader.objectName + "menu" |
192 | + signal childActivated() |
193 | + |
194 | + onLoaded: { |
195 | + item.unityMenuModel = Qt.binding(function() { return submenuLoader.unityMenuModel; }); |
196 | + item.objectName = Qt.binding(function() { return submenuLoader.objectName + "menu"; }); |
197 | + item.desiredX = Qt.binding(function() { return submenuLoader.desiredX; }); |
198 | + item.desiredY = Qt.binding(function() { return submenuLoader.desiredY; }); |
199 | + item.substractWidth = Qt.binding(function() { return submenuLoader.substractWidth; }); |
200 | } |
201 | |
202 | Keys.onLeftPressed: retreat() |
203 | |
204 | + Connections { |
205 | + target: item |
206 | + onChildActivated: childActivated(); |
207 | + } |
208 | + |
209 | Component.onCompleted: item.select(0); |
210 | onVisibleChanged: if (visible) { item.select(0); } |
211 | } |
212 | |
213 | === added file 'qml/ApplicationMenus/qmldir' |
214 | --- qml/ApplicationMenus/qmldir 1970-01-01 00:00:00 +0000 |
215 | +++ qml/ApplicationMenus/qmldir 2017-01-26 09:29:42 +0000 |
216 | @@ -0,0 +1,1 @@ |
217 | +singleton ApplicationMenusLimits 0.1 ApplicationMenusLimits.qml |
218 | |
219 | === modified file 'tests/qmltests/ApplicationMenus/tst_MenuBar.qml' |
220 | --- tests/qmltests/ApplicationMenus/tst_MenuBar.qml 2017-01-24 07:41:35 +0000 |
221 | +++ tests/qmltests/ApplicationMenus/tst_MenuBar.qml 2017-01-26 09:29:42 +0000 |
222 | @@ -180,6 +180,41 @@ |
223 | tryCompare(priv, "currentItem", menuItem); |
224 | } |
225 | |
226 | + function test_menuActivateClosesMenu() { |
227 | + menuBackend.modelData = appMenuData.generateTestData(3,3,0,0,"menu"); |
228 | + var priv = findInvisibleChild(menuBar, "d"); |
229 | + |
230 | + var menuItem = findChild(menuBar, "menuBar-item0"); |
231 | + menuItem.show(); |
232 | + compare(priv.currentItem, menuItem, "CurrentItem should be set to item 0"); |
233 | + compare(priv.currentItem.popupVisible, true, "Popup should be visible"); |
234 | + |
235 | + var actionItem = findChild(menuBar, "menuBar-item0-menu-item0-actionItem"); |
236 | + mouseClick(actionItem); |
237 | + compare(priv.currentItem, null, "CurrentItem should be null"); |
238 | + } |
239 | + |
240 | + function test_subMenuActivateClosesMenu() { |
241 | + menuBackend.modelData = appMenuData.generateTestData(3,4,1,0,"menu"); |
242 | + var priv = findInvisibleChild(menuBar, "d"); |
243 | + |
244 | + var menuItem = findChild(menuBar, "menuBar-item0"); |
245 | + menuItem.show(); |
246 | + compare(priv.currentItem, menuItem, "CurrentItem should be set to item 0"); |
247 | + compare(priv.currentItem.popupVisible, true, "Popup should be visible"); |
248 | + |
249 | + var actionItem = findChild(menuBar, "menuBar-item0-menu-item0-actionItem"); |
250 | + mouseClick(actionItem); |
251 | + |
252 | + actionItem = findChild(menuBar, "menuBar-item0-menu-item0-menu-item0-actionItem"); |
253 | + mouseClick(actionItem); |
254 | + |
255 | + actionItem = findChild(menuBar, "menuBar-item0-menu-item0-menu-item0-menu-item0-actionItem"); |
256 | + mouseClick(actionItem); |
257 | + |
258 | + compare(priv.currentItem, null, "CurrentItem should be null"); |
259 | + } |
260 | + |
261 | function test_openAppMenuShortcut() { |
262 | var priv = findInvisibleChild(menuBar, "d"); |
263 | |
264 | @@ -192,5 +227,20 @@ |
265 | keyClick(Qt.Key_F10, Qt.AltModifier); |
266 | compare(priv.currentItem, menuItem1, "First enabled item should be opened"); |
267 | } |
268 | + |
269 | + function test_clickOpenMenuClosesMenu() { |
270 | + menuBackend.modelData = appMenuData.generateTestData(3,3,0,0,"menu"); |
271 | + var priv = findInvisibleChild(menuBar, "d"); |
272 | + |
273 | + var menuItem = findChild(menuBar, "menuBar-item0"); |
274 | + waitForRendering(menuItem); |
275 | + mouseClick(menuItem); |
276 | + compare(priv.currentItem, menuItem, "CurrentItem should be set to item 0"); |
277 | + compare(priv.currentItem.popupVisible, true, "Popup should be visible"); |
278 | + |
279 | + waitForRendering(menuItem); |
280 | + mouseClick(menuItem); |
281 | + compare(priv.currentItem, null, "CurrentItem should be null"); |
282 | + } |
283 | } |
284 | } |
285 | |
286 | === modified file 'tests/qmltests/Stage/tst_DesktopStage.qml' |
287 | --- tests/qmltests/Stage/tst_DesktopStage.qml 2017-01-10 14:44:29 +0000 |
288 | +++ tests/qmltests/Stage/tst_DesktopStage.qml 2017-01-26 09:29:42 +0000 |
289 | @@ -29,6 +29,7 @@ |
290 | import "../../../qml/Stage" |
291 | import "../../../qml/Components" |
292 | import "../../../qml/Components/PanelState" |
293 | +import "../../../qml/ApplicationMenus" |
294 | |
295 | Item { |
296 | id: root |
297 | @@ -44,6 +45,8 @@ |
298 | } |
299 | |
300 | Component.onCompleted: { |
301 | + ApplicationMenusLimits.screenWidth = Qt.binding( function() { return stageLoader.width; } ); |
302 | + ApplicationMenusLimits.screenHeight = Qt.binding( function() { return stageLoader.height; } ); |
303 | QuickUtils.keyboardAttached = true; |
304 | theme.name = "Ubuntu.Components.Themes.SuruDark"; |
305 | resetGeometry(); |
306 | @@ -830,5 +833,102 @@ |
307 | mouseRelease(decoration); |
308 | tryCompare(Mir, "cursorName", ""); |
309 | } |
310 | + |
311 | + function test_menuPositioning_data() { |
312 | + return [ |
313 | + {tag: "good", |
314 | + windowPosition: Qt.point(units.gu(10), units.gu(10)) |
315 | + }, |
316 | + {tag: "collides right", |
317 | + windowPosition: Qt.point(units.gu(100), units.gu(10)), |
318 | + minimumXDifference: units.gu(8) |
319 | + }, |
320 | + {tag: "collides bottom", |
321 | + windowPosition: Qt.point(units.gu(10), units.gu(80)), |
322 | + minimumYDifference: units.gu(7) |
323 | + }, |
324 | + ] |
325 | + } |
326 | + |
327 | + function test_menuPositioning(data) { |
328 | + var appDelegate = startApplication("dialer-app"); |
329 | + appDelegate.windowedX = data.windowPosition.x; |
330 | + appDelegate.windowedY = data.windowPosition.y; |
331 | + |
332 | + var menuItem = findChild(appDelegate, "menuBar-item3"); |
333 | + menuItem.show(); |
334 | + |
335 | + var menu = findChild(appDelegate, "menuBar-item3-menu"); |
336 | + tryCompare(menu, "visible", true); |
337 | + |
338 | + var normalPositioningX = menuItem.x - units.gu(1); |
339 | + var normalPositioningY = menuItem.height; |
340 | + |
341 | + // We do this fuzzy checking because otherwise we would be duplicating the code |
342 | + // that calculates the coordinates and any bug it may have, what we want is really |
343 | + // to check that on collision with the border the menu is shifted substantially |
344 | + if (data.minimumXDifference) { |
345 | + verify(menu.x < normalPositioningX - data.minimumXDifference); |
346 | + } else { |
347 | + compare(menu.x, normalPositioningX); |
348 | + } |
349 | + |
350 | + if (data.minimumYDifference) { |
351 | + verify(menu.y < normalPositioningY - data.minimumYDifference); |
352 | + } else { |
353 | + compare(menu.y, normalPositioningY); |
354 | + } |
355 | + } |
356 | + |
357 | + function test_submenuPositioning_data() { |
358 | + return [ |
359 | + {tag: "good", |
360 | + windowPosition: Qt.point(units.gu(10), units.gu(10)) |
361 | + }, |
362 | + {tag: "collides right", |
363 | + windowPosition: Qt.point(units.gu(100), units.gu(10)), |
364 | + minimumXDifference: units.gu(35) |
365 | + }, |
366 | + {tag: "collides bottom", |
367 | + windowPosition: Qt.point(units.gu(10), units.gu(80)), |
368 | + minimumYDifference: units.gu(8) |
369 | + }, |
370 | + ] |
371 | + } |
372 | + |
373 | + function test_submenuPositioning(data) { |
374 | + var appDelegate = startApplication("dialer-app"); |
375 | + appDelegate.windowedX = data.windowPosition.x; |
376 | + appDelegate.windowedY = data.windowPosition.y; |
377 | + |
378 | + var menuItem = findChild(appDelegate, "menuBar-item3"); |
379 | + menuItem.show(); |
380 | + |
381 | + var menu = findChild(appDelegate, "menuBar-item3-menu"); |
382 | + menuItem = findChild(menu, "menuBar-item3-menu-item3-actionItem"); |
383 | + tryCompare(menuItem, "visible", true); |
384 | + mouseMove(menuItem); |
385 | + mouseClick(menuItem); |
386 | + |
387 | + menu = findChild(appDelegate, "menuBar-item3-menu-item3-menu"); |
388 | + |
389 | + var normalPositioningX = menuItem.width; |
390 | + var normalPositioningY = menuItem.parent.y; |
391 | + |
392 | + // We do this fuzzy checking because otherwise we would be duplicating the code |
393 | + // that calculates the coordinates and any bug it may have, what we want is really |
394 | + // to check that on collision with the border the menu is shifted substantially |
395 | + if (data.minimumXDifference) { |
396 | + verify(menu.x < normalPositioningX - data.minimumXDifference); |
397 | + } else { |
398 | + compare(menu.x, normalPositioningX); |
399 | + } |
400 | + |
401 | + if (data.minimumYDifference) { |
402 | + verify(menu.y < normalPositioningY - data.minimumYDifference); |
403 | + } else { |
404 | + compare(menu.y, normalPositioningY); |
405 | + } |
406 | + } |
407 | } |
408 | } |
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:/