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