on xenial help sign in
Mouse has to push against edges to show launcher or apps spread

edge-barrier-sensitivity is the property that should show up in the System Settings GUI. edge-barrier-min-push and edge-barrier-max-push are exposed in GSettings merely as a convenience so you can tweak those values without having to restart unity8. But they should NOT show up in system settings.

commitfef6a0d3c11573000e742cecc88ae59af075641e authorDaniel d'Andrada <daniel.dandrada@canonical.com> dateTue 24 Nov 2015, 15:44 (11 years ago) parent340b08c
19 files changed+520−58
data/com.canonical.Unity8.gschema.xml+16 plugins/Cursor/MousePointer.cpp+2 plugins/Cursor/MousePointer.h+4 plugins/Utils/EdgeBarrierSettings.qml+66 plugins/Utils/qmldir+1 qml/Components/EdgeBarrier.qml+111 qml/Components/EdgeBarrierController.qml+71 qml/Launcher/Launcher.qml+30−32 qml/Launcher/LauncherPanel.qml+7 qml/Shell.qml+13 qml/Stages/DesktopSpread.qml+5−19 qml/Stages/DesktopStage.qml+28 tests/mocks/Cursor/Cursor.qml+2 tests/mocks/Utils/EdgeBarrierSettings.qml+22 tests/mocks/Utils/qmldir+1 tests/qmltests/EdgeBarrierControls.qml+97 tests/qmltests/Launcher/tst_Launcher.qml+15−5 tests/qmltests/Stages/tst_DesktopStage.qml+12 tests/qmltests/tst_Shell.qml+17−2
data/com.canonical.Unity8.gschema.xml+16
@@ -11,6 +11,22 @@
1111 <summary>The usage mode.</summary>
1212 <description>The usage mode chosen will affect the Window Management behaviour.</description>
1313 </key>
14+ <key type="y" name="edge-barrier-sensitivity">
15+ <default>35</default>
16+ <range min="1" max="100"/>
17+ <summary>Sensitivity of screen edge barriers for the mouse pointer.</summary>
18+ <description>Some UI actions like revealing the launcher or the applications spread are triggered by pushing the mouse pointer against a screen edge. This key defines how much you have to push in order to trigger the associated action.</description>
19+ </key>
20+ <key type="u" name="edge-barrier-min-push">
21+ <default>2</default>
22+ <summary>Minimum push needed to overcome edge barrier</summary>
23+ <description>How much you have to push (in grid units)the mouse against an edge barrier when sensibility is 100.</description>
24+ </key>
25+ <key type="u" name="edge-barrier-max-push">
26+ <default>60</default>
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>
29+ </key>
1430 </schema>
1531
1632 <schema path="/com/canonical/unity8/greeter/" id="com.canonical.Unity8.Greeter" gettext-domain="unity8">
plugins/Cursor/MousePointer.cpp+2
@@ -44,8 +44,10 @@ void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::Mouse
4444
4545 qreal newX = x() + movement.x();
4646 if (newX < 0) {
47+ Q_EMIT pushedLeftBoundary(qAbs(newX), buttons);
4748 newX = 0;
4849 } else if (newX > parentItem()->width()) {
50+ Q_EMIT pushedRightBoundary(newX - parentItem()->width(), buttons);
4951 newX = parentItem()->width();
5052 }
5153 setX(newX);
plugins/Cursor/MousePointer.h+4
@@ -42,6 +42,10 @@ public Q_SLOTS:
4242 void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
4343 Qt::KeyboardModifiers modifiers) override;
4444
45+Q_SIGNALS:
46+ void pushedLeftBoundary(qreal amount, Qt::MouseButtons buttons);
47+ void pushedRightBoundary(qreal amount, Qt::MouseButtons buttons);
48+
4549protected:
4650 void itemChange(ItemChange change, const ItemChangeData &value) override;
4751
plugins/Utils/EdgeBarrierSettings.qmlnew file+66
@@ -0,0 +1,66 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+pragma Singleton
18+import QtQuick 2.4
19+import GSettings 1.0
20+
21+QtObject {
22+ id: root
23+
24+ // The only public property
25+ readonly property real pushThreshold:
26+ d.minPushThreshold + ((d.maxPushThreshold - d.minPushThreshold) * (1.0 - d.sensitivity))
27+
28+ onPushThresholdChanged: {
29+ // Avoid calling d.printSettings() directly as it would spam the console during initialization
30+ d.printSettingsTimer.start();
31+ }
32+
33+ property QtObject priv: QtObject {
34+ id: d
35+
36+ property Timer printSettingsTimer: Timer {
37+ interval: 1
38+ onTriggered: printSettings();
39+ }
40+ function printSettings() {
41+ console.log("EdgeBarrierSettings: min="+(minPushThreshold/units.gu(1))+"gu("+minPushThreshold+"px)"
42+ +", max="+(maxPushThreshold/units.gu(1))+"gu("+maxPushThreshold+"px)"
43+ +", sensitivity="+sensitivity
44+ +", threshold="+(pushThreshold/units.gu(1))+"gu("+pushThreshold+"px)"
45+ );
46+ }
47+
48+ property real defaultMinPushThreshold: units.gu(2)
49+ property real minPushThreshold: gsettings.edgeBarrierMinPush ? units.gu(gsettings.edgeBarrierMinPush) : defaultMinPushThreshold
50+ property real maxPushThreshold: {
51+ if (gsettings.edgeBarrierMaxPush && units.gu(gsettings.edgeBarrierMaxPush) > minPushThreshold) {
52+ return units.gu(gsettings.edgeBarrierMaxPush);
53+ } else if (minPushThreshold == defaultMinPushThreshold) {
54+ return units.gu(60);
55+ } else {
56+ return minPushThreshold * 10.0;
57+ }
58+ }
59+ // Value range is [0.0, 1.0]
60+ readonly property real sensitivity: gsettings.edgeBarrierSensitivity
61+ ? Math.min(Math.max(0, gsettings.edgeBarrierSensitivity), 100) / 100
62+ : 0.35
63+
64+ property var gsettings: GSettings { schema.id: "com.canonical.Unity8" }
65+ }
66+}
plugins/Utils/qmldir+1
@@ -2,3 +2,4 @@ module Utils
22plugin Utils-qml
33typeinfo Utils.qmltypes
44Style 0.1 Style.js
5+singleton EdgeBarrierSettings 0.1 EdgeBarrierSettings.qml
qml/Components/EdgeBarrier.qmlnew file+111
@@ -0,0 +1,111 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+
20+/*
21+ An edge barrier for the mouse pointer
22+
23+ The further you push against it, the stronger the visual hint. Until it's
24+ overcome, when passed() is emitted.
25+ */
26+Item {
27+ id: root
28+
29+ // Supported values are: Qt.LeftEdge, Qt.RightEdge
30+ property int edge: Qt.LeftEdge
31+
32+ property Item target: parent
33+ function push(amount) { controller.push(amount); }
34+ signal passed()
35+
36+ anchors.top: (edge == Qt.LeftEdge || edge == Qt.RightEdge) ? target.top : undefined
37+ anchors.bottom: (edge == Qt.LeftEdge || edge == Qt.RightEdge) ? target.bottom : undefined
38+ anchors.left: edge == Qt.LeftEdge ? target.left : undefined
39+ anchors.right: edge == Qt.RightEdge ? target.right : undefined
40+
41+ width: units.gu(0.5)
42+
43+ property Component material
44+
45+ Loader {
46+ id: materialContainer
47+
48+ sourceComponent: root.material
49+
50+ anchors.top: parent.top
51+ anchors.bottom: parent.bottom
52+ anchors.left: root.edge == Qt.LeftEdge ? root.left : undefined
53+ anchors.right: root.edge == Qt.RightEdge ? root.right : undefined
54+
55+ anchors.leftMargin: root.edge == Qt.LeftEdge ? -width * (1 - positionProgress) : 0
56+ anchors.rightMargin: root.edge == Qt.RightEdge ? -width * (1 - positionProgress) : 0
57+
58+ property real positionProgress
59+
60+ visible: positionProgress > 0
61+
62+ width: units.gu(2)
63+ }
64+
65+ EdgeBarrierController {
66+ id: controller
67+ objectName: "edgeBarrierController"
68+ anchors.fill: parent
69+ onPassed: root.passed();
70+ }
71+
72+ state: {
73+ if (controller.progress === 0.0) {
74+ return "";
75+ } else if (controller.progress < 1.0) {
76+ return "resisting";
77+ } else { // controller.progress == 1.0
78+ return "passed";
79+ }
80+ }
81+ states: [
82+ State {
83+ name: ""
84+ PropertyChanges { target: materialContainer; opacity: 0.0 }
85+ PropertyChanges { target: materialContainer; positionProgress: 0.0 }
86+ },
87+ State {
88+ name: "resisting"
89+ PropertyChanges { target: materialContainer; opacity: controller.progress }
90+ PropertyChanges { target: materialContainer; positionProgress: controller.progress }
91+ },
92+ State {
93+ name: "passed"
94+ PropertyChanges { target: materialContainer; opacity: 0.0 }
95+ PropertyChanges { target: materialContainer; positionProgress: 1.0 }
96+ }
97+ ]
98+ transitions: [
99+ Transition {
100+ from: "passed"; to: ""
101+ },
102+ Transition {
103+ from: "resisting"; to: ""
104+ UbuntuNumberAnimation { target: materialContainer; properties: "opacity,positionProgress" }
105+ },
106+ Transition {
107+ from: "resisting"; to: "passed"
108+ UbuntuNumberAnimation { target: materialContainer; property: "opacity" }
109+ }
110+ ]
111+}
qml/Components/EdgeBarrierController.qmlnew file+71
@@ -0,0 +1,71 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Utils 0.1 // For EdgeBarrierSettings
19+
20+MouseArea {
21+ id: root
22+
23+ hoverEnabled: true
24+
25+ // Edge push progress
26+ // Value range is [0.0, 1.0]
27+ readonly property real progress: Math.min(Math.max(0.0, _accumulatedPush / EdgeBarrierSettings.pushThreshold), 1.0)
28+
29+ // Emitted when progress reaches 1.0
30+ // Should trigger the action associated with this edge
31+ signal passed()
32+
33+ function push(amount) {
34+ if (!root._containsMouse) {
35+ return;
36+ }
37+
38+ if (_accumulatedPush === EdgeBarrierSettings.pushThreshold) {
39+ // NO-OP
40+ return;
41+ }
42+
43+ if (_accumulatedPush + amount > EdgeBarrierSettings.pushThreshold) {
44+ _accumulatedPush = EdgeBarrierSettings.pushThreshold;
45+ } else {
46+ _accumulatedPush += amount;
47+ }
48+
49+ if (_accumulatedPush === EdgeBarrierSettings.pushThreshold) {
50+ passed();
51+ }
52+ }
53+
54+ onEnabledChanged: {
55+ if (!enabled) {
56+ // reset
57+ _accumulatedPush = 0;
58+ }
59+ }
60+
61+ // to be overwritten by tests
62+ property bool _containsMouse: root.containsMouse
63+ on_ContainsMouseChanged: {
64+ if (!_containsMouse) {
65+ // reset
66+ _accumulatedPush = 0;
67+ }
68+ }
69+
70+ property real _accumulatedPush: 0
71+}
qml/Launcher/Launcher.qml+30−32
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -84,6 +84,12 @@ Item {
8484 }
8585 }
8686
87+ function pushEdge(amount) {
88+ if (root.state === "") {
89+ edgeBarrier.push(amount);
90+ }
91+ }
92+
8793 Timer {
8894 id: teaseTimer
8995 interval: mode == "teasing" ? 200 : 300
@@ -96,7 +102,7 @@ Item {
96102 interval: 500
97103 onTriggered: {
98104 if (root.autohideEnabled) {
99− if (!panel.preventHiding && !hoverArea.containsMouse) {
105+ if (!panel.preventHiding) {
100106 root.state = ""
101107 } else {
102108 dismissTimer.restart()
@@ -176,7 +182,6 @@ Item {
176182 root.switchToNextState("visible")
177183 }
178184 }
179−
180185 }
181186
182187 MultiPointTouchArea {
@@ -203,6 +208,28 @@ Item {
203208 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
204209 }
205210
211+ EdgeBarrier {
212+ id: edgeBarrier
213+ edge: Qt.LeftEdge
214+ target: parent
215+ enabled: root.available
216+ onPassed: { root.switchToNextState("visibleTemporary"); }
217+ material: Component {
218+ Item {
219+ Rectangle {
220+ width: parent.height
221+ height: parent.width
222+ rotation: -90
223+ anchors.centerIn: parent
224+ gradient: Gradient {
225+ GradientStop { position: 0.0; color: panel.color}
226+ GradientStop { position: 1.0; color: Qt.rgba(panel.r,panel.g,panel.b,0)}
227+ }
228+ }
229+ }
230+ }
231+ }
232+
206233 LauncherPanel {
207234 id: panel
208235 objectName: "launcherPanel"
@@ -248,33 +275,6 @@ Item {
248275 }
249276 }
250277
251− // TODO: This should be replaced by some mechanism that reveals the launcher
252− // after a certain resistance has been overcome, like unity7 does. However,
253− // as we don't get relative mouse coordinates yet, this will do for now.
254− MouseArea {
255− id: hoverArea
256− anchors { fill: panel; rightMargin: -1 }
257− hoverEnabled: true
258− propagateComposedEvents: true
259− onContainsMouseChanged: {
260− if (containsMouse) {
261− root.switchToNextState("visibleTemporary");
262− } else {
263− dismissTimer.restart();
264− }
265− }
266− onPressed: mouse.accepted = false;
267−
268− // We need to eat touch events here in order to make sure that
269− // we don't trigger both, the dragArea and the hoverArea
270− MultiPointTouchArea {
271− anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
272− width: units.dp(1)
273− mouseEnabled: false
274− enabled: parent.enabled
275− }
276− }
277−
278278 DirectionalDragArea {
279279 id: dragArea
280280 objectName: "launcherDragArea"
@@ -322,7 +322,6 @@ Item {
322322 target: panel
323323 x: -root.x // so we never go past panelWidth, even when teased by tutorial
324324 }
325− PropertyChanges { target: hoverArea; enabled: false }
326325 },
327326 State {
328327 name: "visibleTemporary"
@@ -331,7 +330,6 @@ Item {
331330 target: root
332331 autohideEnabled: true
333332 }
334− PropertyChanges { target: hoverArea; enabled: true }
335333 },
336334 State {
337335 name: "teasing"
qml/Launcher/LauncherPanel.qml+7
@@ -33,6 +33,7 @@ Rectangle {
3333 property bool dragging: false
3434 property bool moving: launcherListView.moving || launcherListView.flicking
3535 property bool preventHiding: moving || dndArea.draggedIndex >= 0 || quickList.state === "open" || dndArea.pressed
36+ || mouseEventEater.containsMouse || dashItem.hovered
3637 property int highlightIndex: -1
3738
3839 signal applicationSelected(string appId)
@@ -44,6 +45,12 @@ Rectangle {
4445 }
4546 }
4647
48+ MouseArea {
49+ id: mouseEventEater
50+ anchors.fill: parent
51+ hoverEnabled: true
52+ }
53+
4754 Column {
4855 id: mainColumn
4956 anchors {
qml/Shell.qml+13
@@ -663,6 +663,19 @@ Item {
663663 id: cursor
664664 visible: shell.hasMouse
665665 z: screenGrabber.z + 1
666+
667+ onPushedLeftBoundary: {
668+ if (buttons === Qt.NoButton) {
669+ launcher.pushEdge(amount);
670+ }
671+ }
672+
673+ onPushedRightBoundary: {
674+ if (buttons === Qt.NoButton && applicationsDisplayLoader.item
675+ && applicationsDisplayLoader.item.pushRightEdge) {
676+ applicationsDisplayLoader.item.pushRightEdge(amount);
677+ }
678+ }
666679 }
667680
668681 Rectangle {
qml/Stages/DesktopSpread.qml+5−19
@@ -28,6 +28,11 @@ FocusScope {
2828
2929 readonly property alias highlightedIndex: spreadRepeater.highlightedIndex
3030
31+ function show() {
32+ spreadContainer.animateIn = true;
33+ root.state = "altTab";
34+ }
35+
3136 onFocusChanged: {
3237 // When the spread comes active, we want to keep focus to the input handler below
3338 // Make sure nothing inside the ApplicationWindow grabs our focus!
@@ -515,23 +520,4 @@ FocusScope {
515520 }
516521
517522 ]
518−
519− MouseArea {
520− id: rightEdgePushArea
521− anchors {
522− top: parent.top
523− right: parent.right
524− bottom: parent.bottom
525− }
526− // TODO: Make this a push to edge thing like the launcher when we can,
527− // for now, yes, we want 1 pixel, regardless of the scaling
528− width: 1
529− hoverEnabled: true
530− onContainsMouseChanged: {
531− if (containsMouse) {
532− spreadContainer.animateIn = true;
533− root.state = "altTab";
534− }
535− }
536− }
537523}
qml/Stages/DesktopStage.qml+28
@@ -30,6 +30,11 @@ AbstractStage {
3030 // functions to be called from outside
3131 function updateFocusedAppOrientation() { /* TODO */ }
3232 function updateFocusedAppOrientationAnimated() { /* TODO */}
33+ function pushRightEdge(amount) {
34+ if (spread.state === "") {
35+ edgeBarrier.push(amount);
36+ }
37+ }
3338
3439 mainApp: ApplicationManager.focusedApplicationId
3540 ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
@@ -471,6 +476,29 @@ AbstractStage {
471476 enabled: visible
472477 }
473478
479+ EdgeBarrier {
480+ id: edgeBarrier
481+
482+ // NB: it does its own positioning according to the specified edge
483+ edge: Qt.RightEdge
484+
485+ onPassed: { spread.show(); }
486+ material: Component {
487+ Item {
488+ Rectangle {
489+ width: parent.height
490+ height: parent.width
491+ rotation: 90
492+ anchors.centerIn: parent
493+ gradient: Gradient {
494+ GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.7)}
495+ GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
496+ }
497+ }
498+ }
499+ }
500+ }
501+
474502 DesktopSpread {
475503 id: spread
476504 objectName: "spread"
tests/mocks/Cursor/Cursor.qml+2
@@ -17,4 +17,6 @@
1717import QtQuick 2.4
1818
1919Item {
20+ signal pushedLeftBoundary(real amount, int buttons)
21+ signal pushedRightBoundary(real amount, int buttons)
2022}
tests/mocks/Utils/EdgeBarrierSettings.qmlnew file+22
@@ -0,0 +1,22 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+pragma Singleton
18+import QtQuick 2.4
19+
20+QtObject {
21+ readonly property real pushThreshold: units.gu(8)
22+}
tests/mocks/Utils/qmldir+1
@@ -3,3 +3,4 @@ plugin FakeUtils-qml
33typeinfo Utils.qmltypes
44Style 0.1 Style.js
55HomeKeyWatcher 0.1 HomeKeyWatcher.qml
6+singleton EdgeBarrierSettings 0.1 EdgeBarrierSettings.qml
tests/qmltests/EdgeBarrierControls.qmlnew file+97
@@ -0,0 +1,97 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.3
20+import Unity.Test 0.1
21+
22+RowLayout {
23+ id: root
24+ Layout.fillWidth: true
25+
26+ property var target: null
27+ property string backgroundColor: "darkGrey"
28+ property alias text: label.text
29+ signal dragged(real amount);
30+
31+ Binding {
32+ id: containsMouseBinding
33+
34+ // only activate the binding once the controls start to get used in order to not
35+ // interfere with automated tests
36+ property bool activated: false
37+
38+ when: root.target !== undefined && root.target !== null && activated
39+ target: root.target
40+ property: "_containsMouse"
41+ value: checkbox.checked
42+ }
43+
44+
45+ Rectangle {
46+ width: label.width + units.gu(1)
47+ height: units.gu(4)
48+ color: root.backgroundColor
49+
50+ Rectangle {
51+ anchors.fill: parent
52+ color: "darkGreen"
53+ opacity: target ? target.progress : 0
54+ }
55+
56+ Label {
57+ id: label
58+ color: "white"
59+ anchors.centerIn: parent
60+ }
61+ MouseArea {
62+ id: mouseArea
63+ anchors.fill: parent
64+ property real lastX
65+ property bool dragging: false
66+ onPressedChanged: {
67+ if (pressed) {
68+ containsMouseBinding.activated = true;
69+ checkbox.checked = true;
70+ } else {
71+ dragging = false;
72+ }
73+ }
74+ onMouseXChanged: {
75+ if (dragging) {
76+ var amount = Math.abs(mouseX - lastX);
77+ lastX = mouseX;
78+ root.dragged(amount);
79+ } else {
80+ lastX = mouseX;
81+ dragging = true;
82+ }
83+ }
84+ }
85+ }
86+
87+ CheckBox {
88+ id: checkbox
89+ checked: false
90+ activeFocusOnPress: false
91+ onCheckedChanged: {
92+ if (checked) {
93+ containsMouseBinding.activated = true;
94+ }
95+ }
96+ }
97+}
tests/qmltests/Launcher/tst_Launcher.qml+15−5
@@ -22,6 +22,7 @@ import Ubuntu.Components 1.3
2222import ".."
2323import "../../../qml/Launcher"
2424import Unity.Launcher 0.1
25+import Utils 0.1 // For EdgeBarrierSettings
2526
2627/* Nothing is shown at first. If you drag from left edge you will bring up the
2728 launcher. */
@@ -67,6 +68,7 @@ Item {
6768
6869 Component.onCompleted: {
6970 launcherLoader.itemDestroyed = false;
71+ edgeBarrierControls.target = testCase.findChild(this, "edgeBarrierController");
7072 }
7173 Component.onDestruction: {
7274 launcherLoader.itemDestroyed = true;
@@ -82,6 +84,12 @@ Item {
8284
8385 MouseTouchEmulationCheckbox {}
8486
87+ EdgeBarrierControls {
88+ id: edgeBarrierControls
89+ text: "Drag here to pull out launcher"
90+ onDragged: { launcherLoader.item.pushEdge(amount); }
91+ }
92+
8593 Button {
8694 text: "emit hinting signal"
8795 onClicked: LauncherModel.emitHint()
@@ -207,8 +215,10 @@ Item {
207215 tryCompare(launcher, "state", "visible");
208216 }
209217
210− function revealByHover() {
218+ function revealByEdgePush() {
219+ // Place the mouse against the window/screen edge and push beyond the barrier threshold
211220 mouseMove(root, 1, root.height / 2);
221+ launcher.pushEdge(EdgeBarrierSettings.pushThreshold * 1.1);
212222
213223 var panel = findChild(launcher, "launcherPanel");
214224 verify(!!panel);
@@ -278,7 +288,7 @@ Item {
278288
279289 function test_clickingOnAppIconCausesSignalEmission(data) {
280290 if (data.mouse) {
281− revealByHover();
291+ revealByEdgePush();
282292 } else {
283293 dragLauncherIntoView();
284294 }
@@ -748,12 +758,12 @@ Item {
748758 verify(quickList, "state", "")
749759 }
750760
751− function test_revealByHover() {
761+ function test_revealByEdgePush() {
752762 var panel = findChild(launcher, "launcherPanel");
753763 verify(!!panel);
754764
755− revealByHover();
756− tryCompare(launcher, "state", "visibleTemporary");
765+ revealByEdgePush();
766+ compare(launcher.state, "visibleTemporary");
757767
758768 // Now move the mouse away and make sure it hides in less than a second
759769 mouseMove(root, root.width, root.height / 2)
tests/qmltests/Stages/tst_DesktopStage.qml+12
@@ -22,6 +22,7 @@ import Unity.Application 0.1
2222import Unity.Test 0.1
2323import Utils 0.1
2424
25+import ".." // For EdgeBarrierControls
2526import "../../../qml/Stages"
2627import "../../../qml/Components"
2728
@@ -65,6 +66,10 @@ Item {
6566 DesktopStage {
6667 color: "darkblue"
6768 anchors.fill: parent
69+
70+ Component.onCompleted: {
71+ edgeBarrierControls.target = testCase.findChild(this, "edgeBarrierController");
72+ }
6873 Component.onDestruction: {
6974 desktopStageLoader.itemDestroyed = true;
7075 }
@@ -99,6 +104,13 @@ Item {
99104 }
100105 }
101106
107+ EdgeBarrierControls {
108+ id: edgeBarrierControls
109+ text: "Drag here to pull out spread"
110+ backgroundColor: "blue"
111+ onDragged: { desktopStageLoader.item.pushRightEdge(amount); }
112+ }
113+
102114 Divider {}
103115
104116 Repeater {
tests/qmltests/tst_Shell.qml+17−2
@@ -954,6 +954,22 @@ Rectangle {
954954 removeTimeConstraintsFromDirectionalDragAreas(greeter);
955955 }
956956
957+ function revealLauncherByEdgePushWithMouse() {
958+ var launcher = findChild(shell, "launcher");
959+ verify(launcher);
960+
961+ // Place the mouse against the window/screen edge and push beyond the barrier threshold
962+ mouseMove(shell, 0, shell.height / 2);
963+ launcher.pushEdge(EdgeBarrierSettings.pushThreshold * 1.1);
964+
965+ var panel = findChild(launcher, "launcherPanel");
966+ verify(panel);
967+
968+ // wait until it gets fully extended
969+ tryCompare(panel, "x", 0);
970+ tryCompare(launcher, "state", "visibleTemporary");
971+ }
972+
957973 function test_focusRequestedHidesGreeter() {
958974 loadShell("phone");
959975 swipeAwayGreeter();
@@ -1713,8 +1729,7 @@ Rectangle {
17131729
17141730 tryCompare(desktopSpread, "state", "altTab")
17151731
1716− mouseMove(shell, 0, shell.height / 2);
1717− tryCompare(launcher, "state", "visibleTemporary")
1732+ revealLauncherByEdgePushWithMouse();
17181733 waitForRendering(shell)
17191734
17201735 mouseClick(bfb, bfb.width / 2, bfb.height / 2)