@@ -1,566 +0,0 @@
2− * Copyright (C) 2013 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 Unity.Application 0.1
19−import Ubuntu.Components 0.1
20−import Ubuntu.Gestures 0.1
23− Responsible for application switching.
25− Drag from the right edge to switch to the next application.
26− While being dragged, this component will show an animation for the user to
27− visualize that he's changing the stacking of applications (next/new app comes from
28− right edge and current/old app fades out).
36− property int type: ApplicationInfo.MainStage
37− property var applicationManager
38− readonly property var applications:
39− type == ApplicationInfo.MainStage ? applicationManager.mainStageApplications
40− : applicationManager.sideStageApplications
41− property bool fullyShown: false
42− property bool fullyHidden: true
43− readonly property var focusedApplication:
44− type == ApplicationInfo.MainStage ? applicationManager.mainStageFocusedApplication
45− : applicationManager.sideStageFocusedApplication
46− property bool shouldUseScreenshots: true
48− /* Will be true whenever application screenshots are being shown.
49− i.e. during application window transitions */
50− readonly property bool usingScreenshots: {
51− if (newApplicationScreenshot.application != null && oldApplicationScreenshot.application != null) {
52− return newApplicationScreenshot.ready && oldApplicationScreenshot.ready;
54− return newApplicationScreenshot.ready || oldApplicationScreenshot.ready;
58− property bool rightEdgeEnabled: true
59− property bool switchingFromFullscreenToFullscreen: false
61− property alias rightEdgeDraggingAreaWidth: rightEdgeDraggingArea.width
63− // Y coordinate for non-fullscreen applications
64− property int normalApplicationY: 0
66− function activateApplication(desktopFile, addDelay) {
67− var application = applicationManager.getApplicationFromDesktopFile(desktopFile, stage.type);
68− if (application == null) {
70− } else if (application == stage.focusedApplication) {
75− showStartingApplicationAnimation.prepare(application);
76− if (!stage.fullyShown) {
77− // if launching this app reveals the stage, make new application screenshot visible
78− newApplicationScreenshot.visible = true;
81− showStartingApplicationAnimation.start();
85− stage.__startingApplicationDesktopFile = desktopFile;
89− /* Keep a reference to the focused application so that we can safely
90− unfocus it when the stage is not fully shown and refocus it when the stage
91− is fully shown again.
93− property var focusedApplicationWhenUsingScreenshots
94− onShouldUseScreenshotsChanged: {
95− if (showStartingApplicationAnimation.running || switchToApplicationAnimation.running) {
99− if (shouldUseScreenshots) {
100− stage.__focusApplicationUsingScreenshots(stage.focusedApplication);
102− if (stage.focusedApplicationWhenUsingScreenshots) {
103− stage.__focusActualApplication(stage.focusedApplicationWhenUsingScreenshots);
109− property Item newApplicationScreenshot: ApplicationScreenshot {
112− height: stage.height - y
114− property Item oldApplicationScreenshot: ApplicationScreenshot {
117− height: stage.height - y
120− function __getApplicationIndex(application) {
123− for (var i = 0; i < stage.applications.count; i++ ) {
124− if (stage.applications.get(i).desktopFile == application.desktopFile) {
132− property string __startingApplicationDesktopFile
135− id: delayedHideScreenshots
136− interval: 500 // on Galaxy Nexus, Telephony app is slowest application to draw a frame so needs this delay
138− __hideScreenshots();
143− id: hideScreenshotsAfterBriefDelay
144− // Since Mir & Qt have independent event loops, Mir often applies a focus request a frame later than when Qt
145− // asks for it. To work around, delay removing the screenshots a touch so Mir has definitely shown the app.
147− onTriggered: stage.__hideScreenshots();
150− function __hideScreenshots() {
151− newApplicationScreenshot.clearApplication();
152− oldApplicationScreenshot.clearApplication();
153− newApplicationScreenshot.visible = false;
154− oldApplicationScreenshot.visible = false;
157− function __focusApplication(application) {
158− if (!application) return
159− if (shouldUseScreenshots) {
160− stage.__focusApplicationUsingScreenshots(application);
162− stage.__focusActualApplication(application);
167− function __focusActualApplication(application) {
168− if (application == null) {
172− if (stage.focusedApplication == application) {
173− /* FIXME: this case is reached when showing the sidestage, we
174− need to refocus the side stage app in order to show it.
175− FIXME: this case is reached when doing a right edge swipe
176− while in the dash to bring back the stage.
177− FIXME: this case is reached when showStartingApplicationAnimation
178− finishes after the newly started application has been focused
179− automatically by the ApplicationManager.
181− if (stage.type == ApplicationInfo.SideStage) {
182− applicationManager.focusApplication(application);
184− if (!delayedHideScreenshots.running) {
185− hideScreenshotsAfterBriefDelay.start();
188− /* FIXME: calling ApplicationManager::focusApplication does not focus
189− the application immediately nor show the application's surface
190− immediately. Delay hiding the screenshots until it has done so.
192− delayedHideScreenshots.stop();
193− applicationManager.focusApplication(application);
195− stage.focusedApplicationWhenUsingScreenshots = null;
198− function __focusApplicationUsingScreenshots(application) {
199− delayedHideScreenshots.stop();
200− if (application == null) {
204− if (newApplicationScreenshot.application != null) {
205− stage.focusedApplicationWhenUsingScreenshots = application;
209− newApplicationScreenshot.setApplication(application);
210− newApplicationScreenshot.scheduleUpdate();
211− newApplicationScreenshot.opacity = 1.0;
212− newApplicationScreenshot.x = 0.0;
213− newApplicationScreenshot.y = __getApplicationY(application);
214− newApplicationScreenshot.scale = 1.0;
215− newApplicationScreenshot.visible = true;
216− stage.focusedApplicationWhenUsingScreenshots = application;
217− stage.switchingFromFullscreenToFullscreen = false;
220− function __getApplicationY(application) {
221− return Qt.binding( function(){
222− return (application && application.fullscreen
223− && application.stage !== ApplicationInfo.SideStage)
224− ? 0 : normalApplicationY;
228− /* FIXME: should hide the screenshots when the focused application's surface
229− is visible. There is no API in QtUbuntu to support that yet.
233− onFocusedApplicationChanged: {
234− if (stage.focusedApplication != null) {
235− /* Move application to the beginning of the list of running applications */
236− var index = stage.__getApplicationIndex(stage.focusedApplication);
237− applicationManager.moveRunningApplicationStackPosition(index, 0, stage.type);
239− var startingApplication = applicationManager.getApplicationFromDesktopFile(
240− stage.__startingApplicationDesktopFile, stage.type);
242− if (stage.focusedApplication == startingApplication) {
243− stage.__startingApplicationDesktopFile = "";
245− /* Screenshots might still be in use at this point and their
246− use can be stopped before delayedHideScreenshots is complete.
247− Prepare for that case by setting appropriately
248− stage.focusedApplicationWhenUsingScreenshots.
250− stage.focusedApplicationWhenUsingScreenshots = stage.focusedApplication;
252− /* FIXME: should hide the screenshots when the focused application's window
253− is visible. There is no API in qtubuntu to support that yet. Therefore
254− we delay their hiding to avoid a state where neither the screenshot nor
255− the application's window is visible.
257− delayedHideScreenshots.start();
259− stage.focusedApplicationWhenUsingScreenshots = null;
260− hideScreenshotsAfterBriefDelay.start();
267− target: stage.applications
268− onCountChanged: { __discardObsoleteScreenshots(); }
269− ignoreUnknownSignals: true
271− onFullyHiddenChanged: { __discardObsoleteScreenshots(); }
272− function __discardObsoleteScreenshots() {
273− if (stage.fullyHidden && stage.applications && stage.applications.count == 0) {
274− stage.focusedApplicationWhenUsingScreenshots = undefined;
275− stage.__hideScreenshots();
280− SequentialAnimation {
281− id: showStartingApplicationAnimation
283− function prepare(newApplication) {
284− var oldApplication = stage.focusedApplication;
285− newApplicationScreenshot.setApplication(newApplication);
286− newApplicationScreenshot.updateFromCache();
287− oldApplicationScreenshot.setApplication(oldApplication);
288− oldApplicationScreenshot.scheduleUpdate();
289− newApplicationScreenshot.withBackground = true; // FIXME: should be withBackground = newApplication.state == Application.Starting
290− newApplicationScreenshot.y = __getApplicationY(newApplication);
291− oldApplicationScreenshot.y = __getApplicationY(oldApplication);
292− stage.focusedApplicationWhenUsingScreenshots = newApplication;
293− stage.switchingFromFullscreenToFullscreen = false;
296− PropertyAction {target: newApplicationScreenshot; property: "visible"; value: false}
297− PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: false}
298− PropertyAction {target: newApplicationScreenshot; property: "z"; value: 0}
299− PropertyAction {target: oldApplicationScreenshot; property: "z"; value: 1}
300− PropertyAction {target: newApplicationScreenshot; property: "scale"; value: 0.8}
301− PropertyAction {target: oldApplicationScreenshot; property: "scale"; value: 1.0}
302− PropertyAction {target: newApplicationScreenshot; property: "opacity"; value: 0.0}
303− PropertyAction {target: oldApplicationScreenshot; property: "opacity"; value: 1.0}
304− PropertyAction {target: newApplicationScreenshot; property: "x"; value: 0.0}
305− PropertyAction {target: oldApplicationScreenshot; property: "x"; value: 0.0}
308− PropertyAction {target: newApplicationScreenshot; property: "visible"; value: true}
310− target: newApplicationScreenshot
313− easing.type: Easing.OutQuad
317− target: newApplicationScreenshot
320− easing.type: Easing.OutQuad
324− PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: true}
327− target: oldApplicationScreenshot
330− easing.type: Easing.OutQuad
337− // FIXME: only here to support case where application does
338− // not exist yet but is about to
339− if (stage.focusedApplicationWhenUsingScreenshots) {
340− stage.__focusApplication(stage.focusedApplicationWhenUsingScreenshots);
347− SequentialAnimation {
348− id: switchToApplicationAnimation
350− function prepare(newApplication) {
351− var oldApplication = stage.focusedApplication;
352− newApplicationScreenshot.setApplication(newApplication);
353− newApplicationScreenshot.updateFromCache();
354− oldApplicationScreenshot.setApplication(oldApplication);
355− oldApplicationScreenshot.scheduleUpdate();
356− newApplicationScreenshot.withBackground = false;
357− newApplicationScreenshot.y = __getApplicationY(newApplication);
358− oldApplicationScreenshot.y = __getApplicationY(oldApplication);
359− stage.focusedApplicationWhenUsingScreenshots = newApplication;
360− stage.switchingFromFullscreenToFullscreen = newApplication && newApplication.fullscreen;
363− PropertyAction {target: newApplicationScreenshot; property: "visible"; value: false}
364− PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: false}
365− PropertyAction {target: newApplicationScreenshot; property: "z"; value: 1}
366− PropertyAction {target: oldApplicationScreenshot; property: "z"; value: 0}
367− PropertyAction {target: newApplicationScreenshot; property: "scale"; value: 1.0}
368− PropertyAction {target: oldApplicationScreenshot; property: "scale"; value: 1.0}
369− PropertyAction {target: newApplicationScreenshot; property: "opacity"; value: 1.0}
370− PropertyAction {target: oldApplicationScreenshot; property: "opacity"; value: 1.0}
371− PropertyAction {target: newApplicationScreenshot; property: "x"; value: stage.width}
372− PropertyAction {target: oldApplicationScreenshot; property: "x"; value: 0.0}
373− PropertyAction {target: newApplicationScreenshot; property: "visible"; value: true}
374− PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: true}
378− target: oldApplicationScreenshot
381− easing.type: Easing.OutQuad
385− target: oldApplicationScreenshot
388− easing.type: Easing.OutQuad
393− target: newApplicationScreenshot
396− easing.type: Easing.OutQuad
403− // move previously focused application to the end of the list of running applications
404− var oldApplication = oldApplicationScreenshot.application;
405− if (oldApplication != null) {
406− var oldApplicationIndex = stage.__getApplicationIndex(oldApplicationScreenshot.application);
407− applicationManager.moveRunningApplicationStackPosition(
408− oldApplicationIndex, stage.applications.count - 1, stage.type);
411− stage.__focusApplication(stage.focusedApplicationWhenUsingScreenshots);
417− function __dragValueToProgress(dragValue, startValue, applicationsCount) {
418− if (applicationsCount == 0) {
420− } else if (applicationsCount == 1) {
421− // prevent the current app from moving more than 1/4 of the screen
422− var elasticValue = __elastic(dragValue, startValue/4);
423− return elasticValue / startValue;
425− return dragValue / startValue;
429− function __elastic(x, limit) {
430− return limit * ( 1 - Math.pow( ( limit - 1 ) / limit, x ) )
433− property real __startValue: stage.width
435− AnimationControllerWithSignals {
436− id: switchToApplicationAnimationController
438− onAnimationCompletedAtBeginning: {
440− stage.__focusApplication(oldApplicationScreenshot.application);
446− trackedPosition: rightEdgeDraggingArea.touchX
447− direction: rightEdgeDraggingArea.direction
448− maxDragDistance: stage.width
452− id: rightEdgeDraggingArea
455− if (status == DirectionalDragArea.WaitingForTouch) {
456− return switchToApplicationAnimationController.completed
457− && stage.applications.count > 0 && stage.rightEdgeEnabled
458− && !delayedHideScreenshots.running
464− anchors.top: parent.top
465− anchors.bottom: parent.bottom
466− anchors.right: parent.right
468− direction: Direction.Leftwards
470− onDistanceChanged: {
471− if (status !== DirectionalDragArea.Recognized)
474− var targetProgress = __dragValueToProgress(-distance, __startValue,
475− stage.applications.count)
476− var delta = targetProgress - switchToApplicationAnimationController.progress
478− // When the gesture finally gets recognized, the finger will likely be
479− // reasonably far from the edge. If we made the progress immediately
480− // follow the finger position it would be visually unpleasant as there
481− // would be a jump to meet the finger's current position.
483− // The trick is not to go all the way (1.0) as it would cause the
484− // aforementioned jump.
485− switchToApplicationAnimationController.progress += 0.4 * delta
488− property int lastStatus: -1
491− if (status == DirectionalDragArea.Undecided) {
493− } else if (status == DirectionalDragArea.WaitingForTouch) {
494− if (lastStatus == DirectionalDragArea.Undecided) {
496− switchToApplicationAnimationController.completeToBeginningWithSignal();
501− lastStatus = status;
504− function onUndecided() {
505− dragEvaluator.reset();
507− var nextApplication = null;
508− if (stage.applications.count > 1) {
509− nextApplication = stage.applications.get(1);
511− switchToApplicationAnimationController.progress = 0
512− switchToApplicationAnimation.prepare(nextApplication);
513− switchToApplicationAnimationController.animation = switchToApplicationAnimation;
517− function onGestureEnded() {
518− if (stage.applications.count > 1 && dragEvaluator.shouldAutoComplete()) {
519− switchToApplicationAnimationController.completeToEndWithSignal();
521− switchToApplicationAnimationController.completeToBeginningWithSignal();
527− // FIXME: very bad hack to detect when an application was closed or crashed
528− property var __previouslyFocusedApplication
531− onFocusedApplicationChanged: {
532− if (stage.focusedApplication == null
533− && __getApplicationIndex(stage.__previouslyFocusedApplication) == -1) {
534− stage.__onFocusedApplicationClosed();
536− stage.__previouslyFocusedApplication = stage.focusedApplication;
540− function __onFocusedApplicationClosed() {
545− if (stage.applications.count > 0) {
546− // Do not change the focused application from within onFocusedApplicationChanged.
547− // This causes binding loop warnings besides being a bad practice in general
548− // (setting a property from within its "changed" signal handling)
550− focusTopmostAppTimer.start();
557− id: focusTopmostAppTimer
558− interval: 1; repeat: false
560− if (stage.applications.count > 0) {
561− stage.__focusApplication(stage.applications.get(0));