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