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