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 }