on a2a6340 help sign in
unity8/Shell.qml
576 lines 21 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 Ubuntu.Application 0.1
19import Ubuntu.Components 0.1
20import Ubuntu.Gestures 0.1
21import Unity.Launcher 0.1
22import Powerd 0.1
23import SessionManager 0.1
24import "Dash"
25import "Launcher"
26import "Panel"
27import "Hud"
28import "Components"
29import "Components/Math.js" as MathLocal
30import "Bottombar"
31import "SideStage"
32import "Notifications"
33import Unity.Notifications 1.0 as NotificationBackend
35BasicShell {
36 id: shell
38 readonly property real panelHeight: panel.panelHeight
40 property bool dashShown: dash.shown
41 property bool stageScreenshotsReady: {
42 if (sideStage.shown) {
43 if (mainStage.applications.count > 0) {
44 return mainStage.usingScreenshots && sideStage.usingScreenshots;
45 } else {
46 return sideStage.usingScreenshots;
47 }
48 } else {
49 return mainStage.usingScreenshots;
50 }
51 }
53 property ListModel searchHistory: SearchHistoryModel {}
55 property var applicationManager: ApplicationManagerWrapper {}
57 Component.onCompleted: {
58 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
60 applicationManager.sideStageEnabled = Qt.binding(function() { return sideStage.enabled })
62 // FIXME: if application focused before shell starts, shell draws on top of it only.
63 // We should detect already running applications on shell start and bring them to the front.
64 applicationManager.unfocusCurrentApplication();
65 }
67 readonly property bool fullscreenMode: {
68 if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode
69 return mainStage.switchingFromFullscreenToFullscreen;
70 } else if (applicationManager.mainStageFocusedApplication) {
71 return applicationManager.mainStageFocusedApplication.fullscreen;
72 } else {
73 return false;
74 }
75 }
77 Connections {
78 target: applicationManager
79 ignoreUnknownSignals: true
80 onFocusRequested: {
81 shell.activateApplication(desktopFile);
82 }
83 }
85 function activateApplication(desktopFile, argument) {
86 if (applicationManager) {
87 // For newly started applications, as it takes them time to draw their first frame
88 // we add a delay before we hide the animation screenshots to compensate.
89 var addDelay = !applicationManager.getApplicationFromDesktopFile(desktopFile);
91 var application;
92 application = applicationManager.activateApplication(desktopFile, argument);
93 if (application == null) {
94 return;
95 }
96 if (application.stage == ApplicationInfo.MainStage || !sideStage.enabled) {
97 mainStage.activateApplication(desktopFile, addDelay);
98 } else {
99 sideStage.activateApplication(desktopFile, addDelay);
100 }
101 stages.show();
102 }
103 }
105 Item {
106 id: underlay
107 objectName: "underlay"
108 anchors.fill: parent
110 // Whether the underlay is fully covered by opaque UI elements.
111 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
113 readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0)
114 || (sideStage.applications && sideStage.applications.count > 0))
116 // Whether the user should see the topmost application surface (if there's one at all).
117 readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden
118 && !mainStage.usingScreenshots // but want sideStage animating over app surface
122 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
123 // through the translucent parts of the shell surface.
124 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
126 CrossFadeImage {
127 id: backgroundImage
128 objectName: "backgroundImage"
130 anchors.fill: parent
131 source: shell.background
132 }
134 Rectangle {
135 anchors.fill: parent
136 color: "black"
137 opacity: dash.disappearingAnimationProgress
138 }
140 Dash {
141 id: dash
142 objectName: "dash"
144 hides: [stages, launcher, panel.indicators]
145 shown: disappearingAnimationProgress !== 1.0
146 enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
147 // FIXME: unfocus all applications when going back to the dash
148 onEnabledChanged: {
149 if (enabled) {
150 shell.applicationManager.unfocusCurrentApplication()
151 }
152 }
154 anchors {
155 fill: parent
156 topMargin: panel.panelHeight
157 }
159 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
160 opacity: 1.0 - disappearingAnimationProgress
161 property real disappearingAnimationProgress: stagesOuterContainer.showProgress
163 // FIXME: only necessary because stagesOuterContainer.showProgress
164 // is not animated
165 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
166 }
167 }
169 Item {
170 id: stagesOuterContainer
172 width: parent.width
173 height: parent.height
174 x: launcher.progress
175 Behavior on x {SmoothedAnimation{velocity: 600}}
177 property real showProgress:
178 MathLocal.clamp(1 - (x + stages.x) / shell.width, 0, 1)
180 Showable {
181 id: stages
182 objectName: "stages"
184 x: width
186 property bool fullyShown: shown && x == 0 && parent.x == 0
187 property bool fullyHidden: !shown && x == width
188 hides: [launcher, panel.indicators]
189 shown: false
190 opacity: 1.0
191 showAnimation: StandardAnimation { property: "x"; duration: 350; to: 0; easing.type: Easing.OutCubic }
192 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: width; easing.type: Easing.OutCubic }
194 width: parent.width
195 height: parent.height
197 // close the stages when no focused application remains
198 Connections {
199 target: shell.applicationManager
200 onMainStageFocusedApplicationChanged: stages.closeIfNoApplications()
201 onSideStageFocusedApplicationChanged: stages.closeIfNoApplications()
202 ignoreUnknownSignals: true
203 }
205 function closeIfNoApplications() {
206 if (!shell.applicationManager.mainStageFocusedApplication
207 && !shell.applicationManager.sideStageFocusedApplication
208 && shell.applicationManager.mainStageApplications.count == 0
209 && shell.applicationManager.sideStageApplications.count == 0) {
210 stages.hide();
211 }
212 }
214 // show the stages when an application gets the focus
215 Connections {
216 target: shell.applicationManager
217 onMainStageFocusedApplicationChanged: {
218 if (shell.applicationManager.mainStageFocusedApplication) {
219 mainStage.show();
220 stages.show();
221 }
222 }
223 onSideStageFocusedApplicationChanged: {
224 if (shell.applicationManager.sideStageFocusedApplication) {
225 sideStage.show();
226 stages.show();
227 }
228 }
229 ignoreUnknownSignals: true
230 }
232 Stage {
233 id: mainStage
235 anchors.fill: parent
236 fullyShown: stages.fullyShown
237 fullyHidden: stages.fullyHidden
238 shouldUseScreenshots: !fullyShown
239 rightEdgeEnabled: !sideStage.enabled
241 applicationManager: shell.applicationManager
242 rightEdgeDraggingAreaWidth: shell.edgeSize
243 normalApplicationY: shell.panelHeight
245 shown: true
246 function show() {
247 stages.show();
248 }
249 function hide() {
250 }
252 // FIXME: workaround the fact that focusing a main stage application
253 // raises its surface on top of all other surfaces including the ones
254 // that belong to side stage applications.
255 onFocusedApplicationChanged: {
256 if (focusedApplication && sideStage.focusedApplication && sideStage.fullyShown) {
257 shell.applicationManager.focusApplication(sideStage.focusedApplication);
258 }
259 }
260 }
262 SideStage {
263 id: sideStage
265 applicationManager: shell.applicationManager
266 rightEdgeDraggingAreaWidth: shell.edgeSize
267 normalApplicationY: shell.panelHeight
269 onShownChanged: {
270 if (!shown && mainStage.applications.count == 0) {
271 stages.hide();
272 }
273 }
274 // FIXME: when hiding the side stage, refocus the main stage
275 // application so that it goes in front of the side stage
276 // application and hides it
277 onFullyShownChanged: {
278 if (!fullyShown && stages.fullyShown && sideStage.focusedApplication != null) {
279 shell.applicationManager.focusApplication(mainStage.focusedApplication);
280 }
281 }
283 enabled: shell.width >= units.gu(60)
284 visible: enabled
285 fullyShown: stages.fullyShown && shown
286 && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue
287 shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed
289 available: enabled
290 hides: [launcher, panel.indicators]
291 shown: false
292 showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint }
293 hideAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.closedValue; easing.type: Easing.OutQuint }
295 width: units.gu(40)
296 height: stages.height
297 handleExpanded: sideStageRevealer.pressed
298 }
300 Revealer {
301 id: sideStageRevealer
303 enabled: mainStage.applications.count > 0 && sideStage.applications.count > 0
304 && sideStage.available
305 direction: Qt.RightToLeft
306 openedValue: parent.width - sideStage.width
307 hintDisplacement: units.gu(3)
308 /* The size of the sidestage handle needs to be bigger than the
309 typical size used for edge detection otherwise it is really
310 hard to grab.
311 */
312 handleSize: sideStage.shown ? units.gu(4) : shell.edgeSize
313 closedValue: parent.width + sideStage.handleSizeCollapsed
314 target: sideStage
315 x: parent.width - width
316 width: sideStage.width + handleSize * 0.7
317 height: sideStage.height
318 orientation: Qt.Horizontal
319 }
321 DragHandle {
322 id: stagesDragHandle
324 anchors.top: parent.top
325 anchors.bottom: parent.bottom
326 anchors.right: parent.left
328 width: shell.edgeSize
329 direction: Direction.Leftwards
330 enabled: edgeDemo.dashEnabled
331 property bool haveApps: mainStage.applications.count > 0 || sideStage.applications.count > 0
333 maxTotalDragDistance: haveApps ? parent.width : parent.width * 0.7
334 // Make autocompletion impossible when !haveApps
335 edgeDragEvaluator.minDragDistance: haveApps ? maxTotalDragDistance * 0.1 : Number.MAX_VALUE
336 }
337 }
338 }
340 Connections {
341 id: powerConnection
342 target: Powerd
344 property var previousMainApp: null
345 property var previousSideApp: null
347 function setFocused(focused) {
348 if (!focused) {
349 // FIXME: *FocusedApplication are not updated when unfocused, hence the need to check whether
350 // the stage was actually shown
351 if (mainStage.fullyShown) powerConnection.previousMainApp = applicationManager.mainStageFocusedApplication;
352 if (sideStage.fullyShown) powerConnection.previousSideApp = applicationManager.sideStageFocusedApplication;
353 applicationManager.unfocusCurrentApplication();
354 } else {
355 if (powerConnection.previousMainApp) {
356 applicationManager.focusApplication(powerConnection.previousMainApp);
357 powerConnection.previousMainApp = null;
358 }
359 if (powerConnection.previousSideApp) {
360 applicationManager.focusApplication(powerConnection.previousSideApp);
361 powerConnection.previousSideApp = null;
362 }
363 }
364 }
366 onDisplayPowerStateChange: {
367 // We ignore any display-off signals when the proximity sensor
368 // is active. This usually indicates something like a phone call.
369 if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
370 powerConnection.setFocused(false);
371 SessionManager.lock();
372 } else if (status == Powerd.On) {
373 powerConnection.setFocused(true);
374 }
376 // No reason to chew demo CPU when user isn't watching
377 if (status == Powerd.Off) {
378 edgeDemo.paused = true;
379 } else if (status == Powerd.On) {
380 edgeDemo.paused = false;
381 }
382 }
383 }
385 Item {
386 id: overlay
388 anchors.fill: parent
390 Panel {
391 id: panel
392 anchors.fill: parent //because this draws indicator menus
393 indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
394 indicators {
395 hides: [launcher]
396 available: edgeDemo.panelEnabled
397 contentEnabled: edgeDemo.panelContentEnabled
398 }
399 fullscreenMode: shell.fullscreenMode
401 InputFilterArea {
402 anchors.fill: parent
403 blockInput: panel.indicators.shown
404 }
405 }
407 Hud {
408 id: hud
410 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
411 height: parent.height
413 available: !panel.indicators.shown && edgeDemo.dashEnabled
414 shown: false
415 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
416 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
418 Connections {
419 target: shell.applicationManager
420 onMainStageFocusedApplicationChanged: hud.hide()
421 onSideStageFocusedApplicationChanged: hud.hide()
422 }
424 InputFilterArea {
425 anchors.fill: parent
426 blockInput: hud.shown
427 }
428 }
430 Revealer {
431 id: hudRevealer
433 enabled: hud.shown
434 width: hud.width
435 anchors.left: hud.left
436 height: parent.height
437 target: hud.revealerTarget
438 closedValue: height
439 openedValue: 0
440 direction: Qt.RightToLeft
441 orientation: Qt.Vertical
442 handleSize: hud.handleHeight
443 onCloseClicked: target.hide()
444 }
446 Bottombar {
447 theHud: hud
448 anchors.fill: parent
449 enabled: !panel.indicators.shown
450 applicationIsOnForeground: applicationManager.mainStageFocusedApplication
451 || applicationManager.sideStageFocusedApplication
452 }
454 InputFilterArea {
455 blockInput: launcher.shown
456 anchors {
457 top: parent.top
458 bottom: parent.bottom
459 left: parent.left
460 }
461 width: launcher.width
462 }
464 Launcher {
465 id: launcher
467 anchors.top: parent.top
468 anchors.bottom: parent.bottom
469 width: parent.width
470 dragAreaWidth: shell.edgeSize
471 available: edgeDemo.launcherEnabled
472 onDashItemSelected: {
473 // Animate if moving between application and dash
474 if (!stages.shown) {
475 dash.setCurrentScope("home.scope", true, false)
476 } else {
477 dash.setCurrentScope("home.scope", false, false)
478 }
479 stages.hide();
480 }
481 onDash: {
482 if (stages.shown) {
483 dash.setCurrentScope("applications.scope", true, false)
484 stages.hide();
485 launcher.hide();
486 }
487 }
488 onLauncherApplicationSelected:{
489 shell.activateApplication(desktopFile)
490 }
491 onShownChanged: {
492 if (shown) {
493 panel.indicators.hide()
494 hud.hide()
495 }
496 }
497 }
499 Notifications {
500 id: notifications
502 model: NotificationBackend.Model
503 anchors {
504 top: parent.top
505 right: parent.right
506 bottom: parent.bottom
507 leftMargin: units.gu(1)
508 rightMargin: units.gu(1)
509 topMargin: panel.panelHeight + units.gu(1)
510 }
511 states: [
512 State {
513 name: "narrow"
514 when: overlay.width <= units.gu(60)
515 AnchorChanges { target: notifications; anchors.left: parent.left }
516 },
517 State {
518 name: "wide"
519 when: overlay.width > units.gu(60)
520 AnchorChanges { target: notifications; anchors.left: undefined }
521 PropertyChanges { target: notifications; width: units.gu(38) }
522 }
523 ]
524 }
525 }
527 focus: true
529 InputFilterArea {
530 anchors {
531 top: parent.top
532 bottom: parent.bottom
533 left: parent.left
534 }
535 width: shell.edgeSize
536 blockInput: true
537 }
539 Binding {
540 target: i18n
541 property: "domain"
542 value: "unity8"
543 }
545 //FIXME: This should be handled in the input stack, keyboard shouldnt propagate
546 MouseArea {
547 anchors.bottom: parent.bottom
548 anchors.left: parent.left
549 anchors.right: parent.right
550 height: shell.applicationManager ? shell.applicationManager.keyboardHeight : 0
552 enabled: shell.applicationManager && shell.applicationManager.keyboardVisible
553 }
555 Label {
556 anchors.centerIn: parent
557 visible: applicationManager.fake
558 text: "EARLY ALPHA\nNOT READY FOR USE"
559 color: "lightgrey"
560 opacity: 0.2
561 font.weight: Font.Black
562 horizontalAlignment: Text.AlignHCenter
563 verticalAlignment: Text.AlignVCenter
564 fontSizeMode: Text.Fit
565 rotation: -45
566 scale: Math.min(parent.width, parent.height) / width
567 }
569 EdgeDemo {
570 id: edgeDemo
571 launcher: launcher
572 dash: dash
573 indicators: panel.indicators
574 underlay: underlay
575 }