Changeset View
Changeset View
Standalone View
Standalone View
kcms/keyboard/kcmui/package/contents/ui/Layouts.qml
- This file was added.
1 | import QtQuick 2.9 | ||||
---|---|---|---|---|---|
2 | import QtQuick.Layouts 1.3 | ||||
3 | import QtQuick.Controls 2.3 as Controls | ||||
4 | import org.kde.kirigami 2.5 as Kirigami | ||||
5 | import org.kde.plasma.core 2.1 as PlasmaCore | ||||
6 | import org.kde.kcm 1.2 as KCM | ||||
7 | import org.kde.kquickcontrols 2.0 as KQuickControls | ||||
8 | | ||||
9 | KCM.ScrollViewKCM { | ||||
10 | id: root | ||||
11 | | ||||
12 | property var dataModel; | ||||
13 | signal changed(); | ||||
14 | | ||||
15 | Controls.Dialog { | ||||
16 | id: addLayoutDialog | ||||
17 | standardButtons: Controls.Dialog.Ok | Controls.Dialog.Discard | ||||
18 | title: i18n("Select Layout") | ||||
19 | | ||||
20 | x: Math.floor((parent.width - width) / 2) | ||||
21 | y: Math.floor((parent.height - height) / 2) | ||||
22 | width: 500 | ||||
23 | height: Math.floor(Math.min(parent.height, 500)); | ||||
24 | | ||||
25 | property var selected: [] | ||||
26 | | ||||
27 | ColumnLayout { | ||||
28 | anchors.fill: parent | ||||
29 | | ||||
30 | Component { | ||||
31 | id: sectionHeading | ||||
32 | Rectangle { | ||||
33 | width: parent.width | ||||
34 | height: childrenRect.height | ||||
35 | color: "transparent" | ||||
36 | | ||||
37 | Text { | ||||
38 | text: section | ||||
39 | font.bold: true | ||||
40 | font.pixelSize: 20 | ||||
41 | } | ||||
42 | } | ||||
43 | } | ||||
44 | | ||||
45 | ListView { | ||||
46 | id: layoutSelectList | ||||
47 | implicitWidth: parent.width | ||||
48 | Layout.fillHeight: true | ||||
49 | | ||||
50 | Controls.ScrollBar.vertical: Controls.ScrollBar {} | ||||
51 | | ||||
52 | model: PlasmaCore.SortFilterModel { | ||||
53 | id: nameFilterModel | ||||
54 | sourceModel: PlasmaCore.SortFilterModel { | ||||
55 | id: enabledFilterModel | ||||
56 | sourceModel: dataModel.layoutListModel | ||||
57 | | ||||
58 | filterRole: "enabled" | ||||
59 | filterCallback: function(source_row, value) { return !value; } | ||||
60 | | ||||
61 | sortRole: "languages" | ||||
62 | } | ||||
63 | | ||||
64 | filterRole: "description" | ||||
65 | filterCaseSensitivity: Qt.CaseInsensitive | ||||
66 | filterString: filterText.text | ||||
67 | } | ||||
68 | | ||||
69 | clip: true | ||||
70 | | ||||
71 | section.property: "languages" | ||||
72 | section.delegate: sectionHeading | ||||
73 | | ||||
74 | delegate: Kirigami.BasicListItem { | ||||
75 | label: model.description | ||||
76 | checkable: true | ||||
77 | icon: "input-keyboard" | ||||
78 | | ||||
79 | function origIdx() { | ||||
80 | return enabledFilterModel.mapRowToSource( | ||||
81 | nameFilterModel.mapRowToSource(model.index)); | ||||
82 | } | ||||
83 | | ||||
84 | checked: addLayoutDialog.selected[origIdx()] === true | ||||
85 | onCheckedChanged: addLayoutDialog.selected[origIdx()] = checked; | ||||
86 | } | ||||
87 | } | ||||
88 | | ||||
89 | Controls.TextArea { | ||||
90 | id: filterText | ||||
91 | implicitWidth: parent.width | ||||
92 | } | ||||
93 | } | ||||
94 | | ||||
95 | onOpened: filterText.forceActiveFocus() | ||||
96 | onOpenedChanged: filterText.text = "" | ||||
97 | | ||||
98 | onAccepted: { | ||||
99 | selected.forEach(function(checked, index) { | ||||
100 | if (checked) { | ||||
101 | dataModel.layoutListModel.add(index); | ||||
102 | changed(); | ||||
103 | } | ||||
104 | }); | ||||
105 | close(); | ||||
106 | } | ||||
107 | onDiscarded: close(); | ||||
108 | onClosed: { | ||||
109 | selected = [] | ||||
110 | layoutSelectList.positionViewAtBeginning(); | ||||
111 | } | ||||
112 | } | ||||
113 | | ||||
114 | Controls.Dialog { | ||||
115 | id: previewDialog | ||||
116 | title: "Preview" | ||||
117 | standardButtons: Controls.Dialog.Ok | ||||
118 | | ||||
119 | x: Math.floor((parent.width - width) / 2) | ||||
120 | y: Math.floor((parent.height - height) / 2) | ||||
121 | width: 500 | ||||
122 | height: Math.floor(Math.min(parent.height, 500)); | ||||
123 | | ||||
124 | LayoutPreview { | ||||
125 | id: preview | ||||
126 | } | ||||
127 | } | ||||
128 | | ||||
129 | header: Kirigami.FormLayout { | ||||
130 | id: formLayout | ||||
131 | | ||||
132 | Item { | ||||
133 | Kirigami.FormData.isSection: true | ||||
134 | Kirigami.FormData.label: i18n("Layout Indicator Icon") | ||||
135 | } | ||||
136 | | ||||
137 | Controls.CheckBox { | ||||
138 | id: showLayoutIndicatorIconCheckbox | ||||
139 | Kirigami.FormData.label: i18n("Show layout indicator icon") | ||||
140 | | ||||
141 | Binding on checked { | ||||
142 | value: dataModel.showLayoutIndicator | ||||
143 | } | ||||
144 | | ||||
145 | onCheckedChanged: { | ||||
146 | dataModel.showLayoutIndicator = checked; | ||||
147 | root.changed(); | ||||
148 | } | ||||
149 | } | ||||
150 | | ||||
151 | Controls.CheckBox { | ||||
152 | Kirigami.FormData.label: i18n("Show for single layout") | ||||
153 | | ||||
154 | enabled: showLayoutIndicatorIconCheckbox.checked | ||||
155 | | ||||
156 | Binding on checked { | ||||
157 | value: dataModel.showForSingleLayout | ||||
158 | } | ||||
159 | | ||||
160 | onCheckedChanged: { | ||||
161 | dataModel.showForSingleLayout = checked; | ||||
162 | root.changed(); | ||||
163 | } | ||||
164 | } | ||||
165 | | ||||
166 | Item { | ||||
167 | Kirigami.FormData.isSection: true | ||||
168 | Kirigami.FormData.label: i18n("Shortcuts for Switching Layout") | ||||
169 | } | ||||
170 | | ||||
171 | Controls.ComboBox { | ||||
172 | Kirigami.FormData.label: i18n("Main shift key:") | ||||
173 | model: dataModel.mainShiftKeyModel | ||||
174 | | ||||
175 | Binding on currentIndex { | ||||
176 | value: dataModel.mainShiftKeyIndex | ||||
177 | } | ||||
178 | | ||||
179 | onActivated: { | ||||
180 | dataModel.mainShiftKeyIndex = currentIndex; | ||||
181 | root.changed(); | ||||
182 | } | ||||
183 | } | ||||
184 | | ||||
185 | Controls.ComboBox { | ||||
186 | Kirigami.FormData.label: i18n("3rd level shift key:") | ||||
187 | model: dataModel.thirdLevelShortcutModel | ||||
188 | | ||||
189 | Binding on currentIndex { | ||||
190 | value: dataModel.thirdLevelShortcutIndex | ||||
191 | } | ||||
192 | | ||||
193 | onActivated: { | ||||
194 | dataModel.thirdLevelShortcutIndex = currentIndex; | ||||
195 | root.changed(); | ||||
196 | } | ||||
197 | } | ||||
198 | | ||||
199 | KQuickControls.KeySequenceItem { | ||||
200 | Kirigami.FormData.label: i18n("Cycle through layouts:") | ||||
201 | width: 200 | ||||
202 | | ||||
203 | Binding on keySequence { | ||||
204 | value: dataModel.alternativeShortcut | ||||
205 | } | ||||
206 | | ||||
207 | onKeySequenceChanged: { | ||||
208 | dataModel.alternativeShortcut = keySequence; | ||||
209 | root.changed(); | ||||
210 | } | ||||
211 | } | ||||
212 | | ||||
213 | Item { | ||||
214 | Kirigami.FormData.isSection: true | ||||
215 | Kirigami.FormData.label: i18n("Layouts") | ||||
216 | } | ||||
217 | | ||||
218 | Kirigami.InlineMessage { | ||||
219 | Kirigami.FormData.isSection: true | ||||
220 | id: inlineMessage | ||||
221 | | ||||
222 | PlasmaCore.SortFilterModel { | ||||
223 | id: missingLayouts | ||||
224 | sourceModel: dataModel.layoutListModel | ||||
225 | | ||||
226 | filterRole: "source" | ||||
227 | filterCallback: function(source_row, value) { return value == 0; } | ||||
228 | | ||||
229 | function join(str) { | ||||
230 | var l = [] | ||||
231 | for (var i = 0; i < missingLayouts.count; ++i) { | ||||
232 | l.push(missingLayouts.get(i).save_name); | ||||
233 | } | ||||
234 | return l.join(str); | ||||
235 | } | ||||
236 | } | ||||
237 | | ||||
238 | Layout.fillWidth: true | ||||
239 | | ||||
240 | type: Kirigami.MessageType.Error | ||||
241 | | ||||
242 | Connections { | ||||
243 | target: dataModel.layoutListModel | ||||
244 | onMissingCountChanged: { | ||||
245 | inlineMessage.text = | ||||
246 | i18ncp("@info %2 is the layout code", | ||||
247 | "The input support for the keyboard layout with the code '%2' could not be found. If it is not needed, please remove it from your configuration.", | ||||
248 | "The input support for the keyboard layouts with the codes '%2' could not be found. If they are not needed, please remove them from your configuration.", | ||||
249 | missingLayouts.count, | ||||
250 | missingLayouts.join("', '")) | ||||
251 | inlineMessage.visible = missingLayouts.count > 0 | ||||
252 | } | ||||
253 | } | ||||
254 | } | ||||
255 | } | ||||
256 | | ||||
257 | Component { | ||||
258 | id: listviewDelegateComponent | ||||
259 | Kirigami.SwipeListItem { | ||||
260 | checkable: true | ||||
261 | id: listItem | ||||
262 | | ||||
263 | contentItem: RowLayout { | ||||
264 | Kirigami.ListItemDragHandle { | ||||
265 | listItem: listItem | ||||
266 | listView: layoutList | ||||
267 | onMoveRequested: { | ||||
268 | dataModel.currentLayoutListModel.simulateMove(oldIndex, newIndex); | ||||
269 | } | ||||
270 | onDropped: { | ||||
271 | dataModel.currentLayoutListModel.applyOrderChanges(); | ||||
272 | changed(); | ||||
273 | } | ||||
274 | } | ||||
275 | | ||||
276 | Kirigami.Icon { | ||||
277 | visible: model.source == 0 | ||||
278 | | ||||
279 | Layout.alignment: Qt.AlignVCenter | ||||
280 | | ||||
281 | width: Kirigami.Units.iconSizes.smallMedium | ||||
282 | height: width | ||||
283 | | ||||
284 | source: "error" | ||||
285 | color: Kirigami.Theme.negativeTextColor | ||||
286 | } | ||||
287 | | ||||
288 | Controls.Label { | ||||
289 | Layout.fillWidth: true | ||||
290 | text: model.description | ||||
291 | } | ||||
292 | } | ||||
293 | | ||||
294 | onClicked: { | ||||
295 | if (checked) { | ||||
296 | if (layoutList.checkedItem.length === 1) { | ||||
297 | layoutList.checkedItem[0].item.checked = false; | ||||
298 | } | ||||
299 | layoutList.checkedItem[0] = {index: model.index, model: model, item: this}; | ||||
300 | configureLayoutButton.enabled = model.is_configurable; | ||||
301 | } | ||||
302 | else { | ||||
303 | layoutList.checkedItem = [] | ||||
304 | configureLayoutButton.enabled = false; | ||||
305 | } | ||||
306 | } | ||||
307 | | ||||
308 | | ||||
309 | actions: [ | ||||
310 | Kirigami.Action { | ||||
311 | iconName: "list-remove" | ||||
312 | tooltip: i18nc("@info:tooltip", "Remove") | ||||
313 | onTriggered: { | ||||
314 | dataModel.currentLayoutListModel.remove(index) | ||||
315 | changed(); | ||||
316 | } | ||||
317 | }] | ||||
318 | } | ||||
319 | } | ||||
320 | | ||||
321 | | ||||
322 | view: ListView { | ||||
323 | id: layoutList | ||||
324 | | ||||
325 | property var checkedItem; | ||||
326 | | ||||
327 | Component.onCompleted: checkedItem = []; | ||||
328 | | ||||
329 | Rectangle { | ||||
330 | color: "white" | ||||
331 | anchors.fill: parent | ||||
332 | z: -10 | ||||
333 | } | ||||
334 | | ||||
335 | implicitWidth: formLayout.width - buttonColumn.width - parent.spacing | ||||
336 | implicitHeight: 300 | ||||
337 | | ||||
338 | model: dataModel.currentLayoutListModel | ||||
339 | | ||||
340 | clip: true | ||||
341 | | ||||
342 | Controls.ScrollBar.vertical: Controls.ScrollBar { | ||||
343 | active: true | ||||
344 | } | ||||
345 | | ||||
346 | delegate: Kirigami.DelegateRecycler { | ||||
347 | width: layoutList.width | ||||
348 | sourceComponent: listviewDelegateComponent | ||||
349 | } | ||||
350 | } | ||||
351 | | ||||
352 | footer: Row { | ||||
353 | id: buttonColumn | ||||
354 | | ||||
355 | Controls.Button { | ||||
356 | text: i18n("Add...") | ||||
357 | icon.name: "list-add" | ||||
358 | | ||||
359 | onClicked: addLayoutDialog.open(); | ||||
360 | } | ||||
361 | | ||||
362 | /* TODO | ||||
363 | Controls.Button { | ||||
364 | text: i18n("Preview...") | ||||
365 | onClicked: previewDialog.open(); | ||||
366 | } | ||||
367 | */ | ||||
368 | | ||||
369 | Controls.Button { | ||||
370 | id: configureLayoutButton | ||||
371 | text: i18n("Configure..."); | ||||
372 | enabled: false; | ||||
373 | icon.name: "configure" | ||||
374 | | ||||
375 | onClicked: { | ||||
376 | var item = layoutList.checkedItem[0].model; | ||||
377 | var component; | ||||
378 | var imConfigDialog; | ||||
379 | if (item.source === 1) { // xkb | ||||
380 | component = Qt.createComponent("XkbLayoutConfig.qml"); | ||||
381 | } | ||||
382 | else if (item.source === 2) { // fcitx | ||||
383 | component = Qt.createComponent("FcitxIMConfig.qml"); | ||||
384 | } | ||||
385 | else { | ||||
386 | return; | ||||
387 | } | ||||
388 | | ||||
389 | if (component.status === Component.Ready) { | ||||
390 | imConfigDialog = component.createObject(root.parent, {width:700, height:500}) | ||||
391 | imConfigDialog.openForModel(item.config_model); | ||||
392 | } | ||||
393 | else { | ||||
394 | console.log(component.errorString()) | ||||
395 | } | ||||
396 | | ||||
397 | } | ||||
398 | } | ||||
399 | } | ||||
400 | } |