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
32import SessionBroadcast 0.1
41import "Components/PanelState"
42import Unity.Notifications 1.0 as NotificationBackend
43import Unity.Session 0.1
44import Unity.DashCommunicator 0.1
45import Unity.Indicators 0.1 as Indicators
47import WindowManager 1.0
53 theme.name: "Ubuntu.Components.Themes.SuruDark"
55 // to be set from outside
56 property alias surfaceManager: topLevelSurfaceList.surfaceManager
57 property int orientationAngle: 0
58 property int orientation
59 property Orientations orientations
60 property real nativeWidth
61 property real nativeHeight
62 property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
63 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
64 property string mode: "full-greeter"
65 property alias oskEnabled: inputMethod.enabled
66 function updateFocusedAppOrientation() {
67 stage.updateFocusedAppOrientation();
69 function updateFocusedAppOrientationAnimated() {
70 stage.updateFocusedAppOrientationAnimated();
72 property bool hasMouse: false
74 // to be read from outside
75 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
77 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
78 && stage.orientationChangesEnabled
79 && (!greeter || !greeter.animating)
81 readonly property bool showingGreeter: greeter && greeter.shown
83 property bool startingUp: true
84 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
86 property int supportedOrientations: {
88 // Ensure we don't rotate during start up
89 return Qt.PrimaryOrientation;
90 } else if (showingGreeter || notifications.topmostIsFullscreen) {
91 return Qt.PrimaryOrientation;
93 return shell.orientations.map(stage.supportedOrientations);
97 readonly property var mainApp: stage.mainApp
101 _onMainAppChanged(mainApp.appId);
105 target: ApplicationManager
107 if (shell.mainApp && shell.mainApp.appId === appId) {
108 _onMainAppChanged(appId);
112 function _onMainAppChanged(appId) {
113 if (wizard.active && appId != "" && appId != "unity8-dash") {
114 // If this happens on first boot, we may be in the
115 // wizard while receiving a call. But a call is more
116 // important than the wizard so just bail out of it.
120 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
121 // If we are in the middle of a call, make dialer lockedApp and show it.
122 // This can happen if user backs out of dialer back to greeter, then
123 // launches dialer again.
124 greeter.lockedApp = appId;
126 greeter.notifyAppFocusRequested(appId);
128 panel.indicators.hide();
129 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
132 // For autopilot consumption
133 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
135 // Note when greeter is waiting on PAM, so that we can disable edges until
136 // we know which user data to show and whether the session is locked.
137 readonly property bool waitingOnGreeter: greeter && greeter.waiting
139 property real edgeSize: units.gu(settings.edgeDragWidth)
142 id: wallpaperResolver
143 objectName: "wallpaperResolver"
145 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
146 readonly property bool hasCustomBackground: background != defaultBackground
148 // Use a cached version of the scaled-down wallpaper (as sometimes the
149 // image can be quite big compared to the device size, including for
150 // our default wallpaper). We use a name=wallpaper argument here to
151 // make sure we don't litter our cache with lots of scaled images. We
152 // only need to bother caching one at a time.
153 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
156 id: backgroundSettings
157 schema.id: "org.gnome.desktop.background"
161 AccountsService.backgroundFile,
162 backgroundSettings.pictureUri,
167 readonly property alias greeter: greeterLoader.item
169 function activateApplication(appId) {
170 // Either open the app in our own session, or -- if we're acting as a
171 // greeter -- ask the user's session to open it for us.
172 if (shell.mode === "greeter") {
173 activateURL("application:///" + appId + ".desktop");
179 function activateURL(url) {
180 SessionBroadcast.requestUrlStart(AccountsService.user, url);
181 greeter.notifyUserRequestedApp();
182 panel.indicators.hide();
185 function startApp(appId) {
186 if (ApplicationManager.findApplication(appId)) {
187 ApplicationManager.requestFocusApplication(appId);
189 ApplicationManager.startApplication(appId);
193 function startLockedApp(app) {
194 if (greeter.locked) {
195 greeter.lockedApp = app;
197 startApp(app); // locked apps are always in our same session
201 target: LauncherModel
202 property: "applicationManager"
203 value: ApplicationManager
206 Component.onCompleted: {
207 finishStartUpTimer.start();
216 objectName: "dashCommunicator"
220 id: physicalKeysMapper
221 objectName: "physicalKeysMapper"
223 onPowerKeyLongPressed: dialogs.showPowerDialog();
224 onVolumeDownTriggered: volumeControl.volumeDown();
225 onVolumeUpTriggered: volumeControl.volumeUp();
226 onScreenshotTriggered: itemGrabber.capture(shell);
230 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
235 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
236 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
240 objectName: "windowInputMonitor"
241 onHomeKeyActivated: {
242 // Ignore when greeter is active, to avoid pocket presses
243 if (!greeter.active) {
248 onTouchBegun: { cursor.opacity = 0; }
250 // move the (hidden) cursor to the last known touch position
251 var mappedCoords = mapFromItem(null, pos.x, pos.y);
252 cursor.x = mappedCoords.x;
253 cursor.y = mappedCoords.y;
259 schema.id: "com.canonical.Unity8"
264 objectName: "panelState"
271 height: parent.height
273 TopLevelWindowModel {
274 id: topLevelSurfaceList
275 objectName: "topLevelSurfaceList"
276 applicationManager: ApplicationManager // it's a singleton
285 dragAreaWidth: shell.edgeSize
286 background: wallpaperResolver.background
288 applicationManager: ApplicationManager
289 topLevelSurfaceList: topLevelSurfaceList
290 inputMethodRect: inputMethod.visibleRect
292 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
294 : shell.usageScenario
296 mode: usageScenario == "phone" ? "staged"
297 : usageScenario == "tablet" ? "stagedWithSideStage"
300 shellOrientation: shell.orientation
301 shellOrientationAngle: shell.orientationAngle
302 orientations: shell.orientations
303 nativeWidth: shell.nativeWidth
304 nativeHeight: shell.nativeHeight
306 interactive: (!greeter || !greeter.shown)
307 && panel.indicators.fullyClosed
308 && !notifications.useModal
310 onInteractiveChanged: { if (interactive) { focus = true; } }
312 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
313 suspended: greeter.shown
314 altTabPressed: physicalKeysMapper.altTabPressed
315 oskEnabled: shell.oskEnabled
316 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
317 panelState: panelState
323 objectName: "inputMethod"
324 surface: topLevelSurfaceList.inputMethodSurface
327 topMargin: panel.panelHeight
328 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
330 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
335 objectName: "greeterLoader"
337 anchors.topMargin: panel.panelHeight
339 if (shell.mode != "shell") {
340 if (screenWindow.primary) return integratedGreeter;
341 return secondaryGreeter;
343 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
346 item.objectName = "greeter"
351 id: integratedGreeter
354 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
355 hides: [launcher, panel.indicators]
356 tabletMode: shell.usageScenario != "phone"
357 forcedUnlock: wizard.active || shell.mode === "full-shell"
358 background: wallpaperResolver.cachedBackground
359 hasCustomBackground: wallpaperResolver.hasCustomBackground
360 allowFingerprint: !dialogs.hasActiveDialog &&
361 !notifications.topmostIsFullscreen &&
362 !panel.indicators.shown
364 // avoid overlapping with Launcher's edge drag area
365 // FIXME: Fix TouchRegistry & friends and remove this workaround
366 // Issue involves launcher's DDA getting disabled on a long
368 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
375 if (!tutorial.running) {
380 onEmergencyCall: startLockedApp("dialer-app")
387 hides: [launcher, panel.indicators]
392 // See powerConnection for why this is useful
393 id: showGreeterDelayed
405 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
406 // We just received an incoming call while locked. The
407 // indicator will have already launched dialer-app for us, but
408 // there is a race between "hasCalls" changing and the dialer
409 // starting up. So in case we lose that race, we'll start/
410 // focus the dialer ourselves here too. Even if the indicator
411 // didn't launch the dialer for some reason (or maybe a call
412 // started via some other means), if an active call is
413 // happening, we want to be in the dialer.
414 startLockedApp("dialer-app")
424 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
425 !callManager.hasCalls && !wizard.active) {
426 // We don't want to simply call greeter.showNow() here, because
427 // that will take too long. Qt will delay button event
428 // handling until the greeter is done loading and may think the
429 // user held down the power button the whole time, leading to a
430 // power dialog being shown. Instead, delay showing the
431 // greeter until we've finished handling the event. We could
432 // make the greeter load asynchronously instead, but that
433 // introduces a whole host of timing issues, especially with
434 // its animations. So this is simpler.
435 showGreeterDelayed.start();
440 function showHome() {
441 greeter.notifyUserRequestedApp();
443 if (shell.mode === "greeter") {
444 SessionBroadcast.requestHomeShown(AccountsService.user);
446 var animate = !LightDMService.greeter.active && !stages.shown;
447 dash.setCurrentScope(0, animate, false);
448 ApplicationManager.requestFocusApplication("unity8-dash");
461 anchors.fill: parent //because this draws indicator menus
464 available: tutorial.panelEnabled
465 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
466 && (!greeter || !greeter.hasLockedApp)
467 && !shell.waitingOnGreeter
468 && settings.enableIndicatorMenu
469 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
471 minimizedPanelHeight: units.gu(3)
472 expandedPanelHeight: units.gu(7)
474 indicatorsModel: Indicators.IndicatorsModel {
475 // tablet and phone both use the same profile
476 // FIXME: use just "phone" for greeter too, but first fix
477 // greeter app launching to either load the app inside the
478 // greeter or tell the session to load the app. This will
479 // involve taking the url-dispatcher dbus name and using
480 // SessionBroadcast to tell the session.
481 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
482 Component.onCompleted: load();
487 greeterShown: greeter.shown
490 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
491 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
493 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
494 || greeter.hasLockedApp
495 locked: greeter && greeter.active
496 panelState: panelState
501 objectName: "launcher"
503 anchors.top: parent.top
504 anchors.topMargin: inverted ? 0 : panel.panelHeight
505 anchors.bottom: parent.bottom
507 dragAreaWidth: shell.edgeSize
508 available: tutorial.launcherEnabled
509 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
510 && !greeter.hasLockedApp
511 && !shell.waitingOnGreeter
512 && settings.enableLauncher
513 inverted: shell.usageScenario !== "desktop"
514 superPressed: physicalKeysMapper.superPressed
515 superTabPressed: physicalKeysMapper.superTabPressed
516 panelWidth: units.gu(settings.launcherWidth)
517 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
518 blurSource: greeter.shown ? greeter : stages
519 topPanelHeight: panel.panelHeight
520 drawerEnabled: !greeter.shown
522 onShowDashHome: showHome()
523 onLauncherApplicationSelected: {
524 greeter.notifyUserRequestedApp();
525 shell.activateApplication(appId);
529 panel.indicators.hide()
539 shortcut: Qt.MetaModifier | Qt.Key_A
541 launcher.openDrawer(true);
545 shortcut: Qt.AltModifier | Qt.Key_F1
547 launcher.openForKeyboardNavigation();
551 shortcut: Qt.MetaModifier | Qt.Key_0
553 if (LauncherModel.get(9)) {
554 activateApplication(LauncherModel.get(9).appId);
561 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
563 if (LauncherModel.get(index)) {
564 activateApplication(LauncherModel.get(index).appId);
571 KeyboardShortcutsOverlay {
572 objectName: "shortcutsOverlay"
573 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
574 && height < parent.height - padding - panel.panelHeight
575 anchors.centerIn: parent
576 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
577 anchors.verticalCenterOffset: panel.panelHeight/2
579 opacity: enabled ? 0.95 : 0
581 Behavior on opacity {
582 UbuntuNumberAnimation {}
588 objectName: "tutorial"
591 paused: callManager.hasCalls || !greeter || greeter.active ||
593 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
594 inputMethod.visible ||
595 (launcher.shown && !launcher.lockedVisible) ||
596 panel.indicators.shown || stage.rightEdgeDragProgress > 0
597 usageScenario: shell.usageScenario
598 lastInputTimestamp: inputFilter.lastInputTimestamp
608 deferred: shell.mode === "greeter"
610 function unlockWhenDoneWithWizard() {
612 Connectivity.unlockAllModems();
616 Component.onCompleted: unlockWhenDoneWithWizard()
617 onActiveChanged: unlockWhenDoneWithWizard()
620 MouseArea { // modal notifications prevent interacting with other contents
622 visible: notifications.useModal
629 model: NotificationBackend.Model
631 hasMouse: shell.hasMouse
632 background: wallpaperResolver.cachedBackground
634 y: topmostIsFullscreen ? 0 : panel.panelHeight
635 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
640 when: overlay.width <= units.gu(60)
642 target: notifications
643 anchors.left: parent.left
644 anchors.right: parent.right
649 when: overlay.width > units.gu(60)
651 target: notifications
652 anchors.left: undefined
653 anchors.right: parent.right
655 PropertyChanges { target: notifications; width: units.gu(38) }
663 objectName: "dialogs"
666 usageScenario: shell.usageScenario
668 shutdownFadeOutRectangle.enabled = true;
669 shutdownFadeOutRectangle.visible = true;
670 shutdownFadeOut.start();
675 target: SessionBroadcast
676 onShowHome: if (shell.mode !== "greeter") showHome()
681 objectName: "urlDispatcher"
682 active: shell.mode === "greeter"
683 onUrlRequested: shell.activateURL(url)
690 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
693 ignoreUnknownSignals: true
694 onItemSnapshotRequested: itemGrabber.capture(item)
699 id: cursorHidingTimer
701 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
702 onTriggered: cursor.opacity = 0;
707 visible: shell.hasMouse
710 topBoundaryOffset: panel.panelHeight
712 confiningItem: stage.itemConfiningMouseCursor
716 readonly property var previewRectangle: stage.previewRectangle.target &&
717 stage.previewRectangle.target.dragging ?
718 stage.previewRectangle : null
720 onPushedLeftBoundary: {
721 if (buttons === Qt.NoButton) {
722 launcher.pushEdge(amount);
723 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
724 previewRectangle.maximizeLeft(amount);
728 onPushedRightBoundary: {
729 if (buttons === Qt.NoButton) {
730 stage.pushRightEdge(amount);
731 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
732 previewRectangle.maximizeRight(amount);
736 onPushedTopBoundary: {
737 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
738 previewRectangle.maximize(amount);
741 onPushedTopLeftCorner: {
742 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
743 previewRectangle.maximizeTopLeft(amount);
746 onPushedTopRightCorner: {
747 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
748 previewRectangle.maximizeTopRight(amount);
751 onPushedBottomLeftCorner: {
752 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
753 previewRectangle.maximizeBottomLeft(amount);
756 onPushedBottomRightCorner: {
757 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
758 previewRectangle.maximizeBottomRight(amount);
762 if (previewRectangle) {
763 previewRectangle.stop();
771 Behavior on opacity { UbuntuNumberAnimation {} }
774 // non-visual objects
776 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
781 id: shutdownFadeOutRectangle
788 NumberAnimation on opacity {
793 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
794 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