Changeset View
Standalone View
applets/systemtray/package/contents/ui/ConfigEntries.qml
1 | | ||||
---|---|---|---|---|---|
1 | /* | 2 | /* | ||
2 | * Copyright 2013 Sebastian Kügler <sebas@kde.org> | 3 | * Copyright 2013 Sebastian Kügler <sebas@kde.org> | ||
3 | * Copyright 2014 Marco Martin <mart@kde.org> | 4 | * Copyright 2014 Marco Martin <mart@kde.org> | ||
5 | * Copyright 2019 Konrad Materka <materka@gmail.com> | ||||
4 | * | 6 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | 8 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | 9 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | 10 | * (at your option) any later version. | ||
9 | * | 11 | * | ||
10 | * This program is distributed in the hope that it will be useful, | 12 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | 15 | * GNU General Public License for more details. | ||
14 | * | 16 | * | ||
15 | * You should have received a copy of the GNU General Public License | 17 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | 18 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. | ||
18 | */ | 20 | */ | ||
19 | | ||||
20 | import QtQuick 2.5 | 21 | import QtQuick 2.5 | ||
21 | import QtQuick.Controls 1.4 as QQC1 | | |||
22 | import QtQuick.Controls 2.5 as QQC2 | 22 | import QtQuick.Controls 2.5 as QQC2 | ||
23 | import QtQuick.Layouts 1.3 | 23 | import QtQuick.Layouts 1.3 | ||
24 | 24 | | |||
25 | import org.kde.plasma.core 2.0 as PlasmaCore | 25 | import org.kde.plasma.core 2.1 as PlasmaCore | ||
26 | import org.kde.plasma.components 3.0 as PlasmaComponents3 | ||||
26 | import org.kde.kquickcontrolsaddons 2.0 | 27 | import org.kde.kquickcontrolsaddons 2.0 | ||
27 | import org.kde.kquickcontrols 2.0 as KQC | 28 | import org.kde.kquickcontrols 2.0 as KQC | ||
28 | import org.kde.kirigami 2.5 as Kirigami | 29 | import org.kde.kirigami 2.10 as Kirigami | ||
29 | 30 | | |||
30 | ColumnLayout { | 31 | ColumnLayout { | ||
31 | id: iconsPage | 32 | id: iconsPage | ||
32 | 33 | | |||
33 | signal configurationChanged | 34 | signal configurationChanged | ||
34 | 35 | | |||
36 | property bool cfg_applicationStatusShown | ||||
37 | property bool cfg_communicationsShown | ||||
38 | property bool cfg_systemServicesShown | ||||
39 | property bool cfg_hardwareControlShown | ||||
40 | property bool cfg_miscellaneousShown | ||||
35 | property var cfg_shownItems: [] | 41 | property var cfg_shownItems: [] | ||
36 | property var cfg_hiddenItems: [] | 42 | property var cfg_hiddenItems: [] | ||
43 | property var cfg_extraItems: [] | ||||
37 | property alias cfg_showAllItems: showAllCheckBox.checked | 44 | property alias cfg_showAllItems: showAllCheckBox.checked | ||
38 | 45 | | |||
39 | PlasmaCore.SortFilterModel { | 46 | QQC2.ComboBox { | ||
40 | id: systemTrayModel | 47 | id: comboboxMeasure | ||
41 | sourceModel: plasmoid.nativeInterface.systemTrayModel | 48 | visible: false | ||
49 | implicitWidth: Math.round(units.gridUnit * 6.5) // ComboBox sizing is broken | ||||
42 | } | 50 | } | ||
43 | 51 | KQC.KeySequenceItem { | |||
44 | Kirigami.FormLayout { | 52 | id: keyMeasure | ||
53 | visible: false | ||||
54 | } | ||||
55 | Kirigami.Heading { | ||||
56 | id: keyHeadingMeasure | ||||
57 | text: i18n("Keyboard Shortcut") | ||||
58 | level: 2 | ||||
59 | visible: false | ||||
60 | } | ||||
61 | property int keyColumnWidth: Math.max(keyMeasure.width, keyHeadingMeasure.width) | ||||
45 | 62 | | |||
46 | QQC2.CheckBox { | 63 | QQC2.CheckBox { | ||
47 | id: showAllCheckBox | 64 | id: showAllCheckBox | ||
48 | text: i18n("Always show all entries") | 65 | text: i18n("Always show all entries") | ||
49 | } | 66 | } | ||
50 | 67 | | |||
51 | QQC2.Button { // just for measurement | 68 | function categoryName(category) { | ||
52 | id: measureButton | 69 | switch (category) { | ||
53 | text: "measureButton" | 70 | case "ApplicationStatus": | ||
54 | visible: false | 71 | return i18n("Application Status") | ||
72 | case "Communications": | ||||
73 | return i18n("Communications") | ||||
74 | case "SystemServices": | ||||
75 | return i18n("System Services") | ||||
76 | case "Hardware": | ||||
77 | return i18n("Hardware Control") | ||||
78 | case "UnknownCategory": | ||||
79 | default: | ||||
80 | return i18n("Miscellaneous") | ||||
81 | } | ||||
55 | } | 82 | } | ||
56 | 83 | | |||
57 | // resizeToContents does not take into account the heading | 84 | function categoryShown(category) { | ||
58 | QQC2.Label { | 85 | switch (category) { | ||
59 | id: shortcutColumnMeasureLabel | 86 | case "ApplicationStatus": | ||
60 | text: shortcutColumn.title | 87 | return cfg_applicationStatusShown | ||
61 | visible: false | 88 | case "Communications": | ||
89 | return cfg_communicationsShown | ||||
90 | case "SystemServices": | ||||
91 | return cfg_systemServicesShown | ||||
92 | case "Hardware": | ||||
93 | return cfg_hardwareControlShown | ||||
94 | case "UnknownCategory": | ||||
95 | return cfg_miscellaneousShown | ||||
96 | default: | ||||
97 | return false | ||||
62 | } | 98 | } | ||
63 | } | 99 | } | ||
64 | 100 | | |||
65 | // convert to QtObjects compatible with TableView | 101 | QQC2.ScrollView { | ||
66 | function retrieveAllItems() { | 102 | id: scrollView | ||
67 | var list = []; | 103 | | ||
68 | for (var i = 0; i < systemTrayModel.count; i++) { | | |||
69 | var item = systemTrayModel.get(i); | | |||
70 | if (item.itemType === "Plasmoid" && !item.hasApplet) { | | |||
71 | continue; | | |||
72 | } | | |||
73 | list.push({ | | |||
74 | "taskId": item.itemId, | | |||
75 | "name": item.display, | | |||
76 | "icon": item.decoration, | | |||
77 | "applet": item.applet | | |||
78 | }); | | |||
79 | } | | |||
80 | list.sort(function(a, b) { | | |||
81 | return a.name.localeCompare(b.name); | | |||
82 | }); | | |||
83 | return list; | | |||
84 | } | | |||
85 | | ||||
86 | // There is no QQC2 version of TableView yet | | |||
87 | QQC1.TableView { | | |||
88 | id: tableView | | |||
89 | Layout.fillWidth: true | 104 | Layout.fillWidth: true | ||
90 | Layout.fillHeight: true | 105 | Layout.fillHeight: true | ||
106 | contentHeight: itemsList.implicitHeight | ||||
91 | 107 | | |||
92 | model: retrieveAllItems() | 108 | Component.onCompleted: scrollView.background.visible = true | ||
93 | horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff | 109 | | ||
94 | flickableItem.boundsBehavior: Flickable.StopAtBounds | 110 | ListView { | ||
95 | 111 | id: itemsList | |||
96 | Component.onCompleted: { | 112 | Layout.fillWidth: true | ||
97 | visibilityColumn.resizeToContents() | | |||
98 | shortcutColumn.resizeToContents() | | |||
99 | } | | |||
100 | | ||||
101 | // Taken from QtQuickControls BasicTableViewStyle, just to make its height sensible... | | |||
102 | rowDelegate: BorderImage { | | |||
103 | visible: styleData.selected || styleData.alternate | | |||
104 | source: "image://__tablerow/" + (styleData.alternate ? "alternate_" : "") | | |||
105 | + (tableView.activeFocus ? "active" : "") | | |||
106 | height: measureButton.height | | |||
107 | border.left: 4 ; border.right: 4 | | |||
108 | } | | |||
109 | | ||||
110 | QQC1.TableViewColumn { | | |||
111 | id: entryColumn | | |||
112 | width: tableView.viewport.width - visibilityColumn.width - shortcutColumn.width | | |||
113 | title: i18nc("Name of the system tray entry", "Entry") | | |||
114 | movable: false | | |||
115 | resizable: false | | |||
116 | | ||||
117 | delegate: RowLayout { | | |||
118 | Item { // spacer | | |||
119 | Layout.preferredWidth: 1 | | |||
120 | Layout.fillHeight: true | 113 | Layout.fillHeight: true | ||
114 | QQC2.ScrollBar.vertical: scrollView.QQC2.ScrollBar.vertical // For Kirigami.AbstractListItem | ||||
mart: this should never be necessary and will probably cause bugs.
It looks like you're trying to… | |||||
Yes, I don't like this neither. It is a workaround for a problem in Kirigami.AbstractListItem. It automatically calculates paddings when scrollbars are detected: I use plain QQC2.ScrollView, which attaches scrollbars to itself, not ListView. As a result scrollbars are not detected and padding is not added. I don't know how to fix that correctly without duplicating a code in ConfigEntries.qml. I can override leftPadding and rightPadding, but I prefer to use original logic (in case it changes in the future). Can you advice? kmaterka: Yes, I don't like this neither. It is a workaround for a problem in Kirigami.AbstractListItem. | |||||
kmaterka: I replaced this hack with explicit paddings settings | |||||
115 | | ||||
116 | model: PlasmaCore.SortFilterModel { | ||||
117 | sourceModel: PlasmaCore.SortFilterModel { | ||||
118 | sourceModel: plasmoid.nativeInterface.systemTrayModel | ||||
119 | | ||||
120 | sortRole: "display" | ||||
121 | sortColumn: 0 | ||||
122 | isSortLocaleAware: true | ||||
123 | } | ||||
124 | | ||||
125 | sortRole: "category" | ||||
126 | sortColumn: 0 | ||||
127 | isSortLocaleAware: true | ||||
128 | } | ||||
129 | | ||||
130 | header: Kirigami.AbstractListItem { | ||||
131 | | ||||
132 | hoverEnabled: false | ||||
133 | | ||||
134 | RowLayout { | ||||
135 | Kirigami.Heading { | ||||
136 | text: i18nc("Name of the system tray entry", "Entry") | ||||
137 | level: 2 | ||||
138 | Layout.fillWidth: true | ||||
139 | } | ||||
140 | Kirigami.Heading { | ||||
141 | text: i18n("Visibility") | ||||
142 | level: 2 | ||||
143 | Layout.preferredWidth: comboboxMeasure.implicitWidth | ||||
144 | } | ||||
145 | Kirigami.Heading { | ||||
146 | text: i18n("Keyboard Shortcut") | ||||
147 | level: 2 | ||||
148 | Layout.preferredWidth: keyColumnWidth | ||||
149 | } | ||||
150 | } | ||||
121 | } | 151 | } | ||
122 | 152 | | |||
153 | section { | ||||
154 | property: "category" | ||||
155 | delegate: Kirigami.ListSectionHeader { | ||||
156 | label: categoryName(section) | ||||
157 | | ||||
158 | property bool isCategoryShown: categoryShown(section) | ||||
159 | | ||||
160 | PlasmaComponents3.ToolButton { | ||||
161 | Layout.alignment: LayoutMirroring.enabled ? Qt.AlignLeft : Qt.AlignRight | ||||
162 | | ||||
163 | text: isCategoryShown ? i18n("Shown") : i18n("Disabled") | ||||
164 | icon.name: isCategoryShown ? "view-visible" : "view-hidden" | ||||
165 | checkable: true | ||||
166 | checked: isCategoryShown | ||||
167 | onClicked: { | ||||
168 | switch (section) { | ||||
169 | case "ApplicationStatus": | ||||
170 | cfg_applicationStatusShown = checked | ||||
171 | break | ||||
172 | case "Communications": | ||||
173 | cfg_communicationsShown = checked | ||||
174 | break | ||||
175 | case "SystemServices": | ||||
176 | cfg_systemServicesShown = checked | ||||
177 | break | ||||
178 | case "Hardware": | ||||
179 | cfg_hardwareControlShown = checked | ||||
180 | break | ||||
181 | case "UnknownCategory": | ||||
182 | default: | ||||
183 | cfg_miscellaneousShown = checked | ||||
184 | break | ||||
185 | } | ||||
186 | } | ||||
187 | } | ||||
188 | } | ||||
189 | } | ||||
190 | | ||||
191 | delegate: Kirigami.AbstractListItem { | ||||
192 | | ||||
193 | highlighted: false | ||||
194 | hoverEnabled: false | ||||
195 | | ||||
196 | property bool isPlasmoid: itemType === "Plasmoid" | ||||
197 | property bool isCategoryShown: categoryShown(category) | ||||
198 | | ||||
199 | contentItem: RowLayout { | ||||
200 | RowLayout { | ||||
201 | Layout.fillWidth: true | ||||
202 | | ||||
123 | QIconItem { | 203 | QIconItem { | ||
124 | width: units.iconSizes.small | 204 | width: units.iconSizes.small | ||
125 | height: width | 205 | height: width | ||
126 | icon: modelData.icon | 206 | icon: model.decoration | ||
207 | enabled: isCategoryShown | ||||
127 | } | 208 | } | ||
128 | | ||||
129 | QQC2.Label { | 209 | QQC2.Label { | ||
130 | Layout.fillWidth: true | 210 | Layout.fillWidth: true | ||
131 | text: modelData.name | 211 | text: display | ||
132 | elide: Text.ElideRight | 212 | elide: Text.ElideRight | ||
133 | wrapMode: Text.NoWrap | 213 | wrapMode: Text.NoWrap | ||
134 | } | 214 | enabled: isCategoryShown | ||
135 | } | 215 | } | ||
136 | } | 216 | } | ||
137 | 217 | | |||
138 | QQC1.TableViewColumn { | 218 | QQC2.ComboBox { | ||
139 | id: visibilityColumn | 219 | id: visibilityComboBox | ||
140 | title: i18n("Visibility") | | |||
141 | movable: false | | |||
142 | resizable: false | | |||
143 | 220 | | |||
144 | delegate: QQC2.ComboBox { | 221 | implicitWidth: comboboxMeasure.implicitWidth | ||
145 | implicitWidth: Math.round(units.gridUnit * 6.5) // ComboBox sizing is broken | 222 | enabled: (!showAllCheckBox.checked || isPlasmoid) && itemId && isCategoryShown | ||
223 | | ||||
224 | textRole: "text" | ||||
225 | model: comboBoxModel(isCategoryShown) | ||||
146 | 226 | | |||
147 | enabled: !showAllCheckBox.checked | | |||
148 | currentIndex: { | 227 | currentIndex: { | ||
149 | if (cfg_shownItems.indexOf(modelData.taskId) != -1) { | 228 | var value | ||
150 | return 1; | 229 | | ||
151 | } else if (cfg_hiddenItems.indexOf(modelData.taskId) != -1) { | 230 | if (!isCategoryShown) { | ||
152 | return 2; | 231 | value = "disabled" | ||
232 | } else if (cfg_shownItems.indexOf(itemId) !== -1) { | ||||
233 | value = "shown" | ||||
234 | } else if (cfg_hiddenItems.indexOf(itemId) !== -1) { | ||||
235 | value = "hidden" | ||||
236 | } else if (isPlasmoid && cfg_extraItems.indexOf(itemId) === -1) { | ||||
237 | value = "disabled" | ||||
153 | } else { | 238 | } else { | ||
154 | return 0; | 239 | value = "auto" | ||
240 | } | ||||
241 | | ||||
242 | for (var i = 0; i < model.length; i++) { | ||||
243 | if (model[i].value === value) { | ||||
244 | return i | ||||
155 | } | 245 | } | ||
156 | } | 246 | } | ||
157 | 247 | | |||
158 | // activated, in contrast to currentIndexChanged, only fires if the user himself changed the value | 248 | return 0 | ||
249 | } | ||||
250 | | ||||
251 | property var currentValue: model[currentIndex].value | ||||
252 | | ||||
159 | onActivated: { | 253 | onActivated: { | ||
160 | var shownIndex = cfg_shownItems.indexOf(modelData.taskId); | 254 | var shownIndex = cfg_shownItems.indexOf(itemId) | ||
161 | var hiddenIndex = cfg_hiddenItems.indexOf(modelData.taskId); | 255 | var hiddenIndex = cfg_hiddenItems.indexOf(itemId) | ||
256 | var extraIndex = cfg_extraItems.indexOf(itemId) | ||||
162 | 257 | | |||
163 | switch (index) { | 258 | switch (currentValue) { | ||
164 | case 0: { | 259 | case "auto": | ||
165 | if (shownIndex > -1) { | 260 | if (shownIndex > -1) { | ||
166 | cfg_shownItems.splice(shownIndex, 1); | 261 | cfg_shownItems.splice(shownIndex, 1) | ||
167 | } | 262 | } | ||
168 | if (hiddenIndex > -1) { | 263 | if (hiddenIndex > -1) { | ||
169 | cfg_hiddenItems.splice(hiddenIndex, 1); | 264 | cfg_hiddenItems.splice(hiddenIndex, 1) | ||
170 | } | 265 | } | ||
171 | break; | 266 | if (extraIndex === -1) { | ||
267 | cfg_extraItems.push(itemId) | ||||
172 | } | 268 | } | ||
173 | case 1: { | 269 | break | ||
270 | case "shown": | ||||
174 | if (shownIndex === -1) { | 271 | if (shownIndex === -1) { | ||
175 | cfg_shownItems.push(modelData.taskId); | 272 | cfg_shownItems.push(itemId) | ||
176 | } | 273 | } | ||
177 | if (hiddenIndex > -1) { | 274 | if (hiddenIndex > -1) { | ||
178 | cfg_hiddenItems.splice(hiddenIndex, 1); | 275 | cfg_hiddenItems.splice(hiddenIndex, 1) | ||
179 | } | 276 | } | ||
180 | break; | 277 | if (extraIndex === -1) { | ||
278 | cfg_extraItems.push(itemId) | ||||
181 | } | 279 | } | ||
182 | case 2: { | 280 | break | ||
281 | case "hidden": | ||||
183 | if (shownIndex > -1) { | 282 | if (shownIndex > -1) { | ||
184 | cfg_shownItems.splice(shownIndex, 1); | 283 | cfg_shownItems.splice(shownIndex, 1) | ||
185 | } | 284 | } | ||
186 | if (hiddenIndex === -1) { | 285 | if (hiddenIndex === -1) { | ||
187 | cfg_hiddenItems.push(modelData.taskId); | 286 | cfg_hiddenItems.push(itemId) | ||
188 | } | | |||
189 | break; | | |||
190 | } | 287 | } | ||
288 | if (extraIndex === -1) { | ||||
289 | cfg_extraItems.push(itemId) | ||||
191 | } | 290 | } | ||
192 | iconsPage.configurationChanged(); | 291 | break | ||
292 | case "disabled": | ||||
293 | if (extraIndex > -1) { | ||||
294 | cfg_extraItems.splice(extraIndex, 1) | ||||
193 | } | 295 | } | ||
194 | model: [i18n("Auto"), i18n("Shown"), i18n("Hidden")] | 296 | break | ||
195 | } | 297 | } | ||
298 | iconsPage.configurationChanged() | ||||
196 | } | 299 | } | ||
197 | 300 | | |||
198 | QQC1.TableViewColumn { | 301 | function comboBoxModel(isCategoryShown) { | ||
199 | id: shortcutColumn | 302 | var autoElement = {value: "auto", text: i18n("Auto")} | ||
200 | title: i18n("Keyboard Shortcut") // FIXME doesn't fit | 303 | var shownElement = {value: "shown", text: i18n("Shown")} | ||
201 | movable: false | 304 | var hiddenElement = {value: "hidden", text: i18n("Hidden")} | ||
202 | resizable: false | 305 | var disabledElement = {value: "disabled", text: i18n("Disabled")} | ||
203 | 306 | | |||
204 | // this Item wrapper prevents TableView from ripping apart the two KeySequenceItem buttons | 307 | if (!isCategoryShown) { | ||
205 | delegate: Item { | 308 | return [disabledElement] | ||
206 | implicitWidth: Math.max(shortcutColumnMeasureLabel.width, keySequenceItem.width) + 10 | 309 | } | ||
207 | height: keySequenceItem.height | | |||
208 | 310 | | |||
311 | if (showAllCheckBox.checked) { | ||||
312 | if (isPlasmoid) { | ||||
313 | return [autoElement, disabledElement] | ||||
314 | } else { | ||||
315 | return [shownElement] | ||||
316 | } | ||||
317 | } else { | ||||
318 | if (isPlasmoid) { | ||||
319 | return [autoElement, shownElement, hiddenElement, disabledElement] | ||||
320 | } else { | ||||
321 | return [autoElement, shownElement, hiddenElement] | ||||
322 | } | ||||
323 | } | ||||
324 | } | ||||
325 | } | ||||
209 | KQC.KeySequenceItem { | 326 | KQC.KeySequenceItem { | ||
210 | id: keySequenceItem | 327 | id: keySequenceItem | ||
211 | anchors.right: parent.right | 328 | Layout.minimumWidth: keyColumnWidth | ||
329 | Layout.preferredWidth: keyColumnWidth | ||||
212 | 330 | | |||
213 | keySequence: modelData.applet ? modelData.applet.globalShortcut : "" | 331 | visible: isPlasmoid | ||
214 | // only Plasmoids have that | 332 | enabled: isCategoryShown && visibilityComboBox.currentValue !== "disabled" | ||
215 | visible: modelData.hasOwnProperty("applet") | 333 | keySequence: model.applet ? model.applet.globalShortcut : "" | ||
216 | onKeySequenceChanged: { | 334 | onKeySequenceChanged: { | ||
217 | if (modelData.applet && keySequence !== modelData.applet.globalShortcut) { | 335 | if (model.applet && keySequence !== model.applet.globalShortcut) { | ||
218 | modelData.applet.globalShortcut = keySequence | 336 | model.applet.globalShortcut = keySequence | ||
219 | 337 | | |||
220 | iconsPage.configurationChanged() | 338 | iconsPage.configurationChanged() | ||
221 | } | 339 | } | ||
222 | 340 | } | |||
223 | shortcutColumn.resizeToContents() | 341 | } | ||
342 | // Placeholder for when KeySequenceItem is not visible | ||||
343 | Item { | ||||
344 | Layout.minimumWidth: keyColumnWidth | ||||
345 | Layout.preferredWidth: keyColumnWidth | ||||
346 | visible: !keySequenceItem.visible | ||||
224 | } | 347 | } | ||
225 | } | 348 | } | ||
226 | } | 349 | } | ||
227 | } | 350 | } | ||
228 | } | 351 | } | ||
229 | } | 352 | } |
this should never be necessary and will probably cause bugs.
It looks like you're trying to workaround some problem? (which should be rather fixed)