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 * Copyright 2017 Alexander Shaposhnikov (chaez.san@gmail.com)
13 template<class T
, INT GrowthRate
= 10>
22 m_hDpa
= DPA_Create(GrowthRate
);
27 DPA_DestroyCallback(m_hDpa
, s_OnRemoveItem
, this);
31 static INT CALLBACK
s_OnRemoveItem(PVOID ptr
, PVOID context
)
33 CPointerArray
* self
= (CPointerArray
*) context
;
34 return (INT
) self
->OnRemoveItem(reinterpret_cast<T
*>(ptr
));
37 static INT CALLBACK
s_OnCompareItems(PVOID p1
, PVOID p2
, LPARAM lParam
)
39 CPointerArray
* self
= (CPointerArray
*) lParam
;
40 return self
->OnCompareItems(reinterpret_cast<T
*>(p1
), reinterpret_cast<T
*>(p2
));
44 virtual BOOL
OnRemoveItem(T
* ptr
)
49 virtual INT
OnCompareItems(T
* p1
, T
* p2
)
51 INT t
= (reinterpret_cast<INT
>(p2
) - reinterpret_cast<INT
>(p1
));
62 return DPA_GetPtrCount(m_hDpa
);
67 return (T
*) DPA_GetPtr(m_hDpa
, i
);
70 BOOL
Set(INT i
, T
* ptr
)
72 return DPA_SetPtr(m_hDpa
, i
, ptr
);
75 INT
Insert(INT at
, T
* ptr
)
77 return DPA_InsertPtr(m_hDpa
, at
, ptr
);
82 return DPA_InsertPtr(m_hDpa
, DA_LAST
, ptr
);
85 INT
IndexOf(T
* ptr
) const
87 return DPA_GetPtrIndex(m_hDpa
, ptr
);
100 T
* ptr
= (T
*) DPA_GetPtr(m_hDpa
, i
);
102 return DPA_DeletePtr(m_hDpa
, i
);
107 DPA_EnumCallback(s_OnRemoveItem
, this);
108 return DPA_DeleteAllPtrs(m_hDpa
);
113 return DPA_Sort(m_hDpa
, s_OnCompareItems
, (LPARAM
)this);
116 INT
Search(T
* item
, INT iStart
, UINT uFlags
)
118 return DPA_Search(m_hDpa
, s_OnCompareItems
, (LPARAM
)this);
128 left
= right
= top
= bottom
= 0;
131 CUiRect(INT l
, INT t
, INT r
, INT b
)
149 : CUiRect(all
, all
, all
, all
)
153 CUiMargin(INT horz
, INT vert
)
154 : CUiRect(horz
, vert
, horz
, vert
)
177 m_Type
= Type_FitContent
;
181 CUiMeasure(MeasureType type
, INT value
)
187 INT
ComputeMeasure(INT parent
, INT content
)
191 case Type_FitContent
:
196 return max(content
, parent
* m_Value
/ 100);
205 static CUiMeasure
FitContent()
207 return CUiMeasure(Type_FitContent
, 0);
210 static CUiMeasure
FitParent()
212 return CUiMeasure(Type_FitParent
, 0);
215 static CUiMeasure
Fixed(INT pixels
)
217 return CUiMeasure(Type_Fixed
, pixels
);
220 static CUiMeasure
Percent(INT percent
)
222 return CUiMeasure(Type_Percent
, percent
);
239 CUiAlignment m_HorizontalAlignment
;
240 CUiAlignment m_VerticalAlignment
;
245 m_HorizontalAlignment
= UiAlign_LeftTop
;
246 m_VerticalAlignment
= UiAlign_LeftTop
;
249 virtual VOID
ComputeRect(RECT parentRect
, RECT currentRect
, RECT
* newRect
)
251 parentRect
.left
+= m_Margin
.left
;
252 parentRect
.right
-= m_Margin
.right
;
253 parentRect
.top
+= m_Margin
.top
;
254 parentRect
.bottom
-= m_Margin
.bottom
;
256 if (parentRect
.right
< parentRect
.left
)
257 parentRect
.right
= parentRect
.left
;
259 if (parentRect
.bottom
< parentRect
.top
)
260 parentRect
.bottom
= parentRect
.top
;
262 SIZE szParent
= {parentRect
.right
- parentRect
.left
, parentRect
.bottom
- parentRect
.top
};
263 SIZE szCurrent
= {currentRect
.right
- currentRect
.left
, currentRect
.bottom
- currentRect
.top
};
265 currentRect
= parentRect
;
267 switch (m_HorizontalAlignment
)
269 case UiAlign_LeftTop
:
270 currentRect
.right
= currentRect
.left
+ szCurrent
.cx
;
273 currentRect
.left
= parentRect
.left
+ (szParent
.cx
- szCurrent
.cx
) / 2;
274 currentRect
.right
= currentRect
.left
+ szCurrent
.cx
;
276 case UiAlign_RightBtm
:
277 currentRect
.left
= currentRect
.right
- szCurrent
.cx
;
283 switch (m_VerticalAlignment
)
285 case UiAlign_LeftTop
:
286 currentRect
.bottom
= currentRect
.top
+ szCurrent
.cy
;
289 currentRect
.top
= parentRect
.top
+ (szParent
.cy
- szCurrent
.cy
) / 2;
290 currentRect
.bottom
= currentRect
.top
+ szCurrent
.cy
;
292 case UiAlign_RightBtm
:
293 currentRect
.top
= currentRect
.bottom
- szCurrent
.cy
;
299 *newRect
= currentRect
;
304 virtual VOID
ComputeMinimalSize(SIZE
* size
)
306 // Override in subclass
307 size
->cx
= max(size
->cx
, 0);
308 size
->cy
= min(size
->cy
, 0);
311 virtual VOID
ComputeContentBounds(RECT
* rect
)
313 // Override in subclass
316 virtual DWORD_PTR
CountSizableChildren()
318 // Override in subclass
322 virtual HDWP
OnParentSize(RECT parentRect
, HDWP hDwp
)
324 // Override in subclass
332 CUiPrimitive
* m_Parent
;
335 virtual ~CUiPrimitive() {}
337 virtual CUiBox
* AsBox() { return NULL
; }
340 class CUiCollection
:
341 public CPointerArray
< CUiPrimitive
>
343 virtual BOOL
OnRemoveItem(CUiPrimitive
* ptr
)
353 CUiCollection m_Children
;
356 CUiCollection
& Children() { return m_Children
; }
370 m_Width
= CUiMeasure::FitParent();
371 m_Height
= CUiMeasure::FitParent();
378 virtual CUiBox
* AsBox() { return this; }
380 virtual VOID
ComputeMinimalSize(SIZE
* size
)
382 for (INT i
= 0; i
< m_Children
.GetCount(); i
++)
384 CUiBox
* box
= m_Children
.Get(i
)->AsBox();
387 box
->ComputeMinimalSize(size
);
392 virtual VOID
ComputeContentBounds(RECT
* rect
)
394 for (INT i
= 0; i
< m_Children
.GetCount(); i
++)
396 CUiBox
* box
= m_Children
.Get(i
)->AsBox();
399 box
->ComputeContentBounds(rect
);
404 virtual DWORD_PTR
CountSizableChildren()
407 for (INT i
= 0; i
< m_Children
.GetCount(); i
++)
409 CUiBox
* box
= m_Children
.Get(i
)->AsBox();
412 count
+= box
->CountSizableChildren();
418 virtual HDWP
OnParentSize(RECT parentRect
, HDWP hDwp
)
423 ComputeMinimalSize(&content
);
425 INT preferredWidth
= m_Width
.ComputeMeasure(parentRect
.right
- parentRect
.left
, content
.cx
);
426 INT preferredHeight
= m_Height
.ComputeMeasure(parentRect
.bottom
- parentRect
.top
, content
.cy
);
428 rect
.right
= preferredWidth
;
429 rect
.bottom
= preferredHeight
;
431 ComputeRect(parentRect
, rect
, &rect
);
433 for (INT i
= 0; i
< m_Children
.GetCount(); i
++)
435 CUiBox
* box
= m_Children
.Get(i
)->AsBox();
438 hDwp
= box
->OnParentSize(rect
, hDwp
);
446 template<class T
= CWindow
>
453 virtual CUiBox
* AsBox() { return this; }
455 HWND
GetWindow() { return T::m_hWnd
; }
457 virtual VOID
ComputeMinimalSize(SIZE
* size
)
459 // TODO: Maybe use WM_GETMINMAXINFO?
460 return CUiBox::ComputeMinimalSize(size
);
463 virtual VOID
ComputeContentBounds(RECT
* rect
)
466 ::GetWindowRect(T::m_hWnd
, &r
);
467 rect
->left
= min(rect
->left
, r
.left
);
468 rect
->top
= min(rect
->top
, r
.top
);
469 rect
->right
= max(rect
->right
, r
.right
);
470 rect
->bottom
= max(rect
->bottom
, r
.bottom
);
473 virtual DWORD_PTR
CountSizableChildren()
478 virtual HDWP
OnParentSize(RECT parentRect
, HDWP hDwp
)
482 ::GetWindowRect(T::m_hWnd
, &rect
);
484 ComputeRect(parentRect
, rect
, &rect
);
488 return ::DeferWindowPos(hDwp
, T::m_hWnd
, NULL
, rect
.left
, rect
.top
, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
, SWP_NOACTIVATE
| SWP_NOZORDER
);
492 T::SetWindowPos(NULL
, rect
.left
, rect
.top
, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
, SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_DEFERERASE
);
502 VOID
GetWindowTextW(ATL::CStringW
& szText
)
504 INT length
= CWindow::GetWindowTextLengthW() + 1;
505 CWindow::GetWindowTextW(szText
.GetBuffer(length
), length
);
506 szText
.ReleaseBuffer();
510 class CUiSplitPanel
:
513 public CWindowImpl
<CUiSplitPanel
>
515 static const INT THICKNESS
= 4;
540 m_Width
= CUiMeasure::FitParent();
541 m_Height
= CUiMeasure::FitParent();
545 m_DynamicFirst
= FALSE
;
546 m_HasOldRect
= FALSE
;
549 virtual ~CUiSplitPanel()
553 virtual CUiBox
* AsBox() { return this; }
555 CUiCollection
& First() { return m_First
.Children(); }
556 CUiCollection
& Second() { return m_Second
.Children(); }
558 virtual VOID
ComputeMinimalSize(SIZE
* size
)
561 size
->cx
= max(size
->cx
, THICKNESS
);
563 size
->cy
= max(size
->cy
, THICKNESS
);
564 m_First
.ComputeMinimalSize(size
);
565 m_Second
.ComputeMinimalSize(size
);
568 virtual VOID
ComputeContentBounds(RECT
* rect
)
572 m_First
.ComputeContentBounds(rect
);
573 m_Second
.ComputeContentBounds(rect
);
575 ::GetWindowRect(m_hWnd
, &r
);
577 rect
->left
= min(rect
->left
, r
.left
);
578 rect
->top
= min(rect
->top
, r
.top
);
579 rect
->right
= max(rect
->right
, r
.right
);
580 rect
->bottom
= max(rect
->bottom
, r
.bottom
);
583 virtual DWORD_PTR
CountSizableChildren()
586 count
+= m_First
.CountSizableChildren();
587 count
+= m_Second
.CountSizableChildren();
591 virtual HDWP
OnParentSize(RECT parentRect
, HDWP hDwp
)
596 ComputeMinimalSize(&content
);
598 INT preferredWidth
= m_Width
.ComputeMeasure(parentRect
.right
- parentRect
.left
, content
.cx
);
599 INT preferredHeight
= m_Width
.ComputeMeasure(parentRect
.bottom
- parentRect
.top
, content
.cy
);
601 rect
.right
= preferredWidth
;
602 rect
.bottom
= preferredHeight
;
604 ComputeRect(parentRect
, rect
, &rect
);
609 RECT oldRect
= m_LastRect
;
611 growth
.cx
= (parentRect
.right
- parentRect
.left
) - (oldRect
.right
- oldRect
.left
);
612 growth
.cy
= (parentRect
.bottom
- parentRect
.top
) - (oldRect
.bottom
- oldRect
.top
);
615 RECT splitter
= rect
;
621 rect
.top
+= m_MinFirst
;
622 rect
.bottom
-= THICKNESS
+ m_MinSecond
;
627 m_Pos
+= min(growth
.cy
, rect
.bottom
- (m_Pos
+ THICKNESS
));
629 else if (growth
.cy
< 0)
631 m_Pos
+= max(growth
.cy
, rect
.top
- m_Pos
);
635 if (m_Pos
> rect
.bottom
)
638 if (m_Pos
< rect
.top
)
641 splitter
.top
= m_Pos
;
642 splitter
.bottom
= m_Pos
+ THICKNESS
;
643 first
.bottom
= splitter
.top
;
644 second
.top
= splitter
.bottom
;
648 rect
.left
+= m_MinFirst
;
649 rect
.right
-= THICKNESS
+ m_MinSecond
;
654 m_Pos
+= min(growth
.cx
, rect
.right
- (m_Pos
+ THICKNESS
));
656 else if (growth
.cx
< 0)
658 m_Pos
+= max(growth
.cy
, rect
.left
- m_Pos
);
662 if (m_Pos
> rect
.right
)
665 if (m_Pos
< rect
.left
)
668 splitter
.left
= m_Pos
;
669 splitter
.right
= m_Pos
+ THICKNESS
;
670 first
.right
= splitter
.left
;
671 second
.left
= splitter
.right
;
674 m_LastRect
= parentRect
;
677 hDwp
= m_First
.OnParentSize(first
, hDwp
);
678 hDwp
= m_Second
.OnParentSize(second
, hDwp
);
682 return DeferWindowPos(hDwp
, NULL
,
683 splitter
.left
, splitter
.top
,
684 splitter
.right
- splitter
.left
,
685 splitter
.bottom
- splitter
.top
,
686 SWP_NOACTIVATE
| SWP_NOZORDER
);
691 splitter
.left
, splitter
.top
,
692 splitter
.right
- splitter
.left
,
693 splitter
.bottom
- splitter
.top
,
694 SWP_NOACTIVATE
| SWP_NOZORDER
);
700 BOOL
ProcessWindowMessage(HWND hwnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
, LRESULT
& theResult
, DWORD dwMapId
)
706 SetCursor(m_hCursor
);
716 if (GetCapture() == m_hWnd
)
723 if (GetCapture() == m_hWnd
)
726 GetCursorPos(&Point
);
727 ::ScreenToClient(GetParent(), &Point
);
748 VOID
SetPos(INT NewPos
)
752 rcParent
= m_LastRect
;
756 rcParent
.bottom
-= THICKNESS
;
760 if (m_Pos
< rcParent
.top
)
761 m_Pos
= rcParent
.top
;
763 if (m_Pos
> rcParent
.bottom
)
764 m_Pos
= rcParent
.bottom
;
768 rcParent
.right
-= THICKNESS
;
772 if (m_Pos
< rcParent
.left
)
773 m_Pos
= rcParent
.left
;
775 if (m_Pos
> rcParent
.right
)
776 m_Pos
= rcParent
.right
;
779 INT count
= CountSizableChildren();
782 hdwp
= BeginDeferWindowPos(count
);
783 if (hdwp
) hdwp
= OnParentSize(m_LastRect
, hdwp
);
784 if (hdwp
) EndDeferWindowPos(hdwp
);
788 DECLARE_WND_CLASS_EX(_T("SplitterWindowClass"), CS_HREDRAW
| CS_VREDRAW
, COLOR_BTNFACE
)
790 /* Create splitter bar */
791 HWND
Create(HWND hwndParent
)
794 m_hCursor
= LoadCursor(0, IDC_SIZENS
);
796 m_hCursor
= LoadCursor(0, IDC_SIZEWE
);
798 DWORD style
= WS_CHILD
| WS_VISIBLE
;
799 DWORD exStyle
= WS_EX_TRANSPARENT
;
801 RECT size
= {205, 180, 465, THICKNESS
};
802 size
.right
+= size
.left
;
803 size
.bottom
+= size
.top
;
805 return CWindowImpl::Create(hwndParent
, size
, NULL
, style
, exStyle
);