2 * Copyright (C) 2013-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/>.
18import QtQuick.Window 2.2
19import AccountsService 0.1
20import Unity.Application 0.1
21import Ubuntu.Components 1.3
22import Ubuntu.Components.Popups 1.3
23import Ubuntu.Gestures 0.1
24import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Connectivity 0.1
26import Unity.Launcher 0.1
27import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
31import SessionBroadcast 0.1
40import Unity.Notifications 1.0 as NotificationBackend
41import Unity.Session 0.1
42import Unity.DashCommunicator 0.1
43import Unity.Indicators 0.1 as Indicators
45import WindowManager 0.1
51 theme.name: "Ubuntu.Components.Themes.SuruDark"
53 // to be set from outside
54 property int orientationAngle: 0
55 property int orientation
56 property Orientations orientations
57 property real nativeWidth
58 property real nativeHeight
59 property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
60 property bool beingResized
61 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
62 property string mode: "full-greeter"
63 property alias oskEnabled: inputMethod.enabled
64 function updateFocusedAppOrientation() {
65 applicationsDisplayLoader.item.updateFocusedAppOrientation();
67 function updateFocusedAppOrientationAnimated() {
68 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
70 property bool hasMouse: false
72 // to be read from outside
73 readonly property int mainAppWindowOrientationAngle:
74 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
76 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
77 && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.orientationChangesEnabled)
78 && (!greeter || !greeter.animating)
80 readonly property bool showingGreeter: greeter && greeter.shown
82 property bool startingUp: true
83 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
85 property int supportedOrientations: {
87 // Ensure we don't rotate during start up
88 return Qt.PrimaryOrientation;
89 } else if (showingGreeter || notifications.topmostIsFullscreen) {
90 return Qt.PrimaryOrientation;
91 } else if (applicationsDisplayLoader.item) {
92 return shell.orientations.map(applicationsDisplayLoader.item.supportedOrientations);
95 return Qt.PortraitOrientation
96 | Qt.LandscapeOrientation
97 | Qt.InvertedPortraitOrientation
98 | Qt.InvertedLandscapeOrientation;
102 readonly property var mainApp:
103 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
106 _onMainAppChanged(mainApp.appId);
110 target: ApplicationManager
112 if (shell.mainApp && shell.mainApp.appId === appId) {
113 _onMainAppChanged(appId);
117 function _onMainAppChanged(appId) {
118 if (wizard.active && appId != "" && appId != "unity8-dash") {
119 // If this happens on first boot, we may be in the
120 // wizard while receiving a call. But a call is more
121 // important than the wizard so just bail out of it.
125 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
126 // If we are in the middle of a call, make dialer lockedApp and show it.
127 // This can happen if user backs out of dialer back to greeter, then
128 // launches dialer again.
129 greeter.lockedApp = appId;
131 greeter.notifyAppFocusRequested(appId);
133 panel.indicators.hide();
137 // For autopilot consumption
138 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
140 // Note when greeter is waiting on PAM, so that we can disable edges until
141 // we know which user data to show and whether the session is locked.
142 readonly property bool waitingOnGreeter: greeter && greeter.waiting
144 property real edgeSize: units.gu(settings.edgeDragWidth)
147 id: wallpaperResolver
151 readonly property alias greeter: greeterLoader.item
153 function activateApplication(appId) {
154 // Either open the app in our own session, or -- if we're acting as a
155 // greeter -- ask the user's session to open it for us.
156 if (shell.mode === "greeter") {
157 activateURL("application:///" + appId + ".desktop");
163 function activateURL(url) {
164 SessionBroadcast.requestUrlStart(AccountsService.user, url);
165 greeter.notifyUserRequestedApp();
166 panel.indicators.hide();
169 function startApp(appId) {
170 if (ApplicationManager.findApplication(appId)) {
171 ApplicationManager.requestFocusApplication(appId);
173 ApplicationManager.startApplication(appId);
177 function startLockedApp(app) {
178 if (greeter.locked) {
179 greeter.lockedApp = app;
181 startApp(app); // locked apps are always in our same session
185 target: LauncherModel
186 property: "applicationManager"
187 value: ApplicationManager
190 Component.onCompleted: {
191 finishStartUpTimer.start();
200 objectName: "dashCommunicator"
204 id: physicalKeysMapper
205 objectName: "physicalKeysMapper"
207 onPowerKeyLongPressed: dialogs.showPowerDialog();
208 onVolumeDownTriggered: volumeControl.volumeDown();
209 onVolumeUpTriggered: volumeControl.volumeUp();
210 onScreenshotTriggered: itemGrabber.capture(shell);
214 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
219 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
220 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
224 objectName: "windowInputMonitor"
225 onHomeKeyActivated: {
226 // Ignore when greeter is active, to avoid pocket presses
227 if (!greeter.active) {
232 onTouchBegun: { cursor.opacity = 0; }
234 // move the (hidden) cursor to the last known touch position
235 var mappedCoords = mapFromItem(null, pos.x, pos.y);
236 cursor.x = mappedCoords.x;
237 cursor.y = mappedCoords.y;
238 cursor.mouseNeverMoved = false;
244 schema.id: "com.canonical.Unity8"
251 height: parent.height
253 TopLevelSurfaceList {
254 id: topLevelSurfaceList
255 objectName: "topLevelSurfaceList"
256 applicationsModel: ApplicationManager
260 id: applicationsDisplayLoader
261 objectName: "applicationsDisplayLoader"
264 // When we have a locked app, we only want to show that one app.
265 // FIXME: do this in a less traumatic way. We currently only allow
266 // locked apps in phone mode (see FIXME in Lockscreen component in
267 // this same file). When that changes, we need to do something
268 // nicer here. But this code is currently just to prevent a
269 // theoretical attack where user enters lockedApp mode, then makes
270 // the screen larger (maybe connects to monitor) and tries to enter
273 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
275 : shell.usageScenario
276 readonly property string qmlComponent: {
277 if (applicationsDisplayLoader.usageScenario === "phone") {
278 return "Stages/PhoneStage.qml";
279 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
280 return "Stages/TabletStage.qml";
282 return "Stages/DesktopStage.qml";
285 // TODO: Ensure the current stage is destroyed before the new one gets loaded.
286 // Currently the new one will get loaded while the old is still hanging
287 // around for a bit, which might lead to conflicts where both stages
288 // change the model simultaneously.
289 onQmlComponentChanged: {
290 if (item) item.stageAboutToBeUnloaded();
291 source = qmlComponent;
294 property bool interactive: (!greeter || !greeter.shown)
295 && panel.indicators.fullyClosed
296 && launcher.progress == 0
297 && !notifications.useModal
299 onInteractiveChanged: { if (interactive) { focus = true; } }
302 target: applicationsDisplayLoader.item
307 target: applicationsDisplayLoader.item
308 property: "objectName"
312 target: applicationsDisplayLoader.item
313 property: "dragAreaWidth"
314 value: shell.edgeSize
317 target: applicationsDisplayLoader.item
318 property: "maximizedAppTopMargin"
319 // Not just using panel.panelHeight as that changes depending on the focused app.
320 value: panel.indicators.minimizedPanelHeight
323 target: applicationsDisplayLoader.item
324 property: "interactive"
325 value: applicationsDisplayLoader.interactive
328 target: applicationsDisplayLoader.item
329 property: "spreadEnabled"
330 value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
333 target: applicationsDisplayLoader.item
334 property: "inverseProgress"
335 value: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 : launcher.progress
338 target: applicationsDisplayLoader.item
339 property: "shellOrientationAngle"
340 value: shell.orientationAngle
343 target: applicationsDisplayLoader.item
344 property: "shellOrientation"
345 value: shell.orientation
348 target: applicationsDisplayLoader.item
349 property: "orientations"
350 value: shell.orientations
353 target: applicationsDisplayLoader.item
354 property: "background"
355 value: wallpaperResolver.background
358 target: applicationsDisplayLoader.item
359 property: "nativeWidth"
360 value: shell.nativeWidth
363 target: applicationsDisplayLoader.item
364 property: "nativeHeight"
365 value: shell.nativeHeight
368 target: applicationsDisplayLoader.item
369 property: "beingResized"
370 value: shell.beingResized
373 target: applicationsDisplayLoader.item
374 property: "keepDashRunning"
375 value: launcher.shown || launcher.dashSwipe
378 target: applicationsDisplayLoader.item
379 property: "suspended"
383 target: applicationsDisplayLoader.item
384 property: "altTabPressed"
385 value: physicalKeysMapper.altTabPressed
388 target: applicationsDisplayLoader.item
389 property: "leftMargin"
390 value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
393 target: applicationsDisplayLoader.item
394 property: "applicationManager"
395 value: ApplicationManager
398 target: applicationsDisplayLoader.item
399 property: "topLevelSurfaceList"
400 value: topLevelSurfaceList
403 target: applicationsDisplayLoader.item
404 property: "oskEnabled"
405 value: shell.oskEnabled
412 objectName: "inputMethod"
415 topMargin: panel.panelHeight
416 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
418 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
424 anchors.topMargin: panel.panelHeight
425 sourceComponent: shell.mode != "shell" ? integratedGreeter :
426 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
428 item.objectName = "greeter"
433 id: integratedGreeter
436 hides: [launcher, panel.indicators]
437 tabletMode: shell.usageScenario != "phone"
438 launcherOffset: launcher.progress
439 forcedUnlock: wizard.active || shell.mode === "full-shell"
440 background: wallpaperResolver.background
441 allowFingerprint: !dialogs.hasActiveDialog &&
442 !notifications.topmostIsFullscreen &&
443 !panel.indicators.shown
445 // avoid overlapping with Launcher's edge drag area
446 // FIXME: Fix TouchRegistry & friends and remove this workaround
447 // Issue involves launcher's DDA getting disabled on a long
449 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
456 if (!tutorial.running) {
461 onEmergencyCall: startLockedApp("dialer-app")
466 // See powerConnection for why this is useful
467 id: showGreeterDelayed
479 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
480 // We just received an incoming call while locked. The
481 // indicator will have already launched dialer-app for us, but
482 // there is a race between "hasCalls" changing and the dialer
483 // starting up. So in case we lose that race, we'll start/
484 // focus the dialer ourselves here too. Even if the indicator
485 // didn't launch the dialer for some reason (or maybe a call
486 // started via some other means), if an active call is
487 // happening, we want to be in the dialer.
488 startLockedApp("dialer-app")
498 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
499 !callManager.hasCalls && !wizard.active) {
500 // We don't want to simply call greeter.showNow() here, because
501 // that will take too long. Qt will delay button event
502 // handling until the greeter is done loading and may think the
503 // user held down the power button the whole time, leading to a
504 // power dialog being shown. Instead, delay showing the
505 // greeter until we've finished handling the event. We could
506 // make the greeter load asynchronously instead, but that
507 // introduces a whole host of timing issues, especially with
508 // its animations. So this is simpler.
509 showGreeterDelayed.start();
514 function showHome() {
515 greeter.notifyUserRequestedApp();
517 if (shell.mode === "greeter") {
518 SessionBroadcast.requestHomeShown(AccountsService.user);
520 var animate = !LightDMService.greeter.active && !stages.shown;
521 dash.setCurrentScope(0, animate, false);
522 ApplicationManager.requestFocusApplication("unity8-dash");
526 function showDash() {
527 if (greeter.notifyShowingDashFromDrag()) {
531 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
532 && ApplicationManager.focusedApplicationId != "unity8-dash") {
533 ApplicationManager.requestFocusApplication("unity8-dash")
547 anchors.fill: parent //because this draws indicator menus
550 available: tutorial.panelEnabled
551 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
552 && (!greeter || !greeter.hasLockedApp)
553 && !shell.waitingOnGreeter
554 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
556 minimizedPanelHeight: units.gu(3)
557 expandedPanelHeight: units.gu(7)
559 indicatorsModel: Indicators.IndicatorsModel {
560 // tablet and phone both use the same profile
561 // FIXME: use just "phone" for greeter too, but first fix
562 // greeter app launching to either load the app inside the
563 // greeter or tell the session to load the app. This will
564 // involve taking the url-dispatcher dbus name and using
565 // SessionBroadcast to tell the session.
566 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
567 Component.onCompleted: load();
572 greeterShown: greeter.shown
575 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
576 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
578 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
579 || greeter.hasLockedApp
580 locked: greeter && greeter.active
585 objectName: "launcher"
588 * Since the Dash doesn't have the same controll over surfaces that the
589 * Shell does, it can't slowly move the scope out of the way, as the shell
590 * does with apps, and the dash is show instantly. This allows for some
591 * leeway and prevents accidental home swipes.
593 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
594 readonly property bool dashSwipe: progress > offset
596 anchors.top: parent.top
597 anchors.topMargin: inverted ? 0 : panel.panelHeight
598 anchors.bottom: parent.bottom
600 dragAreaWidth: shell.edgeSize
601 available: tutorial.launcherEnabled
602 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
603 && !greeter.hasLockedApp
604 && !shell.waitingOnGreeter
605 inverted: shell.usageScenario !== "desktop"
606 superPressed: physicalKeysMapper.superPressed
607 superTabPressed: physicalKeysMapper.superTabPressed
608 panelWidth: units.gu(settings.launcherWidth)
609 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
611 onShowDashHome: showHome()
613 onDashSwipeChanged: {
615 dash.setCurrentScope(0, false, true)
618 onLauncherApplicationSelected: {
619 greeter.notifyUserRequestedApp();
620 shell.activateApplication(appId);
624 panel.indicators.hide()
629 applicationsDisplayLoader.focus = true;
634 shortcut: Qt.AltModifier | Qt.Key_F1
636 launcher.openForKeyboardNavigation();
640 shortcut: Qt.MetaModifier | Qt.Key_0
642 if (LauncherModel.get(9)) {
643 activateApplication(LauncherModel.get(9).appId);
650 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
652 if (LauncherModel.get(index)) {
653 activateApplication(LauncherModel.get(index).appId);
660 KeyboardShortcutsOverlay {
661 objectName: "shortcutsOverlay"
662 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
663 && height < parent.height - padding - panel.panelHeight
664 anchors.centerIn: parent
665 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
666 anchors.verticalCenterOffset: panel.panelHeight/2
668 opacity: enabled ? 0.95 : 0
670 Behavior on opacity {
671 UbuntuNumberAnimation {}
677 objectName: "tutorial"
680 paused: callManager.hasCalls || !greeter || greeter.shown ||
682 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
683 inputMethod.visible ||
684 (launcher.shown && !launcher.lockedVisible) ||
685 panel.indicators.shown || stage.dragProgress > 0
686 usageScenario: shell.usageScenario
687 lastInputTimestamp: inputFilter.lastInputTimestamp
690 stage: applicationsDisplayLoader.item
697 deferred: shell.mode === "greeter"
699 function unlockWhenDoneWithWizard() {
701 Connectivity.unlockAllModems();
705 Component.onCompleted: unlockWhenDoneWithWizard()
706 onActiveChanged: unlockWhenDoneWithWizard()
709 MouseArea { // modal notifications prevent interacting with other contents
711 visible: notifications.useModal
718 model: NotificationBackend.Model
720 hasMouse: shell.hasMouse
721 background: wallpaperResolver.background
723 y: topmostIsFullscreen ? 0 : panel.panelHeight
724 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
729 when: overlay.width <= units.gu(60)
731 target: notifications
732 anchors.left: parent.left
733 anchors.right: parent.right
738 when: overlay.width > units.gu(60)
740 target: notifications
741 anchors.left: undefined
742 anchors.right: parent.right
744 PropertyChanges { target: notifications; width: units.gu(38) }
752 objectName: "dialogs"
755 usageScenario: shell.usageScenario
757 shutdownFadeOutRectangle.enabled = true;
758 shutdownFadeOutRectangle.visible = true;
759 shutdownFadeOut.start();
764 target: SessionBroadcast
765 onShowHome: if (shell.mode !== "greeter") showHome()
770 objectName: "urlDispatcher"
771 active: shell.mode === "greeter"
772 onUrlRequested: shell.activateURL(url)
779 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
781 target: applicationsDisplayLoader.item
782 ignoreUnknownSignals: true
783 onItemSnapshotRequested: itemGrabber.capture(item)
789 visible: shell.hasMouse
792 property bool mouseNeverMoved: true
794 target: cursor; property: "x"; value: shell.width / 2
795 when: cursor.mouseNeverMoved && cursor.visible
798 target: cursor; property: "y"; value: shell.height / 2
799 when: cursor.mouseNeverMoved && cursor.visible
804 onPushedLeftBoundary: {
805 if (buttons === Qt.NoButton) {
806 launcher.pushEdge(amount);
810 onPushedRightBoundary: {
811 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
812 && applicationsDisplayLoader.item.pushRightEdge) {
813 applicationsDisplayLoader.item.pushRightEdge(amount);
818 mouseNeverMoved = false;
827 id: shutdownFadeOutRectangle
834 NumberAnimation on opacity {
839 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
840 DBusUnitySessionService.shutdown();
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