1 /* PROJECT: ReactOS UI Layout Engine
2 * LICENSE: GPL - See COPYING in the top level directory
3 * AUTHORS: David Quintana <gigaherz@gmail.com>
9 template<class T
, int GrowthRate
= 10>
18 m_hDpa
= DPA_Create(GrowthRate
);
23 DPA_DestroyCallback(m_hDpa
, s_OnRemoveItem
, this);
27 static int CALLBACK
s_OnRemoveItem(void * ptr
, void * context
)
29 CPointerArray
* self
= (CPointerArray
*) context
;
30 return (int) self
->OnRemoveItem(reinterpret_cast<T
*>(ptr
));
33 static int CALLBACK
s_OnCompareItems(void *p1
, void *p2
, LPARAM lParam
)
35 CPointerArray
* self
= (CPointerArray
*) lParam
;
36 return self
->OnCompareItems(reinterpret_cast<T
*>(p1
), reinterpret_cast<T
*>(p2
));
40 virtual BOOL
OnRemoveItem(T
* ptr
)
45 virtual int OnCompareItems(T
* p1
, T
* p2
)
47 int t
= (reinterpret_cast<int>(p2
) -reinterpret_cast<int>(p1
));
58 return DPA_GetPtrCount(m_hDpa
);
63 return (T
*) DPA_GetPtr(m_hDpa
, i
);
66 BOOL
Set(int i
, T
* ptr
)
68 return DPA_SetPtr(m_hDpa
, i
, ptr
);
71 int Insert(int at
, T
* ptr
)
73 return DPA_InsertPtr(m_hDpa
, at
, ptr
);
78 return DPA_InsertPtr(m_hDpa
, DA_LAST
, ptr
);
81 int IndexOf(T
* ptr
) const
83 return DPA_GetPtrIndex(m_hDpa
, ptr
);
96 T
* ptr
= (T
*) DPA_GetPtr(m_hDpa
, i
);
98 return DPA_DeletePtr(m_hDpa
, i
);
103 DPA_EnumCallback(s_OnRemoveItem
, this);
104 return DPA_DeleteAllPtrs(m_hDpa
);
109 return DPA_Sort(m_hDpa
, s_OnCompareItems
, (LPARAM
)this);
112 int Search(T
* item
, int iStart
, UINT uFlags
)
114 return DPA_Search(m_hDpa
, s_OnCompareItems
, (LPARAM
)this);
124 left
= right
= top
= bottom
= 0;
127 CUiRect(int l
, int t
, int r
, int b
)
145 : CUiRect(all
, all
, all
, all
)
149 CUiMargin(int horz
, int vert
)
150 : CUiRect(horz
, vert
, horz
, vert
)
173 m_Type
= Type_FitContent
;
177 CUiMeasure(MeasureType type
, int value
)
183 int ComputeMeasure(int parent
, int content
)
187 case Type_FitContent
:
192 return max(content
, parent
* m_Value
/ 100);
201 static CUiMeasure
FitContent()
203 return CUiMeasure(Type_FitContent
, 0);
206 static CUiMeasure
FitParent()
208 return CUiMeasure(Type_FitParent
, 0);
211 static CUiMeasure
Fixed(int pixels
)
213 return CUiMeasure(Type_Fixed
, pixels
);
216 static CUiMeasure
Percent(int percent
)
218 return CUiMeasure(Type_Percent
, percent
);
235 CUiAlignment m_HorizontalAlignment
;
236 CUiAlignment m_VerticalAlignment
;
241 m_HorizontalAlignment
= UiAlign_LeftTop
;
242 m_VerticalAlignment
= UiAlign_LeftTop
;
245 virtual void ComputeRect(RECT parentRect
, RECT currentRect
, RECT
* newRect
)
247 parentRect
.left
+= m_Margin
.left
;
248 parentRect
.right
-= m_Margin
.right
;
249 parentRect
.top
+= m_Margin
.top
;
250 parentRect
.bottom
-= m_Margin
.bottom
;
252 if (parentRect
.right
< parentRect
.left
)
253 parentRect
.right
= parentRect
.left
;
255 if (parentRect
.bottom
< parentRect
.top
)
256 parentRect
.bottom
= parentRect
.top
;
258 SIZE szParent
= { parentRect
.right
- parentRect
.left
, parentRect
.bottom
- parentRect
.top
};
259 SIZE szCurrent
= { currentRect
.right
- currentRect
.left
, currentRect
.bottom
- currentRect
.top
};
261 currentRect
= parentRect
;
263 switch (m_HorizontalAlignment
)
265 case UiAlign_LeftTop
:
266 currentRect
.right
= currentRect
.left
+ szCurrent
.cx
;
269 currentRect
.left
= parentRect
.left
+ (szParent
.cx
- szCurrent
.cx
) / 2;
270 currentRect
.right
= currentRect
.left
+ szCurrent
.cx
;
272 case UiAlign_RightBtm
:
273 currentRect
.left
= currentRect
.right
- szCurrent
.cx
;
279 switch (m_VerticalAlignment
)
281 case UiAlign_LeftTop
:
282 currentRect
.bottom
= currentRect
.top
+ szCurrent
.cy
;
285 currentRect
.top
= parentRect
.top
+ (szParent
.cy
- szCurrent
.cy
) / 2;
286 currentRect
.bottom
= currentRect
.top
+ szCurrent
.cy
;
288 case UiAlign_RightBtm
:
289 currentRect
.top
= currentRect
.bottom
- szCurrent
.cy
;
295 *newRect
= currentRect
;
300 virtual void ComputeMinimalSize(SIZE
* size
)
302 // Override in subclass
303 size
->cx
= max(size
->cx
, 0);
304 size
->cy
= min(size
->cy
, 0);
307 virtual void ComputeContentBounds(RECT
* rect
)
309 // Override in subclass
312 virtual DWORD_PTR
CountSizableChildren()
314 // Override in subclass
318 virtual HDWP
OnParentSize(RECT parentRect
, HDWP hDwp
)
320 // Override in subclass
328 CUiPrimitive
* m_Parent
;
331 virtual ~CUiPrimitive() {}
333 virtual CUiBox
* AsBox() { return NULL
; }
336 class CUiCollection
:
337 public CPointerArray
< CUiPrimitive
>
339 virtual BOOL
OnRemoveItem(CUiPrimitive
* ptr
)
349 CUiCollection m_Children
;
352 CUiCollection
& Children() { return m_Children
; }
366 m_Width
= CUiMeasure::FitParent();
367 m_Height
= CUiMeasure::FitParent();
374 virtual CUiBox
* AsBox() { return this; }
376 virtual void ComputeMinimalSize(SIZE
* size
)
378 for (int i
= 0; i
< m_Children
.GetCount(); i
++)
380 CUiBox
* box
= m_Children
.Get(i
)->AsBox();
383 box
->ComputeMinimalSize(size
);
388 virtual void ComputeContentBounds(RECT
* rect
)
390 for (int i
= 0; i
< m_Children
.GetCount(); i
++)
392 CUiBox
* box
= m_Children
.Get(i
)->AsBox();
395 box
->ComputeContentBounds(rect
);
400 virtual DWORD_PTR
CountSizableChildren()
403 for (int i
= 0; i
< m_Children
.GetCount(); i
++)
405 CUiBox
* box
= m_Children
.Get(i
)->AsBox();
408 count
+= box
->CountSizableChildren();
414 virtual HDWP
OnParentSize(RECT parentRect
, HDWP hDwp
)
418 SIZE content
= { 0 };
419 ComputeMinimalSize(&content
);
421 int preferredWidth
= m_Width
.ComputeMeasure(parentRect
.right
- parentRect
.left
, content
.cx
);
422 int preferredHeight
= m_Height
.ComputeMeasure(parentRect
.bottom
- parentRect
.top
, content
.cy
);
424 rect
.right
= preferredWidth
;
425 rect
.bottom
= preferredHeight
;
427 ComputeRect(parentRect
, rect
, &rect
);
429 for (int i
= 0; i
< m_Children
.GetCount(); i
++)
431 CUiBox
* box
= m_Children
.Get(i
)->AsBox();
434 hDwp
= box
->OnParentSize(rect
, hDwp
);
442 template<class T
= CWindow
>
449 virtual CUiBox
* AsBox() { return this; }
451 HWND
GetWindow() { return T::m_hWnd
; }
453 virtual void ComputeMinimalSize(SIZE
* size
)
455 // TODO: Maybe use WM_GETMINMAXINFO?
456 return CUiBox::ComputeMinimalSize(size
);
459 virtual void ComputeContentBounds(RECT
* rect
)
462 ::GetWindowRect(T::m_hWnd
, &r
);
463 rect
->left
= min(rect
->left
, r
.left
);
464 rect
->top
= min(rect
->top
, r
.top
);
465 rect
->right
= max(rect
->right
, r
.right
);
466 rect
->bottom
= max(rect
->bottom
, r
.bottom
);
469 virtual DWORD_PTR
CountSizableChildren()
474 virtual HDWP
OnParentSize(RECT parentRect
, HDWP hDwp
)
478 ::GetWindowRect(T::m_hWnd
, &rect
);
480 ComputeRect(parentRect
, rect
, &rect
);
484 return ::DeferWindowPos(hDwp
, T::m_hWnd
, NULL
, rect
.left
, rect
.top
, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
, SWP_NOACTIVATE
| SWP_NOZORDER
);
488 T::SetWindowPos(NULL
, rect
.left
, rect
.top
, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
, SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_DEFERERASE
);
498 void GetWindowTextW(ATL::CStringW
& szText
)
500 INT length
= CWindow::GetWindowTextLengthW() + 1;
501 CWindow::GetWindowTextW(szText
.GetBuffer(length
), length
);
502 szText
.ReleaseBuffer();
506 class CUiSplitPanel
:
509 public CWindowImpl
<CUiSplitPanel
>
511 static const int THICKNESS
= 4;
536 m_Width
= CUiMeasure::FitParent();
537 m_Height
= CUiMeasure::FitParent();
541 m_DynamicFirst
= FALSE
;
542 m_HasOldRect
= FALSE
;
545 virtual ~CUiSplitPanel()
549 virtual CUiBox
* AsBox() { return this; }
551 CUiCollection
& First() { return m_First
.Children(); }
552 CUiCollection
& Second() { return m_Second
.Children(); }
554 virtual void ComputeMinimalSize(SIZE
* size
)
557 size
->cx
= max(size
->cx
, THICKNESS
);
559 size
->cy
= max(size
->cy
, THICKNESS
);
560 m_First
.ComputeMinimalSize(size
);
561 m_Second
.ComputeMinimalSize(size
);
564 virtual void ComputeContentBounds(RECT
* rect
)
568 m_First
.ComputeContentBounds(rect
);
569 m_Second
.ComputeContentBounds(rect
);
571 ::GetWindowRect(m_hWnd
, &r
);
573 rect
->left
= min(rect
->left
, r
.left
);
574 rect
->top
= min(rect
->top
, r
.top
);
575 rect
->right
= max(rect
->right
, r
.right
);
576 rect
->bottom
= max(rect
->bottom
, r
.bottom
);
579 virtual DWORD_PTR
CountSizableChildren()
582 count
+= m_First
.CountSizableChildren();
583 count
+= m_Second
.CountSizableChildren();
587 virtual HDWP
OnParentSize(RECT parentRect
, HDWP hDwp
)
591 SIZE content
= { 0 };
592 ComputeMinimalSize(&content
);
594 int preferredWidth
= m_Width
.ComputeMeasure(parentRect
.right
- parentRect
.left
, content
.cx
);
595 int preferredHeight
= m_Width
.ComputeMeasure(parentRect
.bottom
- parentRect
.top
, content
.cy
);
597 rect
.right
= preferredWidth
;
598 rect
.bottom
= preferredHeight
;
600 ComputeRect(parentRect
, rect
, &rect
);
605 RECT oldRect
= m_LastRect
;
607 growth
.cx
= (parentRect
.right
- parentRect
.left
) - (oldRect
.right
- oldRect
.left
);
608 growth
.cy
= (parentRect
.bottom
- parentRect
.top
) - (oldRect
.bottom
- oldRect
.top
);
611 RECT splitter
= rect
;
617 rect
.top
+= m_MinFirst
;
618 rect
.bottom
-= THICKNESS
+ m_MinSecond
;
623 m_Pos
+= min(growth
.cy
, rect
.bottom
- (m_Pos
+THICKNESS
));
625 else if (growth
.cy
< 0)
627 m_Pos
+= max(growth
.cy
, rect
.top
- m_Pos
);
631 if (m_Pos
> rect
.bottom
)
634 if (m_Pos
< rect
.top
)
637 splitter
.top
= m_Pos
;
638 splitter
.bottom
= m_Pos
+ THICKNESS
;
639 first
.bottom
= splitter
.top
;
640 second
.top
= splitter
.bottom
;
644 rect
.left
+= m_MinFirst
;
645 rect
.right
-= THICKNESS
+ m_MinSecond
;
650 m_Pos
+= min(growth
.cx
, rect
.right
- (m_Pos
+ THICKNESS
));
652 else if (growth
.cx
< 0)
654 m_Pos
+= max(growth
.cy
, rect
.left
- m_Pos
);
658 if (m_Pos
> rect
.right
)
661 if (m_Pos
< rect
.left
)
664 splitter
.left
= m_Pos
;
665 splitter
.right
= m_Pos
+ THICKNESS
;
666 first
.right
= splitter
.left
;
667 second
.left
= splitter
.right
;
670 m_LastRect
= parentRect
;
673 hDwp
= m_First
.OnParentSize(first
, hDwp
);
674 hDwp
= m_Second
.OnParentSize(second
, hDwp
);
678 return DeferWindowPos(hDwp
, NULL
,
679 splitter
.left
, splitter
.top
,
680 splitter
.right
- splitter
.left
,
681 splitter
.bottom
- splitter
.top
,
682 SWP_NOACTIVATE
| SWP_NOZORDER
);
687 splitter
.left
, splitter
.top
,
688 splitter
.right
- splitter
.left
,
689 splitter
.bottom
- splitter
.top
,
690 SWP_NOACTIVATE
| SWP_NOZORDER
);
696 BOOL
ProcessWindowMessage(HWND hwnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
, LRESULT
& theResult
, DWORD dwMapId
)
702 SetCursor(m_hCursor
);
712 if (GetCapture() == m_hWnd
)
719 if (GetCapture() == m_hWnd
)
722 GetCursorPos(&Point
);
723 ::ScreenToClient(GetParent(), &Point
);
744 void SetPos(int NewPos
)
748 rcParent
= m_LastRect
;
752 rcParent
.bottom
-= THICKNESS
;
756 if (m_Pos
< rcParent
.top
)
757 m_Pos
= rcParent
.top
;
759 if (m_Pos
> rcParent
.bottom
)
760 m_Pos
= rcParent
.bottom
;
764 rcParent
.right
-= THICKNESS
;
768 if (m_Pos
< rcParent
.left
)
769 m_Pos
= rcParent
.left
;
771 if (m_Pos
> rcParent
.right
)
772 m_Pos
= rcParent
.right
;
775 int count
= CountSizableChildren();
778 hdwp
= BeginDeferWindowPos(count
);
779 if (hdwp
) hdwp
= OnParentSize(m_LastRect
, hdwp
);
780 if (hdwp
) EndDeferWindowPos(hdwp
);
784 DECLARE_WND_CLASS_EX(_T("SplitterWindowClass"), CS_HREDRAW
| CS_VREDRAW
, COLOR_BTNFACE
)
786 /* Create splitter bar */
787 HWND
Create(HWND hwndParent
)
790 m_hCursor
= LoadCursor(0, IDC_SIZENS
);
792 m_hCursor
= LoadCursor(0, IDC_SIZEWE
);
794 DWORD style
= WS_CHILD
| WS_VISIBLE
;
795 DWORD exStyle
= WS_EX_TRANSPARENT
;
797 RECT size
= { 205, 180, 465, THICKNESS
};
798 size
.right
+= size
.left
;
799 size
.bottom
+= size
.top
;
801 return CWindowImpl::Create(hwndParent
, size
, NULL
, style
, exStyle
);