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 }