1 /*
2  * Copyright (C) 2015-2017, by Laszlo Szeremi under the Boost license.
3  *
4  * Pixel Perfect Engine, concrete.elements module
5  */
6 
7 module PixelPerfectEngine.concrete.elements;
8 
9 import PixelPerfectEngine.graphics.bitmap;
10 public import PixelPerfectEngine.graphics.draw;
11 import PixelPerfectEngine.system.etc;
12 import PixelPerfectEngine.system.inputHandler;
13 import std.algorithm;
14 import std.stdio;
15 import std.conv;
16 import PixelPerfectEngine.concrete.stylesheet;
17 
18 /**
19  * All Window elements inherit from this class. Provides basic interfacing with containers.
20  */
21 abstract class WindowElement{
22 	//public ActionListener[] al;
23 	protected dstring text;
24 	protected string source;
25 	///DO NOT MODIFY IT EXTERNALLY! Contains the position of the element.
26 	public Coordinate position;
27 	///Contains the output of the drawing functions
28 	public BitmapDrawer output;
29 	///Points to the container for two-way communication
30 	public ElementContainer elementContainer;
31 	public StyleSheet customStyle;
32 	protected bool state;
33 
34 	public static InputHandler inputHandler;	///Common input handler, must be set upon program initialization
35 	public static PopUpHandler popUpHandler;	///Common pop-up handler
36 	public static StyleSheet styleSheet;		///Basic stylesheet, all elements default to this if no alternative found
37 
38 	public static void delegate() onDraw;		///Called when drawing is finished
39 
40 	public void delegate(Event ev) onMouseLClickRel;	///Called on left mouseclick released
41 	public void delegate(Event ev) onMouseRClickRel;	///Called on right mouseclick released
42 	public void delegate(Event ev) onMouseMClickRel;	///Called on middle mouseclick released
43 	public void delegate(Event ev) onMouseHover;		///Called if mouse is on object
44 	public void delegate(Event ev) onMouseMove;			///Called if mouse is moved on object
45 	public void delegate(Event ev) onMouseLClickPre;	///Called on left mouseclick pressed
46 	public void delegate(Event ev) onMouseRClickPre;	///Called on right mouseclick pressed
47 	public void delegate(Event ev) onMouseMClickPre;	///Called on middle mouseclick pressed
48 
49 	public void onClick(int offsetX, int offsetY, int state, ubyte button){
50 
51 	}
52 	public void onDrag(int x, int y, int relX, int relY, ubyte button){
53 
54 	}
55 
56 	public void onScroll(int x, int y, int wX, int wY){
57 
58 	}
59 
60 	@property @nogc @safe nothrow public int getX(){
61 		return position.width;
62 	}
63 	@property @nogc @safe nothrow public int getY(){
64 		return position.height;
65 	}
66 	@property @nogc @safe nothrow public Coordinate getPosition(){
67 		return position;
68 	}
69 	/**
70 	 * Updates the output. Every subclass must override it.
71 	 */
72 	public abstract void draw();
73 
74 	/+protected void invokeActionEvent(int type, int value, wstring message = ""){
75 		foreach(ActionListener a; al){
76 			if(a)
77 				a.actionEvent(new Event(source, null, null, null, text, value, type));
78 		}
79 	}
80 
81 	protected void invokeActionEvent(Event e) {
82 		foreach(ActionListener a; al){
83 			if(a)
84 				a.actionEvent(e);
85 		}
86 	}+/
87 	/*private Bitmap16Bit getBrush(int style){
88 		return altStyleBrush.get(style, elementContainer.getStyleBrush(style));
89 	}*/
90 	public @nogc dstring getText(){
91 		return text;
92 	}
93 	public void setText(dstring s){
94 		text = s;
95 		elementContainer.clearArea(this);
96 		draw();
97 
98 	}
99 
100 	public StyleSheet getAvailableStyleSheet(){
101 		if(customStyle !is null){
102 			return customStyle;
103 		}
104 		return styleSheet;
105 	}
106 
107 	public void setCustomStyle(StyleSheet s){
108 		customStyle = s;
109 	}
110 	/**
111 	 * Enables (b = true) or disables (b = false) the element. All element is enabled by default.
112 	 */
113 	@nogc public void setState(bool b){
114 		state = !b;
115 	}
116 	/**
117 	 * Gets the state of the element.
118 	 */
119 	@nogc public bool getState(){
120 		return !state;
121 	}
122 	/**
123 	 * Returns the source string.
124 	 */
125 	@property public string getSource(){
126 		return source;
127 	}
128 }
129 
130 public class Button : WindowElement{
131 	private bool isPressed;
132 	public bool enableRightButtonClick;
133 	public bool enableMiddleButtonClick;
134 	public this(dstring text, string source, Coordinate coordinates){
135 		position = coordinates;
136 		//sizeX = coordinates.width();
137 		//sizeY = coordinates.height();
138 		this.text = text;
139 		this.source = source;
140 		output = new BitmapDrawer(coordinates.width, coordinates.height);
141 		//brushPressed = 1;
142 		//draw();
143 	}
144 	public override void draw(){
145 		if(output.output.width != position.width || output.output.height != position.height)
146 			output = new BitmapDrawer(position.width(), position.height());
147 		if(isPressed){
148 			output.drawFilledRectangle(1, position.width()-1, 1,position.height()-1, getAvailableStyleSheet().getColor("windowinactive"));
149 			output.drawLine(0, position.width()-1, 0, 0, getAvailableStyleSheet().getColor("windowdescent"));
150 			output.drawLine(0, 0, 0, position.height()-1, getAvailableStyleSheet().getColor("windowdescent"));
151 			output.drawLine(0, position.width()-1, position.height()-1, position.height()-1, getAvailableStyleSheet().getColor("windowascent"));
152 			output.drawLine(position.width()-1, position.width()-1, 0, position.height()-1, getAvailableStyleSheet().getColor("windowascent"));
153 		}else{
154 			output.drawFilledRectangle(1, position.width()-1, 1,position.height()-1, getAvailableStyleSheet().getColor("window"));
155 			output.drawLine(0, position.width()-1, 0, 0, getAvailableStyleSheet().getColor("windowascent"));
156 			output.drawLine(0, 0, 0, position.height()-1, getAvailableStyleSheet().getColor("windowascent"));
157 			output.drawLine(0, position.width()-1, position.height()-1, position.height()-1, getAvailableStyleSheet().getColor("windowdescent"));
158 			output.drawLine(position.width()-1, position.width()-1, 0, position.height()-1, getAvailableStyleSheet().getColor("windowdescent"));
159 		}
160 
161 		output.drawColorText(position.width/2, position.height/2, text, getAvailableStyleSheet().getFontset("default"),
162 				getAvailableStyleSheet().getColor("normaltext"), FontFormat.HorizCentered | FontFormat.VertCentered);
163 		elementContainer.drawUpdate(this);
164 		if(onDraw !is null){
165 			onDraw();
166 		}
167 	}
168 	public override void onClick(int offsetX, int offsetY, int state, ubyte button){
169 		if(button == MouseButton.RIGHT && enableRightButtonClick){
170 			if(state == ButtonState.PRESSED){
171 				isPressed = true;
172 				draw();
173 				//invokeActionEvent(EventType.CLICK, -1);
174 				if(onMouseRClickPre !is null){
175 					onMouseRClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
176 				}
177 			}else{
178 				isPressed = false;
179 				draw();
180 				//invokeActionEvent(EventType.CLICK, 0);
181 				if(onMouseRClickRel !is null){
182 					onMouseRClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
183 				}
184 			}
185 		}else if(button == MouseButton.MID && enableMiddleButtonClick){
186 			if(state == ButtonState.PRESSED){
187 				isPressed = true;
188 				draw();
189 				//invokeActionEvent(EventType.CLICK, -1);
190 				if(onMouseMClickPre !is null){
191 					onMouseMClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
192 				}
193 			}else{
194 				isPressed = false;
195 				draw();
196 				//invokeActionEvent(EventType.CLICK, 0);
197 				if(onMouseMClickRel !is null){
198 					onMouseMClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
199 				}
200 			}
201 		}else{
202 			if(state == ButtonState.PRESSED){
203 				isPressed = true;
204 				draw();
205 				//invokeActionEvent(EventType.CLICK, -1);
206 				if(onMouseLClickPre !is null){
207 					onMouseLClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
208 				}
209 			}else{
210 				isPressed = false;
211 				draw();
212 				//invokeActionEvent(EventType.CLICK, 0);
213 				if(onMouseLClickRel !is null){
214 					onMouseLClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
215 				}
216 			}
217 		}
218 
219 	}
220 }
221 
222 public class SmallButton : WindowElement{
223 	private string iconPressed, iconUnpressed;
224 	private bool isPressed;
225 	public bool enableRightButtonClick;
226 	public bool enableMiddleButtonClick;
227 	public int brushPressed, brushNormal;
228 
229 	public this(string iconPressed, string iconUnpressed, string source, Coordinate coordinates){
230 		position = coordinates;
231 
232 		//this.text = text;
233 		this.source = source;
234 		this.iconPressed = iconPressed;
235 		this.iconUnpressed = iconUnpressed;
236 		output = new BitmapDrawer(coordinates.width, coordinates.height);
237 		brushPressed = 1;
238 		//draw();
239 	}
240 	public override void draw(){
241 		output.drawFilledRectangle(0, position.width()-1, 0,position.height()-1, 0);
242 		if(isPressed){
243 			output.insertBitmap(0,0,getAvailableStyleSheet().getImage(iconPressed));
244 		}else{
245 			output.insertBitmap(0,0,getAvailableStyleSheet().getImage(iconUnpressed));
246 		}
247 		elementContainer.drawUpdate(this);
248 		if(onDraw !is null){
249 			onDraw();
250 		}
251 	}
252 	public override void onClick(int offsetX, int offsetY, int state, ubyte button){
253 		if(button == MouseButton.RIGHT && enableRightButtonClick){
254 			if(state == ButtonState.PRESSED){
255 				isPressed = true;
256 				draw();
257 				//invokeActionEvent(EventType.CLICK, -1);
258 				if(onMouseRClickPre !is null){
259 					onMouseRClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
260 				}
261 			}else{
262 				isPressed = false;
263 				draw();
264 				//invokeActionEvent(EventType.CLICK, 0);
265 				if(onMouseRClickRel !is null){
266 					onMouseRClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
267 				}
268 			}
269 		}else if(button == MouseButton.MID && enableMiddleButtonClick){
270 			if(state == ButtonState.PRESSED){
271 				isPressed = true;
272 				draw();
273 				//invokeActionEvent(EventType.CLICK, -1);
274 				if(onMouseMClickPre !is null){
275 					onMouseMClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
276 				}
277 			}else{
278 				isPressed = false;
279 				draw();
280 				//invokeActionEvent(EventType.CLICK, 0);
281 				if(onMouseMClickRel !is null){
282 					onMouseMClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
283 				}
284 			}
285 		}else{
286 			if(state == ButtonState.PRESSED){
287 				isPressed = true;
288 				draw();
289 				//invokeActionEvent(EventType.CLICK, -1);
290 				if(onMouseLClickPre !is null){
291 					onMouseLClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
292 				}
293 			}else{
294 				isPressed = false;
295 				draw();
296 				//invokeActionEvent(EventType.CLICK, 0);
297 				if(onMouseLClickRel !is null){
298 					onMouseLClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
299 				}
300 			}
301 		}
302 
303 	}
304 }
305 
306 public class Label : WindowElement{
307 	public this(dstring text, string source, Coordinate coordinates){
308 		position = coordinates;
309 		this.text = text;
310 		this.source = source;
311 		output = new BitmapDrawer(coordinates.width, coordinates.height);
312 		//draw();
313 	}
314 	public override void draw(){
315 		//writeln(elementContainer);
316 		output = new BitmapDrawer(position.width, position.height);
317 		output.drawColorText(0, 0, text, getAvailableStyleSheet().getFontset("default"),
318 				getAvailableStyleSheet().getColor("normaltext"), 0);
319 		elementContainer.drawUpdate(this);
320 		if(onDraw !is null){
321 			onDraw();
322 		}
323 	}
324 	/*public override void onClick(int offsetX, int offsetY, int state, ubyte button){
325 		if(state == ButtonState.PRESSED)
326 			invokeActionEvent(EventType.CLICK, 0);
327 	}*/
328 	public override void setText(dstring s) {
329 		output.destroy();
330 		output = new BitmapDrawer(position.width, position.height);
331 		super.setText(s);
332 	}
333 	public override void onClick(int offsetX, int offsetY, int state, ubyte button){
334 		if(button == MouseButton.RIGHT){
335 			if(state == ButtonState.PRESSED){
336 				if(onMouseRClickPre !is null){
337 					onMouseRClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
338 				}
339 			}else{
340 				if(onMouseRClickRel !is null){
341 					onMouseRClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
342 				}
343 			}
344 		}else if(button == MouseButton.MID){
345 			if(state == ButtonState.PRESSED){
346 				if(onMouseMClickPre !is null){
347 					onMouseMClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
348 				}
349 			}else{
350 				if(onMouseMClickRel !is null){
351 					onMouseMClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
352 				}
353 			}
354 		}else{
355 			if(state == ButtonState.PRESSED){
356 				if(onMouseLClickPre !is null){
357 					onMouseLClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
358 				}
359 			}else{
360 				if(onMouseLClickRel !is null){
361 					onMouseLClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
362 				}
363 			}
364 		}
365 
366 	}
367 }
368 
369 public class TextBox : WindowElement, TextInputListener{
370 	private bool enableEdit, insert;
371 	private uint pos;
372 	//public int brush, textpos;
373 	//public TextInputHandler tih;
374 	public void delegate(Event ev) onTextInput;
375 
376 	public this(dstring text, string source, Coordinate coordinates){
377 		position = coordinates;
378 		this.text = text;
379 		this.source = source;
380 		output = new BitmapDrawer(coordinates.width, coordinates.height);
381 		//inputHandler.addTextInputListener(source, this);
382 		//insert = true;
383 		//draw();
384 	}
385 
386 	~this(){
387 		//inputHandler.removeTextInputListener(source);
388 	}
389 	public deprecated void addTextInputHandler(TextInputHandler t){	/** DEPRECATED. Will be removed soon in favor of static input handlers. */
390 		/*tih = t;*/
391 		//inputHandler.addTextInputListener(source, this);
392 	}
393 
394 	public override void onClick(int offsetX, int offsetY, int state, ubyte button){
395 		if(button == MouseButton.RIGHT){
396 			if(state == ButtonState.PRESSED){
397 				if(onMouseRClickPre !is null){
398 					onMouseRClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
399 				}
400 			}else{
401 				if(onMouseRClickRel !is null){
402 					onMouseRClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
403 				}
404 			}
405 		}else if(button == MouseButton.MID){
406 			if(state == ButtonState.PRESSED){
407 				if(onMouseMClickPre !is null){
408 					onMouseMClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
409 				}
410 			}else{
411 				if(onMouseMClickRel !is null){
412 					onMouseMClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
413 				}
414 			}
415 		}else{
416 			if(state == ButtonState.PRESSED){
417 				if(onMouseLClickPre !is null){
418 					onMouseLClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
419 				}
420 			}else{
421 				if(onMouseLClickRel !is null){
422 					onMouseLClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
423 				}
424 			}
425 		}
426 		if(!enableEdit && state == ButtonState.PRESSED && button == MouseButton.LEFT){
427 			//invokeActionEvent(EventType.READYFORTEXTINPUT, 0);
428 			enableEdit = true;
429 			inputHandler.startTextInput(this);
430 			draw();
431 		}
432 	}
433 	public override void draw(){
434 		if(output.output.width != position.width || output.output.height != position.height)
435 			output = new BitmapDrawer(position.width, position.height);
436 		output.drawFilledRectangle(0, position.width - 1, 0, position.height - 1, getAvailableStyleSheet().getColor("window"));
437 		output.drawRectangle(0, position.width - 1, 0, position.height - 1, getAvailableStyleSheet().getColor("windowascent"));
438 
439 		//draw cursor
440 		if(enableEdit){
441 			const int x = getAvailableStyleSheet().getFontset("default").chars[' '].xadvance ,
442 					y = getAvailableStyleSheet().getFontset("default").getSize;
443 			if(!insert)
444 				output.drawLine((x*pos) + 2, (x*pos) + 2, 2, 2 + y, getAvailableStyleSheet().getColor("selection"));
445 			else
446 				output.drawFilledRectangle((x*pos) + 2, (x * (pos + 1)) + 2, 2, 2 + y, getAvailableStyleSheet().getColor("selection"));
447 		}
448 
449 		output.drawColorText(2, 2, text, getAvailableStyleSheet().getFontset("default"),
450 				getAvailableStyleSheet().getColor("normaltext"), 0);
451 		elementContainer.drawUpdate(this);
452 		if(onDraw !is null){
453 			onDraw();
454 		}
455 	}
456 
457 	private void deleteCharacter(int n){
458 		//text = remove(text, i);
459 		dstring newtext;
460 		for(int i; i < text.length; i++){
461 			if(i != n - 1){
462 				newtext ~= text[i];
463 			}
464 		}
465 		text = newtext;
466 	}
467 	public void textInputEvent(uint timestamp, uint windowID, dstring text){
468 		//writeln(0);
469 		int j = pos;
470 		dstring newtext;
471 		for(int i ; i < pos ; i++){
472 			newtext ~= this.text[i];
473 		}
474 		for(int i ; i < 32 ; i++){
475 			if(text[i] == 0){
476 				break;
477 			}
478 			else{
479 				newtext ~= text[i];
480 				pos++;
481 				if(insert){
482 					j++;
483 				}
484 			}
485 		}
486 		for( ; j < this.text.length ; j++){
487 			newtext ~= this.text[j];
488 		}
489 		this.text = newtext;
490 		draw();
491 	}
492 
493 	public void dropTextInput(){
494 		enableEdit = false;
495 		//inputHandler.stopTextInput(source);
496 		draw();
497 		//invokeActionEvent(EventType.TEXTINPUT, 0, text);
498 		if(onTextInput !is null)
499 			onTextInput(new Event(source, null, null, null, text, 0, EventType.TEXTINPUT));
500 	}
501 
502 
503 	public void textInputKeyEvent(uint timestamp, uint windowID, TextInputKey key, ushort modifier = 0){
504 		if(key == TextInputKey.ESCAPE || key == TextInputKey.ENTER){
505 			enableEdit = false;
506 			inputHandler.stopTextInput(this);
507 			draw();
508 			//invokeActionEvent(EventType.TEXTINPUT, 0, text);
509 			if(onTextInput !is null){
510 				onTextInput(new Event(source, null, null, null, text, 0, EventType.TEXTINPUT));
511 			}
512 		}else if(key == TextInputKey.BACKSPACE){
513 			if(pos > 0){
514 				deleteCharacter(pos);
515 				pos--;
516 				draw();
517 			}
518 			/*if(pos > 0){
519 			 if(pos == text.length){
520 			 text.length--;
521 			 pos--;
522 
523 			 }
524 			 }*/
525 		}else if(key == TextInputKey.DELETE){
526 			deleteCharacter(pos + 1);
527 			draw();
528 		}else if(key == TextInputKey.CURSORLEFT){
529 			if(pos > 0){
530 				--pos;
531 				draw();
532 			}
533 		}else if(key == TextInputKey.CURSORRIGHT){
534 			if(pos < text.length){
535 				++pos;
536 				draw();
537 			}
538 		}else if(key == TextInputKey.INSERT){
539 			insert = !insert;
540 			draw();
541 		}else if(key == TextInputKey.HOME){
542 			pos = 0;
543 			draw();
544 		}else if(key == TextInputKey.END){
545 			pos = cast(uint)text.length;
546 			draw();
547 		}
548 	}
549 }
550 
551 /**
552  * Displays multiple columns of data, also provides general text input.
553  */
554 public class ListBox : WindowElement, ElementContainer{
555 	//public ListBoxColumn[] columns;
556 	public ListBoxHeader header;
557 	public ListBoxItem[] items;
558 	public int[] columnWidth;
559 	public int selection, brushHeader, brush, fontHeader;
560 	public ushort selectionColor;
561 	private bool fullRedraw, bodyDrawn, enableTextInput, textInputMode, insert, dragMid;
562 	private VSlider vSlider;
563 	private HSlider hSlider;
564 	private Slider dragSld;
565 	private int fullX, hposition, vposition, sliderX, sliderY, startY, endY, selectedColumn, textPos, previousEvent;
566 	private BitmapDrawer textArea, headerArea;
567 	private Coordinate textInputArea;
568 	public void delegate(Event ev) onTextInput;
569 	public void delegate(Event ev) onItemSelect;
570 	public void delegate(Event ev) onScrolling;
571 
572 	public this(string source, Coordinate coordinates, ListBoxItem[] items, ListBoxHeader header, int rowHeight,
573 			bool enableTextInput = false){
574 		this(source, coordinates, items, header, enableTextInput);
575 	}
576 
577 	public this(string source,Coordinate coordinates,ListBoxItem[] items,ListBoxHeader header,bool enableTextInput=false){
578 		position = coordinates;
579 		this.source = source;
580 		//this.rowHeight = rowHeight;
581 		this.items = items;
582 		this.header = header;
583 		updateColumns();
584 
585 		foreach(int i; columnWidth){
586 			fullX += i;
587 		}
588 		if(fullX < position.width()){
589 			fullX = position.width();
590 		}
591 
592 		output = new BitmapDrawer(position.width, position.height);
593 
594 		/*int foo = cast(uint)(rowHeight * this.items.length);
595 		if(foo < position.height())
596 			foo = position.width();*/
597 		this.enableTextInput = enableTextInput;
598 		//inputHandler.addTextInputListener(source, this);
599 
600 	}
601 	/*~this(){
602 		if(!(inputHandler is null))
603 			inputHandler.removeTextInputListener(source);
604 	}*/
605 	/*public void actionEvent(Event event){
606 		if(event.source == "textInput"){
607 			items[selection].setText(selectedColumn, event.text);
608 			invokeActionEvent(new Event(source, null, null, null, event.text, selection,EventType.TEXTINPUT, items[selection]));
609 			updateColumns();
610 			draw();
611 		}else{
612 			draw();
613 		}
614 	}*/
615 	private void textInput(Event ev){
616 		items[selection].setText(selectedColumn, ev.text);
617 		//invokeActionEvent(new Event(source, null, null, null, event.text, selection,EventType.TEXTINPUT, items[selection]));
618 		if(onTextInput !is null){
619 			onTextInput(new Event(source, null, null, null, ev.text, selection,EventType.TEXTINPUT, items[selection]));
620 		}
621 		updateColumns();
622 		draw();
623 	}
624 	private void scrollHoriz(Event ev){
625 		draw();
626 		if(onScrolling !is null){
627 			onScrolling(ev);
628 		}
629 	}
630 	private void scrollVert(Event ev){
631 		draw();
632 		if(onScrolling !is null){
633 			onScrolling(ev);
634 		}
635 	}
636 	public void drawUpdate(WindowElement sender){
637 		output.insertBitmap(sender.getPosition().left,sender.getPosition().top,sender.output.output);
638 
639 		if(!fullRedraw){
640 			/*output.insertBitmapSlice(0,0,headerArea.output,Coordinate(vposition,0,vposition + fullX - 1, rowHeight - 1));
641 			output.insertBitmapSlice(0, rowHeight, textArea.output, Coordinate(vposition,hposition * rowHeight,vposition + fullX - 1 , hposition * rowHeight + (sizeY - hSlider.getPosition().getYSize) - rowHeight));*/
642 			elementContainer.drawUpdate(this);
643 		}
644 	}
645 	public Coordinate getAbsolutePosition(WindowElement sender){
646 		return sender.position;
647 	}
648 	/**
649 	 * Updates the columns with the given data.
650 	 */
651 	public void updateColumns(ListBoxItem[] items){
652 		this.items = items;
653 		updateColumns();
654 		draw();
655 	}
656 	/**
657 	 * Updates the columns with the given data and header.
658 	 */
659 	public void updateColumns(ListBoxItem[] items, ListBoxHeader header){
660 		this.items = items;
661 		this.header = header;
662 		updateColumns();
663 		draw();
664 	}
665 	/**
666 	 * Clears the content of the ListBox.
667 	 */
668 	public void clearData(){
669 		items.length = 0;
670 		updateColumns();
671 		draw();
672 	}
673 	public void updateColumns(){
674 		const int rowHeight = getStyleSheet().drawParameters["ListBoxRowHeight"];
675 		fullX = header.getFullWidth();
676 		selection = 0;
677 
678 		if(fullX < position.width()){
679 			fullX = cast(int)position.width();
680 		}
681 		int foo2 = cast(int)(rowHeight * this.items.length);
682 		if(foo2 < position.height())
683 			foo2 = position.height();
684 
685 		textArea = new BitmapDrawer(fullX, foo2);
686 		headerArea = new BitmapDrawer(fullX, rowHeight);
687 
688 		this.vSlider = new VSlider(cast(uint)items.length - 1, ((position.height()-17-rowHeight) / rowHeight), "vslider",
689 				Coordinate(position.width() - 16, 0, position.width(), position.height() - 16));
690 		this.hSlider = new HSlider(fullX - 16, position.width() - 16, "hslider", Coordinate(0, position.height() - 16,
691 				position.width() - 16, position.height()));
692 		this.vSlider.onScrolling = &scrollVert;
693 		this.vSlider.elementContainer = this;
694 		sliderX = vSlider.getX();
695 
696 
697 		this.hSlider.onScrolling = &scrollHoriz;
698 		this.hSlider.elementContainer = this;
699 		sliderY = hSlider.getY();
700 		bodyDrawn = false;
701 	}
702 
703 	public StyleSheet getStyleSheet(){
704 		return getAvailableStyleSheet;
705 	}
706 
707 	private void drawBody(){
708 		const int rowHeight = getStyleSheet().drawParameters["ListBoxRowHeight"];
709 		int foo;
710 		for(int i; i < header.getNumberOfColumns(); i++){
711 			int bar;
712 			for(int j; j < items.length; j++){
713 				//writeln(foo + 1, bar);
714 				textArea.drawColorText(foo + 1, bar, items[j].getText(i), getStyleSheet().getFontset("default"),
715 						getAvailableStyleSheet().getColor("normaltext"), 0);
716 
717 				bar += rowHeight;
718 			}
719 			foo += header.getColumnWidth(i);
720 
721 			textArea.drawLine(foo, foo, 0, textArea.output.height-2, getStyleSheet().getColor("windowascent"));
722 		}
723 	}
724 
725 	public override void draw(){
726 		const int rowHeight = getStyleSheet().drawParameters["ListBoxRowHeight"];
727 		if(output.output.width != position.width || output.output.height != position.height){
728 			output = new BitmapDrawer(position.width(), position.height());
729 			bodyDrawn = false;
730 			updateColumns();
731 		}
732 		fullRedraw = true;
733 		int areaX, areaY;
734 
735 		vposition = vSlider.value;
736 		areaX = position.width - vSlider.getPosition().width();
737 
738 
739 		hposition = hSlider.value;
740 		areaY = position.height - hSlider.getPosition().height();
741 
742 		output.drawFilledRectangle(0, position.width(), 0, position.height(),getStyleSheet().getColor("window"));
743 		output.drawRectangle(0, position.width() - 1, 0, position.height() - 1,getStyleSheet().getColor("windowascent"));
744 
745 
746 		// draw the header
747 		// TODO: Draw the header only once!!!
748 		output.drawLine(0, position.width() - 1, rowHeight, rowHeight, getStyleSheet().getColor("windowascent"));
749 		int foo;
750 		for(int i; i < header.getNumberOfColumns(); i++){
751 			headerArea.drawColorText(foo + 1, 0, header.getText(i), getStyleSheet().getFontset("default"),
752 					getAvailableStyleSheet().getColor("normaltext"), 0);
753 			foo += header.getColumnWidth(i);
754 			headerArea.drawLine(foo, foo, 0, rowHeight - 2, getStyleSheet().getColor("windowascent"));
755 		}
756 
757 		output.insertBitmapSlice(0,0,headerArea.output,Coordinate(hposition,0,hposition + position.width() - 17,
758 				rowHeight - 1));
759 
760 		//draw the selector
761 		if(selection - vposition >= 0 && vposition + ((position.height()-17-rowHeight) / rowHeight) >= selection &&
762 				items.length != 0)
763 			output.drawFilledRectangle(1, position.width() - 2, rowHeight + (rowHeight * (selection - vposition)),
764 					(rowHeight * 2) + (rowHeight * (selection - vposition)), getStyleSheet().getColor("selection"));
765 
766 		// draw the body
767 		if(!bodyDrawn){
768 			bodyDrawn = true;
769 			drawBody();
770 		}
771 		//writeln(textArea.output.getX(),textArea.output.getY());
772 		output.insertBitmapSlice(0, rowHeight, textArea.output, Coordinate(hposition,vposition * rowHeight,hposition +
773 				position.width() - 17 , vposition * rowHeight + areaY - rowHeight));
774 
775 		vSlider.draw();
776 		hSlider.draw();
777 		elementContainer.drawUpdate(this);
778 
779 		fullRedraw = false;
780 		if(onDraw !is null){
781 			onDraw();
782 		}
783 	}
784 	public override void onClick(int offsetX, int offsetY, int state, ubyte button){
785 		const int rowHeight = getStyleSheet().drawParameters["ListBoxRowHeight"];
786 		if(button == MouseButton.RIGHT){
787 			if(state == ButtonState.PRESSED){
788 				if(onMouseRClickPre !is null){
789 					onMouseRClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
790 				}
791 			}else{
792 				if(onMouseRClickRel !is null){
793 					onMouseRClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
794 				}
795 			}
796 		}else if(button == MouseButton.MID){
797 			if(state == ButtonState.PRESSED){
798 				if(onMouseMClickPre !is null){
799 					onMouseMClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
800 				}
801 			}else{
802 				if(onMouseMClickRel !is null){
803 					onMouseMClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
804 				}
805 			}
806 		}else{
807 			if(state == ButtonState.PRESSED){
808 				if(onMouseLClickPre !is null){
809 					onMouseLClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
810 				}
811 			}else{
812 				if(onMouseLClickRel !is null){
813 					onMouseLClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
814 				}
815 			}
816 		}
817 		if(state == ButtonState.PRESSED){
818 			if(button == MouseButton.LEFT){
819 				if(offsetX > (vSlider.getPosition().left) && offsetY > (vSlider.getPosition().top)){
820 					vSlider.onClick(offsetX - vSlider.getPosition().left, offsetY - vSlider.getPosition().top, state, button);
821 					dragSld = vSlider;
822 					return;
823 
824 				}else if(offsetX > (hSlider.getPosition().left) && offsetY > (hSlider.getPosition().top)){
825 					hSlider.onClick(offsetX - hSlider.getPosition().left, offsetY - hSlider.getPosition().top, state, button);
826 					dragSld = hSlider;
827 					return;
828 
829 				}else if(offsetY > rowHeight && button == MouseButton.LEFT){
830 					offsetY -= rowHeight;
831 					//writeln(selection);
832 					if(selection == (offsetY / rowHeight) + vposition){
833 						//invokeActionEvent(EventType.TEXTBOXSELECT, (offsetY / rowHeight) + vposition);
834 						if(!enableTextInput){
835 							//invokeActionEvent(new Event(source, null, null, null, null, (offsetY / rowHeight) + vposition,EventType.TEXTBOXSELECT, items[selection]));
836 							if(onItemSelect !is null){
837 								onItemSelect(new Event(source, null, null, null, null, (offsetY / rowHeight) + vposition,
838 										EventType.TEXTBOXSELECT, items[selection]));
839 							}
840 						}else{
841 							offsetX += hposition;
842 							selectedColumn = header.getColumnNumFromX(offsetX);
843 							//writeln(offsetX);
844 							if(selectedColumn != -1){
845 								if(items[selection].getTextInputType(selectedColumn) != TextInputType.DISABLE){
846 									text = items[selection].getText(selectedColumn);
847 									//invokeActionEvent(EventType.READYFORTEXTINPUT,selectedColumn);
848 									PopUpTextInput p = new PopUpTextInput("textInput",text,Coordinate(0,0,header.getColumnWidth(selectedColumn),20));
849 									p.onTextInput = &textInput;
850 									popUpHandler.addPopUpElement(p);
851 									/+textInputArea = Coordinate(header.getRangeWidth(0, selectedColumn), (selection + 1) * rowHeight /*- hposition*/,
852 													header.getRangeWidth(0, selectedColumn + 1), (selection + 2) * rowHeight /*- hposition*/);
853 									writeln(textInputArea);+/
854 
855 								}/*else{*/
856 								if(onItemSelect !is null){
857 									onItemSelect(new Event(source, null, null, null, null, (offsetY / rowHeight) + vposition,
858 											EventType.TEXTBOXSELECT, items[selection]));
859 									/*}*/
860 								}
861 							}
862 						}
863 					}else{
864 						if((offsetY / rowHeight) + vposition < items.length){
865 							selection = (offsetY / rowHeight) + vposition;
866 							draw();
867 						}
868 					}
869 				}
870 			}else if(button == MouseButton.MID){
871 				dragMid = true;
872 			}
873 		}else{
874 			dragMid = false;
875 			dragSld = null;
876 		}
877 	}
878 	public override void onDrag(int x, int y, int relX, int relY, ubyte button){
879 		if(dragMid){
880 			hSlider.value += x;
881 			vSlider.value += y;
882 		}else if(dragSld){
883 			dragSld.onDrag(x,y,relX,relY,button);
884 		}
885 	}
886 	public override void onScroll(int x, int y, int wX, int wY){
887 		if(textInputMode) return;
888 		vSlider.onScroll(x,y,0,0);
889 		hSlider.onScroll(x,y,0,0);
890 	}
891 	/**
892 	 * Returns a line.
893 	 */
894 	public @nogc ListBoxItem readLine(int line){
895 		return items[line];
896 	}
897 	/**
898 	 * Adds a line to the bottom of the list.
899 	 */
900 	public void addLine(ListBoxItem i){
901 		items ~= i;
902 	}
903 	/**
904 	 * Inserts a line to a given point of the list.
905 	 */
906 	public void addLine(ListBoxItem i, int n){
907 		if(n == items.length){
908 			items ~= i;
909 		}else{
910 			items.length++;
911 			for(int j = cast(int)items.length - 1; j > n; j++){
912 				items[j] = items[j - 1];
913 			}
914 			items[n] = i;
915 		}
916 	}
917 	/**
918 	 * Removes a line from the list.
919 	 */
920 	public void removeLine(int n){
921 		items.remove(n);
922 	}
923 	public void clearArea(WindowElement sender){
924 
925 	}
926 }
927 /**
928  * A simple toggle button.
929  */
930 public class CheckBox : WindowElement{
931 	public int iconChecked, iconUnchecked;
932 	private bool checked;
933 	public int[] brush;
934 	public void delegate(Event ev) onToggle;
935 
936 	public this(dstring text, string source, Coordinate coordinates, bool checked = false){
937 		position = coordinates;
938 		this.text = text;
939 		this.source = source;
940 		brush ~= 2;
941 		brush ~= 3;
942 		output = new BitmapDrawer(position.width, position.height);
943 		this.checked = checked;
944 		//draw();
945 	}
946 
947 	public override void draw(){
948 		if(output.output.width != position.width || output.output.height != position.height)
949 			output = new BitmapDrawer(position.width, position.height);
950 		output.drawRectangle(getAvailableStyleSheet().getImage("checkBoxA").width, output.output.width - 1, 0,
951 				output.output.height - 1, 0x0);
952 		output.drawColorText(getAvailableStyleSheet().getImage("checkBoxA").width, 0, text,
953 				getAvailableStyleSheet().getFontset("default"), getAvailableStyleSheet().getColor("normaltext"), 0);
954 		if(checked){
955 			output.insertBitmap(0, 0, getAvailableStyleSheet().getImage("checkBoxB"));
956 		}else{
957 			output.insertBitmap(0, 0, getAvailableStyleSheet().getImage("checkBoxA"));
958 		}
959 		elementContainer.drawUpdate(this);
960 		if(onDraw !is null){
961 			onDraw();
962 		}
963 	}
964 
965 	public override void onClick(int offsetX, int offsetY, int state, ubyte button){
966 		/*if(state == ButtonState.PRESSED && button == MouseButton.LEFT){
967 			checked = !checked;
968 			draw();
969 			invokeActionEvent(EventType.CHECKBOX, checked);
970 		}*/
971 		if(button == MouseButton.RIGHT){
972 			if(state == ButtonState.PRESSED){
973 				if(onMouseRClickPre !is null){
974 					onMouseRClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
975 				}
976 			}else{
977 				if(onMouseRClickRel !is null){
978 					onMouseRClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
979 				}
980 			}
981 		}else if(button == MouseButton.MID){
982 			if(state == ButtonState.PRESSED){
983 				if(onMouseMClickPre !is null){
984 					onMouseMClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
985 				}
986 			}else{
987 				if(onMouseMClickRel !is null){
988 					onMouseMClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
989 				}
990 			}
991 		}else{
992 			if(state == ButtonState.PRESSED){
993 				checked = !checked;
994 				draw();
995 				if(onMouseLClickPre !is null){
996 					onMouseLClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
997 				}
998 				if(onToggle !is null){
999 					onToggle(new Event(source, null, null, null, null, checked ? 1 : 0, EventType.CHECKBOX));
1000 				}
1001 			}else{
1002 				if(onMouseLClickRel !is null){
1003 					onMouseLClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1004 				}
1005 			}
1006 		}
1007 	}
1008 	/**
1009 	 * Returns the current value (whether it's checked or not) as a boolean.
1010 	 */
1011 	public @nogc @property bool value(){
1012 		return checked;
1013 	}
1014 	/**
1015 	 * Sets the new value (whether it's checked or not) as a boolean.
1016 	 */
1017 	public @property bool value(bool b){
1018 		checked = b;
1019 		draw();
1020 		return checked;
1021 	}
1022 }
1023 /**
1024  * Radio buttons, for selecting from multiple options.
1025  */
1026 public class RadioButtonGroup : WindowElement{
1027 	public int iconChecked, iconUnchecked;
1028 	private int bposition, rowHeight, buttonpos;
1029 	public dstring[] options;
1030 	public int[] brush;
1031 	public ushort border, background;
1032 	public void delegate(Event ev) onToggle;
1033 
1034 	public this(dstring text, string source, Coordinate coordinates, dstring[] options, int rowHeight, int buttonpos){
1035 		this.position = coordinates;
1036 		this.text = text;
1037 		this.source = source;
1038 		this.options = options;
1039 		this.rowHeight = rowHeight;
1040 		brush ~= 4;
1041 		brush ~= 5;
1042 		output = new BitmapDrawer(position.width, position.height);
1043 		//draw();
1044 	}
1045 
1046 	public override void draw(){
1047 		//output.drawFilledRectangle(0, sizeX-1, 0, sizeY-1, background);
1048 		if(output.output.width != position.width || output.output.height != position.height)
1049 			output = new BitmapDrawer(position.width, position.height);
1050 		output.drawRectangle(0, position.width-1, 0, position.height-1, getAvailableStyleSheet().getColor("windowascent"));
1051 		output.drawColorText(16,0,text, getAvailableStyleSheet().getFontset("default"),
1052 				getAvailableStyleSheet().getColor("normaltext"),1);
1053 		for(int i; i < options.length; i++){
1054 
1055 			output.drawColorText(16, rowHeight * (i+1),options[i],getAvailableStyleSheet().getFontset("default"),
1056 					getAvailableStyleSheet().getColor("normaltext"),0);
1057 			if(bposition == i){
1058 				output.insertBitmap(1, rowHeight * (i+1),getAvailableStyleSheet.getImage("radioButtonB"));
1059 			}else{
1060 				output.insertBitmap(1, rowHeight * (i+1),getAvailableStyleSheet.getImage("radioButtonA"));
1061 			}
1062 		}
1063 		elementContainer.drawUpdate(this);
1064 		if(onDraw !is null){
1065 			onDraw();
1066 		}
1067 	}
1068 
1069 	public override void onClick(int offsetX, int offsetY, int state, ubyte button){
1070 		if(button == MouseButton.RIGHT){
1071 			if(state == ButtonState.PRESSED){
1072 				if(onMouseRClickPre !is null){
1073 					onMouseRClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
1074 				}
1075 			}else{
1076 				if(onMouseRClickRel !is null){
1077 					onMouseRClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1078 				}
1079 			}
1080 		}else if(button == MouseButton.MID){
1081 			if(state == ButtonState.PRESSED){
1082 				if(onMouseMClickPre !is null){
1083 					onMouseMClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
1084 				}
1085 			}else{
1086 				if(onMouseMClickRel !is null){
1087 					onMouseMClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1088 				}
1089 			}
1090 		}else{
1091 			if(state == ButtonState.PRESSED){
1092 				if(onMouseLClickPre !is null){
1093 					onMouseLClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
1094 				}
1095 				bposition = (offsetY) / 16;
1096 				bposition--;
1097 				draw();
1098 				if(onToggle !is null){
1099 					onToggle(new Event(source, null, null, null, null, bposition, EventType.RADIOBUTTON));
1100 				}
1101 			}else{
1102 				if(onMouseLClickRel !is null){
1103 					onMouseLClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1104 				}
1105 			}
1106 		}
1107 	}
1108 	public @property @nogc int value(){
1109 		return bposition;
1110 	}
1111 	public @property int value(int newval){
1112 		bposition = newval;
1113 		draw();
1114 		return bposition;
1115 	}
1116 }
1117 
1118 abstract class Slider : WindowElement{
1119 	public int[] brush;
1120 
1121 	public int value, maxValue, barLength;
1122 	public void delegate(Event ev) onScrolling;
1123 
1124 	/**
1125 	 * Returns the slider position. If barLenght > 1, then it returns the lower value.
1126 	 */
1127 	public @nogc @property int sliderPosition(){
1128 		return value;
1129 	}
1130 	public @property int sliderPosition(int newval){
1131 		if(newval < maxValue){
1132 			value = newval;
1133 			draw();
1134 		}
1135 		return value;
1136 	}
1137 }
1138 /**
1139  * Vertical slider.
1140  */
1141 public class VSlider : Slider{
1142 	//public int[] brush;
1143 
1144 	//private int value, maxValue, barLength;
1145 
1146 	public this(int maxValue, int barLenght, string source, Coordinate coordinates){
1147 		position = coordinates;
1148 		//this.text = text;
1149 		this.source = source;
1150 		this.maxValue = maxValue;
1151 		this.barLength = barLenght;
1152 		output = new BitmapDrawer(position.width, position.height);
1153 		brush ~= 6;
1154 		brush ~= 8;
1155 		//brush ~= 10;
1156 		//draw();
1157 	}
1158 	public override void draw(){
1159 		//draw background
1160 		//Bitmap16Bit sliderStyle = elementContainer.getStyleBrush(brush[2]);
1161 		//ushort backgroundColor = sliderStyle.readPixel(0,0), sliderColor = sliderStyle.readPixel(1,0);
1162 		if(output.output.width != position.width || output.output.height != position.height)
1163 			output = new BitmapDrawer(position.width, position.height);
1164 		output.drawFilledRectangle(0, position.width , 0, position.height , getAvailableStyleSheet.getColor("windowinactive"));
1165 		//draw upper arrow
1166 		output.insertBitmap(0,0,getAvailableStyleSheet.getImage("upArrowA"));
1167 		//draw lower arrow
1168 		output.insertBitmap(0, position.height - getAvailableStyleSheet.getImage("downArrowA").height,getAvailableStyleSheet.getImage("downArrowA"));
1169 		//draw slider
1170 		if(maxValue > barLength){
1171 			double sliderlength = position.height() - (getAvailableStyleSheet.getImage("upArrowA")).height*2, unitlength = sliderlength/maxValue;
1172 			double sliderpos = unitlength * value, bl = unitlength * barLength;
1173 			int posA = to!int(sliderpos) + getAvailableStyleSheet.getImage("upArrowA").height, posB = to!int(bl + sliderpos) + getAvailableStyleSheet.getImage("upArrowA").height;
1174 
1175 			output.drawFilledRectangle(0,position.width,posA, posB, getAvailableStyleSheet.getColor("windowascent"));
1176 		}
1177 		elementContainer.drawUpdate(this);
1178 		if(onDraw !is null){
1179 			onDraw();
1180 		}
1181 	}
1182 
1183 
1184 	public override void onClick(int offsetX, int offsetY, int state, ubyte button){
1185 		if(button == MouseButton.RIGHT){
1186 			if(state == ButtonState.PRESSED){
1187 				if(onMouseRClickPre !is null){
1188 					onMouseRClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
1189 				}
1190 			}else{
1191 				if(onMouseRClickRel !is null){
1192 					onMouseRClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1193 				}
1194 			}
1195 		}else if(button == MouseButton.MID){
1196 			if(state == ButtonState.PRESSED){
1197 				if(onMouseMClickPre !is null){
1198 					onMouseMClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
1199 				}
1200 			}else{
1201 				if(onMouseMClickRel !is null){
1202 					onMouseMClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1203 				}
1204 			}
1205 		}else{
1206 			if(state == ButtonState.PRESSED){
1207 				if(onMouseLClickPre !is null){
1208 					onMouseLClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
1209 				}
1210 				if(offsetY <= getAvailableStyleSheet.getImage("upArrowA").height){
1211 					if(value != 0) value--;
1212 				}else if(position.height-getAvailableStyleSheet.getImage("upArrowA").height <= offsetY){
1213 					if(value < maxValue - barLength) value++;
1214 				}else{
1215 					offsetY -= getAvailableStyleSheet.getImage("upArrowA").height;
1216 					double sliderlength = position.height() - (getAvailableStyleSheet.getImage("upArrowA").height*2), unitlength = sliderlength/maxValue;
1217 					int v = to!int(offsetY / unitlength);
1218 					//value = ((sizeY - (elementContainer.getStyleBrush(brush[1]).getY() * 2)) - offsetY) * (value / maxValue);
1219 					if(v < maxValue - barLength) value = v;
1220 					else value = maxValue - barLength;
1221 
1222 				}
1223 				draw();
1224 				if(onScrolling !is null){
1225 					onScrolling(new Event(source, null, null, null, null, value, EventType.SLIDER));
1226 				}
1227 			}else{
1228 				if(onMouseLClickRel !is null){
1229 					onMouseLClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1230 				}
1231 			}
1232 		}
1233 
1234 	}
1235 	public override void onScroll(int x, int y, int wX, int wY){
1236 
1237 		if(x == 1){
1238 			if(value != 0) value--;
1239 		}else if(x == -1){
1240 			if(value < maxValue - barLength) value++;
1241 		}
1242 		draw();
1243 		if(onScrolling !is null){
1244 			onScrolling(new Event(source, null, null, null, null, value, EventType.SLIDER));
1245 		}
1246 	}
1247 	override public void onDrag(int x,int y,int relX,int relY,ubyte button) {
1248 		value+=relY;
1249 		if(value >= maxValue - barLength)
1250 			value = maxValue;
1251 		else if(value < 0)
1252 			value = 0;
1253 		draw();
1254 		if(onScrolling !is null){
1255 			onScrolling(new Event(source, null, null, null, null, value, EventType.SLIDER));
1256 		}
1257 
1258 	}
1259 }
1260 /**
1261  * Horizontal slider.
1262  */
1263 public class HSlider : Slider{
1264 	public this(int maxValue, int barLenght, string source, Coordinate coordinates){
1265 		position = coordinates;
1266 		//this.text = text;
1267 		this.source = source;
1268 		this.maxValue = maxValue;
1269 		this.barLength = barLenght;
1270 		//writeln(barLenght,',',maxValue);
1271 		output = new BitmapDrawer(position.width, position.height);
1272 		brush ~= 14;
1273 		brush ~= 16;
1274 		//brush ~= 10;
1275 		//draw();
1276 	}
1277 	public override void draw(){
1278 		//draw background
1279 		//Bitmap16Bit sliderStyle = elementContainer.getStyleBrush(brush[2]);
1280 		//ushort backgroundColor = sliderStyle.readPixel(0,0), sliderColor = sliderStyle.readPixel(1,0);
1281 		if(output.output.width != position.width || output.output.height != position.height)
1282 			output = new BitmapDrawer(position.width, position.height);
1283 		output.drawFilledRectangle(0, position.width , 0, position.height , getAvailableStyleSheet().getColor("windowinactive"));
1284 		//draw left arrow
1285 		output.insertBitmap(0,0,getAvailableStyleSheet.getImage("leftArrowA"));
1286 		//draw right arrow
1287 		output.insertBitmap(position.width - getAvailableStyleSheet.getImage("rightArrowA").width,0,getAvailableStyleSheet.getImage("rightArrowA"));
1288 		//draw slider
1289 		if(maxValue > barLength){
1290 			double sliderlength = position.width() - (getAvailableStyleSheet.getImage("rightArrowA").width*2), unitlength = sliderlength/maxValue;
1291 			double sliderpos = unitlength * value, bl = unitlength * barLength;
1292 
1293 			int posA = to!int(sliderpos) + getAvailableStyleSheet.getImage("rightArrowA").height, posB = to!int(bl + sliderpos) + getAvailableStyleSheet.getImage("rightArrowA").height;
1294 
1295 			output.drawFilledRectangle(posA, posB, 0, position.height(),getAvailableStyleSheet().getColor("windowascent"));
1296 		}
1297 		elementContainer.drawUpdate(this);
1298 		if(onDraw !is null){
1299 			onDraw();
1300 		}
1301 	}
1302 	public override void onClick(int offsetX, int offsetY, int state, ubyte button){
1303 		if(button == MouseButton.RIGHT){
1304 			if(state == ButtonState.PRESSED){
1305 				if(onMouseRClickPre !is null){
1306 					onMouseRClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
1307 				}
1308 			}else{
1309 				if(onMouseRClickRel !is null){
1310 					onMouseRClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1311 				}
1312 			}
1313 		}else if(button == MouseButton.MID){
1314 			if(state == ButtonState.PRESSED){
1315 				if(onMouseMClickPre !is null){
1316 					onMouseMClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
1317 				}
1318 			}else{
1319 				if(onMouseMClickRel !is null){
1320 					onMouseMClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1321 				}
1322 			}
1323 		}else{
1324 			if(state == ButtonState.PRESSED){
1325 				if(onMouseLClickPre !is null){
1326 					onMouseLClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
1327 				}
1328 				if(offsetX <= getAvailableStyleSheet.getImage("rightArrowA").width){
1329 					if(value != 0) value--;
1330 				}
1331 				else if(position.width-getAvailableStyleSheet.getImage("rightArrowA").width <= offsetX){
1332 					if(value < maxValue - barLength) value++;
1333 				}
1334 				else{
1335 					offsetX -= getAvailableStyleSheet.getImage("rightArrowA").width;
1336 					double sliderlength = position.width() - (elementContainer.getStyleSheet.getImage("rightArrowA").width*2), unitlength = sliderlength/maxValue;
1337 					int v = to!int(offsetX / unitlength);
1338 					if(v < maxValue - barLength) value = v;
1339 					else value = maxValue - barLength;
1340 				}
1341 				draw();
1342 				if(onScrolling !is null){
1343 					onScrolling(new Event(source, null, null, null, null, value, EventType.SLIDER));
1344 				}
1345 			}else{
1346 				if(onMouseLClickRel !is null){
1347 					onMouseLClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1348 				}
1349 			}
1350 		}
1351 	}
1352 	public override void onScroll(int x, int y, int wX, int wY){
1353 		if(y == -1){
1354 			if(value != 0) value--;
1355 		}else if(y == 1){
1356 			if(value < maxValue - barLength) value++;
1357 		}
1358 		draw();
1359 		if(onScrolling.ptr){
1360 			onScrolling(new Event(source, null, null, null, null, value, EventType.SLIDER));
1361 		}
1362 	}
1363 	override public void onDrag(int x,int y,int relX,int relY,ubyte button) {
1364 		value+=relX;
1365 		if(value >= maxValue - barLength)
1366 			value = maxValue;
1367 		else if(value < 0)
1368 			value = 0;
1369 		draw();
1370 		if(onScrolling.ptr){
1371 			onScrolling(new Event(source, null, null, null, null, value, EventType.SLIDER));
1372 		}
1373 	}
1374 
1375 }
1376 /**
1377  * Menubar containing menus in a tree-like structure.
1378  */
1379 public class MenuBar: WindowElement{
1380 	private PopUpMenuElement[] menus;
1381 	//private wstring[] menuNames;
1382 	private int[] menuWidths;
1383 	//private PopUpHandler popUpHandler;
1384 	private int select, usedWidth;
1385 	public this(string source, Coordinate position, PopUpMenuElement[] menus){
1386 		this.source = source;
1387 		this.position = position;
1388 		//this.popUpHandler = popUpHandler;
1389 		this.menus = menus;
1390 		select = -1;
1391 	}
1392 	public override void draw() {
1393 		StyleSheet ss = getAvailableStyleSheet();
1394 		Fontset!Bitmap8Bit f = ss.getFontset("default");
1395 		if (output is null){
1396 			usedWidth = 1;
1397 			output = new BitmapDrawer(position.width(),position.height());
1398 			foreach(m ; menus){
1399 				menuWidths ~= usedWidth;
1400 				usedWidth += f.getTextLength(m.text) + (ss.drawParameters["MenuBarHorizPadding"] * 2);
1401 
1402 			}
1403 			output.drawFilledRectangle(0, position.width(), 0, position.height(), ss.getColor("window"));
1404 		}else{
1405 			output.drawFilledRectangle(0, usedWidth, 0, position.height(), ss.getColor("window"));
1406 		}
1407 		if(select != -1){
1408 
1409 		}
1410 		int x = ss.drawParameters["MenuBarHorizPadding"] + 1;
1411 		foreach(m ; menus){
1412 			output.drawColorText(x, ss.drawParameters["MenuBarVertPadding"],m.text,f,ss.getColor("normaltext"),0);
1413 			x += f.getTextLength(m.text) + ss.drawParameters["MenuBarHorizPadding"];
1414 			//output.drawLine(x, x, 0, position.height() - 1, ss.getColor("MenuBarSeparatorColor"));
1415 			x += ss.drawParameters["MenuBarHorizPadding"];
1416 		}
1417 		output.drawLine(0, 0, 0, position.height()-1, ss.getColor("windowascent"));
1418 		output.drawLine(0, position.width()-1, 0, 0, ss.getColor("windowascent"));
1419 		output.drawLine(0, position.width()-1, position.height()-1, position.height()-1, ss.getColor("windowdescent"));
1420 		output.drawLine(position.width()-1, position.width()-1, 0, position.height()-1, ss.getColor("windowdescent"));
1421 		elementContainer.drawUpdate(this);
1422 		if(onDraw !is null){
1423 			onDraw();
1424 		}
1425 	}
1426 	private void redirectIncomingEvents(Event ev){
1427 		if(onMouseLClickPre !is null){
1428 			onMouseLClickPre(ev);
1429 		}
1430 	}
1431 	override public void onClick(int offsetX,int offsetY,int state,ubyte button){
1432 		if(button == MouseButton.RIGHT){
1433 			if(state == ButtonState.PRESSED){
1434 				if(onMouseRClickPre !is null){
1435 					onMouseRClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
1436 				}
1437 			}else{
1438 				if(onMouseRClickRel !is null){
1439 					onMouseRClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1440 				}
1441 			}
1442 		}else if(button == MouseButton.MID){
1443 			if(state == ButtonState.PRESSED){
1444 				if(onMouseMClickPre !is null){
1445 					onMouseMClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
1446 				}
1447 			}else{
1448 				if(onMouseMClickRel !is null){
1449 					onMouseMClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1450 				}
1451 			}
1452 		}else{
1453 			if(state == ButtonState.PRESSED){
1454 				if(onMouseLClickPre !is null){
1455 					onMouseLClickPre(new Event(source, null, null, null, null, button, EventType.CLICK));
1456 				}
1457 			}else{
1458 				if(onMouseLClickRel !is null){
1459 					onMouseLClickRel(new Event(source, null, null, null, null, button, EventType.CLICK));
1460 				}
1461 			}
1462 		}
1463 		//writeln(onMouseLClickPre);
1464 		if(offsetX < usedWidth && button == MouseButton.LEFT && state == ButtonState.PRESSED){
1465 			for(int i = cast(int)menuWidths.length - 1 ; i >= 0 ; i--){
1466 				if(menuWidths[i] < offsetX){
1467 					PopUpMenu p = new PopUpMenu(menus[i].getSubElements(), menus[i].source);
1468 					//p.al = al;
1469 					p.onMouseClick = onMouseLClickPre;//&redirectIncomingEvents;
1470 					Coordinate c = elementContainer.getAbsolutePosition(this);
1471 					popUpHandler.addPopUpElement(p, c.left + menuWidths[i], position.height());
1472 					return;
1473 				}
1474 			}
1475 		}
1476 
1477 	}
1478 
1479 }
1480 /**
1481  * For creating pop-up elements like menus.
1482  */
1483 public abstract class PopUpElement{
1484 	//public ActionListener[] al;
1485 	public BitmapDrawer output;
1486 	public static InputHandler inputhandler;
1487 	public static StyleSheet styleSheet;
1488 	public Coordinate coordinates;
1489 	public StyleSheet customStyle;
1490 	protected PopUpHandler parent;
1491 	protected string source;
1492 	protected dstring text;
1493 	/*public void delegate(Event ev) onMouseLClickRel;
1494 	public void delegate(Event ev) onMouseRClickRel;
1495 	public void delegate(Event ev) onMouseMClickRel;
1496 	public void delegate(Event ev) onMouseHover;
1497 	public void delegate(Event ev) onMouseMove;
1498 	public void delegate(Event ev) onMouseLClickPre;
1499 	public void delegate(Event ev) onMouseRClickPre;
1500 	public void delegate(Event ev) onMouseMClickPre;*/
1501 
1502 	public static void delegate() onDraw;
1503 	public void delegate(Event ev) onMouseClick;
1504 
1505 	public abstract void draw();
1506 
1507 	public void onClick(int offsetX, int offsetY, int type = 0){
1508 
1509 	}
1510 	public void onScroll(int x, int y, int wX, int wY){
1511 
1512 	}
1513 	public void onMouseMovement(int x, int y){
1514 
1515 	}
1516 	public void addParent(PopUpHandler p){
1517 		parent = p;
1518 	}
1519 
1520 	protected StyleSheet getStyleSheet(){
1521 		if(customStyle !is null){
1522 			return customStyle;
1523 		}
1524 		if(styleSheet !is null){
1525 			return styleSheet;
1526 		}
1527 		return parent.getStyleSheet();
1528 	}
1529 	/*protected void invokeActionEvent(Event e){
1530 		foreach(ActionListener a; al){
1531 			//a.actionEvent(source, type, value, message);
1532 			//writeln(a);
1533 			a.actionEvent(e);
1534 		}
1535 	}*/
1536 }
1537 
1538 /**
1539  * To create drop-down lists, menu bars, etc.
1540  */
1541 public class PopUpMenu : PopUpElement{
1542 	//private wstring[] texts;
1543 	//private string[] sources;
1544 
1545 	//private uint[int] hotkeyCodes;
1546 	protected Bitmap8Bit[int] icons;
1547 	protected int minwidth, width, height, iconWidth, select;
1548 	PopUpMenuElement[] elements;
1549 
1550 	public this(PopUpMenuElement[] elements, string source, int iconWidth = 0){
1551 		this.elements = elements;
1552 		this.source = source;
1553 		this. iconWidth = iconWidth;
1554 		select = -1;
1555 	}
1556 	public override void draw(){
1557 		StyleSheet ss = getStyleSheet();
1558 		if(output is null){
1559 
1560 			minwidth = (ss.drawParameters["PopUpMenuVertPadding"] * 2) + ss.drawParameters["PopUpMenuMinTextSpace"] + iconWidth;
1561 			width = minwidth;
1562 			foreach(e; elements){
1563 				int newwidth = ss.getFontset("default").getTextLength(e.text~e.secondaryText) + iconWidth;
1564 				if(newwidth > width){
1565 					width = newwidth;
1566 				}
1567 				height += ss.getFontset("default").getSize() + (ss.drawParameters["PopUpMenuVertPadding"] * 2);
1568 			}
1569 			width += (ss.drawParameters["PopUpMenuHorizPadding"] * 2) + ss.drawParameters["PopUpMenuMinTextSpace"];
1570 			height += ss.drawParameters["PopUpMenuVertPadding"] * 2;
1571 			output = new BitmapDrawer(width, height);
1572 			coordinates = Coordinate(0, 0, width, height);
1573 		}
1574 		output.drawFilledRectangle(0,width - 1,0,height - 1,ss.getColor("window"));
1575 
1576 		if(select > -1){
1577 			int y0 = cast(int)((height / elements.length) * select);
1578 			int y1 = cast(int)((height / elements.length) + y0);
1579 			output.drawFilledRectangle(1, width - 1, y0 + 1, y1 + 1, ss.getColor("selection"));
1580 		}
1581 
1582 
1583 		int y = 1 + ss.drawParameters["PopUpMenuVertPadding"];
1584 		foreach(e; elements){
1585 			if(e.secondaryText !is null){
1586 				output.drawColorText(width - ss.drawParameters["PopUpMenuHorizPadding"] - 1, y, e.secondaryText,
1587 						ss.getFontset("default"), ss.getColor("PopUpMenuSecondaryTextColor"), FontFormat.RightJustified);
1588 			}
1589 			output.drawColorText(ss.drawParameters["PopUpMenuHorizPadding"] + iconWidth, y, e.text, ss.getFontset("default"),
1590 					ss.getColor("normaltext"), 0);
1591 			if(e.getIcon() !is null){
1592 				output.insertBitmap(ss.drawParameters["PopUpMenuHorizPadding"], y, e.getIcon());
1593 			}
1594 			y += ss.getFontset("default").getSize() + (ss.drawParameters["PopUpMenuVertPadding"] * 2);
1595 		}
1596 
1597 		//output.drawRectangle(1,1,height-1,width-1,ss.getColor("windowascent"));
1598 		output.drawLine(0,0,0,height-1,ss.getColor("windowascent"));
1599 		output.drawLine(0,width-1,0,0,ss.getColor("windowascent"));
1600 		output.drawLine(0,width-1,height-1,height-1,ss.getColor("windowdescent"));
1601 		output.drawLine(width-1,width-1,0,height-1,ss.getColor("windowdescent"));
1602 		if(onDraw !is null){
1603 			onDraw();
1604 		}
1605 	}
1606 	public override void onClick(int offsetX, int offsetY, int type = 0){
1607 		offsetY /= height / elements.length;
1608 		if(elements[offsetY].source == "\\submenu\\"){
1609 			PopUpMenu m = new PopUpMenu(elements[offsetY].subElements, this.source, elements[offsetY].iconWidth);
1610 			m.onMouseClick = onMouseClick;
1611 			//parent.getAbsolutePosition()
1612 			parent.addPopUpElement(m, coordinates.left + width, coordinates.top + offsetY * cast(int)(height / elements.length));
1613 			//parent.closePopUp(this);
1614 		}else{
1615 			//invokeActionEvent(new Event(elements[offsetY].source, source, null, null, null, offsetY, EventType.CLICK));
1616 			if(onMouseClick !is null)
1617 				onMouseClick(new Event(elements[offsetY].source, source, null, null, null, offsetY, EventType.CLICK));
1618 			parent.endPopUpSession();
1619 			//parent.closePopUp(this);
1620 		}
1621 
1622 	}
1623 	public override void onMouseMovement(int x , int y) {
1624 		if(x == -1){
1625 			if(select != -1){
1626 				select = -1;
1627 				draw;
1628 			}
1629 		}else{
1630 			y /= height / elements.length;
1631 			if(y < elements.length){
1632 				select = y;
1633 			}
1634 			draw();
1635 		}
1636 	}
1637 
1638 }
1639 /**
1640 * Defines a single MenuElement, also can contain multiple subelements.
1641 */
1642 public class PopUpMenuElement{
1643 	public string source;
1644 	public dstring text, secondaryText;
1645 	protected Bitmap8Bit icon;
1646 	private PopUpMenuElement[] subElements;
1647 	private ushort keymod;
1648 	private int keycode;
1649 	public int iconWidth;
1650 
1651 	public this(string source, dstring text, dstring secondaryText = null, Bitmap8Bit icon = null, int iconWidth = 0){
1652 		this.source = source;
1653 		this.text = text;
1654 		this.secondaryText = secondaryText;
1655 		this.icon = icon;
1656 		this.iconWidth = iconWidth;
1657 	}
1658 	public this(string source, dstring text, dstring secondaryText, PopUpMenuElement[] subElements){
1659 		this.source = source;
1660 		this.text = text;
1661 		this.secondaryText = secondaryText;
1662 		this.subElements = subElements;
1663 		/+this.icon = icon;
1664 		this.iconWidth = iconWidth;+/
1665 	}
1666 	public this(string source, dstring text, dstring secondaryText, PopUpMenuElement[] subElements, Bitmap8Bit icon = null,
1667 			int iconWidth = 0){
1668 		this.source = source;
1669 		this.text = text;
1670 		this.secondaryText = secondaryText;
1671 		this.subElements = subElements;
1672 		/+this.icon = icon;
1673 		this.iconWidth = iconWidth;+/
1674 	}
1675 	public Bitmap8Bit getIcon(){
1676 		return icon;
1677 	}
1678 	public void setIcon(Bitmap8Bit icon){
1679 		this.icon = icon;
1680 	}
1681 	public PopUpMenuElement[] getSubElements(){
1682 		return subElements;
1683 	}
1684 	public void loadSubElements(PopUpMenuElement[] e){
1685 		subElements = e;
1686 	}
1687 	public PopUpMenuElement opIndex(size_t i){
1688 		return subElements[i];
1689 	}
1690 	public PopUpMenuElement opIndexAssign(PopUpMenuElement value, size_t i){
1691 		subElements[i] = value;
1692 		return value;
1693 	}
1694 	public PopUpMenuElement opOpAssign(string op)(PopUpMenuElement value){
1695 		static if(op == "~"){
1696 			subElements ~= value;
1697 			return value;
1698 		}else static assert("Operator " ~ op ~ " not supported!");
1699 	}
1700 	public size_t getLength(){
1701 		return subElements.length;
1702 	}
1703 	public void setLength(int l){
1704 		subElements.length = l;
1705 	}
1706 
1707 }
1708 /**
1709  * Text input in pop-up fashion.
1710  */
1711 public class PopUpTextInput : PopUpElement, TextInputListener{
1712 	protected bool enableEdit, insert;
1713 	protected int textPos;
1714 	public void delegate(Event ev) onTextInput;
1715 
1716 	public this(string source, dstring text, Coordinate coordinates){
1717 		this.source = source;
1718 		this.text = text;
1719 		this.coordinates = coordinates;
1720 		enableEdit = true;
1721 		output = new BitmapDrawer(coordinates.width, coordinates.height);
1722 		inputhandler.startTextInput(this);
1723 	}
1724 	public override void draw(){
1725 		output.drawFilledRectangle(0, coordinates.width - 1, 0, coordinates.height - 1, getStyleSheet().getColor("window"));
1726 		output.drawRectangle(0, coordinates.width - 1, 0, coordinates.height - 1, getStyleSheet().getColor("windowascent"));
1727 
1728 		//draw cursor
1729 		if(enableEdit){
1730 			const int x = getStyleSheet().getFontset("default").chars[' '].xadvance ,
1731 					y = getStyleSheet().getFontset("default").getSize;
1732 			if(!insert)
1733 				output.drawLine((x*textPos) + 2, (x*textPos) + 2, 2, 2 + y, getStyleSheet().getColor("selection"));
1734 			else
1735 				output.drawFilledRectangle((x*textPos) + 2, (x*(textPos + 1)) + 2, 2, 2 + y, getStyleSheet().getColor("selection"));
1736 		}
1737 
1738 		output.drawColorText(2, 2, text, getStyleSheet().getFontset("default"), getStyleSheet().getColor("normaltext"), 0);
1739 
1740 		if(onDraw !is null){
1741 			onDraw();
1742 		}
1743 	}
1744 	private void deleteCharacter(int n){
1745 		//text = remove(text, i);
1746 		dstring newtext;
1747 		for(int i; i < text.length; i++){
1748 			if(i != n - 1){
1749 				newtext ~= text[i];
1750 			}
1751 		}
1752 		text = newtext;
1753 	}
1754 	public void textInputEvent(uint timestamp, uint windowID, dstring text){
1755 		int j = textPos;
1756 		dstring newtext;
1757 		for(int i ; i < textPos ; i++){
1758 			newtext ~= this.text[i];
1759 		}
1760 		for(int i ; i < 32 ; i++){
1761 			if(text[i] == 0){
1762 				break;
1763 			}
1764 			else{
1765 				newtext ~= text[i];
1766 				textPos++;
1767 				if(insert){
1768 					j++;
1769 				}
1770 			}
1771 		}
1772 		for( ; j < this.text.length ; j++){
1773 			newtext ~= this.text[j];
1774 		}
1775 		this.text = newtext;
1776 		draw();
1777 	}
1778 	public void textInputKeyEvent(uint timestamp, uint windowID, TextInputKey key, ushort modifier = 0){
1779 		switch(key){
1780 			case TextInputKey.ESCAPE:
1781 				inputhandler.stopTextInput(this);
1782 				/*draw();
1783 				invokeActionEvent(EventType.TEXTINPUT, 0, text);*/
1784 				break;
1785 			case TextInputKey.ENTER:
1786 				inputhandler.stopTextInput(this);
1787 				//invokeActionEvent(new Event(source, null, null, null, text, text.length, EventType.TEXTINPUT));
1788 				if(onTextInput !is null)
1789 					onTextInput(new Event(source, null, null, null, text, cast(int)text.length, EventType.TEXTINPUT));
1790 				break;
1791 			case TextInputKey.BACKSPACE:
1792 				if(textPos > 0){
1793 					deleteCharacter(textPos);
1794 					textPos--;
1795 					draw();
1796 				}
1797 				break;
1798 			case TextInputKey.DELETE:
1799 				deleteCharacter(textPos + 1);
1800 				draw();
1801 				break;
1802 			case TextInputKey.CURSORLEFT:
1803 				if(textPos > 0){
1804 					--textPos;
1805 					draw();
1806 				}
1807 				break;
1808 			case TextInputKey.CURSORRIGHT:
1809 				if(textPos < text.length){
1810 					++textPos;
1811 					draw();
1812 				}
1813 				break;
1814 			case TextInputKey.INSERT:
1815 				insert = !insert;
1816 				draw();
1817 				break;
1818 			case TextInputKey.HOME:
1819 				textPos = 0;
1820 				draw();
1821 				break;
1822 			case TextInputKey.END:
1823 				textPos = cast(int)text.length;
1824 				draw();
1825 				break;
1826 			default:
1827 				break;
1828 
1829 		}
1830 	}
1831 	public void dropTextInput(){
1832 		parent.endPopUpSession();
1833 		//inputHandler.stopTextInput(source);
1834 		/*draw();
1835 		invokeActionEvent(EventType.TEXTINPUT, 0, text);*/
1836 	}
1837 }
1838 
1839 public interface PopUpHandler : StyleSheetContainer{
1840 	public void addPopUpElement(PopUpElement p);
1841 	public void addPopUpElement(PopUpElement p, int x, int y);
1842 	public void endPopUpSession();
1843 	public void closePopUp(PopUpElement p);
1844 	//public Coordinate getAbsolutePosition(PopUpElement sender);
1845 	//public void drawUpdate(PopUpElement sender);
1846 	//public StyleSheet getDefaultStyleSheet();
1847 
1848 }
1849 
1850 /**
1851  * Defines the header of a ListBox.
1852  */
1853 public class ListBoxHeader{
1854 	private dstring[] text;
1855 	private int[] width;
1856 	private uint[] textInputType;
1857 	private int iconColumn;
1858 	public this(dstring[] text, int[] width, int iconColumn = 0){
1859 		this.width = width;
1860 		this.text = text;
1861 		this.iconColumn = iconColumn;
1862 	}
1863 	/// Returns the number of columns before drawing
1864 	@nogc public int getNumberOfColumns(){
1865 		return cast(int)this.text.length;
1866 	}
1867 	/// Returns the text at the given point
1868 	@nogc public dstring getText(int i){
1869 		return text[i];
1870 	}
1871 	/// Returns the width of the column
1872 	@nogc public int getColumnWidth(int i){
1873 		return width[i];
1874 	}
1875 	/// Sets the width of the column
1876 	@nogc public void setRowWidth(int i, int x){
1877 		width[i] = x;
1878 	}
1879 	/// Returns the number of column that contains the icon
1880 	@nogc public int getIconColumn(){
1881 		return iconColumn;
1882 	}
1883 	/// Returns the whole width of the header
1884 	@nogc public int getFullWidth(){
1885 		int result;
1886 		foreach(int i; width){
1887 			result += i;
1888 		}
1889 		return result;
1890 	}
1891 	/// Returns the column number from width, or -1 if x can't fit into any range
1892 	@nogc public int getColumnNumFromX(int x){
1893 		int result = -1;
1894 		if(width[0] > x) return 0;
1895 		for(int i = 1; i < width.length; i++){
1896 			if(width[i - 1] <= x || width[i] > x){
1897 				result = i;
1898 			}
1899 		}
1900 		return result;
1901 	}
1902 	/// Returns the width of the columns in a given range
1903 	@nogc public int getRangeWidth(int begin, int end){
1904 		int result;
1905 		for(; begin < end ; begin++){
1906 			result += width[begin];
1907 		}
1908 		return result;
1909 	}
1910 	/// Returns the TextInputType for the column
1911 	@nogc public uint getTextInputType(int column){
1912 		return textInputType[column];
1913 	}
1914 }
1915 /**
1916  * Defines an item in the row of a ListBox. Can be passed through the Event class
1917  */
1918  public class ListBoxItem{
1919 	private dstring[] text;
1920 	private uint[] textInputType;	///If value or array is null, the ListBoxHeader's textInputType is referred
1921 	private Bitmap8Bit icon;	/// If used, replaces the texts in the column defined by the ListBoxHeader, otherwise defaults to the text.
1922 	public this(dstring[] text, Bitmap8Bit icon = null, uint[] textInputType = null){
1923 		this.text = text;
1924 		this.icon = icon;
1925 		this.textInputType = textInputType;
1926 	}
1927 	public this(dstring[] text, uint[] textInputType){
1928 		this.text = text;
1929 		this.icon = null;
1930 		this.textInputType = textInputType;
1931 	}
1932 	/// Returns the text at the given column
1933 	@nogc public dstring getText(int column){
1934 		return text[column];
1935 	}
1936 	/// Sets the text in the given column
1937 	@nogc public void setText(int column, dstring text){
1938 		this.text[column] = text;
1939 	}
1940 	/// Returns the icon
1941 	public Bitmap8Bit getIcon(){
1942 		return icon;
1943 	}
1944 	/// Returns the input type of the given column. Refer to ListBoxHeader if return value = TextInputType.NULL
1945 	@nogc public uint getTextInputType(int column){
1946 		return textInputType[column];
1947 	}
1948 	public override string toString(){
1949 		dstring result;
1950 		foreach(ws; text)
1951 			result ~= ws;
1952 		return to!string(result);
1953 	}
1954  }
1955 /*
1956  * For use with ListBoxes and similar types. Currently left here for legacy purposes, being replaced with the classes ListBoxHeader and ListBoxElement
1957  *
1958 public struct ListBoxColumn{
1959 	public wstring header;
1960 	public wstring[] elements;
1961 
1962 	this(wstring header, wstring[] elements){
1963 		this.header = header;
1964 		this.elements = elements;
1965 	}
1966 
1967 	/
1968 	public void removeByNumber(int i){
1969 		elements = remove(elements, i);
1970 	}
1971 }*/
1972 
1973 /**
1974  * Defines an action event in the concrete GUI.
1975  */
1976 public class Event{
1977 	public string source, subsource, path, filename;
1978 	public dstring text;
1979 	public int value, type;
1980 	public Object aux;
1981 	/**
1982 	 *If a field is unneeded, leave it blank by setting it to null.
1983 	 */
1984 	this(string source, string subsource, string path, string filename, dstring textinput, int value, int type,
1985 			Object aux = null){
1986 		this.source = source;
1987 		this.subsource = subsource;
1988 		this.path = path;
1989 		this.filename = filename;
1990 		this.text = textinput;
1991 		this.value = value;
1992 		this.type = type;
1993 		this.aux = aux;
1994 	}
1995 	/**
1996 	 * Returns the full path including the filename
1997 	 */
1998 	public @property string getFullPath(){
1999 		return path ~ filename;
2000 	}
2001 }
2002 
2003 /+public interface ActionListener{
2004 	/**
2005 	 * Invoked mostly by WindowElements, Dialogs, and PopUpElements. Used to run the code and pass the eventdata.
2006 	 */
2007 	public void actionEvent(Event event);
2008 }+/
2009 
2010 public interface ElementContainer : StyleSheetContainer{
2011 	public Coordinate getAbsolutePosition(WindowElement sender);
2012 	public void clearArea(WindowElement sender);
2013 }
2014 
2015 public interface StyleSheetContainer{
2016 	public StyleSheet getStyleSheet();
2017 	public void drawUpdate(WindowElement sender);
2018 }
2019 /**
2020  * TODO: Use this for implement tabbing and etc.
2021  */
2022 public interface Focusable{
2023 	public void focusGiven();
2024 	public void focusLost();
2025 	public void tabPressed(bool reverse);
2026 }
2027 
2028 public enum EventType{
2029 	CLICK 				= 0,
2030 	TEXTINPUT			= 1,
2031 	SLIDER				= 2,
2032 	TEXTBOXSELECT		= 3,
2033 	CHECKBOX			= 4,
2034 	RADIOBUTTON			= 5,
2035 	FILEDIALOGEVENT		= 6,
2036 
2037 }