1 /// 2 module fluid.checkbox; 3 4 import fluid.node; 5 import fluid.input; 6 import fluid.utils; 7 import fluid.style; 8 import fluid.backend; 9 import fluid.image_view; 10 11 import fluid.io.canvas; 12 13 @safe: 14 15 /// A checkbox can be selected by the user to indicate a true or false state. 16 alias checkbox = simpleConstructor!Checkbox; 17 18 /// ditto 19 class Checkbox : InputNode!Node { 20 21 mixin enableInputActions; 22 23 CanvasIO canvasIO; 24 25 /// Additional features available for checkbox styling. 26 static class Extra : typeof(super).Extra { 27 28 /// Image to use for the checkmark. 29 Image checkmark; 30 31 this(Image checkmark) { 32 this.checkmark = checkmark; 33 } 34 35 } 36 37 // Button status 38 public { 39 40 /// Size of the checkbox. 41 Vector2 size; 42 43 } 44 45 private { 46 47 DrawableImage _markImage; 48 bool _isChecked; 49 50 } 51 52 /// Create a new checkbox. 53 /// Params: 54 /// isChecked = Whether the checkbox should be checked or not. 55 /// changed = Callback to run whenever the user changes the value. 56 this(bool isChecked, void delegate() @safe changed = null) { 57 58 this(changed); 59 this._isChecked = isChecked; 60 61 } 62 63 /// ditto 64 this(void delegate() @safe changed = null) { 65 66 this.size = Vector2(10, 10); 67 this.changed = changed; 68 69 } 70 71 /// If true, the box is checked. 72 bool isChecked() const { 73 return _isChecked; 74 75 } 76 77 /// ditto 78 bool isChecked(bool value) { 79 updateSize(); 80 return _isChecked = value; 81 } 82 83 /// Get the currently used checkmark image. Requires `CanvasIO`, updated on resize. 84 /// Returns: 85 /// Active checkmark image. 86 inout(Image) markImage() inout { 87 return _markImage; 88 } 89 90 protected override void resizeImpl(Vector2 space) { 91 92 use(canvasIO); 93 94 if (canvasIO) { 95 _markImage = getImage(pickStyle()); 96 load(canvasIO, _markImage); 97 } 98 99 minSize = size; 100 101 } 102 103 protected override void drawImpl(Rectangle outer, Rectangle inner) { 104 105 import std.algorithm : min; 106 107 auto style = pickStyle(); 108 109 style.drawBackground(io, canvasIO, outer); 110 111 if (canvasIO) { 112 113 const size = _markImage.size.fitInto(inner.size); 114 const position = center(inner) - size/2; 115 116 _markImage.draw(Rectangle(position.tupleof, size.tupleof)); 117 118 } 119 120 if (auto texture = getTexture(style)) { 121 122 const size = texture.canvasSize.fitInto(inner.size); 123 const position = center(inner) - size/2; 124 125 texture.draw(Rectangle(position.tupleof, size.tupleof)); 126 127 } 128 129 } 130 131 /// Get checkmark image used by this checkbox. 132 protected Image getImage(Style style) { 133 134 if (auto extra = cast(Extra) style.extra) { 135 return extra.checkmark; 136 } 137 return Image.init; 138 139 } 140 141 /// Get checkmark texture used by this checkbox. 142 protected TextureGC* getTexture(Style style) @trusted { 143 144 auto extra = cast(Extra) style.extra; 145 146 // No valid extra data, ignore 147 if (!extra) return null; 148 149 // Load the texture 150 return extra.getTexture(io, extra.checkmark); 151 152 } 153 154 155 /// Toggle the checkbox. 156 void toggle() { 157 158 isChecked = !isChecked; 159 if (changed) changed(); 160 161 } 162 163 /// ditto 164 @(FluidInputAction.press) 165 protected void press() { 166 167 toggle(); 168 169 } 170 171 static if (is(typeof(text) : string)) 172 override string toString() const { 173 174 if (isChecked) 175 return "checkbox(true)"; 176 else 177 return "checkbox()"; 178 179 } 180 181 } 182 183 /// 184 unittest { 185 186 // Checkbox creates a toggleable button 187 auto myCheckbox = checkbox(); 188 189 assert(!myCheckbox.isChecked); 190 191 }