on xenial help sign in
Implement launcher tooltips.
381271bAndrea Azzarone
debian/control+2
@@ -49,6 +49,7 @@ Build-Depends: cmake,
49 python3-all:any,
49 python3-all:any,
50 python3-setuptools,
50 python3-setuptools,
51 qml-module-qt-labs-folderlistmodel,
51 qml-module-qt-labs-folderlistmodel,
52+ qml-module-qtqml-statemachine,
52 qml-module-qtmultimedia (>= 5.4.1-1ubuntu19~overlay2),
53 qml-module-qtmultimedia (>= 5.4.1-1ubuntu19~overlay2),
53 qml-module-qtquick-layouts,
54 qml-module-qtquick-layouts,
54 qml-module-qtquick-xmllistmodel,
55 qml-module-qtquick-xmllistmodel,
@@ -121,6 +122,7 @@ Depends: dmz-cursor-theme,
121 qmenumodel-qml (>= 0.2.10),
122 qmenumodel-qml (>= 0.2.10),
122 qml-module-biometryd,
123 qml-module-biometryd,
123 qml-module-qt-labs-folderlistmodel,
124 qml-module-qt-labs-folderlistmodel,
125+ qml-module-qtqml-statemachine,
124 qml-module-qtquick-xmllistmodel,
126 qml-module-qtquick-xmllistmodel,
125 qml-module-qtsysteminfo,
127 qml-module-qtsysteminfo,
126 qml-module-ubuntu-web,
128 qml-module-ubuntu-web,
qml/Launcher/LauncherDelegate.qml+1
@@ -22,6 +22,7 @@ Item {
22
22
23 property int itemIndex: 0
23 property int itemIndex: 0
24 property string iconName
24 property string iconName
25+ property string name
25 property int count: 0
26 property int count: 0
26 property bool countVisible: false
27 property bool countVisible: false
27 property int progress: -1
28 property int progress: -1
qml/Launcher/LauncherPanel.qml+79−3
@@ -15,6 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18+import QtQml.StateMachine 1.0 as DSM
18import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
19import Unity.Launcher 0.1
20import Unity.Launcher 0.1
20import Ubuntu.Components.Popups 1.3
21import Ubuntu.Components.Popups 1.3
@@ -31,7 +32,7 @@ Rectangle {
31 property bool dragging: false
32 property bool dragging: false
32 property bool moving: launcherListView.moving || launcherListView.flicking
33 property bool moving: launcherListView.moving || launcherListView.flicking
33 property bool preventHiding: moving || dndArea.draggedIndex >= 0 || quickList.state === "open" || dndArea.pressed
34 property bool preventHiding: moving || dndArea.draggedIndex >= 0 || quickList.state === "open" || dndArea.pressed
34− || mouseEventEater.containsMouse || dashItem.hovered
35+ || dndArea.containsMouse || dashItem.hovered
35 property int highlightIndex: -2
36 property int highlightIndex: -2
36 property bool shortcutHintsShown: false
37 property bool shortcutHintsShown: false
37
38
@@ -40,7 +41,7 @@ Rectangle {
40 signal kbdNavigationCancelled()
41 signal kbdNavigationCancelled()
41
42
42 onXChanged: {
43 onXChanged: {
43− if (quickList.state == "open") {
44+ if (quickList.state === "open") {
44 quickList.state = ""
45 quickList.state = ""
45 }
46 }
46 }
47 }
@@ -69,7 +70,6 @@ Rectangle {
69 id: mouseEventEater
70 id: mouseEventEater
70 anchors.fill: parent
71 anchors.fill: parent
71 acceptedButtons: Qt.AllButtons
72 acceptedButtons: Qt.AllButtons
72− hoverEnabled: true
73 onWheel: wheel.accepted = true;
73 onWheel: wheel.accepted = true;
74 }
74 }
75
75
@@ -225,6 +225,7 @@ Rectangle {
225 // the right app when running autopilot tests for
225 // the right app when running autopilot tests for
226 // multiple apps.
226 // multiple apps.
227 readonly property string appId: model.appId
227 readonly property string appId: model.appId
228+ name: model.name
228 itemIndex: index
229 itemIndex: index
229 itemHeight: launcherListView.itemHeight
230 itemHeight: launcherListView.itemHeight
230 itemWidth: launcherListView.itemWidth
231 itemWidth: launcherListView.itemWidth
@@ -399,6 +400,7 @@ Rectangle {
399 id: dndArea
400 id: dndArea
400 objectName: "dndArea"
401 objectName: "dndArea"
401 acceptedButtons: Qt.LeftButton | Qt.RightButton
402 acceptedButtons: Qt.LeftButton | Qt.RightButton
403+ hoverEnabled: true
402 anchors {
404 anchors {
403 fill: parent
405 fill: parent
404 topMargin: launcherListView.topMargin
406 topMargin: launcherListView.topMargin
@@ -829,4 +831,78 @@ Rectangle {
829 }
831 }
830 }
832 }
831 }
833 }
834+
835+ Tooltip {
836+ id: tooltipShape
837+ objectName: "tooltipShape"
838+
839+ visible: tooltipShownState.active
840+ rotation: root.rotation
841+ y: itemCenter - (height / 2)
842+
843+ anchors {
844+ left: root.inverted ? undefined : parent.right
845+ right: root.inverted ? parent.left : undefined
846+ margins: units.gu(1)
847+ }
848+
849+ readonly property var hoveredItem: dndArea.containsMouse ? launcherListView.itemAt(dndArea.mouseX, dndArea.mouseY + launcherListView.realContentY) : null
850+ readonly property int itemCenter: !hoveredItem ? 0 : root.mapFromItem(hoveredItem, 0, 0).y + (hoveredItem.height / 2) + hoveredItem.offset
851+
852+ text: !hoveredItem ? "" : hoveredItem.name
853+ }
854+
855+ DSM.StateMachine {
856+ id: tooltipStateMachine
857+ initialState: tooltipHiddenState
858+ running: true
859+
860+ DSM.State {
861+ id: tooltipHiddenState
862+
863+ DSM.SignalTransition {
864+ targetState: tooltipShownState
865+ signal: tooltipShape.hoveredItemChanged
866+ // !dndArea.pressed allows us to filter out touch input events
867+ guard: tooltipShape.hoveredItem !== null && !dndArea.pressed && !root.moving
868+ }
869+ }
870+
871+ DSM.State {
872+ id: tooltipShownState
873+
874+ DSM.SignalTransition {
875+ targetState: tooltipHiddenState
876+ signal: tooltipShape.hoveredItemChanged
877+ guard: tooltipShape.hoveredItem === null
878+ }
879+
880+ DSM.SignalTransition {
881+ targetState: tooltipDismissedState
882+ signal: dndArea.onPressed
883+ }
884+
885+ DSM.SignalTransition {
886+ targetState: tooltipDismissedState
887+ signal: quickList.stateChanged
888+ guard: quickList.state === "open"
889+ }
890+ }
891+
892+ DSM.State {
893+ id: tooltipDismissedState
894+
895+ DSM.SignalTransition {
896+ targetState: tooltipHiddenState
897+ signal: dndArea.positionChanged
898+ guard: quickList.state != "open" && !dndArea.pressed && !dndArea.moving
899+ }
900+
901+ DSM.SignalTransition {
902+ targetState: tooltipHiddenState
903+ signal: dndArea.exited
904+ guard: quickList.state != "open"
905+ }
906+ }
907+ }
832}
908}
qml/Launcher/Tooltip.qmlnew file+72
@@ -0,0 +1,72 @@
1+/*
2+ * Copyright (C) 2016 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+UbuntuShape {
21+ id: root
22+
23+ // This property holds the delay (milliseconds) after which the tool tip is shown.
24+ // A tooltip with a negative delay is shown immediately. The default value is 500 ms.
25+ property int delay: 500
26+
27+ // This property holds the text shown on the tool tip.
28+ property alias text: label.text
29+
30+ aspect: UbuntuShape.Flat
31+ color: theme.palette.normal.background
32+ width: label.implicitWidth + units.gu(4)
33+ height: label.implicitHeight + units.gu(2)
34+ opacity: 0
35+
36+ Image {
37+ anchors {
38+ right: parent.left
39+ rightMargin: -units.dp(4)
40+ verticalCenter: parent.verticalCenter
41+ }
42+ source: "graphics/quicklist_tooltip.png"
43+ rotation: 90
44+ }
45+
46+ Label {
47+ id: label
48+ anchors.centerIn: parent
49+ color: theme.palette.normal.backgroundText
50+ }
51+
52+ states: [
53+ State {
54+ name: "visible"
55+ when: root.visible
56+ PropertyChanges {
57+ target: root
58+ opacity: 0.95
59+ }
60+ }
61+ ]
62+
63+ transitions: [
64+ Transition {
65+ from: ""; to: "visible"
66+ SequentialAnimation {
67+ PauseAnimation { duration: root.delay }
68+ UbuntuNumberAnimation { target: root; property: "opacity"; duration: UbuntuAnimation.BriskDuration }
69+ }
70+ }
71+ ]
72+}
tests/qmltests/Launcher/tst_Launcher.qml+89
@@ -1369,6 +1369,95 @@ Rectangle {
1369 tryCompare(clickThroughSpy, "count", 0);
1369 tryCompare(clickThroughSpy, "count", 0);
1370 }
1370 }
1371
1371
1372+ function test_tooltip() {
1373+ launcher.lockedVisible = true;
1374+ revealByEdgePush();
1375+
1376+ var item = findChild(launcher, "launcherDelegate0");
1377+ var tooltipShape = findChild(launcher, "tooltipShape");
1378+
1379+ // Initial state
1380+ tryCompare(tooltipShape, "visible", false);
1381+ tryCompare(tooltipShape, "opacity", .0);
1382+
1383+ // Move the mouse on the launcher icon
1384+ mouseMove(item, item.width / 2, item.height / 2, 10);
1385+ mouseMove(item, item.width / 2 + 1, item.height / 2, 10);
1386+ tryCompare(tooltipShape, "visible", true);
1387+ tryCompare(tooltipShape, "opacity", .95);
1388+
1389+ // Move the mouse away
1390+ mouseMove(root, root.width, root.height / 2, 10);
1391+ tryCompare(tooltipShape, "visible", false);
1392+ tryCompare(tooltipShape, "opacity", .0);
1393+ }
1394+
1395+ function test_quicklist_dismisses_tooltip() {
1396+ launcher.lockedVisible = true;
1397+ revealByEdgePush();
1398+
1399+ var item = findChild(launcher, "launcherDelegate0");
1400+ var tooltipShape = findChild(launcher, "tooltipShape");
1401+ var quickListShape = findChild(launcher, "quickListShape");
1402+
1403+ // Initial state
1404+ tryCompare(tooltipShape, "visible", false);
1405+ tryCompare(tooltipShape, "opacity", .0);
1406+
1407+ // Move the mouse on the launcher icon
1408+ mouseMove(item, item.width / 2, item.height / 2, 10);
1409+ mouseMove(item, item.width / 2 + 1, item.height / 2, 10);
1410+ tryCompare(tooltipShape, "visible", true);
1411+ tryCompare(tooltipShape, "opacity", .95);
1412+
1413+ // Right click to show the quicklist
1414+ mouseClick(item, item.width / 2, item.height / 2, Qt.RightButton);
1415+ tryCompare(quickListShape, "visible", true);
1416+ tryCompare(tooltipShape, "visible", false);
1417+ tryCompare(tooltipShape, "opacity", .0);
1418+
1419+ // Left click hides the quicklist, tooltip is still dismissed
1420+ mouseClick(item, item.width / 2, item.height / 2, Qt.LefftButton);
1421+ tryCompare(quickListShape, "visible", false);
1422+ tryCompare(tooltipShape, "visible", false);
1423+ tryCompare(tooltipShape, "opacity", .0);
1424+
1425+ // Mouse motion should should show tooltip again
1426+ mouseMove(item, item.width / 2, item.height / 2, 10);
1427+ mouseMove(item, item.width / 2 + 1, item.height / 2, 10);
1428+ tryCompare(tooltipShape, "visible", true);
1429+ tryCompare(tooltipShape, "opacity", .95);
1430+ }
1431+
1432+ function test_click_dismisses_tooltip() {
1433+ launcher.lockedVisible = true;
1434+ revealByEdgePush();
1435+
1436+ var item = findChild(launcher, "launcherDelegate0");
1437+ var tooltipShape = findChild(launcher, "tooltipShape");
1438+
1439+ // Initial state
1440+ tryCompare(tooltipShape, "visible", false);
1441+ tryCompare(tooltipShape, "opacity", .0);
1442+
1443+ // Move the mouse on the launcher icon
1444+ mouseMove(item, item.width / 2, item.height / 2, 10);
1445+ mouseMove(item, item.width / 2 + 1, item.height / 2, 10);
1446+ tryCompare(tooltipShape, "visible", true);
1447+ tryCompare(tooltipShape, "opacity", .95);
1448+
1449+ // Left click should dismiss the tooltip
1450+ mouseClick(item, item.width / 2 + 1, item.height / 2, Qt.LeftButton);
1451+ tryCompare(tooltipShape, "visible", false);
1452+ tryCompare(tooltipShape, "opacity", .0);
1453+
1454+ // Mouse motion should should show tooltip again
1455+ mouseMove(item, item.width / 2, item.height / 2, 10);
1456+ mouseMove(item, item.width / 2 + 1, item.height / 2, 10);
1457+ tryCompare(tooltipShape, "visible", true);
1458+ tryCompare(tooltipShape, "opacity", .95);
1459+ }
1460+
1372 function test_launcherEnabledSetting() {
1461 function test_launcherEnabledSetting() {
1373 launcher.available = true;
1462 launcher.available = true;
1374
1463