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