1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4import { CupertinoFontSettings, CupertinoPalette, Icons } from "styling.slint";
5import { FocusBorder } from "components.slint";
6
7export component CheckBox {
8 in property <string> text;
9 in property <bool> enabled <=> i-touch-area.enabled;
10 out property <bool> has-focus: i-focus-scope.has-focus;
11 in-out property <bool> checked;
12
13 callback toggled;
14
15 private property <brush> background: checked && root.enabled ? CupertinoPalette.accent-background : CupertinoPalette.control-background;
16 private property <brush> icon-color: CupertinoPalette.accent-foreground;
17
18 min-height: max(14px, i-layout.min-height);
19 accessible-enabled: root.enabled;
20 accessible-checkable: true;
21 accessible-label: root.text;
22 accessible-checked <=> root.checked;
23 accessible-role: checkbox;
24 accessible-action-default => {
25 root.checked = !root.checked;
26 root.toggled();
27 }
28 forward-focus: i-focus-scope;
29
30 states [
31 disabled when !root.enabled : {
32 opacity: 0.5;
33 icon-color: CupertinoPalette.foreground;
34 }
35 pressed when i-touch-area.pressed : {
36 root.background: root.checked ? CupertinoPalette.secondary-accent-background : CupertinoPalette.secondary-control-background;
37 }
38 ]
39
40 animate background { duration: 150ms; }
41
42 FocusBorder {
43 x: (parent.width - self.width) / 2;
44 y: (parent.height - self.height) / 2;
45 width: parent.width + 6px;
46 height: parent.height + 6px;
47 border-radius: 8px;
48 has-focus: root.has-focus;
49 }
50
51 i-layout := HorizontalLayout {
52 spacing: 6px;
53
54 if (!root.checked) : Rectangle {
55 y: (parent.height - self.height) / 2;
56 width: 14px;
57 height: self.width;
58 border-radius: 4px;
59 background: root.background;
60 clip: true;
61
62 Rectangle {
63 background: @radial-gradient(circle, #00000000 0%, #00000000 50%, #0000001A 100%);
64 }
65
66 Rectangle {
67 width: 100%;
68 height: 100%;
69 border-radius: parent.border-radius;
70 border-width: 0.5px;
71 border-color: CupertinoPalette.border;
72 }
73 }
74
75 if (root.checked) : Rectangle {
76 drop-shadow-blur: 3px;
77 drop-shadow-color: #00000066;
78 drop-shadow-offset-y: 0.5px;
79 y: (parent.height - self.height) / 2;
80 width: 14px;
81 height: self.width;
82 border-radius: 4px;
83 background: root.background;
84
85 Rectangle {
86 drop-shadow-blur: 2px;
87 drop-shadow-color: #00000026;
88 drop-shadow-offset-y: 1px;
89 border-radius: parent.border-radius;
90 background: root.background;
91 }
92
93 Rectangle {
94 drop-shadow-blur: 1px;
95 drop-shadow-color: #00000026;
96 drop-shadow-offset-y: 0.5px;
97 border-radius: parent.border-radius;
98 background: root.background;
99 }
100
101 Rectangle {
102 border-radius: parent.border-radius;
103 background: CupertinoPalette.dimmer;
104 opacity: 0.17;
105 }
106
107 i-icon := Image {
108 image-fit: contain;
109 visible: root.checked;
110 source: Icons.check-mark;
111 colorize: root.icon-color;
112 width: 12px;
113 accessible-role: none;
114
115 animate colorize { duration: 150ms; }
116 }
117 }
118
119 if (root.text != "") : Text {
120 text: root.text;
121 color: CupertinoPalette.foreground;
122 font-size: CupertinoFontSettings.body.font-size;
123 font-weight: CupertinoFontSettings.body.font-weight;
124 vertical-alignment: center;
125 horizontal-alignment: left;
126 }
127 }
128
129 i-touch-area := TouchArea {
130 clicked => {
131 if (root.enabled) {
132 root.checked = !root.checked;
133 root.toggled();
134 }
135 }
136 }
137
138 i-focus-scope := FocusScope {
139 x: 0;
140 width: 0; // Do not react on clicks
141 enabled <=> root.enabled;
142
143 key-pressed(event) => {
144 if (event.text == " " || event.text == "\n") {
145 i-touch-area.clicked();
146 return accept;
147 }
148 return reject;
149 }
150 }
151}
152