2 * Copyright (C) 2014-2015 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/>.
18 import Ubuntu.Components 1.3
19 import Unity.Application 0.1
20 import "../Components/PanelState"
21 import "../Components"
23 import Ubuntu.Gestures 0.1
24 import GlobalShortcut 1.0
30 // functions to be called from outside
31 function updateFocusedAppOrientation() { /* TODO */ }
32 function updateFocusedAppOrientationAnimated() { /* TODO */}
33 function pushRightEdge(amount) {
34 if (spread.state === "") {
35 edgeBarrier.push(amount);
39 mainApp: ApplicationManager.focusedApplicationId
40 ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
44 target: ApplicationManager
46 if (spread.state == "altTab") {
50 ApplicationManager.focusApplication(appId);
53 onApplicationRemoved: {
58 var appIndex = priv.indexOf(appId);
59 var appDelegate = appRepeater.itemAt(appIndex);
60 appDelegate.restore();
62 if (spread.state == "altTab") {
69 id: closeWindowShortcut
70 shortcut: Qt.AltModifier|Qt.Key_F4
71 onTriggered: ApplicationManager.stopApplication(priv.focusedAppId)
72 active: priv.focusedAppId !== ""
76 id: showSpreadShortcut
77 shortcut: Qt.MetaModifier|Qt.Key_W
78 onTriggered: spread.state = "altTab"
82 id: minimizeAllShortcut
83 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_D
84 onTriggered: priv.minimizeAllWindows()
88 id: maximizeWindowShortcut
89 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
90 onTriggered: priv.focusedAppDelegate.maximize()
91 active: priv.focusedAppDelegate !== null
95 id: maximizeWindowLeftShortcut
96 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
97 onTriggered: priv.focusedAppDelegate.maximizeLeft()
98 active: priv.focusedAppDelegate !== null
102 id: maximizeWindowRightShortcut
103 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
104 onTriggered: priv.focusedAppDelegate.maximizeRight()
105 active: priv.focusedAppDelegate !== null
109 id: minimizeRestoreShortcut
110 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
111 onTriggered: priv.focusedAppDelegate.maximized || priv.focusedAppDelegate.maximizedLeft || priv.focusedAppDelegate.maximizedRight
112 ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
113 active: priv.focusedAppDelegate !== null
119 readonly property string focusedAppId: ApplicationManager.focusedApplicationId
120 readonly property var focusedAppDelegate: {
121 var index = indexOf(focusedAppId);
122 return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
124 onFocusedAppDelegateChanged: updateForegroundMaximizedApp();
126 property int foregroundMaximizedAppZ: -1
127 property int foregroundMaximizedAppIndex: -1 // for stuff like drop shadow and focusing maximized app by clicking panel
129 function updateForegroundMaximizedApp() {
132 for (var i = appRepeater.count - 1; i >= 0; i--) {
133 var item = appRepeater.itemAt(i);
134 if (item && item.visuallyMaximized) {
136 tmp = Math.max(tmp, item.normalZ);
139 foregroundMaximizedAppZ = tmp;
140 foregroundMaximizedAppIndex = tmpAppId;
143 function indexOf(appId) {
144 for (var i = 0; i < ApplicationManager.count; i++) {
145 if (ApplicationManager.get(i).appId == appId) {
152 function minimizeAllWindows() {
153 for (var i = 0; i < appRepeater.count; i++) {
154 var appDelegate = appRepeater.itemAt(i);
155 if (appDelegate && !appDelegate.minimized) {
156 appDelegate.minimize();
160 ApplicationManager.unfocusCurrentApplication(); // no app should have focus at this point
163 function focusNext() {
164 ApplicationManager.unfocusCurrentApplication();
165 for (var i = 0; i < appRepeater.count; i++) {
166 var appDelegate = appRepeater.itemAt(i);
167 if (appDelegate && !appDelegate.minimized) {
168 ApplicationManager.focusApplication(appDelegate.appId);
178 ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId)
180 onMinimize: priv.focusedAppDelegate && priv.focusedAppDelegate.minimize();
181 onMaximize: priv.focusedAppDelegate // don't restore minimized apps when double clicking the panel
182 && priv.focusedAppDelegate.restoreFromMaximized();
183 onFocusMaximizedApp: if (priv.foregroundMaximizedAppIndex != -1) {
184 ApplicationManager.focusApplication(appRepeater.itemAt(priv.foregroundMaximizedAppIndex).appId);
190 property: "buttonsVisible"
191 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized // FIXME for Locally integrated menus
192 && spread.state == ""
199 if (priv.focusedAppDelegate !== null && spread.state == "") {
200 if (priv.focusedAppDelegate.maximized)
201 return priv.focusedAppDelegate.title
203 return priv.focusedAppDelegate.appName
207 when: priv.focusedAppDelegate
212 property: "dropShadow"
213 value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppIndex !== -1
216 Component.onDestruction: {
217 PanelState.title = "";
218 PanelState.buttonsVisible = false;
219 PanelState.dropShadow = false;
224 objectName: "appContainer"
226 focus: spread.state !== "altTab"
231 source: root.background
232 sourceSize { height: root.height; width: root.width }
233 fillMode: Image.PreserveAspectCrop
238 model: ApplicationManager
239 objectName: "appRepeater"
241 delegate: FocusScope {
243 objectName: "appDelegate_" + appId
244 // z might be overriden in some cases by effects, but we need z ordering
245 // to calculate occlusion detection
246 property int normalZ: ApplicationManager.count - index
248 y: PanelState.panelHeight
249 focus: appId === priv.focusedAppId
250 width: decoratedWindow.width
251 height: decoratedWindow.height
252 property alias requestedWidth: decoratedWindow.requestedWidth
253 property alias requestedHeight: decoratedWindow.requestedHeight
256 id: appDelegatePrivate
257 property bool maximized: false
258 property bool maximizedLeft: false
259 property bool maximizedRight: false
260 property bool minimized: false
262 readonly property alias maximized: appDelegatePrivate.maximized
263 readonly property alias maximizedLeft: appDelegatePrivate.maximizedLeft
264 readonly property alias maximizedRight: appDelegatePrivate.maximizedRight
265 readonly property alias minimized: appDelegatePrivate.minimized
267 readonly property string appId: model.appId
268 property bool animationsEnabled: true
269 property alias title: decoratedWindow.title
270 readonly property string appName: model.name
271 property bool visuallyMaximized: false
272 property bool visuallyMinimized: false
275 if (focus && ApplicationManager.focusedApplicationId !== appId) {
276 ApplicationManager.focusApplication(appId);
280 onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
282 visible: !visuallyMinimized &&
283 !greeter.fullyShown &&
284 (priv.foregroundMaximizedAppZ === -1 || priv.foregroundMaximizedAppZ <= z) ||
285 (spread.state == "altTab" && index === spread.highlightedIndex)
288 target: ApplicationManager.get(index)
289 property: "requestedState"
290 // TODO: figure out some lifecycle policy, like suspending minimized apps
291 // if running on a tablet or something.
292 // TODO: If the device has a dozen suspended apps because it was running
293 // in staged mode, when it switches to Windowed mode it will suddenly
294 // resume all those apps at once. We might want to avoid that.
295 value: ApplicationInfoInterface.RequestedRunning // Always running for now
298 function maximize(animated) {
299 animationsEnabled = (animated === undefined) || animated;
300 appDelegatePrivate.minimized = false;
301 appDelegatePrivate.maximized = true;
302 appDelegatePrivate.maximizedLeft = false;
303 appDelegatePrivate.maximizedRight = false;
305 function maximizeLeft() {
306 appDelegatePrivate.minimized = false;
307 appDelegatePrivate.maximized = false;
308 appDelegatePrivate.maximizedLeft = true;
309 appDelegatePrivate.maximizedRight = false;
311 function maximizeRight() {
312 appDelegatePrivate.minimized = false;
313 appDelegatePrivate.maximized = false;
314 appDelegatePrivate.maximizedLeft = false;
315 appDelegatePrivate.maximizedRight = true;
317 function minimize(animated) {
318 animationsEnabled = (animated === undefined) || animated;
319 appDelegatePrivate.minimized = true;
321 function restoreFromMaximized(animated) {
322 animationsEnabled = (animated === undefined) || animated;
323 appDelegatePrivate.minimized = false;
324 appDelegatePrivate.maximized = false;
325 appDelegatePrivate.maximizedLeft = false;
326 appDelegatePrivate.maximizedRight = false;
328 function restore(animated) {
329 animationsEnabled = (animated === undefined) || animated;
330 appDelegatePrivate.minimized = false;
333 else if (maximizedLeft)
335 else if (maximizedRight)
337 ApplicationManager.focusApplication(appId);
342 name: "fullscreen"; when: decoratedWindow.fullscreen
346 y: -PanelState.panelHeight
351 when: !appDelegate.maximized && !appDelegate.minimized
352 && !appDelegate.maximizedLeft && !appDelegate.maximizedRight
355 visuallyMinimized: false;
356 visuallyMaximized: false
360 name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
364 requestedWidth: root.width; requestedHeight: root.height;
365 visuallyMinimized: false;
366 visuallyMaximized: true
370 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
371 PropertyChanges { target: appDelegate; x: 0; y: PanelState.panelHeight;
372 requestedWidth: root.width/2; requestedHeight: root.height - PanelState.panelHeight }
375 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
376 PropertyChanges { target: appDelegate; x: root.width/2; y: PanelState.panelHeight;
377 requestedWidth: root.width/2; requestedHeight: root.height - PanelState.panelHeight }
380 name: "minimized"; when: appDelegate.minimized
383 x: -appDelegate.width / 2;
384 scale: units.gu(5) / appDelegate.width;
386 visuallyMinimized: true;
387 visuallyMaximized: false
394 enabled: appDelegate.animationsEnabled
395 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
396 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
400 enabled: appDelegate.animationsEnabled
401 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
402 SequentialAnimation {
403 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
404 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
407 if (appDelegate.minimized) {
415 to: "*" //maximized and fullscreen
416 enabled: appDelegate.animationsEnabled
417 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
418 SequentialAnimation {
419 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
420 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
429 value: ApplicationManager.count + 1
430 when: index == spread.highlightedIndex && blurLayer.ready
434 objectName: "windowResizeArea"
436 minWidth: units.gu(10)
437 minHeight: units.gu(10)
438 borderThickness: units.gu(2)
439 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
440 screenWidth: root.width
441 screenHeight: root.height
443 onPressed: { ApplicationManager.focusApplication(model.appId) }
448 objectName: "decoratedWindow"
449 anchors.left: appDelegate.left
450 anchors.top: appDelegate.top
451 application: ApplicationManager.get(index)
452 active: ApplicationManager.focusedApplicationId === model.appId
455 onClose: ApplicationManager.stopApplication(model.appId)
456 onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
457 ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
458 onMinimize: appDelegate.minimize()
459 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
482 visible: spreadBackground.visible
489 // NB: it does its own positioning according to the specified edge
492 onPassed: { spread.show(); }
493 material: Component {
499 anchors.centerIn: parent
501 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.7)}
502 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
509 DirectionalDragArea {
510 direction: Direction.Leftwards
511 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
513 onDraggingChanged: { if (dragging) { spread.show(); } }
520 workspace: appContainer
521 focus: state == "altTab"
522 altTabPressed: root.altTabPressed