on xenial help sign in
Use a s StateMachine to handle tooltip logic.
0e4d9f5Andrea Azzarone
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−44
@@ -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
@@ -44,9 +45,6 @@ Rectangle {
44 if (quickList.state === "open") {
45 if (quickList.state === "open") {
45 quickList.state = ""
46 quickList.state = ""
46 }
47 }
47− if (tooltip.state === "open") {
48− quickList.state = ""
49− }
50 }
48 }
51
49
52 function highlightNext() {
50 function highlightNext() {
@@ -175,12 +173,6 @@ Rectangle {
175 property real realContentY: contentY - originY + topMargin
173 property real realContentY: contentY - originY + topMargin
176 property int realItemHeight: itemHeight + spacing
174 property int realItemHeight: itemHeight + spacing
177
175
178− onRealContentYChanged : {
179− if (tooltip.state === "open") {
180− tooltip.state = "";
181− }
182− }
183−
184 // In case the start dragging transition is running, we need to delay the
176 // In case the start dragging transition is running, we need to delay the
185 // move because the displaced transition would clash with it and cause items
177 // move because the displaced transition would clash with it and cause items
186 // to be moved to wrong places
178 // to be moved to wrong places
@@ -235,6 +227,7 @@ Rectangle {
235 // the right app when running autopilot tests for
227 // the right app when running autopilot tests for
236 // multiple apps.
228 // multiple apps.
237 readonly property string appId: model.appId
229 readonly property string appId: model.appId
230+ name: model.name
238 itemIndex: index
231 itemIndex: index
239 itemHeight: launcherListView.itemHeight
232 itemHeight: launcherListView.itemHeight
240 itemWidth: launcherListView.itemWidth
233 itemWidth: launcherListView.itemWidth
@@ -431,20 +424,12 @@ Rectangle {
431
424
432 function processPress(mouse) {
425 function processPress(mouse) {
433 selectedItem = launcherListView.itemAt(mouse.x, mouse.y + launcherListView.realContentY)
426 selectedItem = launcherListView.itemAt(mouse.x, mouse.y + launcherListView.realContentY)
434−
435− if (tooltip.state === "open") {
436− tooltip.state = "";
437− }
438 }
427 }
439
428
440 onClicked: {
429 onClicked: {
441 var index = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
430 var index = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
442 var clickedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
431 var clickedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
443
432
444− if (tooltip.state === "open") {
445− tooltip.state = "";
446− }
447−
448 // Check if we actually clicked an item or only at the spacing in between
433 // Check if we actually clicked an item or only at the spacing in between
449 if (clickedItem === null) {
434 if (clickedItem === null) {
450 return;
435 return;
@@ -544,10 +529,6 @@ Rectangle {
544 startY = mouse.y
529 startY = mouse.y
545 }
530 }
546
531
547− onExited: {
548− tooltip.state = "";
549− }
550−
551 onPositionChanged: {
532 onPositionChanged: {
552 processPositionChanged(mouse)
533 processPositionChanged(mouse)
553 }
534 }
@@ -602,17 +583,6 @@ Rectangle {
602 draggedIndex = newIndex
583 draggedIndex = newIndex
603 }
584 }
604 }
585 }
605− } else {
606− var index = Math.floor((mouse.y + launcherListView.realContentY) / launcherListView.realItemHeight);
607− var clickedItem = launcherListView.itemAt(mouse.x, mouse.y + launcherListView.realContentY);
608−
609− // Check if we actually hovered an item or only the spacing in between
610− if (clickedItem === null || mouse.buttons) {
611− tooltip.state = "";
612− return;
613− }
614−
615− tooltip.open(index);
616 }
586 }
617 }
587 }
618 }
588 }
@@ -866,13 +836,15 @@ Rectangle {
866 id: tooltipShape
836 id: tooltipShape
867 objectName: "tooltipShape"
837 objectName: "tooltipShape"
868 anchors.fill: tooltip
838 anchors.fill: tooltip
869− opacity: tooltip.state === "open" && quickList.state != "open" ? 0.95 : 0
839+ opacity: tooltipShownState.active ? 0.95 : 0
870 visible: opacity > 0
840 visible: opacity > 0
871 rotation: root.rotation
841 rotation: root.rotation
872 aspect: UbuntuShape.Flat
842 aspect: UbuntuShape.Flat
873
843
874 Behavior on opacity {
844 Behavior on opacity {
875− UbuntuNumberAnimation {}
845+ UbuntuNumberAnimation {
846+ duration: UbuntuAnimation.BriskDuration
847+ }
876 }
848 }
877
849
878 source: ShaderEffectSource {
850 source: ShaderEffectSource {
@@ -896,6 +868,7 @@ Rectangle {
896
868
897 Rectangle {
869 Rectangle {
898 id: tooltip
870 id: tooltip
871+ objectName: "tooltip"
899
872
900 color: theme.palette.normal.background
873 color: theme.palette.normal.background
901 visible: tooltipShape.visible
874 visible: tooltipShape.visible
@@ -912,19 +885,17 @@ Rectangle {
912 margins: units.gu(1)
885 margins: units.gu(1)
913 }
886 }
914
887
915− property string text
888+ property var hoveredItem: dndArea.containsMouse ? launcherListView.itemAt(dndArea.mouseX, dndArea.mouseY + launcherListView.realContentY) : null
916− property var item
917
889
918− property int itemCenter: item ? root.mapFromItem(tooltip.item, 0, 0).y + (item.height / 2) + tooltip.item.offset : units.gu(1)
890+ property int itemCenter
919 property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height :
891 property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height :
920 itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0
892 itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0
921
893
922− function open(index) {
894+ onHoveredItemChanged : {
923− var itemPosition = index * launcherListView.itemHeight;
895+ if (hoveredItem != null && !root.moving) {
924− var height = launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin
896+ itemCenter = root.mapFromItem(hoveredItem, 0, 0).y + (hoveredItem.height / 2) + hoveredItem.offset
925− item = launcherListView.itemAt(launcherListView.width / 2, itemPosition + launcherListView.itemHeight / 2);
897+ tooltipLabel.text = tooltip.hoveredItem.name
926− tooltip.text = launcherListView.model.get(index).name;
898+ }
927− tooltip.state = "open";
928 }
899 }
929
900
930 Label {
901 Label {
@@ -932,8 +903,72 @@ Rectangle {
932 height: parent.height
903 height: parent.height
933 anchors.centerIn: parent
904 anchors.centerIn: parent
934 verticalAlignment: Label.AlignVCenter
905 verticalAlignment: Label.AlignVCenter
935− text: tooltip.text
936 color: theme.palette.normal.backgroundText
906 color: theme.palette.normal.backgroundText
937 }
907 }
908+
909+ DSM.StateMachine {
910+ id: tooltipStateMachine
911+ initialState: tooltipHiddenState
912+ running: true
913+
914+ DSM.State {
915+ id: tooltipHiddenState
916+
917+ DSM.SignalTransition {
918+ targetState: tooltipShownState
919+ signal: tooltip.hoveredItemChanged
920+ // !dndArea.pressed allows us to filter out touch input events
921+ guard: tooltip.hoveredItem != null && !dndArea.pressed && !root.moving
922+ }
923+ }
924+
925+ DSM.State {
926+ id: tooltipShownState
927+
928+ DSM.SignalTransition {
929+ targetState: tooltipHiddenState
930+ signal: tooltip.hoveredItemChanged
931+ guard: tooltip.hoveredItem == null
932+ }
933+
934+ DSM.SignalTransition {
935+ targetState: tooltipHiddenState
936+ signal: dndArea.exited
937+ }
938+
939+ DSM.SignalTransition {
940+ targetState: tooltipDismissedState
941+ signal: dndArea.onPressed
942+ }
943+
944+ DSM.SignalTransition {
945+ targetState: tooltipDismissedState
946+ signal: quickList.stateChanged
947+ guard: quickList.state === "open"
948+ }
949+
950+ DSM.SignalTransition {
951+ targetState: tooltipDismissedState
952+ signal: root.movingChanged
953+ guard: root.moving
954+ }
955+ }
956+
957+ DSM.State {
958+ id: tooltipDismissedState
959+
960+ DSM.SignalTransition {
961+ targetState: tooltipHiddenState
962+ signal: dndArea.positionChanged
963+ guard: quickList.state != "open" && !dndArea.pressed && !dndArea.moving
964+ }
965+
966+ DSM.SignalTransition {
967+ targetState: tooltipHiddenState
968+ signal: dndArea.exited
969+ guard: quickList.state != "open"
970+ }
971+ }
972+ }
938 }
973 }
939}
974}
tests/qmltests/Launcher/tst_Launcher.qml+63−1
@@ -1356,15 +1356,77 @@ Rectangle {
1356
1356
1357 function test_tooltip() {
1357 function test_tooltip() {
1358 dragLauncherIntoView()
1358 dragLauncherIntoView()
1359+
1360+ var item = findChild(launcher, "launcherDelegate0")
1361+ var tooltipShape = findChild(launcher, "tooltipShape")
1362+
1363+ // Make sure we don't start the test with the mouse hovering the launcher
1364+ mouseMove(root, root.width, root.height / 2)
1365+
1366+ // Initial state
1367+ compare(tooltipShape.visible, false)
1368+
1369+ // Move the mouse on the launcher icon
1370+ mouseMove(item, item.width / 2, item.height / 2)
1371+ mouseMove(item, item.width / 2 + 1, item.height / 2)
1372+ tryCompare(tooltipShape, "visible", true)
1373+
1374+ // Move the mouse away
1375+ mouseMove(root, root.width, root.height / 2)
1376+ tryCompare(tooltipShape, "visible", false)
1377+ }
1378+
1379+ function test_quicklist_dismiss_tooltip() {
1380+ dragLauncherIntoView()
1381+
1382+ var item = findChild(launcher, "launcherDelegate0")
1383+ var tooltipShape = findChild(launcher, "tooltipShape")
1384+ var quickListShape = findChild(launcher, "quickListShape")
1385+
1386+ // Make sure we don't start the test with the mouse hovering the launcher
1387+ mouseMove(root, root.width, root.height / 2)
1388+
1389+ // Initial state
1390+ compare(tooltipShape.visible, false)
1391+
1392+ var tooltip = findChild(launcher, "tooltip")
1393+
1394+
1395+ // Move the mouse on the launcher icon
1396+ mouseMove(item, item.width / 2, item.height / 2)
1397+ mouseMove(item, item.width / 2 + 1, item.height / 2)
1398+ tryCompare(tooltipShape, "visible", true)
1399+ console.log(tooltip.state)
1400+
1401+
1402+ // Right click to show the quicklist
1403+ mouseClick(item, item.width / 2, item.height / 2, Qt.RightButton)
1404+ tryCompare(quickListShape, "visible", true)
1405+
1406+ console.log(tooltip.state)
1407+ tryCompare(tooltipShape, "visible", false)
1408+ }
1409+
1410+ function test_click_dismiss_tooltip() {
1411+ dragLauncherIntoView()
1412+
1359 var item = findChild(launcher, "launcherDelegate0")
1413 var item = findChild(launcher, "launcherDelegate0")
1360 var tooltipShape = findChild(launcher, "tooltipShape")
1414 var tooltipShape = findChild(launcher, "tooltipShape")
1361
1415
1416+ // Make sure we don't start the test with the mouse hovering the launcher
1417+ mouseMove(root, root.width, root.height / 2)
1418+
1362 // Initial state
1419 // Initial state
1363 compare(tooltipShape.visible, false)
1420 compare(tooltipShape.visible, false)
1364
1421
1365 // Move the mouse on the launcher icon
1422 // Move the mouse on the launcher icon
1366− mouseMove(item, item.width / 2, item.height / 2, 2)
1423+ mouseMove(item, item.width / 2, item.height / 2)
1424+ mouseMove(item, item.width / 2 + 1, item.height / 2)
1367 tryCompare(tooltipShape, "visible", true)
1425 tryCompare(tooltipShape, "visible", true)
1426+
1427+ // Left click should dismiss the tooltip
1428+ mouseClick(item, item.width / 2, item.height / 2, Qt.LeftButton)
1429+ tryCompare(tooltipShape, "visible", false)
1368 }
1430 }
1369 }
1431 }
1370}
1432}