1 ///
2 module fluid.radiobox;
3 
4 import fluid.node;
5 import fluid.input;
6 import fluid.utils;
7 import fluid.style;
8 import fluid.backend;
9 import fluid.checkbox;
10 
11 @safe:
12 
13 /// A radiobox is similar to checkbox, except only one in a group can be selected at a time.
14 alias radiobox = simpleConstructor!Radiobox;
15 
16 /// ditto
17 class Radiobox : Checkbox {
18 
19     mixin enableInputActions;
20 
21     static class Extra : Checkbox.Extra {
22 
23         /// Width of the radiobox outline.
24         int outlineWidth;
25 
26         /// Color of the outline.
27         auto outlineColor = Color(0, 0, 0, 0);
28 
29         /// Fill color for the checkbox.
30         auto fillColor = Color(0, 0, 0, 0);
31 
32         this(int outlineWidth, Color outlineColor, Color fillColor) {
33 
34             super(Image.init);
35             this.outlineWidth = outlineWidth;
36             this.outlineColor = outlineColor;
37             this.fillColor = fillColor;
38 
39         }
40 
41     }
42 
43     public {
44 
45         /// Group this radiobox belongs to. In a single group, only one radiobox can be selected.
46         RadioboxGroup group;
47         invariant(group);
48 
49     }
50 
51     /// Create a new radiobox.
52     /// Params:
53     ///     group = Group the radiobox belongs to.
54     ///     isChecked = Whether the radiobox should be checked or not.
55     this(RadioboxGroup group, bool isChecked = false) {
56 
57         this.group = group;
58 
59         // Select if ordered to do so.
60         if (isChecked) group.selection = this;
61 
62     }
63 
64     override bool isChecked() const {
65 
66         return this is group.selection;
67 
68     }
69 
70     override bool isChecked(bool value) {
71 
72         // Select this checkbox if set to true
73         if (value) select();
74 
75         // If set to false, and if checked, nullify selection
76         else if (isChecked) {
77 
78             group.selection = null;
79 
80         }
81 
82         return value;
83 
84     }
85 
86     /// Check this radiobox.
87     void select() {
88 
89         // Do nothing if already checked
90         if (isChecked) return;
91 
92         // Check the box
93         group.selection = this;
94         if (changed) changed();
95 
96     }
97 
98     /// ditto
99     @(FluidInputAction.press)
100     override void press() {
101 
102         select();
103 
104     }
105 
106     protected override void drawImpl(Rectangle outer, Rectangle inner) {
107 
108         import std.algorithm : min;
109 
110         // TODO set rounded borders instead of drawing a circle?
111 
112         auto style = pickStyle();
113 
114         super.drawImpl(outer, inner);
115 
116         if (auto extra = cast(Extra) style.extra) {
117 
118             const outerRadius = min(
119                 outer.width / 2,
120                 outer.height / 2,
121             );
122             const innerRadius = min(
123                 inner.width / 2,
124                 inner.height / 2,
125             );
126 
127             if (canvasIO) {
128 
129                 // Draw the inside
130                 canvasIO.drawCircle(center(inner), innerRadius, extra.fillColor);
131 
132                 // Draw the outline
133                 canvasIO.drawCircleOutline(center(outer), outerRadius, extra.outlineWidth, extra.outlineColor);
134 
135             }
136 
137             else {
138 
139                 // Draw the outline
140                 io.drawCircleOutline(center(outer), outerRadius, extra.outlineColor);
141 
142                 // Draw the inside
143                 if (isChecked)
144                 io.drawCircle(center(inner), innerRadius, extra.fillColor);
145 
146             }
147 
148         }
149 
150     }
151 
152 }
153 
154 ///
155 unittest {
156 
157     // Radioboxes are similar to checkboxes, except that only one in a group
158     // can be checked at a time
159     auto group = new RadioboxGroup;
160     auto box1 = radiobox(group);
161     auto box2 = radiobox(group);
162 
163     box1.select();
164 
165     assert(box1.isChecked);
166 
167     // Checking the other box will uncheck the previous one
168     box2.select();
169 
170     assert(!box1.isChecked);
171     assert(box2.isChecked);
172 
173 }
174 
175 class RadioboxGroup {
176 
177     /// Selected item.
178     Radiobox selection;
179 
180 }