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
139 objectName: "spreadContainer"
143 property bool animateIn: false
147 objectName: "spreadRepeater"
148 model: topLevelSurfaceList
150 property int highlightedIndex: -1
151 property int closingIndex: -1
153 function indexOf(delegateItem) {
154 for (var i = 0; i < spreadRepeater.count; i++) {
155 if (spreadRepeater.itemAt(i) === delegateItem) {
164 objectName: "spreadDelegate"
168 property real angle: 0
169 property real itemScale: 1
170 property int itemScaleOriginX: 0
171 property int itemScaleOriginY: 0
173 readonly property string windowTitle: clippedSpreadDelegate.window.title
177 enabled: spreadRepeater.closingIndex >= 0
178 UbuntuNumberAnimation {
179 onRunningChanged: if (!running) spreadRepeater.closingIndex = -1
183 DesktopSpreadDelegate {
184 id: clippedSpreadDelegate
185 objectName: "clippedSpreadDelegate"
186 anchors.left: parent.left
187 anchors.top: parent.top
188 application: model.application
189 surface: model.surface
190 width: spreadMaths.spreadHeight
191 height: spreadMaths.spreadHeight
195 origin.x: itemScaleOriginX
196 origin.y: itemScaleOriginY
201 origin { x: 0; y: (clippedSpreadDelegate.height - (clippedSpreadDelegate.height * itemScale / 2)) }
202 axis { x: 0; y: 1; z: 0 }
203 angle: spreadDelegate.angle
210 anchors.margins: -units.gu(2)
213 spreadRepeater.highlightedIndex = index;
221 flickable: spreadFlickable
223 totalItems: Math.max(6, topLevelSurfaceList.count)
224 sceneHeight: root.height
225 itemHeight: spreadDelegate.height
230 name: "altTab"; when: root.state == "altTab" && spreadContainer.visible
232 target: spreadDelegate
233 x: spreadMaths.animatedX
234 y: spreadMaths.animatedY + (spreadDelegate.height - clippedSpreadDelegate.height) - units.gu(2)
235 width: spreadMaths.spreadHeight
236 height: spreadMaths.sceneHeight
237 angle: spreadMaths.animatedAngle
238 itemScale: spreadMaths.scale
239 itemScaleOriginY: clippedSpreadDelegate.height / 2;
241 visible: spreadMaths.itemVisible
244 target: clippedSpreadDelegate
245 highlightShown: index == spreadRepeater.highlightedIndex
247 shadowOpacity: spreadMaths.shadowOpacity
248 anchors.topMargin: units.gu(2)
253 opacity: spreadMaths.tileInfoOpacity
256 target: spreadSelectArea
265 SequentialAnimation {
267 PropertyAction { target: spreadDelegate; properties: "y,height,width,angle,z,itemScale,itemScaleOriginY,visible" }
268 PropertyAction { target: clippedSpreadDelegate; properties: "anchors.topMargin" }
270 target: spreadDelegate; properties: "x"
272 duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration :0
273 easing: UbuntuAnimation.StandardEasing
275 UbuntuNumberAnimation { target: clippedSpreadDelegate; property: "shadowOpacity"; from: 0; to: spreadMaths.shadowOpacity; duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration : 0 }
276 UbuntuNumberAnimation { target: tileInfo; property: "opacity"; from: 0; to: spreadMaths.tileInfoOpacity; duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration : 0 }
278 PropertyAction { target: spreadSelectArea; property: "enabled" }
285 objectName: "tileInfo"
288 top: clippedSpreadDelegate.bottom
289 topMargin: ((spreadMaths.sceneHeight - spreadDelegate.y) - clippedSpreadDelegate.height) * 0.2
291 property int nextItemX: spreadRepeater.count > index + 1 ? spreadRepeater.itemAt(index + 1).x : spreadDelegate.x + units.gu(30)
292 width: Math.min(units.gu(30), nextItemX - spreadDelegate.x)
293 height: titleInfoColumn.height
297 onContainsMouseChanged: {
299 spreadRepeater.highlightedIndex = index
309 anchors { left: parent.left; top: parent.top; right: parent.right }
313 Layout.preferredHeight: Math.min(units.gu(6), root.height * .05)
314 Layout.preferredWidth: height * 8 / 7.6
317 source: model.application.icon
321 opacity: clippedSpreadDelegate.highlightShown ? 0 : .1
322 Behavior on opacity {
323 UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration }
329 Layout.fillWidth: true
330 Layout.preferredHeight: units.gu(6)
331 text: model.application ? model.application.name : spreadDelegate.windowTitle
332 wrapMode: Text.WordWrap
333 elide: Text.ElideRight
341 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset + units.gu(2) }
342 source: "graphics/window-close.svg"
343 readonly property var mousePos: hoverMouseArea.mapToItem(spreadDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
344 visible: index == spreadRepeater.highlightedIndex
345 && mousePos.y < (clippedSpreadDelegate.height / 3)
346 && mousePos.y > -units.gu(4)
347 && mousePos.x > -units.gu(4)
348 && mousePos.x < (clippedSpreadDelegate.width * 2 / 3)
349 height: units.gu(1.5)
351 sourceSize.width: width
352 sourceSize.height: height
356 objectName: "closeMouseArea"
357 anchors.fill: closeImage
358 anchors.margins: -units.gu(2)
360 spreadRepeater.closingIndex = index;
361 model.surface.close();
372 objectName: "hoverMouseArea"
373 anchors.fill: spreadContainer
374 propagateComposedEvents: true
379 property int scrollAreaWidth: root.width / 3
380 property bool progressiveScrollingEnabled: false
383 mouse.accepted = false
385 if (hoverMouseArea.pressed) {
389 // Find the hovered item and mark it active
390 var mapped = mapToItem(spreadContainer, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
391 var itemUnder = spreadContainer.childAt(mapped.x, mapped.y)
393 mapped = mapToItem(itemUnder, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
394 var delegateChild = itemUnder.childAt(mapped.x, mapped.y)
395 if (delegateChild && (delegateChild.objectName === "clippedSpreadDelegate" || delegateChild.objectName === "tileInfo")) {
396 spreadRepeater.highlightedIndex = spreadRepeater.indexOf(itemUnder)
400 if (spreadFlickable.contentWidth > spreadFlickable.minContentWidth) {
401 var margins = spreadFlickable.width * 0.05;
403 if (!progressiveScrollingEnabled && mouseX < spreadFlickable.width - scrollAreaWidth) {
404 progressiveScrollingEnabled = true
407 // do we need to scroll?
408 if (mouseX < scrollAreaWidth + margins) {
409 var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
410 var contentX = (1 - progress) * (spreadFlickable.contentWidth - spreadFlickable.width)
411 spreadFlickable.contentX = Math.max(0, Math.min(spreadFlickable.contentX, contentX))
413 if (mouseX > spreadFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
414 var progress = Math.min(1, (mouseX - (spreadFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
415 var contentX = progress * (spreadFlickable.contentWidth - spreadFlickable.width)
416 spreadFlickable.contentX = Math.min(spreadFlickable.contentWidth - spreadFlickable.width, Math.max(spreadFlickable.contentX, contentX))
420 onPressed: mouse.accepted = false
425 objectName: "spreadFlickable"
427 property int minContentWidth: 6 * Math.min(height / 4, width / 5)
428 contentWidth: Math.max(6, topLevelSurfaceList.count) * Math.min(height / 4, width / 5)
431 function snapTo(contentX) {
432 snapAnimation.stop();
433 snapAnimation.to = contentX
434 snapAnimation.start();
437 UbuntuNumberAnimation {
439 target: spreadFlickable
445 id: workspaceSelector
450 topMargin: units.gu(3.5)
452 height: root.height * 0.25
458 Item { Layout.fillWidth: true }
460 model: 1 // TODO: will be a workspacemodel in the future
462 Layout.fillHeight: true
463 Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
465 source: root.background
469 verticalCenter: parent.verticalCenter
471 height: parent.height * 0.75
476 property var source: ShaderEffectSource {
477 id: shaderEffectSource
478 sourceItem: root.workspace
482 varying highp vec2 qt_TexCoord0;
483 uniform sampler2D source;
486 highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
487 gl_FragColor = sourceColor;
492 // TODO: This is the bar for the currently selected workspace
493 // Enable this once the workspace stuff is implemented
495 // anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
496 // height: units.dp(2)
497 // color: theme.palette.normal.focus
498 // visible: index == 0 // TODO: should be active workspace index
503 // TODO: This is the "new workspace" button. Enable this once workspaces are implemented
505 // Layout.fillHeight: true
506 // Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
510 // right: parent.right
511 // verticalCenter: parent.verticalCenter
513 // height: parent.height * 0.75
514 // color: "#22ffffff"
517 // anchors.centerIn: parent
518 // font.pixelSize: parent.height / 2
523 Item { Layout.fillWidth: true }
528 id: currentSelectedLabel
529 anchors { bottom: parent.bottom; bottomMargin: root.height * 0.625; horizontalCenter: parent.horizontalCenter }
530 text: spreadRepeater.highlightedIndex >= 0 ? spreadRepeater.itemAt(spreadRepeater.highlightedIndex).windowTitle: ""
537 name: "altTab"; when: root.altTabPressed
538 PropertyChanges { target: blurLayer; saturation: 0.8; blurRadius: 60; visible: true }
539 PropertyChanges { target: workspaceSelector; visible: true }
540 PropertyChanges { target: spreadContainer; visible: true }
541 PropertyChanges { target: spreadFlickable; enabled: spreadFlickable.contentWidth > spreadFlickable.minContentWidth }
542 PropertyChanges { target: currentSelectedLabel; visible: true }
543 PropertyChanges { target: spreadBackground; visible: true }
544 PropertyChanges { target: hoverMouseArea; enabled: true }
551 SequentialAnimation {
552 PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: Math.min(topLevelSurfaceList.count - 1, 1) }
553 PauseAnimation { duration: spreadContainer.animateIn ? 0 : 140 }
554 PropertyAction { target: workspaceSelector; property: "visible" }
555 PropertyAction { target: spreadContainer; property: "visible" }
557 UbuntuNumberAnimation { target: blurLayer; properties: "saturation,blurRadius"; duration: UbuntuAnimation.SnapDuration }
558 PropertyAction { target: spreadFlickable; property: "visible" }
559 PropertyAction { targets: [currentSelectedLabel,spreadBackground]; property: "visible" }
560 PropertyAction { target: spreadFlickable; property: "contentX"; value: 0 }
562 PropertyAction { target: hoverMouseArea; properties: "enabled,progressiveScrollingEnabled"; value: false }
568 PropertyAnimation { property: "opacity" }
569 ScriptAction { script: { root.focusSelected() } }
570 PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: -1 }
571 PropertyAction { target: spreadContainer; property: "animateIn"; value: false }