2 * Copyright (C) 2015-2016 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"
28 property bool altTabPressed: false
29 property Item workspace: null
31 readonly property alias ready: blurLayer.ready
32 readonly property alias highlightedIndex: spreadRepeater.highlightedIndex
34 signal playFocusAnimation(int index)
37 spreadContainer.animateIn = true;
38 root.state = "altTab";
42 // When the spread comes active, we want to keep focus to the input handler below
43 // Make sure nothing inside the ApplicationWindow grabs our focus!
53 selectPrevious(event.isAutoRepeat)
54 event.accepted = true;
58 selectNext(event.isAutoRepeat)
59 event.accepted = true;
62 spreadRepeater.highlightedIndex = -1
63 // Falling through intentionally
68 event.accepted = true;
72 function selectNext(isAutoRepeat) {
73 if (isAutoRepeat && spreadRepeater.highlightedIndex >= topLevelSurfaceList.count -1) {
74 return; // AutoRepeat is not allowed to wrap around
77 spreadRepeater.highlightedIndex = (spreadRepeater.highlightedIndex + 1) % topLevelSurfaceList.count;
78 var newContentX = ((spreadFlickable.contentWidth) / (topLevelSurfaceList.count + 1)) * Math.max(0, Math.min(topLevelSurfaceList.count - 5, spreadRepeater.highlightedIndex - 3));
79 if (spreadFlickable.contentX < newContentX || spreadRepeater.highlightedIndex == 0) {
80 spreadFlickable.snapTo(newContentX)
84 function selectPrevious(isAutoRepeat) {
85 if (isAutoRepeat && spreadRepeater.highlightedIndex == 0) {
86 return; // AutoRepeat is not allowed to wrap around
89 var newIndex = spreadRepeater.highlightedIndex - 1 >= 0 ? spreadRepeater.highlightedIndex - 1 : topLevelSurfaceList.count - 1;
90 spreadRepeater.highlightedIndex = newIndex;
91 var newContentX = ((spreadFlickable.contentWidth) / (topLevelSurfaceList.count + 1)) * Math.max(0, Math.min(topLevelSurfaceList.count - 5, spreadRepeater.highlightedIndex - 1));
92 if (spreadFlickable.contentX > newContentX || newIndex == topLevelSurfaceList.count -1) {
93 spreadFlickable.snapTo(newContentX)
97 function focusSelected() {
98 if (spreadRepeater.highlightedIndex != -1) {
99 if (spreadContainer.visible) {
100 root.playFocusAnimation(spreadRepeater.highlightedIndex)
102 var surface = topLevelSurfaceList.surfaceAt(spreadRepeater.highlightedIndex);
103 surface.requestFocus();
108 spreadRepeater.highlightedIndex = -1;
115 source: root.workspace
124 opacity: visible ? 1 : 0
125 Behavior on opacity {
126 UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration }
133 visible: spreadBackground.visible
135 acceptedButtons: Qt.AllButtons
136 onWheel: wheel.accepted = true;
141 objectName: "spreadContainer"
145 property bool animateIn: false
149 objectName: "spreadRepeater"
150 model: topLevelSurfaceList
152 property int highlightedIndex: -1
153 property int closingIndex: -1
155 function indexOf(delegateItem) {
156 for (var i = 0; i < spreadRepeater.count; i++) {
157 if (spreadRepeater.itemAt(i) === delegateItem) {
166 objectName: "spreadDelegate"
170 property real angle: 0
171 property real itemScale: 1
172 property int itemScaleOriginX: 0
173 property int itemScaleOriginY: 0
175 readonly property string windowTitle: clippedSpreadDelegate.window.title
179 enabled: spreadRepeater.closingIndex >= 0
180 UbuntuNumberAnimation {
181 onRunningChanged: if (!running) spreadRepeater.closingIndex = -1
185 DesktopSpreadDelegate {
186 id: clippedSpreadDelegate
187 objectName: "clippedSpreadDelegate"
188 anchors.left: parent.left
189 anchors.top: parent.top
190 application: model.application
191 surface: model.surface
192 width: spreadMaths.spreadHeight
193 height: spreadMaths.spreadHeight
197 origin.x: itemScaleOriginX
198 origin.y: itemScaleOriginY
203 origin { x: 0; y: (clippedSpreadDelegate.height - (clippedSpreadDelegate.height * itemScale / 2)) }
204 axis { x: 0; y: 1; z: 0 }
205 angle: spreadDelegate.angle
212 anchors.margins: -units.gu(2)
215 spreadRepeater.highlightedIndex = index;
223 flickable: spreadFlickable
225 totalItems: Math.max(6, topLevelSurfaceList.count)
226 sceneHeight: root.height
227 itemHeight: spreadDelegate.height
232 name: "altTab"; when: root.state == "altTab" && spreadContainer.visible
234 target: spreadDelegate
235 x: spreadMaths.animatedX
236 y: spreadMaths.animatedY + (spreadDelegate.height - clippedSpreadDelegate.height) - units.gu(2)
237 width: spreadMaths.spreadHeight
238 height: spreadMaths.sceneHeight
239 angle: spreadMaths.animatedAngle
240 itemScale: spreadMaths.scale
241 itemScaleOriginY: clippedSpreadDelegate.height / 2;
243 visible: spreadMaths.itemVisible
246 target: clippedSpreadDelegate
247 highlightShown: index == spreadRepeater.highlightedIndex
249 shadowOpacity: spreadMaths.shadowOpacity
250 anchors.topMargin: units.gu(2)
255 opacity: spreadMaths.tileInfoOpacity
258 target: spreadSelectArea
267 SequentialAnimation {
269 PropertyAction { target: spreadDelegate; properties: "y,height,width,angle,z,itemScale,itemScaleOriginY,visible" }
270 PropertyAction { target: clippedSpreadDelegate; properties: "anchors.topMargin" }
272 target: spreadDelegate; properties: "x"
274 duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration :0
275 easing: UbuntuAnimation.StandardEasing
277 UbuntuNumberAnimation { target: clippedSpreadDelegate; property: "shadowOpacity"; from: 0; to: spreadMaths.shadowOpacity; duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration : 0 }
278 UbuntuNumberAnimation { target: tileInfo; property: "opacity"; from: 0; to: spreadMaths.tileInfoOpacity; duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration : 0 }
280 PropertyAction { target: spreadSelectArea; property: "enabled" }
287 objectName: "tileInfo"
290 top: clippedSpreadDelegate.bottom
291 topMargin: ((spreadMaths.sceneHeight - spreadDelegate.y) - clippedSpreadDelegate.height) * 0.2
293 property int nextItemX: spreadRepeater.count > index + 1 ? spreadRepeater.itemAt(index + 1).x : spreadDelegate.x + units.gu(30)
294 width: Math.min(units.gu(30), nextItemX - spreadDelegate.x)
295 height: titleInfoColumn.height
299 onContainsMouseChanged: {
301 spreadRepeater.highlightedIndex = index
311 anchors { left: parent.left; top: parent.top; right: parent.right }
315 Layout.preferredHeight: Math.min(units.gu(6), root.height * .05)
316 Layout.preferredWidth: height * 8 / 7.6
319 source: model.application.icon
323 opacity: clippedSpreadDelegate.highlightShown ? 0 : .1
324 Behavior on opacity {
325 UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration }
331 Layout.fillWidth: true
332 Layout.preferredHeight: units.gu(6)
333 text: model.application ? model.application.name : spreadDelegate.windowTitle
334 wrapMode: Text.WordWrap
335 elide: Text.ElideRight
343 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset + units.gu(2) }
344 source: "graphics/window-close.svg"
345 readonly property var mousePos: hoverMouseArea.mapToItem(spreadDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
346 visible: index == spreadRepeater.highlightedIndex
347 && mousePos.y < (clippedSpreadDelegate.height / 3)
348 && mousePos.y > -units.gu(4)
349 && mousePos.x > -units.gu(4)
350 && mousePos.x < (clippedSpreadDelegate.width * 2 / 3)
351 height: units.gu(1.5)
353 sourceSize.width: width
354 sourceSize.height: height
358 objectName: "closeMouseArea"
359 anchors.fill: closeImage
360 anchors.margins: -units.gu(2)
362 spreadRepeater.closingIndex = index;
363 model.surface.close();
374 objectName: "hoverMouseArea"
375 anchors.fill: spreadContainer
376 propagateComposedEvents: true
381 property int scrollAreaWidth: root.width / 3
382 property bool progressiveScrollingEnabled: false
385 mouse.accepted = false
387 if (hoverMouseArea.pressed) {
391 // Find the hovered item and mark it active
392 var mapped = mapToItem(spreadContainer, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
393 var itemUnder = spreadContainer.childAt(mapped.x, mapped.y)
395 mapped = mapToItem(itemUnder, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
396 var delegateChild = itemUnder.childAt(mapped.x, mapped.y)
397 if (delegateChild && (delegateChild.objectName === "clippedSpreadDelegate" || delegateChild.objectName === "tileInfo")) {
398 spreadRepeater.highlightedIndex = spreadRepeater.indexOf(itemUnder)
402 if (spreadFlickable.contentWidth > spreadFlickable.minContentWidth) {
403 var margins = spreadFlickable.width * 0.05;
405 if (!progressiveScrollingEnabled && mouseX < spreadFlickable.width - scrollAreaWidth) {
406 progressiveScrollingEnabled = true
409 // do we need to scroll?
410 if (mouseX < scrollAreaWidth + margins) {
411 var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
412 var contentX = (1 - progress) * (spreadFlickable.contentWidth - spreadFlickable.width)
413 spreadFlickable.contentX = Math.max(0, Math.min(spreadFlickable.contentX, contentX))
415 if (mouseX > spreadFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
416 var progress = Math.min(1, (mouseX - (spreadFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
417 var contentX = progress * (spreadFlickable.contentWidth - spreadFlickable.width)
418 spreadFlickable.contentX = Math.min(spreadFlickable.contentWidth - spreadFlickable.width, Math.max(spreadFlickable.contentX, contentX))
422 onPressed: mouse.accepted = false
427 objectName: "spreadFlickable"
429 property int minContentWidth: 6 * Math.min(height / 4, width / 5)
430 contentWidth: Math.max(6, topLevelSurfaceList.count) * Math.min(height / 4, width / 5)
433 function snapTo(contentX) {
434 snapAnimation.stop();
435 snapAnimation.to = contentX
436 snapAnimation.start();
439 UbuntuNumberAnimation {
441 target: spreadFlickable
447 id: workspaceSelector
452 topMargin: units.gu(3.5)
454 height: root.height * 0.25
460 Item { Layout.fillWidth: true }
462 model: 1 // TODO: will be a workspacemodel in the future
464 Layout.fillHeight: true
465 Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
467 source: root.background
471 verticalCenter: parent.verticalCenter
473 height: parent.height * 0.75
478 property var source: ShaderEffectSource {
479 id: shaderEffectSource
480 sourceItem: root.workspace
484 varying highp vec2 qt_TexCoord0;
485 uniform sampler2D source;
488 highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
489 gl_FragColor = sourceColor;
494 // TODO: This is the bar for the currently selected workspace
495 // Enable this once the workspace stuff is implemented
497 // anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
498 // height: units.dp(2)
499 // color: theme.palette.normal.focus
500 // visible: index == 0 // TODO: should be active workspace index
505 // TODO: This is the "new workspace" button. Enable this once workspaces are implemented
507 // Layout.fillHeight: true
508 // Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
512 // right: parent.right
513 // verticalCenter: parent.verticalCenter
515 // height: parent.height * 0.75
516 // color: "#22ffffff"
519 // anchors.centerIn: parent
520 // font.pixelSize: parent.height / 2
525 Item { Layout.fillWidth: true }
530 id: currentSelectedLabel
531 anchors { bottom: parent.bottom; bottomMargin: root.height * 0.625; horizontalCenter: parent.horizontalCenter }
532 text: spreadRepeater.highlightedIndex >= 0 ? spreadRepeater.itemAt(spreadRepeater.highlightedIndex).windowTitle: ""
539 name: "altTab"; when: root.altTabPressed
540 PropertyChanges { target: blurLayer; saturation: 0.8; blurRadius: 60; visible: true }
541 PropertyChanges { target: workspaceSelector; visible: true }
542 PropertyChanges { target: spreadContainer; visible: true }
543 PropertyChanges { target: spreadFlickable; enabled: spreadFlickable.contentWidth > spreadFlickable.minContentWidth }
544 PropertyChanges { target: currentSelectedLabel; visible: true }
545 PropertyChanges { target: spreadBackground; visible: true }
546 PropertyChanges { target: hoverMouseArea; enabled: true }
553 SequentialAnimation {
554 PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: Math.min(topLevelSurfaceList.count - 1, 1) }
555 PauseAnimation { duration: spreadContainer.animateIn ? 0 : 140 }
556 PropertyAction { target: workspaceSelector; property: "visible" }
557 PropertyAction { target: spreadContainer; property: "visible" }
559 UbuntuNumberAnimation { target: blurLayer; properties: "saturation,blurRadius"; duration: UbuntuAnimation.SnapDuration }
560 PropertyAction { target: spreadFlickable; property: "visible" }
561 PropertyAction { targets: [currentSelectedLabel,spreadBackground]; property: "visible" }
562 PropertyAction { target: spreadFlickable; property: "contentX"; value: 0 }
564 PropertyAction { target: hoverMouseArea; properties: "enabled,progressiveScrollingEnabled"; value: false }
570 PropertyAnimation { property: "opacity" }
571 ScriptAction { script: { root.focusSelected() } }
572 PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: -1 }
573 PropertyAction { target: spreadContainer; property: "animateIn"; value: false }