1 /*
2  * Copyright (C) 2015-2017, by Laszlo Szeremi under the Boost license.
3  *
4  * Pixel Perfect Engine, concrete.window module
5  */
6 
7 module PixelPerfectEngine.concrete.window;
8 
9 import PixelPerfectEngine.graphics.bitmap;
10 import PixelPerfectEngine.graphics.draw;
11 import PixelPerfectEngine.graphics.layers;
12 
13 public import PixelPerfectEngine.concrete.elements;
14 public import PixelPerfectEngine.concrete.stylesheet;
15 import PixelPerfectEngine.system.etc;
16 import PixelPerfectEngine.system.inputHandler;
17 
18 import std.algorithm.mutation;
19 import std.stdio;
20 import std.conv;
21 import std.file;
22 import std.datetime;
23 
24 public class Window : ElementContainer{
25 	private WindowElement[] elements, mouseC, keyboardC, scrollC;
26 	public wstring title;
27 	public IWindowHandler parent;
28 	//public Bitmap16Bit[int] altStyleBrush;
29 	public BitmapDrawer output;
30 	public int header, sizeX, sizeY;
31 	private int moveX, moveY;
32 	private bool move, fullUpdate;
33 	private string[] extraButtons;
34 	public Coordinate position;
35 	public StyleSheet customStyle;
36 	/**
37 	 * If the current window doesn't contain a custom StyleSheet, it gets from it's parent. 
38 	 */
39 	public StyleSheet getStyleSheet(){
40 		if(customStyle is null)
41 			return parent.getStyleSheet();
42 		return customStyle;
43 	}
44 
45 	public void drawUpdate(WindowElement sender){
46 		/*if(!fullUpdate){ 
47 			this.draw();
48 		}*/
49 		output.insertBitmap(sender.getPosition().left,sender.getPosition().top,sender.output.output);
50 	}
51 
52 	/*public Bitmap16Bit[wchar] getFontSet(int style){
53 		return parent.getFontSet(style);
54 	}*/
55 	/**
56 	 * Standard constructor. "size" sets both the initial position and the size of the window. 
57 	 * Extra buttons are handled from the StyleSheet, even numbers are unpressed, odd numbers are pressed.
58 	 */
59 	public this(Coordinate size, wstring title, string[] extraButtons = []){
60 		position = size;
61 		output = new BitmapDrawer(position.getXSize, position.getYSize);
62 		this.title = title;
63 		sizeX = position.getXSize;
64 		sizeY = position.getYSize;
65 		//style = 0;
66 		//closeButton = 2;
67 		this.extraButtons = extraButtons;
68 	}
69 
70 	public void addElement(WindowElement we, int eventProperties){
71 		elements ~= we;
72 		we.elementContainer = this;
73 		if((eventProperties & EventProperties.KEYBOARD) == EventProperties.KEYBOARD){
74 			keyboardC ~= we;
75 		}
76 		if((eventProperties & EventProperties.MOUSE) == EventProperties.MOUSE){
77 			mouseC ~= we;
78 		}
79 		if((eventProperties & EventProperties.SCROLL) == EventProperties.SCROLL){
80 			scrollC ~= we;
81 		}
82 	}
83 
84 	public void draw(){
85 
86 		output.drawFilledRectangle(0, position.getXSize() - 1, 0, position.getYSize() - 1, getStyleSheet().getColor("window"));
87 		output.insertBitmap(0,0,getStyleSheet().getImage("closeButtonA"));
88 		int x1 = getStyleSheet().getImage("closeButtonA").getX(), y1 = getStyleSheet().getImage("closeButtonA").getY();
89 		/*output.drawRectangle(x1, sizeX - 1, 0, y1, getStyleBrush(header));
90 		output.drawFilledRectangle(x1 + (x1/2), sizeX - 1 - (x1/2), y1/2, y1 - (y1/2), getStyleBrush(header).readPixel(x1/2, y1/2));*/
91 
92 		int headerLength = extraButtons.length == 0 ? position.getXSize() - 1 : position.getXSize() - 1 - ((extraButtons.length/2) * x1) ;
93 		//drawing the header
94 		output.drawLine(x1, headerLength, 0, 0, getStyleSheet().getColor("windowascent"));
95 		output.drawLine(x1, x1, 0, y1 - 1, getStyleSheet().getColor("windowascent"));
96 		output.drawLine(x1, headerLength, y1 - 1, y1 - 1, getStyleSheet().getColor("windowdescent"));
97 		output.drawLine(headerLength, headerLength, 0, y1 - 1, getStyleSheet().getColor("windowdescent"));
98 
99 		//drawing the border of the window
100 		output.drawLine(0, position.getXSize() - 1, y1, y1, getStyleSheet().getColor("windowascent"));
101 		output.drawLine(0, 0, y1, position.getYSize() - 1, getStyleSheet().getColor("windowascent"));
102 		output.drawLine(0, position.getXSize() - 1, position.getYSize() - 1, position.getYSize() - 1,  getStyleSheet().getColor("windowdescent"));
103 		output.drawLine(position.getXSize() - 1, position.getXSize() - 1, y1, position.getYSize() - 1, getStyleSheet().getColor("windowdescent"));
104 
105 		//output.drawText(x1+1, 1, title, getFontSet(0), 1);
106 		output.drawText(x1, (y1-getStyleSheet().getFontset("default").getSize())/2, title, getStyleSheet().getFontset("default"),1);
107 		fullUpdate = true;
108 		foreach(WindowElement we; elements){
109 			we.draw();
110 			//output.insertBitmap(we.getPosition().xa,we.getPosition().ya,we.output.output);
111 		}
112 		fullUpdate = false;
113 	}
114 
115 	public void passMouseEvent(int x, int y, int state = 0){
116 		//writeln(x, ",", y);
117 		if(getStyleSheet.getImage("closeButtonA").getX() > x && getStyleSheet.getImage("closeButtonA").getY() > y && state == 0){
118 			parent.closeWindow(this);
119 			return;
120 		}else if(getStyleSheet.getImage("closeButtonA").getY() > y && state == 0){
121 			/*if(state == 0 && !move){
122 				move = true;
123 				moveY = y;
124 			}
125 			if(state == 1 && move){
126 				move = false;
127 				position.move(x - moveX, y - moveY);
128 				parent.moveUpdate(this);
129 			}*/
130 			parent.moveUpdate(this);
131 		}
132 		//x -= position.xa;
133 		//y -= position.ya;
134 		foreach(WindowElement e; mouseC){
135 			if(e.getPosition().left < x && e.getPosition().right > x && e.getPosition().top < y && e.getPosition().bottom > y){
136 				e.onClick(x - e.getPosition().left, y - e.getPosition().top, state);
137 				return;
138 			}
139 		}
140 
141 	}
142 	public void passScrollEvent(int wX, int wY, int x, int y){
143 		foreach(WindowElement e; scrollC){
144 			if(e.getPosition().left < wX && e.getPosition().right > wX && e.getPosition().top < wX && e.getPosition().bottom > wY){
145 
146 				e.onScroll(x, y, wX, wY);
147 				return;
148 			}
149 		}
150 	}
151 	public void extraButtonEvent(int num){
152 
153 	}
154 	public void passKeyboardEvent(wchar c, int type, int x, int y){
155 
156 	}
157 	public void addParent(IWindowHandler wh){
158 		parent = wh;
159 	}
160 	public void getFocus(WindowElement sender){
161 
162 	}
163 	public void dropFocus(WindowElement sender){
164 
165 	}
166 }
167 
168 public class TextInputDialog : Window, ActionListener{
169 	public ActionListener[] al;
170 	private TextBox textInput;
171 	private string source;
172 
173 	public this(Coordinate size, string source, wstring title, wstring message, wstring text = ""){
174 		this(size, title);
175 		Label msg = new Label(message, "null", Coordinate(8, 20, size.getXSize()-8, 39));
176 		addElement(msg, EventProperties.MOUSE);
177 
178 		textInput = new TextBox(text, "textInput", Coordinate(8, 40, size.getXSize()-8, 59));
179 		addElement(textInput, EventProperties.MOUSE);
180 
181 		Button ok = new Button("Ok","ok", Coordinate(size.getXSize()-48,65,size.getXSize()-8,84));
182 		ok.al ~= this;
183 		addElement(ok,EventProperties.MOUSE);
184 		this.source = source;
185 	}
186 
187 	public this(Coordinate size, wstring title){
188 		super(size, title);
189 	}
190 
191 	public void actionEvent(string source, int type, int value, wstring message){}
192 	public void actionEvent(string source, string subSource, int type, int value, wstring message){}
193 	public void actionEvent(Event event){
194 		if(event.source == "ok"){
195 			foreach(a; al){
196 				a.actionEvent(new Event(this.source, "TextInputDialog", null, null, textInput.getText(), 0, EventType.TEXTINPUT));
197 			}
198 			parent.closeWindow(this);
199 		}
200 	}
201 }
202 
203 public class DefaultDialog : Window, ActionListener{
204 	public ActionListener[] al;
205 	private string source;
206 
207 	public this(Coordinate size, string source, wstring title, wstring message, wstring[] options = ["Ok"]){
208 		this(size, title);
209 		//generate text
210 		//NOTE: currently only works with one line texts, later on multi-line texts will be added
211 		//NOTE: currently only optimized for 8 pixel wide fonts
212 		this.source = source;
213 		int x1 , x2;
214 		//writeln(x1,',',size.getXSize - x1);
215 		Label msg = new Label(message, "null", Coordinate(8, 20, size.getXSize()-8, 40));
216 		addElement(msg, EventProperties.MOUSE);
217 
218 		//generate buttons
219 
220 		x1 = size.getXSize - 10;
221 		Button[] buttons;
222 		for(int i; i < options.length; i++){
223 			x2 = x1 - ((options[i].length + 2) * 8);
224 			buttons ~= new Button(options[i], to!string(options[i]), Coordinate(x2, 40, x1, 60));
225 			buttons[i].al ~= this;
226 			addElement(buttons[i], EventProperties.MOUSE);
227 			x1 = x2;
228 		}
229 	}
230 
231 	public this(Coordinate size, wstring title){
232 		super(size, title);
233 	}
234 
235 	public void actionEvent(string source, int type, int value, wstring message){
236 		foreach(a; al){
237 			//a.actionEvent(source, this.source, type, value, message);
238 			a.actionEvent(new Event(source, this.source, null, null, null, 0, EventType.CLICK));
239 		}
240 	}
241 	public void actionEvent(string source, string subSource, int type, int value, wstring message){}
242 	public void actionEvent(Event event){
243 		foreach(a; al){
244 			//a.actionEvent(source, this.source, type, value, message);
245 			a.actionEvent(new Event(event.source, this.source, null, null, null, 0, EventType.CLICK));
246 		}
247 	}
248 }
249 
250 public class FileDialog : Window, ActionListener{
251 	private ActionListener al;
252 	private string source;
253 	private string[] filetypes, pathList, driveList;
254 	private string directory, filename;
255 	private ListBox lb;
256 	private TextBox tb;
257 	private ListBoxColumn[] columns;
258 	private bool save;
259 	public static const string subsourceID = "filedialog";
260 
261 
262 	public this(wstring title, string source, ActionListener a, string[] filetypes, string startDir, bool save = false, string filename = ""){
263 		this(Coordinate(20,20,240,198), title);
264 		this.source = source;
265 		this.filetypes = filetypes;
266 		this.save = save;
267 		al = a;
268 		directory = startDir;
269 		//generate buttons
270 		Button[] buttons;
271 		buttons ~= new Button("Up","up",Coordinate(4, 154, 54, 174));
272 		buttons ~= new Button("Drive","drv",Coordinate(58, 154, 108, 174));
273 		if(save)
274 			buttons ~= new Button("Save","ok",Coordinate(112, 154, 162, 174));
275 		else
276 			buttons ~= new Button("Load","ok",Coordinate(112, 154, 162, 174));
277 		buttons ~= new Button("Close","close",Coordinate(166, 154, 216, 174));
278 		for(int i; i < buttons.length; i++){
279 			buttons[i].al ~= this;
280 			addElement(buttons[i], EventProperties.MOUSE);
281 		}
282 		//generate textbox
283 		tb = new TextBox(to!wstring(filename), "filename", Coordinate(4, 130, 162, 150));
284 		//tb.addTextInputHandler(tih);
285 		tb.al ~= this;
286 		addElement(tb, EventProperties.MOUSE);
287 		//generate listbox
288 
289 		//test parameters
290 
291 		//writeln(dirEntries(startDir, SpanMode.shallow));
292 
293 
294 		columns ~= ListBoxColumn("Name", ["aaa","aaa","aaa","aaa","aaa","aaa","aaa"]);
295 		columns ~= ListBoxColumn("Type", ["bbb","bbb","bbb","bbb","aaa","aaa","aaa"]);
296 		//Date format: yyyy-mm-dd hh:mm:ss
297 		columns ~= ListBoxColumn("Date", ["yyyy-mm-dd hh:mm:ss","yyyy-mm-dd hh:mm:ss","yyyy-mm-dd hh:mm:ss","yyyy-mm-dd hh:mm:ss","yyyy-mm-dd hh:mm:ss","yyyy-mm-dd hh:mm:ss","yyyy-mm-dd hh:mm:ss"]);
298 		spanDir();
299 
300 		//writeln(pathList);
301 
302 		//VSlider vsl = new VSlider(20,5,"sld",Coordinate(200,20,216,124));
303 		//addElement(vsl, EventProperties.MOUSE);
304 		//HSlider hsl = new HSlider(200,48,"vsld",Coordinate(4, 108, 200, 124));
305 		//addElement(hsl, EventProperties.MOUSE);
306 		lb = new ListBox("lb", Coordinate(4, 20, 216, 126),columns ,[160, 40, 176] ,15);
307 		addElement(lb, EventProperties.MOUSE | EventProperties.SCROLL);
308 		//scrollC ~= lb;
309 		lb.al ~= this;
310 		detectDrive();
311 	}
312 
313 	public this(Coordinate size, wstring title){
314 		super(size, title);
315 	}
316 
317 	private void spanDir(){
318 		pathList.length = 0;
319 		columns[0].elements.length = 0;
320 		columns[1].elements.length = 0;
321 		columns[2].elements.length = 0;
322 		foreach(DirEntry de; dirEntries(directory, SpanMode.shallow)){
323 			if(de.isDir){
324 				pathList ~= de.name;
325 				columns[0].elements ~= to!wstring(getFilenameFromPath(de.name));
326 				columns[1].elements ~= "<DIR>";
327 				columns[2].elements ~= formatDate(de.timeLastModified);
328 			}
329 		}
330 		foreach(ft; filetypes){
331 			foreach(DirEntry de; dirEntries(directory, ft, SpanMode.shallow)){
332 				if(de.isFile){
333 					pathList ~= de.name;
334 					columns[0].elements ~= to!wstring(getFilenameFromPath(de.name, true));
335 					columns[1].elements ~= to!wstring(ft);
336 					columns[2].elements ~= formatDate(de.timeLastModified);
337 			}
338 			}
339 		}
340 
341 
342 	}
343 
344 	private wstring formatDate(SysTime time){
345 		wstring s;
346 		s ~= to!wstring(time.year());
347 		s ~= "-";
348 		s ~= to!wstring(time.month());
349 		s ~= "-";
350 		s ~= to!wstring(time.day());
351 		s ~= " ";
352 		s ~= to!wstring(time.hour());
353 		s ~= ":";
354 		s ~= to!wstring(time.minute());
355 		s ~= ":";
356 		s ~= to!wstring(time.second());
357 		//writeln(s);
358 		return s;
359 	}
360 
361 	private void detectDrive(){
362 		driveList.length = 0;
363 		for(char c = 'A'; c <='Z'; c++){
364 			string s;
365 			s ~= c;
366 			s ~= ":\x5c";
367 			if(exists(s)){
368 				driveList ~= (s);
369 			}
370 		}
371 		//writeln(driveList);
372 	}
373 
374 	private string getFilenameFromPath(string p, bool b = false){
375 		int n, m = p.length;
376 		string s;
377 		for(int i ; i < p.length ; i++){
378 			if(p[i] == '\x5c'){
379 				n = i;
380 			}
381 		}
382 		//n++;
383 		if(b){
384 			for(int i ; i < p.length ; i++){
385 				if(p[i] == '.'){
386 					m = i;
387 				}
388 			}
389 		}
390 		for( ; n < m ; n++){
391 			if(p[n] < 128 && p[n] > 31)
392 				s ~= p[n];
393 		}
394 		return s;
395 	}
396 
397 	private void up(){
398 		int n;
399 		for(int i ; i < directory.length ; i++){
400 			if(directory[i] == '\x5c'){
401 				n = i;
402 			}
403 		}
404 		string newdir;
405 		for(int i ; i < n ; i++){
406 			newdir ~= directory[i];
407 		}
408 		directory = newdir;
409 		spanDir();
410 		lb.updateColumns(columns);
411 		lb.draw();
412 	}
413 
414 	private void changeDrive(){
415 		pathList.length = 0;
416 		columns[0].elements.length = 0;
417 		columns[1].elements.length = 0;
418 		columns[2].elements.length = 0;
419 		foreach(string drive; driveList){
420 			pathList ~= drive;
421 			columns[0].elements ~= to!wstring(drive);
422 			columns[1].elements ~= "<DRV>";
423 			columns[2].elements ~= "N/A";
424 		}
425 		lb.updateColumns(columns);
426 		lb.draw();
427 	}
428 
429 	private void fileEvent(){
430 		//wstring s = to!wstring(directory);
431 		filename = to!string(tb.getText);
432 		//al.actionEvent("file", EventType.FILEDIALOGEVENT, 0, s);
433 		al.actionEvent(new Event(source, "filedialog", directory, filename, null, 0, EventType.FILEDIALOGEVENT));
434 		parent.closeWindow(this);
435 	}
436 
437 	public void actionEvent(string source, int type, int value, wstring message){
438 		/*if(source == "lb"){
439 			//writeln(value);
440 
441 		}else if(source == "up"){
442 			up();
443 		}else if(source == "drv"){
444 			changeDrive();
445 		}else if(source == "ok"){
446 			fileEvent();
447 		}else if(source == "close"){
448 			parent.closeWindow(this);
449 		}*/
450 	}
451 	public void actionEvent(string source, string subSource, int type, int value, wstring message){}
452 	public void actionEvent(Event event){
453 		writeln(event.source);
454 		switch(event.source){
455 			case "lb":
456 				try{
457 					if(isDir(pathList[event.value])){
458 						directory = pathList[event.value];
459 						spanDir();
460 						lb.updateColumns(columns);
461 						lb.draw();
462 						
463 					}else{
464 						filename = getFilenameFromPath(pathList[event.value]);
465 						tb.setText(to!wstring(filename));
466 					}
467 				}catch(Exception e){
468 					writeln(e.msg);
469 				}
470 				break;
471 			case "up": up(); break;
472 			case "drv": changeDrive(); break;
473 			case "ok": fileEvent(); break;
474 			case "close": parent.closeWindow(this); break;
475 			default: break;
476 		}
477 	}
478 }
479 
480 public class WindowHandler : InputListener, MouseListener, IWindowHandler{
481 	private Window[] windows;
482 	private int[] priorities;
483 	public int screenX, screenY, rasterX, rasterY, moveX, moveY;
484 	//public Bitmap16Bit[wchar] basicFont, altFont, alarmFont;
485 	public StyleSheet defaultStyle;
486 	//public Bitmap16Bit[int] styleBrush;
487 	private Bitmap16Bit background;
488 	private ISpriteLayer16Bit spriteLayer;
489 	private bool moveState, dragEventState;
490 	private Window windowToMove, dragEventDest;
491 
492 	public this(int sx, int sy, int rx, int ry,ISpriteLayer16Bit sl){
493 		screenX = sx;
494 		screenY = sy;
495 		rasterX = rx;
496 		rasterY = ry;
497 		spriteLayer = sl;
498 	}
499 
500 	public void addWindow(Window w){
501 		windows ~= w;
502 		w.addParent(this);
503 		//priorities ~= 666;
504 		w.draw();
505 		setWindowToTop(w);
506 
507 		/*for(int i ; i < windows.length ; i++){
508 			spriteLayer.addSprite(windows[i].output.output, i, windows[i].position);
509 		}*/
510 	}
511 
512 	public void addBackground(Bitmap16Bit b){
513 		background = b;
514 		spriteLayer.addSprite(background, 65536, 0, 0);
515 	}
516 
517 	private int whichWindow(Window w){
518 		for(int i ; i < windows.length ; i++){
519 			if(windows[i] == w){
520 				return i;
521 			}
522 		}
523 		return -1;
524 	}
525 
526 	public void setWindowToTop(Window sender){
527 		int s;
528 		foreach(Window w; windows){
529 			if(w == sender){
530 				Window ww = windows[s];
531 				windows[s] = windows[0];
532 				windows[0] = ww;
533 				updateSpriteOrder();
534 				break;
535 			}else{
536 				s++;
537 			}
538 		}
539 
540 	}
541 
542 	private void updateSpriteOrder(){
543 		for(int i ; i < windows.length ; i++){
544 			spriteLayer.removeSprite(i);
545 			spriteLayer.addSprite(windows[i].output.output, i, windows[i].position);
546 
547 		}
548 	}
549 
550 	/*public Bitmap16Bit[wchar] getFontSet(int style){
551 		switch(style){
552 			case 0: return basicFont;
553 			case 1: return altFont;
554 			case 3: return alarmFont;
555 			default: break;
556 		}
557 		return basicFont;
558 
559 	}*/
560 	public StyleSheet getStyleSheet(){
561 		return defaultStyle;
562 	}
563 	public void closeWindow(Window sender){
564 		int p = whichWindow(sender);
565 		for(int i ; i < windows.length ; i++)
566 			spriteLayer.removeSprite(i);
567 		//spriteLayer.removeSprite(p);
568 		windows = remove(windows, p);
569 
570 		updateSpriteOrder();
571 	}
572 
573 	public void moveUpdate(Window sender){
574 		moveState = true;
575 		windowToMove = sender;
576 		//writeln(moveState);
577 	}
578 	public void keyPressed(string ID, Uint32 timestamp, Uint32 devicenumber, Uint32 devicetype){
579 
580 	}
581 	public void keyReleased(string ID, Uint32 timestamp, Uint32 devicenumber, Uint32 devicetype){
582 
583 	}
584 	public void mouseButtonEvent(Uint32 which, Uint32 timestamp, Uint32 windowID, Uint8 button, Uint8 state, Uint8 clicks, Sint32 x, Sint32 y){
585 
586 		//converting the dimensions
587 		double xR = to!double(rasterX) / to!double(screenX) , yR = to!double(rasterY) / to!double(screenY);
588 		x = to!int(x * xR);
589 		y = to!int(y * yR);
590 
591 
592 		if(state == SDL_RELEASED && moveState){
593 			/*windowToMove.position.relMove(x - moveX, y - moveY);
594 			spriteLayer.relMoveSprite(whichWindow(windowToMove), x - moveX, y - moveY);*/
595 			//writeln(x - moveX, y - moveY);
596 			moveState = false;
597 		}else if(state == SDL_RELEASED && moveState){
598 			dragEventState = false;
599 		}
600 		else if(state == SDL_PRESSED){
601 			moveX = x; 
602 			moveY = y;
603 
604 			for(int i ; i < windows.length ; i++){
605 				//writeln(i);
606 				if(x >= windows[i].position.left && x <= windows[i].position.right && y >= windows[i].position.top && y <= windows[i].position.bottom){
607 					if(i == 0){
608 						windows[0].passMouseEvent(x - windows[0].position.left, y - windows[0].position.top, 0);
609 						if(windows.length !=0){
610 							dragEventState = true;
611 							dragEventDest = windows[0];
612 						}
613 						return;
614 					}
615 					else{
616 						setWindowToTop(windows[i]);
617 						return;
618 					}
619 				}
620 			}
621 			passMouseEvent(x,y);
622 		}
623 
624 	}
625 	public void passMouseEvent(int x, int y, int state = 0){
626 
627 	}
628 	public void mouseWheelEvent(uint type, uint timestamp, uint windowID, uint which, int x, int y, int wX, int wY){
629 		double xR = to!double(rasterX) / to!double(screenX) , yR = to!double(rasterY) / to!double(screenY);
630 		wX = to!int(wX * xR);
631 		wY = to!int(wY * yR);
632 		if(windows.length != 0)
633 			windows[0].passScrollEvent(wX - windows[0].position.left, wY - windows[0].position.top, y, x);
634 		passScrollEvent(wX,wY,x,y);
635 	}
636 	public void passScrollEvent(int wX, int wY, int x, int y){
637 
638 	}
639 	public void mouseMotionEvent(uint timestamp, uint windowID, uint which, uint state, int x, int y, int relX, int relY){
640 		double xR = to!double(rasterX) / to!double(screenX) , yR = to!double(rasterY) / to!double(screenY);
641 		x = to!int(x * xR);
642 		y = to!int(y * yR);
643 		relX = to!int(relX * xR);
644 		relY = to!int(relY * yR);
645 		if(state == SDL_PRESSED && moveState){
646 			windowToMove.position.relMove(relX, relY);
647 			spriteLayer.relMoveSprite(whichWindow(windowToMove), relX, relY);
648 		}else if(state == SDL_PRESSED && dragEventState){
649 			dragEventDest.passMouseEvent(x - dragEventDest.position.left,y - dragEventDest.position.top,-1);
650 		}
651 	}
652 }
653 
654 public interface IWindowHandler{
655 	//public Bitmap16Bit[wchar] getFontSet(int style);
656 	public StyleSheet getStyleSheet();
657 	public void closeWindow(Window sender);
658 	public void moveUpdate(Window sender);
659 	public void setWindowToTop(Window sender);
660 	public void addWindow(Window w);
661 }
662 
663 public enum EventProperties : uint{
664 	KEYBOARD		=	1,
665 	MOUSE			=	2,
666 	SCROLL			=	4
667 }