Changeset View
Changeset View
Standalone View
Standalone View
applets/systemtray/package/contents/ui/main.qml
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | * Copyright 2011 Marco Martin <mart@kde.org> | 2 | * Copyright 2011 Marco Martin <mart@kde.org> | ||
3 | * Copyright 2020 Konrad Materka <materka@gmail.com> | ||||
3 | * | 4 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU Library General Public License as | 6 | * it under the terms of the GNU Library General Public License as | ||
6 | * published by the Free Software Foundation; either version 2, or | 7 | * published by the Free Software Foundation; either version 2, or | ||
7 | * (at your option) any later version. | 8 | * (at your option) any later version. | ||
8 | * | 9 | * | ||
9 | * This program is distributed in the hope that it will be useful, | 10 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU Library General Public License for more details | 13 | * GNU Library General Public License for more details | ||
13 | * | 14 | * | ||
14 | * You should have received a copy of the GNU Library General Public | 15 | * You should have received a copy of the GNU Library General Public | ||
15 | * License along with this program; if not, write to the | 16 | * License along with this program; if not, write to the | ||
16 | * Free Software Foundation, Inc., | 17 | * Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
18 | */ | 19 | */ | ||
19 | 20 | | |||
20 | import QtQuick 2.5 | 21 | import QtQuick 2.5 | ||
21 | import QtQuick.Layouts 1.1 | 22 | import QtQuick.Layouts 1.1 | ||
22 | import org.kde.plasma.core 2.0 as PlasmaCore | 23 | import org.kde.plasma.core 2.1 as PlasmaCore | ||
23 | import org.kde.plasma.plasmoid 2.0 | 24 | import org.kde.plasma.plasmoid 2.0 | ||
24 | import org.kde.draganddrop 2.0 as DnD | 25 | import org.kde.draganddrop 2.0 as DnD | ||
25 | import org.kde.kirigami 2.5 as Kirigami | 26 | import org.kde.kirigami 2.5 as Kirigami | ||
26 | 27 | | |||
27 | import "items" | 28 | import "items" | ||
28 | 29 | | |||
29 | MouseArea { | 30 | MouseArea { | ||
30 | id: root | 31 | id: root | ||
31 | 32 | | |||
32 | Layout.minimumWidth: vertical ? units.iconSizes.small : tasksRow.implicitWidth + (expander.visible ? expander.implicitWidth : 0) + units.smallSpacing | 33 | Layout.minimumWidth: vertical ? units.iconSizes.small : tasksGrid.implicitWidth + (expander.visible ? expander.implicitWidth : 0) + units.smallSpacing | ||
33 | 34 | | |||
34 | Layout.minimumHeight: vertical ? tasksRow.implicitHeight + (expander.visible ? expander.implicitHeight : 0) + units.smallSpacing : units.smallSpacing | 35 | Layout.minimumHeight: vertical ? tasksGrid.implicitHeight + (expander.visible ? expander.implicitHeight : 0) + units.smallSpacing : units.smallSpacing | ||
35 | 36 | | |||
36 | Layout.preferredHeight: Layout.minimumHeight | 37 | Layout.preferredHeight: Layout.minimumHeight | ||
37 | LayoutMirroring.enabled: !vertical && Qt.application.layoutDirection === Qt.RightToLeft | 38 | LayoutMirroring.enabled: !vertical && Qt.application.layoutDirection === Qt.RightToLeft | ||
38 | LayoutMirroring.childrenInherit: true | 39 | LayoutMirroring.childrenInherit: true | ||
39 | 40 | | |||
40 | property var iconSizes: ["small", "smallMedium", "medium", "large", "huge", "enormous"]; | 41 | property var iconSizes: ["small", "smallMedium", "medium", "large", "huge", "enormous"]; | ||
41 | property int iconSize: plasmoid.configuration.iconSize + (Kirigami.Settings.tabletMode ? 1 : 0) | 42 | property int iconSize: plasmoid.configuration.iconSize + (Kirigami.Settings.tabletMode ? 1 : 0) | ||
42 | 43 | | |||
43 | property bool vertical: plasmoid.formFactor === PlasmaCore.Types.Vertical | 44 | property bool vertical: plasmoid.formFactor === PlasmaCore.Types.Vertical | ||
44 | readonly property int itemSize: units.roundToIconSize(Math.min(Math.min(width, height), units.iconSizes[iconSizes[Math.min(iconSizes.length-1, iconSize)]])) | 45 | readonly property int itemSize: units.roundToIconSize(Math.min(Math.min(width, height), units.iconSizes[iconSizes[Math.min(iconSizes.length-1, iconSize)]])) | ||
45 | property int hiddenItemSize: units.iconSizes.smallMedium | 46 | property int hiddenItemSize: units.iconSizes.smallMedium | ||
46 | property alias expanded: dialog.visible | 47 | property alias expanded: dialog.visible | ||
47 | property Item activeApplet | 48 | property Item activeApplet | ||
48 | property int status: dialog.visible ? PlasmaCore.Types.RequiresAttentionStatus : PlasmaCore.Types.PassiveStatus | 49 | property int status: dialog.visible ? PlasmaCore.Types.RequiresAttentionStatus : PlasmaCore.Types.PassiveStatus | ||
49 | 50 | | |||
50 | property alias visibleLayout: tasksRow | 51 | property alias visibleLayout: tasksGrid | ||
51 | property alias hiddenLayout: expandedRepresentation.hiddenLayout | 52 | property alias hiddenLayout: expandedRepresentation.hiddenLayout | ||
52 | 53 | | |||
53 | property alias statusNotifierModel: statusNotifierModel | | |||
54 | | ||||
55 | // workaround https://bugreports.qt.io/browse/QTBUG-71238 / https://bugreports.qt.io/browse/QTBUG-72004 | | |||
56 | property Component plasmoidItemComponent: Qt.createComponent("items/PlasmoidItem.qml") | | |||
57 | | ||||
58 | property int creationIdCounter: 0 | | |||
59 | | ||||
60 | Plasmoid.onExpandedChanged: { | 54 | Plasmoid.onExpandedChanged: { | ||
61 | if (!plasmoid.expanded) { | 55 | if (!plasmoid.expanded) { | ||
62 | dialog.visible = plasmoid.expanded; | 56 | dialog.visible = plasmoid.expanded; | ||
63 | } | 57 | } | ||
64 | } | 58 | } | ||
65 | 59 | | |||
66 | // temporary hack to fix known broken categories | | |||
67 | // should go away as soon as fixes are merged | | |||
68 | readonly property var categoryOverride: { | | |||
69 | "org.kde.discovernotifier": "SystemServices", | | |||
70 | "org.kde.plasma.networkmanagement": "Hardware", | | |||
71 | "org.kde.kdeconnect": "Hardware", | | |||
72 | "org.kde.plasma.keyboardindicator": "Hardware", | | |||
73 | "touchpad": "Hardware" | | |||
74 | } | | |||
75 | | ||||
76 | readonly property var categoryOrder: [ | | |||
77 | "UnknownCategory", "ApplicationStatus", "Communications", | | |||
78 | "SystemServices", "Hardware" | | |||
79 | ] | | |||
80 | function indexForItemCategory(item) { | | |||
81 | if (item.itemId == "org.kde.plasma.notifications") { | | |||
82 | return -1 | | |||
83 | } | | |||
84 | var i = categoryOrder.indexOf(categoryOverride[item.itemId] || item.category) | | |||
85 | return i == -1 ? categoryOrder.indexOf("UnknownCategory") : i | | |||
86 | } | | |||
87 | | ||||
88 | // return negative integer if a < b, 0 if a === b, and positive otherwise | | |||
89 | function compareItems(a, b) { | | |||
90 | var categoryDiff = indexForItemCategory(a) - indexForItemCategory(b) | | |||
91 | var textDiff = (categoryDiff != 0 ? categoryDiff : a.text.localeCompare(b.text)) | | |||
92 | return textDiff != 0 ? textDiff : b.creationId - a.creationId | | |||
93 | } | | |||
94 | | ||||
95 | function moveItemAt(item, container, index) { | | |||
96 | if (container.children.length == 0) { | | |||
97 | item.parent = container | | |||
98 | } else { | | |||
99 | if (index == container.children.length) { | | |||
100 | var other = container.children[index - 1] | | |||
101 | if (item != other) { | | |||
102 | plasmoid.nativeInterface.reorderItemAfter(item, other) | | |||
103 | } | | |||
104 | } else { | | |||
105 | var other = container.children[index] | | |||
106 | if (item != other) { | | |||
107 | plasmoid.nativeInterface.reorderItemBefore(item, other) | | |||
108 | } | | |||
109 | } | | |||
110 | } | | |||
111 | } | | |||
112 | | ||||
113 | function reorderItem(item, container) { | | |||
114 | var i = 0; | | |||
115 | while (i < container.children.length && | | |||
116 | compareItems(container.children[i], item) <= 0) { | | |||
117 | i++ | | |||
118 | } | | |||
119 | moveItemAt(item, container, i) | | |||
120 | } | | |||
121 | | ||||
122 | function updateItemVisibility(item) { | | |||
123 | switch (item.effectiveStatus) { | | |||
124 | case PlasmaCore.Types.HiddenStatus: | | |||
125 | if (item.parent != invisibleEntriesContainer) { | | |||
126 | item.parent = invisibleEntriesContainer; | | |||
127 | } | | |||
128 | break; | | |||
129 | | ||||
130 | case PlasmaCore.Types.ActiveStatus: | | |||
131 | reorderItem(item, visibleLayout) | | |||
132 | break; | | |||
133 | | ||||
134 | case PlasmaCore.Types.PassiveStatus: | | |||
135 | reorderItem(item, hiddenLayout) | | |||
136 | item.x = 0; | | |||
137 | break; | | |||
138 | } | | |||
139 | } | | |||
140 | | ||||
141 | onWheel: { | 60 | onWheel: { | ||
142 | // Don't propagate unhandled wheel events | 61 | // Don't propagate unhandled wheel events | ||
143 | wheel.accepted = true; | 62 | wheel.accepted = true; | ||
144 | } | 63 | } | ||
145 | 64 | | |||
146 | Containment.onAppletAdded: { | | |||
147 | //Allow the plasmoid expander to know in what window it will be | | |||
148 | var plasmoidContainer = plasmoidItemComponent.createObject(invisibleEntriesContainer, {"x": x, "y": y, "applet": applet}); | | |||
149 | | ||||
150 | applet.parent = plasmoidContainer | | |||
151 | applet.anchors.left = plasmoidContainer.left | | |||
152 | applet.anchors.top = plasmoidContainer.top | | |||
153 | applet.anchors.bottom = plasmoidContainer.bottom | | |||
154 | applet.width = plasmoidContainer.height | | |||
155 | applet.visible = true | | |||
156 | plasmoidContainer.visible = true | | |||
157 | | ||||
158 | //This is to make preloading effective, minimizes the scene changes | | |||
159 | if (applet.fullRepresentationItem) { | | |||
160 | applet.fullRepresentationItem.width = expandedRepresentation.width | | |||
161 | applet.fullRepresentationItem.width = expandedRepresentation.height | | |||
162 | applet.fullRepresentationItem.parent = preloadedStorage; | | |||
163 | } else { | | |||
164 | applet.fullRepresentationItemChanged.connect(function() { | | |||
165 | applet.fullRepresentationItem.width = expandedRepresentation.width | | |||
166 | applet.fullRepresentationItem.width = expandedRepresentation.height | | |||
167 | applet.fullRepresentationItem.parent = preloadedStorage; | | |||
168 | }); | | |||
169 | } | | |||
170 | } | | |||
171 | | ||||
172 | //being there forces the items to fully load, and they will be reparented in the popup one by one, this item is *never* visible | 65 | //being there forces the items to fully load, and they will be reparented in the popup one by one, this item is *never* visible | ||
173 | Item { | 66 | Item { | ||
174 | id: preloadedStorage | 67 | id: preloadedStorage | ||
175 | visible: false | 68 | visible: false | ||
176 | } | 69 | } | ||
177 | 70 | | |||
178 | Containment.onAppletRemoved: { | | |||
179 | } | | |||
180 | | ||||
181 | Connections { | 71 | Connections { | ||
182 | target: plasmoid | 72 | target: plasmoid | ||
183 | onUserConfiguringChanged: { | 73 | onUserConfiguringChanged: { | ||
184 | if (plasmoid.userConfiguring) { | 74 | if (plasmoid.userConfiguring) { | ||
185 | dialog.visible = false | 75 | dialog.visible = false | ||
186 | } | 76 | } | ||
187 | } | 77 | } | ||
188 | } | 78 | } | ||
189 | 79 | | |||
190 | Connections { | 80 | Connections { | ||
191 | target: plasmoid.configuration | 81 | target: plasmoid.configuration | ||
192 | 82 | | |||
193 | onExtraItemsChanged: plasmoid.nativeInterface.allowedPlasmoids = plasmoid.configuration.extraItems | 83 | onExtraItemsChanged: plasmoid.nativeInterface.allowedPlasmoids = plasmoid.configuration.extraItems | ||
194 | } | 84 | } | ||
195 | 85 | | |||
196 | Component.onCompleted: { | | |||
197 | //script, don't bind | | |||
198 | plasmoid.nativeInterface.allowedPlasmoids = initializePlasmoidList(); | | |||
199 | } | | |||
200 | | ||||
201 | function initializePlasmoidList() { | | |||
202 | var newKnownItems = []; | | |||
203 | var newExtraItems = []; | | |||
204 | | ||||
205 | //NOTE:why this? otherwise the interpreter will execute plasmoid.nativeInterface.defaultPlasmoids() on | | |||
206 | //every access of defaults[], resulting in a very slow iteration | | |||
207 | var defaults = []; | | |||
208 | //defaults = defaults.concat(plasmoid.nativeInterface.defaultPlasmoids); | | |||
209 | defaults = plasmoid.nativeInterface.defaultPlasmoids.slice() | | |||
210 | var candidate; | | |||
211 | | ||||
212 | //Add every plasmoid that is both not enabled explicitly and not already known | | |||
213 | for (var i = 0; i < defaults.length; ++i) { | | |||
214 | candidate = defaults[i]; | | |||
215 | if (plasmoid.configuration.knownItems.indexOf(candidate) === -1) { | | |||
216 | newKnownItems.push(candidate); | | |||
217 | if (plasmoid.configuration.extraItems.indexOf(candidate) === -1) { | | |||
218 | newExtraItems.push(candidate); | | |||
219 | } | | |||
220 | } | | |||
221 | } | | |||
222 | | ||||
223 | if (newExtraItems.length > 0) { | | |||
224 | plasmoid.configuration.extraItems = plasmoid.configuration.extraItems.slice().concat(newExtraItems); | | |||
225 | } | | |||
226 | if (newKnownItems.length > 0) { | | |||
227 | plasmoid.configuration.knownItems = plasmoid.configuration.knownItems.slice().concat(newKnownItems); | | |||
228 | } | | |||
229 | | ||||
230 | return plasmoid.configuration.extraItems; | | |||
231 | } | | |||
232 | | ||||
233 | PlasmaCore.DataSource { | | |||
234 | id: statusNotifierSource | | |||
235 | engine: "statusnotifieritem" | | |||
236 | interval: 0 | | |||
237 | onSourceAdded: { | | |||
238 | connectSource(source) | | |||
239 | } | | |||
240 | Component.onCompleted: { | | |||
241 | connectedSources = sources | | |||
242 | } | | |||
243 | } | | |||
244 | | ||||
245 | PlasmaCore.SortFilterModel { | | |||
246 | id: statusNotifierModel | | |||
247 | sourceModel: PlasmaCore.DataModel { | | |||
248 | dataSource: statusNotifierSource | | |||
249 | } | | |||
250 | } | | |||
251 | | ||||
252 | //This is a dump for items we don't want to be seen or as an incubation, when they are | | |||
253 | //created as a nursery before going in their final place | | |||
254 | Item { | | |||
255 | id: invisibleEntriesContainer | | |||
256 | visible: false | | |||
257 | Repeater { | | |||
258 | id: tasksRepeater | | |||
259 | model: statusNotifierModel | | |||
260 | | ||||
261 | delegate: StatusNotifierItem {} | | |||
262 | } | | |||
263 | //NOTE: this exists mostly for not causing reference errors | | |||
264 | property QtObject marginHints: QtObject { | | |||
265 | property int left: 0 | | |||
266 | property int top: 0 | | |||
267 | property int right: 0 | | |||
268 | property int bottom: 0 | | |||
269 | } | | |||
270 | } | | |||
271 | | ||||
272 | CurrentItemHighLight { | 86 | CurrentItemHighLight { | ||
273 | visualParent: tasksRow | 87 | visualParent: tasksGrid | ||
274 | target: root.activeApplet && root.activeApplet.parent.parent == tasksRow ? root.activeApplet.parent : root | 88 | target: root.activeApplet && root.activeApplet.parent && root.activeApplet.parent.inVisibleLayout ? root.activeApplet.parent.parent : root | ||
275 | location: plasmoid.location | 89 | location: plasmoid.location | ||
276 | } | 90 | } | ||
277 | 91 | | |||
278 | DnD.DropArea { | 92 | DnD.DropArea { | ||
279 | anchors.fill: parent | 93 | anchors.fill: parent | ||
280 | 94 | | |||
281 | preventStealing: true; | 95 | preventStealing: true; | ||
282 | 96 | | |||
Show All 28 Lines | 124 | if (plasmoid.configuration.extraItems.indexOf(plasmoidId) < 0) { | |||
311 | var extraItems = plasmoid.configuration.extraItems; | 125 | var extraItems = plasmoid.configuration.extraItems; | ||
312 | extraItems.push(plasmoidId); | 126 | extraItems.push(plasmoidId); | ||
313 | plasmoid.configuration.extraItems = extraItems; | 127 | plasmoid.configuration.extraItems = extraItems; | ||
314 | } | 128 | } | ||
315 | } | 129 | } | ||
316 | } | 130 | } | ||
317 | 131 | | |||
318 | //Main Layout | 132 | //Main Layout | ||
319 | Flow { | 133 | GridLayout { | ||
320 | id: tasksRow | 134 | id: mainLayout | ||
321 | spacing: 0 | 135 | | ||
322 | height: parent.height - (vertical && expander.visible ? expander.height : 0) | 136 | rowSpacing: 0 | ||
323 | width: parent.width - (vertical || !expander.visible ? 0 : expander.width) | 137 | columnSpacing: 0 | ||
324 | property string skipItems | 138 | anchors.fill: parent | ||
325 | flow: vertical ? Flow.LeftToRight : Flow.TopToBottom | 139 | | ||
326 | //To make it look centered | 140 | flow: vertical ? GridLayout.TopToBottom : GridLayout.LeftToRight | ||
327 | y: Math.round(height/2 - childrenRect.height/2) | 141 | | ||
328 | x: (expander.visible && LayoutMirroring.enabled ? expander.width : 0) + Math.round(width/2 - childrenRect.width/2) | 142 | GridView { | ||
329 | 143 | id: tasksGrid | |||
330 | 144 | Layout.alignment: Qt.AlignCenter | |||
331 | //Do spacing with margins, to correctly compute the number of lines | 145 | | ||
332 | property QtObject marginHints: QtObject { | 146 | interactive: false //disable features we don't need | ||
333 | property int left: Math.round(units.smallSpacing / 2) | 147 | flow: vertical ? GridView.LeftToRight : GridView.TopToBottom | ||
334 | property int top: Math.round(units.smallSpacing / 2) | 148 | | ||
335 | property int right: Math.round(units.smallSpacing / 2) | 149 | cellHeight: root.itemSize + units.smallSpacing | ||
336 | property int bottom: Math.round(units.smallSpacing / 2) | 150 | cellWidth: root.itemSize + units.smallSpacing | ||
151 | | ||||
152 | readonly property int columns: !vertical ? Math.ceil(count / rows) | ||||
153 | : Math.max(1, Math.floor(root.width / cellWidth)) | ||||
154 | readonly property int rows: vertical ? Math.ceil(count / columns) | ||||
155 | : Math.max(1, Math.floor(root.height / cellHeight)) | ||||
156 | | ||||
157 | implicitHeight: rows * cellHeight | ||||
158 | implicitWidth: columns * cellWidth | ||||
159 | | ||||
160 | model: PlasmaCore.SortFilterModel { | ||||
161 | sourceModel: plasmoid.nativeInterface.systemTrayModel | ||||
162 | filterRole: "effectiveStatus" | ||||
163 | filterCallback: function(source_row, value) { | ||||
164 | return value === PlasmaCore.Types.ActiveStatus | ||||
165 | } | ||||
337 | } | 166 | } | ||
338 | 167 | | |||
339 | //add doesn't seem to work used in conjunction with stackBefore/stackAfter | 168 | delegate: ItemLoader {} | ||
340 | /*add: Transition { | 169 | | ||
170 | add: Transition { | ||||
171 | enabled: root.itemSize > 0 | ||||
172 | | ||||
341 | NumberAnimation { | 173 | NumberAnimation { | ||
342 | property: "scale" | 174 | property: "scale" | ||
343 | from: 0 | 175 | from: 0 | ||
344 | to: 1 | 176 | to: 1 | ||
345 | easing.type: Easing.InQuad | 177 | easing.type: Easing.InOutQuad | ||
346 | duration: units.longDuration | 178 | duration: units.longDuration | ||
347 | } | 179 | } | ||
348 | } | 180 | } | ||
181 | | ||||
182 | displaced: Transition { | ||||
183 | //ensure scale value returns to 1.0 | ||||
184 | //https://doc.qt.io/qt-5/qml-qtquick-viewtransition.html#handling-interrupted-animations | ||||
185 | NumberAnimation { | ||||
186 | property: "scale" | ||||
187 | to: 1 | ||||
188 | easing.type: Easing.InOutQuad | ||||
189 | duration: units.longDuration | ||||
190 | } | ||||
191 | } | ||||
192 | | ||||
349 | move: Transition { | 193 | move: Transition { | ||
350 | NumberAnimation { | 194 | NumberAnimation { | ||
351 | properties: "x,y" | 195 | properties: "x,y" | ||
352 | easing.type: Easing.InQuad | 196 | easing.type: Easing.InOutQuad | ||
353 | duration: units.longDuration | 197 | duration: units.longDuration | ||
354 | } | 198 | } | ||
355 | }*/ | 199 | } | ||
356 | } | 200 | } | ||
357 | 201 | | |||
358 | ExpanderArrow { | 202 | ExpanderArrow { | ||
359 | id: expander | 203 | id: expander | ||
360 | anchors { | 204 | Layout.fillWidth: vertical | ||
361 | fill: parent | 205 | Layout.fillHeight: !vertical | ||
362 | leftMargin: vertical ? 0 : parent.width - implicitWidth | | |||
363 | topMargin: vertical ? parent.height - implicitHeight : 0 | | |||
364 | } | 206 | } | ||
365 | } | 207 | } | ||
366 | 208 | | |||
367 | //Main popup | 209 | //Main popup | ||
368 | PlasmaCore.Dialog { | 210 | PlasmaCore.Dialog { | ||
369 | id: dialog | 211 | id: dialog | ||
370 | visualParent: root | 212 | visualParent: root | ||
371 | flags: Qt.WindowStaysOnTopHint | 213 | flags: Qt.WindowStaysOnTopHint | ||
Show All 28 Lines |