[RAPPS] Replaced my @gmail.com email with @reactos,org one & Removed my copyright...
[reactos.git] / reactos / base / applications / rapps / include / rosui.h
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>
13 class CPointerArray
14 {
15 protected:
16 HDPA m_hDpa;
17
18 public:
19 CPointerArray()
20 {
21 m_hDpa = DPA_Create(GrowthRate);
22 }
23
24 ~CPointerArray()
25 {
26 DPA_DestroyCallback(m_hDpa, s_OnRemoveItem, this);
27 }
28
29 private:
30 static INT CALLBACK 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 {
38 CPointerArray * self = (CPointerArray*) lParam;
39 return self->OnCompareItems(reinterpret_cast<T*>(p1), reinterpret_cast<T*>(p2));
40 }
41
42 public:
43 virtual BOOL OnRemoveItem(T * ptr)
44 {
45 return TRUE;
46 }
47
48 virtual INT OnCompareItems(T * p1, T * p2)
49 {
50 INT t = (reinterpret_cast<INT>(p2) - reinterpret_cast<INT>(p1));
51 if (t > 0)
52 return 1;
53 if (t < 0)
54 return -1;
55 return 0;
56 }
57
58 public:
59 INT GetCount() const
60 {
61 return DPA_GetPtrCount(m_hDpa);
62 }
63
64 T* Get(INT i) const
65 {
66 return (T*) DPA_GetPtr(m_hDpa, i);
67 }
68
69 BOOL Set(INT i, T* ptr)
70 {
71 return DPA_SetPtr(m_hDpa, i, ptr);
72 }
73
74 INT Insert(INT at, T* ptr)
75 {
76 return DPA_InsertPtr(m_hDpa, at, ptr);
77 }
78
79 INT Append(T* ptr)
80 {
81 return DPA_InsertPtr(m_hDpa, DA_LAST, ptr);
82 }
83
84 INT IndexOf(T* ptr) const
85 {
86 return DPA_GetPtrIndex(m_hDpa, ptr);
87 }
88
89 BOOL Remove(T* ptr)
90 {
91 INT i = IndexOf(ptr);
92 if (i < 0)
93 return FALSE;
94 return RemoveAt(i);
95 }
96
97 BOOL RemoveAt(INT i)
98 {
99 T* ptr = (T*) DPA_GetPtr(m_hDpa, i);
100 OnRemoveItem(ptr);
101 return DPA_DeletePtr(m_hDpa, i);
102 }
103
104 BOOL Clear()
105 {
106 DPA_EnumCallback(s_OnRemoveItem, this);
107 return DPA_DeleteAllPtrs(m_hDpa);
108 }
109
110 BOOL Sort()
111 {
112 return DPA_Sort(m_hDpa, s_OnCompareItems, (LPARAM)this);
113 }
114
115 INT Search(T* item, INT iStart, UINT uFlags)
116 {
117 return DPA_Search(m_hDpa, s_OnCompareItems, (LPARAM)this);
118 }
119 };
120
121 class CUiRect
122 : public RECT
123 {
124 public:
125 CUiRect()
126 {
127 left = right = top = bottom = 0;
128 }
129
130 CUiRect(INT l, INT t, INT r, INT b)
131 {
132 left = l;
133 right = r;
134 top = t;
135 bottom = b;
136 }
137 };
138
139 class CUiMargin
140 : public CUiRect
141 {
142 public:
143 CUiMargin()
144 {
145 }
146
147 CUiMargin(INT all)
148 : CUiRect(all, all, all, all)
149 {
150 }
151
152 CUiMargin(INT horz, INT vert)
153 : CUiRect(horz, vert, horz, vert)
154 {
155 }
156 };
157
158 class CUiMeasure
159 {
160 public:
161 enum MeasureType
162 {
163 Type_FitContent = 0,
164 Type_Fixed = 1,
165 Type_Percent = 2,
166 Type_FitParent = 3
167 };
168
169 private:
170 MeasureType m_Type;
171 INT m_Value;
172
173 public:
174 CUiMeasure()
175 {
176 m_Type = Type_FitContent;
177 m_Value = 0;
178 }
179
180 CUiMeasure(MeasureType type, INT value)
181 {
182 m_Type = type;
183 m_Value = value;
184 }
185
186 INT ComputeMeasure(INT parent, INT content)
187 {
188 switch (m_Type)
189 {
190 case Type_FitContent:
191 return content;
192 case Type_Fixed:
193 return m_Value;
194 case Type_Percent:
195 return max(content, parent * m_Value / 100);
196 case Type_FitParent:
197 return parent;
198 }
199
200 return 0;
201 }
202
203 public:
204 static CUiMeasure FitContent()
205 {
206 return CUiMeasure(Type_FitContent, 0);
207 }
208
209 static CUiMeasure FitParent()
210 {
211 return CUiMeasure(Type_FitParent, 0);
212 }
213
214 static CUiMeasure Fixed(INT pixels)
215 {
216 return CUiMeasure(Type_Fixed, pixels);
217 }
218
219 static CUiMeasure Percent(INT percent)
220 {
221 return CUiMeasure(Type_Percent, percent);
222 }
223 };
224
225 enum CUiAlignment
226 {
227 UiAlign_LeftTop,
228 UiAlign_Middle,
229 UiAlign_RightBtm,
230 UiAlign_Stretch
231 };
232
233 class CUiBox
234 {
235 public:
236 CUiMargin m_Margin;
237
238 CUiAlignment m_HorizontalAlignment;
239 CUiAlignment m_VerticalAlignment;
240
241 protected:
242 CUiBox()
243 {
244 m_HorizontalAlignment = UiAlign_LeftTop;
245 m_VerticalAlignment = UiAlign_LeftTop;
246 }
247
248 virtual VOID ComputeRect(RECT parentRect, RECT currentRect, RECT* newRect)
249 {
250 parentRect.left += m_Margin.left;
251 parentRect.right -= m_Margin.right;
252 parentRect.top += m_Margin.top;
253 parentRect.bottom -= m_Margin.bottom;
254
255 if (parentRect.right < parentRect.left)
256 parentRect.right = parentRect.left;
257
258 if (parentRect.bottom < parentRect.top)
259 parentRect.bottom = parentRect.top;
260
261 SIZE szParent = {parentRect.right - parentRect.left, parentRect.bottom - parentRect.top};
262 SIZE szCurrent = {currentRect.right - currentRect.left, currentRect.bottom - currentRect.top};
263
264 currentRect = parentRect;
265
266 switch (m_HorizontalAlignment)
267 {
268 case UiAlign_LeftTop:
269 currentRect.right = currentRect.left + szCurrent.cx;
270 break;
271 case UiAlign_Middle:
272 currentRect.left = parentRect.left + (szParent.cx - szCurrent.cx) / 2;
273 currentRect.right = currentRect.left + szCurrent.cx;
274 break;
275 case UiAlign_RightBtm:
276 currentRect.left = currentRect.right - szCurrent.cx;
277 break;
278 default:
279 break;
280 }
281
282 switch (m_VerticalAlignment)
283 {
284 case UiAlign_LeftTop:
285 currentRect.bottom = currentRect.top + szCurrent.cy;
286 break;
287 case UiAlign_Middle:
288 currentRect.top = parentRect.top + (szParent.cy - szCurrent.cy) / 2;
289 currentRect.bottom = currentRect.top + szCurrent.cy;
290 break;
291 case UiAlign_RightBtm:
292 currentRect.top = currentRect.bottom - szCurrent.cy;
293 break;
294 default:
295 break;
296 }
297
298 *newRect = currentRect;
299 }
300
301
302 public:
303 virtual VOID ComputeMinimalSize(SIZE* size)
304 {
305 // Override in subclass
306 size->cx = max(size->cx, 0);
307 size->cy = min(size->cy, 0);
308 };
309
310 virtual VOID ComputeContentBounds(RECT* rect)
311 {
312 // Override in subclass
313 };
314
315 virtual DWORD_PTR CountSizableChildren()
316 {
317 // Override in subclass
318 return 0;
319 };
320
321 virtual HDWP OnParentSize(RECT parentRect, HDWP hDwp)
322 {
323 // Override in subclass
324 return NULL;
325 };
326 };
327
328 class CUiPrimitive
329 {
330 protected:
331 CUiPrimitive * m_Parent;
332
333 public:
334 virtual ~CUiPrimitive() {}
335
336 virtual CUiBox * AsBox() { return NULL; }
337 };
338
339 class CUiCollection :
340 public CPointerArray < CUiPrimitive >
341 {
342 virtual BOOL OnRemoveItem(CUiPrimitive * ptr)
343 {
344 delete ptr;
345 return TRUE;
346 }
347 };
348
349 class CUiContainer
350 {
351 protected:
352 CUiCollection m_Children;
353
354 public:
355 CUiCollection& Children() { return m_Children; }
356 };
357
358 class CUiPanel :
359 public CUiPrimitive,
360 public CUiBox,
361 public CUiContainer
362 {
363 public:
364 CUiMeasure m_Width;
365 CUiMeasure m_Height;
366
367 CUiPanel()
368 {
369 m_Width = CUiMeasure::FitParent();
370 m_Height = CUiMeasure::FitParent();
371 }
372
373 virtual ~CUiPanel()
374 {
375 }
376
377 virtual CUiBox * AsBox() { return this; }
378
379 virtual VOID ComputeMinimalSize(SIZE* size)
380 {
381 for (INT i = 0; i < m_Children.GetCount(); i++)
382 {
383 CUiBox * box = m_Children.Get(i)->AsBox();
384 if (box)
385 {
386 box->ComputeMinimalSize(size);
387 }
388 }
389 };
390
391 virtual VOID ComputeContentBounds(RECT* rect)
392 {
393 for (INT i = 0; i < m_Children.GetCount(); i++)
394 {
395 CUiBox * box = m_Children.Get(i)->AsBox();
396 if (box)
397 {
398 box->ComputeContentBounds(rect);
399 }
400 }
401 };
402
403 virtual DWORD_PTR CountSizableChildren()
404 {
405 INT count = 0;
406 for (INT i = 0; i < m_Children.GetCount(); i++)
407 {
408 CUiBox * box = m_Children.Get(i)->AsBox();
409 if (box)
410 {
411 count += box->CountSizableChildren();
412 }
413 }
414 return count;
415 }
416
417 virtual HDWP OnParentSize(RECT parentRect, HDWP hDwp)
418 {
419 RECT rect = {0};
420
421 SIZE content = {0};
422 ComputeMinimalSize(&content);
423
424 INT preferredWidth = m_Width.ComputeMeasure(parentRect.right - parentRect.left, content.cx);
425 INT preferredHeight = m_Height.ComputeMeasure(parentRect.bottom - parentRect.top, content.cy);
426
427 rect.right = preferredWidth;
428 rect.bottom = preferredHeight;
429
430 ComputeRect(parentRect, rect, &rect);
431
432 for (INT i = 0; i < m_Children.GetCount(); i++)
433 {
434 CUiBox * box = m_Children.Get(i)->AsBox();
435 if (box)
436 {
437 hDwp = box->OnParentSize(rect, hDwp);
438 }
439 }
440
441 return hDwp;
442 }
443 };
444
445 template<class T = CWindow>
446 class CUiWindow :
447 public CUiPrimitive,
448 public CUiBox,
449 public T
450 {
451 public:
452 virtual CUiBox * AsBox() { return this; }
453
454 HWND GetWindow() { return T::m_hWnd; }
455
456 virtual VOID ComputeMinimalSize(SIZE* size)
457 {
458 // TODO: Maybe use WM_GETMINMAXINFO?
459 return CUiBox::ComputeMinimalSize(size);
460 };
461
462 virtual VOID ComputeContentBounds(RECT* rect)
463 {
464 RECT r;
465 ::GetWindowRect(T::m_hWnd, &r);
466 rect->left = min(rect->left, r.left);
467 rect->top = min(rect->top, r.top);
468 rect->right = max(rect->right, r.right);
469 rect->bottom = max(rect->bottom, r.bottom);
470 };
471
472 virtual DWORD_PTR CountSizableChildren()
473 {
474 return 1;
475 };
476
477 virtual HDWP OnParentSize(RECT parentRect, HDWP hDwp)
478 {
479 RECT rect;
480
481 ::GetWindowRect(T::m_hWnd, &rect);
482
483 ComputeRect(parentRect, rect, &rect);
484
485 if (hDwp)
486 {
487 return ::DeferWindowPos(hDwp, T::m_hWnd, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOZORDER);
488 }
489 else
490 {
491 T::SetWindowPos(NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOZORDER | SWP_DEFERERASE);
492 return NULL;
493 }
494 };
495
496 virtual ~CUiWindow()
497 {
498 T::DestroyWindow();
499 }
500
501 VOID GetWindowTextW(ATL::CStringW& szText)
502 {
503 INT length = CWindow::GetWindowTextLengthW() + 1;
504 CWindow::GetWindowTextW(szText.GetBuffer(length), length);
505 szText.ReleaseBuffer();
506 }
507 };
508
509 class CUiSplitPanel :
510 public CUiPrimitive,
511 public CUiBox,
512 public CWindowImpl<CUiSplitPanel>
513 {
514 static const INT THICKNESS = 4;
515
516 protected:
517
518 HCURSOR m_hCursor;
519
520 CUiPanel m_First;
521 CUiPanel m_Second;
522
523 RECT m_LastRect;
524
525 BOOL m_HasOldRect;
526
527 public:
528 INT m_Pos;
529 BOOL m_Horizontal;
530 BOOL m_DynamicFirst;
531 INT m_MinFirst;
532 INT m_MinSecond;
533
534 CUiMeasure m_Width;
535 CUiMeasure m_Height;
536
537 CUiSplitPanel()
538 {
539 m_Width = CUiMeasure::FitParent();
540 m_Height = CUiMeasure::FitParent();
541 m_Pos = 100;
542 m_MinFirst = 100;
543 m_MinSecond = 100;
544 m_DynamicFirst = FALSE;
545 m_HasOldRect = FALSE;
546 }
547
548 virtual ~CUiSplitPanel()
549 {
550 }
551
552 virtual CUiBox * AsBox() { return this; }
553
554 CUiCollection& First() { return m_First.Children(); }
555 CUiCollection& Second() { return m_Second.Children(); }
556
557 virtual VOID ComputeMinimalSize(SIZE* size)
558 {
559 if (m_Horizontal)
560 size->cx = max(size->cx, THICKNESS);
561 else
562 size->cy = max(size->cy, THICKNESS);
563 m_First.ComputeMinimalSize(size);
564 m_Second.ComputeMinimalSize(size);
565 };
566
567 virtual VOID ComputeContentBounds(RECT* rect)
568 {
569 RECT r;
570
571 m_First.ComputeContentBounds(rect);
572 m_Second.ComputeContentBounds(rect);
573
574 ::GetWindowRect(m_hWnd, &r);
575
576 rect->left = min(rect->left, r.left);
577 rect->top = min(rect->top, r.top);
578 rect->right = max(rect->right, r.right);
579 rect->bottom = max(rect->bottom, r.bottom);
580 };
581
582 virtual DWORD_PTR CountSizableChildren()
583 {
584 INT count = 1;
585 count += m_First.CountSizableChildren();
586 count += m_Second.CountSizableChildren();
587 return count;
588 };
589
590 virtual HDWP OnParentSize(RECT parentRect, HDWP hDwp)
591 {
592 RECT rect = {0};
593
594 SIZE content = {0};
595 ComputeMinimalSize(&content);
596
597 INT preferredWidth = m_Width.ComputeMeasure(parentRect.right - parentRect.left, content.cx);
598 INT preferredHeight = m_Width.ComputeMeasure(parentRect.bottom - parentRect.top, content.cy);
599
600 rect.right = preferredWidth;
601 rect.bottom = preferredHeight;
602
603 ComputeRect(parentRect, rect, &rect);
604
605 SIZE growth = {0};
606 if (m_HasOldRect)
607 {
608 RECT oldRect = m_LastRect;
609
610 growth.cx = (parentRect.right - parentRect.left) - (oldRect.right - oldRect.left);
611 growth.cy = (parentRect.bottom - parentRect.top) - (oldRect.bottom - oldRect.top);
612 }
613
614 RECT splitter = rect;
615 RECT first = rect;
616 RECT second = rect;
617
618 if (m_Horizontal)
619 {
620 rect.top += m_MinFirst;
621 rect.bottom -= THICKNESS + m_MinSecond;
622 if (m_DynamicFirst)
623 {
624 if (growth.cy > 0)
625 {
626 m_Pos += min(growth.cy, rect.bottom - (m_Pos + THICKNESS));
627 }
628 else if (growth.cy < 0)
629 {
630 m_Pos += max(growth.cy, rect.top - m_Pos);
631 }
632 }
633
634 if (m_Pos > rect.bottom)
635 m_Pos = rect.bottom;
636
637 if (m_Pos < rect.top)
638 m_Pos = rect.top;
639
640 splitter.top = m_Pos;
641 splitter.bottom = m_Pos + THICKNESS;
642 first.bottom = splitter.top;
643 second.top = splitter.bottom;
644 }
645 else
646 {
647 rect.left += m_MinFirst;
648 rect.right -= THICKNESS + m_MinSecond;
649 if (m_DynamicFirst)
650 {
651 if (growth.cx > 0)
652 {
653 m_Pos += min(growth.cx, rect.right - (m_Pos + THICKNESS));
654 }
655 else if (growth.cx < 0)
656 {
657 m_Pos += max(growth.cy, rect.left - m_Pos);
658 }
659 }
660
661 if (m_Pos > rect.right)
662 m_Pos = rect.right;
663
664 if (m_Pos < rect.left)
665 m_Pos = rect.left;
666
667 splitter.left = m_Pos;
668 splitter.right = m_Pos + THICKNESS;
669 first.right = splitter.left;
670 second.left = splitter.right;
671 }
672
673 m_LastRect = parentRect;
674 m_HasOldRect = TRUE;
675
676 hDwp = m_First.OnParentSize(first, hDwp);
677 hDwp = m_Second.OnParentSize(second, hDwp);
678
679 if (hDwp)
680 {
681 return DeferWindowPos(hDwp, NULL,
682 splitter.left, splitter.top,
683 splitter.right - splitter.left,
684 splitter.bottom - splitter.top,
685 SWP_NOACTIVATE | SWP_NOZORDER);
686 }
687 else
688 {
689 SetWindowPos(NULL,
690 splitter.left, splitter.top,
691 splitter.right - splitter.left,
692 splitter.bottom - splitter.top,
693 SWP_NOACTIVATE | SWP_NOZORDER);
694 return NULL;
695 }
696 };
697
698 private:
699 BOOL ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT& theResult, DWORD dwMapId)
700 {
701 theResult = 0;
702 switch (Msg)
703 {
704 case WM_SETCURSOR:
705 SetCursor(m_hCursor);
706 theResult = TRUE;
707 break;
708
709 case WM_LBUTTONDOWN:
710 SetCapture();
711 break;
712
713 case WM_LBUTTONUP:
714 case WM_RBUTTONDOWN:
715 if (GetCapture() == m_hWnd)
716 {
717 ReleaseCapture();
718 }
719 break;
720
721 case WM_MOUSEMOVE:
722 if (GetCapture() == m_hWnd)
723 {
724 POINT Point;
725 GetCursorPos(&Point);
726 ::ScreenToClient(GetParent(), &Point);
727 if (m_Horizontal)
728 SetPos(Point.y);
729 else
730 SetPos(Point.x);
731 }
732 break;
733
734 default:
735 return FALSE;
736 }
737
738 return TRUE;
739 }
740
741 public:
742 INT GetPos()
743 {
744 return m_Pos;
745 }
746
747 VOID SetPos(INT NewPos)
748 {
749 RECT rcParent;
750
751 rcParent = m_LastRect;
752
753 if (m_Horizontal)
754 {
755 rcParent.bottom -= THICKNESS;
756
757 m_Pos = NewPos;
758
759 if (m_Pos < rcParent.top)
760 m_Pos = rcParent.top;
761
762 if (m_Pos > rcParent.bottom)
763 m_Pos = rcParent.bottom;
764 }
765 else
766 {
767 rcParent.right -= THICKNESS;
768
769 m_Pos = NewPos;
770
771 if (m_Pos < rcParent.left)
772 m_Pos = rcParent.left;
773
774 if (m_Pos > rcParent.right)
775 m_Pos = rcParent.right;
776 }
777
778 INT count = CountSizableChildren();
779
780 HDWP hdwp = NULL;
781 hdwp = BeginDeferWindowPos(count);
782 if (hdwp) hdwp = OnParentSize(m_LastRect, hdwp);
783 if (hdwp) EndDeferWindowPos(hdwp);
784 }
785
786 public:
787 DECLARE_WND_CLASS_EX(_T("SplitterWindowClass"), CS_HREDRAW | CS_VREDRAW, COLOR_BTNFACE)
788
789 /* Create splitter bar */
790 HWND Create(HWND hwndParent)
791 {
792 if (m_Horizontal)
793 m_hCursor = LoadCursor(0, IDC_SIZENS);
794 else
795 m_hCursor = LoadCursor(0, IDC_SIZEWE);
796
797 DWORD style = WS_CHILD | WS_VISIBLE;
798 DWORD exStyle = WS_EX_TRANSPARENT;
799
800 RECT size = {205, 180, 465, THICKNESS};
801 size.right += size.left;
802 size.bottom += size.top;
803
804 return CWindowImpl::Create(hwndParent, size, NULL, style, exStyle);
805 }
806 };