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/>.
18import Unity.Application 0.1
19import Ubuntu.Components 0.1
20import 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 && application == stage.focusedApplication) {
73 showStartingApplicationAnimation.prepare(application);
74 if (!stage.fullyShown) {
75 // if launching this app reveals the stage, make new application screenshot visible
76 newApplicationScreenshot.visible = true;
79 showStartingApplicationAnimation.start();
83 stage.__startingApplicationDesktopFile = desktopFile;
87 /* Keep a reference to the focused application so that we can safely
88 unfocus it when the stage is not fully shown and refocus it when the stage
91 property var focusedApplicationWhenUsingScreenshots
92 onShouldUseScreenshotsChanged: {
93 if (showStartingApplicationAnimation.running || switchToApplicationAnimation.running) {
97 if (shouldUseScreenshots) {
98 stage.__focusApplicationUsingScreenshots(stage.focusedApplication);
100 stage.__focusActualApplication(stage.focusedApplicationWhenUsingScreenshots);
105 property Item newApplicationScreenshot: ApplicationScreenshot {
108 height: stage.height - y
110 property Item oldApplicationScreenshot: ApplicationScreenshot {
113 height: stage.height - y
116 function __getApplicationIndex(application) {
119 for (var i = 0; i < stage.applications.count; i++ ) {
120 if (stage.applications.get(i).desktopFile == application.desktopFile) {
128 property string __startingApplicationDesktopFile
131 id: delayedHideScreenshots
132 interval: 500 // on Galaxy Nexus, Telephony app is slowest application to draw a frame so needs this delay
138 function __hideScreenshots() {
139 newApplicationScreenshot.clearApplication();
140 oldApplicationScreenshot.clearApplication();
141 newApplicationScreenshot.visible = false;
142 oldApplicationScreenshot.visible = false;
145 function __focusApplication(application) {
146 if (!application) return
147 if (shouldUseScreenshots) {
148 stage.__focusApplicationUsingScreenshots(application);
150 stage.__focusActualApplication(application);
155 function __focusActualApplication(application) {
156 if (application == null) {
160 if (stage.focusedApplication == application) {
161 /* FIXME: this case is reached when showing the sidestage, we
162 need to refocus the side stage app in order to show it.
163 FIXME: this case is reached when doing a right edge swipe
164 while in the dash to bring back the stage.
165 FIXME: this case is reached when showStartingApplicationAnimation
166 finishes after the newly started application has been focused
167 automatically by the ApplicationManager.
169 if (stage.type == ApplicationInfo.SideStage) {
170 applicationManager.focusApplication(application);
172 if (!delayedHideScreenshots.running) {
173 stage.__hideScreenshots();
176 /* FIXME: calling ApplicationManager::focusApplication does not focus
177 the application immediately nor show the application's surface
178 immediately. Delay hiding the screenshots until it has done so.
180 delayedHideScreenshots.stop();
181 applicationManager.focusApplication(application);
183 stage.focusedApplicationWhenUsingScreenshots = null;
186 function __focusApplicationUsingScreenshots(application) {
187 delayedHideScreenshots.stop();
188 if (application == null) {
192 if (newApplicationScreenshot.application != null) {
193 stage.focusedApplicationWhenUsingScreenshots = application;
197 newApplicationScreenshot.setApplication(application);
198 newApplicationScreenshot.scheduleUpdate();
199 newApplicationScreenshot.opacity = 1.0;
200 newApplicationScreenshot.x = 0.0;
201 newApplicationScreenshot.y = __getApplicationY(application);
202 newApplicationScreenshot.scale = 1.0;
203 newApplicationScreenshot.visible = true;
204 stage.focusedApplicationWhenUsingScreenshots = application;
205 stage.switchingFromFullscreenToFullscreen = false;
208 function __getApplicationY(application) {
209 return Qt.binding( function(){
210 return (application && application.fullscreen
211 && application.stage !== ApplicationInfo.SideStage)
212 ? 0 : normalApplicationY;
216 /* FIXME: should hide the screenshots when the focused application's surface
217 is visible. There is no API in QtUbuntu to support that yet.
221 onFocusedApplicationChanged: {
222 if (stage.focusedApplication != null) {
223 /* Move application to the beginning of the list of running applications */
224 var index = stage.__getApplicationIndex(stage.focusedApplication);
225 applicationManager.moveRunningApplicationStackPosition(index, 0, stage.type);
227 var startingApplication = applicationManager.getApplicationFromDesktopFile(
228 stage.__startingApplicationDesktopFile, stage.type);
230 if (stage.focusedApplication == startingApplication) {
231 stage.__startingApplicationDesktopFile = "";
233 /* Screenshots might still be in use at this point and their
234 use can be stopped before delayedHideScreenshots is complete.
235 Prepare for that case by setting appropriately
236 stage.focusedApplicationWhenUsingScreenshots.
238 stage.focusedApplicationWhenUsingScreenshots = stage.focusedApplication;
240 /* FIXME: should hide the screenshots when the focused application's window
241 is visible. There is no API in qtubuntu to support that yet. Therefore
242 we delay their hiding to avoid a state where neither the screenshot nor
243 the application's window is visible.
245 delayedHideScreenshots.start();
247 stage.__hideScreenshots();
254 target: stage.applications
255 onCountChanged: { __discardObsoleteScreenshots(); }
256 ignoreUnknownSignals: true
258 onFullyHiddenChanged: { __discardObsoleteScreenshots(); }
259 function __discardObsoleteScreenshots() {
260 if (stage.fullyHidden && stage.applications && stage.applications.count == 0) {
261 stage.focusedApplicationWhenUsingScreenshots = undefined;
262 stage.__hideScreenshots();
267 SequentialAnimation {
268 id: showStartingApplicationAnimation
270 function prepare(newApplication) {
271 var oldApplication = stage.focusedApplication;
272 newApplicationScreenshot.setApplication(newApplication);
273 newApplicationScreenshot.updateFromCache();
274 oldApplicationScreenshot.setApplication(oldApplication);
275 oldApplicationScreenshot.scheduleUpdate();
276 newApplicationScreenshot.withBackground = true; // FIXME: should be withBackground = newApplication.state == Application.Starting
277 newApplicationScreenshot.y = __getApplicationY(newApplication);
278 oldApplicationScreenshot.y = __getApplicationY(oldApplication);
279 stage.focusedApplicationWhenUsingScreenshots = newApplication;
280 stage.switchingFromFullscreenToFullscreen = false;
283 PropertyAction {target: newApplicationScreenshot; property: "visible"; value: false}
284 PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: false}
285 PropertyAction {target: newApplicationScreenshot; property: "z"; value: 0}
286 PropertyAction {target: oldApplicationScreenshot; property: "z"; value: 1}
287 PropertyAction {target: newApplicationScreenshot; property: "scale"; value: 0.8}
288 PropertyAction {target: oldApplicationScreenshot; property: "scale"; value: 1.0}
289 PropertyAction {target: newApplicationScreenshot; property: "opacity"; value: 0.0}
290 PropertyAction {target: oldApplicationScreenshot; property: "opacity"; value: 1.0}
291 PropertyAction {target: newApplicationScreenshot; property: "x"; value: 0.0}
292 PropertyAction {target: oldApplicationScreenshot; property: "x"; value: 0.0}
295 PropertyAction {target: newApplicationScreenshot; property: "visible"; value: true}
297 target: newApplicationScreenshot
300 easing.type: Easing.OutQuad
304 target: newApplicationScreenshot
307 easing.type: Easing.OutQuad
311 PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: true}
314 target: oldApplicationScreenshot
317 easing.type: Easing.OutQuad
324 if (!stage.focusedApplicationWhenUsingScreenshots) {
325 // FIXME: only here to support case where application does
326 // not exist yet but is about to
330 stage.__focusApplication(stage.focusedApplicationWhenUsingScreenshots);
336 SequentialAnimation {
337 id: switchToApplicationAnimation
339 function prepare(newApplication) {
340 var oldApplication = stage.focusedApplication;
341 newApplicationScreenshot.setApplication(newApplication);
342 newApplicationScreenshot.updateFromCache();
343 oldApplicationScreenshot.setApplication(oldApplication);
344 oldApplicationScreenshot.scheduleUpdate();
345 newApplicationScreenshot.withBackground = false;
346 newApplicationScreenshot.y = __getApplicationY(newApplication);
347 oldApplicationScreenshot.y = __getApplicationY(oldApplication);
348 stage.focusedApplicationWhenUsingScreenshots = newApplication;
349 stage.switchingFromFullscreenToFullscreen = newApplication && newApplication.fullscreen;
352 PropertyAction {target: newApplicationScreenshot; property: "visible"; value: false}
353 PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: false}
354 PropertyAction {target: newApplicationScreenshot; property: "z"; value: 1}
355 PropertyAction {target: oldApplicationScreenshot; property: "z"; value: 0}
356 PropertyAction {target: newApplicationScreenshot; property: "scale"; value: 1.0}
357 PropertyAction {target: oldApplicationScreenshot; property: "scale"; value: 1.0}
358 PropertyAction {target: newApplicationScreenshot; property: "opacity"; value: 1.0}
359 PropertyAction {target: oldApplicationScreenshot; property: "opacity"; value: 1.0}
360 PropertyAction {target: newApplicationScreenshot; property: "x"; value: stage.width}
361 PropertyAction {target: oldApplicationScreenshot; property: "x"; value: 0.0}
362 PropertyAction {target: newApplicationScreenshot; property: "visible"; value: true}
363 PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: true}
367 target: oldApplicationScreenshot
370 easing.type: Easing.OutQuad
374 target: oldApplicationScreenshot
377 easing.type: Easing.OutQuad
382 target: newApplicationScreenshot
385 easing.type: Easing.OutQuad
392 // move previously focused application to the end of the list of running applications
393 var oldApplication = oldApplicationScreenshot.application;
394 if (oldApplication != null) {
395 var oldApplicationIndex = stage.__getApplicationIndex(oldApplicationScreenshot.application);
396 applicationManager.moveRunningApplicationStackPosition(
397 oldApplicationIndex, stage.applications.count - 1, stage.type);
400 stage.__focusApplication(stage.focusedApplicationWhenUsingScreenshots);
406 function __dragValueToProgress(dragValue, startValue, applicationsCount) {
407 if (applicationsCount == 0) {
409 } else if (applicationsCount == 1) {
410 // prevent the current app from moving more than 1/4 of the screen
411 var elasticValue = __elastic(dragValue, startValue/4);
412 return elasticValue / startValue;
414 return dragValue / startValue;
418 function __elastic(x, limit) {
419 return limit * ( 1 - Math.pow( ( limit - 1 ) / limit, x ) )
422 property real __startValue: stage.width
424 AnimationControllerWithSignals {
425 id: switchToApplicationAnimationController
427 onAnimationCompletedAtBeginning: {
429 stage.__focusApplication(oldApplicationScreenshot.application);
435 trackedPosition: rightEdgeDraggingArea.touchX
436 direction: rightEdgeDraggingArea.direction
437 maxDragDistance: stage.width
441 id: rightEdgeDraggingArea
444 if (status == DirectionalDragArea.WaitingForTouch) {
445 return switchToApplicationAnimationController.completed
446 && stage.applications.count > 0 && stage.rightEdgeEnabled
447 && !delayedHideScreenshots.running
453 anchors.top: parent.top
454 anchors.bottom: parent.bottom
455 anchors.right: parent.right
457 direction: Direction.Leftwards
460 if (status !== DirectionalDragArea.Recognized)
463 var targetProgress = __dragValueToProgress(-distance, __startValue,
464 stage.applications.count)
465 var delta = targetProgress - switchToApplicationAnimationController.progress
467 // When the gesture finally gets recognized, the finger will likely be
468 // reasonably far from the edge. If we made the progress immediately
469 // follow the finger position it would be visually unpleasant as there
470 // would be a jump to meet the finger's current position.
472 // The trick is not to go all the way (1.0) as it would cause the
473 // aforementioned jump.
474 switchToApplicationAnimationController.progress += 0.4 * delta
477 property bool gotRejected: false
480 if (status == DirectionalDragArea.Undecided) {
483 } else if (status == DirectionalDragArea.Rejected) {
484 switchToApplicationAnimationController.completeToBeginningWithSignal();
486 } else if (status == DirectionalDragArea.WaitingForTouch) {
492 function onUndecided() {
493 dragEvaluator.reset();
495 var nextApplication = null;
496 if (stage.applications.count > 1) {
497 nextApplication = stage.applications.get(1);
499 switchToApplicationAnimationController.progress = 0
500 switchToApplicationAnimation.prepare(nextApplication);
501 switchToApplicationAnimationController.animation = switchToApplicationAnimation;
505 function onGestureEnded() {
506 if (stage.applications.count > 1 && dragEvaluator.shouldAutoComplete()) {
507 switchToApplicationAnimationController.completeToEndWithSignal();
509 switchToApplicationAnimationController.completeToBeginningWithSignal();
515 // FIXME: very bad hack to detect when an application was closed or crashed
516 property var __previouslyFocusedApplication
519 onFocusedApplicationChanged: {
520 if (stage.focusedApplication == null
521 && __getApplicationIndex(stage.__previouslyFocusedApplication) == -1) {
522 stage.__onFocusedApplicationClosed();
524 stage.__previouslyFocusedApplication = stage.focusedApplication;
528 function __onFocusedApplicationClosed() {
533 if (stage.applications.count > 0) {
534 // Do not change the focused application from within onFocusedApplicationChanged.
535 // This causes binding loop warnings besides being a bad practice in general
536 // (setting a property from within its "changed" signal handling)
538 focusTopmostAppTimer.start();
545 id: focusTopmostAppTimer
546 interval: 1; repeat: false
548 if (stage.applications.count > 0) {
549 stage.__focusApplication(stage.applications.get(0));
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list