Lomiri
ScreensAndWorkspaces.qml
1/*
2 * Copyright (C) 2017 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 Lomiri.Components.Popups 1.3
20import WindowManager 1.0
21import QtMir.Application 0.1
22import ".."
23
24Item {
25 id: root
26
27 property string background
28
29 property var screensProxy: Screens.createProxy();
30
31 property QtObject activeWorkspace: null
32
33 property string mode : "staged"
34
35 signal closeSpread();
36
37 Row {
38 id: row
39 anchors.bottom: parent.bottom
40 anchors.horizontalCenter: parent.horizontalCenter
41 Behavior on anchors.horizontalCenterOffset { NumberAnimation { duration: LomiriAnimation.SlowDuration } }
42 spacing: units.gu(1)
43
44 property var selectedIndex: undefined
45
46 Repeater {
47 model: screensProxy
48
49 delegate: Item {
50 height: root.height - units.gu(6)
51 width: workspaces.width
52 visible: root.mode != "staged" // Hides a phone's internal display
53
54 Item {
55 id: header
56 anchors { left: parent.left; top: parent.top; right: parent.right }
57 height: units.gu(7)
58 z: 1
59
60 property bool isCurrent: {
61 // another screen is selected.
62 if (row.selectedIndex != undefined && row.selectedIndex != index) return false;
63
64 // this screen is active.
65 if (WMScreen.active && WMScreen.isSameAs(model.screen) && WMScreen.currentWorkspace.isSameAs(activeWorkspace)) return true;
66 if (model.screen.workspaces.indexOf(activeWorkspace) >= 0) return true;
67
68 // not active.
69 return false;
70 }
71
72 property bool isSelected: screenMA.containsMouse
73 onIsSelectedChanged: {
74 if (isSelected) {
75 row.selectedIndex = Qt.binding(function() { return index; });
76 } else if (row.selectedIndex === index) {
77 row.selectedIndex = undefined;
78 }
79 }
80
81 LomiriShape {
82 anchors.fill: parent
83 backgroundColor: "white"
84 opacity: header.isCurrent || header.isSelected ? 1.0 : 0.5
85 }
86
87 DropArea {
88 anchors.fill: parent
89 keys: ["workspace"]
90
91 onEntered: {
92 workspaces.workspaceModel.insert(workspaces.workspaceModel.count, {text: drag.source.text})
93 drag.source.inDropArea = true;
94 }
95
96 onExited: {
97 workspaces.workspaceModel.remove(workspaces.workspaceModel.count - 1, 1)
98 drag.source.inDropArea = false;
99 }
100
101 onDropped: {
102 drag.source.inDropArea = false;
103 }
104 }
105
106 Column {
107 anchors.fill: parent
108 anchors.margins: units.gu(1)
109
110 Label {
111 text: model.screen.name
112 color: header.isCurrent || header.isSelected ? "black" : "white"
113 }
114
115 Label {
116 text: model.screen.outputTypeName
117 color: header.isCurrent || header.isSelected ? "black" : "white"
118 fontSize: "x-small"
119 }
120
121 Label {
122 text: screen.availableModes[screen.currentModeIndex].size.width + "x" + screen.availableModes[screen.currentModeIndex].size.height
123 color: header.isCurrent || header.isSelected ? "black" : "white"
124 fontSize: "x-small"
125 }
126 }
127
128 Icon {
129 anchors {
130 top: parent.top
131 right: parent.right
132 margins: units.gu(1)
133 }
134 width: units.gu(3)
135 height: width
136 source: "image://theme/select"
137 color: header.isCurrent || header.isSelected ? "black" : "white"
138 visible: model.screen.active
139 }
140
141 MouseArea {
142 id: screenMA
143 hoverEnabled: true
144 anchors.fill: parent
145
146 onClicked: {
147 var obj = screensMenuComponent.createObject(header)
148 obj.open(mouseX, mouseY)
149 }
150 }
151
152 Component {
153 id: screensMenuComponent
154 LomiriShape {
155 id: screensMenu
156 width: units.gu(20)
157 height: contentColumn.childrenRect.height
158 backgroundColor: "white"
159
160 function open(mouseX, mouseY) {
161 x = Math.max(0, Math.min(mouseX - width / 2, parent.width - width))
162 y = mouseY + units.gu(1)
163 }
164
165 InverseMouseArea {
166 anchors.fill: parent
167 onClicked: {
168 screensMenu.destroy()
169 }
170 }
171
172 Column {
173 id: contentColumn
174 width: parent.width
175 ListItem {
176 height: layout.height
177 highlightColor: "transparent"
178 ListItemLayout {
179 id: layout
180 title.text: qsTr("Add workspace")
181 title.color: "black"
182 }
183 onClicked: {
184 screen.workspaces.addWorkspace();
185 Screens.sync(root.screensProxy);
186 screensMenu.destroy();
187 }
188 }
189 }
190 }
191 }
192 }
193
194 Workspaces {
195 id: workspaces
196 height: parent.height - header.height - units.gu(2)
197 width: {
198 var width = 0;
199 if (screensProxy.count == 1) {
200 width = Math.min(implicitWidth, root.width - units.gu(8));
201 } else {
202 width = Math.min(implicitWidth, model.screen.active ? root.width - units.gu(48) : units.gu(40))
203 }
204 return Math.max(workspaces.minimumWidth, width);
205 }
206
207 Behavior on width { LomiriNumberAnimation {} }
208 anchors.bottom: parent.bottom
209 anchors.bottomMargin: units.gu(1)
210 anchors.horizontalCenter: parent.horizontalCenter
211 screen: model.screen
212 background: root.background
213
214 workspaceModel: model.screen.workspaces
215 activeWorkspace: root.activeWorkspace
216 readOnly: false
217
218 onCommitScreenSetup: Screens.sync(root.screensProxy)
219 onCloseSpread: root.closeSpread();
220
221 onClicked: {
222 root.activeWorkspace = workspace;
223 }
224 }
225 }
226 }
227 }
228
229 Rectangle {
230 anchors { left: parent.left; top: parent.top; bottom: parent.bottom; topMargin: units.gu(6); bottomMargin: units.gu(1) }
231 width: units.gu(5)
232 color: "#33000000"
233 visible: (row.width - root.width + units.gu(10)) / 2 - row.anchors.horizontalCenterOffset > units.gu(5)
234 MouseArea {
235 id: leftScrollArea
236 anchors.fill: parent
237 hoverEnabled: true
238 onPressed: mouse.accepted = false;
239 }
240 DropArea {
241 id: leftFakeDropArea
242 anchors.fill: parent
243 keys: ["application", "workspace"]
244 }
245 }
246 Rectangle {
247 anchors { right: parent.right; top: parent.top; bottom: parent.bottom; topMargin: units.gu(6); bottomMargin: units.gu(1) }
248 width: units.gu(5)
249 color: "#33000000"
250 visible: (row.width - root.width + units.gu(10)) / 2 + row.anchors.horizontalCenterOffset > units.gu(5)
251 MouseArea {
252 id: rightScrollArea
253 anchors.fill: parent
254 hoverEnabled: true
255 onPressed: mouse.accepted = false;
256 }
257 DropArea {
258 id: rightFakeDropArea
259 anchors.fill: parent
260 keys: ["application", "workspace"]
261 }
262 }
263 Timer {
264 repeat: true
265 running: leftScrollArea.containsMouse || rightScrollArea.containsMouse || leftFakeDropArea.containsDrag || rightFakeDropArea.containsDrag
266 interval: LomiriAnimation.SlowDuration
267 triggeredOnStart: true
268 onTriggered: {
269 var newOffset = row.anchors.horizontalCenterOffset;
270 var maxOffset = Math.max((row.width - root.width + units.gu(10)) / 2, 0);
271 if (leftScrollArea.containsMouse || leftFakeDropArea.containsDrag) {
272 newOffset += units.gu(20)
273 } else {
274 newOffset -= units.gu(20)
275 }
276 newOffset = Math.max(-maxOffset, Math.min(maxOffset, newOffset));
277 row.anchors.horizontalCenterOffset = newOffset;
278 }
279 }
280}