on xenial help sign in
some first exploration of a spread for desktop
6c53347Michael Zanetti
qml/Components/PhysicalKeysMapper.qml+19
@@ -41,12 +41,18 @@ Item {
41 signal volumeUpTriggered;
41 signal volumeUpTriggered;
42 signal screenshotTriggered;
42 signal screenshotTriggered;
43
43
44+ signal altTabNext();
45+ readonly property bool altTabPressed: d.altTabPressed
46+
44 QtObject {
47 QtObject {
45 id: d
48 id: d
46
49
47 property bool volumeDownKeyPressed: false
50 property bool volumeDownKeyPressed: false
48 property bool volumeUpKeyPressed: false
51 property bool volumeUpKeyPressed: false
49 property bool ignoreVolumeEvents: false
52 property bool ignoreVolumeEvents: false
53+
54+ property bool altPressed: false
55+ property bool altTabPressed: false
50 }
56 }
51
57
52 Timer {
58 Timer {
@@ -93,6 +99,16 @@ Item {
93 }
99 }
94 d.volumeUpKeyPressed = true;
100 d.volumeUpKeyPressed = true;
95 }
101 }
102+ } else if (event.key == Qt.Key_Control) {
103+ print("controll pressed");
104+ d.altPressed = true;
105+ } else if (event.key == Qt.Key_Tab) {
106+ if (d.altPressed && !d.altTabPressed) {
107+ d.altTabPressed = true;
108+ } else if (d.altTabPressed) {
109+ print("alttabnext")
110+ root.altTabNext();
111+ }
96 }
112 }
97 }
113 }
98
114
@@ -108,6 +124,9 @@ Item {
108 if (!d.ignoreVolumeEvents) root.volumeUpTriggered();
124 if (!d.ignoreVolumeEvents) root.volumeUpTriggered();
109 d.volumeUpKeyPressed = false;
125 d.volumeUpKeyPressed = false;
110 if (!d.volumeDownKeyPressed) d.ignoreVolumeEvents = false;
126 if (!d.volumeDownKeyPressed) d.ignoreVolumeEvents = false;
127+ } else if (event.key == Qt.Key_Control) {
128+ d.altTabPressed = false;
129+ d.altPressed = false;
111 }
130 }
112 }
131 }
113}
132}
qml/Shell.qml+7
@@ -156,6 +156,8 @@ Item {
156 onVolumeDownTriggered: volumeControl.volumeDown();
156 onVolumeDownTriggered: volumeControl.volumeDown();
157 onVolumeUpTriggered: volumeControl.volumeUp();
157 onVolumeUpTriggered: volumeControl.volumeUp();
158 onScreenshotTriggered: screenGrabber.capture();
158 onScreenshotTriggered: screenGrabber.capture();
159+
160+ onAltTabNext: applicationsDisplayLoader.item.altTabNext()
159 }
161 }
160
162
161 ScreenGrabber {
163 ScreenGrabber {
@@ -280,6 +282,11 @@ Item {
280 property: "background"
282 property: "background"
281 value: shell.background
283 value: shell.background
282 }
284 }
285+ Binding {
286+ target: applicationsDisplayLoader.item
287+ property: "altTabPressed"
288+ value: physicalKeysMapper.altTabPressed
289+ }
283 }
290 }
284 }
291 }
285
292
qml/Stages/DesktopStage.qml+142−46
@@ -28,6 +28,26 @@ Item {
28
28
29 property alias background: wallpaper.source
29 property alias background: wallpaper.source
30
30
31+ property bool altTabPressed: false
32+
33+ onAltTabPressedChanged: {
34+ print("Alt+Tab pressed:", altTabPressed)
35+ if (altTabPressed) {
36+ appRepeater.highlightedIndex = 1;
37+ } else {
38+ print("focusing app", appRepeater.highlightedIndex)
39+ ApplicationManager.focusApplication(ApplicationManager.get(appRepeater.highlightedIndex).appId)
40+ }
41+ }
42+
43+ function altTabNext() {
44+ if (root.altTabPressed) {
45+ print("should tab next")
46+ appRepeater.highlightedIndex = (appRepeater.highlightedIndex + 1) % ApplicationManager.count
47+ spreadFlickable.snapTo(spreadFlickable.width / 5 * Math.max(0, appRepeater.highlightedIndex - 3))
48+ }
49+ }
50+
31 CrossFadeImage {
51 CrossFadeImage {
32 id: wallpaper
52 id: wallpaper
33 anchors.fill: parent
53 anchors.fill: parent
@@ -83,63 +103,139 @@ Item {
83 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
103 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
84 }
104 }
85
105
106+ Item {
86− Repeater {
107+ Repeater {
87− id: appRepeater
108+ id: appRepeater
88− model: ApplicationManager
109+ model: ApplicationManager
89
110
90− delegate: Item {
111+ property int highlightedIndex: 1
91− id: appDelegate
92− z: ApplicationManager.count - index
93− y: units.gu(3)
94− width: units.gu(60)
95− height: units.gu(50)
96
112
97− readonly property int minWidth: units.gu(10)
113+ delegate: Item {
98− readonly property int minHeight: units.gu(10)
114+ id: appDelegate
115+ z: ApplicationManager.count - index
116+ y: units.gu(3)
117+ width: units.gu(60)
118+ height: units.gu(50)
99
119
100− function focusWindow() {
120+ readonly property int minWidth: units.gu(10)
101− decoratedWindow.window.forceActiveFocus();
121+ readonly property int minHeight: units.gu(10)
102− }
103
122
104− states: [
123+ function focusWindow() {
105− State {
124+ decoratedWindow.window.forceActiveFocus();
106− name: "normal"
107− },
108− State {
109− name: "maximized"
110− PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
111− },
112− State {
113− name: "minimized"
114− PropertyChanges { target: appDelegate; x: -appDelegate.width / 2; scale: units.gu(5) / appDelegate.width; opacity: 0 }
115 }
125 }
126+
127+ states: [
128+ State {
129+ name: "normal"
130+ },
131+ State {
132+ name: "maximized"
133+ PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
134+ },
135+ State {
136+ name: "minimized"
137+ PropertyChanges { target: appDelegate; x: -appDelegate.width / 2; scale: units.gu(5) / appDelegate.width; opacity: 0 }
138+ },
139+ State {
140+ name: "altTab"; when: root.state == "altTab"
141+ PropertyChanges {
142+ target: appDelegate
143+ x: spreadMaths.desktopX(index, root.width, spreadFlickable.contentX)
144+ y: spreadMaths.desktopY(root.height, appDelegate.height)
145+ angle: spreadMaths.desktopAngle(index, spreadFlickable.contentX)
146+ color: "green"
147+ z: index
148+ }
149+ PropertyChanges {
150+ target: darkenOverlay
151+ opacity: index != appRepeater.highlightedIndex ? 0.4 : 0
152+ }
153+ }
116− ]
154+ ]
117− transitions: [
155+ transitions: [
118− Transition {
156+ Transition {
157+ from: "maximized,minimized,normal,"
158+ to: "maximized,minimized,normal,"
119− PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
159+ PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
160+ }
161+ ]
162+ property real angle: 0
163+ transform: [
164+ Rotation {
165+ origin { x: 0; y: appDelegate.height / 2 }
166+ axis { x: 0; y: 1; z: 0 }
167+ angle: appDelegate.angle
168+ }
169+ ]
170+
171+ SpreadMaths {
172+ id: spreadMaths
120 }
173 }
121− ]
122
174
123− WindowMoveResizeArea {
175+ WindowMoveResizeArea {
124− target: appDelegate
176+ target: appDelegate
125− minWidth: appDelegate.minWidth
177+ minWidth: appDelegate.minWidth
126− minHeight: appDelegate.minHeight
178+ minHeight: appDelegate.minHeight
127− resizeHandleWidth: units.gu(0.5)
179+ resizeHandleWidth: units.gu(0.5)
128− windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
180+ windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
129
181
130− onPressed: ApplicationManager.requestFocusApplication(model.appId)
182+ onPressed: ApplicationManager.requestFocusApplication(model.appId)
131− }
183+ }
184+
185+ DecoratedWindow {
186+ id: decoratedWindow
187+ anchors.fill: parent
188+ application: ApplicationManager.get(index)
189+ active: ApplicationManager.focusedApplicationId === model.appId
132
190
133− DecoratedWindow {
134− id: decoratedWindow
135− anchors.fill: parent
136− application: ApplicationManager.get(index)
191+ onClose: ApplicationManager.stopApplication(model.appId)
137− active: ApplicationManager.focusedApplicationId === model.appId
192+ onMaximize: appDelegate.state = (appDelegate.state == "maximized" ? "normal" : "maximized")
193+ onMinimize: appDelegate.state = "minimized"
194+ }
138
195
139− onClose: ApplicationManager.stopApplication(model.appId)
196+ Rectangle {
140− onMaximize: appDelegate.state = (appDelegate.state == "maximized" ? "normal" : "maximized")
197+ id: darkenOverlay
141− onMinimize: appDelegate.state = "minimized"
198+ anchors.fill: parent
199+ color: "black"
200+ opacity: 0
201+ }
142 }
202 }
143 }
203 }
144 }
204 }
205+
206+ Flickable {
207+ id: spreadFlickable
208+ anchors.fill: parent
209+ contentWidth: ApplicationManager.count * width / 5
210+ visible: false
211+ onContentXChanged: print("flickable flicked", contentX)
212+
213+ function snapTo(contentX) {
214+ snapAnimation.to = contentX
215+ snapAnimation.start();
216+ }
217+
218+ UbuntuNumberAnimation {
219+ id: snapAnimation
220+ target: spreadFlickable
221+ property: "contentX"
222+ }
223+ }
224+
225+ states: [
226+ State {
227+ name: "windowed"
228+ },
229+ State {
230+ name: "altTab"; when: root.altTabPressed
231+ PropertyChanges { target: spreadFlickable; visible: true }
232+ }
233+ ]
234+ transitions: [
235+ Transition {
236+ from: "*"
237+ to: "altTab"
238+ PropertyAction { target: spreadFlickable; property: "contentX"; value: 0 }
239+ }
240+ ]
145}
241}
qml/Stages/SpreadMaths.qmlnew file+50
@@ -0,0 +1,50 @@
1+import QtQuick 2.2
2+import Utils 0.1
3+
4+QtObject {
5+ id: root
6+
7+ function desktopX(index, totalWidth, flickableX) {
8+ var distance = totalWidth / 5;
9+ var minX = 0;
10+ var startX = index * distance;
11+
12+ var startProgress = 0;
13+ var endProgress = 1;
14+ var startValue = index * distance;
15+ var endValue = 0;
16+ var progress = 1.0 * flickableX / startValue;
17+ print("progress for tile", index, ":", startProgress, endProgress, startValue, endValue, progress)
18+ var x = easingAnimation(startProgress, endProgress, startValue, endValue, progress);
19+ print("resulting x:", x)
20+ return x;
21+ }
22+
23+ function desktopY(totalHeight, itemHeight) {
24+ return (totalHeight - itemHeight) / 2;
25+ }
26+
27+ function desktopAngle(index, flickableX) {
28+ var startProgress = 0;
29+ var endProgress = 1;
30+ var startValue = index * 5;
31+ var endValue = 0;
32+ var progress = 1.0 * flickableX / startValue;
33+ print("progress for tile", index, ":", startProgress, endProgress, startValue, endValue, progress)
34+ var angle = easingAnimation(startProgress, endProgress, startValue, endValue, progress);
35+ print("resulting angle:", angle)
36+ return angle;
37+ }
38+
39+ function easingAnimation(startProgress, endProgress, startValue, endValue, progress) {
40+ easingCurve.progress = progress - startProgress;
41+ easingCurve.period = endProgress - startProgress;
42+ return easingCurve.value * (endValue - startValue) + startValue;
43+ }
44+
45+ property var easingCurve: EasingCurve {
46+ type: EasingCurve.OutSine
47+ period: 1
48+ }
49+}
50+
tests/qmltests/tst_DesktopShell.qml+12
@@ -113,6 +113,18 @@ Item {
113 }
113 }
114 }
114 }
115 }
115 }
116+ Button {
117+ text: "Hide Greeter"
118+ onClicked: {
119+ if (shellLoader.status !== Loader.Ready)
120+ return
121+
122+ var greeter = testCase.findChild(shellLoader.item, "greeter")
123+ if (greeter.shown) {
124+ greeter.hide()
125+ }
126+ }
127+ }
116
128
117 Repeater {
129 Repeater {
118 id: apps
130 id: apps