1 /** DGui project file. 2 3 Copyright: Trogu Antonio Davide 2011-2013 4 5 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 6 7 Authors: Trogu Antonio Davide 8 */ 9 module dgui.core.controls.control; 10 11 public import dgui.core.interfaces.idisposable; 12 public import dgui.core.events.controlcodeeventargs; 13 public import dgui.core.events.scrolleventargs; 14 public import dgui.core.events.mouseeventargs; 15 public import dgui.core.events.painteventargs; 16 public import dgui.core.events.keyeventargs; 17 public import dgui.core.events.event; 18 public import dgui.core.windowclass; 19 public import dgui.core.message; 20 public import dgui.core.charset; 21 public import dgui.core.winapi; 22 public import dgui.core.exception; 23 public import dgui.core.geometry; 24 public import dgui.core.collection; 25 public import dgui.core.handle; 26 public import dgui.core.utils; 27 public import dgui.core.tag; 28 public import dgui.contextmenu; 29 public import dgui.canvas; 30 31 enum DockStyle: ubyte 32 { 33 none = 0, 34 left = 1, 35 top = 2, 36 right = 4, 37 bottom = 8, 38 fill = 16, 39 height = 32, 40 width = 64 41 } 42 43 enum PositionSpecified 44 { 45 position = 0, 46 size = 1, 47 all = 2, 48 } 49 50 enum ControlBits: ulong 51 { 52 none = 0, 53 erased = 1, 54 mouseEnter = 2, 55 canNotify = 4, 56 modalControl = 8, // For Modal Dialogs 57 doubleBuffered = 16, // Use DGui's double buffered routine to draw components (be careful with this one!) 58 ownClickMsg = 32, // Does the component Handles click itself? 59 cannotAddChild = 64, // The child window will not be added to the parent's child controls' list 60 useCachedText = 128, // Does not send WM_SETTEXT / WM_GETTEXT messages, but it uses it's internal variable only. 61 } 62 63 enum BorderStyle: ubyte 64 { 65 none = 0, 66 manual = 1, // Internal Use 67 fixedSingle = 2, 68 fixed3d = 4, 69 } 70 71 struct CreateControlParams 72 { 73 string className; 74 string superclassName; //Used in Superlassing 75 Color defaultBackColor; 76 Color defaultForeColor; 77 Cursor defaultCursor; 78 ClassStyles classStyle; 79 } 80 81 abstract class Control: Handle!(HWND), IDisposable 82 { 83 private ContextMenu _menu; 84 private Control _parent; 85 private ContextMenu _ctxMenu; 86 private Font _defaultFont; 87 private Cursor _defaultCursor; 88 private HBRUSH _foreBrush; 89 private HBRUSH _backBrush; 90 private uint _extendedStyle = 0; 91 private uint _style = WS_VISIBLE; 92 protected string _text; 93 protected Rect _bounds; 94 protected Color _foreColor; 95 protected Color _backColor; 96 protected DockStyle _dock = DockStyle.none; 97 protected ControlBits _cBits = ControlBits.canNotify; 98 99 public Event!(Control, PaintEventArgs) paint; 100 public Event!(Control, EventArgs) focusChanged; 101 public Event!(Control, KeyCharEventArgs) keyChar; 102 public Event!(Control, ControlCodeEventArgs) controlCode; 103 public Event!(Control, KeyEventArgs) keyDown; 104 public Event!(Control, KeyEventArgs) keyUp; 105 public Event!(Control, MouseEventArgs) doubleClick; 106 public Event!(Control, MouseEventArgs) mouseKeyDown; 107 public Event!(Control, MouseEventArgs) mouseKeyUp; 108 public Event!(Control, MouseEventArgs) mouseMove; 109 public Event!(Control, MouseEventArgs) mouseEnter; 110 public Event!(Control, MouseEventArgs) mouseLeave; 111 public Event!(Control, EventArgs) visibleChanged; 112 public Event!(Control, EventArgs) handleCreated; 113 public Event!(Control, EventArgs) resize; 114 public Event!(Control, EventArgs) click; 115 116 mixin tagProperty; // Insert tag() property in Control 117 118 public this() 119 { 120 121 } 122 123 public ~this() 124 { 125 this.dispose(); 126 } 127 128 public void dispose() 129 { 130 if(this._backBrush) 131 { 132 DeleteObject(this._backBrush); 133 } 134 135 if(this._foreBrush) 136 { 137 DeleteObject(this._foreBrush); 138 } 139 140 if(this._handle) 141 { 142 /* From MSDN: Destroys the specified window. 143 The function sends WM_DESTROY and WM_NCDESTROY messages to the window 144 to deactivate it and remove the keyboard focus from it. 145 The function also destroys the window's menu, flushes the thread message queue, 146 destroys timers, removes clipboard ownership, and breaks the clipboard viewer chain 147 (if the window is at the top of the viewer chain). If the specified window is a parent 148 or owner window, DestroyWindow automatically destroys the associated child or owned 149 windows when it destroys the parent or owner window. The function first destroys child 150 or owned windows, and then it destroys the parent or owner window 151 */ 152 153 DestroyWindow(this._handle); 154 } 155 156 this._handle = null; 157 } 158 159 160 public static void convertRect(ref Rect rect, Control from, Control to) 161 { 162 MapWindowPoints(from ? from.handle : null, to ? to.handle : null, cast(POINT*)&rect.rect, 2); 163 } 164 165 public static void convertPoint(ref Point pt, Control from, Control to) 166 { 167 MapWindowPoints(from ? from.handle : null, to ? to.handle : null, &pt.point, 1); 168 } 169 170 public static void convertSize(ref Size sz, Control from, Control to) 171 { 172 MapWindowPoints(from ? from.handle : null, to ? to.handle : null, cast(POINT*)&sz.size, 1); 173 } 174 175 @property public final Rect bounds() 176 { 177 return this._bounds; 178 } 179 180 @property public void bounds(Rect rect) 181 { 182 this._bounds = rect; 183 184 if(this.created) 185 { 186 this.setWindowPos(rect.left, rect.top, rect.width, rect.height); 187 } 188 } 189 190 @property public final BorderStyle borderStyle() 191 { 192 if(this.getExStyle() & WS_EX_CLIENTEDGE) 193 { 194 return BorderStyle.fixed3d; 195 } 196 else if(this.getStyle() & WS_BORDER) 197 { 198 return BorderStyle.fixedSingle; 199 } 200 201 return BorderStyle.none; 202 } 203 204 @property public final void borderStyle(BorderStyle bs) 205 { 206 switch(bs) 207 { 208 case BorderStyle.fixed3d: 209 this.setStyle(WS_BORDER, false); 210 this.setExStyle(WS_EX_CLIENTEDGE, true); 211 break; 212 213 case BorderStyle.fixedSingle: 214 this.setStyle(WS_BORDER, true); 215 this.setExStyle(WS_EX_CLIENTEDGE, false); 216 break; 217 218 case BorderStyle.none: 219 this.setStyle(WS_BORDER, false); 220 this.setExStyle(WS_EX_CLIENTEDGE, false); 221 break; 222 223 default: 224 assert(0, "Unknown Border Style"); 225 //break; 226 } 227 } 228 229 @property public final Control parent() 230 { 231 return this._parent; 232 } 233 234 @property public void parent(Control c) 235 { 236 this._parent = c; 237 238 if(!Control.hasBit(this._cBits, ControlBits.cannotAddChild)) 239 { 240 c.sendMessage(DGUI_ADDCHILDCONTROL, winCast!(WPARAM)(this), 0); 241 } 242 } 243 244 @property public final Control topLevelControl() 245 { 246 Control topCtrl = this; 247 248 while(topCtrl.parent) 249 { 250 topCtrl = topCtrl.parent; 251 } 252 253 return topCtrl; 254 } 255 256 public final Canvas createCanvas() 257 { 258 return Canvas.fromHDC(GetDC(this._handle)); 259 } 260 261 public final void focus() 262 { 263 if(this.created) 264 { 265 SetFocus(this._handle); 266 } 267 } 268 269 @property public bool focused() 270 { 271 if(this.created) 272 { 273 return GetFocus() == this._handle; 274 } 275 276 return false; 277 } 278 279 @property public final Color backColor() 280 { 281 return this._backColor; 282 } 283 284 @property public final void backColor(Color c) 285 { 286 if(this._backBrush) 287 { 288 DeleteObject(this._backBrush); 289 } 290 291 this._backColor = c; 292 this._backBrush = CreateSolidBrush(c.colorref); 293 294 if(this.created) 295 { 296 this.invalidate(); 297 } 298 } 299 300 @property public final Color foreColor() 301 { 302 return this._foreColor; 303 } 304 305 @property public final void foreColor(Color c) 306 { 307 if(this._foreBrush) 308 { 309 DeleteObject(this._foreBrush); 310 } 311 312 this._foreColor = c; 313 this._foreBrush = CreateSolidBrush(c.colorref); 314 315 if(this.created) 316 { 317 this.invalidate(); 318 } 319 } 320 321 @property public final bool scrollBars() 322 { 323 return cast(bool)(this.getStyle() & (WS_VSCROLL | WS_HSCROLL)); 324 } 325 326 @property public final void scrollBars(bool b) 327 { 328 this.setStyle(WS_VSCROLL | WS_HSCROLL, true); 329 } 330 331 @property public string text() 332 { 333 if(this.created && !Control.hasBit(this._cBits, ControlBits.useCachedText)) 334 { 335 return getWindowText(this._handle); 336 } 337 338 return this._text; 339 } 340 341 @property public void text(string s) //Overwritten in TabPage 342 { 343 this._text = s; 344 345 if(this.created && !Control.hasBit(this._cBits, ControlBits.useCachedText)) 346 { 347 Control.setBit(this._cBits, ControlBits.canNotify, false); //Do not trigger TextChanged Event 348 setWindowText(this._handle, s); 349 Control.setBit(this._cBits, ControlBits.canNotify, true); 350 } 351 } 352 353 @property public final Font font() 354 { 355 if(!this._defaultFont) 356 { 357 /* Font is not set, use Windows Font */ 358 this._defaultFont = SystemFonts.windowsFont; 359 } 360 361 return this._defaultFont; 362 } 363 364 @property public final void font(Font f) 365 { 366 if(this.created) 367 { 368 if(this._defaultFont) 369 { 370 this._defaultFont.dispose(); 371 } 372 373 this.sendMessage(WM_SETFONT, cast(WPARAM)f.handle, true); 374 } 375 376 this._defaultFont = f; 377 } 378 379 @property public final Point position() 380 { 381 return this.bounds.position; 382 } 383 384 @property public final void position(Point pt) 385 { 386 this._bounds.position = pt; 387 388 if(this.created) 389 { 390 this.setPosition(pt.x, pt.y); 391 } 392 } 393 394 @property public final Size size() 395 { 396 return this._bounds.size; 397 } 398 399 @property public final void size(Size sz) 400 { 401 this._bounds.size = sz; 402 403 if(this.created) 404 { 405 this.setSize(sz.width, sz.height); 406 } 407 } 408 409 @property public final Size clientSize() 410 { 411 if(this.created) 412 { 413 Rect r = void; 414 415 GetClientRect(this._handle, &r.rect); 416 return r.size; 417 } 418 419 return this.size; 420 } 421 422 @property public final ContextMenu contextMenu() 423 { 424 return this._ctxMenu; 425 } 426 427 @property public final void contextMenu(ContextMenu cm) 428 { 429 if(this._ctxMenu !is cm) 430 { 431 if(this._ctxMenu) 432 { 433 this._ctxMenu.dispose(); 434 } 435 436 this._ctxMenu = cm; 437 } 438 } 439 440 @property public final int width() 441 { 442 return this._bounds.width; 443 } 444 445 @property public final void width(int w) 446 { 447 this._bounds.width = w; 448 449 if(this.created) 450 { 451 this.setSize(w, this._bounds.height); 452 } 453 } 454 455 @property public final int height() 456 { 457 return this._bounds.height; 458 } 459 460 @property public final void height(int h) 461 { 462 this._bounds.height = h; 463 464 if(this.created) 465 { 466 this.setSize(this._bounds.width, h); 467 } 468 } 469 470 @property public final DockStyle dock() 471 { 472 return this._dock; 473 } 474 475 @property public final void dock(DockStyle ds) 476 { 477 this._dock = ds; 478 } 479 480 @property public final Cursor cursor() 481 { 482 if(this.created) 483 { 484 return Cursor.fromHCURSOR(cast(HCURSOR)GetClassLongW(this._handle, GCL_HCURSOR), false); 485 } 486 487 return this._defaultCursor; 488 } 489 490 @property public final void cursor(Cursor c) 491 { 492 if(this._defaultCursor) 493 { 494 this._defaultCursor.dispose(); 495 } 496 497 this._defaultCursor = c; 498 499 if(this.created) 500 { 501 this.sendMessage(WM_SETCURSOR, cast(WPARAM)this._handle, 0); 502 } 503 } 504 505 @property public final bool visible() 506 { 507 return cast(bool)(this.getStyle() & WS_VISIBLE); 508 } 509 510 @property public final void visible(bool b) 511 { 512 b ? this.show() : this.hide(); 513 } 514 515 @property public final bool enabled() 516 { 517 return !(this.getStyle() & WS_DISABLED); 518 } 519 520 @property public final void enabled(bool b) 521 { 522 if(this.created) 523 { 524 EnableWindow(this._handle, b); 525 } 526 else 527 { 528 this.setStyle(WS_DISABLED, !b); 529 } 530 } 531 532 public void show() 533 { 534 if(this.created) 535 { 536 SetWindowPos(this._handle, null, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); 537 538 if(this._parent) 539 { 540 this._parent.sendMessage(DGUI_DOLAYOUT, 0, 0); 541 } 542 } 543 else 544 { 545 this.setStyle(WS_VISIBLE, true); 546 this.create(); //The component is not created, create it now 547 } 548 } 549 550 public final void hide() 551 { 552 if(this.created) 553 { 554 SetWindowPos(this._handle, null, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); 555 } 556 else 557 { 558 this.setStyle(WS_VISIBLE, false); 559 } 560 } 561 562 public final void redraw() 563 { 564 SetWindowPos(this._handle, null, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); 565 } 566 567 public final void invalidate() 568 { 569 RedrawWindow(this._handle, null, null, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW); 570 } 571 572 public final void sendMessage(ref Message m) 573 { 574 /* 575 * SendMessage() emulation: it allows to send messages even if the control is not created, 576 * it is useful in order to send custom messages to components. 577 */ 578 579 if(m.msg >= DGUI_BASE) /* DGui's Custom Message Handling */ 580 { 581 this.onDGuiMessage(m); 582 } 583 else /* Window Procedure Message Handling */ 584 { 585 //Control.setBit(this._cBits, ControlBits.canNotify, false); 586 this.wndProc(m); 587 //Control.setBit(this._cBits, ControlBits.canNotify, true); 588 } 589 } 590 591 public final uint sendMessage(uint msg, WPARAM wParam, LPARAM lParam) 592 { 593 Message m = Message(this._handle, msg, wParam, lParam); 594 this.sendMessage(m); 595 596 return m.result; 597 } 598 599 extern(Windows) package static LRESULT msgRouter(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam) 600 { 601 if(msg == WM_NCCREATE) 602 { 603 /* 604 * TRICK: Id == hWnd 605 * --- 606 * Inizializzazione Componente 607 */ 608 609 CREATESTRUCTW* pCreateStruct = cast(CREATESTRUCTW*)lParam; 610 LPARAM param = cast(LPARAM)pCreateStruct.lpCreateParams; 611 SetWindowLongW(hWnd, GWL_USERDATA, param); 612 SetWindowLongW(hWnd, GWL_ID, cast(uint)hWnd); 613 614 Control theThis = winCast!(Control)(param); 615 theThis._handle = hWnd; //Assign handle. 616 } 617 618 Control theThis = winCast!(Control)(GetWindowLongW(hWnd, GWL_USERDATA)); 619 Message m = Message(hWnd, msg, wParam, lParam); 620 621 622 if(theThis) 623 { 624 theThis.wndProc(m); 625 } 626 else 627 { 628 Control.defWindowProc(m); 629 } 630 631 return m.result; 632 } 633 634 private void onMenuCommand(WPARAM wParam, LPARAM lParam) 635 { 636 MENUITEMINFOW minfo; 637 638 minfo.cbSize = MENUITEMINFOW.sizeof; 639 minfo.fMask = MIIM_DATA; 640 641 if(GetMenuItemInfoW(cast(HMENU)lParam, cast(UINT)wParam, TRUE, &minfo)) 642 { 643 MenuItem sender = winCast!(MenuItem)(minfo.dwItemData); 644 sender.performClick(); 645 } 646 } 647 648 private void create() 649 { 650 CreateControlParams ccp; 651 ccp.defaultBackColor = SystemColors.colorButtonFace; 652 ccp.defaultForeColor = SystemColors.colorButtonText; 653 654 this.createControlParams(ccp); 655 656 this._backBrush = CreateSolidBrush(ccp.defaultBackColor.colorref); 657 this._foreBrush = CreateSolidBrush(ccp.defaultForeColor.colorref); 658 659 if(ccp.defaultCursor) 660 { 661 this._defaultCursor = ccp.defaultCursor; 662 } 663 664 if(!this._defaultFont) 665 { 666 this._defaultFont = SystemFonts.windowsFont; 667 } 668 669 if(!this._backColor.valid) // Invalid Color 670 { 671 this.backColor = ccp.defaultBackColor; 672 } 673 674 if(!this._foreColor.valid) // Invalid Color 675 { 676 this.foreColor = ccp.defaultForeColor; 677 } 678 679 HWND hParent = null; 680 681 if(Control.hasBit(this._cBits, ControlBits.modalControl)) //Is Modal ? 682 { 683 hParent = GetActiveWindow(); 684 this.setStyle(WS_CHILD, false); 685 this.setStyle(WS_POPUP, true); 686 } 687 else if(this._parent) 688 { 689 hParent = this._parent.handle; 690 691 /* As MSDN says: 692 WS_POPUP: The windows is a pop-up window. *** This style cannot be used with the WS_CHILD style. *** */ 693 694 if(!(this.getStyle() & WS_POPUP)) //The windows doesn't have WS_POPUP style, set WS_CHILD style. 695 { 696 this.setStyle(WS_CHILD, true); 697 } 698 699 this.setStyle(WS_CLIPSIBLINGS, true); 700 } 701 702 createWindowEx(this.getExStyle(), 703 ccp.className, 704 this._text, 705 this.getStyle(), 706 this._bounds.x, 707 this._bounds.y, 708 this._bounds.width, 709 this._bounds.height, 710 hParent, 711 winCast!(void*)(this)); 712 713 if(!this._handle) 714 { 715 throwException!(Win32Exception)("Control Creation failed: (ClassName: '%s', Text: '%s')", 716 ccp.className, this._text); 717 } 718 719 UpdateWindow(this._handle); 720 721 if(this._parent) 722 { 723 this._parent.sendMessage(DGUI_CHILDCONTROLCREATED, winCast!(WPARAM)(this), 0); //Notify the parent window 724 } 725 } 726 727 private void setPosition(int x, int y) 728 { 729 this.setWindowPos(x, y, 0, 0, PositionSpecified.position); 730 } 731 732 private void setSize(int w, int h) 733 { 734 this.setWindowPos(0, 0, w, h, PositionSpecified.size); 735 } 736 737 private void setWindowPos(int x, int y, int w, int h, PositionSpecified ps = PositionSpecified.all) 738 { 739 uint wpf = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE; 740 741 if(ps !is PositionSpecified.all) 742 { 743 if(ps is PositionSpecified.position) 744 { 745 wpf &= ~SWP_NOMOVE; 746 } 747 else //if(ps is PositionSpecified.size) 748 { 749 wpf &= ~SWP_NOSIZE; 750 } 751 } 752 else 753 { 754 wpf &= ~(SWP_NOMOVE | SWP_NOSIZE); 755 } 756 757 SetWindowPos(this._handle, null, x, y, w, h, wpf); //Bounds updated in WM_WINDOWPOSCHANGED 758 } 759 760 private void drawMenuItemImage(DRAWITEMSTRUCT* pDrawItem) 761 { 762 MenuItem mi = winCast!(MenuItem)(pDrawItem.itemData); 763 764 if(mi) 765 { 766 scope Canvas c = Canvas.fromHDC(pDrawItem.hDC, false); //HDC *Not* Owned by Canvas Object 767 int icoSize = GetSystemMetrics(SM_CYMENU); 768 c.drawImage(mi.rootMenu.imageList.images[mi.imageIndex], Rect(0, 0, icoSize, icoSize)); 769 } 770 } 771 772 protected final uint getStyle() 773 { 774 if(this.created) 775 { 776 return GetWindowLongW(this._handle, GWL_STYLE); 777 } 778 779 return this._style; 780 } 781 782 protected final void setStyle(uint cstyle, bool set) 783 { 784 if(this.created) 785 { 786 uint style = this.getStyle(); 787 set ? (style |= cstyle) : (style &= ~cstyle); 788 789 SetWindowLongW(this._handle, GWL_STYLE, style); 790 this.redraw(); 791 this._style = style; 792 } 793 else 794 { 795 set ? (this._style |= cstyle) : (this._style &= ~cstyle); 796 } 797 } 798 799 protected static final void setBit(T)(ref T rBits, T rBit, bool set) 800 if(is(T B == enum) && is(B == ulong)) 801 { 802 set ? (rBits |= rBit) : (rBits &= ~rBit); 803 } 804 805 protected static final bool hasBit(T)(ref T rBits, T rBit) 806 if(is(T B == enum) && is(B == ulong)) 807 { 808 return cast(bool)(rBits & rBit); 809 } 810 811 protected final uint getExStyle() 812 { 813 if(this.created) 814 { 815 return GetWindowLongW(this._handle, GWL_EXSTYLE); 816 } 817 818 return this._extendedStyle; 819 } 820 821 protected final void setExStyle(uint cstyle, bool set) 822 { 823 if(this.created) 824 { 825 uint exStyle = this.getExStyle(); 826 set ? (exStyle |= cstyle) : (exStyle &= ~cstyle); 827 828 SetWindowLongW(this._handle, GWL_EXSTYLE, exStyle); 829 this.redraw(); 830 this._extendedStyle = exStyle; 831 } 832 else 833 { 834 set ? (this._extendedStyle |= cstyle) : (this._extendedStyle &= ~cstyle); 835 } 836 } 837 838 protected void createControlParams(ref CreateControlParams ccp) 839 { 840 ClassStyles cstyle = ccp.classStyle | ClassStyles.doubleClicks; 841 842 WindowClass.register(ccp.className, cstyle, ccp.defaultCursor, cast(WNDPROC) /*FIXME may throw*/ &Control.msgRouter); 843 } 844 845 protected uint originalWndProc(ref Message m) 846 { 847 return Control.defWindowProc(m); 848 } 849 850 protected static uint defWindowProc(ref Message m) 851 { 852 if(IsWindowUnicode(m.hWnd)) 853 { 854 m.result = DefWindowProcW(m.hWnd, m.msg, m.wParam, m.lParam); 855 } 856 else 857 { 858 m.result = DefWindowProcA(m.hWnd, m.msg, m.wParam, m.lParam); 859 } 860 861 return m.result; 862 } 863 864 protected void onDGuiMessage(ref Message m) 865 { 866 switch(m.msg) 867 { 868 case DGUI_REFLECTMESSAGE: 869 Message rm = *(cast(Message*)m.wParam); 870 this.onReflectedMessage(rm); 871 *(cast(Message*)m.wParam) = rm; //Copy the result, so the parent can return result. 872 //m.result = rm.result; // No result here! 873 break; 874 875 case DGUI_CREATEONLY: 876 { 877 if(!this.created) 878 { 879 this.create(); 880 } 881 } 882 break; 883 884 default: 885 m.result = 0; 886 break; 887 } 888 } 889 890 protected void onReflectedMessage(ref Message m) 891 { 892 switch(m.msg) 893 { 894 case WM_CTLCOLOREDIT, WM_CTLCOLORBTN: 895 SetBkColor(cast(HDC)m.wParam, this.backColor.colorref); 896 SetTextColor(cast(HDC)m.wParam, this.foreColor.colorref); 897 m.result = cast(LRESULT)this._backBrush; 898 break; 899 900 case WM_MEASUREITEM: 901 { 902 MEASUREITEMSTRUCT* pMeasureItem = cast(MEASUREITEMSTRUCT*)m.lParam; 903 904 if(pMeasureItem.CtlType == ODT_MENU) 905 { 906 MenuItem mi = winCast!(MenuItem)(pMeasureItem.itemData); 907 908 if(mi) 909 { 910 if(mi.parent.handle == GetMenu(this._handle))// Check if parent of 'mi' is the menu bar 911 { 912 FontMetrics fm = this.font.metrics; 913 914 int icoSize = GetSystemMetrics(SM_CYMENU); 915 pMeasureItem.itemWidth = icoSize + fm.maxCharWidth; 916 } 917 else 918 { 919 pMeasureItem.itemWidth = 10; 920 } 921 } 922 } 923 } 924 break; 925 926 case WM_DRAWITEM: 927 { 928 DRAWITEMSTRUCT* pDrawItem = cast(DRAWITEMSTRUCT*)m.lParam; 929 930 if(pDrawItem.CtlType == ODT_MENU) 931 { 932 this.drawMenuItemImage(pDrawItem); 933 } 934 } 935 break; 936 937 default: 938 //Control.defWindowProc(m); 939 break; 940 } 941 } 942 943 protected void onClick(EventArgs e) 944 { 945 this.click(this, e); 946 } 947 948 protected void onKeyUp(KeyEventArgs e) 949 { 950 this.keyUp(this, e); 951 } 952 953 protected void onKeyDown(KeyEventArgs e) 954 { 955 this.keyDown(this, e); 956 } 957 958 protected void onKeyChar(KeyCharEventArgs e) 959 { 960 this.keyChar(this, e); 961 } 962 963 protected void onPaint(PaintEventArgs e) 964 { 965 this.paint(this, e); 966 } 967 968 protected void onHandleCreated(EventArgs e) 969 { 970 this.handleCreated(this, e); 971 } 972 973 protected void onResize(EventArgs e) 974 { 975 this.resize(this, e); 976 } 977 978 protected void onVisibleChanged(EventArgs e) 979 { 980 this.visibleChanged(this, e); 981 } 982 983 protected void onMouseKeyDown(MouseEventArgs e) 984 { 985 this.mouseKeyDown(this, e); 986 } 987 988 protected void onMouseKeyUp(MouseEventArgs e) 989 { 990 this.mouseKeyUp(this, e); 991 } 992 993 protected void onDoubleClick(MouseEventArgs e) 994 { 995 this.doubleClick(this, e); 996 } 997 998 protected void onMouseMove(MouseEventArgs e) 999 { 1000 this.mouseMove(this, e); 1001 } 1002 1003 protected void onMouseEnter(MouseEventArgs e) 1004 { 1005 this.mouseEnter(this, e); 1006 } 1007 1008 protected void onMouseLeave(MouseEventArgs e) 1009 { 1010 this.mouseLeave(this, e); 1011 } 1012 1013 protected void onFocusChanged(EventArgs e) 1014 { 1015 this.focusChanged(this, e); 1016 } 1017 1018 protected void onControlCode(ControlCodeEventArgs e) 1019 { 1020 this.controlCode(this, e); 1021 } 1022 1023 protected void wndProc(ref Message m) 1024 { 1025 switch(m.msg) 1026 { 1027 case WM_ERASEBKGND: 1028 m.result = 0; // Do nothing here, handle it in WM_PAINT 1029 break; 1030 1031 case WM_PAINT: 1032 { 1033 HDC hdc; 1034 Rect clipRect; 1035 PAINTSTRUCT ps; 1036 1037 if(!m.wParam) 1038 { 1039 hdc = BeginPaint(this._handle, &ps); 1040 clipRect = Rect.fromRECT(&ps.rcPaint); //Clip Rectangle 1041 } 1042 else // Assume WPARAM as HDC 1043 { 1044 hdc = cast(HDC)m.wParam; 1045 GetUpdateRect(this._handle, &clipRect.rect, false); 1046 } 1047 1048 FillRect(hdc, &clipRect.rect, this._backBrush); //Fill with background color; 1049 1050 scope Canvas c = Canvas.fromHDC(hdc, false); 1051 scope PaintEventArgs e = new PaintEventArgs(c, clipRect); 1052 this.onPaint(e); 1053 1054 if(!m.wParam) 1055 { 1056 EndPaint(this._handle, &ps); 1057 } 1058 1059 m.result = 0; 1060 } 1061 break; 1062 1063 case WM_CREATE: // Aggiornamento Font, rimuove FIXED SYS 1064 { 1065 this.sendMessage(WM_SETFONT, cast(WPARAM)this._defaultFont.handle, true); 1066 1067 if(this._ctxMenu) 1068 { 1069 HMENU hDefaultMenu = GetMenu(this._handle); 1070 1071 if(hDefaultMenu) 1072 { 1073 DestroyMenu(hDefaultMenu); //Destroy default menu (if exists) 1074 } 1075 1076 this._ctxMenu.create(); 1077 } 1078 1079 this.onHandleCreated(EventArgs.empty); 1080 m.result = 0; //Continue.. 1081 } 1082 break; 1083 1084 case WM_WINDOWPOSCHANGED: 1085 { 1086 WINDOWPOS* pWndPos = cast(WINDOWPOS*)m.lParam; 1087 1088 if(!(pWndPos.flags & SWP_NOMOVE) || !(pWndPos.flags & SWP_NOSIZE)) 1089 { 1090 /* Note: 'pWndPos' has NonClient coordinates */ 1091 1092 if(!(pWndPos.flags & SWP_NOMOVE)) 1093 { 1094 this._bounds.x = pWndPos.x; 1095 this._bounds.y = pWndPos.y; 1096 } 1097 1098 if(!(pWndPos.flags & SWP_NOSIZE)) 1099 { 1100 this._bounds.width = pWndPos.cx; 1101 this._bounds.height = pWndPos.cy; 1102 } 1103 1104 if(!(pWndPos.flags & SWP_NOSIZE)) 1105 { 1106 this.onResize(EventArgs.empty); 1107 } 1108 } 1109 else if(pWndPos.flags & SWP_SHOWWINDOW || pWndPos.flags & SWP_HIDEWINDOW) 1110 { 1111 if(pWndPos.flags & SWP_SHOWWINDOW && this._parent) 1112 { 1113 this._parent.sendMessage(DGUI_DOLAYOUT, 0, 0); 1114 } 1115 1116 this.onVisibleChanged(EventArgs.empty); 1117 } 1118 1119 this.originalWndProc(m); //Send WM_SIZE too 1120 } 1121 break; 1122 1123 case WM_KEYDOWN: 1124 { 1125 scope KeyEventArgs e = new KeyEventArgs(cast(Keys)m.wParam); 1126 this.onKeyDown(e); 1127 1128 if(e.handled) 1129 { 1130 this.originalWndProc(m); 1131 } 1132 else 1133 { 1134 m.result = 0; 1135 } 1136 } 1137 break; 1138 1139 case WM_KEYUP: 1140 { 1141 scope KeyEventArgs e = new KeyEventArgs(cast(Keys)m.wParam); 1142 this.onKeyUp(e); 1143 1144 if(e.handled) 1145 { 1146 this.originalWndProc(m); 1147 } 1148 else 1149 { 1150 m.result = 0; 1151 } 1152 } 1153 break; 1154 1155 case WM_CHAR: 1156 { 1157 scope KeyCharEventArgs e = new KeyCharEventArgs(cast(Keys)m.wParam, cast(char)m.wParam); 1158 this.onKeyChar(e); 1159 1160 if(e.handled) 1161 { 1162 this.originalWndProc(m); 1163 } 1164 else 1165 { 1166 m.result = 0; 1167 } 1168 } 1169 break; 1170 1171 case WM_MOUSELEAVE: 1172 { 1173 Control.setBit(this._cBits, ControlBits.mouseEnter, false); 1174 1175 scope MouseEventArgs e = new MouseEventArgs(Point(LOWORD(m.lParam), HIWORD(m.lParam)), cast(MouseKeys)m.wParam); 1176 this.onMouseLeave(e); 1177 1178 this.originalWndProc(m); 1179 } 1180 break; 1181 1182 case WM_MOUSEMOVE: 1183 { 1184 scope MouseEventArgs e = new MouseEventArgs(Point(LOWORD(m.lParam), HIWORD(m.lParam)), cast(MouseKeys)m.wParam); 1185 this.onMouseMove(e); 1186 1187 if(!Control.hasBit(this._cBits, ControlBits.mouseEnter)) 1188 { 1189 Control.setBit(this._cBits, ControlBits.mouseEnter, true); 1190 1191 TRACKMOUSEEVENT tme; 1192 1193 tme.cbSize = TRACKMOUSEEVENT.sizeof; 1194 tme.dwFlags = TME_LEAVE; 1195 tme.hwndTrack = this._handle; 1196 1197 TrackMouseEvent(&tme); 1198 1199 this.onMouseEnter(e); 1200 } 1201 1202 this.originalWndProc(m); 1203 } 1204 break; 1205 1206 case WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN: 1207 { 1208 scope MouseEventArgs e = new MouseEventArgs(Point(LOWORD(m.lParam), HIWORD(m.lParam)), cast(MouseKeys)m.wParam); 1209 this.onMouseKeyDown(e); 1210 1211 this.originalWndProc(m); 1212 } 1213 break; 1214 1215 case WM_LBUTTONUP, WM_MBUTTONUP, WM_RBUTTONUP: 1216 { 1217 MouseKeys mk = MouseKeys.none; 1218 1219 if(GetAsyncKeyState(MK_LBUTTON)) 1220 { 1221 mk |= MouseKeys.left; 1222 } 1223 1224 if(GetAsyncKeyState(MK_MBUTTON)) 1225 { 1226 mk |= MouseKeys.middle; 1227 } 1228 1229 if(GetAsyncKeyState(MK_RBUTTON)) 1230 { 1231 mk |= MouseKeys.right; 1232 } 1233 1234 Point p = Point(LOWORD(m.lParam), HIWORD(m.lParam)); 1235 scope MouseEventArgs e = new MouseEventArgs(p, mk); 1236 this.onMouseKeyUp(e); 1237 1238 Control.convertPoint(p, this, null); 1239 1240 if(m.msg == WM_LBUTTONUP && !Control.hasBit(this._cBits, ControlBits.ownClickMsg) && WindowFromPoint(p.point) == this._handle) 1241 { 1242 this.onClick(EventArgs.empty); 1243 } 1244 1245 this.originalWndProc(m); 1246 } 1247 break; 1248 1249 case WM_LBUTTONDBLCLK, WM_MBUTTONDBLCLK, WM_RBUTTONDBLCLK: 1250 { 1251 scope MouseEventArgs e = new MouseEventArgs(Point(LOWORD(m.lParam), HIWORD(m.lParam)), cast(MouseKeys)m.wParam); 1252 this.onDoubleClick(e); 1253 1254 this.originalWndProc(m); 1255 } 1256 break; 1257 1258 case WM_SETCURSOR: 1259 { 1260 if(cast(HWND)m.wParam == this._handle && this._defaultCursor && cast(LONG)this._defaultCursor.handle != GetClassLongW(this._handle, GCL_HCURSOR)) 1261 { 1262 SetClassLongW(this._handle, GCL_HCURSOR, cast(LONG)this._defaultCursor.handle); 1263 } 1264 1265 this.originalWndProc(m); //Continue cursor selection 1266 } 1267 break; 1268 1269 case WM_MENUCOMMAND: 1270 this.onMenuCommand(m.wParam, m.lParam); 1271 break; 1272 1273 case WM_CONTEXTMENU: 1274 { 1275 if(this._ctxMenu) 1276 { 1277 this._ctxMenu.popupMenu(this._handle, Cursor.position); 1278 } 1279 else 1280 { 1281 this.originalWndProc(m); 1282 } 1283 } 1284 break; 1285 1286 case WM_SETFOCUS, WM_KILLFOCUS: 1287 { 1288 this.onFocusChanged(EventArgs.empty); 1289 this.originalWndProc(m); 1290 } 1291 break; 1292 1293 case WM_GETDLGCODE: 1294 { 1295 scope ControlCodeEventArgs e = new ControlCodeEventArgs(); 1296 this.onControlCode(e); 1297 1298 if(e.controlCode is ControlCode.ignore) 1299 { 1300 this.originalWndProc(m); 1301 } 1302 else 1303 { 1304 m.result = e.controlCode; 1305 } 1306 } 1307 break; 1308 1309 case WM_INITMENU: 1310 { 1311 if(this._ctxMenu) 1312 { 1313 this._ctxMenu.onPopup(EventArgs.empty); 1314 } 1315 1316 m.result = 0; 1317 } 1318 break; 1319 1320 default: 1321 this.originalWndProc(m); 1322 break; 1323 } 1324 } 1325 }