1 ///
2 module fluid.checkbox;
3
4 import fluid.node;
5 import fluid.input;
6 import fluid.utils;
7 import fluid.style;
8 import fluid.backend;
9
10 @safe:
11
12 /// A checkbox can be selected by the user to indicate a true or false state.
13 alias checkbox = simpleConstructor!Checkbox;
14
15 /// ditto
16 class Checkbox : InputNode!Node {
17
18 mixin enableInputActions;
19
20 /// Additional features available for checkbox styling.
21 static class Extra : typeof(super).Extra {
22
23 /// Image to use for the checkmark.
24 Image checkmark;
25
26 this(Image checkmark) {
27 this.checkmark = checkmark;
28 }
29
30 }
31
32 // Button status
33 public {
34
35 /// Size of the checkbox.
36 Vector2 size;
37
38 }
39
40 private {
41
42 bool _isChecked;
43
44 }
45
46 /// Create a new checkbox.
47 /// Params:
48 /// isChecked = Whether the checkbox should be checked or not.
49 /// changed = Callback to run whenever the user changes the value.
50 this(bool isChecked, void delegate() @safe changed = null) {
51
52 this(changed);
53 this._isChecked = isChecked;
54
55 }
56
57 /// ditto
58 this(void delegate() @safe changed = null) {
59
60 this.size = Vector2(10, 10);
61 this.changed = changed;
62
63 }
64
65 /// If true, the box is checked.
66 bool isChecked() const {
67
68 return _isChecked;
69
70 }
71
72 /// ditto
73 bool isChecked(bool value) {
74
75 return _isChecked = value;
76
77 }
78
79 protected override void resizeImpl(Vector2 space) {
80
81 minSize = size;
82
83 }
84
85 protected override void drawImpl(Rectangle outer, Rectangle inner) {
86
87 import std.algorithm : min;
88
89 auto style = pickStyle();
90
91 style.drawBackground(io, outer);
92
93 if (auto texture = getTexture(style)) {
94
95 // Get the scale
96 const scale = min(
97 inner.width / texture.width,
98 inner.height / texture.height
99 );
100 const size = Vector2(texture.width * scale, texture.height * scale);
101 const position = center(inner) - size/2;
102
103 texture.draw(Rectangle(position.tupleof, size.tupleof));
104
105 }
106
107 }
108
109 /// Get checkmark texture used by this checkbox.
110 protected TextureGC* getTexture(Style style) @trusted {
111
112 auto extra = cast(Extra) style.extra;
113
114 // No valid extra data, ignore
115 if (!extra) return null;
116
117 // Load the texture
118 return extra.getTexture(io, extra.checkmark);
119
120 }
121
122
123 /// Toggle the checkbox.
124 void toggle() {
125
126 isChecked = !isChecked;
127 if (changed) changed();
128
129 }
130
131 /// ditto
132 @(FluidInputAction.press)
133 protected void press() {
134
135 toggle();
136
137 }
138
139 static if (is(typeof(text) : string))
140 override string toString() const {
141
142 if (isChecked)
143 return "checkbox(true)";
144 else
145 return "checkbox()";
146
147 }
148
149 }
150
151 ///
152 unittest {
153
154 // Checkbox creates a toggleable button
155 auto myCheckbox = checkbox();
156
157 assert(!myCheckbox.isChecked);
158
159 }
160
161 unittest {
162
163 int changed;
164
165 auto io = new HeadlessBackend;
166 auto root = checkbox(delegate {
167
168 changed++;
169
170 });
171
172 root.io = io;
173 root.runInputAction!(FluidInputAction.press);
174
175 assert(changed == 1);
176 assert(root.isChecked);
177
178 root.runInputAction!(FluidInputAction.press);
179
180 assert(changed == 2);
181 assert(!root.isChecked);
182
183 }