1 /* 2 * PROJECT: ReactOS UI Layout Engine 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * FILE: base/applications/rapps/include/rosui.h 5 * PURPOSE: ATL Layout engine for RAPPS 6 * COPYRIGHT: Copyright 2015 David Quintana (gigaherz@gmail.com) 7 */ 8 #pragma once 9 10 #include <atlwin.h> 11 12 template <class T, INT GrowthRate = 10> class CPointerArray 13 { 14 protected: 15 HDPA m_hDpa; 16 17 public: CPointerArray()18 CPointerArray() 19 { 20 m_hDpa = DPA_Create(GrowthRate); 21 } 22 ~CPointerArray()23 ~CPointerArray() 24 { 25 DPA_DestroyCallback(m_hDpa, s_OnRemoveItem, this); 26 } 27 28 private: 29 static INT CALLBACK s_OnRemoveItem(PVOID ptr,PVOID context)30 s_OnRemoveItem(PVOID ptr, PVOID context) 31 { 32 CPointerArray *self = (CPointerArray *)context; 33 return (INT)self->OnRemoveItem(reinterpret_cast<T *>(ptr)); 34 } 35 36 static INT CALLBACK s_OnCompareItems(PVOID p1,PVOID p2,LPARAM lParam)37 s_OnCompareItems(PVOID p1, PVOID p2, LPARAM lParam) 38 { 39 CPointerArray *self = (CPointerArray *)lParam; 40 return self->OnCompareItems(reinterpret_cast<T *>(p1), reinterpret_cast<T *>(p2)); 41 } 42 43 public: 44 virtual BOOL OnRemoveItem(T * ptr)45 OnRemoveItem(T *ptr) 46 { 47 return TRUE; 48 } 49 50 virtual INT OnCompareItems(T * p1,T * p2)51 OnCompareItems(T *p1, T *p2) 52 { 53 INT_PTR t = (reinterpret_cast<INT_PTR>(p2) - reinterpret_cast<INT_PTR>(p1)); 54 if (t > 0) 55 return 1; 56 if (t < 0) 57 return -1; 58 return 0; 59 } 60 61 public: 62 INT GetCount()63 GetCount() const 64 { 65 return DPA_GetPtrCount(m_hDpa); 66 } 67 68 T * Get(INT i)69 Get(INT i) const 70 { 71 return (T *)DPA_GetPtr(m_hDpa, i); 72 } 73 74 BOOL Set(INT i,T * ptr)75 Set(INT i, T *ptr) 76 { 77 return DPA_SetPtr(m_hDpa, i, ptr); 78 } 79 80 INT Insert(INT at,T * ptr)81 Insert(INT at, T *ptr) 82 { 83 return DPA_InsertPtr(m_hDpa, at, ptr); 84 } 85 86 INT Append(T * ptr)87 Append(T *ptr) 88 { 89 return DPA_InsertPtr(m_hDpa, DA_LAST, ptr); 90 } 91 92 INT IndexOf(T * ptr)93 IndexOf(T *ptr) const 94 { 95 return DPA_GetPtrIndex(m_hDpa, ptr); 96 } 97 98 BOOL Remove(T * ptr)99 Remove(T *ptr) 100 { 101 INT i = IndexOf(ptr); 102 if (i < 0) 103 return FALSE; 104 return RemoveAt(i); 105 } 106 107 BOOL RemoveAt(INT i)108 RemoveAt(INT i) 109 { 110 PVOID ptr = DPA_DeletePtr(m_hDpa, i); 111 if (ptr != NULL) 112 { 113 OnRemoveItem(reinterpret_cast<T *>(ptr)); 114 return TRUE; 115 } 116 return FALSE; 117 } 118 119 BOOL Clear()120 Clear() 121 { 122 DPA_EnumCallback(s_OnRemoveItem, this); 123 return DPA_DeleteAllPtrs(m_hDpa); 124 } 125 126 BOOL Sort()127 Sort() 128 { 129 return DPA_Sort(m_hDpa, s_OnCompareItems, (LPARAM)this); 130 } 131 132 INT Search(T * item,INT iStart,UINT uFlags)133 Search(T *item, INT iStart, UINT uFlags) 134 { 135 return DPA_Search(m_hDpa, item, 0, s_OnCompareItems, (LPARAM)this, 0); 136 } 137 }; 138 139 class CUiRect : public RECT 140 { 141 public: CUiRect()142 CUiRect() 143 { 144 left = right = top = bottom = 0; 145 } 146 CUiRect(INT l,INT t,INT r,INT b)147 CUiRect(INT l, INT t, INT r, INT b) 148 { 149 left = l; 150 right = r; 151 top = t; 152 bottom = b; 153 } 154 }; 155 156 class CUiMargin : public CUiRect 157 { 158 public: CUiMargin()159 CUiMargin() 160 { 161 } 162 CUiMargin(INT all)163 CUiMargin(INT all) : CUiRect(all, all, all, all) 164 { 165 } 166 CUiMargin(INT horz,INT vert)167 CUiMargin(INT horz, INT vert) : CUiRect(horz, vert, horz, vert) 168 { 169 } 170 }; 171 172 class CUiMeasure 173 { 174 public: 175 enum MeasureType 176 { 177 Type_FitContent = 0, 178 Type_Fixed = 1, 179 Type_Percent = 2, 180 Type_FitParent = 3 181 }; 182 183 private: 184 MeasureType m_Type; 185 INT m_Value; 186 187 public: CUiMeasure()188 CUiMeasure() 189 { 190 m_Type = Type_FitContent; 191 m_Value = 0; 192 } 193 CUiMeasure(MeasureType type,INT value)194 CUiMeasure(MeasureType type, INT value) 195 { 196 m_Type = type; 197 m_Value = value; 198 } 199 200 INT ComputeMeasure(INT parent,INT content)201 ComputeMeasure(INT parent, INT content) 202 { 203 switch (m_Type) 204 { 205 case Type_FitContent: 206 return content; 207 case Type_Fixed: 208 return m_Value; 209 case Type_Percent: 210 return max(content, parent * m_Value / 100); 211 case Type_FitParent: 212 return parent; 213 } 214 215 return 0; 216 } 217 218 public: 219 static CUiMeasure FitContent()220 FitContent() 221 { 222 return CUiMeasure(Type_FitContent, 0); 223 } 224 225 static CUiMeasure FitParent()226 FitParent() 227 { 228 return CUiMeasure(Type_FitParent, 0); 229 } 230 231 static CUiMeasure Fixed(INT pixels)232 Fixed(INT pixels) 233 { 234 return CUiMeasure(Type_Fixed, pixels); 235 } 236 237 static CUiMeasure Percent(INT percent)238 Percent(INT percent) 239 { 240 return CUiMeasure(Type_Percent, percent); 241 } 242 }; 243 244 enum CUiAlignment 245 { 246 UiAlign_LeftTop, 247 UiAlign_Middle, 248 UiAlign_RightBtm, 249 UiAlign_Stretch 250 }; 251 252 class CUiBox 253 { 254 public: 255 CUiMargin m_Margin; 256 257 CUiAlignment m_HorizontalAlignment; 258 CUiAlignment m_VerticalAlignment; 259 260 protected: CUiBox()261 CUiBox() 262 { 263 m_HorizontalAlignment = UiAlign_LeftTop; 264 m_VerticalAlignment = UiAlign_LeftTop; 265 } 266 267 virtual VOID ComputeRect(RECT parentRect,RECT currentRect,RECT * newRect)268 ComputeRect(RECT parentRect, RECT currentRect, RECT *newRect) 269 { 270 parentRect.left += m_Margin.left; 271 parentRect.right -= m_Margin.right; 272 parentRect.top += m_Margin.top; 273 parentRect.bottom -= m_Margin.bottom; 274 275 if (parentRect.right < parentRect.left) 276 parentRect.right = parentRect.left; 277 278 if (parentRect.bottom < parentRect.top) 279 parentRect.bottom = parentRect.top; 280 281 SIZE szParent = {parentRect.right - parentRect.left, parentRect.bottom - parentRect.top}; 282 SIZE szCurrent = {currentRect.right - currentRect.left, currentRect.bottom - currentRect.top}; 283 284 currentRect = parentRect; 285 286 switch (m_HorizontalAlignment) 287 { 288 case UiAlign_LeftTop: 289 currentRect.right = currentRect.left + szCurrent.cx; 290 break; 291 case UiAlign_Middle: 292 currentRect.left = parentRect.left + (szParent.cx - szCurrent.cx) / 2; 293 currentRect.right = currentRect.left + szCurrent.cx; 294 break; 295 case UiAlign_RightBtm: 296 currentRect.left = currentRect.right - szCurrent.cx; 297 break; 298 default: 299 break; 300 } 301 302 switch (m_VerticalAlignment) 303 { 304 case UiAlign_LeftTop: 305 currentRect.bottom = currentRect.top + szCurrent.cy; 306 break; 307 case UiAlign_Middle: 308 currentRect.top = parentRect.top + (szParent.cy - szCurrent.cy) / 2; 309 currentRect.bottom = currentRect.top + szCurrent.cy; 310 break; 311 case UiAlign_RightBtm: 312 currentRect.top = currentRect.bottom - szCurrent.cy; 313 break; 314 default: 315 break; 316 } 317 318 *newRect = currentRect; 319 } 320 321 public: 322 virtual VOID ComputeMinimalSize(SIZE * size)323 ComputeMinimalSize(SIZE *size) 324 { 325 // Override in subclass 326 size->cx = max(size->cx, 0); 327 size->cy = min(size->cy, 0); 328 }; 329 330 virtual VOID ComputeContentBounds(RECT * rect)331 ComputeContentBounds(RECT *rect){ 332 // Override in subclass 333 }; 334 335 virtual DWORD_PTR CountSizableChildren()336 CountSizableChildren() 337 { 338 // Override in subclass 339 return 0; 340 }; 341 342 virtual HDWP OnParentSize(RECT parentRect,HDWP hDwp)343 OnParentSize(RECT parentRect, HDWP hDwp) 344 { 345 // Override in subclass 346 return NULL; 347 }; 348 }; 349 350 class CUiPrimitive 351 { 352 protected: 353 CUiPrimitive *m_Parent; 354 355 public: ~CUiPrimitive()356 virtual ~CUiPrimitive() 357 { 358 } 359 360 virtual CUiBox * AsBox()361 AsBox() 362 { 363 return NULL; 364 } 365 }; 366 367 class CUiCollection : public CPointerArray<CUiPrimitive> 368 { 369 virtual BOOL OnRemoveItem(CUiPrimitive * ptr)370 OnRemoveItem(CUiPrimitive *ptr) 371 { 372 delete ptr; 373 return TRUE; 374 } 375 }; 376 377 class CUiContainer 378 { 379 protected: 380 CUiCollection m_Children; 381 382 public: 383 CUiCollection & Children()384 Children() 385 { 386 return m_Children; 387 } 388 }; 389 390 class CUiPanel : public CUiPrimitive, public CUiBox, public CUiContainer 391 { 392 public: 393 CUiMeasure m_Width; 394 CUiMeasure m_Height; 395 CUiPanel()396 CUiPanel() 397 { 398 m_Width = CUiMeasure::FitParent(); 399 m_Height = CUiMeasure::FitParent(); 400 } 401 ~CUiPanel()402 virtual ~CUiPanel() 403 { 404 } 405 406 virtual CUiBox * AsBox()407 AsBox() 408 { 409 return this; 410 } 411 412 virtual VOID ComputeMinimalSize(SIZE * size)413 ComputeMinimalSize(SIZE *size) 414 { 415 for (INT i = 0; i < m_Children.GetCount(); i++) 416 { 417 CUiBox *box = m_Children.Get(i)->AsBox(); 418 if (box) 419 { 420 box->ComputeMinimalSize(size); 421 } 422 } 423 }; 424 425 virtual VOID ComputeContentBounds(RECT * rect)426 ComputeContentBounds(RECT *rect) 427 { 428 for (INT i = 0; i < m_Children.GetCount(); i++) 429 { 430 CUiBox *box = m_Children.Get(i)->AsBox(); 431 if (box) 432 { 433 box->ComputeContentBounds(rect); 434 } 435 } 436 }; 437 438 virtual DWORD_PTR CountSizableChildren()439 CountSizableChildren() 440 { 441 INT count = 0; 442 for (INT i = 0; i < m_Children.GetCount(); i++) 443 { 444 CUiBox *box = m_Children.Get(i)->AsBox(); 445 if (box) 446 { 447 count += box->CountSizableChildren(); 448 } 449 } 450 return count; 451 } 452 453 virtual HDWP OnParentSize(RECT parentRect,HDWP hDwp)454 OnParentSize(RECT parentRect, HDWP hDwp) 455 { 456 RECT rect = {0}; 457 458 SIZE content = {0}; 459 ComputeMinimalSize(&content); 460 461 INT preferredWidth = m_Width.ComputeMeasure(parentRect.right - parentRect.left, content.cx); 462 INT preferredHeight = m_Height.ComputeMeasure(parentRect.bottom - parentRect.top, content.cy); 463 464 rect.right = preferredWidth; 465 rect.bottom = preferredHeight; 466 467 ComputeRect(parentRect, rect, &rect); 468 469 for (INT i = 0; i < m_Children.GetCount(); i++) 470 { 471 CUiBox *box = m_Children.Get(i)->AsBox(); 472 if (box) 473 { 474 hDwp = box->OnParentSize(rect, hDwp); 475 } 476 } 477 478 return hDwp; 479 } 480 }; 481 482 template <class T = CWindow> class CUiWindow : public CUiPrimitive, public CUiBox, public T 483 { 484 public: 485 virtual CUiBox * AsBox()486 AsBox() 487 { 488 return this; 489 } 490 491 HWND GetWindow()492 GetWindow() 493 { 494 return T::m_hWnd; 495 } 496 497 virtual VOID ComputeMinimalSize(SIZE * size)498 ComputeMinimalSize(SIZE *size) 499 { 500 // TODO: Maybe use WM_GETMINMAXINFO? 501 return CUiBox::ComputeMinimalSize(size); 502 }; 503 504 virtual VOID ComputeContentBounds(RECT * rect)505 ComputeContentBounds(RECT *rect) 506 { 507 RECT r; 508 ::GetWindowRect(T::m_hWnd, &r); 509 rect->left = min(rect->left, r.left); 510 rect->top = min(rect->top, r.top); 511 rect->right = max(rect->right, r.right); 512 rect->bottom = max(rect->bottom, r.bottom); 513 }; 514 515 virtual DWORD_PTR CountSizableChildren()516 CountSizableChildren() 517 { 518 return 1; 519 }; 520 521 virtual HDWP OnParentSize(RECT parentRect,HDWP hDwp)522 OnParentSize(RECT parentRect, HDWP hDwp) 523 { 524 RECT rect; 525 526 ::GetWindowRect(T::m_hWnd, &rect); 527 528 ComputeRect(parentRect, rect, &rect); 529 530 if (hDwp) 531 { 532 return ::DeferWindowPos( 533 hDwp, T::m_hWnd, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 534 SWP_NOACTIVATE | SWP_NOZORDER); 535 } 536 else 537 { 538 T::SetWindowPos( 539 NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 540 SWP_NOACTIVATE | SWP_NOZORDER | SWP_DEFERERASE); 541 return NULL; 542 } 543 }; 544 545 virtual VOID AppendTabOrderWindow(int Direction,ATL::CSimpleArray<HWND> & TabOrderList)546 AppendTabOrderWindow(int Direction, ATL::CSimpleArray<HWND> &TabOrderList) 547 { 548 TabOrderList.Add(T::m_hWnd); 549 return; 550 } 551 ~CUiWindow()552 virtual ~CUiWindow() 553 { 554 if (T::IsWindow()) 555 { 556 T::DestroyWindow(); 557 } 558 } 559 560 VOID GetWindowTextW(CStringW & szText)561 GetWindowTextW(CStringW &szText) 562 { 563 INT length = CWindow::GetWindowTextLengthW() + 1; 564 CWindow::GetWindowTextW(szText.GetBuffer(length), length); 565 szText.ReleaseBuffer(); 566 } 567 }; 568 569 class CUiSplitPanel : public CUiPrimitive, public CUiBox, public CWindowImpl<CUiSplitPanel> 570 { 571 static const INT THICKNESS = 4; 572 573 protected: 574 HCURSOR m_hCursor; 575 576 CUiPanel m_First; 577 CUiPanel m_Second; 578 579 RECT m_LastRect; 580 581 BOOL m_HasOldRect; 582 583 public: 584 INT m_Pos; 585 BOOL m_Horizontal; 586 BOOL m_DynamicFirst; 587 INT m_MinFirst; 588 INT m_MinSecond; 589 590 CUiMeasure m_Width; 591 CUiMeasure m_Height; 592 CUiSplitPanel()593 CUiSplitPanel() 594 { 595 m_hCursor = NULL; 596 m_Width = CUiMeasure::FitParent(); 597 m_Height = CUiMeasure::FitParent(); 598 m_Pos = 100; 599 m_Horizontal = FALSE; 600 m_MinFirst = 100; 601 m_MinSecond = 100; 602 m_DynamicFirst = FALSE; 603 m_HasOldRect = FALSE; 604 memset(&m_LastRect, 0, sizeof(m_LastRect)); 605 } 606 ~CUiSplitPanel()607 virtual ~CUiSplitPanel() 608 { 609 } 610 611 virtual CUiBox * AsBox()612 AsBox() 613 { 614 return this; 615 } 616 617 CUiCollection & First()618 First() 619 { 620 return m_First.Children(); 621 } 622 CUiCollection & Second()623 Second() 624 { 625 return m_Second.Children(); 626 } 627 628 virtual VOID ComputeMinimalSize(SIZE * size)629 ComputeMinimalSize(SIZE *size) 630 { 631 if (m_Horizontal) 632 size->cx = max(size->cx, THICKNESS); 633 else 634 size->cy = max(size->cy, THICKNESS); 635 m_First.ComputeMinimalSize(size); 636 m_Second.ComputeMinimalSize(size); 637 }; 638 639 virtual VOID ComputeContentBounds(RECT * rect)640 ComputeContentBounds(RECT *rect) 641 { 642 RECT r; 643 644 m_First.ComputeContentBounds(rect); 645 m_Second.ComputeContentBounds(rect); 646 647 ::GetWindowRect(m_hWnd, &r); 648 649 rect->left = min(rect->left, r.left); 650 rect->top = min(rect->top, r.top); 651 rect->right = max(rect->right, r.right); 652 rect->bottom = max(rect->bottom, r.bottom); 653 }; 654 655 virtual DWORD_PTR CountSizableChildren()656 CountSizableChildren() 657 { 658 INT count = 1; 659 count += m_First.CountSizableChildren(); 660 count += m_Second.CountSizableChildren(); 661 return count; 662 }; 663 664 virtual HDWP OnParentSize(RECT parentRect,HDWP hDwp)665 OnParentSize(RECT parentRect, HDWP hDwp) 666 { 667 RECT rect = {0}; 668 669 SIZE content = {0}; 670 ComputeMinimalSize(&content); 671 672 INT preferredWidth = m_Width.ComputeMeasure(parentRect.right - parentRect.left, content.cx); 673 INT preferredHeight = m_Width.ComputeMeasure(parentRect.bottom - parentRect.top, content.cy); 674 675 rect.right = preferredWidth; 676 rect.bottom = preferredHeight; 677 678 ComputeRect(parentRect, rect, &rect); 679 680 SIZE growth = {0}; 681 if (m_HasOldRect) 682 { 683 RECT oldRect = m_LastRect; 684 685 growth.cx = (parentRect.right - parentRect.left) - (oldRect.right - oldRect.left); 686 growth.cy = (parentRect.bottom - parentRect.top) - (oldRect.bottom - oldRect.top); 687 } 688 689 RECT splitter = rect; 690 RECT first = rect; 691 RECT second = rect; 692 693 if (m_Horizontal) 694 { 695 rect.top += m_MinFirst; 696 rect.bottom -= THICKNESS + m_MinSecond; 697 if (m_DynamicFirst) 698 { 699 if (growth.cy > 0) 700 { 701 m_Pos += min(growth.cy, rect.bottom - (m_Pos + THICKNESS)); 702 } 703 else if (growth.cy < 0) 704 { 705 m_Pos += max(growth.cy, rect.top - m_Pos); 706 } 707 } 708 709 if (m_Pos > rect.bottom) 710 m_Pos = rect.bottom; 711 712 if (m_Pos < rect.top) 713 m_Pos = rect.top; 714 715 splitter.top = m_Pos; 716 splitter.bottom = m_Pos + THICKNESS; 717 first.bottom = splitter.top; 718 second.top = splitter.bottom; 719 } 720 else 721 { 722 rect.left += m_MinFirst; 723 rect.right -= THICKNESS + m_MinSecond; 724 if (m_DynamicFirst) 725 { 726 if (growth.cx > 0) 727 { 728 m_Pos += min(growth.cx, rect.right - (m_Pos + THICKNESS)); 729 } 730 else if (growth.cx < 0) 731 { 732 m_Pos += max(growth.cy, rect.left - m_Pos); 733 } 734 } 735 736 if (m_Pos > rect.right) 737 m_Pos = rect.right; 738 739 if (m_Pos < rect.left) 740 m_Pos = rect.left; 741 742 splitter.left = m_Pos; 743 splitter.right = m_Pos + THICKNESS; 744 first.right = splitter.left; 745 second.left = splitter.right; 746 } 747 748 m_LastRect = parentRect; 749 m_HasOldRect = TRUE; 750 751 hDwp = m_First.OnParentSize(first, hDwp); 752 hDwp = m_Second.OnParentSize(second, hDwp); 753 754 if (hDwp) 755 { 756 return DeferWindowPos( 757 hDwp, NULL, splitter.left, splitter.top, splitter.right - splitter.left, splitter.bottom - splitter.top, 758 SWP_NOACTIVATE | SWP_NOZORDER); 759 } 760 else 761 { 762 SetWindowPos( 763 NULL, splitter.left, splitter.top, splitter.right - splitter.left, splitter.bottom - splitter.top, 764 SWP_NOACTIVATE | SWP_NOZORDER); 765 return NULL; 766 } 767 }; 768 769 private: 770 BOOL ProcessWindowMessage(HWND hwnd,UINT Msg,WPARAM wParam,LPARAM lParam,LRESULT & theResult,DWORD dwMapId)771 ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT &theResult, DWORD dwMapId) 772 { 773 theResult = 0; 774 switch (Msg) 775 { 776 case WM_SETCURSOR: 777 SetCursor(m_hCursor); 778 theResult = TRUE; 779 break; 780 781 case WM_LBUTTONDOWN: 782 SetCapture(); 783 break; 784 785 case WM_LBUTTONUP: 786 case WM_RBUTTONDOWN: 787 if (GetCapture() == m_hWnd) 788 { 789 ReleaseCapture(); 790 } 791 break; 792 793 case WM_MOUSEMOVE: 794 if (GetCapture() == m_hWnd) 795 { 796 POINT Point; 797 GetCursorPos(&Point); 798 ::ScreenToClient(GetParent(), &Point); 799 if (m_Horizontal) 800 SetPos(Point.y); 801 else 802 SetPos(Point.x); 803 } 804 break; 805 806 default: 807 return FALSE; 808 } 809 810 return TRUE; 811 } 812 813 public: 814 INT GetPos()815 GetPos() 816 { 817 return m_Pos; 818 } 819 820 VOID SetPos(INT NewPos)821 SetPos(INT NewPos) 822 { 823 RECT rcParent; 824 825 rcParent = m_LastRect; 826 827 if (m_Horizontal) 828 { 829 rcParent.bottom -= THICKNESS; 830 831 m_Pos = NewPos; 832 833 if (m_Pos < rcParent.top) 834 m_Pos = rcParent.top; 835 836 if (m_Pos > rcParent.bottom) 837 m_Pos = rcParent.bottom; 838 } 839 else 840 { 841 rcParent.right -= THICKNESS; 842 843 m_Pos = NewPos; 844 845 if (m_Pos < rcParent.left) 846 m_Pos = rcParent.left; 847 848 if (m_Pos > rcParent.right) 849 m_Pos = rcParent.right; 850 } 851 852 INT count = CountSizableChildren(); 853 854 HDWP hdwp = NULL; 855 hdwp = BeginDeferWindowPos(count); 856 if (hdwp) 857 hdwp = OnParentSize(m_LastRect, hdwp); 858 if (hdwp) 859 EndDeferWindowPos(hdwp); 860 } 861 862 public: 863 DECLARE_WND_CLASS_EX(_T("SplitterWindowClass"), CS_HREDRAW | CS_VREDRAW, COLOR_BTNFACE) 864 865 /* Create splitter bar */ 866 HWND Create(HWND hwndParent)867 Create(HWND hwndParent) 868 { 869 if (m_Horizontal) 870 m_hCursor = LoadCursor(0, IDC_SIZENS); 871 else 872 m_hCursor = LoadCursor(0, IDC_SIZEWE); 873 874 DWORD style = WS_CHILD | WS_VISIBLE; 875 DWORD exStyle = WS_EX_TRANSPARENT; 876 877 RECT size = {205, 180, 465, THICKNESS}; 878 size.right += size.left; 879 size.bottom += size.top; 880 881 return CWindowImpl::Create(hwndParent, size, NULL, style, exStyle); 882 } 883 }; 884