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 | | ||||
151 | //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 | ||
152 | Item { | 66 | Item { | ||
153 | id: preloadedStorage | 67 | id: preloadedStorage | ||
154 | visible: false | 68 | visible: false | ||
155 | } | 69 | } | ||
156 | 70 | | |||
157 | Connections { | 71 | Connections { | ||
158 | target: plasmoid | 72 | target: plasmoid | ||
159 | onUserConfiguringChanged: { | 73 | onUserConfiguringChanged: { | ||
160 | if (plasmoid.userConfiguring) { | 74 | if (plasmoid.userConfiguring) { | ||
161 | dialog.visible = false | 75 | dialog.visible = false | ||
162 | } | 76 | } | ||
163 | } | 77 | } | ||
164 | } | 78 | } | ||
165 | 79 | | |||
166 | Connections { | 80 | Connections { | ||
167 | target: plasmoid.configuration | 81 | target: plasmoid.configuration | ||
168 | 82 | | |||
169 | onExtraItemsChanged: plasmoid.nativeInterface.allowedPlasmoids = plasmoid.configuration.extraItems | 83 | onExtraItemsChanged: plasmoid.nativeInterface.allowedPlasmoids = plasmoid.configuration.extraItems | ||
170 | } | 84 | } | ||
171 | 85 | | |||
172 | Component.onCompleted: { | | |||
173 | //script, don't bind | | |||
174 | plasmoid.nativeInterface.allowedPlasmoids = initializePlasmoidList(); | | |||
175 | } | | |||
176 | | ||||
177 | function initializePlasmoidList() { | | |||
178 | var newKnownItems = []; | | |||
179 | var newExtraItems = []; | | |||
180 | | ||||
181 | //NOTE:why this? otherwise the interpreter will execute plasmoid.nativeInterface.defaultPlasmoids() on | | |||
182 | //every access of defaults[], resulting in a very slow iteration | | |||
183 | var defaults = []; | | |||
184 | //defaults = defaults.concat(plasmoid.nativeInterface.defaultPlasmoids); | | |||
185 | defaults = plasmoid.nativeInterface.defaultPlasmoids.slice() | | |||
186 | var candidate; | | |||
187 | | ||||
188 | //Add every plasmoid that is both not enabled explicitly and not already known | | |||
189 | for (var i = 0; i < defaults.length; ++i) { | | |||
190 | candidate = defaults[i]; | | |||
191 | if (plasmoid.configuration.knownItems.indexOf(candidate) === -1) { | | |||
192 | newKnownItems.push(candidate); | | |||
193 | if (plasmoid.configuration.extraItems.indexOf(candidate) === -1) { | | |||
194 | newExtraItems.push(candidate); | | |||
195 | } | | |||
196 | } | | |||
197 | } | | |||
198 | | ||||
199 | if (newExtraItems.length > 0) { | | |||
200 | plasmoid.configuration.extraItems = plasmoid.configuration.extraItems.slice().concat(newExtraItems); | | |||
201 | } | | |||
202 | if (newKnownItems.length > 0) { | | |||
203 | plasmoid.configuration.knownItems = plasmoid.configuration.knownItems.slice().concat(newKnownItems); | | |||
204 | } | | |||
205 | | ||||
206 | return plasmoid.configuration.extraItems; | | |||
207 | } | | |||
208 | | ||||
209 | PlasmaCore.DataSource { | | |||
210 | id: statusNotifierSource | | |||
211 | engine: "statusnotifieritem" | | |||
212 | interval: 0 | | |||
213 | onSourceAdded: { | | |||
214 | connectSource(source) | | |||
215 | } | | |||
216 | Component.onCompleted: { | | |||
217 | connectedSources = sources | | |||
218 | } | | |||
219 | } | | |||
220 | | ||||
221 | PlasmaCore.SortFilterModel { | | |||
222 | id: statusNotifierModel | | |||
223 | sourceModel: PlasmaCore.DataModel { | | |||
224 | dataSource: statusNotifierSource | | |||
225 | } | | |||
226 | } | | |||
227 | | ||||
228 | //This is a dump for items we don't want to be seen or as an incubation, when they are | | |||
229 | //created as a nursery before going in their final place | | |||
230 | Item { | | |||
231 | id: invisibleEntriesContainer | | |||
232 | visible: false | | |||
233 | Repeater { | | |||
234 | id: tasksRepeater | | |||
235 | model: statusNotifierModel | | |||
236 | | ||||
237 | delegate: StatusNotifierItem {} | | |||
238 | } | | |||
239 | } | | |||
240 | | ||||
241 | CurrentItemHighLight { | 86 | CurrentItemHighLight { | ||
242 | visualParent: tasksRow | 87 | visualParent: tasksGrid | ||
243 | target: root.activeApplet && root.activeApplet.parent && root.activeApplet.parent.inVisibleLayout ? root.activeApplet.parent : root | 88 | target: root.activeApplet && root.activeApplet.parent && root.activeApplet.parent.inVisibleLayout ? root.activeApplet.parent.parent : root | ||
244 | location: plasmoid.location | 89 | location: plasmoid.location | ||
245 | } | 90 | } | ||
246 | 91 | | |||
247 | DnD.DropArea { | 92 | DnD.DropArea { | ||
248 | anchors.fill: parent | 93 | anchors.fill: parent | ||
249 | 94 | | |||
250 | preventStealing: true; | 95 | preventStealing: true; | ||
251 | 96 | | |||
Show All 28 Lines | 124 | if (plasmoid.configuration.extraItems.indexOf(plasmoidId) < 0) { | |||
280 | var extraItems = plasmoid.configuration.extraItems; | 125 | var extraItems = plasmoid.configuration.extraItems; | ||
281 | extraItems.push(plasmoidId); | 126 | extraItems.push(plasmoidId); | ||
282 | plasmoid.configuration.extraItems = extraItems; | 127 | plasmoid.configuration.extraItems = extraItems; | ||
283 | } | 128 | } | ||
284 | } | 129 | } | ||
285 | } | 130 | } | ||
286 | 131 | | |||
287 | //Main Layout | 132 | //Main Layout | ||
288 | Flow { | 133 | GridLayout { | ||
289 | id: tasksRow | 134 | id: mainLayout | ||
290 | spacing: 0 | 135 | | ||
291 | height: parent.height - (vertical && expander.visible ? expander.height : 0) | 136 | rowSpacing: 0 | ||
292 | width: parent.width - (vertical || !expander.visible ? 0 : expander.width) | 137 | columnSpacing: 0 | ||
293 | property string skipItems | 138 | anchors.fill: parent | ||
294 | flow: vertical ? Flow.LeftToRight : Flow.TopToBottom | 139 | | ||
295 | //To make it look centered | 140 | flow: vertical ? GridLayout.TopToBottom : GridLayout.LeftToRight | ||
296 | y: Math.round(height/2 - childrenRect.height/2) | 141 | | ||
297 | x: (expander.visible && LayoutMirroring.enabled ? expander.width : 0) + Math.round(width/2 - childrenRect.width/2) | 142 | GridView { | ||
143 | id: tasksGrid | ||||
144 | Layout.alignment: Qt.AlignCenter | ||||
298 | 145 | | |||
299 | readonly property var iconSize: root.itemSize + units.smallSpacing | 146 | interactive: false //disable features we don't need | ||
147 | flow: vertical ? GridView.LeftToRight : GridView.TopToBottom | ||||
148 | | ||||
149 | cellHeight: root.itemSize + units.smallSpacing | ||||
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 | } | ||||
166 | } | ||||
167 | | ||||
168 | delegate: ItemLoader {} | ||||
169 | | ||||
170 | add: Transition { | ||||
171 | enabled: root.itemSize > 0 | ||||
300 | 172 | | |||
301 | //add doesn't seem to work used in conjunction with stackBefore/stackAfter | | |||
302 | /*add: Transition { | | |||
303 | NumberAnimation { | 173 | NumberAnimation { | ||
304 | property: "scale" | 174 | property: "scale" | ||
305 | from: 0 | 175 | from: 0 | ||
306 | to: 1 | 176 | to: 1 | ||
307 | easing.type: Easing.InQuad | 177 | easing.type: Easing.InOutQuad | ||
308 | duration: units.longDuration | 178 | duration: units.longDuration | ||
309 | } | 179 | } | ||
310 | } | 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 | | ||||
311 | move: Transition { | 193 | move: Transition { | ||
312 | NumberAnimation { | 194 | NumberAnimation { | ||
313 | properties: "x,y" | 195 | properties: "x,y" | ||
314 | easing.type: Easing.InQuad | 196 | easing.type: Easing.InOutQuad | ||
315 | duration: units.longDuration | 197 | duration: units.longDuration | ||
316 | } | 198 | } | ||
317 | }*/ | 199 | } | ||
318 | } | 200 | } | ||
319 | 201 | | |||
320 | ExpanderArrow { | 202 | ExpanderArrow { | ||
321 | id: expander | 203 | id: expander | ||
322 | anchors { | 204 | Layout.fillWidth: vertical | ||
323 | fill: parent | 205 | Layout.fillHeight: !vertical | ||
324 | leftMargin: vertical ? 0 : parent.width - implicitWidth | | |||
325 | topMargin: vertical ? parent.height - implicitHeight : 0 | | |||
326 | } | 206 | } | ||
327 | } | 207 | } | ||
328 | 208 | | |||
329 | //Main popup | 209 | //Main popup | ||
330 | PlasmaCore.Dialog { | 210 | PlasmaCore.Dialog { | ||
331 | id: dialog | 211 | id: dialog | ||
332 | visualParent: root | 212 | visualParent: root | ||
333 | flags: Qt.WindowStaysOnTopHint | 213 | flags: Qt.WindowStaysOnTopHint | ||
Show All 28 Lines |