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.path; 23 import std.datetime; 24 25 /** 26 * Basic window. All other windows are inherited from this class. 27 */ 28 public class Window : ElementContainer{ 29 protected WindowElement[] elements, mouseC, keyboardC, scrollC; 30 protected wstring title; 31 protected WindowElement draggedElement; 32 public IWindowHandler parent; 33 //public Bitmap16Bit[int] altStyleBrush; 34 public BitmapDrawer output; 35 public int header;//, sizeX, sizeY; 36 protected int moveX, moveY; 37 protected bool fullUpdate; 38 protected string[] extraButtons; 39 public Coordinate position; 40 public StyleSheet customStyle; 41 public static StyleSheet defaultStyle; 42 public static void delegate() onDrawUpdate; 43 /** 44 * If the current window doesn't contain a custom StyleSheet, it gets from it's parent. 45 */ 46 public StyleSheet getStyleSheet(){ 47 if(customStyle is null){ 48 if(parent is null){ 49 return defaultStyle; 50 } 51 return parent.getStyleSheet(); 52 } 53 return customStyle; 54 } 55 /** 56 * Updates the output of the elements. 57 */ 58 public void drawUpdate(WindowElement sender){ 59 /*if(!fullUpdate){ 60 this.draw(); 61 }*/ 62 output.insertBitmap(sender.getPosition().left,sender.getPosition().top,sender.output.output); 63 } 64 65 66 /** 67 * Standard constructor. "size" sets both the initial position and the size of the window. 68 * Extra buttons are handled from the StyleSheet, currently unimplemented. 69 */ 70 public this(Coordinate size, wstring title, string[] extraButtons = []){ 71 position = size; 72 output = new BitmapDrawer(position.width(), position.height()); 73 this.title = title; 74 //sizeX = position.width(); 75 //sizeY = position.height(); 76 //style = 0; 77 //closeButton = 2; 78 this.extraButtons = extraButtons; 79 } 80 /** 81 * Adds a new WindowElement with the given event properties. 82 */ 83 public void addElement(WindowElement we, int eventProperties){ 84 elements ~= we; 85 we.elementContainer = this; 86 if((eventProperties & EventProperties.KEYBOARD) == EventProperties.KEYBOARD){ 87 keyboardC ~= we; 88 } 89 if((eventProperties & EventProperties.MOUSE) == EventProperties.MOUSE){ 90 mouseC ~= we; 91 } 92 if((eventProperties & EventProperties.SCROLL) == EventProperties.SCROLL){ 93 scrollC ~= we; 94 } 95 we.draw(); 96 } 97 /** 98 * Removes the WindowElement if 'we' is found within its ranges, does nothing otherwise. 99 */ 100 public void removeElement(WindowElement we){ 101 int i; 102 while(elements.length > i){ 103 if(elements[i] == we){ 104 elements = remove(elements, i); 105 }else{ 106 i++; 107 } 108 } 109 i = 0; 110 while(mouseC.length > i){ 111 if(mouseC[i] == we){ 112 mouseC = remove(mouseC, i); 113 }else{ 114 i++; 115 } 116 } 117 i = 0; 118 while(scrollC.length > i){ 119 if(scrollC[i] == we){ 120 scrollC = remove(scrollC, i); 121 }else{ 122 i++; 123 } 124 } 125 i = 0; 126 while(keyboardC.length > i){ 127 if(keyboardC[i] == we){ 128 keyboardC = remove(keyboardC, i); 129 }else{ 130 i++; 131 } 132 } 133 draw(); 134 } 135 /** 136 * Draws the window. Intended to be used by the WindowHandler. 137 */ 138 public void draw(){ 139 if(output.output.width != position.width || output.output.height != position.height) 140 output = new BitmapDrawer(position.width(), position.height()); 141 output.drawFilledRectangle(0, position.width() - 1, getStyleSheet().getImage("closeButtonA").height, position.height() - 1, getStyleSheet().getColor("window")); 142 int x1 = getStyleSheet().getImage("closeButtonA").width, y1 = getStyleSheet().getImage("closeButtonA").height, x2 = position.width; 143 /*output.drawRectangle(x1, sizeX - 1, 0, y1, getStyleBrush(header)); 144 output.drawFilledRectangle(x1 + (x1/2), sizeX - 1 - (x1/2), y1/2, y1 - (y1/2), getStyleBrush(header).readPixel(x1/2, y1/2));*/ 145 146 int headerLength = extraButtons.length == 0 ? position.width - 1 : position.width() - 1 - ((extraButtons.length>>2) * x1) ; 147 //drawing the header 148 drawHeader(); 149 150 //drawing the border of the window 151 output.drawLine(0, position.width() - 1, y1, y1, getStyleSheet().getColor("windowascent")); 152 output.drawLine(0, 0, y1, position.height() - 1, getStyleSheet().getColor("windowascent")); 153 output.drawLine(0, position.width() - 1, position.height() - 1, position.height() - 1, getStyleSheet().getColor("windowdescent")); 154 output.drawLine(position.width() - 1, position.width() - 1, y1, position.height() - 1, getStyleSheet().getColor("windowdescent")); 155 156 //output.drawText(x1+1, 1, title, getFontSet(0), 1); 157 158 fullUpdate = true; 159 foreach(WindowElement we; elements){ 160 we.draw(); 161 //output.insertBitmap(we.getPosition().xa,we.getPosition().ya,we.output.output); 162 } 163 fullUpdate = false; 164 parent.drawUpdate(this); 165 if(onDrawUpdate !is null){ 166 onDrawUpdate(); 167 } 168 } 169 /** 170 * 171 */ 172 protected void drawHeader(){ 173 output.drawFilledRectangle(0, position.width() - 1, 0, getStyleSheet().getImage("closeButtonA").height - 1, getStyleSheet().getColor("window")); 174 output.insertBitmap(0,0,getStyleSheet().getImage("closeButtonA")); 175 int x1 = getStyleSheet().getImage("closeButtonA").width, y1 = getStyleSheet().getImage("closeButtonA").height, x2 = position.width; 176 int headerLength = extraButtons.length == 0 ? position.width() - 1 : position.width() - 1 - ((extraButtons.length>>1) * x1) ; 177 foreach(s ; extraButtons){ 178 x2 -= x1; 179 output.insertBitmap(x2,0,getStyleSheet().getImage(s)); 180 } 181 output.drawLine(x1, headerLength, 0, 0, getStyleSheet().getColor("windowascent")); 182 output.drawLine(x1, x1, 0, y1 - 1, getStyleSheet().getColor("windowascent")); 183 output.drawLine(x1, headerLength, y1 - 1, y1 - 1, getStyleSheet().getColor("windowdescent")); 184 output.drawLine(headerLength, headerLength, 0, y1 - 1, getStyleSheet().getColor("windowdescent")); 185 output.drawText(x1, (y1-getStyleSheet().getFontset("default").getSize())/2, title, getStyleSheet().getFontset("default"),1); 186 } 187 public void setTitle(wstring s){ 188 title = s; 189 drawHeader; 190 } 191 public wstring getTitle(){ 192 return title; 193 } 194 /** 195 * Detects where the mouse is clicked, then it either passes to an element, or tests whether the close button, 196 * an extra button was clicked, also tests for the header, which creates a drag event for moving the window. 197 */ 198 public void passMouseEvent(int x, int y, int state, ubyte button){ 199 if(state == ButtonState.PRESSED){ 200 if(getStyleSheet.getImage("closeButtonA").width > x && getStyleSheet.getImage("closeButtonA").height > y && button == MouseButton.LEFT){ 201 close(); 202 return; 203 }else if(getStyleSheet.getImage("closeButtonA").height > y){ 204 if(y > position.width - (getStyleSheet.getImage("closeButtonA").width * extraButtons.length)){ 205 y -= position.width - (getStyleSheet.getImage("closeButtonA").width * extraButtons.length); 206 extraButtonEvent(y / getStyleSheet.getImage("closeButtonA").width, button, state); 207 return; 208 } 209 parent.moveUpdate(this); 210 return; 211 } 212 //x -= position.xa; 213 //y -= position.ya; 214 215 foreach(WindowElement e; mouseC){ 216 if(e.getPosition().left < x && e.getPosition().right > x && e.getPosition().top < y && e.getPosition().bottom > y){ 217 e.onClick(x - e.getPosition().left, y - e.getPosition().top, state, button); 218 draggedElement = e; 219 220 return; 221 } 222 } 223 }else{ 224 if(draggedElement){ 225 draggedElement.onClick(x - draggedElement.getPosition().left, y - draggedElement.getPosition().top, state, button); 226 draggedElement = null; 227 } 228 } 229 } 230 /** 231 * Passes a mouseDragEvent if the user clicked on an element, held down the button, and moved the mouse. 232 */ 233 public void passMouseDragEvent(int x, int y, int relX, int relY, ubyte button){ 234 if(draggedElement){ 235 draggedElement.onDrag(x, y, relX, relY, button); 236 } 237 } 238 /** 239 * Passes a mouseMotionEvent if the user moved the mouse. 240 */ 241 public void passMouseMotionEvent(int x, int y, int relX, int relY, ubyte button){ 242 243 } 244 /** 245 * Closes the window by calling the WindowHandler's closeWindow function. 246 */ 247 public void close(){ 248 parent.closeWindow(this); 249 } 250 /** 251 * Passes the scroll event to the element where the mouse pointer currently stands. 252 */ 253 public void passScrollEvent(int wX, int wY, int x, int y){ 254 foreach(WindowElement e; scrollC){ 255 if(e.getPosition().left < wX && e.getPosition().right > wX && e.getPosition().top < wX && e.getPosition().bottom > wY){ 256 257 e.onScroll(x, y, wX, wY); 258 return; 259 } 260 } 261 } 262 /** 263 * Called if an extra button was pressed. 264 */ 265 public void extraButtonEvent(int num, ubyte button, int state){ 266 267 } 268 /** 269 * Passes a keyboard event. 270 */ 271 public void passKeyboardEvent(wchar c, int type, int x, int y){ 272 273 } 274 /** 275 * Adds a WindowHandler to the window. 276 */ 277 public void addParent(IWindowHandler wh){ 278 parent = wh; 279 } 280 public void getFocus(){ 281 282 } 283 public void dropFocus(){ 284 285 } 286 public Coordinate getAbsolutePosition(WindowElement sender){ 287 return Coordinate(sender.position.left + position.left, sender.position.top + position.top, sender.position.right + position.right, sender.position.bottom + position.bottom); 288 } 289 /** 290 * Moves the window to the exact location. 291 */ 292 public void move(int x, int y){ 293 parent.moveWindow(x, y, this); 294 position.move(x,y); 295 } 296 /** 297 * Moves the window by the given values. 298 */ 299 public void relMove(int x, int y){ 300 parent.relMoveWindow(x, y, this); 301 position.relMove(x,y); 302 } 303 /** 304 * Sets the height of the window, also issues a redraw. 305 */ 306 public void setHeight(int y){ 307 position.bottom = position.top + y; 308 draw(); 309 } 310 /** 311 * Sets the width of the window, also issues a redraw. 312 */ 313 public void setWidth(int x){ 314 position.right = position.left + x; 315 draw(); 316 } 317 /** 318 * Sets the size of the window, also issues a redraw. 319 */ 320 public void setSize(int x, int y){ 321 position.right = position.left + x; 322 position.bottom = position.top + y; 323 draw(); 324 } 325 } 326 327 /** 328 * Standard text input form for various applications. 329 */ 330 public class TextInputDialog : Window{ 331 //public ActionListener[] al; 332 private TextBox textInput; 333 private string source; 334 public void function(wstring text) textOutput; 335 /** 336 * Creates a TextInputDialog. Auto-sizing version is not implemented yet. 337 */ 338 public this(Coordinate size, string source, wstring title, wstring message, wstring text = ""){ 339 super(size, title); 340 Label msg = new Label(message, "null", Coordinate(8, 20, size.width()-8, 39)); 341 addElement(msg, EventProperties.MOUSE); 342 343 textInput = new TextBox(text, "textInput", Coordinate(8, 40, size.width()-8, 59)); 344 addElement(textInput, EventProperties.MOUSE); 345 346 Button ok = new Button("Ok","ok", Coordinate(size.width()-48,65,size.width()-8,84)); 347 ok.onMouseLClickRel = &button_onClick; 348 addElement(ok,EventProperties.MOUSE); 349 this.source = source; 350 } 351 352 public void button_onClick(Event ev){ 353 if(textOutput !is null){ 354 textOutput(textInput.getText); 355 } 356 357 close(); 358 359 } 360 } 361 /** 362 * Default dialog for simple messageboxes. 363 */ 364 public class DefaultDialog : Window{ 365 private string source; 366 public void delegate(Event ev) output; 367 368 public this(Coordinate size, string source, wstring title, wstring[] message, wstring[] options = ["Ok"], string[] values = ["close"]){ 369 this(size, title); 370 //generate text 371 this.source = source; 372 int x1, x2, y1 = 20, y2 = getStyleSheet.drawParameters["TextSpacingTop"] + getStyleSheet.drawParameters["TextSpacingBottom"] 373 + getStyleSheet.getFontset(getStyleSheet.fontTypes["Label"]).letters[' '].height; 374 //Label msg = new Label(message[0], "null", Coordinate(5, 20, size.width()-5, 40)); 375 //addElement(msg, EventProperties.MOUSE); 376 377 //generate buttons 378 379 x1 = size.width() - 10; 380 Button[] buttons; 381 int button1 = size.height - getStyleSheet.drawParameters["WindowBottomPadding"]; 382 int button2 = button1 - getStyleSheet.drawParameters["ComponentHeight"]; 383 for(int i; i < options.length; i++){ 384 x2 = x1 - ((getStyleSheet().getFontset("default").getTextLength(options[i]) + 16)); 385 buttons ~= new Button(options[i], values[i], Coordinate(x2, button2, x1, button1)); 386 buttons[i].onMouseLClickRel = &actionEvent; 387 addElement(buttons[i], EventProperties.MOUSE); 388 x1 = x2; 389 } 390 //add labels 391 for(int i; i < message.length; i++){ 392 Label msg = new Label(message[i], "null", Coordinate(getStyleSheet.drawParameters["WindowLeftPadding"], 393 y1, size.width()-getStyleSheet.drawParameters["WindowRightPadding"], y1 + y2)); 394 addElement(msg, EventProperties.MOUSE); 395 y1 += y2; 396 } 397 } 398 399 /*public this(string source, wstring title, wstring[] message, wstring[] options = ["Ok"], string[] values = ["ok"]){ 400 this(size, title); 401 }*/ 402 403 public this(Coordinate size, wstring title){ 404 super(size, title); 405 } 406 public void actionEvent(Event ev){ 407 if(ev.source == "close"){ 408 close(); 409 }else{ 410 if(output !is null){ 411 ev.subsource = source; 412 output(ev); 413 } 414 } 415 } 416 } 417 /** 418 * File dialog window for opening files. 419 * TODO: rewrite for new event handling system. 420 */ 421 public class FileDialog : Window{ 422 /** 423 * Defines file association descriptions 424 */ 425 public struct FileAssociationDescriptor{ 426 public wstring description; /// Describes the file type. Eg. "PPE map files" 427 public string[] types; /// The extensions associated with a given file format. Eg. ["*.htm","*.html"]. First is preferred one at saving. 428 /** 429 * Creates a single FileAssociationDescriptor 430 */ 431 public this(wstring description, string[] types){ 432 this.description = description; 433 this.types = types; 434 } 435 /** 436 * Returns the types as a single string. 437 */ 438 public wstring getTypesForSelector(){ 439 wstring result; 440 foreach(string s ; types){ 441 result ~= to!wstring(s); 442 result ~= ";"; 443 } 444 result.length--; 445 return result; 446 } 447 } 448 449 //private ActionListener al; 450 private string source; 451 private string[] pathList, driveList; 452 private string directory, filename; 453 private ListBox lb; 454 private TextBox tb; 455 456 private bool save; 457 private FileAssociationDescriptor[] filetypes; 458 public static const string subsourceID = "filedialog"; 459 private int selectedType; 460 public void delegate(Event ev) onFileselect; 461 462 /** 463 * Creates a file dialog with the given parameters. 464 * File types are given in the format '*.format', later implementations will enable file type descriptions. 465 */ 466 public this(wstring title, string source, void delegate(Event ev) onFileselect, FileAssociationDescriptor[] filetypes, string startDir, bool save = false, string filename = ""){ 467 this(Coordinate(20,20,240,198), title); 468 this.source = source; 469 this.filetypes = filetypes; 470 this.save = save; 471 //al = a; 472 directory = startDir; 473 //generate buttons 474 Button[] buttons; 475 buttons ~= new Button("Up","up",Coordinate(4, 154, 54, 174)); 476 buttons ~= new Button("Drive","drv",Coordinate(58, 154, 108, 174)); 477 if(save) 478 buttons ~= new Button("Save","ok",Coordinate(112, 154, 162, 174)); 479 else 480 buttons ~= new Button("Load","ok",Coordinate(112, 154, 162, 174)); 481 buttons ~= new Button("Close","close",Coordinate(166, 154, 216, 174)); 482 buttons ~= new Button("Type","type",Coordinate(166, 130, 216, 150)); 483 for(int i; i < buttons.length; i++){ 484 buttons[i].onMouseLClickRel = &actionEvent; 485 addElement(buttons[i], EventProperties.MOUSE); 486 } 487 //generate textbox 488 tb = new TextBox(to!wstring(filename), "filename", Coordinate(4, 130, 162, 150)); 489 //tb.addTextInputHandler(tih); 490 tb.onTextInput = &actionEvent; 491 addElement(tb, EventProperties.MOUSE); 492 //generate listbox 493 494 //test parameters 495 496 //Date format: yyyy-mm-dd hh:mm:ss 497 lb = new ListBox("lb", Coordinate(4, 20, 216, 126),null, new ListBoxHeader(["Name", "Type", "Date"], [160, 40, 176]) ,15); 498 499 addElement(lb, EventProperties.MOUSE | EventProperties.SCROLL); 500 spanDir(); 501 //scrollC ~= lb; 502 lb.onItemSelect = &actionEvent; 503 detectDrive(); 504 } 505 506 public this(Coordinate size, wstring title){ 507 super(size, title); 508 } 509 /** 510 * Iterates throught a directory for listing. 511 */ 512 private void spanDir(){ 513 pathList.length = 0; 514 ListBoxItem[] items; 515 foreach(DirEntry de; dirEntries(directory, SpanMode.shallow)){ 516 if(de.isDir){ 517 pathList ~= de.name; 518 /*columns[0].elements ~= to!wstring(getFilenameFromPath(de.name)); 519 columns[1].elements ~= "<DIR>"; 520 columns[2].elements ~= formatDate(de.timeLastModified);*/ 521 items ~= new ListBoxItem([to!wstring(getFilenameFromPath(de.name)),"<DIR>",formatDate(de.timeLastModified)]); 522 } 523 } 524 //foreach(f; filetypes){ 525 foreach(ft; filetypes[selectedType].types){ 526 foreach(DirEntry de; dirEntries(directory, ft, SpanMode.shallow)){ 527 if(de.isFile){ 528 pathList ~= de.name; 529 /*columns[0].elements ~= to!wstring(getFilenameFromPath(de.name, true)); 530 columns[1].elements ~= to!wstring(ft); 531 columns[2].elements ~= formatDate(de.timeLastModified);*/ 532 items ~= new ListBoxItem([to!wstring(getFilenameFromPath(de.name)),to!wstring(ft),formatDate(de.timeLastModified)]); 533 } 534 } 535 } 536 lb.updateColumns(items); 537 lb.draw(); 538 539 } 540 /** 541 * Standard date formatting tool. 542 */ 543 private wstring formatDate(SysTime time){ 544 wstring s; 545 s ~= to!wstring(time.year()); 546 s ~= "-"; 547 s ~= to!wstring(time.month()); 548 s ~= "-"; 549 s ~= to!wstring(time.day()); 550 s ~= " "; 551 s ~= to!wstring(time.hour()); 552 s ~= ":"; 553 s ~= to!wstring(time.minute()); 554 s ~= ":"; 555 s ~= to!wstring(time.second()); 556 return s; 557 } 558 /** 559 * Detects the available drives, currently only used under windows. 560 */ 561 private void detectDrive(){ 562 version(Windows){ 563 driveList.length = 0; 564 for(char c = 'A'; c <='Z'; c++){ 565 string s; 566 s ~= c; 567 s ~= ":\x5c"; 568 if(exists(s)){ 569 driveList ~= (s); 570 } 571 } 572 } 573 else{ 574 575 } 576 } 577 /** 578 * Returns the filename from the path. 579 */ 580 private string getFilenameFromPath(string p, bool b = false){ 581 int n, m = p.length; 582 string s; 583 for(int i ; i < p.length ; i++){ 584 if(std.path.isDirSeparator(p[i])){ 585 n = i; 586 } 587 } 588 //n++; 589 if(b){ 590 for(int i ; i < p.length ; i++){ 591 if(p[i] == '.'){ 592 m = i; 593 } 594 } 595 } 596 for( ; n < m ; n++){ 597 if(p[n] < 128 && p[n] > 31) 598 s ~= p[n]; 599 } 600 return s; 601 } 602 /** 603 * Called when the up button is pressed. Goes up in the folder hiearchy. 604 */ 605 private void up(){ 606 int n; 607 for(int i ; i < directory.length ; i++){ 608 if(std.path.isDirSeparator(directory[i])){ 609 n = i; 610 } 611 } 612 string newdir; 613 for(int i ; i < n ; i++){ 614 newdir ~= directory[i]; 615 } 616 directory = newdir; 617 spanDir(); 618 619 } 620 /** 621 * Displays the drives. Under Linux, it goes into the /dev/ folder. 622 */ 623 private void changeDrive(){ 624 version(Windows){ 625 pathList.length = 0; 626 ListBoxItem[] items; 627 foreach(string drive; driveList){ 628 pathList ~= drive; 629 items ~= new ListBoxItem([to!wstring(drive),"<DIR>","N/A"]); 630 } 631 lb.updateColumns(items); 632 lb.draw(); 633 }else version(Posix){ 634 directory = "/dev/"; 635 spanDir(); 636 } 637 } 638 /** 639 * Creates an action event, then closes the window. 640 */ 641 private void fileEvent(){ 642 //wstring s = to!wstring(directory); 643 filename = to!string(tb.getText); 644 //al.actionEvent("file", EventType.FILEDIALOGEVENT, 0, s); 645 if(onFileselect !is null) 646 onFileselect(new Event(source, "filedialog", directory, filename, null, 0, EventType.FILEDIALOGEVENT)); 647 parent.closeWindow(this); 648 } 649 650 public void actionEvent(Event event){ 651 652 if(event.subsource == "fileSelector"){ 653 selectedType = event.value; 654 spanDir(); 655 } 656 switch(event.source){ 657 case "lb": 658 try{ 659 if(pathList.length == 0) return; 660 if(isDir(pathList[event.value])){ 661 directory = pathList[event.value]; 662 spanDir(); 663 664 665 }else{ 666 filename = getFilenameFromPath(pathList[event.value]); 667 tb.setText(to!wstring(filename)); 668 } 669 }catch(Exception e){ 670 DefaultDialog d = new DefaultDialog(Coordinate(10,10,256,80),"null",to!wstring("Error!"), PixelPerfectEngine.system.etc.stringArrayConv([e.msg])); 671 parent.addWindow(d); 672 } 673 break; 674 case "up": up(); break; 675 case "drv": changeDrive(); break; 676 case "ok": fileEvent(); break; 677 case "close": parent.closeWindow(this); break; 678 case "type": 679 PopUpMenuElement[] e; 680 for(int i ; i < filetypes.length ; i++){ 681 e ~= new PopUpMenuElement(to!string(i),filetypes[i].description, filetypes[i].getTypesForSelector()); 682 } 683 PopUpMenu p = new PopUpMenu(e,"fileSelector"); 684 p.onMouseClick = &actionEvent; 685 parent.addPopUpElement(p); 686 break; 687 default: break; 688 } 689 } 690 } 691 /** 692 * Handles windows as well as PopUpElements. 693 */ 694 public class WindowHandler : InputListener, MouseListener, IWindowHandler{ 695 private Window[] windows; 696 private PopUpElement[] popUpElements; 697 private int numOfPopUpElements; 698 private int[] priorities; 699 public int screenX, screenY, rasterX, rasterY, moveX, moveY, mouseX, mouseY; 700 //public Bitmap16Bit[wchar] basicFont, altFont, alarmFont; 701 public StyleSheet defaultStyle; 702 //public Bitmap16Bit[int] styleBrush; 703 private ABitmap background; 704 private ISpriteLayer spriteLayer; 705 private bool moveState, dragEventState; 706 private Window windowToMove, dragEventDest; 707 private PopUpElement dragEventDestPopUp; 708 private ubyte lastMouseButton; 709 710 public this(int sx, int sy, int rx, int ry,ISpriteLayer sl){ 711 screenX = sx; 712 screenY = sy; 713 rasterX = rx; 714 rasterY = ry; 715 spriteLayer = sl; 716 } 717 718 public void addWindow(Window w){ 719 windows ~= w; 720 w.addParent(this); 721 //priorities ~= 666; 722 w.draw(); 723 setWindowToTop(w); 724 725 /*for(int i ; i < windows.length ; i++){ 726 spriteLayer.addSprite(windows[i].output.output, i, windows[i].position); 727 }*/ 728 } 729 730 /** 731 * Adds a DefaultDialog as a message box 732 */ 733 public void messageWindow(wstring title, wstring message, int width = 256){ 734 StyleSheet ss = getDefaultStyleSheet(); 735 wstring[] formattedMessage = ss.getFontset(ss.fontTypes["Label"]).breakTextIntoMultipleLines(message, width - ss.drawParameters["WindowLeftPadding"] - ss.drawParameters["WindowRightPadding"]); 736 int height = formattedMessage.length * (ss.getFontset(ss.fontTypes["Label"]).getSize() + ss.drawParameters["TextSpacingTop"] + ss.drawParameters["TextSpacingBottom"]); 737 height += ss.drawParameters["WindowTopPadding"] + ss.drawParameters["WindowBottomPadding"] + ss.drawParameters["ComponentHeight"]; 738 Coordinate c = Coordinate(mouseX - width / 2, mouseY - height / 2, mouseX + width / 2, mouseY + height / 2); 739 addWindow(new DefaultDialog(c, null, title, formattedMessage)); 740 } 741 public void addBackground(ABitmap b){ 742 background = b; 743 spriteLayer.addSprite(background, 65536, 0, 0, BitmapAttrib(false,false,0)); 744 } 745 746 private int whichWindow(Window w){ 747 for(int i ; i < windows.length ; i++){ 748 if(windows[i] == w){ 749 return i; 750 } 751 } 752 return -1; 753 } 754 755 public void setWindowToTop(Window sender){ 756 int s; 757 foreach(Window w; windows){ 758 if(w == sender){ 759 Window ww = windows[s]; 760 windows[s] = windows[0]; 761 windows[0] = ww; 762 updateSpriteOrder(); 763 break; 764 }else{ 765 s++; 766 } 767 } 768 769 } 770 771 private void updateSpriteOrder(){ 772 for(int i ; i < windows.length ; i++){ 773 spriteLayer.removeSprite(i); 774 spriteLayer.addSprite(windows[i].output.output, i, windows[i].position, BitmapAttrib(false,false,0)); 775 776 } 777 } 778 779 /*public Bitmap16Bit[wchar] getFontSet(int style){ 780 switch(style){ 781 case 0: return basicFont; 782 case 1: return altFont; 783 case 3: return alarmFont; 784 default: break; 785 } 786 return basicFont; 787 788 }*/ 789 public StyleSheet getStyleSheet(){ 790 return defaultStyle; 791 } 792 public void closeWindow(Window sender){ 793 //writeln(sender); 794 dragEventState = false; 795 dragEventDest = null; 796 int p = whichWindow(sender); 797 for(int i ; i < windows.length ; i++) 798 spriteLayer.removeSprite(i); 799 //spriteLayer.removeSprite(p); 800 windows = remove(windows, p); 801 802 updateSpriteOrder(); 803 } 804 805 public void moveUpdate(Window sender){ 806 moveState = true; 807 windowToMove = sender; 808 } 809 public void keyPressed(string ID, Uint32 timestamp, Uint32 devicenumber, Uint32 devicetype){ 810 811 } 812 public void keyReleased(string ID, Uint32 timestamp, Uint32 devicenumber, Uint32 devicetype){ 813 814 } 815 public void mouseButtonEvent(uint which, uint timestamp, uint windowID, ubyte button, ubyte state, ubyte clicks, int x, int y){ 816 817 //converting the dimensions 818 double xR = to!double(rasterX) / to!double(screenX) , yR = to!double(rasterY) / to!double(screenY); 819 x = to!int(x * xR); 820 y = to!int(y * yR); 821 mouseX = x; 822 mouseY = y; 823 //if(button == MouseButton.LEFT){ 824 if(state == ButtonState.PRESSED){ 825 if(numOfPopUpElements < 0){ 826 foreach(p ; popUpElements){ 827 if(y >= p.coordinates.top && y <= p.coordinates.bottom && x >= p.coordinates.left && x <= p.coordinates.right){ 828 p.onClick(x - p.coordinates.left, y - p.coordinates.top); 829 return; 830 } 831 } 832 //removeAllPopUps(); 833 removeTopPopUp(); 834 }else{ 835 moveX = x; 836 moveY = y; 837 for(int i ; i < windows.length ; i++){ 838 if(x >= windows[i].position.left && x <= windows[i].position.right && y >= windows[i].position.top && y <= windows[i].position.bottom){ 839 //if(i == 0){ 840 dragEventState = true; 841 windows[i].passMouseEvent(x - windows[i].position.left, y - windows[i].position.top, state, button); 842 if(dragEventState) 843 dragEventDest = windows[i]; 844 /*if(windows.length !=0){ 845 dragEventState = true; 846 dragEventDest = windows[0]; 847 }*/ 848 //return; 849 //}else{ 850 if(i != 0){ 851 setWindowToTop(windows[i]); 852 853 } 854 lastMouseButton = button; 855 return; 856 } 857 } 858 passMouseEvent(x,y,state,button); 859 860 } 861 }else{ 862 if(moveState){ 863 moveState = false; 864 }else if(dragEventDest){ 865 dragEventDest.passMouseEvent(x - dragEventDest.position.left, y - dragEventDest.position.top, state, button); 866 dragEventDest = null; 867 }else{ 868 passMouseEvent(x,y,state,button); 869 } 870 } 871 } 872 public void passMouseEvent(int x, int y, int state, ubyte button){ 873 874 } 875 public void passMouseDragEvent(int x, int y, int relX, int relY, ubyte button){ 876 } 877 public void passMouseMotionEvent(int x, int y, int relX, int relY, ubyte button){ 878 } 879 public void mouseWheelEvent(uint type, uint timestamp, uint windowID, uint which, int x, int y, int wX, int wY){ 880 double xR = to!double(rasterX) / to!double(screenX) , yR = to!double(rasterY) / to!double(screenY); 881 wX = to!int(wX * xR); 882 wY = to!int(wY * yR); 883 if(windows.length != 0) 884 windows[0].passScrollEvent(wX - windows[0].position.left, wY - windows[0].position.top, y, x); 885 passScrollEvent(wX,wY,x,y); 886 } 887 public void passScrollEvent(int wX, int wY, int x, int y){ 888 889 } 890 public void mouseMotionEvent(uint timestamp, uint windowID, uint which, uint state, int x, int y, int relX, int relY){ 891 //coordinate conversion 892 double xR = to!double(rasterX) / to!double(screenX) , yR = to!double(rasterY) / to!double(screenY); 893 x = to!int(x * xR); 894 y = to!int(y * yR); 895 relX = to!int(relX * xR); 896 relY = to!int(relY * yR); 897 //passing mouseMovementEvent onto PopUps 898 if(numOfPopUpElements < 0){ 899 PopUpElement p = popUpElements[popUpElements.length - 1]; 900 if(p.coordinates.top < y && p.coordinates.bottom > y && p.coordinates.left < x && p.coordinates.right > x){ 901 p.onMouseMovement(x - p.coordinates.left, y - p.coordinates.top); 902 return; 903 }else{ 904 p.onMouseMovement(-1,-1); 905 } 906 907 } 908 if(state == ButtonState.PRESSED && moveState){ 909 windowToMove.relMove(relX, relY); 910 }else if(state == ButtonState.PRESSED && dragEventDest){ 911 dragEventDest.passMouseDragEvent(x, y, relX, relY, lastMouseButton); 912 }else{ 913 if(windows.length){ 914 windows[0].passMouseMotionEvent(x, y, relX, relY, lastMouseButton); 915 } 916 } 917 } 918 public void moveWindow(int x, int y, Window w){ 919 spriteLayer.relMoveSprite(whichWindow(w), x, y); 920 921 } 922 public void relMoveWindow(int x, int y, Window w){ 923 spriteLayer.relMoveSprite(whichWindow(w), x, y); 924 } 925 public void addPopUpElement(PopUpElement p){ 926 popUpElements ~= p; 927 p.addParent(this); 928 p.draw; 929 p.coordinates.move(mouseX, mouseY); 930 numOfPopUpElements--; 931 spriteLayer.addSprite(p.output.output,numOfPopUpElements,mouseX,mouseY,BitmapAttrib(false,false,0)); 932 933 } 934 public void addPopUpElement(PopUpElement p, int x, int y){ 935 popUpElements ~= p; 936 p.addParent(this); 937 p.draw; 938 p.coordinates.move(x, y); 939 numOfPopUpElements--; 940 spriteLayer.addSprite(p.output.output,numOfPopUpElements, x, y,BitmapAttrib(false,false,0)); 941 } 942 private void removeAllPopUps(){ 943 for( ; numOfPopUpElements < 0 ; numOfPopUpElements++){ 944 spriteLayer.removeSprite(numOfPopUpElements); 945 } 946 popUpElements.length = 0; 947 } 948 private void removeTopPopUp(){ 949 950 spriteLayer.removeSprite(numOfPopUpElements++); 951 952 popUpElements.length--; 953 } 954 public StyleSheet getDefaultStyleSheet(){ 955 return defaultStyle; 956 } 957 public void endPopUpSession(){ 958 removeAllPopUps(); 959 } 960 public void closePopUp(PopUpElement p){ 961 962 } 963 public void drawUpdate(WindowElement sender){} 964 public void getFocus(WindowElement sender){} 965 public void dropFocus(WindowElement sender){} 966 public void drawUpdate(Window sender){ 967 int p = whichWindow(sender); 968 spriteLayer.removeSprite(p); 969 spriteLayer.addSprite(sender.output.output,p,sender.position,BitmapAttrib(false,false,0)); 970 } 971 } 972 973 public interface IWindowHandler : PopUpHandler{ 974 //public Bitmap16Bit[wchar] getFontSet(int style); 975 public StyleSheet getStyleSheet(); 976 public void closeWindow(Window sender); 977 public void moveUpdate(Window sender); 978 public void setWindowToTop(Window sender); 979 public void addWindow(Window w); 980 public void moveWindow(int x, int y, Window w); 981 public void relMoveWindow(int x, int y, Window w); 982 public void drawUpdate(Window sender); 983 public void messageWindow(wstring title, wstring message, int width = 256); 984 } 985 986 public enum EventProperties : uint{ 987 KEYBOARD = 1, 988 MOUSE = 2, 989 SCROLL = 4 990 }