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