on 8f21147 help sign in
unity8/qml/Shell.qml
512 lines 16 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 "Components"
31import "Notifications"
32import Unity.Notifications 1.0 as NotificationBackend
34BasicShell {
35 id: shell
37 readonly property real panelHeight: panel.panelHeight
39 property bool dashShown: dash.shown
41 property bool sideStageEnabled: shell.width >= units.gu(100)
42 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
44 function activateApplication(appId) {
45 if (ApplicationManager.findApplication(appId)) {
46 ApplicationManager.requestFocusApplication(appId);
47 stages.show(true);
48 if (stages.locked && ApplicationManager.focusedApplicationId == appId) {
49 applicationsDisplayLoader.item.select(appId);
50 }
51 } else {
52 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
53 ApplicationManager.startApplication(appId, execFlags);
54 stages.show(false);
55 }
56 }
58 Binding {
59 target: LauncherModel
60 property: "applicationManager"
61 value: ApplicationManager
62 }
64 Component.onCompleted: {
65 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
66 }
68 GSettings {
69 id: backgroundSettings
70 schema.id: "org.gnome.desktop.background"
71 }
72 backgroundSource: backgroundSettings.pictureUri
74 Item {
75 id: underlayClipper
76 anchors.fill: parent
77 anchors.rightMargin: stages.overlayWidth
78 clip: stages.overlayMode && !stages.painting
80 InputFilterArea {
81 anchors.fill: parent
82 blockInput: parent.clip
83 }
85 Item {
86 id: underlay
87 objectName: "underlay"
88 anchors.fill: parent
89 anchors.rightMargin: -parent.anchors.rightMargin
91 // Whether the underlay is fully covered by opaque UI elements.
92 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
94 // Whether the user should see the topmost application surface (if there's one at all).
95 readonly property bool applicationSurfaceShouldBeSeen: stages.shown && !stages.painting && !stages.overlayMode
97 // NB! Application surfaces are stacked behind the shell one. So they can only be seen by the user
98 // through the translucent parts of the shell surface.
99 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
101 Rectangle {
102 anchors.fill: parent
103 color: "black"
104 opacity: dash.disappearingAnimationProgress
105 }
107 Image {
108 anchors.fill: dash
109 source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
110 fillMode: Image.PreserveAspectCrop
111 horizontalAlignment: Image.AlignRight
112 verticalAlignment: Image.AlignTop
113 }
115 Dash {
116 id: dash
117 objectName: "dash"
119 hides: [stages, launcher, panel.indicators]
120 shown: disappearingAnimationProgress !== 1.0
121 enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
123 anchors {
124 fill: parent
125 topMargin: panel.panelHeight
126 }
128 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
129 opacity: 1.0 - disappearingAnimationProgress
130 property real disappearingAnimationProgress: {
131 if (stages.overlayMode) {
132 return 0;
133 } else {
134 return stages.showProgress
135 }
136 }
138 // FIXME: only necessary because stages.showProgress is not animated
139 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
140 }
141 }
142 }
144 EdgeDragArea {
145 id: stagesDragHandle
146 direction: Direction.Leftwards
148 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
149 width: shell.edgeSize
151 property real progress: stages.width
153 onTouchXChanged: {
154 if (status == DirectionalDragArea.Recognized) {
155 if (ApplicationManager.count == 0) {
156 progress = Math.max(stages.width - stagesDragHandle.width + touchX, stages.width * .3)
157 } else {
158 progress = stages.width - stagesDragHandle.width + touchX
159 }
160 }
161 }
163 onDraggingChanged: {
164 if (!dragging) {
165 if (ApplicationManager.count > 0 && progress < stages.width - units.gu(10)) {
166 stages.show(true)
167 }
168 stagesDragHandle.progress = stages.width;
169 }
170 }
171 }
173 Item {
174 id: stages
175 objectName: "stages"
176 width: parent.width
177 height: parent.height
179 x: {
180 if (shown) {
181 if (overlayMode || locked) {
182 return 0;
183 }
184 return launcher.progress
185 } else {
186 return stagesDragHandle.progress
187 }
188 }
190 Behavior on x { SmoothedAnimation { velocity: 600; duration: UbuntuAnimation.FastDuration } }
192 property bool shown: false
194 property real showProgress: overlayMode ? 0 : MathUtils.clamp(1 - x / shell.width, 0, 1)
196 property bool fullyShown: x == 0
197 property bool fullyHidden: x == width
199 property bool painting: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.painting : false
200 property bool fullscreen: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.fullscreen : false
201 property bool overlayMode: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.overlayMode : false
202 property int overlayWidth: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.overlayWidth : false
203 property bool locked: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.locked : false
205 function show(focusApp) {
206 shown = true;
207 panel.indicators.hide();
208 edgeDemo.stopDemo();
209 if (!ApplicationManager.focusedApplicationId && ApplicationManager.count > 0 && focusApp) {
210 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
211 }
212 }
214 function hide() {
215 shown = false;
216 if (ApplicationManager.focusedApplicationId) {
217 ApplicationManager.unfocusCurrentApplication();
218 }
219 }
221 Connections {
222 target: ApplicationManager
224 onFocusRequested: {
225 stages.show(true);
226 }
228 onFocusedApplicationIdChanged: {
229 if (ApplicationManager.focusedApplicationId.length > 0) {
230 stages.show(false);
231 } else {
232 if (!stages.overlayMode) {
233 stages.hide();
234 }
235 }
236 }
238 onApplicationAdded: {
239 stages.show(false);
240 }
242 onApplicationRemoved: {
243 if (ApplicationManager.focusedApplicationId.length == 0) {
244 stages.hide();
245 }
246 }
247 }
249 Binding {
250 target: ApplicationManager
251 property: "suspended"
252 value: !SessionManager.active
253 }
255 Loader {
256 id: applicationsDisplayLoader
257 anchors.fill: parent
259 source: shell.sideStageEnabled ? "Stages/StageWithSideStage.qml" : "Stages/PhoneStage.qml"
261 Binding {
262 target: applicationsDisplayLoader.item
263 property: "moving"
264 value: !stages.fullyShown
265 }
266 Binding {
267 target: applicationsDisplayLoader.item
268 property: "shown"
269 value: stages.shown
270 }
271 Binding {
272 target: applicationsDisplayLoader.item
273 property: "dragAreaWidth"
274 value: shell.edgeSize
275 }
276 }
277 }
279 InputFilterArea {
280 anchors.fill: parent
281 blockInput: ApplicationManager.focusedApplicationId.length === 0 || launcher.shown
282 || panel.indicators.shown
283 }
285 Connections {
286 id: powerConnection
287 target: Powerd
289 onDisplayPowerStateChange: {
290 // We ignore any display-off signals when the proximity sensor
291 // is active. This usually indicates something like a phone call.
292 if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
293 SessionManager.lock();
294 }
296 // No reason to chew demo CPU when user isn't watching
297 if (status == Powerd.Off) {
298 edgeDemo.paused = true;
299 } else if (status == Powerd.On) {
300 edgeDemo.paused = false;
301 }
302 }
303 }
305 function showHome(fromLauncher) {
306 var animate = fromLauncher && !stages.shown
307 dash.setCurrentScope("clickscope", animate, false)
308 stages.hide()
309 }
311 Item {
312 id: overlay
314 anchors.fill: parent
316 Panel {
317 id: panel
318 anchors.fill: parent //because this draws indicator menus
319 indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
320 indicators {
321 hides: [launcher]
322 available: edgeDemo.panelEnabled
323 contentEnabled: edgeDemo.panelContentEnabled
324 }
325 property string focusedAppId: ApplicationManager.focusedApplicationId
326 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
327 fullscreenMode: focusedApplication && stages.fullscreen
328 searchVisible: dash.shown && dash.searchable
330 InputFilterArea {
331 anchors {
332 top: parent.top
333 left: parent.left
334 right: parent.right
335 }
336 height: (panel.fullscreenMode) ? shell.edgeSize : panel.panelHeight
337 blockInput: true
338 }
339 }
341 InputFilterArea {
342 blockInput: launcher.shown
343 anchors {
344 top: parent.top
345 bottom: parent.bottom
346 left: parent.left
347 }
348 width: launcher.width
349 }
351 Launcher {
352 id: launcher
354 readonly property bool dashSwipe: progress > 0
356 anchors.top: parent.top
357 anchors.bottom: parent.bottom
358 width: parent.width
359 dragAreaWidth: shell.edgeSize
360 available: edgeDemo.launcherEnabled
362 onShowDashHome: {
363 if (edgeDemo.running)
364 return;
366 showHome(true)
367 }
369 onDash: {
370 if (stages.shown && !stages.overlayMode) {
371 if (!stages.locked) {
372 stages.hide();
373 launcher.hide();
374 }
375 }
376 }
377 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
378 onLauncherApplicationSelected: {
379 if (!edgeDemo.running)
380 shell.activateApplication(appId)
381 }
382 onShownChanged: {
383 if (shown) {
384 panel.indicators.hide()
385 }
386 }
387 }
389 Rectangle {
390 id: modalNotificationBackground
392 visible: notifications.useModal && (notifications.state == "narrow")
393 color: "#000000"
394 anchors.fill: parent
395 opacity: 0.5
397 MouseArea {
398 anchors.fill: parent
399 }
401 InputFilterArea {
402 anchors.fill: parent
403 blockInput: modalNotificationBackground.visible
404 }
405 }
407 Notifications {
408 id: notifications
410 model: NotificationBackend.Model
411 margin: units.gu(1)
413 anchors {
414 top: parent.top
415 right: parent.right
416 bottom: parent.bottom
417 topMargin: panel.panelHeight
418 }
419 states: [
420 State {
421 name: "narrow"
422 when: overlay.width <= units.gu(60)
423 AnchorChanges { target: notifications; anchors.left: parent.left }
424 },
425 State {
426 name: "wide"
427 when: overlay.width > units.gu(60)
428 AnchorChanges { target: notifications; anchors.left: undefined }
429 PropertyChanges { target: notifications; width: units.gu(38) }
430 }
431 ]
433 InputFilterArea {
434 anchors { left: parent.left; right: parent.right }
435 height: parent.contentHeight
436 blockInput: height > 0
437 }
438 }
440 Connections {
441 target: SessionManager
442 onActiveChanged: {
443 if (!SessionManager.active) {
444 launcher.hide()
445 panel.indicators.hide()
446 }
447 }
448 }
449 }
451 InputFilterArea {
452 anchors {
453 top: parent.top
454 bottom: parent.bottom
455 left: parent.left
456 }
457 width: shell.edgeSize
458 blockInput: true
459 }
461 InputFilterArea {
462 anchors {
463 top: parent.top
464 bottom: parent.bottom
465 right: parent.right
466 }
467 width: shell.edgeSize
468 blockInput: true
469 }
471 OSKController {
472 anchors.topMargin: panel.panelHeight
473 anchors.fill: parent // as needs to know the geometry of the shell
474 }
476 //FIXME: This should be handled in the input stack, keyboard shouldnt propagate
477 MouseArea {
478 anchors.bottom: parent.bottom
479 anchors.left: parent.left
480 anchors.right: parent.right
481 height: ApplicationManager.keyboardVisible ? ApplicationManager.keyboardHeight : 0
483 enabled: ApplicationManager.keyboardVisible
484 }
486 Label {
487 anchors.centerIn: parent
488 visible: ApplicationManager.fake ? ApplicationManager.fake : false
489 text: "EARLY ALPHA\nNOT READY FOR USE"
490 color: "lightgrey"
491 opacity: 0.2
492 font.weight: Font.Black
493 horizontalAlignment: Text.AlignHCenter
494 verticalAlignment: Text.AlignVCenter
495 fontSizeMode: Text.Fit
496 rotation: -45
497 scale: Math.min(parent.width, parent.height) / width
498 }
500 EdgeDemo {
501 id: edgeDemo
502 launcher: launcher
503 dash: dash
504 indicators: panel.indicators
505 underlay: underlay
506 }
508 Connections {
509 target: SessionBroadcast
510 onShowHome: showHome(false)
511 }