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
26 property bool altTabPressed: false
27 property Item workspace: null
29 readonly property alias highlightedIndex: spreadRepeater.highlightedIndex
32 // When the spread comes active, we want to keep focus to the input handler below
33 // Make sure nothing inside the ApplicationWindow grabs our focus!
43 selectPrevious(event.isAutoRepeat)
44 event.accepted = true;
48 selectNext(event.isAutoRepeat)
49 event.accepted = true;
52 spreadRepeater.highlightedIndex = -1
53 // Falling through intentionally
58 event.accepted = true;
62 function selectNext(isAutoRepeat) {
63 if (isAutoRepeat && spreadRepeater.highlightedIndex >= ApplicationManager.count -1) {
64 return; // AutoRepeat is not allowed to wrap around
67 spreadRepeater.highlightedIndex = (spreadRepeater.highlightedIndex + 1) % ApplicationManager.count;
68 var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, spreadRepeater.highlightedIndex - 3));
69 if (spreadFlickable.contentX < newContentX || spreadRepeater.highlightedIndex == 0) {
70 spreadFlickable.snapTo(newContentX)
74 function selectPrevious(isAutoRepeat) {
75 if (isAutoRepeat && spreadRepeater.highlightedIndex == 0) {
76 return; // AutoRepeat is not allowed to wrap around
79 var newIndex = spreadRepeater.highlightedIndex - 1 >= 0 ? spreadRepeater.highlightedIndex - 1 : ApplicationManager.count - 1;
80 spreadRepeater.highlightedIndex = newIndex;
81 var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, spreadRepeater.highlightedIndex - 1));
82 if (spreadFlickable.contentX > newContentX || newIndex == ApplicationManager.count -1) {
83 spreadFlickable.snapTo(newContentX)
87 function focusSelected() {
88 if (spreadRepeater.highlightedIndex != -1) {
89 var application = ApplicationManager.get(spreadRepeater.highlightedIndex);
90 ApplicationManager.focusApplication(application.appId);
95 spreadRepeater.highlightedIndex = -1;
101 objectName: "spreadContainer"
105 property bool animateIn: false
109 objectName: "spreadRepeater"
110 model: ApplicationManager
112 property int highlightedIndex: -1
113 property int closingIndex: -1
115 function indexOf(delegateItem) {
116 for (var i = 0; i < spreadRepeater.count; i++) {
117 if (spreadRepeater.itemAt(i) === delegateItem) {
126 objectName: "spreadDelegate"
130 property real angle: 0
131 property real itemScale: 1
132 property int itemScaleOriginX: 0
133 property int itemScaleOriginY: 0
137 enabled: spreadRepeater.closingIndex >= 0
138 UbuntuNumberAnimation {
139 onRunningChanged: if (!running) spreadRepeater.closingIndex = -1
143 DesktopSpreadDelegate {
144 id: clippedSpreadDelegate
145 objectName: "clippedSpreadDelegate"
146 anchors.left: parent.left
147 anchors.top: parent.top
148 application: ApplicationManager.get(index)
149 width: spreadMaths.spreadHeight
150 height: spreadMaths.spreadHeight
154 origin.x: itemScaleOriginX
155 origin.y: itemScaleOriginY
160 origin { x: 0; y: (clippedSpreadDelegate.height - (clippedSpreadDelegate.height * itemScale / 2)) }
161 axis { x: 0; y: 1; z: 0 }
162 angle: spreadDelegate.angle
169 anchors.margins: -units.gu(2)
172 spreadRepeater.highlightedIndex = index;
180 flickable: spreadFlickable
182 totalItems: Math.max(6, ApplicationManager.count)
183 sceneHeight: root.height
184 itemHeight: spreadDelegate.height
189 name: "altTab"; when: root.state == "altTab" && spreadContainer.visible
191 target: spreadDelegate
192 x: spreadMaths.animatedX
193 y: spreadMaths.animatedY + (spreadDelegate.height - clippedSpreadDelegate.height) - units.gu(2)
194 width: spreadMaths.spreadHeight
195 height: spreadMaths.sceneHeight
196 angle: spreadMaths.animatedAngle
197 itemScale: spreadMaths.scale
198 itemScaleOriginY: clippedSpreadDelegate.height / 2;
200 visible: spreadMaths.itemVisible
203 target: clippedSpreadDelegate
204 highlightShown: index == spreadRepeater.highlightedIndex
206 shadowOpacity: spreadMaths.shadowOpacity
207 anchors.topMargin: units.gu(2)
212 opacity: spreadMaths.tileInfoOpacity
215 target: spreadSelectArea
224 PropertyAction { target: spreadDelegate; properties: "y,height,width,angle,z,itemScale,itemScaleOriginY,visible" }
225 PropertyAction { target: clippedSpreadDelegate; properties: "anchors.topMargin" }
227 target: spreadDelegate; properties: "x"
229 duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration :0
230 easing: UbuntuAnimation.StandardEasing
237 objectName: "tileInfo"
238 anchors { left: parent.left; top: clippedSpreadDelegate.bottom; topMargin: units.gu(5) }
239 property int nextItemX: spreadRepeater.count > index + 1 ? spreadRepeater.itemAt(index + 1).x : spreadDelegate.x + units.gu(30)
240 width: Math.min(units.gu(30), nextItemX - spreadDelegate.x)
241 height: titleInfoColumn.height
245 onContainsMouseChanged: {
247 spreadRepeater.highlightedIndex = index
257 anchors { left: parent.left; top: parent.top; right: parent.right }
261 Layout.preferredHeight: Math.min(units.gu(6), root.height * .05)
262 Layout.preferredWidth: height * 8 / 7.6
269 Layout.fillWidth: true
270 Layout.preferredHeight: units.gu(6)
272 wrapMode: Text.WordWrap
273 elide: Text.ElideRight
281 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset + units.gu(2) }
282 source: "graphics/window-close.svg"
283 readonly property var mousePos: hoverMouseArea.mapToItem(spreadDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
284 visible: index == spreadRepeater.highlightedIndex
285 && mousePos.y < (clippedSpreadDelegate.height / 3)
286 && mousePos.y > -units.gu(4)
287 && mousePos.x > -units.gu(4)
288 && mousePos.x < (clippedSpreadDelegate.width * 2 / 3)
289 height: units.gu(1.5)
291 sourceSize.width: width
292 sourceSize.height: height
296 objectName: "closeMouseArea"
297 anchors.fill: closeImage
298 anchors.margins: -units.gu(2)
300 spreadRepeater.closingIndex = index;
301 ApplicationManager.stopApplication(model.appId)
312 objectName: "hoverMouseArea"
313 anchors.fill: spreadContainer
314 propagateComposedEvents: true
319 property int scrollAreaWidth: root.width / 3
320 property bool progressiveScrollingEnabled: false
323 mouse.accepted = false
325 if (hoverMouseArea.pressed) {
329 // Find the hovered item and mark it active
330 var mapped = mapToItem(spreadContainer, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
331 var itemUnder = spreadContainer.childAt(mapped.x, mapped.y)
333 mapped = mapToItem(itemUnder, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
334 var delegateChild = itemUnder.childAt(mapped.x, mapped.y)
335 if (delegateChild && (delegateChild.objectName === "clippedSpreadDelegate" || delegateChild.objectName === "tileInfo")) {
336 spreadRepeater.highlightedIndex = spreadRepeater.indexOf(itemUnder)
340 if (spreadFlickable.contentWidth > spreadFlickable.minContentWidth) {
341 var margins = spreadFlickable.width * 0.05;
343 if (!progressiveScrollingEnabled && mouseX < spreadFlickable.width - scrollAreaWidth) {
344 progressiveScrollingEnabled = true
347 // do we need to scroll?
348 if (mouseX < scrollAreaWidth + margins) {
349 var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
350 var contentX = (1 - progress) * (spreadFlickable.contentWidth - spreadFlickable.width)
351 spreadFlickable.contentX = Math.max(0, Math.min(spreadFlickable.contentX, contentX))
353 if (mouseX > spreadFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
354 var progress = Math.min(1, (mouseX - (spreadFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
355 var contentX = progress * (spreadFlickable.contentWidth - spreadFlickable.width)
356 spreadFlickable.contentX = Math.min(spreadFlickable.contentWidth - spreadFlickable.width, Math.max(spreadFlickable.contentX, contentX))
360 onPressed: mouse.accepted = false
365 objectName: "spreadFlickable"
367 property int minContentWidth: 6 * Math.min(height / 4, width / 5)
368 contentWidth: Math.max(6, ApplicationManager.count) * Math.min(height / 4, width / 5)
371 function snapTo(contentX) {
372 snapAnimation.stop();
373 snapAnimation.to = contentX
374 snapAnimation.start();
377 UbuntuNumberAnimation {
379 target: spreadFlickable
385 id: workspaceSelector
390 topMargin: units.gu(3.5)
392 height: root.height * 0.25
398 Item { Layout.fillWidth: true }
400 model: 1 // TODO: will be a workspacemodel in the future
402 Layout.fillHeight: true
403 Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
405 source: root.background
409 verticalCenter: parent.verticalCenter
411 height: parent.height * 0.75
416 property var source: ShaderEffectSource {
417 id: shaderEffectSource
418 sourceItem: appContainer
422 varying highp vec2 qt_TexCoord0;
423 uniform sampler2D source;
426 highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
427 gl_FragColor = sourceColor;
432 // TODO: This is the bar for the currently selected workspace
433 // Enable this once the workspace stuff is implemented
435 // anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
436 // height: units.dp(2)
437 // color: UbuntuColors.orange
438 // visible: index == 0 // TODO: should be active workspace index
443 // TODO: This is the "new workspace" button. Enable this once workspaces are implemented
445 // Layout.fillHeight: true
446 // Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
450 // right: parent.right
451 // verticalCenter: parent.verticalCenter
453 // height: parent.height * 0.75
454 // color: "#22ffffff"
457 // anchors.centerIn: parent
458 // font.pixelSize: parent.height / 2
463 Item { Layout.fillWidth: true }
468 id: currentSelectedLabel
469 anchors { bottom: parent.bottom; bottomMargin: root.height * 0.625; horizontalCenter: parent.horizontalCenter }
470 text: spreadRepeater.highlightedIndex >= 0 ? ApplicationManager.get(spreadRepeater.highlightedIndex).name : ""
477 name: "altTab"; when: root.altTabPressed
478 PropertyChanges { target: blurLayer; saturation: 0.8; blurRadius: 60; visible: true }
479 PropertyChanges { target: workspaceSelector; visible: true }
480 PropertyChanges { target: spreadContainer; visible: true }
481 PropertyChanges { target: spreadFlickable; enabled: spreadFlickable.contentWidth > spreadFlickable.minContentWidth }
482 PropertyChanges { target: currentSelectedLabel; visible: true }
483 PropertyChanges { target: spreadBackground; visible: true }
484 PropertyChanges { target: hoverMouseArea; enabled: true }
491 SequentialAnimation {
492 PropertyAction { target: hoverMouseArea; property: "progressiveScrollingEnabled"; value: false }
493 PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: Math.min(ApplicationManager.count - 1, 1) }
494 PauseAnimation { duration: 140 }
495 PropertyAction { target: workspaceSelector; property: "visible" }
496 PropertyAction { target: spreadContainer; property: "visible" }
498 UbuntuNumberAnimation {
499 target: blurLayer; properties: "saturation,blurRadius";
500 duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration : 0
502 PropertyAction { target: spreadFlickable; property: "visible" }
503 PropertyAction { targets: [currentSelectedLabel,spreadBackground]; property: "visible" }
504 PropertyAction { target: spreadFlickable; property: "contentX"; value: 0 }
511 PropertyAnimation { property: "opacity" }
512 ScriptAction { script: { root.focusSelected() } }
513 PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: -1 }
514 PropertyAction { target: spreadContainer; property: "animateIn"; value: false }
520 id: rightEdgePushArea
524 bottom: parent.bottom
526 // TODO: Make this a push to edge thing like the launcher when we can,
527 // for now, yes, we want 1 pixel, regardless of the scaling
530 onContainsMouseChanged: {
532 spreadContainer.animateIn = true;
533 root.state = "altTab";