1 module PixelPerfectEngine.concrete.elements.textbox; 2 3 public import PixelPerfectEngine.concrete.elements.base; 4 5 /** 6 * Text input box 7 */ 8 public class TextBox : WindowElement, TextInputListener { 9 //protected bool enableEdit, insert; 10 protected static enum INSERT = 1<<9; 11 protected static enum ENABLE_TEXT_EDIT = 1<<10; 12 protected size_t cursorPos; 13 protected int horizTextOffset, select; 14 protected Text oldText; 15 //public int brush, textpos; 16 //public TextInputHandler tih; 17 public void delegate(Event ev) onTextInput; 18 public this(dstring text, string source, Coordinate coordinates) { 19 this(new Text(text, getStyleSheet().getChrFormatting("textBox")), source, coordinates); 20 } 21 public this(Text text, string source, Coordinate coordinates) { 22 position = coordinates; 23 this.text = text; 24 this.source = source; 25 //inputHandler.addTextInputListener(source, this); 26 //insert = true; 27 //draw(); 28 } 29 /+public override void onClick(int offsetX, int offsetY, int state, ubyte button){ 30 if(button == MouseButton.RIGHT){ 31 if(state == ButtonState.PRESSED){ 32 if(onMouseRClickPre !is null){ 33 onMouseRClickPre(new Event(source, null, null, null, null, button, EventType.CLICK, null, this)); 34 } 35 }else{ 36 if(onMouseRClickRel !is null){ 37 onMouseRClickRel(new Event(source, null, null, null, null, button, EventType.CLICK, null, this)); 38 } 39 } 40 }else if(button == MouseButton.MID){ 41 if(state == ButtonState.PRESSED){ 42 if(onMouseMClickPre !is null){ 43 onMouseMClickPre(new Event(source, null, null, null, null, button, EventType.CLICK, null, this)); 44 } 45 }else{ 46 if(onMouseMClickRel !is null){ 47 onMouseMClickRel(new Event(source, null, null, null, null, button, EventType.CLICK, null, this)); 48 } 49 } 50 }else{ 51 if(state == ButtonState.PRESSED){ 52 if(onMouseLClickPre !is null){ 53 onMouseLClickPre(new Event(source, null, null, null, null, button, EventType.CLICK, null, this)); 54 } 55 }else{ 56 if(onMouseLClickRel !is null){ 57 onMouseLClickRel(new Event(source, null, null, null, null, button, EventType.CLICK, null, this)); 58 } 59 } 60 } 61 if(!enableEdit && state == ButtonState.PRESSED && button == MouseButton.LEFT){ 62 //invokeActionEvent(EventType.READYFORTEXTINPUT, 0); 63 enableEdit = true; 64 inputHandler.startTextInput(this); 65 oldText = new Text(text); 66 draw(); 67 } 68 }+/ 69 ///Called when an object loses focus. 70 public void focusLost() { 71 flags &= ~IS_FOCUSED; 72 dropTextInput(); 73 inputHandler.stopTextInput(); 74 75 } 76 public override void passMCE(MouseEventCommons mec, MouseClickEvent mce) { 77 if (!(flags & ENABLE_TEXT_EDIT)) inputHandler.startTextInput(this, false, position); 78 super.passMCE(mec, mce); 79 } 80 public override void draw(){ 81 82 StyleSheet ss = getStyleSheet(); 83 const int textPadding = ss.drawParameters["TextSpacingSides"]; 84 with (parent) { 85 clearArea(position); 86 drawBox(position, ss.getColor("windowascent")); 87 } 88 //draw cursor 89 if (flags & ENABLE_TEXT_EDIT) { 90 //calculate cursor first 91 Box cursor = Box(position.left + textPadding, position.top + textPadding, position.left + textPadding, position.bottom - textPadding); 92 cursor.left += text.getWidth(0, cursorPos) - horizTextOffset; 93 //cursor must be at least single pixel wide 94 cursor.right = cursor.left; 95 if (select) { 96 cursor.right += text.getWidth(cursorPos, cursorPos + select); 97 } else if (flags & INSERT) { 98 if (cursorPos < text.charLength) cursor.right += text.getWidth(cursorPos, cursorPos+1); 99 else cursor.right += text.font.chars(' ').xadvance; 100 } else { 101 cursor.right++; 102 } 103 //Clamp down if cursor is wider than the text editing area 104 cursor.right = cursor.right <= position.right - textPadding ? cursor.right : position.right - textPadding; 105 //Draw cursor 106 parent.drawFilledBox(cursor, ss.getColor("selection")); 107 108 } 109 //draw text 110 parent.drawTextSL(position - textPadding, text, Point(horizTextOffset, 0)); 111 if (isFocused) { 112 parent.drawBoxPattern(position - textPadding, ss.pattern["blackDottedLine"]); 113 } 114 115 if (state == ElementState.Disabled) { 116 parent.bitBLTPattern(position, ss.getImage("ElementDisabledPtrn")); 117 } 118 } 119 /** 120 * Passes text editing events to the target, alongside with a window ID and a timestamp. 121 */ 122 public void textEditingEvent(uint timestamp, uint windowID, dstring text, int start, int length) { 123 for (int i ; i < length ; i++) { 124 this.text.overwriteChar(start + i, text[i]); 125 } 126 cursorPos = start + length; 127 } 128 private void deleteCharacter(size_t n){ 129 text.removeChar(n); 130 } 131 public void textInputEvent(uint timestamp, uint windowID, dstring text){ 132 if (select) { 133 this.text.removeChar(cursorPos, select); 134 select = 0; 135 for(int j ; j < text.length ; j++){ 136 this.text.insertChar(cursorPos++, text[j]); 137 } 138 } else if (flags & INSERT) { 139 for(int j ; j < text.length ; j++){ 140 this.text.overwriteChar(cursorPos++, text[j]); 141 } 142 } else { 143 for(int j ; j < text.length ; j++){ 144 this.text.insertChar(cursorPos++, text[j]); 145 } 146 } 147 const int textPadding = getStyleSheet.drawParameters["TextSpacingSides"]; 148 const Coordinate textPos = Coordinate(textPadding,(position.height / 2) - (this.text.font.size / 2) , 149 position.width,position.height - textPadding); 150 const int x = this.text.getWidth(), cursorPixelPos = this.text.getWidth(0, cursorPos); 151 if(x > textPos.width) { 152 if(cursorPos == this.text.text.length) { 153 horizTextOffset = x - textPos.width; 154 } else if(cursorPixelPos < horizTextOffset) { //Test for whether the cursor would fall out from the current text area 155 horizTextOffset = cursorPixelPos; 156 } else if(cursorPixelPos > horizTextOffset + textPos.width) { 157 horizTextOffset = horizTextOffset + textPos.width; 158 } 159 } 160 draw(); 161 } 162 163 public void dropTextInput() { 164 flags &= ~ENABLE_TEXT_EDIT; 165 horizTextOffset = 0; 166 cursorPos = 0; 167 //inputHandler.stopTextInput(source); 168 draw(); 169 //invokeActionEvent(EventType.TEXTINPUT, 0, text); 170 /+if(onTextInput !is null) 171 onTextInput(new Event(source, null, null, null, text, 0, EventType.TEXTINPUT, null, this));+/ 172 } 173 public void initTextInput() { 174 flags |= ENABLE_TEXT_EDIT; 175 select = cast(int)text.charLength; 176 oldText = new Text(text); 177 draw(); 178 } 179 180 public void textInputKeyEvent(uint timestamp, uint windowID, TextInputKey key, ushort modifier = 0){ 181 switch(key) { 182 case TextInputKey.Enter: 183 inputHandler.stopTextInput(); 184 if(onTextInput !is null) 185 onTextInput(new Event(this, text, EventType.TextInput, SourceType.WindowElement)); 186 //onTextInput(new Event(source, null, null, null, text, 0, EventType.T, null, this)); 187 break; 188 case TextInputKey.Escape: 189 text = oldText; 190 inputHandler.stopTextInput(); 191 192 193 break; 194 case TextInputKey.Backspace: 195 if(cursorPos > 0){ 196 deleteCharacter(cursorPos - 1); 197 cursorPos--; 198 draw(); 199 } 200 break; 201 case TextInputKey.Delete: 202 deleteCharacter(cursorPos); 203 draw(); 204 break; 205 case TextInputKey.CursorLeft: 206 if (modifier != KeyModifier.Shift) { 207 select = 0; 208 if(cursorPos > 0){ 209 --cursorPos; 210 draw(); 211 } 212 } 213 break; 214 case TextInputKey.CursorRight: 215 if (modifier != KeyModifier.Shift) { 216 select = 0; 217 if(cursorPos < text.charLength){ 218 ++cursorPos; 219 draw(); 220 } 221 } 222 break; 223 case TextInputKey.Home: 224 if (modifier != KeyModifier.Shift) { 225 select = 0; 226 cursorPos = 0; 227 draw(); 228 } 229 break; 230 case TextInputKey.End: 231 if (modifier != KeyModifier.Shift) { 232 select = 0; 233 cursorPos = text.charLength; 234 draw(); 235 } 236 break; 237 case TextInputKey.Insert: 238 flags ^= INSERT; 239 draw(); 240 break; 241 default: 242 break; 243 } 244 } 245 }