on e7bca7e help sign in
unity8/Shell.qml
620 lines 22 kB history raw
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
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.
7 *
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.
12 *
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/>.
15 */
17import QtQuick 2.0
18import AccountsService 0.1
19import GSettings 1.0
20import Unity.Application 0.1
21import Ubuntu.Components 0.1
22import Ubuntu.Gestures 0.1
23import Unity.Launcher 0.1
24import Powerd 0.1
25import SessionBroadcast 0.1
26import SessionManager 0.1
27import "Dash"
28import "Launcher"
29import "Panel"
30import "Hud"
31import "Components"
32import "Components/Math.js" as MathLocal
33import "Bottombar"
34import "SideStage"
35import "Notifications"
36import Unity.Notifications 1.0 as NotificationBackend
38BasicShell {
39 id: shell
41 readonly property real panelHeight: panel.panelHeight
43 property bool dashShown: dash.shown
44 property bool stageScreenshotsReady: {
45 if (sideStage.shown) {
46 if (mainStage.applications.count > 0) {
47 return mainStage.usingScreenshots && sideStage.usingScreenshots;
48 } else {
49 return sideStage.usingScreenshots;
50 }
51 } else {
52 return mainStage.usingScreenshots;
53 }
54 }
56 property ListModel searchHistory: SearchHistoryModel {}
58 property var applicationManager: ApplicationManagerWrapper {}
60 Binding {
61 target: LauncherModel
62 property: "applicationManager"
63 value: ApplicationManager
64 }
66 Component.onCompleted: {
67 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
69 applicationManager.sideStageEnabled = Qt.binding(function() { return sideStage.enabled })
71 // FIXME: if application focused before shell starts, shell draws on top of it only.
72 // We should detect already running applications on shell start and bring them to the front.
73 applicationManager.unfocusCurrentApplication();
74 }
76 readonly property bool applicationFocused: !!applicationManager.mainStageFocusedApplication
77 || !!applicationManager.sideStageFocusedApplication
78 // Used for autopilot testing.
79 readonly property string currentFocusedAppId: ApplicationManager.focusedApplicationId
81 readonly property bool fullscreenMode: {
82 if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode
83 return mainStage.switchingFromFullscreenToFullscreen;
84 } else if (applicationManager.mainStageFocusedApplication) {
85 return applicationManager.mainStageFocusedApplication.fullscreen;
86 } else {
87 return false;
88 }
89 }
91 function activateApplication(desktopFile, argument) {
92 if (applicationManager) {
93 // For newly started applications, as it takes them time to draw their first frame
94 // we add a delay before we hide the animation screenshots to compensate.
95 var addDelay = !applicationManager.getApplicationFromDesktopFile(desktopFile);
97 var application;
98 application = applicationManager.activateApplication(desktopFile, argument);
99 if (application == null) {
100 return;
101 }
102 if (application.stage == ApplicationInfo.MainStage || !sideStage.enabled) {
103 mainStage.activateApplication(desktopFile, addDelay);
104 } else {
105 sideStage.activateApplication(desktopFile, addDelay);
106 }
107 stages.show();
108 }
109 }
111 GSettings {
112 id: backgroundSettings
113 schema.id: "org.gnome.desktop.background"
114 }
115 backgroundSource: backgroundSettings.pictureUri
117 Item {
118 id: underlay
119 objectName: "underlay"
120 anchors.fill: parent
122 // Whether the underlay is fully covered by opaque UI elements.
123 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
125 readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0)
126 || (sideStage.applications && sideStage.applications.count > 0))
128 // Whether the user should see the topmost application surface (if there's one at all).
129 readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden
130 && !mainStage.usingScreenshots // but want sideStage animating over app surface
134 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
135 // through the translucent parts of the shell surface.
136 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
138 CrossFadeImage {
139 id: backgroundImage
140 objectName: "backgroundImage"
142 anchors.fill: parent
143 source: shell.background
144 fillMode: Image.PreserveAspectCrop
145 }
147 Rectangle {
148 anchors.fill: parent
149 color: "black"
150 opacity: dash.disappearingAnimationProgress
151 }
153 Dash {
154 id: dash
155 objectName: "dash"
157 hides: [stages, launcher, panel.indicators]
158 shown: disappearingAnimationProgress !== 1.0
159 enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
160 // FIXME: unfocus all applications when going back to the dash
161 onEnabledChanged: {
162 if (enabled) {
163 shell.applicationManager.unfocusCurrentApplication()
164 }
165 }
167 anchors {
168 fill: parent
169 topMargin: panel.panelHeight
170 }
172 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
173 opacity: 1.0 - disappearingAnimationProgress
174 property real disappearingAnimationProgress: stagesOuterContainer.showProgress
176 // FIXME: only necessary because stagesOuterContainer.showProgress
177 // is not animated
178 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
179 }
180 }
182 Item {
183 id: stagesOuterContainer
185 width: parent.width
186 height: parent.height
187 x: launcher.progress
188 Behavior on x {SmoothedAnimation{velocity: 600}}
190 property real showProgress:
191 MathLocal.clamp(1 - (x + stages.x) / shell.width, 0, 1)
193 Showable {
194 id: stages
195 objectName: "stages"
197 x: width
199 property bool fullyShown: shown && x == 0 && parent.x == 0
200 property bool fullyHidden: !shown && x == width
201 hides: [panel.indicators]
202 shown: false
203 opacity: 1.0
204 showAnimation: StandardAnimation { property: "x"; duration: 350; to: 0; easing.type: Easing.OutCubic }
205 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: width; easing.type: Easing.OutCubic }
207 width: parent.width
208 height: parent.height
210 // close the stages when no focused application remains
211 Connections {
212 target: shell.applicationManager
213 onMainStageFocusedApplicationChanged: stages.closeIfNoApplications()
214 onSideStageFocusedApplicationChanged: stages.closeIfNoApplications()
215 ignoreUnknownSignals: true
216 }
218 function closeIfNoApplications() {
219 if (!shell.applicationManager.mainStageFocusedApplication
220 && !shell.applicationManager.sideStageFocusedApplication
221 && shell.applicationManager.mainStageApplications.count == 0
222 && shell.applicationManager.sideStageApplications.count == 0) {
223 stages.hide();
224 }
225 }
227 // show the stages when an application gets the focus
228 Connections {
229 target: shell.applicationManager
230 onMainStageFocusedApplicationChanged: {
231 if (shell.applicationManager.mainStageFocusedApplication) {
232 mainStage.show();
233 stages.show();
234 }
235 }
236 onSideStageFocusedApplicationChanged: {
237 if (shell.applicationManager.sideStageFocusedApplication) {
238 sideStage.show();
239 stages.show();
240 }
241 }
242 ignoreUnknownSignals: true
243 }
245 Stage {
246 id: mainStage
248 anchors.fill: parent
249 fullyShown: stages.fullyShown
250 fullyHidden: stages.fullyHidden
251 shouldUseScreenshots: !fullyShown
252 rightEdgeEnabled: !sideStage.enabled
254 applicationManager: shell.applicationManager
255 rightEdgeDraggingAreaWidth: shell.edgeSize
256 normalApplicationY: shell.panelHeight
258 shown: true
259 function show() {
260 stages.show();
261 }
262 function hide() {
263 }
265 // FIXME: workaround the fact that focusing a main stage application
266 // raises its surface on top of all other surfaces including the ones
267 // that belong to side stage applications.
268 onFocusedApplicationChanged: {
269 if (focusedApplication && sideStage.focusedApplication && sideStage.fullyShown) {
270 shell.applicationManager.focusApplication(sideStage.focusedApplication);
271 }
272 }
273 }
275 SideStage {
276 id: sideStage
278 applicationManager: shell.applicationManager
279 rightEdgeDraggingAreaWidth: shell.edgeSize
280 normalApplicationY: shell.panelHeight
282 onShownChanged: {
283 if (!shown && mainStage.applications.count == 0) {
284 stages.hide();
285 }
286 }
287 // FIXME: when hiding the side stage, refocus the main stage
288 // application so that it goes in front of the side stage
289 // application and hides it
290 onFullyShownChanged: {
291 if (!fullyShown && stages.fullyShown && sideStage.focusedApplication != null) {
292 shell.applicationManager.focusApplication(mainStage.focusedApplication);
293 }
294 }
296 enabled: shell.width >= units.gu(60)
297 visible: enabled
298 fullyShown: stages.fullyShown && shown
299 && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue
300 shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed
302 available: enabled
303 hides: [launcher, panel.indicators]
304 shown: false
305 showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint }
306 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.closedValue; easing.type: Easing.OutQuint }
308 width: units.gu(40)
309 height: stages.height
310 handleExpanded: sideStageRevealer.pressed
311 }
313 Revealer {
314 id: sideStageRevealer
316 enabled: mainStage.applications.count > 0 && sideStage.applications.count > 0
317 && sideStage.available
318 direction: Qt.RightToLeft
319 openedValue: parent.width - sideStage.width
320 hintDisplacement: units.gu(3)
321 /* The size of the sidestage handle needs to be bigger than the
322 typical size used for edge detection otherwise it is really
323 hard to grab.
324 */
325 handleSize: sideStage.shown ? units.gu(4) : shell.edgeSize
326 closedValue: parent.width + sideStage.handleSizeCollapsed
327 target: sideStage
328 x: parent.width - width
329 width: sideStage.width + handleSize * 0.7
330 height: sideStage.height
331 orientation: Qt.Horizontal
332 }
334 DragHandle {
335 id: stagesDragHandle
337 anchors.top: parent.top
338 anchors.bottom: parent.bottom
339 anchors.right: parent.left
341 width: shell.edgeSize
342 direction: Direction.Leftwards
343 enabled: greeter.showProgress == 0 && edgeDemo.dashEnabled
344 property bool haveApps: mainStage.applications.count > 0 || sideStage.applications.count > 0
346 maxTotalDragDistance: haveApps ? parent.width : parent.width * 0.7
347 // Make autocompletion impossible when !haveApps
348 edgeDragEvaluator.minDragDistance: haveApps ? maxTotalDragDistance * 0.1 : Number.MAX_VALUE
349 }
350 }
351 }
353 InputFilterArea {
354 anchors.fill: parent
355 blockInput: !applicationFocused || launcher.shown
356 || panel.indicators.shown || hud.shown
357 }
359 Connections {
360 id: sessionConnection
361 target: SessionManager
363 property var previousMainApp: null
364 property var previousSideApp: null
366 function removeApplicationFocus() {
367 sessionConnection.previousMainApp = applicationManager.mainStageFocusedApplication;
368 sessionConnection.previousSideApp = applicationManager.sideStageFocusedApplication;
369 applicationManager.unfocusCurrentApplication();
370 }
372 function restoreApplicationFocus() {
373 if (sessionConnection.previousMainApp) {
374 applicationManager.focusApplication(sessionConnection.previousMainApp);
375 sessionConnection.previousMainApp = null;
376 }
377 if (sessionConnection.previousSideApp) {
378 applicationManager.focusApplication(sessionConnection.previousSideApp);
379 sessionConnection.previousSideApp = null;
380 }
381 }
383 onActiveChanged: {
384 if (SessionManager.active) {
385 restoreApplicationFocus();
386 } else {
387 removeApplicationFocus();
388 }
389 }
390 }
392 Connections {
393 id: powerConnection
394 target: Powerd
396 onDisplayPowerStateChange: {
397 // We ignore any display-off signals when the proximity sensor
398 // is active. This usually indicates something like a phone call.
399 if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
400 SessionManager.lock();
401 }
403 // No reason to chew demo CPU when user isn't watching
404 if (status == Powerd.Off) {
405 edgeDemo.paused = true;
406 } else if (status == Powerd.On) {
407 edgeDemo.paused = false;
408 }
409 }
410 }
412 function showHome(fromLauncher) {
413 var animate = fromLauncher && !stages.shown
414 dash.setCurrentScope("home.scope", animate, false)
415 stages.hide()
416 }
418 Item {
419 id: overlay
421 anchors.fill: parent
423 Panel {
424 id: panel
425 anchors.fill: parent //because this draws indicator menus
426 indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
427 indicators {
428 hides: [launcher]
429 available: edgeDemo.panelEnabled
430 contentEnabled: edgeDemo.panelContentEnabled
431 }
432 fullscreenMode: shell.fullscreenMode
433 searchVisible: dash.shown
435 InputFilterArea {
436 anchors {
437 top: parent.top
438 left: parent.left
439 right: parent.right
440 }
441 height: (panel.fullscreenMode) ? shell.edgeSize : panel.panelHeight
442 blockInput: true
443 }
444 }
446 Hud {
447 id: hud
449 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
450 height: parent.height
452 available: !panel.indicators.shown && edgeDemo.dashEnabled
453 shown: false
454 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
455 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
457 Connections {
458 target: shell.applicationManager
459 onMainStageFocusedApplicationChanged: hud.hide()
460 onSideStageFocusedApplicationChanged: hud.hide()
461 }
462 }
464 Revealer {
465 id: hudRevealer
467 enabled: hud.shown
468 width: hud.width
469 anchors.left: hud.left
470 height: parent.height
471 target: hud.revealerTarget
472 closedValue: height
473 openedValue: 0
474 direction: Qt.RightToLeft
475 orientation: Qt.Vertical
476 handleSize: hud.handleHeight
477 onCloseClicked: target.hide()
478 }
480 Bottombar {
481 id: bottombar
482 theHud: hud
483 anchors.fill: parent
484 enabled: hud.available
485 applicationIsOnForeground: applicationFocused
486 }
488 InputFilterArea {
489 blockInput: launcher.shown
490 anchors {
491 top: parent.top
492 bottom: parent.bottom
493 left: parent.left
494 }
495 width: launcher.width
496 }
498 Launcher {
499 id: launcher
501 anchors.top: parent.top
502 anchors.bottom: parent.bottom
503 width: parent.width
504 dragAreaWidth: shell.edgeSize
505 available: edgeDemo.launcherEnabled
506 onDashItemSelected: showHome(true)
507 onDash: {
508 if (stages.shown) {
509 dash.setCurrentScope("applications.scope", true /* animate */, true /* reset */)
510 stages.hide();
511 launcher.hide();
512 }
513 }
514 onLauncherApplicationSelected:{
515 shell.activateApplication(appId)
516 }
517 onShownChanged: {
518 if (shown) {
519 panel.indicators.hide()
520 hud.hide()
521 bottombar.hide()
522 }
523 }
524 }
526 Notifications {
527 id: notifications
529 model: NotificationBackend.Model
530 anchors {
531 top: parent.top
532 right: parent.right
533 bottom: parent.bottom
534 leftMargin: units.gu(1)
535 rightMargin: units.gu(1)
536 topMargin: panel.panelHeight + units.gu(1)
537 }
538 states: [
539 State {
540 name: "narrow"
541 when: overlay.width <= units.gu(60)
542 AnchorChanges { target: notifications; anchors.left: parent.left }
543 },
544 State {
545 name: "wide"
546 when: overlay.width > units.gu(60)
547 AnchorChanges { target: notifications; anchors.left: undefined }
548 PropertyChanges { target: notifications; width: units.gu(38) }
549 }
550 ]
552 InputFilterArea {
553 anchors { left: parent.left; right: parent.right }
554 height: parent.contentHeight
555 blockInput: height > 0
556 }
557 }
558 }
560 focus: true
561 onFocusChanged: if (!focus) forceActiveFocus();
563 InputFilterArea {
564 anchors {
565 top: parent.top
566 bottom: parent.bottom
567 left: parent.left
568 }
569 width: shell.edgeSize
570 blockInput: true
571 }
573 Binding {
574 target: i18n
575 property: "domain"
576 value: "unity8"
577 }
579 OSKController {
580 anchors.topMargin: panel.panelHeight
581 anchors.fill: parent // as needs to know the geometry of the shell
582 }
584 //FIXME: This should be handled in the input stack, keyboard shouldnt propagate
585 MouseArea {
586 anchors.bottom: parent.bottom
587 anchors.left: parent.left
588 anchors.right: parent.right
589 height: shell.applicationManager ? shell.applicationManager.keyboardHeight : 0
591 enabled: shell.applicationManager && shell.applicationManager.keyboardVisible
592 }
594 Label {
595 anchors.centerIn: parent
596 visible: applicationManager.fake
597 text: "EARLY ALPHA\nNOT READY FOR USE"
598 color: "lightgrey"
599 opacity: 0.2
600 font.weight: Font.Black
601 horizontalAlignment: Text.AlignHCenter
602 verticalAlignment: Text.AlignVCenter
603 fontSizeMode: Text.Fit
604 rotation: -45
605 scale: Math.min(parent.width, parent.height) / width
606 }
608 EdgeDemo {
609 id: edgeDemo
610 launcher: launcher
611 dash: dash
612 indicators: panel.indicators
613 underlay: underlay
614 }
616 Connections {
617 target: SessionBroadcast
618 onShowHome: showHome(false)
619 }