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         Color outlineColor;
28 
29         /// Fill color for the checkbox.
30         Color fillColor;
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         group.selection = this;
90 
91     }
92 
93     @(FluidInputAction.press)
94     override protected void _pressed() {
95 
96         // Do nothing if already checked
97         if (isChecked) return;
98 
99         // Check the box
100         select();
101         if (changed) changed();
102 
103     }
104 
105     protected override void drawImpl(Rectangle outer, Rectangle inner) {
106 
107         import std.algorithm : min;
108 
109         // TODO set rounded borders instead of drawing a circle?
110 
111         auto style = pickStyle();
112 
113         super.drawImpl(outer, inner);
114 
115         if (auto extra = cast(Extra) style.extra) {
116 
117             const outerRadius = min(
118                 outer.width / 2,
119                 outer.height / 2,
120             );
121             const innerRadius = min(
122                 inner.width / 2,
123                 inner.height / 2,
124             );
125 
126             // Draw the outline
127             io.drawCircleOutline(center(outer), outerRadius, extra.outlineColor);
128 
129             // Draw the inside
130             if (isChecked)
131             io.drawCircle(center(inner), innerRadius, extra.fillColor);
132 
133         }
134 
135     }
136 
137 }
138 
139 ///
140 unittest {
141 
142     // Radioboxes are similar to checkboxes, except that only one in a group
143     // can be checked at a time
144     auto group = new RadioboxGroup;
145     auto box1 = radiobox(group);
146     auto box2 = radiobox(group);
147 
148     box1.select();
149 
150     assert(box1.isChecked);
151 
152     // Checking the other box will uncheck the previous one
153     box2.select();
154 
155     assert(!box1.isChecked);
156     assert(box2.isChecked);
157 
158 }
159 
160 class RadioboxGroup {
161 
162     /// Selected item.
163     Radiobox selection;
164 
165 }