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