on xenial help sign in
implement windowed transition to spread
666ce13Michael Zanetti
qml/Stage/DecoratedWindow.qml+5−5
@@ -37,7 +37,7 @@ FocusScope {
37 // Changing this will actually add/remove a decoration, meaning, requestedHeight will take the decoration into account.
37 // Changing this will actually add/remove a decoration, meaning, requestedHeight will take the decoration into account.
38 property bool hasDecoration: true
38 property bool hasDecoration: true
39 // This will temporarily show/hide the decoration without actually changing the surface's dimensions
39 // This will temporarily show/hide the decoration without actually changing the surface's dimensions
40− property bool showDecoration: true
40+ property real showDecoration: 1
41 property bool animateDecoration: false
41 property bool animateDecoration: false
42 property bool showHighlight: false
42 property bool showHighlight: false
43 property int highlightSize: units.gu(1)
43 property int highlightSize: units.gu(1)
@@ -72,10 +72,10 @@ FocusScope {
72
72
73 QtObject {
73 QtObject {
74 id: d
74 id: d
75− property int requestedDecorationHeight: root.hasDecoration && root.hasDecoration ? decoration.height : 0
75+ property int requestedDecorationHeight: root.hasDecoration ? decoration.height : 0
76 Behavior on requestedDecorationHeight { enabled: root.animateDecoration; UbuntuNumberAnimation { duration: priv.animationDuration } }
76 Behavior on requestedDecorationHeight { enabled: root.animateDecoration; UbuntuNumberAnimation { duration: priv.animationDuration } }
77
77
78− property int visibleDecorationHeight: root.showDecoration && root.hasDecoration ? decoration.height : 0
78+ property int visibleDecorationHeight: root.hasDecoration ? root.showDecoration * decoration.height : 0
79 Behavior on visibleDecorationHeight { enabled: root.animateDecoration; UbuntuNumberAnimation { duration: priv.animationDuration } }
79 Behavior on visibleDecorationHeight { enabled: root.animateDecoration; UbuntuNumberAnimation { duration: priv.animationDuration } }
80 }
80 }
81
81
@@ -133,7 +133,7 @@ FocusScope {
133 height: units.gu(3)
133 height: units.gu(3)
134 width: root.width
134 width: root.width
135 title: applicationWindow.title
135 title: applicationWindow.title
136− opacity: root.hasDecoration && root.showDecoration ? 1 : 0
136+ opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0
137
137
138 // FIXME: priv.animationDuration reaches out of context... neads cleanup before landing
138 // FIXME: priv.animationDuration reaches out of context... neads cleanup before landing
139 Behavior on opacity { UbuntuNumberAnimation { duration: priv.animationDuration } }
139 Behavior on opacity { UbuntuNumberAnimation { duration: priv.animationDuration } }
@@ -150,7 +150,7 @@ FocusScope {
150 id: applicationWindow
150 id: applicationWindow
151 objectName: "appWindow"
151 objectName: "appWindow"
152 anchors.top: parent.top
152 anchors.top: parent.top
153− anchors.topMargin: root.decorationHeight
153+ anchors.topMargin: root.decorationHeight * Math.min(1, root.showDecoration)
154 anchors.left: parent.left
154 anchors.left: parent.left
155 width: root.width
155 width: root.width
156 height: root.height - anchors.topMargin
156 height: root.height - anchors.topMargin
qml/Stage/Spread/OpacityMask.qmlnew file+66
@@ -0,0 +1,66 @@
1+import QtQuick 2.4
2+
3+Item {
4+ id: root
5+ anchors.fill: sourceItem
6+ anchors.margins: -units.gu(2)
7+ visible: sourceItem !== null
8+
9+ property var sourceItem: null
10+ property int maskX: 0
11+ property int maskY: 0
12+ property int maskWidth: 0
13+ property int maskHeight: 0
14+
15+ property real opacityValue: 1
16+
17+ Item {
18+ id: opacityMask
19+ anchors.fill: parent
20+
21+ Rectangle {
22+ id: clipRect
23+ color: "black"
24+ x: root.maskX - root.anchors.margins
25+ y: root.maskY - root.anchors.margins
26+ width: root.maskWidth
27+ height: root.maskHeight
28+ opacity: 1 - root.opacityValue
29+ }
30+ }
31+
32+ ShaderEffect {
33+ id: opacityEffect
34+ anchors.fill: parent
35+
36+ property variant source: ShaderEffectSource {
37+ id: shaderEffectSource
38+ sourceItem: root.sourceItem
39+ sourceRect: root.sourceItem ? Qt.rect(sourceItem.x + root.anchors.margins,
40+ sourceItem.y + root.anchors.margins,
41+ sourceItem.width - root.anchors.margins * 2,
42+ sourceItem.height - root.anchors.margins * 2)
43+ : Qt.rect(0,0,0,0)
44+ hideSource: true
45+ }
46+
47+ property var mask: ShaderEffectSource {
48+ sourceItem: opacityMask
49+ hideSource: true
50+ }
51+
52+ fragmentShader: "
53+ varying highp vec2 qt_TexCoord0;
54+ uniform sampler2D source;
55+ uniform sampler2D mask;
56+ void main(void)
57+ {
58+ highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
59+ highp vec4 maskColor = texture2D(mask, qt_TexCoord0);
60+
61+ sourceColor *= 1.0 - maskColor.a;
62+
63+ gl_FragColor = sourceColor;
64+ }"
65+ }
66+}
qml/Stage/Spread/WindowedRightEdgeMaths.qmlnew file+65
@@ -0,0 +1,65 @@
1+import QtQuick 2.4
2+import Ubuntu.Components 1.3
3+import Unity.Application 0.1
4+import "MathUtils.js" as MathUtils
5+
6+QtObject {
7+ id: root
8+
9+ // Input
10+ property int itemIndex: 0
11+ property int normalZ: 0
12+ property real progress: 0
13+ property int startWidth: 0
14+ property int startHeight: 0
15+ property int startX: 0
16+ property int targetX: 0
17+ property int startY: 0
18+ property int targetY: 0
19+// property real startAngle: 40
20+ property real targetAngle: 0
21+ property int targetHeight: 0
22+ property real targetScale: 0
23+
24+ // Config
25+ property real breakPoint: 0.4
26+
27+ // Output
28+
29+ readonly property real scaleToPreviewProgress: {
30+ return progress < breakPoint ? 0 : MathUtils.clamp(MathUtils.linearAnimation(breakPoint, 1, 0, 1, progress), 0, 1)
31+ }
32+ readonly property int animatedWidth: {
33+ return progress < breakPoint ? root.startWidth : MathUtils.linearAnimation(breakPoint, 1, root.startWidth, targetHeight, progress)
34+ }
35+
36+ readonly property int animatedHeight: {
37+ return progress < breakPoint ? root.startHeight : MathUtils.linearAnimation(breakPoint, 1, root.startHeight, targetHeight, progress)
38+ }
39+
40+ readonly property int animatedX: {
41+ if (progress < breakPoint) {
42+ return startX;
43+ }
44+ return MathUtils.linearAnimation(breakPoint, 1, startX, targetX, progress)
45+ }
46+
47+ readonly property int animatedY: progress < breakPoint ? startY : MathUtils.linearAnimation(breakPoint, 1, startY, targetY, progress)
48+
49+ readonly property real animatedAngle: progress < breakPoint ? 0 : MathUtils.linearAnimation(breakPoint, 1, 0, targetAngle, progress);
50+
51+ readonly property real decorationHeight: progress < breakPoint ? 1 : MathUtils.linearAnimation(breakPoint, 1, 1, 0, progress);
52+
53+ readonly property int animatedZ: {
54+ if (progress < breakPoint + (1 - breakPoint) / 2) {
55+ return itemIndex == 1 ? normalZ + 2 : normalZ
56+ }
57+ return itemIndex
58+ }
59+
60+ readonly property real opacityMask: itemIndex == 1 ? MathUtils.linearAnimation(0, breakPoint, 0, 1, progress) : 1
61+
62+ readonly property real animatedScale: progress < breakPoint ? 1 : MathUtils.linearAnimation(breakPoint, 1, 1, targetScale, progress)
63+
64+// readonly property bool itemVisible: true //animatedX < sceneWidth
65+}
qml/Stage/Stage.qml+94−13
@@ -23,6 +23,7 @@ import Utils 0.1
23import Ubuntu.Gestures 0.1
23import Ubuntu.Gestures 0.1
24import GlobalShortcut 1.0
24import GlobalShortcut 1.0
25import "Spread"
25import "Spread"
26+import "Spread/MathUtils.js" as MathUtils
26
27
27AbstractStage {
28AbstractStage {
28 id: root
29 id: root
@@ -288,6 +289,7 @@ AbstractStage {
288 when: !appRepeater.startingUp && root.parent
289 when: !appRepeater.startingUp && root.parent
289 }
290 }
290
291
292+ onStateChanged: print("*** Main State changed to:", state)
291 states: [
293 states: [
292 State {
294 State {
293 name: "spread"; when: priv.goneToSpread
295 name: "spread"; when: priv.goneToSpread
@@ -295,14 +297,14 @@ AbstractStage {
295 PropertyChanges { target: spreadItem; focus: true }
297 PropertyChanges { target: spreadItem; focus: true }
296 },
298 },
297 State {
299 State {
298− name: "stagedrightedge"; when: rightEdgeDragArea.dragging && root.mode == "staged"
300+ name: "stagedRightEdge"; when: rightEdgeDragArea.dragging && root.mode == "staged"
299 },
301 },
300 State {
302 State {
301− name: "sidestagedrightedge"; when: rightEdgeDragArea.dragging && root.mode == "stagedWithSideStage"
303+ name: "sideStagedRightEdge"; when: rightEdgeDragArea.dragging && root.mode == "stagedWithSideStage"
302 PropertyChanges { target: priv; nextInStack: priv.sideStageDelegate && priv.sideStageDelegate.itemIndex < 2 ? 2 : 1 }
304 PropertyChanges { target: priv; nextInStack: priv.sideStageDelegate && priv.sideStageDelegate.itemIndex < 2 ? 2 : 1 }
303 },
305 },
304 State {
306 State {
305− name: "windowedrightedge"; when: rightEdgeDragArea.dragging && root.mode == "windowed"
307+ name: "windowedRightEdge"; when: rightEdgeDragArea.dragging && root.mode == "windowed"
306 },
308 },
307 State {
309 State {
308 name: "staged"; when: root.mode === "staged"
310 name: "staged"; when: root.mode === "staged"
@@ -319,7 +321,7 @@ AbstractStage {
319 ]
321 ]
320 transitions: [
322 transitions: [
321 Transition {
323 Transition {
322− from: "stagedrightedge"; to: "spread"
324+ from: "stagedRightEdge"; to: "spread"
323 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
325 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
324 },
326 },
325 Transition {
327 Transition {
@@ -333,7 +335,6 @@ AbstractStage {
333 ScriptAction {
335 ScriptAction {
334 script: {
336 script: {
335 var item = appRepeater.itemAt(Math.max(0, spreadItem.highlightedIndex));
337 var item = appRepeater.itemAt(Math.max(0, spreadItem.highlightedIndex));
336− item.claimFocus();
337 item.playFocusAnimation();
338 item.playFocusAnimation();
338 }
339 }
339 }
340 }
@@ -341,7 +342,7 @@ AbstractStage {
341 }
342 }
342 },
343 },
343 Transition {
344 Transition {
344− to: "stagedrightedge"
345+ to: "stagedRightEdge"
345 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
346 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
346 }
347 }
347
348
@@ -744,7 +745,7 @@ AbstractStage {
744
745
745 function playFocusAnimation() {
746 function playFocusAnimation() {
746 print("playing focus animation", state, root.mode, "app", model.application.appId)
747 print("playing focus animation", state, root.mode, "app", model.application.appId)
747− if (state == "stagedrightedge") {
748+ if (state == "stagedRightEdge") {
748 // TODO: Can we drop this if and find something that always works?
749 // TODO: Can we drop this if and find something that always works?
749 if (root.mode == "staged") {
750 if (root.mode == "staged") {
750 rightEdgeFocusAnimation.targetX = 0
751 rightEdgeFocusAnimation.targetX = 0
@@ -754,12 +755,16 @@ AbstractStage {
754 rightEdgeFocusAnimation.targetX = appDelegate.stage == ApplicationInfoInterface.SideStage ? sideStage.x : 0
755 rightEdgeFocusAnimation.targetX = appDelegate.stage == ApplicationInfoInterface.SideStage ? sideStage.x : 0
755 rightEdgeFocusAnimation.start()
756 rightEdgeFocusAnimation.start()
756 }
757 }
758+ } else if (state == "windowedRightEdge") {
759+ claimFocus();
757 } else {
760 } else {
758 focusAnimation.start()
761 focusAnimation.start()
759 }
762 }
760 }
763 }
761 function playHidingAnimation() {
764 function playHidingAnimation() {
765+ if (state != "windowedRightEdge") {
762− hidingAnimation.start()
766+ hidingAnimation.start()
767+ }
763 }
768 }
764
769
765 UbuntuNumberAnimation {
770 UbuntuNumberAnimation {
@@ -769,6 +774,8 @@ AbstractStage {
769 from: 0.98
774 from: 0.98
770 to: 1
775 to: 1
771 duration: UbuntuAnimation.SnapDuration
776 duration: UbuntuAnimation.SnapDuration
777+ onStarted: topLevelSurfaceList.raiseId(model.id);
778+ onStopped: appDelegate.focus = true
772 }
779 }
773 ParallelAnimation {
780 ParallelAnimation {
774 id: rightEdgeFocusAnimation
781 id: rightEdgeFocusAnimation
@@ -819,9 +826,24 @@ AbstractStage {
819 targetScale: spreadMaths.targetScale
826 targetScale: spreadMaths.targetScale
820 }
827 }
821
828
829+ WindowedRightEdgeMaths {
830+ id: windowedRightEdgeMaths
831+ itemIndex: index
832+ startWidth: appDelegate.requestedWidth
833+ startHeight: appDelegate.requestedHeight
834+ targetHeight: spreadItem.stackHeight
835+ startX: appDelegate.windowedX
836+ startY: appDelegate.windowedY
837+ targetX: spreadMaths.targetX
838+ targetY: spreadMaths.targetY
839+ normalZ: appDelegate.normalZ
840+ targetAngle: spreadMaths.targetAngle
841+ targetScale: spreadMaths.targetScale
842+ }
843+
822// onXChanged: if (model.application.appId == "unity8-dash") print("dash moved to", x)
844// onXChanged: if (model.application.appId == "unity8-dash") print("dash moved to", x)
823// onRequestedWidthChanged: if (index == 0) print("requestedWidth", requestedWidth)
845// onRequestedWidthChanged: if (index == 0) print("requestedWidth", requestedWidth)
824− onStateChanged: if (model.application.appId == "unity8-dash") print("state changed", state)
846+ onStateChanged: print("+++ App", model.application.appId, "state changed to:", state)
825 states: [
847 states: [
826 State {
848 State {
827 name: "spread"; when: root.state == "spread"
849 name: "spread"; when: root.state == "spread"
@@ -852,8 +874,8 @@ AbstractStage {
852 PropertyChanges { target: windowInfoItem; opacity: spreadMaths.tileInfoOpacity; visible: spreadMaths.itemVisible }
874 PropertyChanges { target: windowInfoItem; opacity: spreadMaths.tileInfoOpacity; visible: spreadMaths.itemVisible }
853 },
875 },
854 State {
876 State {
855− name: "stagedrightedge"
877+ name: "stagedRightEdge"
856− when: root.state == "sidestagedrightedge" || root.state == "stagedrightedge" || rightEdgeFocusAnimation.running || hidingAnimation.running
878+ when: (root.mode == "staged" || root.mode == "stagedWithSideStage") && (root.state == "sideStagedRightEdge" || root.state == "stagedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running)
857 PropertyChanges {
879 PropertyChanges {
858 target: stagedRightEdgeMaths
880 target: stagedRightEdgeMaths
859 progress: rightEdgeDragArea.progress
881 progress: rightEdgeDragArea.progress
@@ -877,11 +899,42 @@ AbstractStage {
877 shadowOpacity: .3
899 shadowOpacity: .3
878 }
900 }
879 },
901 },
902+ State {
903+ name: "windowedRightEdge"
904+ when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running)
905+ PropertyChanges {
906+ target: windowedRightEdgeMaths
907+ progress: rightEdgeDragArea.progress
908+ }
909+ PropertyChanges {
910+ target: appDelegate
911+ x: windowedRightEdgeMaths.animatedX
912+ y: windowedRightEdgeMaths.animatedY
913+ z: windowedRightEdgeMaths.animatedZ
914+ height: stagedRightEdgeMaths.animatedHeight
915+ requestedWidth: decoratedWindow.oldRequestedWidth
916+ requestedHeight: decoratedWindow.oldRequestedHeight
917+ }
918+ PropertyChanges {
919+ target: decoratedWindow
920+ showDecoration: windowedRightEdgeMaths.decorationHeight
921+ angle: windowedRightEdgeMaths.animatedAngle
922+ itemScale: windowedRightEdgeMaths.animatedScale
923+ scaleToPreviewSize: spreadItem.stackHeight
924+ scaleToPreviewProgress: windowedRightEdgeMaths.scaleToPreviewProgress
925+ shadowOpacity: .3
926+ }
927+ PropertyChanges {
928+ target: opacityEffect;
929+ opacityValue: windowedRightEdgeMaths.opacityMask
930+ sourceItem: windowedRightEdgeMaths.opacityMask < 1 ? decoratedWindow : null
931+ }
932+ },
880 State {
933 State {
881 name: "staged"; when: root.state == "staged"
934 name: "staged"; when: root.state == "staged"
882 PropertyChanges {
935 PropertyChanges {
883 target: appDelegate
936 target: appDelegate
884− x: appDelegate.focus ? 0 : root.width
937+ x: appDelegate.itemIndex == 0 ? 0 : root.width
885 y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
938 y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
886 requestedWidth: appContainer.width
939 requestedWidth: appContainer.width
887 requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
940 requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
@@ -1099,7 +1152,30 @@ AbstractStage {
1099 from: "normal,staged"; to: "stagedWithSideStage"
1152 from: "normal,staged"; to: "stagedWithSideStage"
1100 UbuntuNumberAnimation { target: appDelegate; properties: "x,y"; duration: priv.animationDuration }
1153 UbuntuNumberAnimation { target: appDelegate; properties: "x,y"; duration: priv.animationDuration }
1101 UbuntuNumberAnimation { target: appDelegate; properties: "requestedWidth,requestedHeight"; duration: priv.animationDuration }
1154 UbuntuNumberAnimation { target: appDelegate; properties: "requestedWidth,requestedHeight"; duration: priv.animationDuration }
1155+ },
1156+ Transition {
1157+ to: "windowedRightEdge"
1158+ ScriptAction {
1159+ script: {
1160+ if (index == 1) {
1161+ print("should calculate overlap from", model.application.appId, "with", appRepeater.itemAt(0).application.appId)
1162+ var thisRect = { x: appDelegate.windowedX, y: appDelegate.windowedY, width: appDelegate.requestedWidth, height: appDelegate.requestedHeight }
1163+ print("thisRect:", thisRect.x, thisRect.y, thisRect.width, thisRect.height)
1164+ var otherDelegate = appRepeater.itemAt(0);
1165+ var otherRect = { x: otherDelegate.windowedX, y: otherDelegate.windowedY, width: otherDelegate.requestedWidth, height: otherDelegate.requestedHeight }
1166+ print("otherRect:", otherRect.x, otherRect.y, otherRect.width, otherRect.height)
1167+ var intersectionRect = MathUtils.intersectionRect(thisRect, otherRect)
1168+ print("intersection is", intersectionRect.x, intersectionRect.y, intersectionRect.width, intersectionRect.height)
1169+ var mappedInterSectionRect = appDelegate.mapFromItem(root, intersectionRect.x, intersectionRect.y)
1170+ opacityEffect.maskX = mappedInterSectionRect.x
1171+ opacityEffect.maskY = mappedInterSectionRect.y
1172+ opacityEffect.maskWidth = intersectionRect.width
1173+ opacityEffect.maskHeight = intersectionRect.height
1174+ }
1175+ }
1176+ }
1102 }
1177 }
1178+
1103 ]
1179 ]
1104
1180
1105 Binding {
1181 Binding {
@@ -1151,7 +1227,7 @@ AbstractStage {
1151 surface: model.surface
1227 surface: model.surface
1152 active: appDelegate.focus
1228 active: appDelegate.focus
1153 focus: true
1229 focus: true
1154− showDecoration: true
1230+ showDecoration: 1
1155 maximizeButtonShown: (maximumWidth == 0 || maximumWidth >= appContainer.width) &&
1231 maximizeButtonShown: (maximumWidth == 0 || maximumWidth >= appContainer.width) &&
1156 (maximumHeight == 0 || maximumHeight >= appContainer.height)
1232 (maximumHeight == 0 || maximumHeight >= appContainer.height)
1157 overlayShown: touchControls.overlayShown
1233 overlayShown: touchControls.overlayShown
@@ -1179,6 +1255,7 @@ AbstractStage {
1179
1255
1180 property real angle: 0
1256 property real angle: 0
1181 property real itemScale: 1
1257 property real itemScale: 1
1258+ onAngleChanged: print("decoratedWindowd", model.application.appId, "angle", angle)
1182 transform: [
1259 transform: [
1183 Scale {
1260 Scale {
1184 origin.x: 0
1261 origin.x: 0
@@ -1194,6 +1271,10 @@ AbstractStage {
1194 ]
1271 ]
1195 }
1272 }
1196
1273
1274+ OpacityMask {
1275+ id: opacityEffect
1276+ anchors.fill: decoratedWindow
1277+ }
1197
1278
1198 WindowControlsOverlay {
1279 WindowControlsOverlay {
1199 id: touchControls
1280 id: touchControls