1 module pixelperfectengine.concrete.elements.radiobutton; 2 3 public import pixelperfectengine.concrete.elements.base; 4 import collections.linkedlist; 5 6 /** 7 * Implements a single radio button. 8 * Needs to be grouped to used. 9 * Equality is checked by comparing `source`, so give each RadioButton a different source value. 10 */ 11 public class RadioButton : WindowElement, IRadioButton, ISmallButton { 12 protected IRadioButtonGroup group; ///The group which this object belongs to. 13 //protected bool _isLatched; ///The state of the RadioButton 14 public string iconLatched = "radioButtonB"; ///Sets the icon for latched positions 15 public string iconUnlatched = "radioButtonA"; ///Sets the icon for unlatched positions 16 public this(Text text, string source, Box position, IRadioButtonGroup group = null) { 17 this.position = position; 18 this.text = text; 19 this.source = source; 20 if (group) group.add(this); 21 } 22 ///Ditto 23 public this(dstring text, string source, Box position, IRadioButtonGroup group = null) { 24 this(new Text(text, getStyleSheet().getChrFormatting("checkBox")), source, position, group); 25 } 26 ///Ditto 27 public this(string iconLatched, string iconUnlatched, string source, Box position, IRadioButtonGroup group = null) { 28 this.position = position; 29 this.iconLatched = iconLatched; 30 this.iconUnlatched = iconUnlatched; 31 this.source = source; 32 this.group = group; 33 if (group) 34 group.add(this); 35 } 36 override public void draw() { 37 parent.clearArea(position); 38 StyleSheet ss = getStyleSheet(); 39 Bitmap8Bit icon = isChecked ? ss.getImage(iconLatched) : ss.getImage(iconUnlatched); 40 parent.bitBLT(position.cornerUL, icon); 41 if (text) { 42 Box textPos = position; 43 textPos.left += icon.width + getStyleSheet.drawParameters["TextSpacingSides"]; 44 parent.drawTextSL(textPos, text, Point.init); 45 } 46 47 if (isFocused) { 48 const int textPadding = ss.drawParameters["horizTextPadding"]; 49 parent.drawBoxPattern(position - textPadding, ss.pattern["blackDottedLine"]); 50 } 51 52 if (state == ElementState.Disabled) { 53 parent.bitBLTPattern(Box(position.left, position.top, position.left + icon.width - 1, position.top + icon.height - 1 54 ), ss.getImage("ElementDisabledPtrn")); 55 } 56 if (onDraw !is null) { 57 onDraw(); 58 } 59 } 60 61 public override void passMCE(MouseEventCommons mec, MouseClickEvent mce) { 62 if (state != ElementState.Enabled) return; 63 if (mce.button == MouseButton.Left && mce.state == ButtonState.Pressed) group.latch(this); 64 super.passMCE(mec, mce); 65 } 66 /** 67 * If the radio button is pressed, then it sets to unpressed. Does nothing otherwise. 68 */ 69 public void latchOff() @trusted { 70 if (isChecked) { 71 isChecked = false; 72 draw(); 73 } 74 } 75 /** 76 * Sets the radio button into its pressed state. 77 */ 78 public void latchOn() @trusted { 79 if (!isChecked) { 80 isChecked = true; 81 draw(); 82 } 83 } 84 85 /** 86 * Sets the group of the radio button. 87 */ 88 public void setGroup(IRadioButtonGroup group) @safe @property { 89 this.group = group; 90 } 91 public bool equals(IRadioButton rhs) @safe pure @nogc nothrow const { 92 WindowElement we = cast(WindowElement)rhs; 93 return source == we.source; 94 } 95 public string value() @property @safe @nogc pure nothrow const { 96 return source; 97 } 98 public bool isSmallButtonHeight(int height) { 99 if (text) return false; 100 else if (position.width == height && position.height == height) return true; 101 else return false; 102 } 103 ///Returns true if left side justified, false otherwise. 104 public bool isLeftSide() @nogc @safe pure nothrow const { 105 return flags & IS_LHS ? true : false; 106 } 107 ///Sets the small button to the left side if true. 108 public bool isLeftSide(bool val) @nogc @safe pure nothrow { 109 if (val) flags |= IS_LHS; 110 else flags &= ~IS_LHS; 111 return flags & IS_LHS ? true : false; 112 } 113 } 114 115 /** 116 * Radio Button Group implementation. 117 * Can send events via it's delegates. 118 */ 119 public class RadioButtonGroup : IRadioButtonGroup { 120 alias RadioButtonSet = LinkedList!(IRadioButton, false, "a is b"); 121 protected RadioButtonSet radioButtons; 122 protected IRadioButton latchedButton; 123 protected size_t _latchPos; 124 public EventDeleg onToggle; ///If set, it'll be called when the group is toggled. 125 ///Empty ctor 126 public this() @safe pure nothrow @nogc { 127 128 } 129 /** 130 * Creates a new group with some starting elements from a compatible range. 131 */ 132 public this(R)(R range) @safe { 133 foreach (key; range) { 134 synchronized 135 add(key); 136 } 137 } 138 /** 139 * Adds a new RadioButton to the group. 140 */ 141 public void add(IRadioButton rg) @safe { 142 radioButtons.put(rg); 143 rg.setGroup(this); 144 } 145 /** 146 * Removes the given RadioButton from the group. 147 */ 148 public void remove(IRadioButton rg) @safe { 149 radioButtons.removeByElem(rg); 150 rg.setGroup(null); 151 } 152 /** 153 * Latches the group. 154 */ 155 public void latch(IRadioButton sender) @safe { 156 latchedButton = sender; 157 for (int i ; i < radioButtons.length ; i++) { 158 IRadioButton elem = radioButtons[i]; 159 if (sender.equals(elem)) { 160 _latchPos = i; 161 } 162 elem.latchOff; 163 } 164 sender.latchOn; 165 callOnToggle(new Event(this, cast(Object)sender, EventType.Toggle, SourceType.RadioButtonGroup)); 166 } 167 /** 168 * Latches to the given position. 169 */ 170 public size_t latchPos(size_t val) @safe { 171 foreach(elem; radioButtons) { 172 elem.latchOff; 173 } 174 radioButtons[val].latchOn; 175 _latchPos = val; 176 callOnToggle(new Event(this, cast(Object)radioButtons[val], EventType.Toggle, SourceType.RadioButtonGroup)); 177 return _latchPos; 178 } 179 ///Calls the `onToggle` delegate if set 180 protected void callOnToggle(Event ev) @trusted { 181 if (onToggle !is null) 182 onToggle (ev); 183 } 184 /** 185 * Returns the current latch position. 186 */ 187 public size_t latchPos() @nogc @safe pure nothrow { 188 return _latchPos; 189 } 190 /** 191 * Returns the value of this group. 192 */ 193 public @property string value() const @nogc @safe pure nothrow { 194 if(latchedButton !is null) return latchedButton.value; 195 else return null; 196 } 197 }