on xenial help sign in
Make launcher scalable, allow it locking Fixes: #1511015
2584355Michael Zanetti
data/com.canonical.Unity8.gschema.xml+11
@@ -27,6 +27,17 @@
27 <summary>Maximum push needed to overcome edge barrier</summary>
27 <summary>Maximum push needed to overcome edge barrier</summary>
28 <description>How much you have to push (in grid units) the mouse against an edge barrier when sensibility is 1.</description>
28 <description>How much you have to push (in grid units) the mouse against an edge barrier when sensibility is 1.</description>
29 </key>
29 </key>
30+ <key type="b" name="autohide-launcher">
31+ <default>true</default>
32+ <summary>Autohide the launcher</summary>
33+ <description>This will only be applied in windowed mode. In staged mode, the launcher will always hide.</description>
34+ </key>
35+ <key type="u" name="launcher-width">
36+ <default>8</default>
37+ <range min="6" max="12"/>
38+ <summary>Width of the launcher in grid units.</summary>
39+ <description>Changes the width of the launcher in all usage modes.</description>
40+ </key>
30 </schema>
41 </schema>
31
42
32 <schema path="/com/canonical/unity8/greeter/" id="com.canonical.Unity8.Greeter" gettext-domain="unity8">
43 <schema path="/com/canonical/unity8/greeter/" id="com.canonical.Unity8.Greeter" gettext-domain="unity8">
qml/Launcher/Launcher.qml+32−10
@@ -25,11 +25,12 @@ FocusScope {
25 id: root
25 id: root
26
26
27 property bool autohideEnabled: false
27 property bool autohideEnabled: false
28+ property bool lockedVisible: false
28 property bool available: true // can be used to disable all interactions
29 property bool available: true // can be used to disable all interactions
29 property alias inverted: panel.inverted
30 property alias inverted: panel.inverted
30 property bool shadeBackground: true // can be used to disable background shade when launcher is visible
31 property bool shadeBackground: true // can be used to disable background shade when launcher is visible
31
32
32− property int panelWidth: units.gu(8)
33+ property int panelWidth: units.gu(10)
33 property int dragAreaWidth: units.gu(1)
34 property int dragAreaWidth: units.gu(1)
34 property int minimizeDistance: units.gu(26)
35 property int minimizeDistance: units.gu(26)
35 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
36 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
@@ -92,12 +93,24 @@ FocusScope {
92 }
93 }
93 }
94 }
94
95
96+ onLockedVisibleChanged: {
97+ if (lockedVisible && state == "") {
98+ panel.dismissTimer.stop();
99+ fadeOutAnimation.stop();
100+ switchToNextState("visible")
101+ } else if (!lockedVisible && state == "visible") {
102+ hide();
103+ }
104+ }
105+
95 function hide() {
106 function hide() {
96 switchToNextState("")
107 switchToNextState("")
97 }
108 }
98
109
99 function fadeOut() {
110 function fadeOut() {
111+ if (!root.lockedVisible) {
100− fadeOutAnimation.start();
112+ fadeOutAnimation.start();
113+ }
101 }
114 }
102
115
103 function switchToNextState(state) {
116 function switchToNextState(state) {
@@ -172,8 +185,8 @@ FocusScope {
172 } else if (panel.highlightIndex >= 0) {
185 } else if (panel.highlightIndex >= 0) {
173 launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);
186 launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);
174 }
187 }
188+ root.hide();
175 panel.highlightIndex = -2
189 panel.highlightIndex = -2
176− root.state = ""
177 event.accepted = true;
190 event.accepted = true;
178 root.focus = false;
191 root.focus = false;
179 }
192 }
@@ -212,6 +225,13 @@ FocusScope {
212 interval: 1
225 interval: 1
213 property string nextState: ""
226 property string nextState: ""
214 onTriggered: {
227 onTriggered: {
228+ if (root.lockedVisible && nextState == "") {
229+ // Due to binding updates when switching between modes
230+ // it could happen that our request to show will be overwritten
231+ // with a hide request. Rewrite it when we know hiding is not allowed.
232+ nextState = "visible"
233+ }
234+
215 // switching to an intermediate state here to make sure all the
235 // switching to an intermediate state here to make sure all the
216 // values are restored, even if we were already in the target state
236 // values are restored, even if we were already in the target state
217 root.state = "tmp"
237 root.state = "tmp"
@@ -257,7 +277,7 @@ FocusScope {
257
277
258 MouseArea {
278 MouseArea {
259 id: launcherDragArea
279 id: launcherDragArea
260− enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary")
280+ enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary") && !root.lockedVisible
261 anchors.fill: panel
281 anchors.fill: panel
262 anchors.rightMargin: -units.gu(2)
282 anchors.rightMargin: -units.gu(2)
263 drag {
283 drag {
@@ -278,9 +298,10 @@ FocusScope {
278 InverseMouseArea {
298 InverseMouseArea {
279 id: closeMouseArea
299 id: closeMouseArea
280 anchors.fill: panel
300 anchors.fill: panel
281− enabled: root.shadeBackground && root.state == "visible"
301+ enabled: root.shadeBackground && root.state == "visible" && (!root.lockedVisible || panel.highlightIndex >= -1)
282 visible: enabled
302 visible: enabled
283 onPressed: {
303 onPressed: {
304+ panel.highlightIndex = -2
284 root.hide();
305 root.hide();
285 }
306 }
286 }
307 }
@@ -289,7 +310,7 @@ FocusScope {
289 id: backgroundShade
310 id: backgroundShade
290 anchors.fill: parent
311 anchors.fill: parent
291 color: "black"
312 color: "black"
292− opacity: root.shadeBackground && root.state == "visible" ? 0.6 : 0
313+ opacity: root.shadeBackground && root.state == "visible" && !root.lockedVisible ? 0.6 : 0
293
314
294 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
315 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
295 }
316 }
@@ -333,7 +354,7 @@ FocusScope {
333 Connections {
354 Connections {
334 target: panel.dismissTimer
355 target: panel.dismissTimer
335 onTriggered: {
356 onTriggered: {
336− if (root.autohideEnabled) {
357+ if (root.autohideEnabled && !root.lockedVisible) {
337 if (!panel.preventHiding) {
358 if (!panel.preventHiding) {
338 root.state = ""
359 root.state = ""
339 } else {
360 } else {
@@ -346,11 +367,11 @@ FocusScope {
346 property bool animate: true
367 property bool animate: true
347
368
348 onApplicationSelected: {
369 onApplicationSelected: {
349− root.state = ""
370+ root.hide();
350 launcherApplicationSelected(appId)
371 launcherApplicationSelected(appId)
351 }
372 }
352 onShowDashHome: {
373 onShowDashHome: {
353− root.state = ""
374+ root.hide();
354 root.showDashHome();
375 root.showDashHome();
355 }
376 }
356
377
@@ -361,7 +382,8 @@ FocusScope {
361 }
382 }
362
383
363 onKbdNavigationCancelled: {
384 onKbdNavigationCancelled: {
385+ panel.highlightIndex = -2;
364− root.state = "";
386+ root.hide();
365 root.focus = false;
387 root.focus = false;
366 }
388 }
367
389
qml/Launcher/LauncherDelegate.qml+8−12
@@ -124,7 +124,7 @@ Item {
124
124
125 Item {
125 Item {
126 id: iconItem
126 id: iconItem
127− width: parent.itemWidth + units.gu(1)
127+ width: root.width
128 height: parent.itemHeight + units.gu(1)
128 height: parent.itemHeight + units.gu(1)
129 anchors.centerIn: parent
129 anchors.centerIn: parent
130
130
@@ -142,7 +142,7 @@ Item {
142 ProportionalShape {
142 ProportionalShape {
143 id: iconShape
143 id: iconShape
144 anchors.centerIn: parent
144 anchors.centerIn: parent
145− width: parent.width - units.gu(2)
145+ width: root.itemWidth
146 aspect: UbuntuShape.DropShadow
146 aspect: UbuntuShape.DropShadow
147 source: Image {
147 source: Image {
148 id: iconImage
148 id: iconImage
@@ -158,7 +158,8 @@ Item {
158 anchors {
158 anchors {
159 right: parent.right
159 right: parent.right
160 bottom: parent.bottom
160 bottom: parent.bottom
161+ rightMargin: (iconItem.width - root.itemWidth) / 2 - units.dp(2)
161− margins: units.dp(3)
162+ margins: units.dp(5)
162 }
163 }
163 width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
164 width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
164 height: units.gu(2)
165 height: units.gu(2)
@@ -186,16 +187,11 @@ Item {
186 id: progressOverlay
187 id: progressOverlay
187 objectName: "progressOverlay"
188 objectName: "progressOverlay"
188
189
189− anchors {
190+ anchors.centerIn: parent
190− left: iconItem.left
191+ width: root.itemWidth * .8
191− right: iconItem.right
192− verticalCenter: parent.verticalCenter
193− leftMargin: units.gu(1.5)
194− rightMargin: units.gu(1.5)
195− }
196 height: units.gu(1)
192 height: units.gu(1)
197 visible: root.progress > -1
193 visible: root.progress > -1
198− color: UbuntuColors.darkGrey
194+ backgroundColor: UbuntuColors.darkGrey
199 borderSource: "none"
195 borderSource: "none"
200
196
201 Item {
197 Item {
@@ -213,7 +209,7 @@ Item {
213 top: parent.top
209 top: parent.top
214 bottom: parent.bottom
210 bottom: parent.bottom
215 }
211 }
216− color: "white"
212+ backgroundColor: "white"
217 borderSource: "none"
213 borderSource: "none"
218 width: progressOverlay.width
214 width: progressOverlay.width
219 }
215 }
qml/Launcher/LauncherPanel.qml+34−34
@@ -53,14 +53,14 @@ Rectangle {
53 if (highlightIndex >= launcherListView.count) {
53 if (highlightIndex >= launcherListView.count) {
54 highlightIndex = -1;
54 highlightIndex = -1;
55 }
55 }
56− moveAnimation.moveToIndex(Math.max(highlightIndex, 0));
56+ launcherListView.moveToIndex(Math.max(highlightIndex, 0));
57 }
57 }
58 function highlightPrevious() {
58 function highlightPrevious() {
59 highlightIndex--;
59 highlightIndex--;
60 if (highlightIndex <= -2) {
60 if (highlightIndex <= -2) {
61 highlightIndex = launcherListView.count - 1;
61 highlightIndex = launcherListView.count - 1;
62 }
62 }
63− moveAnimation.moveToIndex(Math.max(highlightIndex, 0));
63+ launcherListView.moveToIndex(Math.max(highlightIndex, 0));
64 }
64 }
65 function openQuicklist(index) {
65 function openQuicklist(index) {
66 quickList.open(index);
66 quickList.open(index);
@@ -83,13 +83,13 @@ Rectangle {
83 Rectangle {
83 Rectangle {
84 objectName: "buttonShowDashHome"
84 objectName: "buttonShowDashHome"
85 width: parent.width
85 width: parent.width
86− height: units.gu(7)
86+ height: width * .9
87 color: UbuntuColors.orange
87 color: UbuntuColors.orange
88 readonly property bool highlighted: root.highlightIndex == -1;
88 readonly property bool highlighted: root.highlightIndex == -1;
89
89
90 Image {
90 Image {
91 objectName: "dashItem"
91 objectName: "dashItem"
92− width: units.gu(5)
92+ width: parent.width * .6
93 height: width
93 height: width
94 anchors.centerIn: parent
94 anchors.centerIn: parent
95 source: "graphics/home.png"
95 source: "graphics/home.png"
@@ -125,10 +125,8 @@ Rectangle {
125 objectName: "launcherListView"
125 objectName: "launcherListView"
126 anchors {
126 anchors {
127 fill: parent
127 fill: parent
128− topMargin: -extensionSize + units.gu(0.5)
128+ topMargin: -extensionSize + width * .15
129− bottomMargin: -extensionSize + units.gu(1)
129+ bottomMargin: -extensionSize + width * .15
130− leftMargin: units.gu(0.5)
131− rightMargin: units.gu(0.5)
132 }
130 }
133 topMargin: extensionSize
131 topMargin: extensionSize
134 bottomMargin: extensionSize
132 bottomMargin: extensionSize
@@ -163,11 +161,11 @@ Rectangle {
163 }
161 }
164
162
165 // The height of the area where icons start getting folded
163 // The height of the area where icons start getting folded
166− property int foldingStartHeight: units.gu(6.5)
164+ property int foldingStartHeight: itemHeight
167 // The height of the area where the items reach the final folding angle
165 // The height of the area where the items reach the final folding angle
168 property int foldingStopHeight: foldingStartHeight - itemHeight - spacing
166 property int foldingStopHeight: foldingStartHeight - itemHeight - spacing
169− property int itemWidth: units.gu(7)
167+ property int itemWidth: width * .75
170− property int itemHeight: units.gu(6.5)
168+ property int itemHeight: itemWidth * 15 / 16 + units.gu(1)
171 property int clickFlickSpeed: units.gu(60)
169 property int clickFlickSpeed: units.gu(60)
172 property int draggedIndex: dndArea.draggedIndex
170 property int draggedIndex: dndArea.draggedIndex
173 property real realContentY: contentY - originY + topMargin
171 property real realContentY: contentY - originY + topMargin
@@ -195,22 +193,24 @@ Rectangle {
195
193
196 UbuntuNumberAnimation {
194 UbuntuNumberAnimation {
197 id: moveAnimation
195 id: moveAnimation
196+ objectName: "moveAnimation"
198 target: launcherListView
197 target: launcherListView
199 property: "contentY"
198 property: "contentY"
200 function moveTo(contentY) {
199 function moveTo(contentY) {
201 from = launcherListView.contentY;
200 from = launcherListView.contentY;
202 to = contentY;
201 to = contentY;
203− start();
202+ restart();
204 }
203 }
204+ }
205− function moveToIndex(index) {
205+ function moveToIndex(index) {
206+ var totalItemHeight = launcherListView.itemHeight + launcherListView.spacing
206− var itemPosition = index * launcherListView.itemHeight;
207+ var itemPosition = index * totalItemHeight;
207− var height = launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin
208+ var height = launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin
208− var distanceToEnd = index == 0 || index == launcherListView.count - 1 ? 0 : launcherListView.itemHeight
209+ var distanceToEnd = index == 0 || index == launcherListView.count - 1 ? 0 : totalItemHeight
209− if (itemPosition + launcherListView.itemHeight + distanceToEnd > launcherListView.contentY + launcherListView.topMargin + height) {
210+ if (itemPosition + totalItemHeight + distanceToEnd > launcherListView.contentY + launcherListView.originY + launcherListView.topMargin + height) {
210− moveAnimation.moveTo(itemPosition + launcherListView.itemHeight - launcherListView.topMargin - height + distanceToEnd);
211+ moveAnimation.moveTo(itemPosition + launcherListView.itemHeight - launcherListView.topMargin - height + distanceToEnd - launcherListView.originY);
211− } else if (itemPosition - distanceToEnd < launcherListView.contentY + launcherListView.topMargin) {
212+ } else if (itemPosition - distanceToEnd < launcherListView.contentY - launcherListView.originY + launcherListView.topMargin) {
212− moveAnimation.moveTo(itemPosition - distanceToEnd - launcherListView.topMargin);
213+ moveAnimation.moveTo(itemPosition - distanceToEnd - launcherListView.topMargin + launcherListView.originY);
213− }
214 }
214 }
215 }
215 }
216
216
@@ -228,7 +228,7 @@ Rectangle {
228 itemIndex: index
228 itemIndex: index
229 itemHeight: launcherListView.itemHeight
229 itemHeight: launcherListView.itemHeight
230 itemWidth: launcherListView.itemWidth
230 itemWidth: launcherListView.itemWidth
231− width: itemWidth
231+ width: parent.width
232 height: itemHeight
232 height: itemHeight
233 iconName: model.icon
233 iconName: model.icon
234 count: model.count
234 count: model.count
@@ -277,7 +277,7 @@ Rectangle {
277 onAlertingChanged: {
277 onAlertingChanged: {
278 if(alerting) {
278 if(alerting) {
279 if (!dragging && (launcherListView.peekingIndex === -1 || launcher.visibleWidth > 0)) {
279 if (!dragging && (launcherListView.peekingIndex === -1 || launcher.visibleWidth > 0)) {
280− moveAnimation.moveToIndex(index)
280+ launcherListView.moveToIndex(index)
281 if (!dragging && launcher.state !== "visible") {
281 if (!dragging && launcher.state !== "visible") {
282 peekingAnimation.start()
282 peekingAnimation.start()
283 }
283 }
@@ -439,10 +439,8 @@ Rectangle {
439
439
440 // First/last item do the scrolling at more than 12 degrees
440 // First/last item do the scrolling at more than 12 degrees
441 if (index == 0 || index == launcherListView.count - 1) {
441 if (index == 0 || index == launcherListView.count - 1) {
442− if (clickedItem.angle > 12) {
442+ if (clickedItem.angle > 12 || clickedItem.angle < -12) {
443− launcherListView.flick(0, -launcherListView.clickFlickSpeed);
443+ launcherListView.moveToIndex(index);
444− } else if (clickedItem.angle < -12) {
445− launcherListView.flick(0, launcherListView.clickFlickSpeed);
446 } else {
444 } else {
447 root.applicationSelected(LauncherModel.get(index).appId);
445 root.applicationSelected(LauncherModel.get(index).appId);
448 }
446 }
@@ -450,10 +448,8 @@ Rectangle {
450 }
448 }
451
449
452 // the rest launches apps up to an angle of 30 degrees
450 // the rest launches apps up to an angle of 30 degrees
453− if (clickedItem.angle > 30) {
451+ if (clickedItem.angle > 30 || clickedItem.angle < -30) {
454− launcherListView.flick(0, -launcherListView.clickFlickSpeed);
452+ launcherListView.moveToIndex(index);
455− } else if (clickedItem.angle < -30) {
456− launcherListView.flick(0, launcherListView.clickFlickSpeed);
457 } else {
453 } else {
458 root.applicationSelected(LauncherModel.get(index).appId);
454 root.applicationSelected(LauncherModel.get(index).appId);
459 }
455 }
@@ -668,7 +664,6 @@ Rectangle {
668 onClicked: {
664 onClicked: {
669 quickList.state = "";
665 quickList.state = "";
670 quickList.focus = false;
666 quickList.focus = false;
671− root.highlightIndex = -2;
672 root.kbdNavigationCancelled();
667 root.kbdNavigationCancelled();
673 }
668 }
674
669
@@ -739,16 +734,21 @@ Rectangle {
739 case Qt.Key_Left:
734 case Qt.Key_Left:
740 case Qt.Key_Escape:
735 case Qt.Key_Escape:
741 quickList.selectedIndex = -1;
736 quickList.selectedIndex = -1;
742− // Falling through intentionally
737+ quickList.focus = false;
738+ quickList.state = ""
739+ event.accepted = true;
740+ break;
743 case Qt.Key_Enter:
741 case Qt.Key_Enter:
744 case Qt.Key_Return:
742 case Qt.Key_Return:
745 case Qt.Key_Space:
743 case Qt.Key_Space:
746 if (quickList.selectedIndex >= 0) {
744 if (quickList.selectedIndex >= 0) {
747 LauncherModel.quickListActionInvoked(quickList.appId, quickList.selectedIndex)
745 LauncherModel.quickListActionInvoked(quickList.appId, quickList.selectedIndex)
748 }
746 }
747+ quickList.selectedIndex = -1;
749 quickList.focus = false;
748 quickList.focus = false;
750 quickList.state = ""
749 quickList.state = ""
751− // Don't consume the event. We want to close the Launcher too, not just the quicklist.
750+ root.kbdNavigationCancelled();
751+ event.accepted = true;
752 break;
752 break;
753 }
753 }
754 }
754 }
qml/Shell.qml+18−1
@@ -25,6 +25,7 @@ import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Connectivity 0.1
25import Unity.Connectivity 0.1
26import Unity.Launcher 0.1
26import Unity.Launcher 0.1
27import GlobalShortcut 1.0 // has to be before Utils, because of WindowKeysFilter
27import GlobalShortcut 1.0 // has to be before Utils, because of WindowKeysFilter
28+import GSettings 1.0
28import Utils 0.1
29import Utils 0.1
29import Powerd 0.1
30import Powerd 0.1
30import SessionBroadcast 0.1
31import SessionBroadcast 0.1
@@ -187,6 +188,11 @@ Item {
187 }
188 }
188 }
189 }
189
190
191+ GSettings {
192+ id: settings
193+ schema.id: "com.canonical.Unity8"
194+ }
195+
190 Item {
196 Item {
191 id: stages
197 id: stages
192 objectName: "stages"
198 objectName: "stages"
@@ -343,6 +349,11 @@ Item {
343 property: "altTabPressed"
349 property: "altTabPressed"
344 value: physicalKeysMapper.altTabPressed
350 value: physicalKeysMapper.altTabPressed
345 }
351 }
352+ Binding {
353+ target: applicationsDisplayLoader.item
354+ property: "leftMargin"
355+ value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
356+ }
346 }
357 }
347
358
348 Tutorial {
359 Tutorial {
@@ -373,7 +384,11 @@ Item {
373 InputMethod {
384 InputMethod {
374 id: inputMethod
385 id: inputMethod
375 objectName: "inputMethod"
386 objectName: "inputMethod"
387+ anchors {
388+ fill: parent
376− anchors { fill: parent; topMargin: panel.panelHeight }
389+ topMargin: panel.panelHeight
390+ leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
391+ }
377 z: notifications.useModal || panel.indicators.shown || wizard.active ? overlay.z + 1 : overlay.z - 1
392 z: notifications.useModal || panel.indicators.shown || wizard.active ? overlay.z + 1 : overlay.z - 1
378 }
393 }
379
394
@@ -559,6 +574,8 @@ Item {
559 shadeBackground: !tutorial.running
574 shadeBackground: !tutorial.running
560 superPressed: physicalKeysMapper.superPressed
575 superPressed: physicalKeysMapper.superPressed
561 superTabPressed: physicalKeysMapper.superTabPressed
576 superTabPressed: physicalKeysMapper.superTabPressed
577+ panelWidth: units.gu(settings.launcherWidth)
578+ lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
562
579
563 onShowDashHome: showHome()
580 onShowDashHome: showHome()
564 onDash: showDash()
581 onDash: showDash()
qml/Stages/AbstractStage.qml+2
@@ -39,6 +39,8 @@ Rectangle {
39 property int shellOrientationAngle
39 property int shellOrientationAngle
40 property bool spreadEnabled: true // If false, animations and right edge will be disabled
40 property bool spreadEnabled: true // If false, animations and right edge will be disabled
41 property bool suspended
41 property bool suspended
42+ // A Stage should paint a wallpaper etc over its full size but not use the margins for window placement
43+ property int leftMargin: 0
42
44
43 // To be read from outside
45 // To be read from outside
44 property var mainApp: null
46 property var mainApp: null
qml/Stages/DesktopStage.qml+15−12
@@ -224,6 +224,7 @@ AbstractStage {
224 PanelState.dropShadow = false;
224 PanelState.dropShadow = false;
225 }
225 }
226
226
227+
227 FocusScope {
228 FocusScope {
228 id: appContainer
229 id: appContainer
229 objectName: "appContainer"
230 objectName: "appContainer"
@@ -293,6 +294,7 @@ AbstractStage {
293 visible: !visuallyMinimized &&
294 visible: !visuallyMinimized &&
294 !greeter.fullyShown &&
295 !greeter.fullyShown &&
295 (priv.foregroundMaximizedAppZ === -1 || priv.foregroundMaximizedAppZ <= z) ||
296 (priv.foregroundMaximizedAppZ === -1 || priv.foregroundMaximizedAppZ <= z) ||
297+ decoratedWindow.fullscreen ||
296 (spread.state == "altTab" && index === spread.highlightedIndex)
298 (spread.state == "altTab" && index === spread.highlightedIndex)
297
299
298 Binding {
300 Binding {
@@ -351,10 +353,10 @@ AbstractStage {
351 states: [
353 states: [
352 State {
354 State {
353 name: "fullscreen"; when: decoratedWindow.fullscreen
355 name: "fullscreen"; when: decoratedWindow.fullscreen
354− extend: "maximized"
355 PropertyChanges {
356 PropertyChanges {
356 target: appDelegate;
357 target: appDelegate;
357− y: -PanelState.panelHeight
358+ x: 0; y: -PanelState.panelHeight
359+ requestedWidth: appContainer.width; requestedHeight: appContainer.height;
358 }
360 }
359 },
361 },
360 State {
362 State {
@@ -371,21 +373,21 @@ AbstractStage {
371 name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
373 name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
372 PropertyChanges {
374 PropertyChanges {
373 target: appDelegate;
375 target: appDelegate;
374− x: 0; y: 0;
376+ x: root.leftMargin; y: 0;
375− requestedWidth: root.width; requestedHeight: root.height;
377+ requestedWidth: appContainer.width - root.leftMargin; requestedHeight: appContainer.height;
376 visuallyMinimized: false;
378 visuallyMinimized: false;
377 visuallyMaximized: true
379 visuallyMaximized: true
378 }
380 }
379 },
381 },
380 State {
382 State {
381 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
383 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
382− PropertyChanges { target: appDelegate; x: 0; y: PanelState.panelHeight;
384+ PropertyChanges { target: appDelegate; x: root.leftMargin; y: PanelState.panelHeight;
383− requestedWidth: root.width/2; requestedHeight: root.height - PanelState.panelHeight }
385+ requestedWidth: (appContainer.width - root.leftMargin)/2; requestedHeight: appContainer.height - PanelState.panelHeight }
384 },
386 },
385 State {
387 State {
386 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
388 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
387− PropertyChanges { target: appDelegate; x: root.width/2; y: PanelState.panelHeight;
389+ PropertyChanges { target: appDelegate; x: (appContainer.width + root.leftMargin)/2; y: PanelState.panelHeight;
388− requestedWidth: root.width/2; requestedHeight: root.height - PanelState.panelHeight }
390+ requestedWidth: (appContainer.width - root.leftMargin)/2; requestedHeight: appContainer.height - PanelState.panelHeight }
389 },
391 },
390 State {
392 State {
391 name: "minimized"; when: appDelegate.minimized
393 name: "minimized"; when: appDelegate.minimized
@@ -448,8 +450,9 @@ AbstractStage {
448 minHeight: units.gu(10)
450 minHeight: units.gu(10)
449 borderThickness: units.gu(2)
451 borderThickness: units.gu(2)
450 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
452 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
451− screenWidth: root.width
453+ screenWidth: appContainer.width
452− screenHeight: root.height
454+ screenHeight: appContainer.height
455+ leftMargin: root.leftMargin
453
456
454 onPressed: { ApplicationManager.focusApplication(model.appId) }
457 onPressed: { ApplicationManager.focusApplication(model.appId) }
455 }
458 }
@@ -475,7 +478,7 @@ AbstractStage {
475
478
476 BlurLayer {
479 BlurLayer {
477 id: blurLayer
480 id: blurLayer
478− anchors.fill: parent
481+ anchors.fill: appContainer
479 source: appContainer
482 source: appContainer
480 visible: false
483 visible: false
481 }
484 }
@@ -527,7 +530,7 @@ AbstractStage {
527 DesktopSpread {
530 DesktopSpread {
528 id: spread
531 id: spread
529 objectName: "spread"
532 objectName: "spread"
530− anchors.fill: parent
533+ anchors.fill: appContainer
531 workspace: appContainer
534 workspace: appContainer
532 focus: state == "altTab"
535 focus: state == "altTab"
533 altTabPressed: root.altTabPressed
536 altTabPressed: root.altTabPressed
qml/Stages/WindowResizeArea.qml+3−2
@@ -40,6 +40,7 @@ MouseArea {
40 property int defaultHeight: units.gu(50)
40 property int defaultHeight: units.gu(50)
41 property int screenWidth: 0
41 property int screenWidth: 0
42 property int screenHeight: 0
42 property int screenHeight: 0
43+ property int leftMargin: 0
43
44
44 QtObject {
45 QtObject {
45 id: priv
46 id: priv
@@ -72,9 +73,9 @@ MouseArea {
72 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
73 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
73 Qt.rect(target.x, target.y, defaultWidth, defaultHeight));
74 Qt.rect(target.x, target.y, defaultWidth, defaultHeight));
74
75
75− target.requestedWidth = Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth);
76+ target.requestedWidth = Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth - root.leftMargin);
76 target.requestedHeight = Math.min(Math.max(windowGeometry.height, d.minimumHeight), root.screenHeight - PanelState.panelHeight);
77 target.requestedHeight = Math.min(Math.max(windowGeometry.height, d.minimumHeight), root.screenHeight - PanelState.panelHeight);
77− target.x = Math.max(Math.min(windowGeometry.x, root.screenWidth - target.requestedWidth), 0)
78+ target.x = Math.max(Math.min(windowGeometry.x, root.screenWidth - root.leftMargin - target.requestedWidth), root.leftMargin)
78 target.y = Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight)
79 target.y = Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight)
79
80
80 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
81 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
tests/mocks/GSettings.1.0/fake_gsettings.cpp+64
@@ -22,6 +22,8 @@ GSettingsControllerQml* GSettingsControllerQml::s_controllerInstance = 0;
22
22
23GSettingsControllerQml::GSettingsControllerQml()
23GSettingsControllerQml::GSettingsControllerQml()
24 : m_usageMode("Staged")
24 : m_usageMode("Staged")
25+ , m_autohideLauncher(false)
26+ , m_launcherWidth(8)
25{
27{
26}
28}
27
29
@@ -88,6 +90,32 @@ void GSettingsControllerQml::setLifecycleExemptAppids(const QStringList &appIds)
88 }
90 }
89}
91}
90
92
93+bool GSettingsControllerQml::autohideLauncher() const
94+{
95+ return m_autohideLauncher;
96+}
97+
98+void GSettingsControllerQml::setAutohideLauncher(bool autohideLauncher)
99+{
100+ if (m_autohideLauncher != autohideLauncher) {
101+ m_autohideLauncher = autohideLauncher;
102+ Q_EMIT autohideLauncherChanged(autohideLauncher);
103+ }
104+}
105+
106+int GSettingsControllerQml::launcherWidth() const
107+{
108+ return m_launcherWidth;
109+}
110+
111+void GSettingsControllerQml::setLauncherWidth(int launcherWidth)
112+{
113+ if (m_launcherWidth != launcherWidth) {
114+ m_launcherWidth = launcherWidth;
115+ Q_EMIT launcherWidthChanged(launcherWidth);
116+ }
117+}
118+
91GSettingsSchemaQml::GSettingsSchemaQml(QObject *parent): QObject(parent) {
119GSettingsSchemaQml::GSettingsSchemaQml(QObject *parent): QObject(parent) {
92}
120}
93
121
@@ -129,6 +157,10 @@ GSettingsQml::GSettingsQml(QObject *parent)
129 this, &GSettingsQml::lockedOutTimeChanged);
157 this, &GSettingsQml::lockedOutTimeChanged);
130 connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::lifecycleExemptAppidsChanged,
158 connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::lifecycleExemptAppidsChanged,
131 this, &GSettingsQml::lifecycleExemptAppidsChanged);
159 this, &GSettingsQml::lifecycleExemptAppidsChanged);
160+ connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::autohideLauncherChanged,
161+ this, &GSettingsQml::autohideLauncherChanged);
162+ connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::launcherWidthChanged,
163+ this, &GSettingsQml::launcherWidthChanged);
132}
164}
133
165
134GSettingsSchemaQml * GSettingsQml::schema() const {
166GSettingsSchemaQml * GSettingsQml::schema() const {
@@ -192,9 +224,41 @@ QStringList GSettingsQml::lifecycleExemptAppids() const
192 }
224 }
193}
225}
194
226
227+bool GSettingsQml::autohideLauncher() const
228+{
229+ if (m_schema->id() == "com.canonical.Unity8") {
230+ return GSettingsControllerQml::instance()->autohideLauncher();
231+ } else {
232+ return false;
233+ }
234+}
235+
236+int GSettingsQml::launcherWidth() const
237+{
238+ if (m_schema->id() == "com.canonical.Unity8") {
239+ return GSettingsControllerQml::instance()->launcherWidth();
240+ } else {
241+ return false;
242+ }
243+}
244+
195void GSettingsQml::setLifecycleExemptAppids(const QStringList &appIds)
245void GSettingsQml::setLifecycleExemptAppids(const QStringList &appIds)
196{
246{
197 if (m_schema->id() == "com.canonical.qtmir") {
247 if (m_schema->id() == "com.canonical.qtmir") {
198 GSettingsControllerQml::instance()->setLifecycleExemptAppids(appIds);
248 GSettingsControllerQml::instance()->setLifecycleExemptAppids(appIds);
199 }
249 }
200}
250}
251+
252+void GSettingsQml::setAutohideLauncher(bool autohideLauncher)
253+{
254+ if (m_schema->id() == "com.canonical.Unity8") {
255+ GSettingsControllerQml::instance()->setAutohideLauncher(autohideLauncher);
256+ }
257+}
258+
259+void GSettingsQml::setLauncherWidth(int launcherWidth)
260+{
261+ if (m_schema->id() == "com.canonical.Unity8") {
262+ GSettingsControllerQml::instance()->setLauncherWidth(launcherWidth);
263+ }
264+}
tests/mocks/GSettings.1.0/fake_gsettings.h+18
@@ -50,6 +50,8 @@ class GSettingsQml: public QObject
50 Q_PROPERTY(QString usageMode READ usageMode WRITE setUsageMode NOTIFY usageModeChanged)
50 Q_PROPERTY(QString usageMode READ usageMode WRITE setUsageMode NOTIFY usageModeChanged)
51 Q_PROPERTY(qint64 lockedOutTime READ lockedOutTime WRITE setLockedOutTime NOTIFY lockedOutTimeChanged)
51 Q_PROPERTY(qint64 lockedOutTime READ lockedOutTime WRITE setLockedOutTime NOTIFY lockedOutTimeChanged)
52 Q_PROPERTY(QStringList lifecycleExemptAppids READ lifecycleExemptAppids WRITE setLifecycleExemptAppids NOTIFY lifecycleExemptAppidsChanged)
52 Q_PROPERTY(QStringList lifecycleExemptAppids READ lifecycleExemptAppids WRITE setLifecycleExemptAppids NOTIFY lifecycleExemptAppidsChanged)
53+ Q_PROPERTY(bool autohideLauncher READ autohideLauncher WRITE setAutohideLauncher NOTIFY autohideLauncherChanged)
54+ Q_PROPERTY(int launcherWidth READ launcherWidth WRITE setLauncherWidth NOTIFY launcherWidthChanged)
53
55
54public:
56public:
55 GSettingsQml(QObject *parent = nullptr);
57 GSettingsQml(QObject *parent = nullptr);
@@ -59,11 +61,15 @@ public:
59 QString usageMode() const;
61 QString usageMode() const;
60 qint64 lockedOutTime() const;
62 qint64 lockedOutTime() const;
61 QStringList lifecycleExemptAppids() const;
63 QStringList lifecycleExemptAppids() const;
64+ bool autohideLauncher() const;
65+ int launcherWidth() const;
62
66
63 void setPictureUri(const QString &str);
67 void setPictureUri(const QString &str);
64 void setUsageMode(const QString &usageMode);
68 void setUsageMode(const QString &usageMode);
65 void setLockedOutTime(qint64 timestamp);
69 void setLockedOutTime(qint64 timestamp);
66 void setLifecycleExemptAppids(const QStringList &appIds);
70 void setLifecycleExemptAppids(const QStringList &appIds);
71+ void setAutohideLauncher(bool autohideLauncher);
72+ void setLauncherWidth(int launcherWidth);
67
73
68Q_SIGNALS:
74Q_SIGNALS:
69 void schemaChanged();
75 void schemaChanged();
@@ -71,6 +77,8 @@ Q_SIGNALS:
71 void usageModeChanged(const QString&);
77 void usageModeChanged(const QString&);
72 void lockedOutTimeChanged(qint64);
78 void lockedOutTimeChanged(qint64);
73 void lifecycleExemptAppidsChanged(const QStringList &);
79 void lifecycleExemptAppidsChanged(const QStringList &);
80+ void autohideLauncherChanged(bool);
81+ void launcherWidthChanged(int launcherWidth);
74
82
75private:
83private:
76 GSettingsSchemaQml* m_schema;
84 GSettingsSchemaQml* m_schema;
@@ -98,11 +106,19 @@ public:
98 QStringList lifecycleExemptAppids() const;
106 QStringList lifecycleExemptAppids() const;
99 Q_INVOKABLE void setLifecycleExemptAppids(const QStringList &appIds);
107 Q_INVOKABLE void setLifecycleExemptAppids(const QStringList &appIds);
100
108
109+ bool autohideLauncher() const;
110+ Q_INVOKABLE void setAutohideLauncher(bool autohideLauncher);
111+
112+ int launcherWidth() const;
113+ Q_INVOKABLE void setLauncherWidth(int launcherWidth);
114+
101Q_SIGNALS:
115Q_SIGNALS:
102 void pictureUriChanged(const QString&);
116 void pictureUriChanged(const QString&);
103 void usageModeChanged(const QString&);
117 void usageModeChanged(const QString&);
104 void lockedOutTimeChanged(qint64 timestamp);
118 void lockedOutTimeChanged(qint64 timestamp);
105 void lifecycleExemptAppidsChanged(const QStringList&);
119 void lifecycleExemptAppidsChanged(const QStringList&);
120+ void autohideLauncherChanged(bool autohideLauncher);
121+ void launcherWidthChanged(int launcherWidth);
106
122
107private:
123private:
108 GSettingsControllerQml();
124 GSettingsControllerQml();
@@ -111,6 +127,8 @@ private:
111 QString m_usageMode;
127 QString m_usageMode;
112 qint64 m_lockedOutTime;
128 qint64 m_lockedOutTime;
113 QStringList m_lifecycleExemptAppids;
129 QStringList m_lifecycleExemptAppids;
130+ bool m_autohideLauncher;
131+ int m_launcherWidth;
114
132
115 static GSettingsControllerQml* s_controllerInstance;
133 static GSettingsControllerQml* s_controllerInstance;
116 QList<GSettingsQml *> m_registeredGSettings;
134 QList<GSettingsQml *> m_registeredGSettings;
tests/qmltests/Launcher/tst_Launcher.qml+110−25
@@ -28,8 +28,8 @@ import Utils 0.1 // For EdgeBarrierSettings
28 launcher. */
28 launcher. */
29Item {
29Item {
30 id: root
30 id: root
31− width: units.gu(50)
31+ width: units.gu(140)
32− height: units.gu(55)
32+ height: units.gu(70)
33
33
34 Loader {
34 Loader {
35 id: launcherLoader
35 id: launcherLoader
@@ -79,11 +79,40 @@ Item {
79 }
79 }
80 }
80 }
81
81
82+ Binding {
83+ target: launcherLoader.item
84+ property: "lockedVisible"
85+ value: lockedVisibleCheckBox.checked
86+ }
87+ Binding {
88+ target: launcherLoader.item
89+ property: "panelWidth"
90+ value: units.gu(Math.round(widthSlider.value))
91+ }
92+
82 ColumnLayout {
93 ColumnLayout {
83 anchors { bottom: parent.bottom; right: parent.right; margins: units.gu(1) }
94 anchors { bottom: parent.bottom; right: parent.right; margins: units.gu(1) }
84 spacing: units.gu(1)
95 spacing: units.gu(1)
85 width: childrenRect.width
96 width: childrenRect.width
86
97
98+ RowLayout {
99+ CheckBox {
100+ id: lockedVisibleCheckBox
101+ checked: false
102+ }
103+ Label {
104+ text: "Launcher always visible"
105+ }
106+ }
107+
108+ Slider {
109+ id: widthSlider
110+ Layout.fillWidth: true
111+ minimumValue: 6
112+ maximumValue: 12
113+ value: 10
114+ }
115+
87 MouseTouchEmulationCheckbox {}
116 MouseTouchEmulationCheckbox {}
88
117
89 EdgeBarrierControls {
118 EdgeBarrierControls {
@@ -217,10 +246,6 @@ Item {
217 // growing while populating it with icons etc.
246 // growing while populating it with icons etc.
218 tryCompare(listView, "flicking", false);
247 tryCompare(listView, "flicking", false);
219
248
220− // Make sure noone changed the height of the window. The issue this test case
221− // is verifying only happens on certain heights of the Launcher
222− compare(root.height, units.gu(55));
223−
224 compare(listView.contentY, -listView.topMargin, "Launcher did not start up with first item unfolded");
249 compare(listView.contentY, -listView.topMargin, "Launcher did not start up with first item unfolded");
225
250
226 // Now do check that snapping is in fact enabled
251 // Now do check that snapping is in fact enabled
@@ -277,23 +302,31 @@ Item {
277
302
278 function positionLauncherListAtBeginning() {
303 function positionLauncherListAtBeginning() {
279 var listView = testCase.findChild(launcherLoader.item, "launcherListView");
304 var listView = testCase.findChild(launcherLoader.item, "launcherListView");
280− listView.contentY = -listView.topMargin;
305+ var moveAnimation = findInvisibleChild(listView, "moveAnimation")
306+
307+ listView.moveToIndex(0);
308+
309+ waitForRendering(listView);
310+ tryCompare(moveAnimation, "running", false);
281 }
311 }
282 function positionLauncherListAtEnd() {
312 function positionLauncherListAtEnd() {
283 var listView = testCase.findChild(launcherLoader.item, "launcherListView");
313 var listView = testCase.findChild(launcherLoader.item, "launcherListView");
284− if ((listView.contentHeight + listView.topMargin + listView.bottomMargin) > listView.height) {
314+ var moveAnimation = findInvisibleChild(listView, "moveAnimation")
285− listView.contentY = listView.topMargin + listView.contentHeight
315+
316+ listView.moveToIndex(listView.count -1);
317+
286− - listView.height;
318+ waitForRendering(listView);
287− }
319+ tryCompare(moveAnimation, "running", false);
288 }
320 }
289
321
290 function assertFocusOnIndex(index) {
322 function assertFocusOnIndex(index) {
291− var launcherPanel = findChild(launcherLoader.item, "launcherPanel");
323+ var launcherPanel = findChild(launcher, "launcherPanel");
292− var launcherListView = findChild(launcherLoader.item, "launcherListView");
324+ var launcherListView = findChild(launcher, "launcherListView");
293− var bfbFocusHighlight = findChild(launcherLoader.item, "bfbFocusHighlight");
325+ var bfbFocusHighlight = findChild(launcher, "bfbFocusHighlight");
326+
294− waitForRendering(launcherLoader.item);
327+ waitForRendering(launcher);
295 tryCompare(launcherPanel, "highlightIndex", index);
328 tryCompare(launcherPanel, "highlightIndex", index);
296− tryCompare(bfbFocusHighlight, "visible", index === -1);
329+ compare(bfbFocusHighlight.visible, index === -1);
297 for (var i = 0; i < launcherListView.count; i++) {
330 for (var i = 0; i < launcherListView.count; i++) {
298 var item = findChild(launcher, "launcherDelegate" + i);
331 var item = findChild(launcher, "launcherDelegate" + i);
299 // Delegates might be destroyed when not visible. We can't check if they paint a focus highlight.
332 // Delegates might be destroyed when not visible. We can't check if they paint a focus highlight.
@@ -406,6 +439,7 @@ Item {
406 wait(100)
439 wait(100)
407 compare(launcher.maxPanelX, -launcher.panelWidth, "Launcher moved even if it shouldn't")
440 compare(launcher.maxPanelX, -launcher.panelWidth, "Launcher moved even if it shouldn't")
408 }
441 }
442+
409 waitUntilLauncherDisappears();
443 waitUntilLauncherDisappears();
410 launcher.available = true;
444 launcher.available = true;
411 }
445 }
@@ -429,6 +463,8 @@ Item {
429 dragLauncherIntoView();
463 dragLauncherIntoView();
430 var launcherListView = findChild(launcher, "launcherListView");
464 var launcherListView = findChild(launcher, "launcherListView");
431 for (var i = 0; i < launcherListView.count; ++i) {
465 for (var i = 0; i < launcherListView.count; ++i) {
466+ launcherListView.moveToIndex(i);
467+ waitForRendering(launcherListView);
432 var delegate = findChild(launcherListView, "launcherDelegate" + i)
468 var delegate = findChild(launcherListView, "launcherDelegate" + i)
433 compare(findChild(delegate, "countEmblem").visible, LauncherModel.get(i).countVisible)
469 compare(findChild(delegate, "countEmblem").visible, LauncherModel.get(i).countVisible)
434 // Intentionally allow type coercion (string/number)
470 // Intentionally allow type coercion (string/number)
@@ -489,6 +525,7 @@ Item {
489 launcher.lastSelectedApplication = "";
525 launcher.lastSelectedApplication = "";
490 dragLauncherIntoView();
526 dragLauncherIntoView();
491 var listView = findChild(launcher, "launcherListView");
527 var listView = findChild(launcher, "launcherListView");
528+ var moveAnimation = findInvisibleChild(listView, "moveAnimation")
492
529
493 // flicking is unreliable. sometimes it works, sometimes the
530 // flicking is unreliable. sometimes it works, sometimes the
494 // list view moves just a tiny bit or not at all, making tests fail.
531 // list view moves just a tiny bit or not at all, making tests fail.
@@ -499,12 +536,14 @@ Item {
499 } else {
536 } else {
500 positionLauncherListAtEnd();
537 positionLauncherListAtEnd();
501 }
538 }
502− tryCompare(listView, "flicking", false);
503−
504 var oldY = listView.contentY;
539 var oldY = listView.contentY;
505
540
506 mouseClick(listView, listView.width / 2, data.clickY);
541 mouseClick(listView, listView.width / 2, data.clickY);
542+
543+ if (data.expectFlick) {
544+ tryCompare(moveAnimation, "running", true);
545+ }
507− tryCompare(listView, "flicking", false);
546+ tryCompare(moveAnimation, "running", false);
508
547
509 if (data.expectFlick) {
548 if (data.expectFlick) {
510 verify(listView.contentY != oldY);
549 verify(listView.contentY != oldY);
@@ -1092,11 +1131,14 @@ Item {
1092 function test_keyboardNavigation() {
1131 function test_keyboardNavigation() {
1093 var bfbFocusHighlight = findChild(launcher, "bfbFocusHighlight");
1132 var bfbFocusHighlight = findChild(launcher, "bfbFocusHighlight");
1094 var quickList = findChild(launcher, "quickList");
1133 var quickList = findChild(launcher, "quickList");
1134+ var launcherPanel = findChild(launcher, "launcherPanel");
1095 var launcherListView = findChild(launcher, "launcherListView");
1135 var launcherListView = findChild(launcher, "launcherListView");
1096 var last = launcherListView.count - 1;
1136 var last = launcherListView.count - 1;
1097
1137
1098 compare(bfbFocusHighlight.visible, false);
1138 compare(bfbFocusHighlight.visible, false);
1099 launcher.openForKeyboardNavigation();
1139 launcher.openForKeyboardNavigation();
1140+ tryCompare(launcherPanel, "x", 0);
1141+ waitForRendering(launcher);
1100
1142
1101 assertFocusOnIndex(-1);
1143 assertFocusOnIndex(-1);
1102
1144
@@ -1173,11 +1215,47 @@ Item {
1173 compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
1215 compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
1174 compare(signalSpy.signalArguments[0][0], LauncherModel.get(1).appId)
1216 compare(signalSpy.signalArguments[0][0], LauncherModel.get(1).appId)
1175 compare(signalSpy.signalArguments[0][1], 2)
1217 compare(signalSpy.signalArguments[0][1], 2)
1218+ assertFocusOnIndex(-2);
1219+ }
1220+
1221+ function test_hideNotWorkingWhenLockedOut_data() {
1222+ return [
1223+ {tag: "locked visible", locked: true},
1224+ {tag: "no locked visible", locked: false},
1225+ ]
1226+ }
1227+
1228+ function test_hideNotWorkingWhenLockedOut(data) {
1229+ launcher.lockedVisible = data.locked;
1230+ if (data.locked) {
1231+ tryCompare(launcher, "state", "visible");
1232+ } else {
1233+ tryCompare(launcher, "state", "");
1234+ }
1235+
1236+ launcher.hide();
1237+ waitForRendering(launcher);
1238+ if (data.locked) {
1239+ verify(launcher.state == "visible");
1240+ } else {
1241+ verify(launcher.state == "");
1242+ }
1176 }
1243 }
1177
1244
1245+ function test_cancelKbdNavigationWitMouse_data() {
1246+ return [
1247+ {tag: "locked out - no quicklist", autohide: false, withQuickList: false },
1248+ {tag: "locked out - with quicklist", autohide: false, withQuickList: true },
1249+ {tag: "autohide - no quicklist", autohide: true, withQuickList: false },
1250+ {tag: "autohide - with quicklist", autohide: true, withQuickList: true },
1251+ ]
1252+ }
1253+
1178− function test_cancelKbdNavigationWitMouse() {
1254+ function test_cancelKbdNavigationWitMouse(data) {
1255+ launcher.autohideEnabled = data.autohide;
1179 launcher.openForKeyboardNavigation();
1256 launcher.openForKeyboardNavigation();
1180 waitForRendering(launcher);
1257 waitForRendering(launcher);
1258+
1181 var launcherPanel = findChild(launcher, "launcherPanel");
1259 var launcherPanel = findChild(launcher, "launcherPanel");
1182 tryCompare(launcherPanel, "x", 0);
1260 tryCompare(launcherPanel, "x", 0);
1183
1261
@@ -1185,15 +1263,22 @@ Item {
1185
1263
1186 keyClick(Qt.Key_Down); // Down to launcher item 0
1264 keyClick(Qt.Key_Down); // Down to launcher item 0
1187 keyClick(Qt.Key_Down); // Down to launcher item 1
1265 keyClick(Qt.Key_Down); // Down to launcher item 1
1188− keyClick(Qt.Key_Right); // Into quicklist
1189
1266
1267+ if (data.withQuickList) {
1268+ keyClick(Qt.Key_Right); // Into quicklist
1269+ tryCompare(quickList, "visible", true)
1270+ }
1190 waitForRendering(launcher)
1271 waitForRendering(launcher)
1191− tryCompare(quickList, "visible", true)
1192
1272
1193− mouseClick(root, root.width / 2, units.gu(2));
1273+ mouseClick(root);
1194
1274
1275+ if (data.autohide) {
1195− tryCompare(launcher, "state", "");
1276+ tryCompare(launcher, "state", "");
1196− tryCompare(launcherPanel, "highlightIndex", -2);
1277+ } else {
1278+ tryCompare(launcher, "state", "visible");
1279+ }
1280+
1281+ assertFocusOnIndex(-2);
1197 }
1282 }
1198 }
1283 }
1199}
1284}
tests/qmltests/tst_Shell.qml+92−13
@@ -223,6 +223,19 @@ Rectangle {
223 }
223 }
224 }
224 }
225
225
226+ Row {
227+ anchors { left: parent.left; right: parent.right }
228+ CheckBox {
229+ id: autohideLauncherCheckbox
230+ onCheckedChanged: {
231+ GSettingsController.setAutohideLauncher(checked)
232+ }
233+ }
234+ Label {
235+ text: "Autohide launcher"
236+ }
237+ }
238+
226 Label { text: "Applications"; font.bold: true }
239 Label { text: "Applications"; font.bold: true }
227
240
228 Button {
241 Button {
@@ -921,6 +934,7 @@ Rectangle {
921 function dragLauncherIntoView() {
934 function dragLauncherIntoView() {
922 var launcher = findChild(shell, "launcher");
935 var launcher = findChild(shell, "launcher");
923 var launcherPanel = findChild(launcher, "launcherPanel");
936 var launcherPanel = findChild(launcher, "launcherPanel");
937+ waitForRendering(launcher);
924 verify(launcherPanel.x = - launcherPanel.width);
938 verify(launcherPanel.x = - launcherPanel.width);
925
939
926 var touchStartX = 2;
940 var touchStartX = 2;
@@ -1678,7 +1692,7 @@ Rectangle {
1678 var x = 0;
1692 var x = 0;
1679 var y = shell.height * .5
1693 var y = shell.height * .5
1680 mouseMove(shell, x, y)
1694 mouseMove(shell, x, y)
1681− while (x <= spreadFlickable.width) {
1695+ while (x <= shell.width) {
1682 x+=10;
1696 x+=10;
1683 mouseMove(shell, x, y)
1697 mouseMove(shell, x, y)
1684 wait(0); // spin the loop so bindings get evaluated
1698 wait(0); // spin the loop so bindings get evaluated
@@ -1752,25 +1766,38 @@ Rectangle {
1752 keyRelease(Qt.Key_Alt);
1766 keyRelease(Qt.Key_Alt);
1753 }
1767 }
1754
1768
1755− function test_focusAppFromLauncherExitsSpread() {
1769+ function test_focusAppFromLauncherExitsSpread_data() {
1756− loadDesktopShellWithApps()
1770+ return [
1771+ {tag: "autohide launcher", launcherLocked: false },
1772+ {tag: "locked launcher", launcherLocked: true }
1773+ ]
1774+ }
1757
1775
1758− var desktopSpread = findChild(shell, "spread");
1776+ function test_focusAppFromLauncherExitsSpread(data) {
1777+ loadDesktopShellWithApps()
1759 var launcher = findChild(shell, "launcher");
1778 var launcher = findChild(shell, "launcher");
1779+ var desktopSpread = findChild(shell, "spread");
1760 var bfb = findChild(launcher, "buttonShowDashHome");
1780 var bfb = findChild(launcher, "buttonShowDashHome");
1761
1781
1782+ GSettingsController.setAutohideLauncher(!data.launcherLocked);
1783+ waitForRendering(shell);
1784+
1762 keyPress(Qt.Key_Alt)
1785 keyPress(Qt.Key_Alt)
1763 keyClick(Qt.Key_Tab);
1786 keyClick(Qt.Key_Tab);
1764
1787
1765 tryCompare(desktopSpread, "state", "altTab")
1788 tryCompare(desktopSpread, "state", "altTab")
1766
1789
1790+ if (!data.launcherLocked) {
1767− revealLauncherByEdgePushWithMouse();
1791+ revealLauncherByEdgePushWithMouse();
1768− tryCompare(launcher, "x", 0);
1792+ tryCompare(launcher, "x", 0);
1769− mouseMove(bfb, bfb.width / 2, bfb.height / 2)
1793+ mouseMove(bfb, bfb.width / 2, bfb.height / 2)
1770− waitForRendering(shell)
1794+ waitForRendering(shell)
1795+ }
1771
1796
1772 mouseClick(bfb, bfb.width / 2, bfb.height / 2)
1797 mouseClick(bfb, bfb.width / 2, bfb.height / 2)
1798+ if (!data.launcherLocked) {
1773− tryCompare(launcher, "state", "")
1799+ tryCompare(launcher, "state", "")
1800+ }
1774 tryCompare(desktopSpread, "state", "")
1801 tryCompare(desktopSpread, "state", "")
1775
1802
1776 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash")
1803 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash")
@@ -1998,16 +2025,24 @@ Rectangle {
1998 }
2025 }
1999 }
2026 }
2000
2027
2028+ function test_superTabToCycleLauncher_data() {
2029+ return [
2030+ {tag: "autohide launcher", launcherLocked: false},
2031+ {tag: "locked launcher", launcherLocked: true}
2032+ ]
2033+ }
2034+
2001− function test_superTabToCycleLauncher() {
2035+ function test_superTabToCycleLauncher(data) {
2002 loadShell("desktop");
2036 loadShell("desktop");
2003 shell.usageScenario = "desktop";
2037 shell.usageScenario = "desktop";
2038+ GSettingsController.setAutohideLauncher(!data.launcherLocked);
2004 waitForRendering(shell);
2039 waitForRendering(shell);
2005
2040
2006 var launcher = findChild(shell, "launcher");
2041 var launcher = findChild(shell, "launcher");
2007 var launcherPanel = findChild(launcher, "launcherPanel");
2042 var launcherPanel = findChild(launcher, "launcherPanel");
2008 var firstAppInLauncher = LauncherModel.get(0).appId;
2043 var firstAppInLauncher = LauncherModel.get(0).appId;
2009
2044
2010− compare(launcher.state, "");
2045+ compare(launcher.state, data.launcherLocked ? "visible": "");
2011 compare(launcherPanel.highlightIndex, -2);
2046 compare(launcherPanel.highlightIndex, -2);
2012 compare(ApplicationManager.focusedApplicationId, "unity8-dash");
2047 compare(ApplicationManager.focusedApplicationId, "unity8-dash");
2013
2048
@@ -2019,7 +2054,7 @@ Rectangle {
2019 keyClick(Qt.Key_Tab);
2054 keyClick(Qt.Key_Tab);
2020 tryCompare(launcherPanel, "highlightIndex", 0);
2055 tryCompare(launcherPanel, "highlightIndex", 0);
2021 keyRelease(Qt.Key_Super_L, Qt.MetaModifier);
2056 keyRelease(Qt.Key_Super_L, Qt.MetaModifier);
2022− tryCompare(launcher, "state", "");
2057+ tryCompare(launcher, "state", data.launcherLocked ? "visible" : "");
2023 tryCompare(launcherPanel, "highlightIndex", -2);
2058 tryCompare(launcherPanel, "highlightIndex", -2);
2024 tryCompare(ApplicationManager, "focusedApplicationId", firstAppInLauncher);
2059 tryCompare(ApplicationManager, "focusedApplicationId", firstAppInLauncher);
2025
2060
@@ -2029,7 +2064,7 @@ Rectangle {
2029 tryCompare(launcher, "state", "visible");
2064 tryCompare(launcher, "state", "visible");
2030 tryCompare(launcherPanel, "highlightIndex", -1);
2065 tryCompare(launcherPanel, "highlightIndex", -1);
2031 keyRelease(Qt.Key_Super_L, Qt.MetaModifier);
2066 keyRelease(Qt.Key_Super_L, Qt.MetaModifier);
2032− tryCompare(launcher, "state", "");
2067+ tryCompare(launcher, "state", data.launcherLocked ? "visible" : "");
2033 tryCompare(launcherPanel, "highlightIndex", -2);
2068 tryCompare(launcherPanel, "highlightIndex", -2);
2034 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
2069 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
2035 }
2070 }
@@ -2078,6 +2113,50 @@ Rectangle {
2078 tryCompare(launcher, "focus", true)
2113 tryCompare(launcher, "focus", true)
2079 }
2114 }
2080
2115
2116+ function test_lockedOutLauncherAddsMarginsToMaximized() {
2117+ loadShell("desktop");
2118+ shell.usageScenario = "desktop";
2119+ waitForRendering(shell);
2120+ var appContainer = findChild(shell, "appContainer");
2121+ var launcher = findChild(shell, "launcher");
2122+
2123+ var app = ApplicationManager.startApplication("music-app");
2124+ waitUntilAppWindowIsFullyLoaded(app);
2125+ var appDelegate = findChild(appContainer, "appDelegate_music-app");
2126+ appDelegate.maximize();
2127+ tryCompare(appDelegate, "visuallyMaximized", true);
2128+ waitForRendering(shell);
2129+
2130+ GSettingsController.setAutohideLauncher(true);
2131+ waitForRendering(shell)
2132+ var hiddenSize = appDelegate.width;
2133+
2134+ GSettingsController.setAutohideLauncher(false);
2135+ waitForRendering(shell)
2136+ var shownSize = appDelegate.width;
2137+
2138+ compare(shownSize + launcher.panelWidth, hiddenSize);
2139+ }
2140+
2141+ function test_fullscreenAppHidesLockedOutLauncher() {
2142+ loadShell("desktop");
2143+ shell.usageScenario = "desktop";
2144+
2145+ var launcher = findChild(shell, "launcher");
2146+ var launcherPanel = findChild(launcher, "launcherPanel");
2147+
2148+ GSettingsController.setAutohideLauncher(false);
2149+ waitForRendering(shell)
2150+
2151+ tryCompare(launcher, "lockedVisible", true);
2152+
2153+ var cameraApp = ApplicationManager.startApplication("camera-app");
2154+ waitUntilAppWindowIsFullyLoaded(cameraApp);
2155+
2156+ tryCompare(launcher, "lockedVisible", false);
2157+ }
2158+
2159+
2081 function test_inputEventsOnEdgesEndUpInAppSurface_data() {
2160 function test_inputEventsOnEdgesEndUpInAppSurface_data() {
2082 return [
2161 return [
2083 { tag: "phone", repeaterName: "spreadRepeater" },
2162 { tag: "phone", repeaterName: "spreadRepeater" },