@@ -1,571 +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;
90− target: applicationManager
91− onFocusRequested: activateApplication(appId)
94− /* Keep a reference to the focused application so that we can safely
95− unfocus it when the stage is not fully shown and refocus it when the stage
96− is fully shown again.
98− property var focusedApplicationWhenUsingScreenshots
99− onShouldUseScreenshotsChanged: {
100− if (showStartingApplicationAnimation.running || switchToApplicationAnimation.running) {
104− if (shouldUseScreenshots) {
105− stage.__focusApplicationUsingScreenshots(stage.focusedApplication);
107− if (stage.focusedApplicationWhenUsingScreenshots) {
108− stage.__focusActualApplication(stage.focusedApplicationWhenUsingScreenshots);
114− property Item newApplicationScreenshot: ApplicationScreenshot {
117− height: stage.height - y
119− property Item oldApplicationScreenshot: ApplicationScreenshot {
122− height: stage.height - y
125− function __getApplicationIndex(application) {
128− for (var i = 0; i < stage.applications.count; i++ ) {
129− if (stage.applications.get(i).desktopFile == application.desktopFile) {
137− property string __startingApplicationDesktopFile
140− id: delayedHideScreenshots
141− interval: 500 // on Galaxy Nexus, Telephony app is slowest application to draw a frame so needs this delay
143− __hideScreenshots();
148− id: hideScreenshotsAfterBriefDelay
149− // Since Mir & Qt have independent event loops, Mir often applies a focus request a frame later than when Qt
150− // asks for it. To work around, delay removing the screenshots a touch so Mir has definitely shown the app.
152− onTriggered: stage.__hideScreenshots();
155− function __hideScreenshots() {
156− newApplicationScreenshot.clearApplication();
157− oldApplicationScreenshot.clearApplication();
158− newApplicationScreenshot.visible = false;
159− oldApplicationScreenshot.visible = false;
162− function __focusApplication(application) {
163− if (!application) return
164− if (shouldUseScreenshots) {
165− stage.__focusApplicationUsingScreenshots(application);
167− stage.__focusActualApplication(application);
172− function __focusActualApplication(application) {
173− if (application == null) {
177− if (stage.focusedApplication == application) {
178− /* FIXME: this case is reached when showing the sidestage, we
179− need to refocus the side stage app in order to show it.
180− FIXME: this case is reached when doing a right edge swipe
181− while in the dash to bring back the stage.
182− FIXME: this case is reached when showStartingApplicationAnimation
183− finishes after the newly started application has been focused
184− automatically by the ApplicationManager.
186− if (stage.type == ApplicationInfo.SideStage) {
187− applicationManager.focusApplication(application);
189− if (!delayedHideScreenshots.running) {
190− hideScreenshotsAfterBriefDelay.start();
193− /* FIXME: calling ApplicationManager::focusApplication does not focus
194− the application immediately nor show the application's surface
195− immediately. Delay hiding the screenshots until it has done so.
197− delayedHideScreenshots.stop();
198− applicationManager.focusApplication(application);
200− stage.focusedApplicationWhenUsingScreenshots = null;
203− function __focusApplicationUsingScreenshots(application) {
204− delayedHideScreenshots.stop();
205− if (application == null) {
209− if (newApplicationScreenshot.application != null) {
210− stage.focusedApplicationWhenUsingScreenshots = application;
214− newApplicationScreenshot.setApplication(application);
215− newApplicationScreenshot.scheduleUpdate();
216− newApplicationScreenshot.opacity = 1.0;
217− newApplicationScreenshot.x = 0.0;
218− newApplicationScreenshot.y = __getApplicationY(application);
219− newApplicationScreenshot.scale = 1.0;
220− newApplicationScreenshot.visible = true;
221− stage.focusedApplicationWhenUsingScreenshots = application;
222− stage.switchingFromFullscreenToFullscreen = false;
225− function __getApplicationY(application) {
226− return Qt.binding( function(){
227− return (application && application.fullscreen
228− && application.stage !== ApplicationInfo.SideStage)
229− ? 0 : normalApplicationY;
233− /* FIXME: should hide the screenshots when the focused application's surface
234− is visible. There is no API in QtUbuntu to support that yet.
238− onFocusedApplicationChanged: {
239− if (stage.focusedApplication != null) {
240− /* Move application to the beginning of the list of running applications */
241− var index = stage.__getApplicationIndex(stage.focusedApplication);
242− applicationManager.moveRunningApplicationStackPosition(index, 0, stage.type);
244− var startingApplication = applicationManager.getApplicationFromDesktopFile(
245− stage.__startingApplicationDesktopFile, stage.type);
247− if (stage.focusedApplication == startingApplication) {
248− stage.__startingApplicationDesktopFile = "";
250− /* Screenshots might still be in use at this point and their
251− use can be stopped before delayedHideScreenshots is complete.
252− Prepare for that case by setting appropriately
253− stage.focusedApplicationWhenUsingScreenshots.
255− stage.focusedApplicationWhenUsingScreenshots = stage.focusedApplication;
257− /* FIXME: should hide the screenshots when the focused application's window
258− is visible. There is no API in qtubuntu to support that yet. Therefore
259− we delay their hiding to avoid a state where neither the screenshot nor
260− the application's window is visible.
262− delayedHideScreenshots.start();
264− stage.focusedApplicationWhenUsingScreenshots = null;
265− hideScreenshotsAfterBriefDelay.start();
272− target: stage.applications
273− onCountChanged: { __discardObsoleteScreenshots(); }
274− ignoreUnknownSignals: true
276− onFullyHiddenChanged: { __discardObsoleteScreenshots(); }
277− function __discardObsoleteScreenshots() {
278− if (stage.fullyHidden && stage.applications && stage.applications.count == 0) {
279− stage.focusedApplicationWhenUsingScreenshots = undefined;
280− stage.__hideScreenshots();
285− SequentialAnimation {
286− id: showStartingApplicationAnimation
288− function prepare(newApplication) {
289− var oldApplication = stage.focusedApplication;
290− newApplicationScreenshot.setApplication(newApplication);
291− newApplicationScreenshot.updateFromCache();
292− oldApplicationScreenshot.setApplication(oldApplication);
293− oldApplicationScreenshot.scheduleUpdate();
294− newApplicationScreenshot.withBackground = true; // FIXME: should be withBackground = newApplication.state == Application.Starting
295− newApplicationScreenshot.y = __getApplicationY(newApplication);
296− oldApplicationScreenshot.y = __getApplicationY(oldApplication);
297− stage.focusedApplicationWhenUsingScreenshots = newApplication;
298− stage.switchingFromFullscreenToFullscreen = false;
301− PropertyAction {target: newApplicationScreenshot; property: "visible"; value: false}
302− PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: false}
303− PropertyAction {target: newApplicationScreenshot; property: "z"; value: 0}
304− PropertyAction {target: oldApplicationScreenshot; property: "z"; value: 1}
305− PropertyAction {target: newApplicationScreenshot; property: "scale"; value: 0.8}
306− PropertyAction {target: oldApplicationScreenshot; property: "scale"; value: 1.0}
307− PropertyAction {target: newApplicationScreenshot; property: "opacity"; value: 0.0}
308− PropertyAction {target: oldApplicationScreenshot; property: "opacity"; value: 1.0}
309− PropertyAction {target: newApplicationScreenshot; property: "x"; value: 0.0}
310− PropertyAction {target: oldApplicationScreenshot; property: "x"; value: 0.0}
313− PropertyAction {target: newApplicationScreenshot; property: "visible"; value: true}
315− target: newApplicationScreenshot
318− easing.type: Easing.OutQuad
322− target: newApplicationScreenshot
325− easing.type: Easing.OutQuad
329− PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: true}
332− target: oldApplicationScreenshot
335− easing.type: Easing.OutQuad
342− // FIXME: only here to support case where application does
343− // not exist yet but is about to
344− if (stage.focusedApplicationWhenUsingScreenshots) {
345− stage.__focusApplication(stage.focusedApplicationWhenUsingScreenshots);
352− SequentialAnimation {
353− id: switchToApplicationAnimation
355− function prepare(newApplication) {
356− var oldApplication = stage.focusedApplication;
357− newApplicationScreenshot.setApplication(newApplication);
358− newApplicationScreenshot.updateFromCache();
359− oldApplicationScreenshot.setApplication(oldApplication);
360− oldApplicationScreenshot.scheduleUpdate();
361− newApplicationScreenshot.withBackground = false;
362− newApplicationScreenshot.y = __getApplicationY(newApplication);
363− oldApplicationScreenshot.y = __getApplicationY(oldApplication);
364− stage.focusedApplicationWhenUsingScreenshots = newApplication;
365− stage.switchingFromFullscreenToFullscreen = newApplication && newApplication.fullscreen;
368− PropertyAction {target: newApplicationScreenshot; property: "visible"; value: false}
369− PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: false}
370− PropertyAction {target: newApplicationScreenshot; property: "z"; value: 1}
371− PropertyAction {target: oldApplicationScreenshot; property: "z"; value: 0}
372− PropertyAction {target: newApplicationScreenshot; property: "scale"; value: 1.0}
373− PropertyAction {target: oldApplicationScreenshot; property: "scale"; value: 1.0}
374− PropertyAction {target: newApplicationScreenshot; property: "opacity"; value: 1.0}
375− PropertyAction {target: oldApplicationScreenshot; property: "opacity"; value: 1.0}
376− PropertyAction {target: newApplicationScreenshot; property: "x"; value: stage.width}
377− PropertyAction {target: oldApplicationScreenshot; property: "x"; value: 0.0}
378− PropertyAction {target: newApplicationScreenshot; property: "visible"; value: true}
379− PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: true}
383− target: oldApplicationScreenshot
386− easing.type: Easing.OutQuad
390− target: oldApplicationScreenshot
393− easing.type: Easing.OutQuad
398− target: newApplicationScreenshot
401− easing.type: Easing.OutQuad
408− // move previously focused application to the end of the list of running applications
409− var oldApplication = oldApplicationScreenshot.application;
410− if (oldApplication != null) {
411− var oldApplicationIndex = stage.__getApplicationIndex(oldApplicationScreenshot.application);
412− applicationManager.moveRunningApplicationStackPosition(
413− oldApplicationIndex, stage.applications.count - 1, stage.type);
416− stage.__focusApplication(stage.focusedApplicationWhenUsingScreenshots);
422− function __dragValueToProgress(dragValue, startValue, applicationsCount) {
423− if (applicationsCount == 0) {
425− } else if (applicationsCount == 1) {
426− // prevent the current app from moving more than 1/4 of the screen
427− var elasticValue = __elastic(dragValue, startValue/4);
428− return elasticValue / startValue;
430− return dragValue / startValue;
434− function __elastic(x, limit) {
435− return limit * ( 1 - Math.pow( ( limit - 1 ) / limit, x ) )
438− property real __startValue: stage.width
440− AnimationControllerWithSignals {
441− id: switchToApplicationAnimationController
443− onAnimationCompletedAtBeginning: {
445− stage.__focusApplication(oldApplicationScreenshot.application);
451− trackedPosition: rightEdgeDraggingArea.touchX
452− direction: rightEdgeDraggingArea.direction
453− maxDragDistance: stage.width
457− id: rightEdgeDraggingArea
460− if (status == DirectionalDragArea.WaitingForTouch) {
461− return switchToApplicationAnimationController.completed
462− && stage.applications.count > 0 && stage.rightEdgeEnabled
463− && !delayedHideScreenshots.running
469− anchors.top: parent.top
470− anchors.bottom: parent.bottom
471− anchors.right: parent.right
473− direction: Direction.Leftwards
475− onDistanceChanged: {
476− if (status !== DirectionalDragArea.Recognized)
479− var targetProgress = __dragValueToProgress(-distance, __startValue,
480− stage.applications.count)
481− var delta = targetProgress - switchToApplicationAnimationController.progress
483− // When the gesture finally gets recognized, the finger will likely be
484− // reasonably far from the edge. If we made the progress immediately
485− // follow the finger position it would be visually unpleasant as there
486− // would be a jump to meet the finger's current position.
488− // The trick is not to go all the way (1.0) as it would cause the
489− // aforementioned jump.
490− switchToApplicationAnimationController.progress += 0.4 * delta
493− property int lastStatus: -1
496− if (status == DirectionalDragArea.Undecided) {
498− } else if (status == DirectionalDragArea.WaitingForTouch) {
499− if (lastStatus == DirectionalDragArea.Undecided) {
501− switchToApplicationAnimationController.completeToBeginningWithSignal();
506− lastStatus = status;
509− function onUndecided() {
510− dragEvaluator.reset();
512− var nextApplication = null;
513− if (stage.applications.count > 1) {
514− nextApplication = stage.applications.get(1);
516− switchToApplicationAnimationController.progress = 0
517− switchToApplicationAnimation.prepare(nextApplication);
518− switchToApplicationAnimationController.animation = switchToApplicationAnimation;
522− function onGestureEnded() {
523− if (stage.applications.count > 1 && dragEvaluator.shouldAutoComplete()) {
524− switchToApplicationAnimationController.completeToEndWithSignal();
526− switchToApplicationAnimationController.completeToBeginningWithSignal();
532− // FIXME: very bad hack to detect when an application was closed or crashed
533− property var __previouslyFocusedApplication
536− onFocusedApplicationChanged: {
537− if (stage.focusedApplication == null
538− && __getApplicationIndex(stage.__previouslyFocusedApplication) == -1) {
539− stage.__onFocusedApplicationClosed();
541− stage.__previouslyFocusedApplication = stage.focusedApplication;
545− function __onFocusedApplicationClosed() {
550− if (stage.applications.count > 0) {
551− // Do not change the focused application from within onFocusedApplicationChanged.
552− // This causes binding loop warnings besides being a bad practice in general
553− // (setting a property from within its "changed" signal handling)
555− focusTopmostAppTimer.start();
562− id: focusTopmostAppTimer
563− interval: 1; repeat: false
565− if (stage.applications.count > 0) {
566− stage.__focusApplication(stage.applications.get(0));