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 }