on xenial help sign in
implement windowed transition to spread
commit666ce13b2c9e4a26a9de71306957212673e4c6d4 authorMichael Zanetti <michael.zanetti@canonical.com> dateThu 25 Aug 2016, 19:00 (10 years ago) parent4f47a9c
4 files changed+230−18
qml/Stage/DecoratedWindow.qml+5−5 qml/Stage/Spread/OpacityMask.qml+66 qml/Stage/Spread/WindowedRightEdgeMaths.qml+65 qml/Stage/Stage.qml+94−13
qml/Stage/DecoratedWindow.qml+5−5
@@ -37,7 +37,7 @@ FocusScope {
3737 // Changing this will actually add/remove a decoration, meaning, requestedHeight will take the decoration into account.
3838 property bool hasDecoration: true
3939 // This will temporarily show/hide the decoration without actually changing the surface's dimensions
40− property bool showDecoration: true
40+ property real showDecoration: 1
4141 property bool animateDecoration: false
4242 property bool showHighlight: false
4343 property int highlightSize: units.gu(1)
@@ -72,10 +72,10 @@ FocusScope {
7272
7373 QtObject {
7474 id: d
75− property int requestedDecorationHeight: root.hasDecoration && root.hasDecoration ? decoration.height : 0
75+ property int requestedDecorationHeight: root.hasDecoration ? decoration.height : 0
7676 Behavior on requestedDecorationHeight { enabled: root.animateDecoration; UbuntuNumberAnimation { duration: priv.animationDuration } }
7777
78− property int visibleDecorationHeight: root.showDecoration && root.hasDecoration ? decoration.height : 0
78+ property int visibleDecorationHeight: root.hasDecoration ? root.showDecoration * decoration.height : 0
7979 Behavior on visibleDecorationHeight { enabled: root.animateDecoration; UbuntuNumberAnimation { duration: priv.animationDuration } }
8080 }
8181
@@ -133,7 +133,7 @@ FocusScope {
133133 height: units.gu(3)
134134 width: root.width
135135 title: applicationWindow.title
136− opacity: root.hasDecoration && root.showDecoration ? 1 : 0
136+ opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0
137137
138138 // FIXME: priv.animationDuration reaches out of context... neads cleanup before landing
139139 Behavior on opacity { UbuntuNumberAnimation { duration: priv.animationDuration } }
@@ -150,7 +150,7 @@ FocusScope {
150150 id: applicationWindow
151151 objectName: "appWindow"
152152 anchors.top: parent.top
153− anchors.topMargin: root.decorationHeight
153+ anchors.topMargin: root.decorationHeight * Math.min(1, root.showDecoration)
154154 anchors.left: parent.left
155155 width: root.width
156156 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
2323import Ubuntu.Gestures 0.1
2424import GlobalShortcut 1.0
2525import "Spread"
26+import "Spread/MathUtils.js" as MathUtils
2627
2728AbstractStage {
2829 id: root
@@ -288,6 +289,7 @@ AbstractStage {
288289 when: !appRepeater.startingUp && root.parent
289290 }
290291
292+ onStateChanged: print("*** Main State changed to:", state)
291293 states: [
292294 State {
293295 name: "spread"; when: priv.goneToSpread
@@ -295,14 +297,14 @@ AbstractStage {
295297 PropertyChanges { target: spreadItem; focus: true }
296298 },
297299 State {
298− name: "stagedrightedge"; when: rightEdgeDragArea.dragging && root.mode == "staged"
300+ name: "stagedRightEdge"; when: rightEdgeDragArea.dragging && root.mode == "staged"
299301 },
300302 State {
301− name: "sidestagedrightedge"; when: rightEdgeDragArea.dragging && root.mode == "stagedWithSideStage"
303+ name: "sideStagedRightEdge"; when: rightEdgeDragArea.dragging && root.mode == "stagedWithSideStage"
302304 PropertyChanges { target: priv; nextInStack: priv.sideStageDelegate && priv.sideStageDelegate.itemIndex < 2 ? 2 : 1 }
303305 },
304306 State {
305− name: "windowedrightedge"; when: rightEdgeDragArea.dragging && root.mode == "windowed"
307+ name: "windowedRightEdge"; when: rightEdgeDragArea.dragging && root.mode == "windowed"
306308 },
307309 State {
308310 name: "staged"; when: root.mode === "staged"
@@ -319,7 +321,7 @@ AbstractStage {
319321 ]
320322 transitions: [
321323 Transition {
322− from: "stagedrightedge"; to: "spread"
324+ from: "stagedRightEdge"; to: "spread"
323325 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
324326 },
325327 Transition {
@@ -333,7 +335,6 @@ AbstractStage {
333335 ScriptAction {
334336 script: {
335337 var item = appRepeater.itemAt(Math.max(0, spreadItem.highlightedIndex));
336− item.claimFocus();
337338 item.playFocusAnimation();
338339 }
339340 }
@@ -341,7 +342,7 @@ AbstractStage {
341342 }
342343 },
343344 Transition {
344− to: "stagedrightedge"
345+ to: "stagedRightEdge"
345346 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
346347 }
347348
@@ -744,7 +745,7 @@ AbstractStage {
744745
745746 function playFocusAnimation() {
746747 print("playing focus animation", state, root.mode, "app", model.application.appId)
747− if (state == "stagedrightedge") {
748+ if (state == "stagedRightEdge") {
748749 // TODO: Can we drop this if and find something that always works?
749750 if (root.mode == "staged") {
750751 rightEdgeFocusAnimation.targetX = 0
@@ -754,12 +755,16 @@ AbstractStage {
754755 rightEdgeFocusAnimation.targetX = appDelegate.stage == ApplicationInfoInterface.SideStage ? sideStage.x : 0
755756 rightEdgeFocusAnimation.start()
756757 }
758+ } else if (state == "windowedRightEdge") {
759+ claimFocus();
757760 } else {
758761 focusAnimation.start()
759762 }
760763 }
761764 function playHidingAnimation() {
762− hidingAnimation.start()
765+ if (state != "windowedRightEdge") {
766+ hidingAnimation.start()
767+ }
763768 }
764769
765770 UbuntuNumberAnimation {
@@ -769,6 +774,8 @@ AbstractStage {
769774 from: 0.98
770775 to: 1
771776 duration: UbuntuAnimation.SnapDuration
777+ onStarted: topLevelSurfaceList.raiseId(model.id);
778+ onStopped: appDelegate.focus = true
772779 }
773780 ParallelAnimation {
774781 id: rightEdgeFocusAnimation
@@ -819,9 +826,24 @@ AbstractStage {
819826 targetScale: spreadMaths.targetScale
820827 }
821828
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+
822844// onXChanged: if (model.application.appId == "unity8-dash") print("dash moved to", x)
823845// 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)
825847 states: [
826848 State {
827849 name: "spread"; when: root.state == "spread"
@@ -852,8 +874,8 @@ AbstractStage {
852874 PropertyChanges { target: windowInfoItem; opacity: spreadMaths.tileInfoOpacity; visible: spreadMaths.itemVisible }
853875 },
854876 State {
855− name: "stagedrightedge"
856− when: root.state == "sidestagedrightedge" || root.state == "stagedrightedge" || rightEdgeFocusAnimation.running || hidingAnimation.running
877+ name: "stagedRightEdge"
878+ when: (root.mode == "staged" || root.mode == "stagedWithSideStage") && (root.state == "sideStagedRightEdge" || root.state == "stagedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running)
857879 PropertyChanges {
858880 target: stagedRightEdgeMaths
859881 progress: rightEdgeDragArea.progress
@@ -877,11 +899,42 @@ AbstractStage {
877899 shadowOpacity: .3
878900 }
879901 },
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+ },
880933 State {
881934 name: "staged"; when: root.state == "staged"
882935 PropertyChanges {
883936 target: appDelegate
884− x: appDelegate.focus ? 0 : root.width
937+ x: appDelegate.itemIndex == 0 ? 0 : root.width
885938 y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
886939 requestedWidth: appContainer.width
887940 requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
@@ -1099,7 +1152,30 @@ AbstractStage {
10991152 from: "normal,staged"; to: "stagedWithSideStage"
11001153 UbuntuNumberAnimation { target: appDelegate; properties: "x,y"; duration: priv.animationDuration }
11011154 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+ }
11021177 }
1178+
11031179 ]
11041180
11051181 Binding {
@@ -1151,7 +1227,7 @@ AbstractStage {
11511227 surface: model.surface
11521228 active: appDelegate.focus
11531229 focus: true
1154− showDecoration: true
1230+ showDecoration: 1
11551231 maximizeButtonShown: (maximumWidth == 0 || maximumWidth >= appContainer.width) &&
11561232 (maximumHeight == 0 || maximumHeight >= appContainer.height)
11571233 overlayShown: touchControls.overlayShown
@@ -1179,6 +1255,7 @@ AbstractStage {
11791255
11801256 property real angle: 0
11811257 property real itemScale: 1
1258+ onAngleChanged: print("decoratedWindowd", model.application.appId, "angle", angle)
11821259 transform: [
11831260 Scale {
11841261 origin.x: 0
@@ -1194,6 +1271,10 @@ AbstractStage {
11941271 ]
11951272 }
11961273
1274+ OpacityMask {
1275+ id: opacityEffect
1276+ anchors.fill: decoratedWindow
1277+ }
11971278
11981279 WindowControlsOverlay {
11991280 id: touchControls