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         // 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             // Draw the outline
128             io.drawCircleOutline(center(outer), outerRadius, extra.outlineColor);
129 
130             // Draw the inside
131             if (isChecked)
132             io.drawCircle(center(inner), innerRadius, extra.fillColor);
133 
134         }
135 
136     }
137 
138 }
139 
140 ///
141 unittest {
142 
143     // Radioboxes are similar to checkboxes, except that only one in a group
144     // can be checked at a time
145     auto group = new RadioboxGroup;
146     auto box1 = radiobox(group);
147     auto box2 = radiobox(group);
148 
149     box1.select();
150 
151     assert(box1.isChecked);
152 
153     // Checking the other box will uncheck the previous one
154     box2.select();
155 
156     assert(!box1.isChecked);
157     assert(box2.isChecked);
158 
159 }
160 
161 class RadioboxGroup {
162 
163     /// Selected item.
164     Radiobox selection;
165 
166 }