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