@@ -0,0 +1,219 @@
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+}