Changeset View
Changeset View
Standalone View
Standalone View
kcms/mouse/kcm/libinput/main.qml
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | * Copyright 2018 Roman Gilg <subdiff@gmail.com> | 2 | * Copyright 2018 Roman Gilg <subdiff@gmail.com> | ||
3 | * Copyright 2018 Furkan Tokac <furkantokac34@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 General Public License as published by | 6 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | 7 | * the Free Software Foundation; either version 2 of the License, 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 General Public License for more details. | 13 | * GNU General Public License for more details. | ||
13 | * | 14 | * | ||
14 | * You should have received a copy of the GNU General Public License | 15 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | 16 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | */ | 18 | */ | ||
18 | 19 | | |||
19 | import QtQuick 2.7 | 20 | import QtQuick 2.7 | ||
20 | import QtQuick.Controls 1.4 as Controls | 21 | import QtQuick.Controls 1.4 as Controls | ||
21 | import QtQuick.Layouts 1.3 as Layouts | 22 | import QtQuick.Layouts 1.3 as Layouts | ||
22 | import QtQuick.Controls.Styles 1.4 as Styles | 23 | import QtQuick.Controls.Styles 1.4 as Styles | ||
23 | 24 | | |||
25 | import org.kde.kcm 1.1 as KCM | ||||
26 | import org.kde.kirigami 2.4 as Kirigami | ||||
24 | import org.kde.plasma.core 2.0 as PlasmaCore | 27 | import org.kde.plasma.core 2.0 as PlasmaCore | ||
mart: never import anything from plasma in a kcm (an exception which we have to solve is… | |||||
25 | 28 | | |||
26 | import "components" | 29 | import "components" | ||
27 | 30 | | |||
28 | Item { | 31 | // TODO: Change ScrollablePage as KCM.SimpleKCM | ||
32 | // after rewrite the KCM in KConfigModule. | ||||
33 | Kirigami.ScrollablePage { | ||||
mart: KCM.SimpleKCM | |||||
This is not based on KConfigModule yet so it is not okay to convert it to SimpleKCM since it'll look for "kcm" reference from the backend but will not be able to find. It is added as TODO task to the code. After this rewrite, I am planning to work on the backend so that time it may be converted to furkantokac: This is not based on KConfigModule yet so it is not okay to convert it to SimpleKCM since it'll… | |||||
29 | id: root | 34 | id: root | ||
30 | 35 | | |||
31 | property size sizeHint: Qt.size(maincol.width, maincol.height) | | |||
32 | property size minimumSizeHint: Qt.size(maincol.width/2, deviceSelector.height) | | |||
Please re-add the minimumSizeHint line so that the window sizes itself properly when run with kcmshell5 mouse. ngraham: Please re-add the `minimumSizeHint` line so that the window sizes itself properly when run with… | |||||
33 | property alias deviceIndex: deviceSelector.currentIndex | 36 | property alias deviceIndex: deviceSelector.currentIndex | ||
34 | signal changeSignal() | 37 | signal changeSignal() | ||
35 | 38 | | |||
36 | property QtObject device | 39 | property QtObject device | ||
37 | property int deviceCount: backend.deviceCount | 40 | property int deviceCount: backend.deviceCount | ||
38 | 41 | | |||
39 | property bool loading: false | 42 | property bool loading: false | ||
40 | 43 | | |||
41 | function resetModel(index) { | 44 | function resetModel(index) { | ||
42 | deviceCount = backend.deviceCount | 45 | deviceCount = backend.deviceCount | ||
43 | maincol.enabled = deviceCount | 46 | formLayout.enabled = deviceCount | ||
44 | deviceSelector.enabled = deviceCount > 1 | 47 | deviceSelector.enabled = deviceCount > 1 | ||
45 | 48 | | |||
46 | loading = true | 49 | loading = true | ||
47 | if (deviceCount) { | 50 | if (deviceCount) { | ||
48 | device = deviceModel[index] | 51 | device = deviceModel[index] | ||
49 | deviceSelector.model = deviceModel | 52 | deviceSelector.model = deviceModel | ||
50 | deviceSelector.currentIndex = index | 53 | deviceSelector.currentIndex = index | ||
51 | console.log("Configuration of device '" + | 54 | console.log("Configuration of device '" + | ||
Show All 13 Lines | 63 | function syncValuesFromBackend() { | |||
65 | accelSpeed.load() | 68 | accelSpeed.load() | ||
66 | accelProfile.load() | 69 | accelProfile.load() | ||
67 | 70 | | |||
68 | naturalScroll.load() | 71 | naturalScroll.load() | ||
69 | 72 | | |||
70 | loading = false | 73 | loading = false | ||
71 | } | 74 | } | ||
72 | 75 | | |||
73 | Controls.ScrollView { | 76 | Kirigami.FormLayout { | ||
74 | anchors.fill: parent | 77 | id: formLayout | ||
75 | | ||||
76 | Layouts.ColumnLayout { | | |||
77 | id: maincol | | |||
78 | enabled: deviceCount | | |||
romangg: This needs to stay. Try it without mouse being connected. | |||||
79 | spacing: units.largeSpacing | | |||
80 | | ||||
81 | Layouts.RowLayout { | | |||
82 | spacing: units.largeSpacing | | |||
83 | | ||||
84 | Controls.Label { | | |||
85 | text: i18n("Device:") | | |||
86 | } | | |||
87 | 78 | | |||
79 | // Device | ||||
88 | Controls.ComboBox { | 80 | Controls.ComboBox { | ||
81 | Kirigami.FormData.label: i18n("Device:") | ||||
89 | id: deviceSelector | 82 | id: deviceSelector | ||
90 | enabled: deviceCount > 1 | 83 | enabled: deviceCount > 1 | ||
91 | Layouts.Layout.fillWidth: true | 84 | Layouts.Layout.fillWidth: true | ||
92 | model: deviceModel | 85 | model: deviceModel | ||
93 | textRole: "name" | 86 | textRole: "name" | ||
94 | 87 | | |||
95 | onCurrentIndexChanged: { | 88 | onCurrentIndexChanged: { | ||
96 | if (deviceCount) { | 89 | if (deviceCount) { | ||
97 | device = deviceModel[currentIndex] | 90 | device = deviceModel[currentIndex] | ||
98 | if (!loading) { | 91 | if (!loading) { | ||
99 | changeSignal() | 92 | changeSignal() | ||
100 | } | 93 | } | ||
101 | console.log("Configuration of device '" + | 94 | console.log("Configuration of device '" + | ||
102 | (currentIndex+1) + " : " + device.name + "' opened") | 95 | (currentIndex+1) + " : " + device.name + "' opened") | ||
103 | } | 96 | } | ||
104 | root.syncValuesFromBackend() | 97 | root.syncValuesFromBackend() | ||
105 | } | 98 | } | ||
106 | } | 99 | } | ||
107 | } | | |||
108 | | ||||
109 | Column { | | |||
110 | spacing: units.smallSpacing * 2 | | |||
111 | 100 | | |||
112 | Column { | 101 | Kirigami.Separator { | ||
113 | leftPadding: units.smallSpacing | | |||
114 | Column { | | |||
115 | spacing: units.smallSpacing | | |||
116 | Controls.Label { | | |||
117 | text: i18n("General settings:") | | |||
118 | } | 102 | } | ||
this is broken, it needs Kirigami.FormData.isSection: true mart: this is broken, it needs Kirigami.FormData.isSection: true
if you don't want it to be visible… | |||||
119 | 103 | | |||
120 | Column { | 104 | // General | ||
121 | leftPadding: units.smallSpacing | | |||
122 | Column { | | |||
123 | spacing: units.smallSpacing | | |||
124 | Controls.CheckBox { | 105 | Controls.CheckBox { | ||
106 | Kirigami.FormData.label: i18n("General:") | ||||
125 | id: deviceEnabled | 107 | id: deviceEnabled | ||
126 | text: i18n("Device enabled") | 108 | text: i18n("Device enabled") | ||
127 | 109 | | |||
128 | function load() { | 110 | function load() { | ||
129 | if (!maincol.enabled) { | 111 | if (!formLayout.enabled) { | ||
130 | checked = false | 112 | checked = false | ||
131 | return | 113 | return | ||
132 | } | 114 | } | ||
133 | enabled = device.supportsDisableEvents | 115 | enabled = device.supportsDisableEvents | ||
134 | checked = enabled && device.enabled | 116 | checked = enabled && device.enabled | ||
135 | } | 117 | } | ||
136 | 118 | | |||
137 | onCheckedChanged: { | 119 | onCheckedChanged: { | ||
138 | if (enabled && !root.loading) { | 120 | if (enabled && !root.loading) { | ||
139 | device.enabled = checked | 121 | device.enabled = checked | ||
140 | root.changeSignal() | 122 | root.changeSignal() | ||
141 | } | 123 | } | ||
142 | } | 124 | } | ||
143 | 125 | | |||
144 | ToolTip { | 126 | ToolTip { | ||
145 | text: i18n("Accept input through this device.") | 127 | text: i18n("Accept input through this device.") | ||
146 | } | 128 | } | ||
147 | } | 129 | } | ||
148 | 130 | | |||
149 | Controls.CheckBox { | 131 | Controls.CheckBox { | ||
150 | id: leftHanded | 132 | id: leftHanded | ||
151 | text: i18n("Left handed mode") | 133 | text: i18n("Left handed mode") | ||
152 | 134 | | |||
153 | function load() { | 135 | function load() { | ||
154 | if (!maincol.enabled) { | 136 | if (!formLayout.enabled) { | ||
155 | checked = false | 137 | checked = false | ||
156 | return | 138 | return | ||
157 | } | 139 | } | ||
158 | enabled = device.supportsLeftHanded | 140 | enabled = device.supportsLeftHanded | ||
159 | checked = enabled && device.leftHanded | 141 | checked = enabled && device.leftHanded | ||
160 | } | 142 | } | ||
161 | 143 | | |||
162 | onCheckedChanged: { | 144 | onCheckedChanged: { | ||
163 | if (enabled && !root.loading) { | 145 | if (enabled && !root.loading) { | ||
164 | device.leftHanded = checked | 146 | device.leftHanded = checked | ||
165 | root.changeSignal() | 147 | root.changeSignal() | ||
166 | } | 148 | } | ||
167 | } | 149 | } | ||
168 | 150 | | |||
169 | ToolTip { | 151 | ToolTip { | ||
170 | text: i18n("Swap left and right buttons.") | 152 | text: i18n("Swap left and right buttons.") | ||
171 | } | 153 | } | ||
172 | } | 154 | } | ||
173 | 155 | | |||
174 | Controls.CheckBox { | 156 | Controls.CheckBox { | ||
175 | id: middleEmulation | 157 | id: middleEmulation | ||
176 | text: i18n("Emulate middle button") | 158 | text: i18n("Press left and right buttons for middle-click") | ||
177 | 159 | | |||
178 | function load() { | 160 | function load() { | ||
179 | if (!maincol.enabled) { | 161 | if (!formLayout.enabled) { | ||
180 | checked = false | 162 | checked = false | ||
181 | return | 163 | return | ||
182 | } | 164 | } | ||
183 | enabled = device.supportsMiddleEmulation | 165 | enabled = device.supportsMiddleEmulation | ||
184 | checked = enabled && device.middleEmulation | 166 | checked = enabled && device.middleEmulation | ||
185 | } | 167 | } | ||
186 | 168 | | |||
187 | onCheckedChanged: { | 169 | onCheckedChanged: { | ||
188 | if (enabled && !root.loading) { | 170 | if (enabled && !root.loading) { | ||
189 | device.middleEmulation = checked | 171 | device.middleEmulation = checked | ||
190 | root.changeSignal() | 172 | root.changeSignal() | ||
191 | } | 173 | } | ||
192 | } | 174 | } | ||
193 | 175 | | |||
194 | ToolTip { | 176 | ToolTip { | ||
195 | text: i18n("Clicking left and right button simultaneously sends middle button click.") | 177 | text: i18n("Clicking left and right button simultaneously sends middle button click.") | ||
196 | } | 178 | } | ||
197 | } | 179 | } | ||
198 | } | | |||
199 | } | | |||
200 | } | | |||
201 | } | | |||
202 | 180 | | |||
203 | Column { | 181 | Kirigami.Separator { | ||
204 | leftPadding: units.smallSpacing | | |||
205 | Column { | | |||
206 | spacing: units.smallSpacing | | |||
207 | Controls.Label { | | |||
208 | text: i18n("Acceleration:") | | |||
209 | } | 182 | } | ||
210 | 183 | | |||
211 | Column { | 184 | // Acceleration | ||
212 | leftPadding: units.smallSpacing | | |||
213 | Column { | | |||
214 | spacing: units.smallSpacing * 2 | | |||
215 | | ||||
216 | Row { | | |||
217 | Controls.Slider { | 185 | Controls.Slider { | ||
186 | Kirigami.FormData.label: i18n("Pointer speed:") | ||||
218 | id: accelSpeed | 187 | id: accelSpeed | ||
219 | anchors.verticalCenter: parent.verticalCenter | | |||
220 | 188 | | |||
221 | tickmarksEnabled: true | 189 | tickmarksEnabled: true | ||
222 | 190 | | |||
223 | minimumValue: 1 | 191 | minimumValue: 1 | ||
224 | maximumValue: 10 | 192 | maximumValue: 10 | ||
225 | stepSize: 1 | 193 | stepSize: 1 | ||
226 | 194 | | |||
227 | implicitWidth: units.gridUnit * 9 | 195 | implicitWidth: units.gridUnit * 9 | ||
mart: Kirigami.Units.gridUnit
(tough controls shouldn't have explicit widths set) | |||||
228 | 196 | | |||
229 | function load() { | 197 | function load() { | ||
230 | enabled = device.supportsPointerAcceleration | 198 | enabled = device.supportsPointerAcceleration | ||
231 | if (!enabled) { | 199 | if (!enabled) { | ||
232 | value = 0.1 | 200 | value = 0.1 | ||
233 | return | 201 | return | ||
234 | } | 202 | } | ||
235 | // transform libinput's pointer acceleration range [-1, 1] to slider range [1, 10] | 203 | // transform libinput's pointer acceleration range [-1, 1] to slider range [1, 10] | ||
236 | value = 4.5 * device.pointerAcceleration + 5.5 | 204 | value = 4.5 * device.pointerAcceleration + 5.5 | ||
237 | } | 205 | } | ||
238 | 206 | | |||
239 | onValueChanged: { | 207 | onValueChanged: { | ||
240 | if (device != undefined && enabled && !root.loading) { | 208 | if (device != undefined && enabled && !root.loading) { | ||
241 | // transform slider range [1, 10] to libinput's pointer acceleration range [-1, 1] | 209 | // transform slider range [1, 10] to libinput's pointer acceleration range [-1, 1] | ||
242 | device.pointerAcceleration = Math.round( (value - 5.5) / 4.5 * 100 ) / 100 | 210 | device.pointerAcceleration = Math.round( (value - 5.5) / 4.5 * 100 ) / 100 | ||
243 | root.changeSignal() | 211 | root.changeSignal() | ||
244 | } | 212 | } | ||
245 | } | 213 | } | ||
246 | } | 214 | } | ||
247 | } | | |||
248 | 215 | | |||
249 | ExclGroupBox { | 216 | Controls.ExclusiveGroup { | ||
mart: radiobuttons are auto exclusive now | |||||
250 | id: accelProfile | 217 | id: accelProfile | ||
251 | label: i18n("Acceleration Profile:") | | |||
252 | model: [i18n("Flat"), i18n("Adaptive")] | | |||
253 | 218 | | |||
254 | function load() { | 219 | function load() { | ||
255 | enabled = device.supportsPointerAccelerationProfileAdaptive | 220 | enabled = device.supportsPointerAccelerationProfileAdaptive | ||
256 | 221 | | |||
257 | if (!enabled) { | 222 | if (!enabled) { | ||
258 | itemAt(0).checked = false | 223 | accelProfileAdaptive.checked = false | ||
259 | itemAt(1).checked = false | 224 | accelProfileFlat.checked = false | ||
260 | return | 225 | return | ||
261 | } | 226 | } | ||
262 | 227 | | |||
263 | itemAt(0).tooltiptext = i18n("Cursor moves the same distance as finger.") | 228 | if(device.pointerAccelerationProfileAdaptive) { | ||
264 | itemAt(1).tooltiptext = i18n("Cursor travel distance depends on movement speed of finger.") | 229 | accelProfileAdaptive.checked = true | ||
265 | 230 | accelProfileFlat.checked = false | |||
266 | var toCheck = device.pointerAccelerationProfileAdaptive ? 1 : 0 | 231 | } else { | ||
267 | itemAt(toCheck).checked = true | 232 | accelProfileAdaptive.checked = false | ||
233 | accelProfileFlat.checked = true | ||||
234 | } | ||||
268 | } | 235 | } | ||
269 | 236 | | |||
270 | onCurrentChanged: { | 237 | onCurrentChanged: { | ||
271 | if (enabled && !root.loading) { | 238 | if (enabled && !root.loading) { | ||
272 | device.pointerAccelerationProfileFlat = itemAt(0).checked | 239 | device.pointerAccelerationProfileFlat = accelProfileFlat.checked | ||
273 | device.pointerAccelerationProfileAdaptive = itemAt(1).checked | 240 | device.pointerAccelerationProfileAdaptive = accelProfileAdaptive.checked | ||
274 | root.changeSignal() | 241 | root.changeSignal() | ||
275 | } | 242 | } | ||
276 | } | 243 | } | ||
277 | } | 244 | } | ||
245 | | ||||
246 | Controls.RadioButton { | ||||
247 | id: accelProfileFlat | ||||
248 | Kirigami.FormData.label: i18n("Acceleration profile:") | ||||
249 | text: i18n("Flat") | ||||
250 | exclusiveGroup: accelProfile | ||||
251 | | ||||
252 | ToolTip { | ||||
253 | text: i18n("Cursor moves the same distance as the mouse movement.") | ||||
278 | } | 254 | } | ||
279 | } | 255 | } | ||
256 | | ||||
257 | Controls.RadioButton { | ||||
258 | id: accelProfileAdaptive | ||||
259 | text: i18n("Adaptive") | ||||
260 | exclusiveGroup: accelProfile | ||||
261 | | ||||
262 | ToolTip { | ||||
263 | text: i18n("Cursor travel distance depends on the mouse movement speed.") | ||||
280 | } | 264 | } | ||
281 | } | 265 | } | ||
282 | 266 | | |||
283 | Column { | 267 | Kirigami.Separator { | ||
284 | leftPadding: units.smallSpacing | | |||
285 | Column { | | |||
286 | spacing: units.smallSpacing | | |||
287 | Controls.Label { | | |||
288 | text: i18n("Scrolling:") | | |||
289 | } | 268 | } | ||
290 | 269 | | |||
291 | Column { | 270 | // Scrolling | ||
292 | leftPadding: units.smallSpacing | | |||
293 | Column { | | |||
294 | spacing: units.smallSpacing | | |||
295 | | ||||
296 | Controls.CheckBox { | 271 | Controls.CheckBox { | ||
272 | Kirigami.FormData.label: i18n("Scrolling:") | ||||
297 | id: naturalScroll | 273 | id: naturalScroll | ||
298 | text: i18n("Invert scroll direction") | 274 | text: i18n("Invert scroll direction") | ||
299 | 275 | | |||
300 | function load() { | 276 | function load() { | ||
301 | enabled = device.supportsNaturalScroll | 277 | enabled = device.supportsNaturalScroll | ||
302 | checked = enabled && device.naturalScroll | 278 | checked = enabled && device.naturalScroll | ||
303 | } | 279 | } | ||
304 | 280 | | |||
305 | onCheckedChanged: { | 281 | onCheckedChanged: { | ||
306 | if (enabled && !root.loading) { | 282 | if (enabled && !root.loading) { | ||
307 | device.naturalScroll = checked | 283 | device.naturalScroll = checked | ||
308 | root.changeSignal() | 284 | root.changeSignal() | ||
309 | } | 285 | } | ||
310 | } | 286 | } | ||
311 | 287 | | |||
312 | ToolTip { | 288 | ToolTip { | ||
313 | text: i18n("Touchscreen like scrolling.") | 289 | text: i18n("Touchscreen like scrolling.") | ||
314 | } | 290 | } | ||
315 | } | 291 | } | ||
316 | } | 292 | } | ||
317 | } | 293 | } | ||
This label doesn't show up. If we put it to Column, it shows up vertically centered. Please recommend a solution for this. furkantokac: This label doesn't show up. If we put it to Column, it shows up vertically centered. Please… | |||||
318 | } | | |||
319 | } | | |||
320 | } | | |||
321 | } | | |||
322 | } | | |||
323 | } | |
never import anything from plasma in a kcm (an exception which we have to solve is SortFilterProxyModel)
Kirigami has its own units