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