on xenial help sign in
some first exploration of a spread for desktop
commit6c533474110f26aadb5c99d601bb656f6e562799 authorMichael Zanetti <michael.zanetti@canonical.com> dateMon 16 Mar 2015, 13:19 (12 years ago) parenta2f95be
5 files changed+230−46
qml/Components/PhysicalKeysMapper.qml+19 qml/Shell.qml+7 qml/Stages/DesktopStage.qml+142−46 qml/Stages/SpreadMaths.qml+50 tests/qmltests/tst_DesktopShell.qml+12
qml/Components/PhysicalKeysMapper.qml+19
@@ -41,12 +41,18 @@ Item {
4141 signal volumeUpTriggered;
4242 signal screenshotTriggered;
4343
44+ signal altTabNext();
45+ readonly property bool altTabPressed: d.altTabPressed
46+
4447 QtObject {
4548 id: d
4649
4750 property bool volumeDownKeyPressed: false
4851 property bool volumeUpKeyPressed: false
4952 property bool ignoreVolumeEvents: false
53+
54+ property bool altPressed: false
55+ property bool altTabPressed: false
5056 }
5157
5258 Timer {
@@ -93,6 +99,16 @@ Item {
9399 }
94100 d.volumeUpKeyPressed = true;
95101 }
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+ }
96112 }
97113 }
98114
@@ -108,6 +124,9 @@ Item {
108124 if (!d.ignoreVolumeEvents) root.volumeUpTriggered();
109125 d.volumeUpKeyPressed = false;
110126 if (!d.volumeDownKeyPressed) d.ignoreVolumeEvents = false;
127+ } else if (event.key == Qt.Key_Control) {
128+ d.altTabPressed = false;
129+ d.altPressed = false;
111130 }
112131 }
113132}
qml/Shell.qml+7
@@ -156,6 +156,8 @@ Item {
156156 onVolumeDownTriggered: volumeControl.volumeDown();
157157 onVolumeUpTriggered: volumeControl.volumeUp();
158158 onScreenshotTriggered: screenGrabber.capture();
159+
160+ onAltTabNext: applicationsDisplayLoader.item.altTabNext()
159161 }
160162
161163 ScreenGrabber {
@@ -280,6 +282,11 @@ Item {
280282 property: "background"
281283 value: shell.background
282284 }
285+ Binding {
286+ target: applicationsDisplayLoader.item
287+ property: "altTabPressed"
288+ value: physicalKeysMapper.altTabPressed
289+ }
283290 }
284291 }
285292
qml/Stages/DesktopStage.qml+142−46
@@ -28,6 +28,26 @@ Item {
2828
2929 property alias background: wallpaper.source
3030
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+
3151 CrossFadeImage {
3252 id: wallpaper
3353 anchors.fill: parent
@@ -83,63 +103,139 @@ Item {
83103 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
84104 }
85105
86− Repeater {
87− id: appRepeater
88− model: ApplicationManager
106+ Item {
107+ Repeater {
108+ id: appRepeater
109+ model: ApplicationManager
89110
90− delegate: Item {
91− id: appDelegate
92− z: ApplicationManager.count - index
93− y: units.gu(3)
94− width: units.gu(60)
95− height: units.gu(50)
111+ property int highlightedIndex: 1
96112
97− readonly property int minWidth: units.gu(10)
98− readonly property int minHeight: units.gu(10)
113+ delegate: Item {
114+ id: appDelegate
115+ z: ApplicationManager.count - index
116+ y: units.gu(3)
117+ width: units.gu(60)
118+ height: units.gu(50)
99119
100− function focusWindow() {
101− decoratedWindow.window.forceActiveFocus();
102− }
120+ readonly property int minWidth: units.gu(10)
121+ readonly property int minHeight: units.gu(10)
103122
104− states: [
105− State {
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 }
123+ function focusWindow() {
124+ decoratedWindow.window.forceActiveFocus();
115125 }
116− ]
117− transitions: [
118− Transition {
119− PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
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+ }
154+ ]
155+ transitions: [
156+ Transition {
157+ from: "maximized,minimized,normal,"
158+ to: "maximized,minimized,normal,"
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
120173 }
121− ]
122174
123− WindowMoveResizeArea {
124− target: appDelegate
125− minWidth: appDelegate.minWidth
126− minHeight: appDelegate.minHeight
127− resizeHandleWidth: units.gu(0.5)
128− windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
175+ WindowMoveResizeArea {
176+ target: appDelegate
177+ minWidth: appDelegate.minWidth
178+ minHeight: appDelegate.minHeight
179+ resizeHandleWidth: units.gu(0.5)
180+ windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
129181
130− onPressed: ApplicationManager.requestFocusApplication(model.appId)
131− }
182+ onPressed: ApplicationManager.requestFocusApplication(model.appId)
183+ }
184+
185+ DecoratedWindow {
186+ id: decoratedWindow
187+ anchors.fill: parent
188+ application: ApplicationManager.get(index)
189+ active: ApplicationManager.focusedApplicationId === model.appId
132190
133− DecoratedWindow {
134− id: decoratedWindow
135− anchors.fill: parent
136− application: ApplicationManager.get(index)
137− active: ApplicationManager.focusedApplicationId === model.appId
191+ onClose: ApplicationManager.stopApplication(model.appId)
192+ onMaximize: appDelegate.state = (appDelegate.state == "maximized" ? "normal" : "maximized")
193+ onMinimize: appDelegate.state = "minimized"
194+ }
138195
139− onClose: ApplicationManager.stopApplication(model.appId)
140− onMaximize: appDelegate.state = (appDelegate.state == "maximized" ? "normal" : "maximized")
141− onMinimize: appDelegate.state = "minimized"
196+ Rectangle {
197+ id: darkenOverlay
198+ anchors.fill: parent
199+ color: "black"
200+ opacity: 0
201+ }
142202 }
143203 }
144204 }
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+ ]
145241}
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 {
113113 }
114114 }
115115 }
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+ }
116128
117129 Repeater {
118130 id: apps