1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: MIT |
3 | |
4 | struct ButtonColors { |
5 | base: color, |
6 | pressed: color, |
7 | hovered: color, |
8 | } |
9 | |
10 | export global DemoPalette { |
11 | // Color of the home/settings/ink buttons on the left side bar |
12 | out property <color> active-page-icon-color: root.night-mode ? #6284FF : #122F7B; |
13 | out property <color> inactive-page-icon-color: #BDC0D1; |
14 | out property <color> main-background: #0E133F; |
15 | out property <color> neutral-box: #BDC0D1; |
16 | out property <color> page-background-color: root.night-mode ? #122F7B : white; |
17 | out property <color> text-foreground-color: root.night-mode ? #F4F6FF : black; |
18 | out property <color> secondary-foreground-color: root.night-mode ? #C1C3CA : #6C6E7A; |
19 | out property <color> printer-action-background-color: root.night-mode ? root.main-background : white; |
20 | out property <color> printer-queue-item-background-color: root.page-background-color; |
21 | out property <color> status-label-text-color: root.night-mode ? #F1FF98 : #6284FF; |
22 | |
23 | // Color used for the border / outline of items that can be clicked on, such as the |
24 | // "Print"/"Scan" buttons, the printer queue items (for expansion) or controls such |
25 | // as the combo box or spin box. |
26 | out property <color> control-outline-color: #FFBF63; |
27 | out property <color> control-secondary: #6284FF; |
28 | out property <color> control-foreground: root.night-mode ? white : #122F7B; // FIXME: the night mode color was not part of the design |
29 | out property <color> primary-push-button-base: #6284FF; |
30 | out property <ButtonColors> primary-push-button-colors: { |
31 | base: root.primary-push-button-base, |
32 | pressed: root.primary-push-button-base.darker(40%), |
33 | hovered: root.primary-push-button-base.darker(20%), |
34 | }; |
35 | out property <color> secondary-push-button-base: #FFBF63; |
36 | out property <ButtonColors> secondary-push-button-colors: { |
37 | base: root.secondary-push-button-base, |
38 | pressed: root.secondary-push-button-base.darker(40%), |
39 | hovered: root.secondary-push-button-base.darker(20%), |
40 | }; |
41 | out property <color> push-button-text-color: white; |
42 | out property <length> base-font-size: 12px; |
43 | in-out property <bool> night-mode: false; |
44 | } |
45 | |
46 | export component Page inherits Rectangle { |
47 | in property <string> header <=> h.text; |
48 | in property <bool> has-back-button: false; |
49 | |
50 | callback back; |
51 | |
52 | background: DemoPalette.page-background-color; |
53 | |
54 | TouchArea {} // protect underneath controls |
55 | |
56 | if (root.has-back-button) : Image { |
57 | source: @image-url("images/back.svg" ); |
58 | image-fit: contain; |
59 | colorize: DemoPalette.control-secondary; |
60 | y: h.y + (h.height - self.height) / 2; |
61 | x: 5px; |
62 | width: 14px; |
63 | height: 24px; |
64 | |
65 | TouchArea { |
66 | pointer-event(ev) => { |
67 | if (ev.kind == PointerEventKind.up) { |
68 | root.back() |
69 | } |
70 | } |
71 | width: 400%; |
72 | height: 200%; |
73 | x: (parent.width - self.width) / 2; |
74 | y: (parent.height - self.height) / 2; |
75 | } |
76 | } |
77 | |
78 | h := Text { |
79 | font-weight: 900; |
80 | font-size: DemoPalette.base-font-size * 1.75; |
81 | color: DemoPalette.text-foreground-color; |
82 | y: 23px - self.font-size; |
83 | x: root.has-back-button ? 30px + 16px : 0px; |
84 | |
85 | // Allow clicking on the title as well to get back easier when just |
86 | // using fingers on a small screen. |
87 | if (root.has-back-button) : TouchArea { |
88 | pointer-event(ev) => { |
89 | if (ev.kind == PointerEventKind.up) { |
90 | root.back() |
91 | } |
92 | } |
93 | } |
94 | } |
95 | } |
96 | export component Label inherits Text { |
97 | color: DemoPalette.text-foreground-color; |
98 | vertical-alignment: center; |
99 | font-weight: 700; |
100 | vertical-stretch: 0; |
101 | } |
102 | |
103 | component SquareButton inherits Rectangle { |
104 | in property <image> img; |
105 | |
106 | callback clicked; |
107 | |
108 | border-radius: 3px; |
109 | border-width: 2px; |
110 | border-color: DemoPalette.control-outline-color; |
111 | |
112 | touch := TouchArea { |
113 | pointer-event(ev) => { |
114 | if (ev.kind == PointerEventKind.up) { |
115 | root.clicked() |
116 | } |
117 | } |
118 | |
119 | x: -4px; |
120 | y: -4px; |
121 | width: parent.width + 8px; |
122 | height: parent.height + 8px; |
123 | } |
124 | |
125 | Image { |
126 | height: 12px; |
127 | width: 12px; |
128 | x: (parent.width - self.width)/2; |
129 | y: (parent.height - self.height)/2; |
130 | source <=> root.img; |
131 | image-fit: contain; |
132 | colorize: DemoPalette.control-secondary; |
133 | } |
134 | } |
135 | |
136 | export component SpinBox inherits Rectangle { |
137 | in property <int> minimum; |
138 | in property <int> maximum: 100; |
139 | in-out property <int> value; |
140 | |
141 | height: 32px; |
142 | |
143 | HorizontalLayout { |
144 | spacing: 12px; |
145 | padding: 0; |
146 | |
147 | SquareButton { |
148 | clicked => { |
149 | if (root.value > root.minimum) { |
150 | root.value -= 1; |
151 | } |
152 | } |
153 | |
154 | width: root.height - parent.padding * 2; |
155 | img: @image-url("images/minus.svg" ); |
156 | } |
157 | |
158 | Rectangle { |
159 | border-radius: 3px; |
160 | border-width: 2px; |
161 | border-color: DemoPalette.control-outline-color; |
162 | |
163 | Text { |
164 | width: 100%; |
165 | height: 100%; |
166 | vertical-alignment: center; |
167 | horizontal-alignment: center; |
168 | text: root.value; |
169 | color: DemoPalette.control-foreground; |
170 | } |
171 | } |
172 | |
173 | SquareButton { |
174 | clicked => { |
175 | if (root.value < root.maximum) { |
176 | root.value += 1; |
177 | } |
178 | } |
179 | |
180 | width: root.height - parent.padding * 2; |
181 | img: @image-url("images/plus.svg" ); |
182 | } |
183 | } |
184 | } |
185 | |
186 | export component ComboBox inherits Rectangle { |
187 | in property <[string]> choices; |
188 | in-out property <string> value; |
189 | |
190 | border-radius: 3px; |
191 | border-width: 2px; |
192 | border-color: DemoPalette.control-outline-color; |
193 | height: 32px; |
194 | min-width: label.x + label.width + i.width; |
195 | horizontal-stretch: 1; // Work around #2284 |
196 | |
197 | label := Text { |
198 | vertical-alignment: center; |
199 | horizontal-alignment: left; |
200 | text <=> root.value; |
201 | color: DemoPalette.control-foreground; |
202 | height: 100%; |
203 | x: 12px; |
204 | } |
205 | |
206 | i := Image { |
207 | source: @image-url("images/down.svg" ); |
208 | colorize: DemoPalette.control-secondary; |
209 | height: 40%; |
210 | width: self.height; |
211 | image-fit: contain; |
212 | x: parent.width - self.width - self.y; |
213 | y: (parent.height - self.height)/2; |
214 | } |
215 | |
216 | TouchArea { |
217 | pointer-event(ev) => { |
218 | if (ev.kind == PointerEventKind.up) { |
219 | popup.show() |
220 | } |
221 | } |
222 | |
223 | width: 100%; |
224 | height: 100%; |
225 | } |
226 | |
227 | popup := PopupWindow { |
228 | x: 0; |
229 | y: root.height; |
230 | width: root.width; |
231 | |
232 | Rectangle { |
233 | background: DemoPalette.page-background-color; |
234 | border-radius: 3px; |
235 | border-width: 2px; |
236 | border-color: DemoPalette.control-outline-color; |
237 | } |
238 | |
239 | VerticalLayout { |
240 | spacing: 6px; |
241 | padding: 3px; |
242 | |
243 | for value[idx] in root.choices: Rectangle { |
244 | border-radius: 3px; |
245 | background: item-area.has-hover ? DemoPalette.primary-push-button-colors.hovered : #0000; |
246 | |
247 | HorizontalLayout { |
248 | Text { |
249 | text: value; |
250 | color: item-area.has-hover ? DemoPalette.push-button-text-color : DemoPalette.text-foreground-color; |
251 | font-size: DemoPalette.base-font-size; |
252 | } |
253 | } |
254 | |
255 | item-area := TouchArea { |
256 | clicked => { |
257 | root.value = value; |
258 | } |
259 | } |
260 | } |
261 | } |
262 | } |
263 | } |
264 | |
265 | export component CheckBox inherits Rectangle { |
266 | in property <string> text; |
267 | in-out property <bool> checked; |
268 | |
269 | height: 32px; |
270 | |
271 | HorizontalLayout { |
272 | spacing: 12px; |
273 | padding: 0; |
274 | |
275 | SquareButton { |
276 | clicked => { root.checked = !root.checked; } |
277 | |
278 | width: root.height - parent.padding * 2; |
279 | img: root.checked ? @image-url("images/check.svg" ) : @image-url("" ); |
280 | } |
281 | |
282 | if root.text != "" : Text { |
283 | text <=> root.text; |
284 | vertical-alignment: center; |
285 | horizontal-alignment: center; |
286 | color: DemoPalette.control-foreground; |
287 | horizontal-stretch: 1; |
288 | } |
289 | } |
290 | |
291 | TouchArea { |
292 | pointer-event(ev) => { |
293 | if (ev.kind == PointerEventKind.up) { |
294 | root.checked = !root.checked; |
295 | } |
296 | } |
297 | } |
298 | } |
299 | |
300 | export component PushButton inherits Rectangle { |
301 | |
302 | in property <string> text <=> label.text; |
303 | in property <image> icon <=> img.source; |
304 | in property <bool> primary: true; |
305 | out property <bool> pressed: touch-area.pressed; |
306 | out property <ButtonColors> colors: root.primary ? DemoPalette.primary-push-button-colors : DemoPalette.secondary-push-button-colors; |
307 | |
308 | callback clicked; |
309 | |
310 | border-radius: 13.5px; |
311 | background: root.pressed ? root.colors.pressed : (touch-area.has-hover ? root.colors.hovered : root.colors.base); |
312 | horizontal-stretch: 1; |
313 | |
314 | HorizontalLayout { |
315 | padding-top: 5px; |
316 | padding-bottom: 5px; |
317 | padding-left: parent.border-radius; |
318 | padding-right: parent.border-radius; |
319 | |
320 | img := Image { |
321 | horizontal-stretch: 0; |
322 | colorize: DemoPalette.push-button-text-color; |
323 | image-fit: contain; |
324 | width: 17px; |
325 | } |
326 | |
327 | label := Text { |
328 | font-weight: 900; |
329 | font-size: DemoPalette.base-font-size * 0.975; |
330 | color: DemoPalette.push-button-text-color; |
331 | horizontal-alignment: center; |
332 | vertical-alignment: center; |
333 | } |
334 | } |
335 | |
336 | touch-area := TouchArea { |
337 | pointer-event(ev) => { |
338 | if (ev.kind == PointerEventKind.up) { |
339 | root.clicked() |
340 | } |
341 | } |
342 | } |
343 | } |
344 | |