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