1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT
3
4import { Button, Palette } from "std-widgets.slint";
5
6import { Icons } from "icons.slint";
7
8component VirtualKeyboardButton {
9 in property <string> key;
10 in property <image> icon;
11
12 callback key-pressed(/* key */ string);
13
14 min-width: 32px;
15 min-height: 32px;
16 horizontal-stretch: 0;
17
18 states [
19 pressed when i-touch-area.pressed : {
20 i-state-area.opacity: 0.5;
21 }
22 ]
23
24 i-container := Rectangle {
25 border-radius: 4px;
26 background: Palette.color-scheme == ColorScheme.dark ? #373737 : #ffffff;
27
28 HorizontalLayout {
29 padding: 8px;
30
31 if (root.key != "") : Text {
32 text: root.key;
33 color: Palette.color-scheme == ColorScheme.dark ? #ffffff : #000000;
34 font-size: 12px;
35 vertical-alignment: center;
36 horizontal-alignment: center;
37 }
38
39 if (root.key == "") : Image {
40 y: (parent.height - self.height) / 2;
41 source: root.icon;
42 height: 18px;
43 colorize: Palette.color-scheme == ColorScheme.dark ? #ffffff : #000000;
44 }
45 }
46 }
47
48 i-state-area := Rectangle {
49 border-radius: i-container.border-radius;
50 opacity: 0;
51 background: #000000;
52
53 animate opacity { duration: 150ms; }
54 }
55
56 i-touch-area := TouchArea {
57 pointer-event(event) => {
58 if(event.kind == PointerEventKind.down) {
59 root.key-pressed(key);
60 }
61 }
62 }
63}
64
65export struct KeyModel {
66 key: string,
67 shift-key: string,
68}
69
70export global VirtualKeyboardHandler {
71 in property <bool> enabled;
72
73 in property <[[[KeyModel]]]> default-key-sets: [
74 [
75 [
76 { key: "q", shift-key: "Q" },
77 { key: "w", shift-key: "W" },
78 { key: "e", shift-key: "E" },
79 { key: "r", shift-key: "R" },
80 { key: "t", shift-key: "T" },
81 { key: "y", shift-key: "Y" },
82 { key: "u", shift-key: "U" },
83 { key: "i", shift-key: "I" },
84 { key: "o", shift-key: "O" },
85 { key: "p", shift-key: "P" }
86 ],
87 [
88 { key: "a", shift-key: "A" },
89 { key: "s", shift-key: "S" },
90 { key: "d", shift-key: "D" },
91 { key: "f", shift-key: "F" },
92 { key: "g", shift-key: "G" },
93 { key: "h", shift-key: "H" },
94 { key: "j", shift-key: "J" },
95 { key: "k", shift-key: "K" },
96 { key: "l", shift-key: "L" }
97 ],
98 [
99 { key: "z", shift-key: "Z" },
100 { key: "x", shift-key: "X" },
101 { key: "c", shift-key: "C" },
102 { key: "v", shift-key: "V" },
103 { key: "b", shift-key: "B" },
104 { key: "n", shift-key: "N" },
105 { key: "m", shift-key: "M" },
106 { key: ",", shift-key: ";" },
107 { key: ".", shift-key: ":" },
108 { key: "?", shift-key: "?" }
109 ],
110 ],
111 [
112 [
113 { key: "1", shift-key: "[" },
114 { key: "2", shift-key: "]" },
115 { key: "3", shift-key: "{" },
116 { key: "4", shift-key: "}" },
117 { key: "5", shift-key: "#" },
118 { key: "6", shift-key: "%" },
119 { key: "7", shift-key: "^" },
120 { key: "8", shift-key: "*" },
121 { key: "9", shift-key: "+" },
122 { key: "0", shift-key: "=" }
123 ],
124 [
125 { key: "-", shift-key: "_" },
126 { key: "/", shift-key: "\\" },
127 { key: ":", shift-key: "|" },
128 { key: ";", shift-key: "~" },
129 { key: "(", shift-key: "<" },
130 { key: ")", shift-key: ">" },
131 { key: "€", shift-key: "$" },
132 { key: "&", shift-key: "€" },
133 { key: "@", shift-key: "°" },
134 { key: "'", shift-key: "#" },
135 ],
136 [
137 { key: ".", shift-key: "." },
138 { key: ",", shift-key: "," },
139 { key: "?", shift-key: "?" },
140 { key: "!", shift-key: "!" },
141 { key: "'", shift-key: "'" },
142 ],
143 ]
144 ];
145
146 out property <int> current-key-set;
147 out property <[[KeyModel]]> keys: default-key-sets[self.current-key-set];
148 in-out property <bool> open;
149
150 callback key_pressed(/* key */ string);
151
152 public function switch-keyboard() {
153 if (self.current-key-set < self.default-key-sets.length - 1) {
154 self.current-key-set += 1;
155 } else {
156 self.current-key-set -= 1;
157 }
158
159 self.current-key-set = min(self.default-key-sets.length - 1, max(0, self.current-key-set))
160 }
161}
162
163export component VirtualKeyboard {
164 private property <bool> shift;
165
166 callback close();
167
168 preferred-width: 100%;
169
170 TouchArea {}
171
172 Rectangle {
173 background: Palette.color-scheme == ColorScheme.dark ? #1c1c1c : #d4d4d4;
174 height: 100%;
175 }
176
177 i-layout := VerticalLayout {
178 padding: 8px;
179 spacing: 4px;
180
181 for row[index] in VirtualKeyboardHandler.keys : HorizontalLayout {
182 spacing: 4px;
183
184 if (index == 0) : VirtualKeyboardButton {
185 key: "ESC";
186
187 key-pressed => {
188 VirtualKeyboardHandler.key-pressed(Key.Escape);
189 }
190 }
191
192 if (index == 1) : VirtualKeyboardButton {
193 key: "Tab";
194
195 key-pressed => {
196 VirtualKeyboardHandler.key-pressed(Key.Tab);
197 }
198 }
199
200 // shift
201 if (index == 2) : VirtualKeyboardButton {
202 icon: Icons.arrow-up;
203
204 key-pressed => {
205 root.shift = !root.shift;
206 }
207 }
208
209 for km in row : VirtualKeyboardButton {
210 key: root.shift ? km.shift-key : km.key;
211
212 key-pressed(key) => {
213 VirtualKeyboardHandler.key-pressed(key);
214 root.shift = false;
215 }
216 }
217
218 if (index == 0) : VirtualKeyboardButton {
219 icon: Icons.chevron-left;
220
221 key-pressed => {
222 VirtualKeyboardHandler.key-pressed(Key.Backspace);
223 }
224 }
225
226 if (index == 1) : VirtualKeyboardButton {
227 icon: Icons.arrow-circle-o-left;
228
229 key-pressed => {
230 VirtualKeyboardHandler.key-pressed(Key.Return);
231 }
232 }
233
234 // shift
235 if (index == 2) : VirtualKeyboardButton {
236 icon: Icons.arrow-up;
237
238 key-pressed => {
239 root.shift = !root.shift;
240 }
241 }
242 }
243
244 HorizontalLayout {
245 spacing: 4px;
246
247 VirtualKeyboardButton {
248 icon: Icons.expand-more;
249
250 key-pressed(key) => {
251 root.close();
252 }
253 }
254
255 VirtualKeyboardButton {
256 icon: Icons.globe;
257
258 key-pressed(key) => {
259 VirtualKeyboardHandler.switch-keyboard();
260 }
261 }
262 VirtualKeyboardButton {
263 horizontal-stretch: 1;
264 key: " ";
265
266 key-pressed(key) => {
267 root.shift = false;
268 VirtualKeyboardHandler.key-pressed(key);
269 }
270 }
271 VirtualKeyboardButton {
272 icon: Icons.arrow-left;
273
274 key-pressed(key) => {
275 VirtualKeyboardHandler.key-pressed(Key.LeftArrow);
276 }
277 }
278 VirtualKeyboardButton {
279 icon: Icons.arrow-right;
280
281 key-pressed(key) => {
282 VirtualKeyboardHandler.key-pressed(Key.RightArrow);
283 }
284 }
285 }
286
287
288 }
289
290 animate y { duration: 500ms; easing: cubic-bezier(0.05, 0.7, 0.1, 1.0); }
291}
292