2
* Copyright (C) 2014-2016 Canonical, Ltd.
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.
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.
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/>.
18
import Ubuntu.Components 1.3
19
import Ubuntu.Gestures 0.1
20
import Unity.Application 0.1
23
import "../Components"
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
37
paintBackground: spreadView.shiftedContentX !== 0
39
// Functions to be called from outside
40
function updateFocusedAppOrientation() {
41
var mainStageIndex = root.topLevelSurfaceList.indexForId(priv.mainStageItemId);
43
if (priv.mainStageItemId && mainStageIndex >= 0 && mainStageIndex < spreadRepeater.count) {
44
spreadRepeater.itemAt(mainStageIndex).matchShellOrientation();
47
for (var i = 0; i < spreadRepeater.count; ++i) {
49
if (i === mainStageIndex) {
53
var spreadDelegate = spreadRepeater.itemAt(i);
55
var delta = spreadDelegate.appWindowOrientationAngle - root.shellOrientationAngle;
56
if (delta < 0) { delta += 360; }
59
var supportedOrientations = spreadDelegate.supportedOrientations;
60
if (supportedOrientations === Qt.PrimaryOrientation) {
61
supportedOrientations = spreadDelegate.orientations.primary;
64
if (delta === 180 && (supportedOrientations & spreadDelegate.shellOrientation)) {
65
spreadDelegate.matchShellOrientation();
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();
75
var sideStageIndex = root.topLevelSurfaceList.indexForId(priv.sideStageItemId);
76
if (sideStageIndex >= 0 && sideStageIndex < spreadRepeater.count) {
77
spreadRepeater.itemAt(sideStageIndex).matchShellOrientation();
81
function pushRightEdge(amount) {
82
if (spreadView.contentX == -spreadView.shift) {
83
edgeBarrier.push(amount);
87
function closeFocusedDelegate() {
88
if (priv.focusedAppDelegate && priv.focusedAppDelegate.closeable) {
89
priv.focusedAppDelegate.closed();
93
orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
96
if (priv.mainStageItemId > 0) {
97
var index = root.topLevelSurfaceList.indexForId(priv.mainStageItemId);
98
return root.topLevelSurfaceList.applicationAt(index);
104
supportedOrientations: {
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;
115
// we just don't care
116
return Qt.PortraitOrientation |
117
Qt.LandscapeOrientation |
118
Qt.InvertedPortraitOrientation |
119
Qt.InvertedLandscapeOrientation;
123
// How far left the stage has been dragged, used externally by tutorial code
124
dragProgress: spreadRepeater.count > 0 ? spreadRepeater.itemAt(0).animatedProgress : 0
127
spreadView.selectedIndex = -1;
128
spreadView.phase = 0;
129
spreadView.contentX = -spreadView.shift;
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");
142
priv.oldInverseProgress = inverseProgress;
145
onAltTabPressedChanged: {
146
if (!spreadEnabled) {
150
priv.highlightIndex = Math.min(spreadRepeater.count - 1, 1);
151
spreadView.snapToSpread();
153
for (var i = 0; i < spreadRepeater.count; i++) {
154
if (spreadRepeater.itemAt(i).zIndex === priv.highlightIndex) {
155
spreadView.snapTo(i);
163
focus: root.altTabPressed
168
priv.highlightIndex = (priv.highlightIndex + 1) % spreadRepeater.count
171
priv.highlightIndex = (priv.highlightIndex + spreadRepeater.count - 1) % spreadRepeater.count
178
target: root.topLevelSurfaceList
179
onListChanged: priv.updateMainAndSideStageIndexes()
184
objectName: "stagesPriv"
186
function updateMainAndSideStageIndexes() {
187
var choseMainStage = false;
188
var choseSideStage = false;
190
if (!root.topLevelSurfaceList)
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;
208
if (!choseMainStage) {
209
priv.mainStageDelegate = null;
210
priv.mainStageItemId = 0;
211
priv.mainStageAppId = "";
213
if (!choseSideStage) {
214
priv.sideStageDelegate = null;
215
priv.sideStageItemId = 0;
216
priv.sideStageAppId = "";
220
property var focusedAppDelegate: null
222
property bool mainAppOrientationChangesEnabled: false
224
property real landscapeHeight: root.orientations.native_ == Qt.LandscapeOrientation ?
225
root.nativeHeight : root.nativeWidth
227
property bool shellIsLandscape: root.shellOrientation === Qt.LandscapeOrientation
228
|| root.shellOrientation === Qt.InvertedLandscapeOrientation
230
property var mainStageDelegate: null
231
property var sideStageDelegate: null
233
property int mainStageItemId: 0
234
property int sideStageItemId: 0
236
property string mainStageAppId: ""
237
property string sideStageAppId: ""
239
property int oldInverseProgress: 0
241
property int highlightIndex: 0
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) {
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)) {
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)) {
264
smallestX = gesturePoints[i];
269
onHighlightIndexChanged: {
270
spreadView.contentX = highlightIndex * spreadView.contentWidth / (spreadRepeater.count + 2)
273
readonly property bool sideStageEnabled: root.shellOrientation == Qt.LandscapeOrientation ||
274
root.shellOrientation == Qt.InvertedLandscapeOrientation
278
model: root.applicationManager
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"
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 &&
289
(isDash && root.keepDashRunning)
290
|| (!root.suspended && (model.application.appId === priv.mainStageAppId
291
|| model.application.appId === priv.sideStageAppId))
293
? ApplicationInfoInterface.RequestedRunning
294
: ApplicationInfoInterface.RequestedSuspended
297
property var lifecycleBinding: Binding {
298
target: model.application
299
property: "exemptFromLifecycle"
300
value: model.application
301
? (!model.application.isTouchApp || isExemptFromLifecycle(model.application.appId))
308
target: MirFocusController
309
property: "focusedSurface"
310
value: priv.focusedAppDelegate ? priv.focusedAppDelegate.focusedSurface : null
311
when: root.parent && !spreadRepeater.startingUp
316
objectName: "spreadView"
318
interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
319
contentWidth: spreadRow.width - shift
322
property int tileDistance: units.gu(20)
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
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
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
342
readonly property int phase0Width: sideStageWidth
343
readonly property int phase1Width: sideStageWidth
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
355
readonly property int startSnapPosition: phase0Width * 0.5
356
readonly property int endSnapPosition: phase0Width * 0.75
357
readonly property real snapPosition: 0.75
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
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).
370
if (!undoContentXReset()) {
371
forceItToRemainStillIfBeingResized();
374
onShiftChanged: { forceItToRemainStillIfBeingResized(); }
375
function forceItToRemainStillIfBeingResized() {
376
if (root.beingResized && contentX != -spreadView.shift) {
377
contentX = -spreadView.shift;
380
function undoContentXReset() {
381
if (contentWidth <= 0) {
382
contentWidthOnLastContentXChange = contentWidth;
383
lastContentX = contentX;
387
if (contentWidth != contentWidthOnLastContentXChange
388
&& lastContentX == -shift && contentX == 0) {
389
// Flickable is resetting contentX because contentWidth has changed. Undo it.
394
contentWidthOnLastContentXChange = contentWidth;
395
lastContentX = contentX;
398
property real contentWidthOnLastContentXChange: -1
399
property real lastContentX: 0
402
property bool animateX: true
403
property bool beingResized: root.beingResized
404
onBeingResizedChanged: {
406
// Brace yourselves for impact!
413
property real sideStageWidth: units.gu(40)
415
property bool surfaceDragging: triGestureArea.recognisedDrag
417
readonly property bool sideStageVisible: priv.sideStageItemId != 0
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
425
property int nextInStack: {
426
var mainStageIndex = priv.mainStageDelegate ? priv.mainStageDelegate.index : -1;
427
var sideStageIndex = priv.sideStageDelegate ? priv.sideStageDelegate.index : -1;
430
if (root.topLevelSurfaceList.count > 1) {
434
case "mainAndOverlay":
435
if (root.topLevelSurfaceList.count <= 2) {
438
if (mainStageIndex == 0 || sideStageIndex == 0) {
439
if (mainStageIndex == 1 || sideStageIndex == 1) {
450
property int nextZInStack
459
State { // Side Stage only in overlay mode
462
State { // Main Stage and Side Stage in overlay mode
463
name: "mainAndOverlay"
465
State { // Main Stage and Side Stage in split mode
470
if ((priv.mainStageItemId && !priv.sideStageItemId) || !priv.sideStageEnabled) {
473
if (!priv.mainStageItemId && priv.sideStageItemId) {
476
if (priv.mainStageItemId && priv.sideStageItemId) {
477
return "mainAndOverlay";
482
onShiftedContentXChanged: {
483
if (root.beingResized) {
484
// Flickabe.contentX wiggles during resizes. Don't react to it.
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
495
if (root.spreadEnabled && shiftedContentX > width * positionMarker2) {
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
504
if (shiftedContentX < width * positionMarker2) {
506
} else if (shiftedContentX >= width * positionMarker4 && !spreadDragArea.dragging) {
513
if (shiftedContentX < phase0Width) {
514
snapAnimation.targetContentX = -shift;
515
snapAnimation.start();
516
} else if (shiftedContentX < phase1Width) {
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();
529
function snapTo(index) {
530
snapAnimation.stop();
531
spreadView.selectedIndex = index;
532
snapAnimation.targetContentX = -shift;
533
snapAnimation.start();
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;
543
var app = root.topLevelSurfaceList.applicationAt(index);
547
var stage = spreadRepeater.itemAt(index) ? spreadRepeater.itemAt(index).stage : ApplicationInfoInterface.MainStage;
549
// don't shuffle indexes greater than "actives or next"
550
if (index > 2) return index;
552
var mainStageIndex = root.topLevelSurfaceList.indexForId(priv.mainStageItemId);
554
if (index == mainStageIndex) {
555
// Active main stage always at 0
559
if (spreadView.nextInStack > 0) {
560
var stageOfNextInStack = spreadRepeater.itemAt(spreadView.nextInStack).stage;
562
if (index === spreadView.nextInStack) {
563
// this is the next app in stack.
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);
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
575
return Math.min(2, root.topLevelSurfaceList.count-1);
577
return Math.min(index+1, root.topLevelSurfaceList.count-1);
580
SequentialAnimation {
582
property int targetContentX: -spreadView.shift
584
UbuntuNumberAnimation {
587
to: snapAnimation.targetContentX
588
duration: UbuntuAnimation.FastDuration
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) {
600
spreadView.selectedIndex = -1;
601
spreadDelegate.focus = true;
602
spreadView.phase = 0;
603
spreadView.contentX = -spreadView.shift;
609
Behavior on contentX {
610
enabled: root.altTabPressed
611
UbuntuNumberAnimation {}
616
x: spreadView.contentX
617
width: spreadView.width + Math.max(spreadView.width, root.topLevelSurfaceList.count * spreadView.tileDistance)
621
spreadView.snapTo(0);
625
objectName: "MainStageDropArea"
629
bottom: parent.bottom
631
width: spreadView.width - sideStage.width
632
enabled: priv.sideStageEnabled
635
drop.source.spreadDelegate.saveStage(ApplicationInfoInterface.MainStage);
636
drop.source.spreadDelegate.focus = true;
643
objectName: "sideStage"
644
height: priv.landscapeHeight
645
x: spreadView.width - width
647
if (!priv.mainStageItemId) return 0;
649
if (priv.sideStageItemId && spreadView.nextInStack > 0) {
650
var nextDelegateInStack = spreadRepeater.itemAt(spreadView.nextInStack);
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.
661
visible: progress != 0
662
enabled: priv.sideStageEnabled && sideStageDropArea.dropAllowed
663
opacity: priv.sideStageEnabled && !spreadView.active ? 1 : 0
664
Behavior on opacity { UbuntuNumberAnimation {} }
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;
676
id: sideStageDropArea
677
objectName: "SideStageDropArea"
680
property bool dropAllowed: true
683
dropAllowed = drag.keys != "Disabled";
689
if (drop.keys == "MainStage") {
690
drop.source.spreadDelegate.saveStage(ApplicationInfoInterface.SideStage);
691
drop.source.spreadDelegate.focus = true;
696
if (!sideStageDropArea.drag.source) {
704
TopLevelSurfaceRepeater {
706
objectName: "spreadRepeater"
707
model: root.topLevelSurfaceList
710
priv.updateMainAndSideStageIndexes();
711
if (spreadView.phase == 2) {
712
spreadView.snapTo(index);
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;
726
function focusTopMostApp() {
727
if (spreadRepeater.count > 0) {
728
var topmostDelegate = spreadRepeater.itemAt(0);
729
topmostDelegate.focus = true;
733
delegate: TransformedTabletSpreadDelegate {
735
objectName: "spreadDelegate_" + model.id
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)
743
if (spreadView.nextInStack == model.index) {
744
spreadView.nextZInStack = zIndex;
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
758
highlightShown: root.altTabPressed && priv.highlightIndex == zIndex
759
dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
761
readonly property bool wantsMainStage: stage == ApplicationInfoInterface.MainStage
763
readonly property bool isDash: application.appId == "unity8-dash"
766
if (focus && !spreadRepeater.startingUp) {
767
priv.focusedAppDelegate = spreadTile;
768
root.topLevelSurfaceList.raiseId(model.id);
770
if (focus && priv.sideStageEnabled && stage === ApplicationInfoInterface.SideStage) {
775
target: model.surface
776
onFocusRequested: spreadTile.focus = true;
779
target: spreadTile.application
781
if (!model.surface) {
782
// when an app has no surfaces, we assume there's only one entry representing it:
784
spreadTile.focus = true;
786
// if the application has surfaces, focus request should be at surface-level.
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;
803
supportedOrientations: {
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;
813
// we just don't care
814
return Qt.PortraitOrientation |
815
Qt.LandscapeOrientation |
816
Qt.InvertedPortraitOrientation |
817
Qt.InvertedLandscapeOrientation;
821
function saveStage(newStage) {
823
WindowStateStorage.saveStage(application.appId, newStage);
826
// FIXME: A regular binding doesn't update any more after closing an app.
827
// Using a Binding for now.
831
value: (!spreadView.active && isDash && !active) ? -1 : spreadTile.zIndex
835
property real behavioredZIndex: zIndex
836
Behavior on behavioredZIndex {
837
enabled: spreadView.closingIndex >= 0
838
UbuntuNumberAnimation {}
842
onSideStageEnabledChanged: refreshStage()
845
property bool _constructing: true;
847
if (!_constructing) {
848
priv.updateMainAndSideStageIndexes();
852
Component.onCompleted: {
853
// a top level window is always the focused one when it first appears, unfocusing
854
// any preexisting one
857
_constructing = false;
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;
869
newStage = WindowStateStorage.getStage(application.appId, defaultStage);
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;
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;
894
visible: Powerd.status == Powerd.On &&
895
!greeter.fullyShown &&
899
if (spreadView.phase == 0 && (spreadTile.active || spreadView.nextInStack == index)) {
900
if (progress < spreadView.positionMarker1) {
902
} else if (progress < spreadView.positionMarker1 + snappingCurve.period) {
903
return spreadView.positionMarker1 + snappingCurve.value * 3;
905
return spreadView.positionMarker2;
911
shellOrientationAngle: root.shellOrientationAngle
912
shellOrientation: root.shellOrientation
913
orientations: root.orientations
918
when: spreadTile.stage == ApplicationInfoInterface.MainStage
922
when: spreadTile.stage == ApplicationInfoInterface.SideStage
926
width: spreadView.sideStageWidth
927
height: priv.landscapeHeight
929
supportedOrientations: Qt.PortraitOrientation
930
shellOrientationAngle: 0
931
shellOrientation: Qt.PortraitOrientation
932
orientations: sideStageOrientations
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
950
SequentialAnimation {
953
properties: "width,height,supportedOrientations,shellOrientationAngle,shellOrientation,orientations"
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.
970
SequentialAnimation {
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;
982
properties: "width,height,supportedOrientations,shellOrientationAngle,shellOrientation,orientations"
984
ScriptAction { script: { spreadTile.matchShellOrientation(); } }
990
if (spreadView.phase == 2) {
991
spreadView.snapTo(index);
997
spreadView.draggedDelegateCount++;
999
spreadView.draggedDelegateCount--;
1004
spreadView.closingIndex = index;
1005
if (spreadTile.surface) {
1006
spreadTile.surface.close();
1007
} else if (spreadTile.application) {
1008
root.applicationManager.stopApplication(spreadTile.application.appId);
1010
// should never happen
1011
console.warn("Can't close topLevelSurfaceList entry as it has neither"
1012
+ " a surface nor an application");
1018
when: model.id == priv.mainStageItemId
1019
property: "mainAppWindowOrientationAngle"
1020
value: appWindowOrientationAngle
1024
when: model.id == priv.mainStageItemId
1025
property: "mainAppOrientationChangesEnabled"
1026
value: orientationChangesEnabled
1031
type: EasingCurve.Linear
1032
period: (spreadView.positionMarker2 - spreadView.positionMarker1) / 3
1033
progress: spreadTile.progress - spreadView.positionMarker1
1036
StagedFullscreenPolicy {
1037
id: fullscreenPolicy
1038
surface: model.surface
1042
onStageAboutToBeUnloaded: fullscreenPolicy.active = false
1049
TabletSideStageTouchGesture {
1051
anchors.fill: parent
1052
enabled: priv.sideStageEnabled && !spreadView.active
1054
property Item spreadDelegate
1056
dragComponent: dragComponent
1057
dragComponentProperties: { "spreadDelegate": spreadDelegate }
1060
function matchDelegate(obj) { return String(obj.objectName).indexOf("spreadDelegate") >= 0; }
1062
var delegateAtCenter = Functions.itemAt(spreadRow, x, y, matchDelegate);
1063
if (!delegateAtCenter) return;
1065
spreadDelegate = delegateAtCenter;
1069
if (sideStage.shown) {
1077
// If we're dragging to the sidestage.
1078
if (!sideStage.shown) {
1086
property Item spreadDelegate
1088
surface: spreadDelegate ? spreadDelegate.surface : null
1090
consumesInput: false
1092
resizeSurface: false
1096
height: units.gu(40)
1098
Drag.hotSpot.x: width/2
1099
Drag.hotSpot.y: height/2
1100
// only accept opposite stage.
1102
if (!surface) return "Disabled";
1103
if (spreadDelegate.isDash) return "Disabled";
1105
if (spreadDelegate.stage === ApplicationInfo.MainStage) {
1106
if (spreadDelegate.application.supportedOrientations
1107
& (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
1118
//eat touch events during the right edge gesture
1120
anchors.fill: parent
1121
enabled: spreadDragArea.dragging
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
1133
property var gesturePoints: new Array()
1135
onTouchPositionChanged: {
1137
spreadView.phase = 0;
1138
spreadView.contentX = -spreadView.shift;
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);
1146
gesturePoints.push(touchPosition.x);
1149
onDraggingChanged: {
1151
// Gesture recognized. Start recording this gesture
1154
// Ok. The user released. Find out if it was a one-way movement.
1155
var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
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);
1163
if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
1165
} else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
1166
spreadView.snapTo(spreadView.nextInStack);
1168
// otherwise snap to the closest snap position we can find
1169
// (might be back to start, to app 1 or to spread)
1180
// NB: it does its own positioning according to the specified edge
1184
spreadView.snapToSpread();
1186
material: Component {
1189
width: parent.height
1190
height: parent.width
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)}