on xenial help sign in
spread redone! lookin goooood
commit88ced031b09cbfa4c5dcdd1772bcb31c7fc6a1ae authorMichael Zanetti <michael.zanetti@canonical.com> dateThu 14 Jul 2016, 15:54 (10 years ago) parentfb7fcf9
12 files changed+415−208
debian/changelog+6 po/unity8.pot+1−1 qml/Components/FloatingFlickable.qml+2 qml/Stages/DecoratedWindow.qml+1−1 qml/Stages/DesktopStage.qml+53−38 qml/Stages/Spread/BezierCurve.qml+29 qml/Stages/Spread/KeySpline.js+67 qml/Stages/Spread/MathUtils.js+74 qml/Stages/Spread/Spread.qml+95 qml/Stages/Spread/SpreadMaths.qml+62 qml/Stages/Spread/cubic-bezier.js+25 qml/Stages/SpreadMaths.qml−168
debian/changelog+6
@@ -1,3 +1,9 @@
1+unity8 (8.12+16.10.20160707-0ubuntu1local~1468504442) UNRELEASED; urgency=medium
2+
3+ * ''
4+
5+ -- Michael Zanetti <michael.zanetti@canonical.com> Thu, 14 Jul 2016 15:54:05 +0200
6+
17unity8 (8.12+16.10.20160707-0ubuntu1) yakkety; urgency=medium
28
39 [ Michael Zanetti ]
po/unity8.pot+1−1
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2016-07-12 11:45+0200\n"
11+"POT-Creation-Date: 2016-07-14 15:54+0200\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
qml/Components/FloatingFlickable.qml+2
@@ -34,6 +34,8 @@ Item {
3434 property alias contentX: flickable.contentX
3535 property alias contentY: flickable.contentY
3636 property alias direction: swipeArea.direction
37+ property alias leftMargin: flickable.leftMargin
38+ property alias rightMargin: flickable.rightMargin
3739
3840 MouseEventGenerator {
3941 id: mouseEventGenerator
qml/Stages/DecoratedWindow.qml+1−1
@@ -175,5 +175,5 @@ FocusScope {
175175 ]
176176 }
177177
178− Rectangle { anchors.fill: parent; color: "blue"; opacity: .3 }
178+// Rectangle { anchors.fill: parent; color: "blue"; opacity: .3 }
179179}
qml/Stages/DesktopStage.qml+53−38
@@ -22,6 +22,7 @@ import "../Components"
2222import Utils 0.1
2323import Ubuntu.Gestures 0.1
2424import GlobalShortcut 1.0
25+import "Spread"
2526
2627AbstractStage {
2728 id: root
@@ -313,6 +314,12 @@ AbstractStage {
313314 ]
314315 onStateChanged: print("spread going to state:", state)
315316
317+ Spread {
318+ id: spreadItem
319+ anchors.fill: appContainer
320+ totalItemCount: appRepeater.count
321+ z: 10
322+ }
316323
317324 FocusScope {
318325 id: appContainer
@@ -689,8 +696,8 @@ AbstractStage {
689696
690697 SpreadMaths {
691698 id: spreadMaths
699+ spread: spreadItem
692700 itemIndex: index
693− totalItems: appRepeater.count
694701 flickable: floatingFlickable
695702 }
696703 StageMaths {
@@ -710,8 +717,8 @@ AbstractStage {
710717 sceneWidth: appContainer.width - root.leftMargin
711718 sceneHeight: appContainer.height
712719 progress: 0
713− targetX: spreadMaths.animatedX
714− targetAngle: spreadMaths.animatedAngle
720+ targetX: spreadMaths.targetX
721+ targetAngle: spreadMaths.targetAngle
715722 }
716723
717724// onXChanged: if (model.application.appId == "unity8-dash") print("dash moved to", x)
@@ -720,21 +727,23 @@ AbstractStage {
720727 states: [
721728 State {
722729 name: "spread"; when: root.state == "spread"
723− PropertyChanges {
724− target: appDelegate;
725− x: spreadMaths.animatedX
726− y: spreadMaths.animatedY
727− z: index
728− height: spreadMaths.tileSize + windowInfoItem.height + units.gu(2)
729− requestedWidth: decoratedWindow.oldRequestedWidth
730− requestedHeight: decoratedWindow.oldRequestedHeight
731− }
732730 PropertyChanges {
733731 target: decoratedWindow;
734732 showDecoration: false;
735− angle: spreadMaths.animatedAngle
733+ angle: spreadMaths.targetAngle
734+ itemScale: spreadMaths.scale
735+ scaleToPreviewSize: spreadItem.stackHeight
736736 scaleToPreview: true
737737 }
738+ PropertyChanges {
739+ target: appDelegate
740+ x: spreadMaths.targetX
741+ y: spreadMaths.targetY
742+ z: index
743+ height: spreadItem.spreadItemHeight
744+ requestedWidth: decoratedWindow.oldRequestedWidth
745+ requestedHeight: decoratedWindow.oldRequestedHeight
746+ }
738747 PropertyChanges { target: inputBlocker; enabled: true }
739748 PropertyChanges { target: windowInfoItem; opacity: 1 }
740749 },
@@ -840,6 +849,7 @@ AbstractStage {
840849 requestedHeight: appDelegate.windowedHeight
841850 }
842851 PropertyChanges { target: touchControls; enabled: true }
852+ PropertyChanges { target: resizeArea; enabled: true }
843853 },
844854 State {
845855 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
@@ -930,25 +940,19 @@ AbstractStage {
930940 },
931941 Transition {
932942 to: "spread"
943+ // DecoratedWindow wants the sceleToPreviewSize set before enabling scaleToPreview
944+ PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
945+ PropertyAction { target: decoratedWindow; property: "scaleToPreview" }
933946 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,height"; duration: priv.animationDuration }
934− UbuntuNumberAnimation { target: decoratedWindow; properties: "width,height,angle,baseScale"; duration: priv.animationDuration }
947+ UbuntuNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle"; duration: priv.animationDuration }
935948 },
936949 Transition {
937950 from: "spread"; to: "staged,normal"
938951 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,height"; duration: priv.animationDuration }
939− UbuntuNumberAnimation { target: decoratedWindow; properties: "angle,width,height,baseScale"; duration: priv.animationDuration }
952+ UbuntuNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle"; duration: priv.animationDuration }
940953 },
941954 Transition {
942− from: "normal"; to: "staged";
943− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedWidth,requestedHeight"; duration: priv.animationDuration }
944− },
945− Transition {
946− from: "normal,spread"; to: "staged"
947− UbuntuNumberAnimation { target: appDelegate; properties: "x,y"; duration: priv.animationDuration }
948− UbuntuNumberAnimation { target: appDelegate; properties: "requestedWidth,requestedHeight"; duration: priv.animationDuration }
949− },
950− Transition {
951− from: "nromal,staged"; to: "stagedWithSideStage"
955+ from: "normal,staged"; to: "stagedWithSideStage"
952956 UbuntuNumberAnimation { target: appDelegate; properties: "x,y"; duration: priv.animationDuration }
953957 UbuntuNumberAnimation { target: appDelegate; properties: "requestedWidth,requestedHeight"; duration: priv.animationDuration }
954958 }
@@ -983,6 +987,7 @@ AbstractStage {
983987 screenWidth: appContainer.width
984988 screenHeight: appContainer.height
985989 leftMargin: root.leftMargin
990+ enabled: false
986991
987992 onPressed: {
988993 print("***** focusing because of resize area press", model.application.appId)
@@ -1047,8 +1052,8 @@ AbstractStage {
10471052 property real itemScale: 1
10481053 transform: [
10491054 Scale {
1050− origin.x: itemScaleOriginX
1051− origin.y: itemScaleOriginY
1055+ origin.x: 0
1056+ origin.y: decoratedWindow.implicitHeight / 2
10521057 xScale: decoratedWindow.itemScale
10531058 yScale: decoratedWindow.itemScale
10541059 },
@@ -1094,11 +1099,7 @@ AbstractStage {
10941099 priv.goneToSpread = false;
10951100 }
10961101 }
1097−// Rectangle {
1098−// anchors.fill: parent
1099−// color: "blue"
1100−// opacity: .4
1101−// }
1102+// Rectangle { anchors.fill: parent; color: "blue"; opacity: .4 }
11021103
11031104 WindowInfoItem {
11041105 id: windowInfoItem
@@ -1138,11 +1139,17 @@ AbstractStage {
11381139
11391140 FloatingFlickable {
11401141 id: floatingFlickable
1141− anchors.fill: parent
1142+ anchors.fill: appContainer
11421143 enabled: false
1144+ contentWidth: spreadItem.spreadTotalWidth
1145+ rightMargin: leftMargin
1146+ leftMargin: {
1147+ var stepOnXAxis = 1 / spreadItem.visibleItemCount
1148+ var distanceToIndex1 = spreadItem.curve.getYFromX(stepOnXAxis)
1149+ var middlePartDistance = spreadItem.curve.getYFromX(0.5 + stepOnXAxis/2) - spreadItem.curve.getYFromX(0.5 - stepOnXAxis/2)
1150+ return 1.2 * (middlePartDistance - distanceToIndex1) * spreadItem.spreadWidth
1151+ }
11431152
1144− property int minContentWidth: 6 * Math.min(height / 4, width / 5)
1145− contentWidth: Math.max(6, appRepeater.count) * Math.min(height / 4, width / 5)
11461153 }
11471154
11481155 PropertyAnimation {
@@ -1191,8 +1198,12 @@ AbstractStage {
11911198 if (gesturePoints[gesturePoints.length - 1] < -root.width / 2) {
11921199 priv.goneToSpread = true;
11931200 } else {
1194− appRepeater.itemAt(0).playHidingAnimation()
1195− appRepeater.itemAt(1).playFocusAnimation()
1201+ if (appRepeater.count > 1) {
1202+ appRepeater.itemAt(0).playHidingAnimation()
1203+ appRepeater.itemAt(1).playFocusAnimation()
1204+ } else {
1205+ appRepeater.itemAt(0).playFocusAnimation();
1206+ }
11961207 }
11971208
11981209// // Ok. The user released. Find out if it was a one-way movement.
@@ -1295,6 +1306,10 @@ AbstractStage {
12951306
12961307 Label {
12971308 anchors { left: parent.left; top: parent.top; margins: units.gu(4) }
1298− text: "sidestage z: " + sideStage.z
1309+ text: "spreadWidth: " + spreadItem.spreadWidth
1310+ + "\n spreadItemWidth: " + spreadItem.spreadItemWidth
1311+ + "\n flickableContentWidth: " + floatingFlickable.contentWidth
1312+ + "\n visibleItemCount: " + spreadItem.visibleItemCount
1313+ + "\n contentTopMargin: " + spreadItem.contentTopMargin
12991314 }
13001315}
qml/Stages/Spread/BezierCurve.qmlnew file+29
@@ -0,0 +1,29 @@
1+import QtQuick 2.4
2+import Ubuntu.Components 1.3
3+import QtQuick.Window 2.2
4+import 'cubic-bezier.js' as Bezier
5+import 'KeySpline.js' as KeySpline
6+
7+Item {
8+ id: root
9+
10+ property var controlPoint1: {'x':0, 'y':0}
11+ property var controlPoint2: {'x':0, 'y':0}
12+ property var controlPoint3: {'x':0.58, 'y':1}
13+ property var controlPoint4: {'x':1, 'y':1}
14+
15+ function getValues(t) {
16+ if (t<0) t=0
17+ else if (t>1)t=1
18+
19+ return Bezier.getBezier(t, controlPoint1, controlPoint2, controlPoint3, controlPoint4)
20+ }
21+
22+ function getYFromX(x) {
23+ var spline = new KeySpline.keySpline(controlPoint2.x, controlPoint2.y, controlPoint3.x, controlPoint3.y)
24+ if (x<0) x=0
25+ else if (x>1)x=1
26+
27+ return spline.get(x)
28+ }
29+}
qml/Stages/Spread/KeySpline.jsnew file+67
@@ -0,0 +1,67 @@
1+.pragma library
2+
3+/** MIT License
4+ *
5+ * KeySpline - use bezier curve for transition easing function
6+ * Copyright (c) 2012 Gaetan Renaudeau <renaudeau.gaetan@gmail.com>
7+ *
8+ * Permission is hereby granted, free of charge, to any person obtaining a
9+ * copy of this software and associated documentation files (the "Software"),
10+ * to deal in the Software without restriction, including without limitation
11+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12+ * and/or sell copies of the Software, and to permit persons to whom the
13+ * Software is furnished to do so, subject to the following conditions:
14+ *
15+ * The above copyright notice and this permission notice shall be included in
16+ * all copies or substantial portions of the Software.
17+ *
18+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24+ * DEALINGS IN THE SOFTWARE.
25+ */
26+/**
27+* KeySpline - use bezier curve for transition easing function
28+* is inspired from Firefox's nsSMILKeySpline.cpp
29+* Usage:
30+* var spline = new KeySpline(0.25, 0.1, 0.25, 1.0)
31+* spline.get(x) => returns the easing value | x must be in [0, 1] range
32+*/
33+function keySpline (mX1, mY1, mX2, mY2) {
34+
35+ this.get = function(aX) {
36+ if (mX1 == mY1 && mX2 == mY2) return aX; // linear
37+ return CalcBezier(GetTForX(aX), mY1, mY2);
38+ }
39+
40+ function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
41+ function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
42+ function C(aA1) { return 3.0 * aA1; }
43+
44+ // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
45+ function CalcBezier(aT, aA1, aA2) {
46+ return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT;
47+ }
48+
49+ // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
50+ function GetSlope(aT, aA1, aA2) {
51+ return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
52+ }
53+
54+ function GetTForX(aX) {
55+ // Newton raphson iteration
56+ var aGuessT = aX;
57+ for (var i = 0; i < 4; ++i) {
58+ var currentSlope = GetSlope(aGuessT, mX1, mX2);
59+ if (currentSlope == 0.0) return aGuessT;
60+ var currentX = CalcBezier(aGuessT, mX1, mX2) - aX;
61+ aGuessT -= currentX / currentSlope;
62+ }
63+ return aGuessT;
64+ }
65+}
66+
67+
qml/Stages/Spread/MathUtils.jsnew file+74
@@ -0,0 +1,74 @@
1+.pragma library
2+
3+/**
4+ * From processing.js: https://raw.githubusercontent.com/processing-js/processing-js/v1.4.8/processing.js
5+ *
6+ * Re-map a number from one range to another. In the example above, the number
7+ * '25' is converted from a value in the range 0..100 into a value that
8+ * ranges from the left edge (0) to the right edge (width) of the screen.
9+ * Numbers outside the range are not clamped to 0 and 1, because out-of-range
10+ * values are often intentional and useful.
11+ *
12+ * @param {Number} value The incoming value to be converted
13+ * @param {Number} istart Lower bound of the value's current range
14+ * @param {Number} istop Upper bound of the value's current range
15+ * @param {Number} ostart Lower bound of the value's target range
16+ * @param {Number} ostop Upper bound of the value's target range
17+ *
18+ * @returns {Number}
19+ */
20+function map(value, istart, istop, ostart, ostop) {
21+ return ostart + (ostop - ostart) * ((value - istart) / (istop - istart))
22+}
23+
24+/**
25+ * Return a value which is always between `min` and `max`
26+ *
27+ * @param {Number} value The current value
28+ * @param {Number} min The minimum value
29+ * @param {Number} max The maximum value
30+ *
31+ * @returns {Number}
32+ */
33+function clamp(value, min, max) {
34+ if (value < min) return min
35+ if (value > max) return max
36+ return value
37+}
38+
39+// calculates the distance from the middle of one rect to middle of other rect
40+function rectDistance(rect1, rect2) {
41+ return pointDistance(Qt.point(rect1.x + rect1.width / 2, rect1.y + rect1.height / 2),
42+ Qt.point(rect2.x + rect2.width / 2, rect2.y + rect2.height / 2))
43+}
44+
45+// calculates the distance between two points
46+function pointDistance(point1, point2) {
47+ return Math.sqrt(Math.pow(point1.x - point2.x, 2) +
48+ Math.pow(point1.y - point2.y, 2)
49+ )
50+}
51+
52+// from http://stackoverflow.com/questions/14616829/java-method-to-find-the-rectangle-that-is-the-intersection-of-two-rectangles-usi
53+function intersectionRect(r1, r2) {
54+ var xmin = Math.max(r1.x, r2.x);
55+ var xmax1 = r1.x + r1.width;
56+ var xmax2 = r2.x + r2.width;
57+ var xmax = Math.min(xmax1, xmax2);
58+ var out = {x:0, y:0, width:0, height:0}
59+ if (xmax > xmin) {
60+ var ymin = Math.max(r1.y, r2.y);
61+ var ymax1 = r1.y + r1.height;
62+ var ymax2 = r2.y + r2.height;
63+ var ymax = Math.min(ymax1, ymax2);
64+ if (ymax > ymin) {
65+ out.x = xmin;
66+ out.y = ymin;
67+ out.width = xmax - xmin;
68+ out.height = ymax - ymin;
69+ }
70+ }
71+ return out;
72+}
73+
74+function easeOutCubic(t) { return (--t)*t*t+1 }
qml/Stages/Spread/Spread.qmlnew file+95
@@ -0,0 +1,95 @@
1+import QtQuick 2.4
2+import Ubuntu.Components 1.3
3+import "MathUtils.js" as MathUtils
4+
5+Item {
6+ id: root
7+
8+ // Information about the environment
9+ property int totalItemCount: 0
10+
11+ // some config options
12+ property real contentMargin: 0.16 * root.height
13+ property real contentTopMargin: 0.65 * contentMargin
14+ property real contentBottomMargin: 0.35 * contentMargin
15+ property real windowTitleTopMargin: 3/4 * (contentTopMargin - windowTitle.height)
16+ property int stackItemCount: 3
17+ property real leftRotationAngle: 22
18+ property real rightRotationAngle: 32
19+ property real leftStackScale: .82
20+ property real rightStackScale: 1
21+
22+
23+ // Calculated stuff
24+ readonly property real leftStackXPos: 0.03 * root.width
25+ readonly property real rightStackXPos: root.width - 1.5 * leftStackXPos
26+
27+ readonly property real stackHeight: spreadItemHeight - appInfoHeight
28+ readonly property real stackWidth: Math.min(leftStackXPos/3, units.gu(1.5))
29+
30+ readonly property real spreadWidth: rightStackXPos - leftStackXPos
31+ readonly property real spreadHeight: root.height
32+ readonly property real spreadItemHeight: 0.84 * spreadHeight
33+ readonly property real spreadItemWidth: stackHeight
34+
35+ readonly property real dynamicLeftRotationAngle: leftRotationAngle * rotationAngleFactor
36+ readonly property real dynamicRightRotationAngle: rightRotationAngle * rotationAngleFactor
37+
38+ readonly property real appInfoHeight: {
39+ var screenHeightReferencePoint = 40 // ref screen height in gu
40+ var valueAtReferencePoint = 0.17 // of screen height at the reference point
41+ var appInfoHeightValueChange = -0.0014 // units / gu
42+ var minAppInfoHeight = 0.08
43+ var maxAppInfoHeight = 0.2
44+ var screenHeightInGU = root.height / units.gu(1) // screenHeight in gu
45+
46+ return MathUtils.clamp(valueAtReferencePoint + appInfoHeightValueChange * (screenHeightInGU - screenHeightReferencePoint), minAppInfoHeight, maxAppInfoHeight) * root.height
47+ }
48+
49+ property real rotationAngleFactor: {
50+ var spreadHeightReferencePoint = 28 // reference spread height in gu
51+ var valueAtReferencePoint = 1.3
52+ var rotationAngleValueChange = -0.008 // units / gu
53+ var minRotationAngleFactor = 0.6
54+ var maxRotationAngleFactor = 1.5
55+ var spreadHeightInGU = spreadHeight / units.gu(1)
56+
57+ return MathUtils.clamp(valueAtReferencePoint + rotationAngleValueChange * (spreadHeightInGU - spreadHeightReferencePoint), minRotationAngleFactor, maxRotationAngleFactor)
58+ }
59+ readonly property real itemOverlap: {
60+ var spreadAspectRatioReferencePoint = 1.0 // ref screen height in gu
61+ var valueAtReferencePoint = 0.74 // of screen height at the reference point
62+ var itemOverlapValueChange = -0.068
63+ var minOverlap = 0.55
64+ var maxOverlap = 0.82
65+ var spreadAspectRatio = spreadWidth / stackHeight // spread stack aspect ratio (app info not included)
66+
67+ return MathUtils.clamp(valueAtReferencePoint + itemOverlapValueChange * (spreadAspectRatio - spreadAspectRatioReferencePoint), minOverlap, maxOverlap)
68+ }
69+
70+ readonly property real visibleItemCount: (spreadWidth / spreadItemWidth) / (1 - itemOverlap)
71+
72+ readonly property real spreadTotalWidth: totalItemCount * spreadWidth / visibleItemCount
73+
74+ readonly property real centeringOffset: Math.max(spreadWidth - spreadTotalWidth ,0) / (2 * spreadWidth)
75+
76+
77+ readonly property var curve: BezierCurve {
78+ controlPoint2: {'x': 0.19, 'y': 0.00}
79+ controlPoint3: {'x': 0.91, 'y': 1.00}
80+ }
81+
82+ Label {
83+ id: windowTitle
84+
85+ width: Math.min(implicitWidth, 0.5*root.width)
86+ elide: Qt.ElideMiddle
87+ anchors.horizontalCenter: parent.horizontalCenter
88+ y: windowTitleTopMargin
89+ //y: priv.spreadTopMargin + priv.contentTopMargin + settings.spreadOffset + settings.titleOffset - height - (priv.contentTopMargin - height) / 4
90+ visible: height < priv.contentTopMargin
91+ text: 'focused window title'
92+ fontSize: root.height < units.gu(85) ? 'medium' : 'large'
93+ color: 'white'
94+ }
95+}
qml/Stages/Spread/SpreadMaths.qmlnew file+62
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (C) 2015 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 Utils 0.1
20+import "MathUtils.js" as MathUtils
21+
22+Item {
23+ id: root
24+ anchors { left: parent.left; top: parent.top; margins: units.gu(1) }
25+
26+ // Information about the environment
27+ property Item flickable: null
28+ property Spread spread: null
29+ property int itemIndex: 0
30+
31+
32+ // Internal
33+ property real spreadPosition: itemIndex/spread.visibleItemCount - flickable.contentX/spread.spreadWidth // 0 -> left stack, 1 -> right stack
34+ property real leftStackingProgress: MathUtils.clamp(MathUtils.map(spreadPosition, 0, -spread.stackItemCount/spread.visibleItemCount , 0, 1), 0, 1)
35+ property real rightStackingProgress: MathUtils.clamp(MathUtils.map(spreadPosition, 1, 1 + spread.stackItemCount/spread.visibleItemCount , 0, 1), 0, 1)
36+ property real stackingX: (MathUtils.easeOutCubic(rightStackingProgress) - MathUtils.easeOutCubic(leftStackingProgress)) * spread.stackWidth
37+
38+
39+
40+ // Output
41+ readonly property int targetX: spread.leftStackXPos +
42+ spread.spreadWidth * spread.curve.getYFromX(spreadPosition + spread.centeringOffset) +
43+ stackingX
44+
45+ readonly property int targetY: spread.contentTopMargin
46+
47+ readonly property real targetAngle: MathUtils.clamp(
48+ MathUtils.map(targetX, spread.leftStackXPos, spread.rightStackXPos, spread.dynamicLeftRotationAngle, spread.dynamicRightRotationAngle),
49+ Math.min(spread.dynamicLeftRotationAngle, spread.dynamicRightRotationAngle), Math.max(spread.dynamicLeftRotationAngle, spread.dynamicRightRotationAngle))
50+
51+
52+ readonly property real scale: MathUtils.clamp(
53+ MathUtils.map(spreadPosition, 0, 1, spread.leftStackScale, spread.rightStackScale),
54+ spread.leftStackScale, spread.rightStackScale)
55+
56+ readonly property real closeIconOffset: (scale - 1) * (-root.spreadHeight / 2)
57+
58+ readonly property real tileInfoOpacity: 1
59+
60+ readonly property bool itemVisible: true
61+ readonly property real shadowOpacity: 1
62+}
qml/Stages/Spread/cubic-bezier.jsnew file+25
@@ -0,0 +1,25 @@
1+
2+//====================================\\
3+// 13thParallel.org Beziér Curve Code \\
4+// by Dan Pupius (www.pupius.net) \\
5+//====================================\\
6+
7+
8+var coord = function (x,y) {
9+ if(!x) var x=0;
10+ if(!y) var y=0;
11+ return {x: x, y: y};
12+}
13+
14+function B4(t) { return t*t*t }
15+function B3(t) { return 3*t*t*(1-t) }
16+function B2(t) { return 3*t*(1-t)*(1-t) }
17+function B1(t) { return (1-t)*(1-t)*(1-t) }
18+
19+var getBezier = function(percent,C1,C2,C3,C4) {
20+ var pos = new coord();
21+ pos.x = C1.x*B1(percent) + C2.x*B2(percent) + C3.x*B3(percent) + C4.x*B4(percent);
22+ pos.y = C1.y*B1(percent) + C2.y*B2(percent) + C3.y*B3(percent) + C4.y*B4(percent);
23+ return pos;
24+}
25+
qml/Stages/SpreadMaths.qmldeleted−168
@@ -1,168 +0,0 @@
1−/*
2− * Copyright (C) 2015 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 Utils 0.1
20−
21−Item {
22− id: root
23− anchors { left: parent.left; top: parent.top; margins: units.gu(1) }
24−
25− // Information about the transformed item
26− property int itemIndex: 0
27− property real itemHeight: units.gu(10)
28−
29− // Information about the environment
30− property int totalItems: 0
31− property Item flickable: null
32− property int sceneHeight: units.gu(20)
33−
34− // Spread properties
35− property real spreadHeight: sceneHeight * 0.4
36− property int spreadBottomOffset: sceneHeight * 0.18
37− property int foldingAreaWidth: flickableWidth * 0.2
38− property int maxVisibleItems: 7
39− property int margins: flickableWidth * 0.05
40− property real stackScale: 0.1
41− property int leftEndFoldedAngle: 70
42− property int rightEndFoldedAngle: 65
43− property int unfoldedAngle: 30
44− property int stackWidth: flickableWidth * 0.01
45−
46−
47− // Internal
48− readonly property int flickableWidth: flickable ? flickable.width : 0
49− readonly property int flickableContentWidth: flickable ? flickable.contentWidth: 0
50− readonly property real flickableProgress: flickable ? flickable.contentX / (flickable.contentWidth - flickableWidth) : 0
51−
52− readonly property int contentWidth: flickableWidth - root.margins * 2
53−
54− readonly property int distance: (flickableContentWidth - (margins * 2) - (foldingAreaWidth * 2)) / (totalItems - 2)
55− readonly property int startPos: margins + foldingAreaWidth + (itemIndex - 1) * distance
56− readonly property int linearX: startPos - flickableProgress * (flickableContentWidth - flickableWidth)
57−
58− readonly property int leftFoldingAreaX: margins + foldingAreaWidth
59− readonly property int rightFoldingAreaX: flickableWidth - foldingAreaWidth - margins
60−
61− readonly property real leftFoldingAreaProgress: linearAnimation(leftFoldingAreaX, margins, 0, 1, linearX)
62− readonly property real rightFoldingAreaProgress: linearAnimation(rightFoldingAreaX, flickableWidth - margins, 0, 1, linearX)
63−
64− readonly property real limitedLeftProgress: Math.min(2, leftFoldingAreaProgress)
65− readonly property real limitedRightProgress: Math.min(2, rightFoldingAreaProgress)
66−
67− readonly property real middleSectionProgress: (linearX - margins - foldingAreaWidth) / (flickableWidth - (margins + foldingAreaWidth) * 2)
68−
69− // Output
70− readonly property int tileSize: units.gu(40)
71−
72− readonly property int animatedX: {
73− if (itemIndex == 0) print("leftFoldingAreaProgress", leftFoldingAreaProgress, "rightFoldingAreaProgress", rightFoldingAreaProgress)
74− if (leftFoldingAreaProgress > 4) { // Stop it at the edge
75− return margins;
76− }
77− if (leftFoldingAreaProgress > 2) { // move it slowly through the stack
78− return linearAnimation(2, 4, margins + stackWidth, margins, leftFoldingAreaProgress)
79− }
80− if (leftFoldingAreaProgress > 1 && itemIndex == 0) {
81− // The leftmost runs faster... make it stop before the stack and wait for others
82− return margins + stackWidth;
83− }
84−
85− if (leftFoldingAreaProgress > 0) { // slow it down in a curve
86− if (itemIndex == 0) { // except if it's the leftmost. that one goes straigt
87− return linearAnimation(0, 1, leftFoldingAreaX, margins + stackWidth, leftFoldingAreaProgress)
88− }
89− return linearAnimation(0, 1, leftFoldingAreaX, margins + stackWidth, leftEasing.value)
90− }
91− // same for the right side stack... mostly... don't need to treat the rightmost special...
92− if (rightFoldingAreaProgress > 4) {
93− return flickableWidth - margins
94− }
95− if (rightFoldingAreaProgress > 2) {
96− return linearAnimation(2, 4, flickableWidth - margins - stackWidth, flickableWidth - margins, rightFoldingAreaProgress)
97− }
98−
99− if (rightFoldingAreaProgress > 0) {
100− print("returning this")
101− return linearAnimation(0, 1, rightFoldingAreaX, flickableWidth - margins - stackWidth, rightEasing.value);
102− }
103−
104− return linearX
105− }
106−
107− readonly property int animatedY: sceneHeight - itemHeight - spreadBottomOffset
108−
109− readonly property real animatedAngle: {
110− if (limitedLeftProgress > 0) {
111− // Leftmost is special...
112− if (index == 0) {
113− if (limitedLeftProgress < 1) {
114− return unfoldedAngle;
115− } else {
116− return linearAnimation(1, 2, unfoldedAngle, leftEndFoldedAngle, limitedLeftProgress)
117− }
118− }
119− return linearAnimation(0, 2, unfoldedAngle, leftEndFoldedAngle, limitedLeftProgress)
120− } else if (limitedRightProgress > 0) {
121− return linearAnimation(0, 1, unfoldedAngle, rightEndFoldedAngle, rightEasing.value)
122− } else {
123− return unfoldedAngle
124− }
125− }
126−
127− readonly property real scale: limitedLeftProgress > 0 ?
128− linearAnimation(0, 1, 1, 1 + stackScale, leftEasing.value)
129− : limitedRightProgress > 0 ?
130− linearAnimation(0, 1, 1, 1 + stackScale, rightEasing.value)
131− : 0.95 + Math.abs(middleSectionProgress - 0.5) * 0.1
132−
133− readonly property real closeIconOffset: (scale - 1) * (-root.spreadHeight / 2)
134−
135− readonly property real tileInfoOpacity: leftFoldingAreaProgress > 0 ?
136− linearAnimation(1, 1.5, 1, 0, leftFoldingAreaProgress)
137− : rightFoldingAreaProgress > 0 ?
138− linearAnimation(1, 1.5, 1, 0, rightFoldingAreaProgress)
139− : 1
140−
141− readonly property bool itemVisible: itemIndex == totalItems - 1 ? true : leftFoldingAreaProgress < 5 && rightFoldingAreaProgress < 5
142− readonly property real shadowOpacity: itemIndex == totalItems -1 ?
143− 1
144− : leftFoldingAreaProgress > 3 ?
145− linearAnimation(3, 3.5, 1, 0, leftFoldingAreaProgress)
146− : rightFoldingAreaProgress > 3 ?
147− linearAnimation(3, 3.5, 1, 0, rightFoldingAreaProgress)
148− : 1
149−
150−
151− // Helpers
152− function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {
153− // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff
154− return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
155− }
156−
157− EasingCurve {
158− id: leftEasing
159− type: EasingCurve.OutSine
160− progress: limitedLeftProgress / 2 // OutSine starts with twice the speed. slow it down.
161− }
162−
163− EasingCurve {
164− id: rightEasing
165− type: EasingCurve.OutSine
166− progress: limitedRightProgress / 2 // OutSine starts with twice the speed. slow it down.
167− }
168−}