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