on xenial help sign in
Fix being unable to use window control buttons or menus with touch (LP: #1667604, #1668387)

Approved by: Albert Astals Cid, Unity8 CI Bot

commit6948e5fe8e580b114358d1cebf5d067c31922768 authorLukáš Tinkl <lukas.tinkl@canonical.com> dateWed 8 Mar 2017, 09:53 (10 years ago) parent5c86f20f4ef2e3
9 files changed+100−46
qml/ApplicationMenus/MenuBar.qml+35−11 qml/Components/WindowControlButtons.qml−21 qml/Panel/Panel.qml+2 qml/Stage/DecoratedWindow.qml+3 qml/Stage/MoveHandler.qml+4 qml/Stage/WindowDecoration.qml+12−14 tests/qmltests/ApplicationMenus/tst_MenuBar.qml+2 tests/qmltests/Stage/tst_DesktopStage.qml+18 tests/qmltests/tst_Shell.qml+24
qml/ApplicationMenus/MenuBar.qml+35−11
@@ -28,11 +28,19 @@ Item {
2828 property alias unityMenuModel: rowRepeater.model
2929 property bool enableKeyFilter: false
3030 property real overflowWidth: width
31+ property bool windowMoving: false
3132
3233 // read from outside
3334 readonly property bool valid: rowRepeater.count > 0
3435 readonly property bool showRequested: d.longAltPressed || d.currentItem != null
3536
37+ // MoveHandler API for DecoratedWindow
38+ signal pressed(var mouse)
39+ signal pressedChangedEx(bool pressed, var pressedButtons, real mouseX, real mouseY)
40+ signal positionChanged(var mouse)
41+ signal released(var mouse)
42+ signal doubleClicked(var mouse)
43+
3644 implicitWidth: row.width
3745 height: parent.height
3846
@@ -77,7 +85,7 @@ Item {
7785 anchors.fill: parent
7886 enabled: d.currentItem != null
7987 hoverEnabled: enabled && d.currentItem && d.currentItem.__popup != null
80− onPressed: d.dismissAll()
88+ onPressed: { mouse.accepted = false; d.dismissAll(); }
8189 }
8290
8391 Row {
@@ -218,24 +226,40 @@ Item {
218226 } // Row
219227
220228 MouseArea {
221− anchors.fill: row
229+ anchors.fill: parent
222230 hoverEnabled: d.currentItem
223231
232+ property bool moved: false
233+
224234 onEntered: {
225235 if (d.currentItem) {
226236 updateCurrentItemFromPosition(Qt.point(mouseX, mouseY))
227237 }
228238 }
229− onPositionChanged: {
230− if (d.currentItem) {
231− updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
239+
240+ onClicked: {
241+ if (!moved) {
242+ var prevItem = d.currentItem;
243+ updateCurrentItemFromPosition(Qt.point(mouseX, mouseY));
244+ if (prevItem && d.currentItem == prevItem) {
245+ prevItem.hide();
246+ }
232247 }
248+ moved = false;
233249 }
234− onClicked: {
235− var prevItem = d.currentItem;
236− updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
237− if (prevItem && d.currentItem == prevItem) {
238− prevItem.hide();
250+
251+ // for the MoveHandler
252+ onPressed: root.pressed(mouse)
253+ onPressedChanged: root.pressedChangedEx(pressed, pressedButtons, mouseX, mouseY)
254+ onReleased: root.released(mouse)
255+ onDoubleClicked: root.doubleClicked(mouse)
256+
257+ Mouse.ignoreSynthesizedEvents: true
258+ Mouse.onPositionChanged: {
259+ root.positionChanged(mouse);
260+ moved = root.windowMoving;
261+ if (d.currentItem) {
262+ updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
239263 }
240264 }
241265
@@ -260,7 +284,7 @@ Item {
260284 hoverEnabled: d.currentItem
261285 onEntered: d.currentItem = this
262286 onPositionChanged: d.currentItem = this
263− onClicked: d.currentItem = this
287+ onPressed: d.currentItem = this
264288
265289 property Item __popup: null;
266290 readonly property bool popupVisible: __popup && __popup.visible
qml/Components/WindowControlButtons.qml−21
@@ -47,13 +47,6 @@ Row {
4747 onClicked: root.closeClicked()
4848 visible: root.closeButtonShown
4949
50− // We dont want touch events to fall through to parent,
51− // otherwise the containsMouse will not work.
52− MouseArea {
53− anchors.fill: parent
54− propagateComposedEvents: true
55− }
56−
5750 Rectangle {
5851 anchors.fill: parent
5952 anchors.margins: windowIsMaximized ? units.dp(3) : 0
@@ -78,13 +71,6 @@ Row {
7871 onClicked: root.minimizeClicked()
7972 visible: root.minimizeButtonVisible
8073
81− // We dont want touch events to fall through to parent,
82− // otherwise the containsMouse will not work.
83− MouseArea {
84− anchors.fill: parent
85− propagateComposedEvents: true
86− }
87−
8874 Rectangle {
8975 anchors.fill: parent
9076 anchors.margins: windowIsMaximized ? units.dp(3) : 0
@@ -118,13 +104,6 @@ Row {
118104 }
119105 }
120106
121− // We dont want touch events to fall through to parent,
122− // otherwise the containsMouse will not work.
123− MouseArea {
124− anchors.fill: parent
125− propagateComposedEvents: true
126− }
127−
128107 Rectangle {
129108 anchors.fill: parent
130109 anchors.margins: windowIsMaximized ? units.dp(3) : 0
qml/Panel/Panel.qml+2
@@ -229,6 +229,8 @@ Item {
229229 target: __indicators
230230 onShownChanged: bar.dismiss();
231231 }
232+
233+ onDoubleClicked: PanelState.restoreClicked()
232234 }
233235 }
234236 }
qml/Stage/DecoratedWindow.qml+3
@@ -208,12 +208,14 @@ FocusScope {
208208 height: units.gu(3)
209209
210210 title: applicationWindow.title
211+ windowMoving: moveHandler.moving && !altDragHandler.dragging
211212
212213 opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0
213214 Behavior on opacity { UbuntuNumberAnimation { } }
214215
215216 onPressed: root.decorationPressed();
216217 onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
218+ onPressedChangedEx: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
217219 onPositionChanged: moveHandler.handlePositionChanged(mouse)
218220 onReleased: {
219221 root.decorationReleased();
@@ -259,6 +261,7 @@ FocusScope {
259261 }
260262
261263 MouseArea {
264+ id: altDragHandler
262265 anchors.fill: applicationWindow
263266 acceptedButtons: Qt.LeftButton
264267 property bool dragging: false
qml/Stage/MoveHandler.qml+4
@@ -29,6 +29,7 @@ QtObject {
2929 property real boundsTopMargin: 0
3030
3131 readonly property bool dragging: priv.dragging
32+ readonly property bool moving: priv.moving
3233
3334 signal fakeMaximizeAnimationRequested(real amount)
3435 signal fakeMaximizeLeftAnimationRequested(real amount)
@@ -43,6 +44,7 @@ QtObject {
4344 property real distanceX
4445 property real distanceY
4546 property bool dragging
47+ property bool moving
4648
4749 readonly property int triggerArea: units.gu(8)
4850 property bool nearLeftEdge: target.maximizedLeft
@@ -110,6 +112,7 @@ QtObject {
110112
111113 function handlePositionChanged(mouse, sensingPoints) {
112114 if (priv.dragging) {
115+ priv.moving = true;
113116 priv.mouseDownTimer.stop();
114117 Mir.cursorName = "grabbing";
115118
@@ -180,6 +183,7 @@ QtObject {
180183 }
181184
182185 function handleReleased(touchMode) {
186+ priv.moving = false;
183187 if (touchMode) {
184188 priv.progress = 0;
185189 priv.resetEdges();
qml/Stage/WindowDecoration.qml+12−14
@@ -33,23 +33,21 @@ MouseArea {
3333 property alias overlayShown: buttons.overlayShown
3434 property var menu: undefined
3535 property bool enableMenus: true
36+ property bool windowMoving: false
3637
3738 readonly property real buttonsWidth: buttons.width + row.spacing
3839
3940 acceptedButtons: Qt.AllButtons // prevent leaking unhandled mouse events
4041 hoverEnabled: true
4142
42− drag.target: Item {}
43− drag.filterChildren: true
44− drag.threshold: 0
45−
4643 signal closeClicked()
4744 signal minimizeClicked()
4845 signal maximizeClicked()
4946 signal maximizeHorizontallyClicked()
5047 signal maximizeVerticallyClicked()
5148
52− onClicked: mouse.accepted = true // propogated event
49+ signal pressedChangedEx(bool pressed, var pressedButtons, real mouseX, real mouseY)
50+
5351 onDoubleClicked: {
5452 if (mouse.button == Qt.LeftButton) {
5553 root.maximizeClicked();
@@ -69,13 +67,6 @@ MouseArea {
6967 (menuBar.showRequested || root.containsMouse)
7068 }
7169
72− // We dont want touch events to fall through to parent as it expect some child MouseArea to have them
73− // If not some MouseArea in the menu bar, it will be this one.
74− MouseArea {
75− anchors.fill: parent
76− propagateComposedEvents: true
77− }
78−
7970 Rectangle {
8071 id: background
8172 anchors.fill: parent
@@ -136,7 +127,7 @@ MouseArea {
136127 fontSize: "medium"
137128 font.weight: root.active ? Font.Light : Font.Medium
138129 elide: Text.ElideRight
139− opacity: overlayShown || priv.shouldShowMenus ? 0 : 1
130+ opacity: overlayShown || menuBarLoader.visible ? 0 : 1
140131 visible: opacity != 0
141132 Behavior on opacity { UbuntuNumberAnimation {} }
142133 }
@@ -154,9 +145,16 @@ MouseArea {
154145 height: menuBarLoader.height
155146 enableKeyFilter: valid && root.active && root.enableMenus
156147 unityMenuModel: root.menu
148+ windowMoving: root.windowMoving
149+
150+ onPressed: root.onPressed(mouse)
151+ onPressedChangedEx: root.pressedChangedEx(pressed, pressedButtons, mouseX, mouseY)
152+ onPositionChanged: root.onPositionChanged(mouse)
153+ onReleased: root.onReleased(mouse)
154+ onDoubleClicked: root.onDoubleClicked(mouse)
157155 }
158156
159− opacity: !overlayShown && priv.shouldShowMenus ? 1 : 0
157+ opacity: (!overlayShown && priv.shouldShowMenus) || (active && priv.menuBar.valid && root.windowMoving) ? 1 : 0
160158 visible: opacity == 1
161159 Behavior on opacity { UbuntuNumberAnimation {} }
162160 }
tests/qmltests/ApplicationMenus/tst_MenuBar.qml+2
@@ -98,6 +98,7 @@ Item {
9898 var menuItem2 = findChild(menuBar, "menuBar-item2"); verify(menuItem2);
9999
100100 menuItem0.show();
101+ mouseMove(menuItem0, menuItem0.width/2, menuItem0.height/2);
101102 compare(priv.currentItem, menuItem0, "CurrentItem should be set to item 0");
102103 compare(priv.currentItem.popupVisible, true, "Popup should be visible");
103104
@@ -179,6 +180,7 @@ Item {
179180
180181 keyPress(data.tag, Qt.AltModifier, 100);
181182 tryCompare(priv, "currentItem", menuItem);
183+ keyRelease(data.tag, Qt.AltModifier, 100);
182184 }
183185
184186 function test_menuActivateClosesMenu() {
tests/qmltests/Stage/tst_DesktopStage.qml+18
@@ -1039,5 +1039,23 @@ Item {
10391039 expectFail("", "Hovering the window controls should be ignored when the menu is open");
10401040 tryCompare(closeButton, "containsMouse", true);
10411041 }
1042+
1043+ function test_windowControlsTouchInteractionWithMenu() {
1044+ var appDelegate = startApplication("gmail-webapp");
1045+
1046+ var wd = findChild(appDelegate, "appWindowDecoration");
1047+ var maxButton = findChild(wd, "maximizeWindowButton");
1048+ var menuBarLoader = findChild(wd, "menuBarLoader");
1049+ var menuNav = findInvisibleChild(menuBarLoader, "d");
1050+
1051+ // make the menubar active and visible, select first item
1052+ menuBarLoader.active = true;
1053+ menuNav.select(0);
1054+ tryCompare(menuBarLoader.item, "visible", true);
1055+
1056+ // verify the maximized button can still be tapped
1057+ tap(maxButton);
1058+ tryCompare(appDelegate, "state", "maximized");
1059+ }
10421060 }
10431061}
tests/qmltests/tst_Shell.qml+24
@@ -2907,5 +2907,29 @@ Rectangle {
29072907 tryCompare(topLevelSurfaceList.focusedWindow, "surface", appDelegate.surface);
29082908 tryCompare(topLevelSurfaceList.applicationAt(0), "appId", "dialer-app");
29092909 }
2910+
2911+ function test_doubleClickPanelRestoresWindow() {
2912+ loadShell("desktop");
2913+ shell.usageScenario = "desktop";
2914+ waitForRendering(shell);
2915+ swipeAwayGreeter();
2916+
2917+ // start dialer
2918+ var appDelegate = startApplication("dialer-app")
2919+ verify(appDelegate);
2920+ tryCompare(appDelegate, "state", "normal");
2921+
2922+ // maximize dialer
2923+ var decoration = findChild(appDelegate, "appWindowDecoration");
2924+ verify(decoration);
2925+ mouseDoubleClickSequence(decoration);
2926+ tryCompare(appDelegate, "state", "maximized");
2927+
2928+ // double click the panel
2929+ var panel = findChild(shell, "panel");
2930+ verify(panel);
2931+ mouseDoubleClickSequence(panel, panel.width/2, PanelState.panelHeight/2, Qt.LeftButton, Qt.NoModifier, 300);
2932+ tryCompare(appDelegate, "state", "restored");
2933+ }
29102934 }
29112935}