Lomiri
Loading...
Searching...
No Matches
Spread.qml
1/*
2 * Copyright (C) 2016 Canonical Ltd.
3 *
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.
7 *
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.
12 *
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/>.
15 */
16
17import QtQuick 2.12
18import Lomiri.Components 1.3
19import "MathUtils.js" as MathUtils
20
21Item {
22 id: root
23
24 // Information about the environment
25 property int highlightedIndex: -1
26 property var model: null
27 property int leftMargin: 0
28 property var spreadFlickable
29
30 // some config options
31 property real contentMargin: 0.1 * root.height
32 property real contentTopMargin: contentMargin + root.y + windowTitle.height
33 property real contentBottomMargin: contentMargin
34 property real windowTitleTopMargin: contentMargin - windowTitle.height
35 property int stackItemCount: 3
36 property real leftRotationAngle: 22
37 property real rightRotationAngle: 32
38 property real leftStackScale: .82
39 property real rightStackScale: 1
40 property real rightEdgeBreakPoint: Math.min(units.gu(40) / root.width, .35)
41
42 signal leaveSpread()
43 signal closeCurrentApp();
44
45 // Calculated stuff
46 readonly property int totalItemCount: model.count
47 readonly property real leftStackXPos: 0.03 * root.width + leftMargin
48 readonly property real rightStackXPos: root.width - 1.5 * leftStackXPos + leftMargin
49
50 readonly property real stackHeight: spreadItemHeight - appInfoHeight
51 readonly property real stackWidth: Math.min(leftStackXPos/3, units.gu(1.5))
52
53 readonly property real spreadWidth: rightStackXPos - leftStackXPos
54 readonly property real spreadHeight: root.height
55 readonly property real spreadItemHeight: spreadHeight - contentMargin * 2
56 readonly property real spreadItemWidth: stackHeight
57
58 readonly property real dynamicLeftRotationAngle: leftRotationAngle * rotationAngleFactor
59 readonly property real dynamicRightRotationAngle: rightRotationAngle * rotationAngleFactor
60
61 readonly property real appInfoHeight: {
62 var screenHeightReferencePoint = 40 // ref screen height in gu
63 var valueAtReferencePoint = 0.17 // of screen height at the reference point
64 var appInfoHeightValueChange = -0.0014 // units / gu
65 var minAppInfoHeight = 0.08
66 var maxAppInfoHeight = 0.2
67 var screenHeightInGU = root.height / units.gu(1) // screenHeight in gu
68
69 return MathUtils.clamp(valueAtReferencePoint + appInfoHeightValueChange * (screenHeightInGU - screenHeightReferencePoint), minAppInfoHeight, maxAppInfoHeight) * root.height
70 }
71
72 property real rotationAngleFactor: {
73 var spreadHeightReferencePoint = 28 // reference spread height in gu
74 var valueAtReferencePoint = 1.3
75 var rotationAngleValueChange = -0.008 // units / gu
76 var minRotationAngleFactor = 0.6
77 var maxRotationAngleFactor = 1.5
78 var spreadHeightInGU = spreadHeight / units.gu(1)
79
80 return MathUtils.clamp(valueAtReferencePoint + rotationAngleValueChange * (spreadHeightInGU - spreadHeightReferencePoint), minRotationAngleFactor, maxRotationAngleFactor)
81 }
82 readonly property real itemOverlap: {
83 var spreadAspectRatioReferencePoint = 1.0 // ref screen height in gu
84 var valueAtReferencePoint = 0.74 // of screen height at the reference point
85 var itemOverlapValueChange = -0.068
86 var minOverlap = 0.55
87 var maxOverlap = 0.82
88 var spreadAspectRatio = spreadWidth / stackHeight // spread stack aspect ratio (app info not included)
89
90 return MathUtils.clamp(valueAtReferencePoint + itemOverlapValueChange * (spreadAspectRatio - spreadAspectRatioReferencePoint), minOverlap, maxOverlap)
91 }
92
93 readonly property real visibleItemCount: (spreadWidth / spreadItemWidth) / (1 - itemOverlap)
94
95 readonly property real spreadTotalWidth: Math.max(2,totalItemCount) * spreadWidth / visibleItemCount
96
97 readonly property real centeringOffset: Math.max(spreadWidth - spreadTotalWidth + (leftStackXPos - leftMargin) * 2, 0) / (2 * spreadWidth)
98
99 readonly property var curve: BezierCurve {
100 controlPoint2: {'x': 0.19, 'y': 0.00}
101 controlPoint3: {'x': 0.91, 'y': 1.00}
102 }
103
104 Label {
105 id: windowTitle
106
107 width: Math.min(implicitWidth, 0.5*root.width)
108 elide: Qt.ElideMiddle
109 anchors.horizontalCenter: parent.horizontalCenter
110 y: windowTitleTopMargin
111 readonly property var highlightedSurface: root.model ? root.model.surfaceAt(root.highlightedIndex) : null
112 readonly property var highlightedApp: root.model ? root.model.applicationAt(root.highlightedIndex) : null
113 text: root.highlightedIndex >= 0 && highlightedSurface && highlightedSurface.name != "" ? highlightedSurface.name :
114 highlightedApp ? highlightedApp.name : ""
115 fontSize: root.height < units.gu(85) ? 'medium' : 'large'
116 color: "white"
117 opacity: root.highlightedIndex >= 0 ? 1 : 0
118 Behavior on opacity { LomiriNumberAnimation { } }
119 }
120
121 readonly property int itemCount: root.model.count
122 onItemCountChanged: {
123 if (highlightedIndex >= itemCount) {
124 highlightedIndex = itemCount - 1
125 }
126 }
127
128 Keys.onPressed: {
129 switch (event.key) {
130 case Qt.Key_Left:
131 case Qt.Key_Backtab:
132 selectPrevious(event.isAutoRepeat)
133 event.accepted = true;
134 break;
135 case Qt.Key_Right:
136 case Qt.Key_Tab:
137 selectNext(event.isAutoRepeat)
138 event.accepted = true;
139 break;
140 case Qt.Key_Q:
141 closeCurrentApp();
142 break;
143 case Qt.Key_Escape:
144 highlightedIndex = -1
145 // Falling through intentionally
146 case Qt.Key_Enter:
147 case Qt.Key_Return:
148 case Qt.Key_Space:
149 root.leaveSpread();
150 event.accepted = true;
151 }
152 }
153
154
155 function selectNext(isAutoRepeat) {
156 if (isAutoRepeat && highlightedIndex >= totalItemCount -1) {
157 return; // AutoRepeat is not allowed to wrap around
158 }
159
160 highlightedIndex = (highlightedIndex + 1) % totalItemCount;
161 spreadFlickable.snap(highlightedIndex)
162 }
163
164 function selectPrevious(isAutoRepeat) {
165 if (isAutoRepeat && highlightedIndex == 0) {
166 return; // AutoRepeat is not allowed to wrap around
167 }
168
169 highlightedIndex = highlightedIndex - 1 >= 0 ? highlightedIndex - 1 : totalItemCount - 1;
170 spreadFlickable.snap(highlightedIndex)
171 }
172}