1 /* PROJECT: ReactOS UI Layout Engine
2 * LICENSE: GPL - See COPYING in the top level directory
3 * AUTHORS: David Quintana <gigaherz@gmail.com>
7 template<class T
, int GrowthRate
= 10>
16 m_hDpa
= DPA_Create(GrowthRate
);
21 DPA_DestroyCallback(m_hDpa
, s_OnRemoveItem
, this);
25 static int CALLBACK
s_OnRemoveItem(void * ptr
, void * context
)
27 CPointerArray
* self
= (CPointerArray
*) context
;
28 return (int) self
->OnRemoveItem(reinterpret_cast<T
*>(ptr
));
31 static int CALLBACK
s_OnCompareItems(void *p1
, void *p2
, LPARAM lParam
)
33 CPointerArray
* self
= (CPointerArray
*) lParam
;
34 return self
->OnCompareItems(reinterpret_cast<T
*>(p1
), reinterpret_cast<T
*>(p2
));
38 virtual BOOL
OnRemoveItem(T
* ptr
)
43 virtual int OnCompareItems(T
* p1
, T
* p2
)
45 int t
= (reinterpret_cast<int>(p2
) -reinterpret_cast<int>(p1
));
56 return DPA_GetPtrCount(m_hDpa
);
61 return (T
*) DPA_GetPtr(m_hDpa
, i
);
64 BOOL
Set(int i
, T
* ptr
)
66 return DPA_SetPtr(m_hDpa
, i
, ptr
);
69 int Insert(int at
, T
* ptr
)
71 return DPA_InsertPtr(m_hDpa
, at
, ptr
);
76 return DPA_InsertPtr(m_hDpa
, DA_LAST
, ptr
);
79 int IndexOf(T
* ptr
) const
81 return DPA_GetPtrIndex(m_hDpa
, ptr
);
94 T
* ptr
= (T
*) DPA_GetPtr(m_hDpa
, i
);
96 return DPA_DeletePtr(m_hDpa
, i
);
101 DPA_EnumCallback(s_OnRemoveItem
, this);
102 return DPA_DeleteAllPtrs(m_hDpa
);
107 return DPA_Sort(m_hDpa
, s_OnCompareItems
, (LPARAM
)this);
110 int Search(T
* item
, int iStart
, UINT uFlags
)
112 return DPA_Search(m_hDpa
, s_OnCompareItems
, (LPARAM
)this);
122 left
= right
= top
= bottom
= 0;
125 CUiRect(int l
, int t
, int r
, int b
)
143 : CUiRect(all
, all
, all
, all
)
147 CUiMargin(int horz
, int vert
)
148 : CUiRect(horz
, vert
, horz
, vert
)
171 m_Type
= Type_FitContent
;
175 CUiMeasure(MeasureType type
, int value
)
181 int ComputeMeasure(int parent
, int content
)
185 case Type_FitContent
:
190 return max(content
, parent
* m_Value
/ 100);
199 static CUiMeasure
FitContent()
201 return CUiMeasure(Type_FitContent
, 0);
204 static CUiMeasure
FitParent()
206 return CUiMeasure(Type_FitParent
, 0);
209 static CUiMeasure
Fixed(int pixels
)
211 return CUiMeasure(Type_Fixed
, pixels
);
214 static CUiMeasure
Percent(int percent
)
216 return CUiMeasure(Type_Percent
, percent
);
233 CUiAlignment m_HorizontalAlignment
;
234 CUiAlignment m_VerticalAlignment
;
239 m_HorizontalAlignment
= UiAlign_LeftTop
;
240 m_VerticalAlignment
= UiAlign_LeftTop
;
243 virtual void ComputeRect(RECT parentRect
, RECT currentRect
, RECT
* newRect
)
245 parentRect
.left
+= m_Margin
.left
;
246 parentRect
.right
-= m_Margin
.right
;
247 parentRect
.top
+= m_Margin
.top
;
248 parentRect
.bottom
-= m_Margin
.bottom
;
250 if (parentRect
.right
< parentRect
.left
)
251 parentRect
.right
= parentRect
.left
;
253 if (parentRect
.bottom
< parentRect
.top
)
254 parentRect
.bottom
= parentRect
.top
;
256 SIZE szParent
= { parentRect
.right
- parentRect
.left
, parentRect
.bottom
- parentRect
.top
};
257 SIZE szCurrent
= { currentRect
.right
- currentRect
.left
, currentRect
.bottom
- currentRect
.top
};
259 currentRect
= parentRect
;
261 switch (m_HorizontalAlignment
)
263 case UiAlign_LeftTop
:
264 currentRect
.right
= currentRect
.left
+ szCurrent
.cx
;
267 currentRect
.left
= parentRect
.left
+ (szParent
.cx
- szCurrent
.cx
) / 2;
268 currentRect
.right
= currentRect
.left
+ szCurrent
.cx
;
270 case UiAlign_RightBtm
:
271 currentRect
.left
= currentRect
.right
- szCurrent
.cx
;
277 switch (m_VerticalAlignment
)
279 case UiAlign_LeftTop
:
280 currentRect
.bottom
= currentRect
.top
+ szCurrent
.cy
;
283 currentRect
.top
= parentRect
.top
+ (szParent
.cy
- szCurrent
.cy
) / 2;
284 currentRect
.bottom
= currentRect
.top
+ szCurrent
.cy
;
286 case UiAlign_RightBtm
:
287 currentRect
.top
= currentRect
.bottom
- szCurrent
.cy
;
293 *newRect
= currentRect
;
298 virtual void ComputeMinimalSize(SIZE
* size
)
300 // Override in subclass
301 size
->cx
= max(size
->cx
, 0);
302 size
->cy
= min(size
->cy
, 0);
305 virtual void ComputeContentBounds(RECT
* rect
)
307 // Override in subclass
310 virtual DWORD_PTR
CountSizableChildren()
312 // Override in subclass
316 virtual HDWP
OnParentSize(RECT parentRect
, HDWP hDwp
)
318 // Override in subclass
326 CUiPrimitive
* m_Parent
;
329 virtual ~CUiPrimitive() {}
331 virtual CUiBox
* AsBox() { return NULL
; }
334 class CUiCollection
:
335 public CPointerArray
< CUiPrimitive
>
337 virtual BOOL
OnRemoveItem(CUiPrimitive
* ptr
)
347 CUiCollection m_Children
;
350 CUiCollection
& Children() { return m_Children
; }
364 m_Width
= CUiMeasure::FitParent();
365 m_Height
= CUiMeasure::FitParent();
372 virtual CUiBox
* AsBox() { return this; }
374 virtual void ComputeMinimalSize(SIZE
* size
)
376 for (int i
= 0; i
< m_Children
.GetCount(); i
++)
378 CUiBox
* box
= m_Children
.Get(i
)->AsBox();
381 box
->ComputeMinimalSize(size
);
386 virtual void ComputeContentBounds(RECT
* rect
)
388 for (int i
= 0; i
< m_Children
.GetCount(); i
++)
390 CUiBox
* box
= m_Children
.Get(i
)->AsBox();
393 box
->ComputeContentBounds(rect
);
398 virtual DWORD_PTR
CountSizableChildren()
401 for (int i
= 0; i
< m_Children
.GetCount(); i
++)
403 CUiBox
* box
= m_Children
.Get(i
)->AsBox();
406 count
+= box
->CountSizableChildren();
412 virtual HDWP
OnParentSize(RECT parentRect
, HDWP hDwp
)
416 SIZE content
= { 0 };
417 ComputeMinimalSize(&content
);
419 int preferredWidth
= m_Width
.ComputeMeasure(parentRect
.right
- parentRect
.left
, content
.cx
);
420 int preferredHeight
= m_Height
.ComputeMeasure(parentRect
.bottom
- parentRect
.top
, content
.cy
);
422 rect
.right
= preferredWidth
;
423 rect
.bottom
= preferredHeight
;
425 ComputeRect(parentRect
, rect
, &rect
);
427 for (int i
= 0; i
< m_Children
.GetCount(); i
++)
429 CUiBox
* box
= m_Children
.Get(i
)->AsBox();
432 hDwp
= box
->OnParentSize(rect
, hDwp
);
440 template<class T
= CWindow
>
447 virtual CUiBox
* AsBox() { return this; }
449 HWND
GetWindow() { return T::m_hWnd
; }
451 virtual void ComputeMinimalSize(SIZE
* size
)
453 // TODO: Maybe use WM_GETMINMAXINFO?
454 return CUiBox::ComputeMinimalSize(size
);
457 virtual void ComputeContentBounds(RECT
* rect
)
460 ::GetWindowRect(T::m_hWnd
, &r
);
461 rect
->left
= min(rect
->left
, r
.left
);
462 rect
->top
= min(rect
->top
, r
.top
);
463 rect
->right
= max(rect
->right
, r
.right
);
464 rect
->bottom
= max(rect
->bottom
, r
.bottom
);
467 virtual DWORD_PTR
CountSizableChildren()
472 virtual HDWP
OnParentSize(RECT parentRect
, HDWP hDwp
)
476 ::GetWindowRect(T::m_hWnd
, &rect
);
478 ComputeRect(parentRect
, rect
, &rect
);
482 return ::DeferWindowPos(hDwp
, T::m_hWnd
, NULL
, rect
.left
, rect
.top
, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
, SWP_NOACTIVATE
| SWP_NOZORDER
);
486 T::SetWindowPos(NULL
, rect
.left
, rect
.top
, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
, SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_DEFERERASE
);
496 void GetWindowTextW(ATL::CStringW
& szText
)
498 INT length
= CWindow::GetWindowTextLengthW() + 1;
499 CWindow::GetWindowTextW(szText
.GetBuffer(length
), length
);
500 szText
.ReleaseBuffer();
504 class CUiSplitPanel
:
507 public CWindowImpl
<CUiSplitPanel
>
509 static const int THICKNESS
= 4;
534 m_Width
= CUiMeasure::FitParent();
535 m_Height
= CUiMeasure::FitParent();
539 m_DynamicFirst
= FALSE
;
540 m_HasOldRect
= FALSE
;
543 virtual ~CUiSplitPanel()
547 virtual CUiBox
* AsBox() { return this; }
549 CUiCollection
& First() { return m_First
.Children(); }
550 CUiCollection
& Second() { return m_Second
.Children(); }
552 virtual void ComputeMinimalSize(SIZE
* size
)
555 size
->cx
= max(size
->cx
, THICKNESS
);
557 size
->cy
= max(size
->cy
, THICKNESS
);
558 m_First
.ComputeMinimalSize(size
);
559 m_Second
.ComputeMinimalSize(size
);
562 virtual void ComputeContentBounds(RECT
* rect
)
566 m_First
.ComputeContentBounds(rect
);
567 m_Second
.ComputeContentBounds(rect
);
569 ::GetWindowRect(m_hWnd
, &r
);
571 rect
->left
= min(rect
->left
, r
.left
);
572 rect
->top
= min(rect
->top
, r
.top
);
573 rect
->right
= max(rect
->right
, r
.right
);
574 rect
->bottom
= max(rect
->bottom
, r
.bottom
);
577 virtual DWORD_PTR
CountSizableChildren()
580 count
+= m_First
.CountSizableChildren();
581 count
+= m_Second
.CountSizableChildren();
585 virtual HDWP
OnParentSize(RECT parentRect
, HDWP hDwp
)
589 SIZE content
= { 0 };
590 ComputeMinimalSize(&content
);
592 int preferredWidth
= m_Width
.ComputeMeasure(parentRect
.right
- parentRect
.left
, content
.cx
);
593 int preferredHeight
= m_Width
.ComputeMeasure(parentRect
.bottom
- parentRect
.top
, content
.cy
);
595 rect
.right
= preferredWidth
;
596 rect
.bottom
= preferredHeight
;
598 ComputeRect(parentRect
, rect
, &rect
);
603 RECT oldRect
= m_LastRect
;
605 growth
.cx
= (parentRect
.right
- parentRect
.left
) - (oldRect
.right
- oldRect
.left
);
606 growth
.cy
= (parentRect
.bottom
- parentRect
.top
) - (oldRect
.bottom
- oldRect
.top
);
609 RECT splitter
= rect
;
615 rect
.top
+= m_MinFirst
;
616 rect
.bottom
-= THICKNESS
+ m_MinSecond
;
621 m_Pos
+= min(growth
.cy
, rect
.bottom
- (m_Pos
+THICKNESS
));
623 else if (growth
.cy
< 0)
625 m_Pos
+= max(growth
.cy
, rect
.top
- m_Pos
);
629 if (m_Pos
> rect
.bottom
)
632 if (m_Pos
< rect
.top
)
635 splitter
.top
= m_Pos
;
636 splitter
.bottom
= m_Pos
+ THICKNESS
;
637 first
.bottom
= splitter
.top
;
638 second
.top
= splitter
.bottom
;
642 rect
.left
+= m_MinFirst
;
643 rect
.right
-= THICKNESS
+ m_MinSecond
;
648 m_Pos
+= min(growth
.cx
, rect
.right
- (m_Pos
+ THICKNESS
));
650 else if (growth
.cx
< 0)
652 m_Pos
+= max(growth
.cy
, rect
.left
- m_Pos
);
656 if (m_Pos
> rect
.right
)
659 if (m_Pos
< rect
.left
)
662 splitter
.left
= m_Pos
;
663 splitter
.right
= m_Pos
+ THICKNESS
;
664 first
.right
= splitter
.left
;
665 second
.left
= splitter
.right
;
668 m_LastRect
= parentRect
;
671 hDwp
= m_First
.OnParentSize(first
, hDwp
);
672 hDwp
= m_Second
.OnParentSize(second
, hDwp
);
676 return DeferWindowPos(hDwp
, NULL
,
677 splitter
.left
, splitter
.top
,
678 splitter
.right
- splitter
.left
,
679 splitter
.bottom
- splitter
.top
,
680 SWP_NOACTIVATE
| SWP_NOZORDER
);
685 splitter
.left
, splitter
.top
,
686 splitter
.right
- splitter
.left
,
687 splitter
.bottom
- splitter
.top
,
688 SWP_NOACTIVATE
| SWP_NOZORDER
);
694 BOOL
ProcessWindowMessage(HWND hwnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
, LRESULT
& theResult
, DWORD dwMapId
)
700 SetCursor(m_hCursor
);
710 if (GetCapture() == m_hWnd
)
717 if (GetCapture() == m_hWnd
)
720 GetCursorPos(&Point
);
721 ::ScreenToClient(GetParent(), &Point
);
742 void SetPos(int NewPos
)
746 rcParent
= m_LastRect
;
750 rcParent
.bottom
-= THICKNESS
;
754 if (m_Pos
< rcParent
.top
)
755 m_Pos
= rcParent
.top
;
757 if (m_Pos
> rcParent
.bottom
)
758 m_Pos
= rcParent
.bottom
;
762 rcParent
.right
-= THICKNESS
;
766 if (m_Pos
< rcParent
.left
)
767 m_Pos
= rcParent
.left
;
769 if (m_Pos
> rcParent
.right
)
770 m_Pos
= rcParent
.right
;
773 int count
= CountSizableChildren();
776 hdwp
= BeginDeferWindowPos(count
);
777 if (hdwp
) hdwp
= OnParentSize(m_LastRect
, hdwp
);
778 if (hdwp
) EndDeferWindowPos(hdwp
);
782 DECLARE_WND_CLASS_EX(_T("SplitterWindowClass"), CS_HREDRAW
| CS_VREDRAW
, COLOR_BTNFACE
)
784 /* Create splitter bar */
785 HWND
Create(HWND hwndParent
)
788 m_hCursor
= LoadCursor(0, IDC_SIZENS
);
790 m_hCursor
= LoadCursor(0, IDC_SIZEWE
);
792 DWORD style
= WS_CHILD
| WS_VISIBLE
;
793 DWORD exStyle
= WS_EX_TRANSPARENT
;
795 RECT size
= { 205, 180, 465, THICKNESS
};
796 size
.right
+= size
.left
;
797 size
.bottom
+= size
.top
;
799 return CWindowImpl::Create(hwndParent
, size
, NULL
, style
, exStyle
);