reactos/base/applications/rapps/include/rosui.h
Joachim Henze a9b1e059f4
[RAPPS] Style-addendum, no functional change (#5091)
* Just rename the 4 new files. Absolutely no other change.
* CMakeLists.txt, adapt the filenames
* Adapt all includes to make it compile
* CApplicationDB->CAppDB
* CApplicationInfo->CAppInfo
* Follow Mark Jansens good example and antipad the headers
2023-02-28 01:00:29 +01:00

883 lines
19 KiB
C++

/*
* PROJECT: ReactOS UI Layout Engine
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* FILE: base/applications/rapps/include/rosui.h
* PURPOSE: ATL Layout engine for RAPPS
* COPYRIGHT: Copyright 2015 David Quintana (gigaherz@gmail.com)
*/
#pragma once
#include <atlwin.h>
template <class T, INT GrowthRate = 10> class CPointerArray
{
protected:
HDPA m_hDpa;
public:
CPointerArray()
{
m_hDpa = DPA_Create(GrowthRate);
}
~CPointerArray()
{
DPA_DestroyCallback(m_hDpa, s_OnRemoveItem, this);
}
private:
static INT CALLBACK
s_OnRemoveItem(PVOID ptr, PVOID context)
{
CPointerArray *self = (CPointerArray *)context;
return (INT)self->OnRemoveItem(reinterpret_cast<T *>(ptr));
}
static INT CALLBACK
s_OnCompareItems(PVOID p1, PVOID p2, LPARAM lParam)
{
CPointerArray *self = (CPointerArray *)lParam;
return self->OnCompareItems(reinterpret_cast<T *>(p1), reinterpret_cast<T *>(p2));
}
public:
virtual BOOL
OnRemoveItem(T *ptr)
{
return TRUE;
}
virtual INT
OnCompareItems(T *p1, T *p2)
{
INT_PTR t = (reinterpret_cast<INT_PTR>(p2) - reinterpret_cast<INT_PTR>(p1));
if (t > 0)
return 1;
if (t < 0)
return -1;
return 0;
}
public:
INT
GetCount() const
{
return DPA_GetPtrCount(m_hDpa);
}
T *
Get(INT i) const
{
return (T *)DPA_GetPtr(m_hDpa, i);
}
BOOL
Set(INT i, T *ptr)
{
return DPA_SetPtr(m_hDpa, i, ptr);
}
INT
Insert(INT at, T *ptr)
{
return DPA_InsertPtr(m_hDpa, at, ptr);
}
INT
Append(T *ptr)
{
return DPA_InsertPtr(m_hDpa, DA_LAST, ptr);
}
INT
IndexOf(T *ptr) const
{
return DPA_GetPtrIndex(m_hDpa, ptr);
}
BOOL
Remove(T *ptr)
{
INT i = IndexOf(ptr);
if (i < 0)
return FALSE;
return RemoveAt(i);
}
BOOL
RemoveAt(INT i)
{
PVOID ptr = DPA_DeletePtr(m_hDpa, i);
if (ptr != NULL)
{
OnRemoveItem(reinterpret_cast<T *>(ptr));
return TRUE;
}
return FALSE;
}
BOOL
Clear()
{
DPA_EnumCallback(s_OnRemoveItem, this);
return DPA_DeleteAllPtrs(m_hDpa);
}
BOOL
Sort()
{
return DPA_Sort(m_hDpa, s_OnCompareItems, (LPARAM)this);
}
INT
Search(T *item, INT iStart, UINT uFlags)
{
return DPA_Search(m_hDpa, item, 0, s_OnCompareItems, (LPARAM)this, 0);
}
};
class CUiRect : public RECT
{
public:
CUiRect()
{
left = right = top = bottom = 0;
}
CUiRect(INT l, INT t, INT r, INT b)
{
left = l;
right = r;
top = t;
bottom = b;
}
};
class CUiMargin : public CUiRect
{
public:
CUiMargin()
{
}
CUiMargin(INT all) : CUiRect(all, all, all, all)
{
}
CUiMargin(INT horz, INT vert) : CUiRect(horz, vert, horz, vert)
{
}
};
class CUiMeasure
{
public:
enum MeasureType
{
Type_FitContent = 0,
Type_Fixed = 1,
Type_Percent = 2,
Type_FitParent = 3
};
private:
MeasureType m_Type;
INT m_Value;
public:
CUiMeasure()
{
m_Type = Type_FitContent;
m_Value = 0;
}
CUiMeasure(MeasureType type, INT value)
{
m_Type = type;
m_Value = value;
}
INT
ComputeMeasure(INT parent, INT content)
{
switch (m_Type)
{
case Type_FitContent:
return content;
case Type_Fixed:
return m_Value;
case Type_Percent:
return max(content, parent * m_Value / 100);
case Type_FitParent:
return parent;
}
return 0;
}
public:
static CUiMeasure
FitContent()
{
return CUiMeasure(Type_FitContent, 0);
}
static CUiMeasure
FitParent()
{
return CUiMeasure(Type_FitParent, 0);
}
static CUiMeasure
Fixed(INT pixels)
{
return CUiMeasure(Type_Fixed, pixels);
}
static CUiMeasure
Percent(INT percent)
{
return CUiMeasure(Type_Percent, percent);
}
};
enum CUiAlignment
{
UiAlign_LeftTop,
UiAlign_Middle,
UiAlign_RightBtm,
UiAlign_Stretch
};
class CUiBox
{
public:
CUiMargin m_Margin;
CUiAlignment m_HorizontalAlignment;
CUiAlignment m_VerticalAlignment;
protected:
CUiBox()
{
m_HorizontalAlignment = UiAlign_LeftTop;
m_VerticalAlignment = UiAlign_LeftTop;
}
virtual VOID
ComputeRect(RECT parentRect, RECT currentRect, RECT *newRect)
{
parentRect.left += m_Margin.left;
parentRect.right -= m_Margin.right;
parentRect.top += m_Margin.top;
parentRect.bottom -= m_Margin.bottom;
if (parentRect.right < parentRect.left)
parentRect.right = parentRect.left;
if (parentRect.bottom < parentRect.top)
parentRect.bottom = parentRect.top;
SIZE szParent = {parentRect.right - parentRect.left, parentRect.bottom - parentRect.top};
SIZE szCurrent = {currentRect.right - currentRect.left, currentRect.bottom - currentRect.top};
currentRect = parentRect;
switch (m_HorizontalAlignment)
{
case UiAlign_LeftTop:
currentRect.right = currentRect.left + szCurrent.cx;
break;
case UiAlign_Middle:
currentRect.left = parentRect.left + (szParent.cx - szCurrent.cx) / 2;
currentRect.right = currentRect.left + szCurrent.cx;
break;
case UiAlign_RightBtm:
currentRect.left = currentRect.right - szCurrent.cx;
break;
default:
break;
}
switch (m_VerticalAlignment)
{
case UiAlign_LeftTop:
currentRect.bottom = currentRect.top + szCurrent.cy;
break;
case UiAlign_Middle:
currentRect.top = parentRect.top + (szParent.cy - szCurrent.cy) / 2;
currentRect.bottom = currentRect.top + szCurrent.cy;
break;
case UiAlign_RightBtm:
currentRect.top = currentRect.bottom - szCurrent.cy;
break;
default:
break;
}
*newRect = currentRect;
}
public:
virtual VOID
ComputeMinimalSize(SIZE *size)
{
// Override in subclass
size->cx = max(size->cx, 0);
size->cy = min(size->cy, 0);
};
virtual VOID
ComputeContentBounds(RECT *rect){
// Override in subclass
};
virtual DWORD_PTR
CountSizableChildren()
{
// Override in subclass
return 0;
};
virtual HDWP
OnParentSize(RECT parentRect, HDWP hDwp)
{
// Override in subclass
return NULL;
};
};
class CUiPrimitive
{
protected:
CUiPrimitive *m_Parent;
public:
virtual ~CUiPrimitive()
{
}
virtual CUiBox *
AsBox()
{
return NULL;
}
};
class CUiCollection : public CPointerArray<CUiPrimitive>
{
virtual BOOL
OnRemoveItem(CUiPrimitive *ptr)
{
delete ptr;
return TRUE;
}
};
class CUiContainer
{
protected:
CUiCollection m_Children;
public:
CUiCollection &
Children()
{
return m_Children;
}
};
class CUiPanel : public CUiPrimitive, public CUiBox, public CUiContainer
{
public:
CUiMeasure m_Width;
CUiMeasure m_Height;
CUiPanel()
{
m_Width = CUiMeasure::FitParent();
m_Height = CUiMeasure::FitParent();
}
virtual ~CUiPanel()
{
}
virtual CUiBox *
AsBox()
{
return this;
}
virtual VOID
ComputeMinimalSize(SIZE *size)
{
for (INT i = 0; i < m_Children.GetCount(); i++)
{
CUiBox *box = m_Children.Get(i)->AsBox();
if (box)
{
box->ComputeMinimalSize(size);
}
}
};
virtual VOID
ComputeContentBounds(RECT *rect)
{
for (INT i = 0; i < m_Children.GetCount(); i++)
{
CUiBox *box = m_Children.Get(i)->AsBox();
if (box)
{
box->ComputeContentBounds(rect);
}
}
};
virtual DWORD_PTR
CountSizableChildren()
{
INT count = 0;
for (INT i = 0; i < m_Children.GetCount(); i++)
{
CUiBox *box = m_Children.Get(i)->AsBox();
if (box)
{
count += box->CountSizableChildren();
}
}
return count;
}
virtual HDWP
OnParentSize(RECT parentRect, HDWP hDwp)
{
RECT rect = {0};
SIZE content = {0};
ComputeMinimalSize(&content);
INT preferredWidth = m_Width.ComputeMeasure(parentRect.right - parentRect.left, content.cx);
INT preferredHeight = m_Height.ComputeMeasure(parentRect.bottom - parentRect.top, content.cy);
rect.right = preferredWidth;
rect.bottom = preferredHeight;
ComputeRect(parentRect, rect, &rect);
for (INT i = 0; i < m_Children.GetCount(); i++)
{
CUiBox *box = m_Children.Get(i)->AsBox();
if (box)
{
hDwp = box->OnParentSize(rect, hDwp);
}
}
return hDwp;
}
};
template <class T = CWindow> class CUiWindow : public CUiPrimitive, public CUiBox, public T
{
public:
virtual CUiBox *
AsBox()
{
return this;
}
HWND
GetWindow()
{
return T::m_hWnd;
}
virtual VOID
ComputeMinimalSize(SIZE *size)
{
// TODO: Maybe use WM_GETMINMAXINFO?
return CUiBox::ComputeMinimalSize(size);
};
virtual VOID
ComputeContentBounds(RECT *rect)
{
RECT r;
::GetWindowRect(T::m_hWnd, &r);
rect->left = min(rect->left, r.left);
rect->top = min(rect->top, r.top);
rect->right = max(rect->right, r.right);
rect->bottom = max(rect->bottom, r.bottom);
};
virtual DWORD_PTR
CountSizableChildren()
{
return 1;
};
virtual HDWP
OnParentSize(RECT parentRect, HDWP hDwp)
{
RECT rect;
::GetWindowRect(T::m_hWnd, &rect);
ComputeRect(parentRect, rect, &rect);
if (hDwp)
{
return ::DeferWindowPos(
hDwp, T::m_hWnd, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOZORDER);
}
else
{
T::SetWindowPos(
NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_DEFERERASE);
return NULL;
}
};
virtual VOID
AppendTabOrderWindow(int Direction, ATL::CSimpleArray<HWND> &TabOrderList)
{
TabOrderList.Add(T::m_hWnd);
return;
}
virtual ~CUiWindow()
{
if (T::IsWindow())
{
T::DestroyWindow();
}
}
VOID
GetWindowTextW(CStringW &szText)
{
INT length = CWindow::GetWindowTextLengthW() + 1;
CWindow::GetWindowTextW(szText.GetBuffer(length), length);
szText.ReleaseBuffer();
}
};
class CUiSplitPanel : public CUiPrimitive, public CUiBox, public CWindowImpl<CUiSplitPanel>
{
static const INT THICKNESS = 4;
protected:
HCURSOR m_hCursor;
CUiPanel m_First;
CUiPanel m_Second;
RECT m_LastRect;
BOOL m_HasOldRect;
public:
INT m_Pos;
BOOL m_Horizontal;
BOOL m_DynamicFirst;
INT m_MinFirst;
INT m_MinSecond;
CUiMeasure m_Width;
CUiMeasure m_Height;
CUiSplitPanel()
{
m_hCursor = NULL;
m_Width = CUiMeasure::FitParent();
m_Height = CUiMeasure::FitParent();
m_Pos = 100;
m_Horizontal = FALSE;
m_MinFirst = 100;
m_MinSecond = 100;
m_DynamicFirst = FALSE;
m_HasOldRect = FALSE;
memset(&m_LastRect, 0, sizeof(m_LastRect));
}
virtual ~CUiSplitPanel()
{
}
virtual CUiBox *
AsBox()
{
return this;
}
CUiCollection &
First()
{
return m_First.Children();
}
CUiCollection &
Second()
{
return m_Second.Children();
}
virtual VOID
ComputeMinimalSize(SIZE *size)
{
if (m_Horizontal)
size->cx = max(size->cx, THICKNESS);
else
size->cy = max(size->cy, THICKNESS);
m_First.ComputeMinimalSize(size);
m_Second.ComputeMinimalSize(size);
};
virtual VOID
ComputeContentBounds(RECT *rect)
{
RECT r;
m_First.ComputeContentBounds(rect);
m_Second.ComputeContentBounds(rect);
::GetWindowRect(m_hWnd, &r);
rect->left = min(rect->left, r.left);
rect->top = min(rect->top, r.top);
rect->right = max(rect->right, r.right);
rect->bottom = max(rect->bottom, r.bottom);
};
virtual DWORD_PTR
CountSizableChildren()
{
INT count = 1;
count += m_First.CountSizableChildren();
count += m_Second.CountSizableChildren();
return count;
};
virtual HDWP
OnParentSize(RECT parentRect, HDWP hDwp)
{
RECT rect = {0};
SIZE content = {0};
ComputeMinimalSize(&content);
INT preferredWidth = m_Width.ComputeMeasure(parentRect.right - parentRect.left, content.cx);
INT preferredHeight = m_Width.ComputeMeasure(parentRect.bottom - parentRect.top, content.cy);
rect.right = preferredWidth;
rect.bottom = preferredHeight;
ComputeRect(parentRect, rect, &rect);
SIZE growth = {0};
if (m_HasOldRect)
{
RECT oldRect = m_LastRect;
growth.cx = (parentRect.right - parentRect.left) - (oldRect.right - oldRect.left);
growth.cy = (parentRect.bottom - parentRect.top) - (oldRect.bottom - oldRect.top);
}
RECT splitter = rect;
RECT first = rect;
RECT second = rect;
if (m_Horizontal)
{
rect.top += m_MinFirst;
rect.bottom -= THICKNESS + m_MinSecond;
if (m_DynamicFirst)
{
if (growth.cy > 0)
{
m_Pos += min(growth.cy, rect.bottom - (m_Pos + THICKNESS));
}
else if (growth.cy < 0)
{
m_Pos += max(growth.cy, rect.top - m_Pos);
}
}
if (m_Pos > rect.bottom)
m_Pos = rect.bottom;
if (m_Pos < rect.top)
m_Pos = rect.top;
splitter.top = m_Pos;
splitter.bottom = m_Pos + THICKNESS;
first.bottom = splitter.top;
second.top = splitter.bottom;
}
else
{
rect.left += m_MinFirst;
rect.right -= THICKNESS + m_MinSecond;
if (m_DynamicFirst)
{
if (growth.cx > 0)
{
m_Pos += min(growth.cx, rect.right - (m_Pos + THICKNESS));
}
else if (growth.cx < 0)
{
m_Pos += max(growth.cy, rect.left - m_Pos);
}
}
if (m_Pos > rect.right)
m_Pos = rect.right;
if (m_Pos < rect.left)
m_Pos = rect.left;
splitter.left = m_Pos;
splitter.right = m_Pos + THICKNESS;
first.right = splitter.left;
second.left = splitter.right;
}
m_LastRect = parentRect;
m_HasOldRect = TRUE;
hDwp = m_First.OnParentSize(first, hDwp);
hDwp = m_Second.OnParentSize(second, hDwp);
if (hDwp)
{
return DeferWindowPos(
hDwp, NULL, splitter.left, splitter.top, splitter.right - splitter.left, splitter.bottom - splitter.top,
SWP_NOACTIVATE | SWP_NOZORDER);
}
else
{
SetWindowPos(
NULL, splitter.left, splitter.top, splitter.right - splitter.left, splitter.bottom - splitter.top,
SWP_NOACTIVATE | SWP_NOZORDER);
return NULL;
}
};
private:
BOOL
ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT &theResult, DWORD dwMapId)
{
theResult = 0;
switch (Msg)
{
case WM_SETCURSOR:
SetCursor(m_hCursor);
theResult = TRUE;
break;
case WM_LBUTTONDOWN:
SetCapture();
break;
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
if (GetCapture() == m_hWnd)
{
ReleaseCapture();
}
break;
case WM_MOUSEMOVE:
if (GetCapture() == m_hWnd)
{
POINT Point;
GetCursorPos(&Point);
::ScreenToClient(GetParent(), &Point);
if (m_Horizontal)
SetPos(Point.y);
else
SetPos(Point.x);
}
break;
default:
return FALSE;
}
return TRUE;
}
public:
INT
GetPos()
{
return m_Pos;
}
VOID
SetPos(INT NewPos)
{
RECT rcParent;
rcParent = m_LastRect;
if (m_Horizontal)
{
rcParent.bottom -= THICKNESS;
m_Pos = NewPos;
if (m_Pos < rcParent.top)
m_Pos = rcParent.top;
if (m_Pos > rcParent.bottom)
m_Pos = rcParent.bottom;
}
else
{
rcParent.right -= THICKNESS;
m_Pos = NewPos;
if (m_Pos < rcParent.left)
m_Pos = rcParent.left;
if (m_Pos > rcParent.right)
m_Pos = rcParent.right;
}
INT count = CountSizableChildren();
HDWP hdwp = NULL;
hdwp = BeginDeferWindowPos(count);
if (hdwp)
hdwp = OnParentSize(m_LastRect, hdwp);
if (hdwp)
EndDeferWindowPos(hdwp);
}
public:
DECLARE_WND_CLASS_EX(_T("SplitterWindowClass"), CS_HREDRAW | CS_VREDRAW, COLOR_BTNFACE)
/* Create splitter bar */
HWND
Create(HWND hwndParent)
{
if (m_Horizontal)
m_hCursor = LoadCursor(0, IDC_SIZENS);
else
m_hCursor = LoadCursor(0, IDC_SIZEWE);
DWORD style = WS_CHILD | WS_VISIBLE;
DWORD exStyle = WS_EX_TRANSPARENT;
RECT size = {205, 180, 465, THICKNESS};
size.right += size.left;
size.bottom += size.top;
return CWindowImpl::Create(hwndParent, size, NULL, style, exStyle);
}
};