@@ -1,1201 +0,0 @@
1−/*
2− * Copyright (C) 2014-2016 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 Ubuntu.Gestures 0.1
20−import Unity.Application 0.1
21−import Utils 0.1
22−import Powerd 0.1
23−import "../Components"
24−
25−AbstractStage {
26− id: root
27− objectName: "stages"
28− anchors.fill: parent
29−
30− // <tutorial-hacks> The Tutorial looks into our implementation details
31− property alias sideStageVisible: spreadView.sideStageVisible
32− property alias sideStageWidth: spreadView.sideStageWidth
33− // The stage the currently focused surface is in
34− property int stageFocusedSurface: priv.focusedAppDelegate ? priv.focusedAppDelegate.stage : ApplicationInfoInterface.MainStage
35− // </tutorial-hacks>
36−
37− paintBackground: spreadView.shiftedContentX !== 0
38−
39− // Functions to be called from outside
40− function updateFocusedAppOrientation() {
41− var mainStageIndex = root.topLevelSurfaceList.indexForId(priv.mainStageItemId);
42−
43− if (priv.mainStageItemId && mainStageIndex >= 0 && mainStageIndex < spreadRepeater.count) {
44− spreadRepeater.itemAt(mainStageIndex).matchShellOrientation();
45− }
46−
47− for (var i = 0; i < spreadRepeater.count; ++i) {
48−
49− if (i === mainStageIndex) {
50− continue;
51− }
52−
53− var spreadDelegate = spreadRepeater.itemAt(i);
54−
55− var delta = spreadDelegate.appWindowOrientationAngle - root.shellOrientationAngle;
56− if (delta < 0) { delta += 360; }
57− delta = delta % 360;
58−
59− var supportedOrientations = spreadDelegate.supportedOrientations;
60− if (supportedOrientations === Qt.PrimaryOrientation) {
61− supportedOrientations = spreadDelegate.orientations.primary;
62− }
63−
64− if (delta === 180 && (supportedOrientations & spreadDelegate.shellOrientation)) {
65− spreadDelegate.matchShellOrientation();
66− }
67− }
68− }
69− function updateFocusedAppOrientationAnimated() {
70− var mainStageIndex = root.topLevelSurfaceList.indexForId(priv.mainStageItemId);
71− if (priv.mainStageItemId && mainStageIndex >= 0 && mainStageIndex < spreadRepeater.count) {
72− spreadRepeater.itemAt(mainStageIndex).animateToShellOrientation();
73− }
74−
75− var sideStageIndex = root.topLevelSurfaceList.indexForId(priv.sideStageItemId);
76− if (sideStageIndex >= 0 && sideStageIndex < spreadRepeater.count) {
77− spreadRepeater.itemAt(sideStageIndex).matchShellOrientation();
78− }
79− }
80−
81− function pushRightEdge(amount) {
82− if (spreadView.contentX == -spreadView.shift) {
83− edgeBarrier.push(amount);
84− }
85− }
86−
87− function closeFocusedDelegate() {
88− if (priv.focusedAppDelegate && priv.focusedAppDelegate.closeable) {
89− priv.focusedAppDelegate.closed();
90− }
91− }
92−
93− orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
94−
95− mainApp: {
96− if (priv.mainStageItemId > 0) {
97− var index = root.topLevelSurfaceList.indexForId(priv.mainStageItemId);
98− return root.topLevelSurfaceList.applicationAt(index);
99− } else {
100− return null;
101− }
102− }
103−
104− supportedOrientations: {
105− if (mainApp) {
106− var orientations = mainApp.supportedOrientations;
107− orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation;
108− if (priv.sideStageItemId && !spreadView.surfaceDragging) {
109− // If we have a sidestage app, support Portrait orientation
110− // so that it will switch the sidestage app to mainstage on rotate to portrait
111− orientations |= Qt.PortraitOrientation|Qt.InvertedPortraitOrientation;
112− }
113− return orientations;
114− } else {
115− // we just don't care
116− return Qt.PortraitOrientation |
117− Qt.LandscapeOrientation |
118− Qt.InvertedPortraitOrientation |
119− Qt.InvertedLandscapeOrientation;
120− }
121− }
122−
123− // How far left the stage has been dragged, used externally by tutorial code
124− dragProgress: spreadRepeater.count > 0 ? spreadRepeater.itemAt(0).animatedProgress : 0
125−
126− onWidthChanged: {
127− spreadView.selectedIndex = -1;
128− spreadView.phase = 0;
129− spreadView.contentX = -spreadView.shift;
130− }
131−
132− onInverseProgressChanged: {
133− // This can't be a simple binding because that would be triggered after this handler
134− // while we need it active before doing the anition left/right
135− spreadView.animateX = (inverseProgress == 0)
136− if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
137− // left edge drag released. Minimum distance is given by design.
138− if (priv.oldInverseProgress > units.gu(22)) {
139− root.applicationManager.requestFocusApplication("unity8-dash");
140− }
141− }
142− priv.oldInverseProgress = inverseProgress;
143− }
144−
145− onAltTabPressedChanged: {
146− if (!spreadEnabled) {
147− return;
148− }
149− if (altTabPressed) {
150− priv.highlightIndex = Math.min(spreadRepeater.count - 1, 1);
151− spreadView.snapToSpread();
152− } else {
153− for (var i = 0; i < spreadRepeater.count; i++) {
154− if (spreadRepeater.itemAt(i).zIndex === priv.highlightIndex) {
155− spreadView.snapTo(i);
156− return;
157− }
158− }
159− }
160− }
161−
162− FocusScope {
163− focus: root.altTabPressed
164−
165− Keys.onPressed: {
166− switch (event.key) {
167− case Qt.Key_Tab:
168− priv.highlightIndex = (priv.highlightIndex + 1) % spreadRepeater.count
169− break;
170− case Qt.Key_Backtab:
171− priv.highlightIndex = (priv.highlightIndex + spreadRepeater.count - 1) % spreadRepeater.count
172− break;
173− }
174− }
175− }
176−
177− Connections {
178− target: root.topLevelSurfaceList
179− onListChanged: priv.updateMainAndSideStageIndexes()
180− }
181−
182− QtObject {
183− id: priv
184− objectName: "stagesPriv"
185−
186− function updateMainAndSideStageIndexes() {
187− var choseMainStage = false;
188− var choseSideStage = false;
189−
190− if (!root.topLevelSurfaceList)
191− return;
192−
193− for (var i = 0; i < spreadRepeater.count && (!choseMainStage || !choseSideStage); ++i) {
194− var spreadDelegate = spreadRepeater.itemAt(i);
195− if (sideStage.shown && spreadDelegate.stage == ApplicationInfoInterface.SideStage
196− && !choseSideStage) {
197− priv.sideStageDelegate = spreadDelegate
198− priv.sideStageItemId = root.topLevelSurfaceList.idAt(i);
199− priv.sideStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
200− choseSideStage = true;
201− } else if (!choseMainStage && spreadDelegate.stage == ApplicationInfoInterface.MainStage) {
202− priv.mainStageDelegate = spreadDelegate;
203− priv.mainStageItemId = root.topLevelSurfaceList.idAt(i);
204− priv.mainStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
205− choseMainStage = true;
206− }
207− }
208− if (!choseMainStage) {
209− priv.mainStageDelegate = null;
210− priv.mainStageItemId = 0;
211− priv.mainStageAppId = "";
212− }
213− if (!choseSideStage) {
214− priv.sideStageDelegate = null;
215− priv.sideStageItemId = 0;
216− priv.sideStageAppId = "";
217− }
218− }
219−
220− property var focusedAppDelegate: null
221−
222− property bool mainAppOrientationChangesEnabled: false
223−
224− property real landscapeHeight: root.orientations.native_ == Qt.LandscapeOrientation ?
225− root.nativeHeight : root.nativeWidth
226−
227− property bool shellIsLandscape: root.shellOrientation === Qt.LandscapeOrientation
228− || root.shellOrientation === Qt.InvertedLandscapeOrientation
229−
230− property var mainStageDelegate: null
231− property var sideStageDelegate: null
232−
233− property int mainStageItemId: 0
234− property int sideStageItemId: 0
235−
236− property string mainStageAppId: ""
237− property string sideStageAppId: ""
238−
239− property int oldInverseProgress: 0
240−
241− property int highlightIndex: 0
242−
243− property bool focusedAppDelegateIsDislocated: focusedAppDelegate &&
244− (focusedAppDelegate.dragOffset !== 0 || focusedAppDelegate.xTranslateAnimating)
245− function evaluateOneWayFlick(gesturePoints) {
246− // Need to have at least 3 points to recognize it as a flick
247− if (gesturePoints.length < 3) {
248− return false;
249− }
250− // Need to have a movement of at least 2 grid units to recognize it as a flick
251− if (Math.abs(gesturePoints[gesturePoints.length - 1] - gesturePoints[0]) < units.gu(2)) {
252− return false;
253− }
254−
255− var oneWayFlick = true;
256− var smallestX = gesturePoints[0];
257− var leftWards = gesturePoints[1] < gesturePoints[0];
258− for (var i = 1; i < gesturePoints.length; i++) {
259− if ((leftWards && gesturePoints[i] >= smallestX)
260− || (!leftWards && gesturePoints[i] <= smallestX)) {
261− oneWayFlick = false;
262− break;
263− }
264− smallestX = gesturePoints[i];
265− }
266− return oneWayFlick;
267− }
268−
269− onHighlightIndexChanged: {
270− spreadView.contentX = highlightIndex * spreadView.contentWidth / (spreadRepeater.count + 2)
271− }
272−
273− readonly property bool sideStageEnabled: root.shellOrientation == Qt.LandscapeOrientation ||
274− root.shellOrientation == Qt.InvertedLandscapeOrientation
275− }
276−
277− Instantiator {
278− model: root.applicationManager
279− delegate: QtObject {
280− property var stateBinding: Binding {
281− readonly property bool isDash: model.application ? model.application.appId == "unity8-dash" : false
282− target: model.application
283− property: "requestedState"
284−
285− // NB: the first application clause is just to ensure we never get warnings for trying to access
286− // members of a null variable.
287− value: model.application &&
288− (
289− (isDash && root.keepDashRunning)
290− || (!root.suspended && (model.application.appId === priv.mainStageAppId
291− || model.application.appId === priv.sideStageAppId))
292− )
293− ? ApplicationInfoInterface.RequestedRunning
294− : ApplicationInfoInterface.RequestedSuspended
295− }
296−
297− property var lifecycleBinding: Binding {
298− target: model.application
299− property: "exemptFromLifecycle"
300− value: model.application
301− ? (!model.application.isTouchApp || isExemptFromLifecycle(model.application.appId))
302− : false
303− }
304− }
305− }
306−
307− Binding {
308− target: MirFocusController
309− property: "focusedSurface"
310− value: priv.focusedAppDelegate ? priv.focusedAppDelegate.focusedSurface : null
311− when: root.parent && !spreadRepeater.startingUp
312− }
313−
314− Flickable {
315− id: spreadView
316− objectName: "spreadView"
317− anchors.fill: parent
318− interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
319− contentWidth: spreadRow.width - shift
320− contentX: -shift
321−
322− property int tileDistance: units.gu(20)
323−
324− // This indicates when the spreadView is active. That means, all the animations
325− // are activated and tiles need to line up for the spread.
326− readonly property bool active: shiftedContentX > 0 || spreadDragArea.dragging
327−
328− // The flickable needs to fill the screen in order to get touch events all over.
329− // However, we don't want to the user to be able to scroll back all the way. For
330− // that, the beginning of the gesture starts with a negative value for contentX
331− // so the flickable wants to pull it into the view already. "shift" tunes the
332− // distance where to "lock" the content.
333− readonly property real shift: width / 2
334− readonly property real shiftedContentX: contentX + shift
335−
336− // Phase of the animation:
337− // 0: Starting from right edge, a new app (index 1) comes in from the right
338− // 1: The app has reached the first snap position.
339− // 2: The list is dragged further and snaps into the spread view when entering phase 2
340− property int phase
341−
342− readonly property int phase0Width: sideStageWidth
343− readonly property int phase1Width: sideStageWidth
344−
345− // Those markers mark the various positions in the spread (ratio to screen width from right to left):
346− // 0 - 1: following finger, snap back to the beginning on release
347− readonly property real positionMarker1: 0.2
348− // 1 - 2: curved snapping movement, snap to nextInStack on release
349− readonly property real positionMarker2: sideStageWidth / spreadView.width
350− // 2 - 3: movement follows finger, snaps to phase 2 (full spread) on release
351− readonly property real positionMarker3: 0.6
352− // passing 3, we detach movement from the finger and snap to phase 2 (full spread)
353− readonly property real positionMarker4: 0.8
354−
355− readonly property int startSnapPosition: phase0Width * 0.5
356− readonly property int endSnapPosition: phase0Width * 0.75
357− readonly property real snapPosition: 0.75
358−
359− property int selectedIndex: -1
360− property int draggedDelegateCount: 0
361− property int closingIndex: -1
362− property var selectedDelegate: selectedIndex !== -1 ? spreadRepeater.itemAt(selectedIndex) : null
363−
364− // <FIXME-contentX> Workaround Flickable's behavior of bringing contentX back between valid boundaries
365− // when resized. The proper way to fix this is refactoring PhoneStage so that it doesn't
366− // rely on having Flickable.contentX keeping an out-of-bounds value when it's set programatically
367− // (as opposed to having contentX reaching an out-of-bounds value through dragging, which will trigger
368− // the Flickable.boundsBehavior upon release).
369− onContentXChanged: {
370− if (!undoContentXReset()) {
371− forceItToRemainStillIfBeingResized();
372− }
373− }
374− onShiftChanged: { forceItToRemainStillIfBeingResized(); }
375− function forceItToRemainStillIfBeingResized() {
376− if (root.beingResized && contentX != -spreadView.shift) {
377− contentX = -spreadView.shift;
378− }
379− }
380− function undoContentXReset() {
381− if (contentWidth <= 0) {
382− contentWidthOnLastContentXChange = contentWidth;
383− lastContentX = contentX;
384− return false;
385− }
386−
387− if (contentWidth != contentWidthOnLastContentXChange
388− && lastContentX == -shift && contentX == 0) {
389− // Flickable is resetting contentX because contentWidth has changed. Undo it.
390− contentX = -shift;
391− return true;
392− }
393−
394− contentWidthOnLastContentXChange = contentWidth;
395− lastContentX = contentX;
396− return false;
397− }
398− property real contentWidthOnLastContentXChange: -1
399− property real lastContentX: 0
400− // </FIXME-contentX>
401−
402− property bool animateX: true
403− property bool beingResized: root.beingResized
404− onBeingResizedChanged: {
405− if (beingResized) {
406− // Brace yourselves for impact!
407− selectedIndex = -1;
408− phase = 0;
409− contentX = -shift;
410− }
411− }
412−
413− property real sideStageWidth: units.gu(40)
414−
415− property bool surfaceDragging: triGestureArea.recognisedDrag
416−
417− readonly property bool sideStageVisible: priv.sideStageItemId != 0
418−
419− // In case applicationManager already holds an app when starting up we're missing animations
420− // Make sure we end up in the same state
421− Component.onCompleted: {
422− spreadView.contentX = -spreadView.shift
423− }
424−
425− property int nextInStack: {
426− var mainStageIndex = priv.mainStageDelegate ? priv.mainStageDelegate.index : -1;
427− var sideStageIndex = priv.sideStageDelegate ? priv.sideStageDelegate.index : -1;
428− switch (state) {
429− case "main":
430− if (root.topLevelSurfaceList.count > 1) {
431− return 1;
432− }
433− return -1;
434− case "mainAndOverlay":
435− if (root.topLevelSurfaceList.count <= 2) {
436− return -1;
437− }
438− if (mainStageIndex == 0 || sideStageIndex == 0) {
439− if (mainStageIndex == 1 || sideStageIndex == 1) {
440− return 2;
441− }
442− return 1;
443− }
444− return 0;
445− case "overlay":
446− return 1;
447− }
448− return -1;
449− }
450− property int nextZInStack
451−
452− states: [
453− State {
454− name: "empty"
455− },
456− State {
457− name: "main"
458− },
459− State { // Side Stage only in overlay mode
460− name: "overlay"
461− },
462− State { // Main Stage and Side Stage in overlay mode
463− name: "mainAndOverlay"
464− },
465− State { // Main Stage and Side Stage in split mode
466− name: "mainAndSplit"
467− }
468− ]
469− state: {
470− if ((priv.mainStageItemId && !priv.sideStageItemId) || !priv.sideStageEnabled) {
471− return "main";
472− }
473− if (!priv.mainStageItemId && priv.sideStageItemId) {
474− return "overlay";
475− }
476− if (priv.mainStageItemId && priv.sideStageItemId) {
477− return "mainAndOverlay";
478− }
479− return "empty";
480− }
481−
482− onShiftedContentXChanged: {
483− if (root.beingResized) {
484− // Flickabe.contentX wiggles during resizes. Don't react to it.
485− return;
486− }
487−
488− if (phase === 0) {
489− // the "spreadEnabled" part is because when code does "phase = 0; contentX = -shift" to
490− // dismiss the spread because spreadEnabled went to false, for some reason, during tests,
491− // Flickable might jump in and change contentX value back, causing the code below to do
492− // "phase = 1" which will make the spread stay.
493− // It sucks that we have no control whatsoever over whether or when Flickable animates its
494− // contentX.
495− if (root.spreadEnabled && shiftedContentX > width * positionMarker2) {
496− phase = 1;
497− }
498− }
499−
500− // Do not turn to else if
501− // Sometimes the animation of shiftedContentX is very fast and we need to jump from phase 0 to 1 to 2
502− // in the same onShiftedContentXChanged
503− if (phase === 1) {
504− if (shiftedContentX < width * positionMarker2) {
505− phase = 0;
506− } else if (shiftedContentX >= width * positionMarker4 && !spreadDragArea.dragging) {
507− phase = 2;
508− }
509− }
510− }
511−
512− function snap() {
513− if (shiftedContentX < phase0Width) {
514− snapAnimation.targetContentX = -shift;
515− snapAnimation.start();
516− } else if (shiftedContentX < phase1Width) {
517− snapTo(1);
518− } else {
519− snapToSpread();
520− }
521− }
522−
523− function snapToSpread() {
524− // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
525− snapAnimation.targetContentX = (spreadView.width * spreadView.positionMarker4) + 1 - shift;
526− snapAnimation.start();
527− }
528−
529− function snapTo(index) {
530− snapAnimation.stop();
531− spreadView.selectedIndex = index;
532− snapAnimation.targetContentX = -shift;
533− snapAnimation.start();
534− }
535−
536− // We need to shuffle z ordering a bit in order to keep side stage apps above main stage apps.
537− // We don't want to really reorder them in the model because that allows us to keep track
538− // of the last focused order.
539− function indexToZIndex(index) {
540− // only shuffle when we've got a main and overlay
541− if (state !== "mainAndOverlay") return index;
542−
543− var app = root.topLevelSurfaceList.applicationAt(index);
544− if (!app) {
545− return index;
546− }
547− var stage = spreadRepeater.itemAt(index) ? spreadRepeater.itemAt(index).stage : ApplicationInfoInterface.MainStage;
548−
549− // don't shuffle indexes greater than "actives or next"
550− if (index > 2) return index;
551−
552− var mainStageIndex = root.topLevelSurfaceList.indexForId(priv.mainStageItemId);
553−
554− if (index == mainStageIndex) {
555− // Active main stage always at 0
556− return 0;
557− }
558−
559− if (spreadView.nextInStack > 0) {
560− var stageOfNextInStack = spreadRepeater.itemAt(spreadView.nextInStack).stage;
561−
562− if (index === spreadView.nextInStack) {
563− // this is the next app in stack.
564−
565− if (stage === ApplicationInfoInterface.SideStage) {
566− // if the next app in stack is a sidestage app, it must order on top of other side stage app
567− return Math.min(2, root.topLevelSurfaceList.count-1);
568− }
569− return 1;
570− }
571− if (stageOfNextInStack === ApplicationInfoInterface.SideStage) {
572− // if the next app in stack is a sidestage app, it must order on top of other side stage app
573− return 1;
574− }
575− return Math.min(2, root.topLevelSurfaceList.count-1);
576− }
577− return Math.min(index+1, root.topLevelSurfaceList.count-1);
578− }
579−
580− SequentialAnimation {
581− id: snapAnimation
582− property int targetContentX: -spreadView.shift
583−
584− UbuntuNumberAnimation {
585− target: spreadView
586− property: "contentX"
587− to: snapAnimation.targetContentX
588− duration: UbuntuAnimation.FastDuration
589− }
590−
591− ScriptAction {
592− script: {
593− if (spreadView.selectedIndex >= 0) {
594− var newIndex = spreadView.selectedIndex;
595− var application = root.topLevelSurfaceList.applicationAt(newIndex);
596− var spreadDelegate = spreadRepeater.itemAt(newIndex);
597− if (spreadDelegate.stage === ApplicationInfoInterface.SideStage) {
598− sideStage.showNow();
599− }
600− spreadView.selectedIndex = -1;
601− spreadDelegate.focus = true;
602− spreadView.phase = 0;
603− spreadView.contentX = -spreadView.shift;
604− }
605− }
606− }
607− }
608−
609− Behavior on contentX {
610− enabled: root.altTabPressed
611− UbuntuNumberAnimation {}
612− }
613−
614− MouseArea {
615− id: spreadRow
616− x: spreadView.contentX
617− width: spreadView.width + Math.max(spreadView.width, root.topLevelSurfaceList.count * spreadView.tileDistance)
618− height: root.height
619−
620− onClicked: {
621− spreadView.snapTo(0);
622− }
623−
624− DropArea {
625− objectName: "MainStageDropArea"
626− anchors {
627− left: parent.left
628− top: parent.top
629− bottom: parent.bottom
630− }
631− width: spreadView.width - sideStage.width
632− enabled: priv.sideStageEnabled
633−
634− onDropped: {
635− drop.source.spreadDelegate.saveStage(ApplicationInfoInterface.MainStage);
636− drop.source.spreadDelegate.focus = true;
637− }
638− keys: "SideStage"
639− }
640−
641− SideStage {
642− id: sideStage
643− objectName: "sideStage"
644− height: priv.landscapeHeight
645− x: spreadView.width - width
646− z: {
647− if (!priv.mainStageItemId) return 0;
648−
649− if (priv.sideStageItemId && spreadView.nextInStack > 0) {
650− var nextDelegateInStack = spreadRepeater.itemAt(spreadView.nextInStack);
651−
652− if (nextDelegateInStack.stage === ApplicationInfoInterface.MainStage) {
653− // if the next app in stack is a main stage app, put the sidestage on top of it.
654− return 2;
655− }
656− return 1;
657− }
658−
659− return 1;
660− }
661− visible: progress != 0
662− enabled: priv.sideStageEnabled && sideStageDropArea.dropAllowed
663− opacity: priv.sideStageEnabled && !spreadView.active ? 1 : 0
664− Behavior on opacity { UbuntuNumberAnimation {} }
665−
666− onShownChanged: {
667− if (!shown && priv.sideStageDelegate && priv.focusedAppDelegate === priv.sideStageDelegate
668− && priv.mainStageDelegate) {
669− priv.mainStageDelegate.focus = true;
670− } else if (shown && priv.sideStageDelegate) {
671− priv.sideStageDelegate.focus = true;
672− }
673− }
674−
675− DropArea {
676− id: sideStageDropArea
677− objectName: "SideStageDropArea"
678− anchors.fill: parent
679−
680− property bool dropAllowed: true
681−
682− onEntered: {
683− dropAllowed = drag.keys != "Disabled";
684− }
685− onExited: {
686− dropAllowed = true;
687− }
688− onDropped: {
689− if (drop.keys == "MainStage") {
690− drop.source.spreadDelegate.saveStage(ApplicationInfoInterface.SideStage);
691− drop.source.spreadDelegate.focus = true;
692− }
693− }
694− drag {
695− onSourceChanged: {
696− if (!sideStageDropArea.drag.source) {
697− dropAllowed = true;
698− }
699− }
700− }
701− }
702− }
703−
704− TopLevelSurfaceRepeater {
705− id: spreadRepeater
706− objectName: "spreadRepeater"
707− model: root.topLevelSurfaceList
708−
709− onItemAdded: {
710− priv.updateMainAndSideStageIndexes();
711− if (spreadView.phase == 2) {
712− spreadView.snapTo(index);
713− }
714− }
715−
716− onItemRemoved: {
717− priv.updateMainAndSideStageIndexes();
718− // Unless we're closing the app ourselves,
719− // lets make sure the spread doesn't mess up by the changing app list.
720− if (spreadView.closingIndex == -1) {
721− spreadView.phase = 0;
722− spreadView.contentX = -spreadView.shift;
723− }
724− focusTopMostApp();
725− }
726− function focusTopMostApp() {
727− if (spreadRepeater.count > 0) {
728− var topmostDelegate = spreadRepeater.itemAt(0);
729− topmostDelegate.focus = true;
730− }
731− }
732−
733− delegate: TransformedTabletSpreadDelegate {
734− id: spreadTile
735− objectName: "spreadDelegate_" + model.id
736−
737− readonly property int index: model.index
738− width: spreadView.width
739− height: spreadView.height
740− active: model.id == priv.mainStageItemId || model.id == priv.sideStageItemId
741− zIndex: selected && stage == ApplicationInfoInterface.MainStage ? 0 : spreadView.indexToZIndex(index)
742− onZIndexChanged: {
743− if (spreadView.nextInStack == model.index) {
744− spreadView.nextZInStack = zIndex;
745− }
746− }
747− selected: spreadView.selectedIndex == index
748− otherSelected: spreadView.selectedIndex >= 0 && !selected
749− isInSideStage: priv.sideStageItemId == model.id
750− interactive: !spreadView.interactive && spreadView.phase === 0 && root.interactive
751− swipeToCloseEnabled: spreadView.interactive && !snapAnimation.running
752− maximizedAppTopMargin: root.maximizedAppTopMargin
753− dragOffset: !isDash && model.id == priv.mainStageItemId && root.inverseProgress > 0
754− && spreadView.phase === 0 ? root.inverseProgress : 0
755− application: model.application
756− surface: model.surface
757− closeable: !isDash
758− highlightShown: root.altTabPressed && priv.highlightIndex == zIndex
759− dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
760−
761− readonly property bool wantsMainStage: stage == ApplicationInfoInterface.MainStage
762−
763− readonly property bool isDash: application.appId == "unity8-dash"
764−
765− onFocusChanged: {
766− if (focus && !spreadRepeater.startingUp) {
767− priv.focusedAppDelegate = spreadTile;
768− root.topLevelSurfaceList.raiseId(model.id);
769− }
770− if (focus && priv.sideStageEnabled && stage === ApplicationInfoInterface.SideStage) {
771− sideStage.show();
772− }
773− }
774− Connections {
775− target: model.surface
776− onFocusRequested: spreadTile.focus = true;
777− }
778− Connections {
779− target: spreadTile.application
780− onFocusRequested: {
781− if (!model.surface) {
782− // when an app has no surfaces, we assume there's only one entry representing it:
783− // this delegate.
784− spreadTile.focus = true;
785− } else {
786− // if the application has surfaces, focus request should be at surface-level.
787− }
788− }
789− }
790−
791− fullscreen: {
792− if (priv.mainStageDelegate && stage === ApplicationInfoInterface.SideStage) {
793− return priv.mainStageDelegate.fullscreen;
794− } else if (surface) {
795− return surface.state === Mir.FullscreenState;
796− } else if (application) {
797− return application.fullscreen;
798− } else {
799− return false;
800− }
801− }
802−
803− supportedOrientations: {
804− if (application) {
805− var orientations = application.supportedOrientations;
806− if (stage == ApplicationInfoInterface.MainStage) {
807− // When an app is in the mainstage, it always supports Landscape|InvertedLandscape
808− // so that we can drag it from the main stage to the side stage
809− orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation;
810− }
811− return orientations;
812− } else {
813− // we just don't care
814− return Qt.PortraitOrientation |
815− Qt.LandscapeOrientation |
816− Qt.InvertedPortraitOrientation |
817− Qt.InvertedLandscapeOrientation;
818− }
819− }
820−
821− function saveStage(newStage) {
822− stage = newStage;
823− WindowStateStorage.saveStage(application.appId, newStage);
824− }
825−
826− // FIXME: A regular binding doesn't update any more after closing an app.
827− // Using a Binding for now.
828− Binding {
829− target: spreadTile
830− property: "z"
831− value: (!spreadView.active && isDash && !active) ? -1 : spreadTile.zIndex
832− }
833− x: spreadView.width
834−
835− property real behavioredZIndex: zIndex
836− Behavior on behavioredZIndex {
837− enabled: spreadView.closingIndex >= 0
838− UbuntuNumberAnimation {}
839− }
840− Connections {
841− target: priv
842− onSideStageEnabledChanged: refreshStage()
843− }
844−
845− property bool _constructing: true;
846− onStageChanged: {
847− if (!_constructing) {
848− priv.updateMainAndSideStageIndexes();
849− }
850− }
851−
852− Component.onCompleted: {
853− // a top level window is always the focused one when it first appears, unfocusing
854− // any preexisting one
855− focus = true;
856− refreshStage();
857− _constructing = false;
858− }
859−
860− function refreshStage() {
861− var newStage = ApplicationInfoInterface.MainStage;
862− if (priv.sideStageEnabled) { // we're in lanscape rotation.
863− if (!isDash && application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
864− var defaultStage = ApplicationInfoInterface.SideStage; // if application supports portrait, it defaults to sidestage.
865− if (application.supportedOrientations & (Qt.LandscapeOrientation|Qt.InvertedLandscapeOrientation)) {
866− // if it supports lanscape, it defaults to mainstage.
867− defaultStage = ApplicationInfoInterface.MainStage;
868− }
869− newStage = WindowStateStorage.getStage(application.appId, defaultStage);
870− }
871− }
872−
873− stage = newStage;
874− }
875−
876− progress: {
877− var tileProgress = (spreadView.shiftedContentX - behavioredZIndex * spreadView.tileDistance) / spreadView.width;
878− // Some tiles (nextInStack, active) need to move directly from the beginning, normalize progress to immediately start at 0
879− if ((index == spreadView.nextInStack && spreadView.phase < 2) || (active && spreadView.phase < 1)) {
880− tileProgress += behavioredZIndex * spreadView.tileDistance / spreadView.width;
881− }
882− return tileProgress;
883− }
884−
885− // TODO: Hiding tile when progress is such that it will be off screen.
886− property bool occluded: {
887− if (spreadView.active && !offScreen) return false;
888− else if (spreadTile.active) return false;
889− else if (xTranslateAnimating) return false;
890− else if (z <= 1 && priv.focusedAppDelegateIsDislocated) return false;
891− return true;
892− }
893−
894− visible: Powerd.status == Powerd.On &&
895− !greeter.fullyShown &&
896− !occluded
897−
898− animatedProgress: {
899− if (spreadView.phase == 0 && (spreadTile.active || spreadView.nextInStack == index)) {
900− if (progress < spreadView.positionMarker1) {
901− return progress;
902− } else if (progress < spreadView.positionMarker1 + snappingCurve.period) {
903− return spreadView.positionMarker1 + snappingCurve.value * 3;
904− } else {
905− return spreadView.positionMarker2;
906− }
907− }
908− return progress;
909− }
910−
911− shellOrientationAngle: root.shellOrientationAngle
912− shellOrientation: root.shellOrientation
913− orientations: root.orientations
914−
915− states: [
916− State {
917− name: "MainStage"
918− when: spreadTile.stage == ApplicationInfoInterface.MainStage
919− },
920− State {
921− name: "SideStage"
922− when: spreadTile.stage == ApplicationInfoInterface.SideStage
923−
924− PropertyChanges {
925− target: spreadTile
926− width: spreadView.sideStageWidth
927− height: priv.landscapeHeight
928−
929− supportedOrientations: Qt.PortraitOrientation
930− shellOrientationAngle: 0
931− shellOrientation: Qt.PortraitOrientation
932− orientations: sideStageOrientations
933− }
934− }
935− ]
936−
937− Orientations {
938− id: sideStageOrientations
939− primary: Qt.PortraitOrientation
940− native_: Qt.PortraitOrientation
941− portrait: root.orientations.portrait
942− invertedPortrait: root.orientations.invertedPortrait
943− landscape: root.orientations.landscape
944− invertedLandscape: root.orientations.invertedLandscape
945− }
946−
947− transitions: [
948− Transition {
949− to: "SideStage"
950− SequentialAnimation {
951− PropertyAction {
952− target: spreadTile
953− properties: "width,height,supportedOrientations,shellOrientationAngle,shellOrientation,orientations"
954− }
955− ScriptAction {
956− script: {
957− // rotate immediately.
958− spreadTile.matchShellOrientation();
959− if (priv.focusedAppDelegate === spreadTile &&
960− priv.sideStageEnabled && !sideStage.shown) {
961− // Sidestage was focused, so show the side stage.
962− sideStage.show();
963− }
964− }
965− }
966− }
967− },
968− Transition {
969− from: "SideStage"
970− SequentialAnimation {
971− ScriptAction {
972− script: {
973− if (priv.sideStageDelegate === spreadTile &&
974− mainApp && (mainApp.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) == 0) {
975− // The mainstage app did not natively support portrait orientation, so focus the sidestage.
976− spreadTile.focus = true;
977− }
978− }
979− }
980− PropertyAction {
981− target: spreadTile
982− properties: "width,height,supportedOrientations,shellOrientationAngle,shellOrientation,orientations"
983− }
984− ScriptAction { script: { spreadTile.matchShellOrientation(); } }
985− }
986− }
987− ]
988−
989− onClicked: {
990− if (spreadView.phase == 2) {
991− spreadView.snapTo(index);
992− }
993− }
994−
995− onDraggedChanged: {
996− if (dragged) {
997− spreadView.draggedDelegateCount++;
998− } else {
999− spreadView.draggedDelegateCount--;
1000− }
1001− }
1002−
1003− onClosed: {
1004− spreadView.closingIndex = index;
1005− if (spreadTile.surface) {
1006− spreadTile.surface.close();
1007− } else if (spreadTile.application) {
1008− root.applicationManager.stopApplication(spreadTile.application.appId);
1009− } else {
1010− // should never happen
1011− console.warn("Can't close topLevelSurfaceList entry as it has neither"
1012− + " a surface nor an application");
1013− }
1014− }
1015−
1016− Binding {
1017− target: root
1018− when: model.id == priv.mainStageItemId
1019− property: "mainAppWindowOrientationAngle"
1020− value: appWindowOrientationAngle
1021− }
1022− Binding {
1023− target: priv
1024− when: model.id == priv.mainStageItemId
1025− property: "mainAppOrientationChangesEnabled"
1026− value: orientationChangesEnabled
1027− }
1028−
1029− EasingCurve {
1030− id: snappingCurve
1031− type: EasingCurve.Linear
1032− period: (spreadView.positionMarker2 - spreadView.positionMarker1) / 3
1033− progress: spreadTile.progress - spreadView.positionMarker1
1034− }
1035−
1036− StagedFullscreenPolicy {
1037− id: fullscreenPolicy
1038− surface: model.surface
1039− }
1040− Connections {
1041− target: root
1042− onStageAboutToBeUnloaded: fullscreenPolicy.active = false
1043− }
1044− }
1045− }
1046− }
1047− }
1048−
1049− TabletSideStageTouchGesture {
1050− id: triGestureArea
1051− anchors.fill: parent
1052− enabled: priv.sideStageEnabled && !spreadView.active
1053−
1054− property Item spreadDelegate
1055−
1056− dragComponent: dragComponent
1057− dragComponentProperties: { "spreadDelegate": spreadDelegate }
1058−
1059− onPressed: {
1060− function matchDelegate(obj) { return String(obj.objectName).indexOf("spreadDelegate") >= 0; }
1061−
1062− var delegateAtCenter = Functions.itemAt(spreadRow, x, y, matchDelegate);
1063− if (!delegateAtCenter) return;
1064−
1065− spreadDelegate = delegateAtCenter;
1066− }
1067−
1068− onClicked: {
1069− if (sideStage.shown) {
1070− sideStage.hide();
1071− } else {
1072− sideStage.show();
1073− }
1074− }
1075−
1076− onDragStarted: {
1077− // If we're dragging to the sidestage.
1078− if (!sideStage.shown) {
1079− sideStage.show();
1080− }
1081− }
1082−
1083− Component {
1084− id: dragComponent
1085− SurfaceContainer {
1086− property Item spreadDelegate
1087−
1088− surface: spreadDelegate ? spreadDelegate.surface : null
1089−
1090− consumesInput: false
1091− interactive: false
1092− resizeSurface: false
1093− focus: false
1094−
1095− width: units.gu(40)
1096− height: units.gu(40)
1097−
1098− Drag.hotSpot.x: width/2
1099− Drag.hotSpot.y: height/2
1100− // only accept opposite stage.
1101− Drag.keys: {
1102− if (!surface) return "Disabled";
1103− if (spreadDelegate.isDash) return "Disabled";
1104−
1105− if (spreadDelegate.stage === ApplicationInfo.MainStage) {
1106− if (spreadDelegate.application.supportedOrientations
1107− & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
1108− return "MainStage";
1109− }
1110− return "Disabled";
1111− }
1112− return "SideStage";
1113− }
1114− }
1115− }
1116− }
1117−
1118− //eat touch events during the right edge gesture
1119− MouseArea {
1120− anchors.fill: parent
1121− enabled: spreadDragArea.dragging
1122− }
1123−
1124− SwipeArea {
1125− id: spreadDragArea
1126− objectName: "spreadDragArea"
1127− x: parent.width - root.dragAreaWidth
1128− anchors { top: parent.top; bottom: parent.bottom }
1129− width: root.dragAreaWidth
1130− direction: Direction.Leftwards
1131− enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging
1132−
1133− property var gesturePoints: new Array()
1134−
1135− onTouchPositionChanged: {
1136− if (!dragging) {
1137− spreadView.phase = 0;
1138− spreadView.contentX = -spreadView.shift;
1139− }
1140−
1141− if (dragging) {
1142− var dragX = -touchPosition.x + spreadDragArea.width - spreadView.shift;
1143− var maxDrag = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
1144− spreadView.contentX = Math.min(dragX, maxDrag);
1145− }
1146− gesturePoints.push(touchPosition.x);
1147− }
1148−
1149− onDraggingChanged: {
1150− if (dragging) {
1151− // Gesture recognized. Start recording this gesture
1152− gesturePoints = [];
1153− } else {
1154− // Ok. The user released. Find out if it was a one-way movement.
1155− var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
1156− gesturePoints = [];
1157−
1158− if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
1159− // If it was a short one-way movement, do the Alt+Tab switch
1160− // no matter if we didn't cross positionMarker1 yet.
1161− spreadView.snapTo(spreadView.nextInStack);
1162− } else {
1163− if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
1164− spreadView.snap();
1165− } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
1166− spreadView.snapTo(spreadView.nextInStack);
1167− } else {
1168− // otherwise snap to the closest snap position we can find
1169− // (might be back to start, to app 1 or to spread)
1170− spreadView.snap();
1171− }
1172− }
1173− }
1174− }
1175− }
1176−
1177− EdgeBarrier {
1178− id: edgeBarrier
1179−
1180− // NB: it does its own positioning according to the specified edge
1181− edge: Qt.RightEdge
1182−
1183− onPassed: {
1184− spreadView.snapToSpread();
1185− }
1186− material: Component {
1187− Item {
1188− Rectangle {
1189− width: parent.height
1190− height: parent.width
1191− rotation: 90
1192− anchors.centerIn: parent
1193− gradient: Gradient {
1194− GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.7)}
1195− GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
1196− }
1197− }
1198− }
1199− }
1200− }
1201−}