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