Unity 8
WindowControlsOverlay.qml
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 import Ubuntu.Gestures 0.1
20 import Unity.Application 0.1
21 import "../Components/PanelState"
22 
23 Item {
24  id: root
25  enabled: target && !target.fullscreen
26 
27  // to be set from outside
28  property Item target // appDelegate
29 
30  // to be read from outside
31  readonly property alias overlayShown: overlay.visible
32 
33  TouchGestureArea {
34  id: gestureArea
35  anchors.fill: parent
36 
37  // NB: for testing set to 2, not to clash with unity7 touch overlay controls
38  minimumTouchPoints: 3
39  maximumTouchPoints: minimumTouchPoints
40 
41  readonly property bool recognizedPress: status == TouchGestureArea.Recognized &&
42  touchPoints.length >= minimumTouchPoints &&
43  touchPoints.length <= maximumTouchPoints
44  onRecognizedPressChanged: {
45  if (recognizedPress) {
46  target.focus = true;
47  overlayTimer.start();
48  }
49  }
50 
51  readonly property bool recognizedDrag: recognizedPress && dragging
52  onRecognizedDragChanged: {
53  if (recognizedDrag) {
54  priv.handlePressedChanged(true, tp.x, tp.y);
55  } else if (!moveHandler.containsPress) { // prevent interfering with the central piece drag/move
56  priv.dragging = false;
57  }
58  }
59 
60  readonly property point tp: recognizedPress ? Qt.point(touchPoints[0].x, touchPoints[0].y) : Qt.point(-1, -1)
61  onUpdated: {
62  if (recognizedDrag) {
63  priv.handlePositionChanged(tp.x, tp.y);
64  }
65  }
66  }
67 
68  // dismiss timer
69  Timer {
70  id: overlayTimer
71  interval: 2000
72  repeat: priv.dragging || (priv.resizeArea && priv.resizeArea.dragging)
73  }
74 
75  QtObject {
76  id: priv
77  property real distanceX
78  property real distanceY
79  property bool dragging
80 
81  readonly property var resizeArea: root.target && root.target.resizeArea ? root.target.resizeArea : null
82 
83  function handlePressedChanged(pressed, mouseX, mouseY) {
84  if (pressed) {
85  var pos = mapToItem(root.target, mouseX, mouseY);
86  priv.distanceX = pos.x;
87  priv.distanceY = pos.y;
88  priv.dragging = true;
89  } else {
90  priv.dragging = false;
91  }
92  }
93 
94  function handlePositionChanged(mouseX, mouseY) {
95  if (priv.dragging) {
96  var pos = mapToItem(root.target.parent, mouseX, mouseY);
97  root.target.x = Math.round(pos.x - priv.distanceX);
98  root.target.y = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
99  }
100  }
101  }
102 
103  // the visual overlay
104  Item {
105  id: overlay
106  objectName: "windowControlsOverlay"
107  anchors.fill: parent
108  enabled: overlayTimer.running
109  visible: opacity > 0
110  opacity: enabled ? 0.95 : 0
111 
112  Behavior on opacity {
113  UbuntuNumberAnimation {}
114  }
115 
116  readonly property bool anyMaximized: target && (target.maximized || target.maximizedLeft || target.maximizedRight)
117 
118  Image {
119  source: "graphics/arrows-centre.png"
120  width: units.gu(10)
121  height: width
122  sourceSize: Qt.size(width, height)
123  anchors.centerIn: parent
124  visible: target && target.width > units.gu(12) && target.height > units.gu(12)
125 
126  // move handler
127  MouseArea {
128  id: moveHandler
129  anchors.fill: parent
130  visible: overlay.visible
131  enabled: visible
132  hoverEnabled: true
133  cursorShape: priv.dragging ? Qt.ClosedHandCursor : (overlay.visible ? Qt.OpenHandCursor : Qt.ArrowCursor)
134 
135  onPressedChanged: priv.handlePressedChanged(pressed, mouseX, mouseY)
136  onPositionChanged: priv.handlePositionChanged(mouseX, mouseY)
137  }
138 
139  // dismiss area
140  InverseMouseArea {
141  anchors.fill: parent
142  visible: overlay.visible
143  enabled: visible
144  onPressed: {
145  if (gestureArea.recognizedPress || gestureArea.recognizedDrag) {
146  mouse.accepted = false;
147  return;
148  }
149 
150  overlayTimer.stop();
151  mouse.accepted = root.contains(Qt.point(mouse.x, mouse.y));
152  }
153  propagateComposedEvents: true
154  }
155  }
156 
157  ResizeGrip { // top left
158  anchors.horizontalCenter: parent.left
159  anchors.verticalCenter: parent.top
160  visible: target && !overlay.anyMaximized && !target.maximizedHorizontally && !target.maximizedVertically
161  resizeTarget: priv.resizeArea
162  }
163 
164  ResizeGrip { // top center
165  anchors.horizontalCenter: parent.horizontalCenter
166  anchors.verticalCenter: parent.top
167  rotation: 45
168  visible: target && !overlay.anyMaximized && !target.maximizedVertically
169  resizeTarget: priv.resizeArea
170  }
171 
172  ResizeGrip { // top right
173  anchors.horizontalCenter: parent.right
174  anchors.verticalCenter: parent.top
175  rotation: 90
176  visible: target && !overlay.anyMaximized && !target.maximizedHorizontally && !target.maximizedVertically
177  resizeTarget: priv.resizeArea
178  }
179 
180  ResizeGrip { // right
181  anchors.horizontalCenter: parent.right
182  anchors.verticalCenter: parent.verticalCenter
183  rotation: 135
184  visible: target && !target.maximizedRight && !target.maximized && !target.maximizedHorizontally
185  resizeTarget: priv.resizeArea
186  }
187 
188  ResizeGrip { // bottom right
189  anchors.horizontalCenter: parent.right
190  anchors.verticalCenter: parent.bottom
191  visible: target && !overlay.anyMaximized && !target.maximizedHorizontally && !target.maximizedVertically
192  resizeTarget: priv.resizeArea
193  }
194 
195  ResizeGrip { // bottom center
196  anchors.horizontalCenter: parent.horizontalCenter
197  anchors.verticalCenter: parent.bottom
198  rotation: 45
199  visible: target && !overlay.anyMaximized && !target.maximizedVertically
200  resizeTarget: priv.resizeArea
201  }
202 
203  ResizeGrip { // bottom left
204  anchors.horizontalCenter: parent.left
205  anchors.verticalCenter: parent.bottom
206  rotation: 90
207  visible: target && !overlay.anyMaximized && !target.maximizedHorizontally && !target.maximizedVertically
208  resizeTarget: priv.resizeArea
209  }
210 
211  ResizeGrip { // left
212  anchors.horizontalCenter: parent.left
213  anchors.verticalCenter: parent.verticalCenter
214  rotation: 135
215  visible: target && !target.maximizedLeft && !target.maximized && !target.maximizedHorizontally
216  resizeTarget: priv.resizeArea
217  }
218  }
219 }