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(position, ss.getImage("ElementDisabledPtrn")); 54 } 55 /+if(text) { 56 const int textPadding = getAvailableStyleSheet.drawParameters["TextSpacingSides"]; 57 const Coordinate textPos = Coordinate(textPadding + getAvailableStyleSheet().getImage(iconUnlatched).width, 58 (position.height / 2) - (text.font.size / 2), position.width, position.height - textPadding); 59 output.drawSingleLineText(textPos, text); 60 } 61 /+output.drawColorText(getAvailableStyleSheet().getImage("checkBoxA").width, 0, text, 62 getAvailableStyleSheet().getFontset("default"), getAvailableStyleSheet().getColor("normaltext"), 0);+/ 63 if(_isLatched) { 64 output.insertBitmap(0, 0, getAvailableStyleSheet().getImage(iconLatched)); 65 } else { 66 output.insertBitmap(0, 0, getAvailableStyleSheet().getImage(iconUnlatched)); 67 } 68 elementContainer.drawUpdate(this); 69 if(onDraw !is null) { 70 onDraw(); 71 }+/ 72 } 73 74 public override void passMCE(MouseEventCommons mec, MouseClickEvent mce) { 75 if (mce.button == MouseButton.Left && mce.state == ButtonState.Pressed) group.latch(this); 76 super.passMCE(mec, mce); 77 } 78 /** 79 * If the radio button is pressed, then it sets to unpressed. Does nothing otherwise. 80 */ 81 public void latchOff() @trusted { 82 if (isChecked) { 83 isChecked = false; 84 draw(); 85 } 86 } 87 /** 88 * Sets the radio button into its pressed state. 89 */ 90 public void latchOn() @trusted { 91 if (!isChecked) { 92 isChecked = true; 93 draw(); 94 } 95 } 96 97 /** 98 * Sets the group of the radio button. 99 */ 100 public void setGroup(IRadioButtonGroup group) @safe @property { 101 this.group = group; 102 } 103 public bool equals(IRadioButton rhs) @safe pure @nogc nothrow const { 104 WindowElement we = cast(WindowElement)rhs; 105 return source == we.source; 106 } 107 public string value() @property @safe @nogc pure nothrow const { 108 return source; 109 } 110 public bool isSmallButtonHeight(int height) { 111 if (text) return false; 112 else if (position.width == height && position.height == height) return true; 113 else return false; 114 } 115 ///Returns true if left side justified, false otherwise. 116 public bool isLeftSide() @nogc @safe pure nothrow const { 117 return flags & IS_LHS ? true : false; 118 } 119 ///Sets the small button to the left side if true. 120 public bool isLeftSide(bool val) @nogc @safe pure nothrow { 121 if (val) flags |= IS_LHS; 122 else flags &= ~IS_LHS; 123 return flags & IS_LHS ? true : false; 124 } 125 } 126 127 /** 128 * Radio Button Group implementation. 129 * Can send events via it's delegates. 130 */ 131 public class RadioButtonGroup : IRadioButtonGroup { 132 alias RadioButtonSet = LinkedList!(IRadioButton, false, "a.equals(b)"); 133 protected RadioButtonSet radioButtons; 134 protected IRadioButton latchedButton; 135 protected size_t _latchPos; 136 public EventDeleg onToggle; ///If set, it'll be called when the group is toggled. 137 ///Empty ctor 138 public this() @safe pure nothrow @nogc { 139 140 } 141 /** 142 * Creates a new group with some starting elements from a compatible range. 143 */ 144 public this(R)(R range) @safe { 145 foreach (key; range) { 146 radioButtons.put(key); 147 } 148 } 149 /** 150 * Adds a new RadioButton to the group. 151 */ 152 public void add(IRadioButton rg) @safe { 153 radioButtons.put(rg); 154 rg.setGroup(this); 155 } 156 /** 157 * Removes the given RadioButton from the group. 158 */ 159 public void remove(IRadioButton rg) @safe { 160 radioButtons.removeByElem(rg); 161 rg.setGroup(null); 162 } 163 /** 164 * Latches the group. 165 */ 166 public void latch(IRadioButton sender) @safe { 167 latchedButton = sender; 168 for (int i ; i < radioButtons.length ; i++) { 169 IRadioButton elem = radioButtons[i]; 170 if (sender.equals(elem)) { 171 _latchPos = i; 172 } 173 elem.latchOff; 174 } 175 sender.latchOn; 176 callOnToggle(new Event(this, cast(Object)sender, EventType.Toggle, SourceType.RadioButtonGroup)); 177 } 178 /** 179 * Latches to the given position. 180 */ 181 public size_t latchPos(size_t val) @safe { 182 foreach(elem; radioButtons) { 183 elem.latchOff; 184 } 185 radioButtons[val].latchOn; 186 _latchPos = val; 187 callOnToggle(new Event(this, cast(Object)radioButtons[val], EventType.Toggle, SourceType.RadioButtonGroup)); 188 return _latchPos; 189 } 190 ///Calls the `onToggle` delegate if set 191 protected void callOnToggle(Event ev) @trusted { 192 if (onToggle !is null) 193 onToggle (ev); 194 } 195 /** 196 * Returns the current latch position. 197 */ 198 public size_t latchPos() @nogc @safe pure nothrow { 199 return _latchPos; 200 } 201 /** 202 * Returns the value of this group. 203 */ 204 public @property string value() const @nogc @safe pure nothrow { 205 if(latchedButton !is null) return latchedButton.value; 206 else return null; 207 } 208 }