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 }