2 * Copyright (C) 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 QtQuick.Layouts 1.1
19 import Ubuntu.Components 1.3
20 import Ubuntu.Gestures 0.1
21 import Unity.Application 0.1
22 import "../Components"
27 property bool altTabPressed: false
28 property Item workspace: null
30 readonly property alias ready: blurLayer.ready
31 readonly property alias highlightedIndex: spreadRepeater.highlightedIndex
33 signal playFocusAnimation(int index)
36 spreadContainer.animateIn = true;
37 root.state = "altTab";
41 // When the spread comes active, we want to keep focus to the input handler below
42 // Make sure nothing inside the ApplicationWindow grabs our focus!
52 selectPrevious(event.isAutoRepeat)
53 event.accepted = true;
57 selectNext(event.isAutoRepeat)
58 event.accepted = true;
61 spreadRepeater.highlightedIndex = -1
62 // Falling through intentionally
67 event.accepted = true;
71 function selectNext(isAutoRepeat) {
72 if (isAutoRepeat && spreadRepeater.highlightedIndex >= ApplicationManager.count -1) {
73 return; // AutoRepeat is not allowed to wrap around
76 spreadRepeater.highlightedIndex = (spreadRepeater.highlightedIndex + 1) % ApplicationManager.count;
77 var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, spreadRepeater.highlightedIndex - 3));
78 if (spreadFlickable.contentX < newContentX || spreadRepeater.highlightedIndex == 0) {
79 spreadFlickable.snapTo(newContentX)
83 function selectPrevious(isAutoRepeat) {
84 if (isAutoRepeat && spreadRepeater.highlightedIndex == 0) {
85 return; // AutoRepeat is not allowed to wrap around
88 var newIndex = spreadRepeater.highlightedIndex - 1 >= 0 ? spreadRepeater.highlightedIndex - 1 : ApplicationManager.count - 1;
89 spreadRepeater.highlightedIndex = newIndex;
90 var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, spreadRepeater.highlightedIndex - 1));
91 if (spreadFlickable.contentX > newContentX || newIndex == ApplicationManager.count -1) {
92 spreadFlickable.snapTo(newContentX)
96 function focusSelected() {
97 if (spreadRepeater.highlightedIndex != -1) {
98 if (spreadContainer.visible) {
99 root.playFocusAnimation(spreadRepeater.highlightedIndex)
101 var application = ApplicationManager.get(spreadRepeater.highlightedIndex);
102 ApplicationManager.requestFocusApplication(application.appId);
107 spreadRepeater.highlightedIndex = -1;
114 source: root.workspace
123 opacity: visible ? 1 : 0
124 Behavior on opacity {
125 UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration }
132 visible: spreadBackground.visible
138 objectName: "spreadContainer"
142 property bool animateIn: false
146 objectName: "spreadRepeater"
147 model: ApplicationManager
149 property int highlightedIndex: -1
150 property int closingIndex: -1
152 function indexOf(delegateItem) {
153 for (var i = 0; i < spreadRepeater.count; i++) {
154 if (spreadRepeater.itemAt(i) === delegateItem) {
163 objectName: "spreadDelegate"
167 property real angle: 0
168 property real itemScale: 1
169 property int itemScaleOriginX: 0
170 property int itemScaleOriginY: 0
174 enabled: spreadRepeater.closingIndex >= 0
175 UbuntuNumberAnimation {
176 onRunningChanged: if (!running) spreadRepeater.closingIndex = -1
180 DesktopSpreadDelegate {
181 id: clippedSpreadDelegate
182 objectName: "clippedSpreadDelegate"
183 anchors.left: parent.left
184 anchors.top: parent.top
185 application: ApplicationManager.get(index)
186 width: spreadMaths.spreadHeight
187 height: spreadMaths.spreadHeight
191 origin.x: itemScaleOriginX
192 origin.y: itemScaleOriginY
197 origin { x: 0; y: (clippedSpreadDelegate.height - (clippedSpreadDelegate.height * itemScale / 2)) }
198 axis { x: 0; y: 1; z: 0 }
199 angle: spreadDelegate.angle
206 anchors.margins: -units.gu(2)
209 spreadRepeater.highlightedIndex = index;
217 flickable: spreadFlickable
219 totalItems: Math.max(6, ApplicationManager.count)
220 sceneHeight: root.height
221 itemHeight: spreadDelegate.height
226 name: "altTab"; when: root.state == "altTab" && spreadContainer.visible
228 target: spreadDelegate
229 x: spreadMaths.animatedX
230 y: spreadMaths.animatedY + (spreadDelegate.height - clippedSpreadDelegate.height) - units.gu(2)
231 width: spreadMaths.spreadHeight
232 height: spreadMaths.sceneHeight
233 angle: spreadMaths.animatedAngle
234 itemScale: spreadMaths.scale
235 itemScaleOriginY: clippedSpreadDelegate.height / 2;
237 visible: spreadMaths.itemVisible
240 target: clippedSpreadDelegate
241 highlightShown: index == spreadRepeater.highlightedIndex
243 shadowOpacity: spreadMaths.shadowOpacity
244 anchors.topMargin: units.gu(2)
249 opacity: spreadMaths.tileInfoOpacity
252 target: spreadSelectArea
261 SequentialAnimation {
263 PropertyAction { target: spreadDelegate; properties: "y,height,width,angle,z,itemScale,itemScaleOriginY,visible" }
264 PropertyAction { target: clippedSpreadDelegate; properties: "anchors.topMargin" }
266 target: spreadDelegate; properties: "x"
268 duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration :0
269 easing: UbuntuAnimation.StandardEasing
271 UbuntuNumberAnimation { target: clippedSpreadDelegate; property: "shadowOpacity"; from: 0; to: spreadMaths.shadowOpacity; duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration : 0 }
272 UbuntuNumberAnimation { target: tileInfo; property: "opacity"; from: 0; to: spreadMaths.tileInfoOpacity; duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration : 0 }
274 PropertyAction { target: spreadSelectArea; property: "enabled" }
281 objectName: "tileInfo"
284 top: clippedSpreadDelegate.bottom
285 topMargin: ((spreadMaths.sceneHeight - spreadDelegate.y) - clippedSpreadDelegate.height) * 0.2
287 property int nextItemX: spreadRepeater.count > index + 1 ? spreadRepeater.itemAt(index + 1).x : spreadDelegate.x + units.gu(30)
288 width: Math.min(units.gu(30), nextItemX - spreadDelegate.x)
289 height: titleInfoColumn.height
293 onContainsMouseChanged: {
295 spreadRepeater.highlightedIndex = index
305 anchors { left: parent.left; top: parent.top; right: parent.right }
309 Layout.preferredHeight: Math.min(units.gu(6), root.height * .05)
310 Layout.preferredWidth: height * 8 / 7.6
317 opacity: clippedSpreadDelegate.highlightShown ? 0 : .1
318 Behavior on opacity {
319 UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration }
325 Layout.fillWidth: true
326 Layout.preferredHeight: units.gu(6)
328 wrapMode: Text.WordWrap
329 elide: Text.ElideRight
337 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset + units.gu(2) }
338 source: "graphics/window-close.svg"
339 readonly property var mousePos: hoverMouseArea.mapToItem(spreadDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
340 visible: index == spreadRepeater.highlightedIndex
341 && mousePos.y < (clippedSpreadDelegate.height / 3)
342 && mousePos.y > -units.gu(4)
343 && mousePos.x > -units.gu(4)
344 && mousePos.x < (clippedSpreadDelegate.width * 2 / 3)
345 height: units.gu(1.5)
347 sourceSize.width: width
348 sourceSize.height: height
352 objectName: "closeMouseArea"
353 anchors.fill: closeImage
354 anchors.margins: -units.gu(2)
356 spreadRepeater.closingIndex = index;
357 ApplicationManager.stopApplication(model.appId)
368 objectName: "hoverMouseArea"
369 anchors.fill: spreadContainer
370 propagateComposedEvents: true
375 property int scrollAreaWidth: root.width / 3
376 property bool progressiveScrollingEnabled: false
379 mouse.accepted = false
381 if (hoverMouseArea.pressed) {
385 // Find the hovered item and mark it active
386 var mapped = mapToItem(spreadContainer, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
387 var itemUnder = spreadContainer.childAt(mapped.x, mapped.y)
389 mapped = mapToItem(itemUnder, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
390 var delegateChild = itemUnder.childAt(mapped.x, mapped.y)
391 if (delegateChild && (delegateChild.objectName === "clippedSpreadDelegate" || delegateChild.objectName === "tileInfo")) {
392 spreadRepeater.highlightedIndex = spreadRepeater.indexOf(itemUnder)
396 if (spreadFlickable.contentWidth > spreadFlickable.minContentWidth) {
397 var margins = spreadFlickable.width * 0.05;
399 if (!progressiveScrollingEnabled && mouseX < spreadFlickable.width - scrollAreaWidth) {
400 progressiveScrollingEnabled = true
403 // do we need to scroll?
404 if (mouseX < scrollAreaWidth + margins) {
405 var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
406 var contentX = (1 - progress) * (spreadFlickable.contentWidth - spreadFlickable.width)
407 spreadFlickable.contentX = Math.max(0, Math.min(spreadFlickable.contentX, contentX))
409 if (mouseX > spreadFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
410 var progress = Math.min(1, (mouseX - (spreadFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
411 var contentX = progress * (spreadFlickable.contentWidth - spreadFlickable.width)
412 spreadFlickable.contentX = Math.min(spreadFlickable.contentWidth - spreadFlickable.width, Math.max(spreadFlickable.contentX, contentX))
416 onPressed: mouse.accepted = false
421 objectName: "spreadFlickable"
423 property int minContentWidth: 6 * Math.min(height / 4, width / 5)
424 contentWidth: Math.max(6, ApplicationManager.count) * Math.min(height / 4, width / 5)
427 function snapTo(contentX) {
428 snapAnimation.stop();
429 snapAnimation.to = contentX
430 snapAnimation.start();
433 UbuntuNumberAnimation {
435 target: spreadFlickable
441 id: workspaceSelector
446 topMargin: units.gu(3.5)
448 height: root.height * 0.25
454 Item { Layout.fillWidth: true }
456 model: 1 // TODO: will be a workspacemodel in the future
458 Layout.fillHeight: true
459 Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
461 source: root.background
465 verticalCenter: parent.verticalCenter
467 height: parent.height * 0.75
472 property var source: ShaderEffectSource {
473 id: shaderEffectSource
474 sourceItem: root.workspace
478 varying highp vec2 qt_TexCoord0;
479 uniform sampler2D source;
482 highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
483 gl_FragColor = sourceColor;
488 // TODO: This is the bar for the currently selected workspace
489 // Enable this once the workspace stuff is implemented
491 // anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
492 // height: units.dp(2)
493 // color: UbuntuColors.orange
494 // visible: index == 0 // TODO: should be active workspace index
499 // TODO: This is the "new workspace" button. Enable this once workspaces are implemented
501 // Layout.fillHeight: true
502 // Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
506 // right: parent.right
507 // verticalCenter: parent.verticalCenter
509 // height: parent.height * 0.75
510 // color: "#22ffffff"
513 // anchors.centerIn: parent
514 // font.pixelSize: parent.height / 2
519 Item { Layout.fillWidth: true }
524 id: currentSelectedLabel
525 anchors { bottom: parent.bottom; bottomMargin: root.height * 0.625; horizontalCenter: parent.horizontalCenter }
526 text: spreadRepeater.highlightedIndex >= 0 ? ApplicationManager.get(spreadRepeater.highlightedIndex).name : ""
533 name: "altTab"; when: root.altTabPressed
534 PropertyChanges { target: blurLayer; saturation: 0.8; blurRadius: 60; visible: true }
535 PropertyChanges { target: workspaceSelector; visible: true }
536 PropertyChanges { target: spreadContainer; visible: true }
537 PropertyChanges { target: spreadFlickable; enabled: spreadFlickable.contentWidth > spreadFlickable.minContentWidth }
538 PropertyChanges { target: currentSelectedLabel; visible: true }
539 PropertyChanges { target: spreadBackground; visible: true }
540 PropertyChanges { target: hoverMouseArea; enabled: true }
547 SequentialAnimation {
548 PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: Math.min(ApplicationManager.count - 1, 1) }
549 PauseAnimation { duration: spreadContainer.animateIn ? 0 : 140 }
550 PropertyAction { target: workspaceSelector; property: "visible" }
551 PropertyAction { target: spreadContainer; property: "visible" }
553 UbuntuNumberAnimation { target: blurLayer; properties: "saturation,blurRadius"; duration: UbuntuAnimation.SnapDuration }
554 PropertyAction { target: spreadFlickable; property: "visible" }
555 PropertyAction { targets: [currentSelectedLabel,spreadBackground]; property: "visible" }
556 PropertyAction { target: spreadFlickable; property: "contentX"; value: 0 }
558 PropertyAction { target: hoverMouseArea; properties: "enabled,progressiveScrollingEnabled"; value: false }
564 PropertyAnimation { property: "opacity" }
565 ScriptAction { script: { root.focusSelected() } }
566 PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: -1 }
567 PropertyAction { target: spreadContainer; property: "animateIn"; value: false }