forked from gildor2/UEViewer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
BaseDialog.h
1252 lines (1000 loc) · 35.3 KB
/
BaseDialog.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Simple UI library.
// Copyright (C) 2018 Konstantin Nosov
// Licensed under the BSD license. See LICENSE.txt file in the project root for full license information.
#ifndef __BASE_DIALOG_H__
#define __BASE_DIALOG_H__
#include "Core.h"
#if HAS_UI // defined in Build.h, included from Core.h
#include "Win32Types.h"
#include "UnCore.h" // for TArray and FString
#include "callback.h"
// forwards
class UIMenu;
class UIBaseDialog;
enum ETextAlign
{
TA_Left,
TA_Right,
TA_Center,
};
struct UIRect
{
int X;
int Y;
int Width;
int Height;
UIRect(int inX, int inY, int inWidth, int inHeight)
: X(inX)
, Y(inY)
, Width(inWidth)
, Height(inHeight)
{}
UIRect(const UIRect& other)
{
memcpy(this, &other, sizeof(UIRect));
}
inline void Set(int InX, int InY, int InWidth, int InHeight)
{
X = InX;
Y = InY;
Width = InWidth;
Height = InHeight;
}
};
/*-----------------------------------------------------------------------------
UIElement
-----------------------------------------------------------------------------*/
class UIElement
{
friend class UIGroup;
friend class UIPageControl;
public:
UIElement();
virtual ~UIElement();
virtual const char* ClassName() const;
// Disable/enable window updates. Guaranteed for only one locked window at a time (WinAPI restriction)
void LockUpdate();
virtual void UnlockUpdate();
UIElement& Enable(bool enable);
FORCEINLINE bool IsEnabled() const { return Enabled; }
UIElement& Show(bool visible);
FORCEINLINE bool IsVisible() const { return Visible; }
UIElement& SetParent(UIGroup* group);
FORCEINLINE HWND GetWnd() const { return Wnd; }
UIBaseDialog* GetDialog();
virtual bool IsA(const char* type) const
{
return !strcmp("UIElement", type);
}
// Layout functions
UIElement& SetRect(int x, int y, int width, int height);
FORCEINLINE int GetWidth() const { return Layout.Width; }
FORCEINLINE int GetHeight() const { return Layout.Height; }
UIElement& SetMenu(UIMenu* menu);
void Repaint();
//!! add SetFontHeight(int)
/* Also support bold-italic. Update 'Height' when AutoSize is set.
Requires updates in MeasureTextSize()
Snippet:
HFONT hFont = (HFONT)m_ProgList.SendMessage(WM_GETFONT);
LOGFONT lf = {0};
GetObject(hFont, sizeof(LOGFONT), &lf);
lf.lfWeight = FW_BOLD;
m_boldFont = CreateFontIndirect(&lf);
*/
static FORCEINLINE int EncodeWidth(float w)
{
w = bound(w, 0, 1);
int iw = (int)(w * 255.0f); // float -> int
return 0xFFFF0000 | iw;
}
static FORCEINLINE float DecodeWidth(int w)
{
#if MAX_DEBUG
assert((w & 0xFFFFFF00) == 0xFFFF0000 || w == -1);
#endif
return (w & 0xFF) / 255.0f; // w=-1 -> 1.0f
}
// Measure text size. There should be a single line of text.
void MeasureTextSize(const char* text, int* width, int* height = NULL, HWND wnd = 0);
// Measure text size. 'width' contains width limit for multiline text.
void MeasureTextVSize(const char* text, int* width, int* height = NULL, HWND wnd = 0);
friend UIElement& operator+(UIElement& elem, UIElement& next);
protected:
// Layout settings for this control
UIRect Layout;
short MinWidth;
short MinHeight;
short TopMargin;
short BottomMargin;
short LeftMargin;
short RightMargin;
// Computed control's position depending on Layout
UIRect Rect;
bool IsGroup:1;
bool IsRadioButton:1;
bool Enabled:1;
bool Visible:1;
bool IsUpdateLocked;
UIGroup* Parent;
UIElement* NextChild;
UIMenu* Menu;
HWND Wnd;
int Id;
virtual int ComputeWidth() const;
virtual int ComputeHeight() const;
HWND Window(const char* className, const char* text, DWORD style, DWORD exstyle, UIBaseDialog* dialog,
int id = -1, int x = -1, int y = -1, int w = -1, int h = -1);
// Unicode version of Window()
HWND Window(const wchar_t* className, const wchar_t* text, DWORD style, DWORD exstyle, UIBaseDialog* dialog,
int id = -1, int x = -1, int y = -1, int w = -1, int h = -1);
virtual void Create(UIBaseDialog* dialog) = 0;
virtual void UpdateSize(UIBaseDialog* dialog)
{}
virtual void UpdateLayout();
// Process WM_COMMAND message. 'id' is useless in most cases, useful for
// groups only.
virtual bool HandleCommand(int id, int cmd, LPARAM lParam)
{
return false;
}
virtual void DialogClosed(bool cancel)
{}
virtual void UpdateEnabled();
virtual void UpdateVisible();
};
// Declare some functions which could be useful for UE4-like declarative syntax:
// Expose(var) save pointer to control in variable
// SetParent(parent) attach control to parent
// Some functions exists in UIElement but overrided here to be able to chain them
// without falling back to UIElement class: UIElement's functions can't return
// 'this' of derived type, so we're redeclaring functions here.
// To add controls to a UIGroup object use the following syntax:
// NewControl(UIGroup)
// .Expose(GroupVar)
// .SomeGroupFunc()
// [
// NewControl(ControlType1, args1)
// .SomeControl1Func1()
// .SomeControl1Func2()
// + NewControl(ControlType2, args2)
// .Expose(Control2Var)
// ...
// ]
// This code is identical to:
// GroupVar = new UIGroup(); // make a group
// GroupVar->SomeGroupFunc();
// tmpControl1 = new ControlType1(args1); // make a control 1
// tmpControl1->SomeControl1Func1();
// tmpControl1->SomeControl1Func2();
// ControlVar2 = new ControlType2(args2); // make a control 2
// GroupVar->Add(tmpControl1); // add controls to group
// GroupVar->Add(ControlVar2);
// Note: DECLARE_UI_CLASS allows access to private members from GetDebugLabel(). Probably
// should change the way how label obtained, like - adding virtual function for debug build etc.
#define DECLARE_UI_CLASS(Class, Base) \
typedef Class ThisClass; \
typedef Base Super; \
friend const char* GetDebugLabel(const UIElement* ctl); \
public: \
virtual const char* ClassName() const override { return #Class; } \
virtual bool IsA(const char* type) const override \
{ \
return !strcmp(#Class, type) || Super::IsA(type); \
} \
FORCEINLINE ThisClass& SetRect(int x, int y, int width, int height) \
{ return (ThisClass&) Super::SetRect(x, y, width, height); } \
FORCEINLINE ThisClass& SetX(int x) { Layout.X = x; return *this; } \
FORCEINLINE ThisClass& SetY(int y) { Layout.Y = y; return *this; } \
FORCEINLINE ThisClass& SetWidth(int width) { Layout.Width = width; return *this; } \
FORCEINLINE ThisClass& SetHeight(int height) { Layout.Height = height; return *this; } \
FORCEINLINE ThisClass& Enable(bool enable) { return (ThisClass&) Super::Enable(enable); } \
FORCEINLINE ThisClass& Show(bool visible) { return (ThisClass&) Super::Show(visible); } \
template<class T> \
FORCEINLINE ThisClass& Expose(T*& var) { var = this; return *this; } \
FORCEINLINE ThisClass& SetParent(UIGroup* group) { return (ThisClass&) Super::SetParent(group); } \
FORCEINLINE ThisClass& SetParent(UIGroup& group) { return (ThisClass&) Super::SetParent(&group); } \
FORCEINLINE ThisClass& SetMenu(UIMenu* menu) { return (ThisClass&) Super::SetMenu(menu); } \
private:
// Use this macro to declare a callback type, its variable and SetCallback function.
// Notes:
// - It will automatically add 'ThisClass' pointer as a first parameter of callback function
// - SetCallback function name depends on VarName
#define DECLARE_CALLBACK(VarName, ...) \
public: \
typedef ::Callback<void(ThisClass*, __VA_ARGS__)> VarName##_t; \
template<typename CB> \
FORCEINLINE ThisClass& Set##VarName(CB&& cb) \
{ \
this->VarName = Detail::Forward<CB>(cb); return *this; \
} \
protected: \
VarName##_t VarName; \
private:
// Control creation helper.
// Use this to receive 'Type&' value instead of 'Type*' available with 'new Type' call
#define NewControl(type, ...) (* new type(__VA_ARGS__))
/*-----------------------------------------------------------------------------
Controls
-----------------------------------------------------------------------------*/
class UISpacer : public UIElement
{
DECLARE_UI_CLASS(UISpacer, UIElement);
public:
UISpacer(int size = 0);
protected:
virtual void Create(UIBaseDialog* dialog) override
{}
};
//?? could extend to show box
class UIHorizontalLine : public UIElement
{
DECLARE_UI_CLASS(UIHorizontalLine, UIElement);
public:
UIHorizontalLine();
protected:
virtual void Create(UIBaseDialog* dialog) override;
};
class UIVerticalLine : public UIElement
{
DECLARE_UI_CLASS(UIVerticalLine, UIElement);
public:
UIVerticalLine();
protected:
virtual void Create(UIBaseDialog* dialog) override;
};
class UIBitmap : public UIElement
{
DECLARE_UI_CLASS(UIBitmap, UIElement);
public:
UIBitmap();
// System bitmaps
enum
{
BI_Warning = -1,
BI_Question = -2,
BI_Error = -3,
BI_Information = -4,
};
//!! win32 version; check Linux
// If you want to specify custom resource size, call SetWidth/SetHeight for UIBitmap.
// Otherwise, image size will be placed into Width and Height fields.
// WARNING: SetResource... functions will ignore SetWidth/SetHeight AFTER this call.
UIBitmap& SetResourceIcon(int resId);
UIBitmap& SetResourceBitmap(int resId);
protected:
HANDLE hImage;
bool IsIcon;
virtual void Create(UIBaseDialog* dialog) override;
void LoadResourceImage(int id, UINT type, UINT fuLoad);
};
class UILabel : public UIElement
{
DECLARE_UI_CLASS(UILabel, UIElement);
public:
UILabel(const char* text, ETextAlign align = TA_Left);
UILabel& SetAutoSize() { AutoSize = true; return *this; }
void SetText(const char* text);
protected:
FString Label;
ETextAlign Align;
bool AutoSize;
virtual void UpdateSize(UIBaseDialog* dialog) override;
virtual void Create(UIBaseDialog* dialog) override;
};
class UIHyperLink : public UILabel
{
DECLARE_UI_CLASS(UIHyperLink, UILabel);
public:
UIHyperLink(const char* text, const char* link /*, ETextAlign align = TA_Left*/); // align is not working
UIHyperLink& SetAutoSize() { return (ThisClass&)Super::SetAutoSize(); }
protected:
FString Link;
virtual void UpdateSize(UIBaseDialog* dialog) override;
virtual void Create(UIBaseDialog* dialog) override;
virtual bool HandleCommand(int id, int cmd, LPARAM lParam) override;
};
class UIProgressBar : public UIElement
{
DECLARE_UI_CLASS(UIProgressBar, UIElement);
public:
UIProgressBar();
void SetValue(float value);
protected:
float Value;
virtual void Create(UIBaseDialog* dialog) override;
};
class UIButton : public UIElement
{
DECLARE_UI_CLASS(UIButton, UIElement);
DECLARE_CALLBACK(Callback);
public:
UIButton(const char* text);
UIButton& SetOK();
UIButton& SetCancel();
protected:
FString Label;
virtual void UpdateSize(UIBaseDialog* dialog) override;
virtual void Create(UIBaseDialog* dialog) override;
virtual bool HandleCommand(int id, int cmd, LPARAM lParam) override;
};
class UIMenuButton : public UIElement
{
DECLARE_UI_CLASS(UIMenuButton, UIElement);
DECLARE_CALLBACK(Callback);
public:
UIMenuButton(const char* text);
protected:
FString Label;
virtual void UpdateSize(UIBaseDialog* dialog) override;
virtual void Create(UIBaseDialog* dialog) override;
virtual bool HandleCommand(int id, int cmd, LPARAM lParam) override;
};
class UICheckbox : public UIElement
{
DECLARE_UI_CLASS(UICheckbox, UIElement);
DECLARE_CALLBACK(Callback, bool);
public:
UICheckbox(const char* text, bool value, bool autoSize = true);
UICheckbox(const char* text, bool* value, bool autoSize = true);
protected:
FString Label;
bool bValue; // local bool value
bool* pValue; // pointer to editable value
bool AutoSize;
HWND DlgWnd;
virtual void UpdateSize(UIBaseDialog* dialog) override;
virtual void Create(UIBaseDialog* dialog) override;
virtual bool HandleCommand(int id, int cmd, LPARAM lParam) override;
};
class UIRadioButton : public UIElement
{
friend class UIGroup;
DECLARE_UI_CLASS(UIRadioButton, UIElement);
DECLARE_CALLBACK(Callback, bool);
public:
// UIRadioButton with automatic value
// Value will be assigned in UIGroup::InitializeRadioGroup()
UIRadioButton(const char* text, bool autoSize = true);
// UIRadioButton with explicit value
UIRadioButton(const char* text, int value, bool autoSize = true);
protected:
FString Label;
int Value;
bool Checked;
bool AutoSize;
void ButtonSelected(bool value);
void SelectButton();
virtual void UpdateSize(UIBaseDialog* dialog) override;
virtual void Create(UIBaseDialog* dialog) override;
virtual bool HandleCommand(int id, int cmd, LPARAM lParam) override;
};
class UITextEdit : public UIElement
{
DECLARE_UI_CLASS(UITextEdit, UIElement);
DECLARE_CALLBACK(Callback, const char*);
public:
UITextEdit(const char* text);
UITextEdit(FString* text);
FORCEINLINE UITextEdit& SetMultiline(bool multiline = true)
{
IsMultiline = multiline;
return *this;
}
FORCEINLINE UITextEdit& SetReadOnly(bool readOnly = true)
{
IsReadOnly = readOnly;
return *this;
}
FORCEINLINE UITextEdit& SetWantFocus(bool focus = true)
{
IsWantFocus = focus;
return *this;
}
// This function may be used for creating text output window
void AppendText(const char* text);
// Set text in editor field. Passing NULL as text will just refresh value display.
void SetText(const char* text = NULL);
const char* GetText();
protected:
FString sValue;
FString* pValue;
bool IsMultiline;
bool IsReadOnly;
bool IsWantFocus;
bool TextDirty;
virtual void Create(UIBaseDialog* dialog) override;
virtual bool HandleCommand(int id, int cmd, LPARAM lParam) override;
// request edited text from UI
void UpdateText();
};
class UICombobox : public UIElement
{
DECLARE_UI_CLASS(UICombobox, UIElement);
DECLARE_CALLBACK(Callback, int, const char*);
struct ComboboxItem
{
FString Text;
int Value;
ComboboxItem(const char* text, int value)
: Text(text)
, Value(value)
{}
};
public:
UICombobox();
UICombobox(int* value)
: UICombobox()
{
pValue = value;
}
template<typename T, typename = typename Detail::TEnableIf<__is_enum(T)>::Type>
UICombobox(T* value)
: UICombobox()
{
static_assert(sizeof(T) == sizeof(int), "T should be castable to integer");
pValue = (int*)value;
}
UICombobox& AddItem(const char* item, int value = -1);
template<typename T, typename = typename Detail::TEnableIf<__is_enum(T)>::Type>
UICombobox& AddItem(const char* item, T value)
{
AddItem(item, int(value));
return *this;
}
UICombobox& AddItems(const char** items);
void RemoveAllItems();
UICombobox& SelectItem(int index);
UICombobox& SelectItem(const char* item);
FORCEINLINE const char* GetItem(int index) const
{
return *Items[index].Text;
}
FORCEINLINE int GetSelectionIndex() const
{
return Selection;
}
FORCEINLINE const char* GetSelectionText() const
{
return (Selection >= 0) ? *Items[Selection].Text : NULL;
}
protected:
TArray<ComboboxItem> Items;
int Selection;
int Value;
int* pValue;
virtual void Create(UIBaseDialog* dialog) override;
virtual bool HandleCommand(int id, int cmd, LPARAM lParam) override;
};
// This class internally is very similar to UICombobox
//!! add "int* pValue" like for other controls
class UIListbox : public UIElement
{
DECLARE_UI_CLASS(UIListbox, UIElement);
DECLARE_CALLBACK(Callback, int, const char*);
DECLARE_CALLBACK(DblClickCallback, int, const char*);
public:
UIListbox();
UIListbox& ReserveItems(int count);
UIListbox& AddItem(const char* item);
UIListbox& AddItems(const char** items);
void RemoveAllItems();
UIListbox& SelectItem(int index);
UIListbox& SelectItem(const char* item);
FORCEINLINE const char* GetItem(int index) const
{
return *Items[index];
}
FORCEINLINE int GetSelectionIndex() const
{
return Value;
}
FORCEINLINE const char* GetSelectionText() const
{
return (Value >= 0) ? *Items[Value] : NULL;
}
protected:
TArray<FString> Items;
int Value;
virtual void Create(UIBaseDialog* dialog) override;
virtual bool HandleCommand(int id, int cmd, LPARAM lParam) override;
};
//?? Probably rename to ListView? But we're supporting only "report" style, so it generally looks
//?? like a Listbox with columns.
// Ways of using UIMulticolumnListbox:
// 1. Normal mode: create, add/remove items, display.
// Items are stored inside UIMulticolumnListbox. Win32 object contains placeholders - empty items.
// 2. Virtual mode: create, SetVirtualMode, add/remove items, display.
// Items are stored inside UIMulticolumnListbox. Win32 object contains just number of items. Application
// works with UIMulticolumnListbox in this mode in exactly the same was as in "normal" mode, however
// work with control performed much faster.
// 3. "True" virtual model with callbacks: create, SetVirtualMode, set callbacks. Set number of items, display.
// Items are stored on the side which created this control. UIMulticolumnListbox and Win32 objects
// both holds only items count. This is the fastest mode, with lowest possible memory requirement.
class UIMulticolumnListbox : public UIElement
{
DECLARE_UI_CLASS(UIMulticolumnListbox, UIElement);
DECLARE_CALLBACK(Callback, int); // when single item selected (not for multiselect)
DECLARE_CALLBACK(SelChangedCallback); // when selection changed
DECLARE_CALLBACK(DblClickCallback, int); // when double-clicked an item
DECLARE_CALLBACK(OnGetItemCount, int&); // true virtual mode (#3): (int& OutItemCount)
DECLARE_CALLBACK(OnGetItemText, const char*&, int, int); // true virtual mode (#3): (char*& OutText, int ItemIndex, int SubItemIndex)
DECLARE_CALLBACK(OnColumnClick, int); // column clicked
public:
static const int MAX_COLUMNS = 16;
UIMulticolumnListbox(int numColumns);
UIMulticolumnListbox& AddColumn(const char* title, int width = -1, ETextAlign align = TA_Left);
UIMulticolumnListbox& AllowMultiselect() { Multiselect = true; return *this; }
UIMulticolumnListbox& SetVirtualMode() { IsVirtualMode = true; return *this; }
UIMulticolumnListbox& ShowSortArrow(int columnIndex, bool reverseSort);
UIMulticolumnListbox& ReserveItems(int count); // not suitable for "true" virtual mode
int AddItem(const char* item); // not suitable for "true" virtual mode
void AddSubItem(int itemIndex, int column, const char* text); // not suitable for "true" virtual mode
void RemoveItem(int itemIndex); // not suitable for "true" virtual mode
void RemoveAllItems();
// select an item; if index==-1, unselect all items; if add==true - extend current selection
// with this item (only when multiselect is enabled)
UIMulticolumnListbox& SelectItem(int index, bool add = false);
UIMulticolumnListbox& SelectItem(const char* item, bool add = false); // not suitable for "true" virtual mode
// unselect an item
UIMulticolumnListbox& UnselectItem(int index);
UIMulticolumnListbox& UnselectItem(const char* item); // not suitable for "true" virtual mode
UIMulticolumnListbox& UnselectAllItems();
int GetItemCount() const;
FORCEINLINE const char* GetItem(int itemIndex) const { return GetSubItem(itemIndex, 0); }
const char* GetSubItem(int itemIndex, int column) const;
FORCEINLINE bool IsTrueVirtualMode() const { return (OnGetItemCount != NULL) && (OnGetItemText != NULL); }
int GetSelectionIndex(int i = 0) const; // returns -1 when no items selected
int GetSelectionCount() const { return SelectedItems.Num(); } // returns 0 when no items selected
// UIElement functions
virtual void UnlockUpdate() override;
protected:
bool IsVirtualMode;
int NumColumns;
int ColumnSizes[MAX_COLUMNS];
ETextAlign ColumnAlign[MAX_COLUMNS];
bool Multiselect;
int SortColumn;
bool SortMode;
TArray<FString> Items; // first NumColumns items - column headers, next NumColumns - 1st line, 2nd line, ...
TStaticArray<int, 32> SelectedItems;
void SetItemSelection(int index, bool select);
void UpdateListViewHeaderSort();
virtual void Create(UIBaseDialog* dialog) override;
virtual bool HandleCommand(int id, int cmd, LPARAM lParam) override;
};
struct TreeViewItem;
class UITreeView : public UIElement
{
DECLARE_UI_CLASS(UITreeView, UIElement);
DECLARE_CALLBACK(Callback, const char*);
public:
UITreeView();
virtual ~UITreeView() override;
//!! TODO:
//!! - HideRootItem() -- may be when label is empty?
FORCEINLINE UITreeView& SetRootLabel(const char* root)
{
RootLabel = root;
return *this;
}
UITreeView& AddItem(const char* item);
void RemoveAllItems();
UITreeView& SelectItem(const char* item);
const char* GetSelectedItem();
UITreeView& UseFolderIcons() { bUseFolderIcons = true; return *this; }
UITreeView& UseCheckboxes() { bUseCheckboxes = true; return *this; }
UITreeView& SetItemHeight(int value) { ItemHeight = value; return *this; }
// Checkbox management
void SetChecked(const char* item, bool checked = true);
bool GetChecked(const char* item);
void Expand(const char* item);
void CollapseAll();
void ExpandCheckedNodes();
protected:
TArray<TreeViewItem*> Items;
FString RootLabel;
TreeViewItem* SelectedItem;
int ItemHeight;
bool bUseFolderIcons;
bool bUseCheckboxes;
TreeViewItem** HashTable;
static int GetHash(const char* text);
FORCEINLINE TreeViewItem* GetRoot() { return Items[0]; }
virtual void Create(UIBaseDialog* dialog) override;
virtual bool HandleCommand(int id, int cmd, LPARAM lParam) override;
void CreateItem(TreeViewItem& item);
TreeViewItem* FindItem(const char* item);
TreeViewItem* FindItem(void* hItem);
void UpdateCheckedStates();
virtual void DialogClosed(bool cancel) override;
};
/*-----------------------------------------------------------------------------
UIMenu
-----------------------------------------------------------------------------*/
class UIMenuItem
{
typedef UIMenuItem ThisClass;
DECLARE_CALLBACK(Callback); // this callback is executed for any clicked menu item
DECLARE_CALLBACK(CheckboxCallback, bool); // this callback is executed for checkboxes, in addition to main 'Callback'
DECLARE_CALLBACK(RadioCallback, int); // this callback is executed for RadioGroup when one of its children clicked
public:
enum EType
{
MI_Text,
MI_HyperLink,
MI_Checkbox,
MI_RadioButton,
MI_RadioGroup, // container for radio buttons
MI_Separator,
MI_Submenu,
};
// Normal menu item
UIMenuItem(const char* text)
{
Init(MI_Text, text);
}
// Hyperlink
UIMenuItem(const char* text, const char* link);
// Checkbox
UIMenuItem(const char* text, bool checked);
UIMenuItem(const char* text, bool* checked);
UIMenuItem(const char* text, int* value, int mask);
// RadioGroup
UIMenuItem(int value);
UIMenuItem(int* value);
// RadioButton
UIMenuItem(const char* text, int value);
// other types
UIMenuItem(EType type, const char* text = NULL)
{
Init(type, text);
}
~UIMenuItem();
// Making a list of menu items
friend UIMenuItem& operator+(UIMenuItem& item, UIMenuItem& next);
UIMenuItem& Expose(UIMenuItem*& var) { var = this; return *this; }
UIMenuItem& Enable(bool enable);
UIMenuItem& SetName(const char* newName);
const char* GetText() const { return *Label; }
// Replace sumbenu content. 'other' will be destroyed after this function call.
void ReplaceWith(UIMenuItem* other);
// Update checkboxes and radio groups according to attached variables
void Update();
// Submenu methods
void Add(UIMenuItem* item);
FORCEINLINE void Add(UIMenuItem& item)
{
Add(&item);
}
// Function for adding children in declarative syntax
FORCEINLINE UIMenuItem& operator[](UIMenuItem& item)
{
Add(item); return *this;
}
int GetItemIndex() const;
protected:
FStaticString<32> Label;
const char* Link; // web page link
int Id;
HMENU hMenu; // valid for MI_Submenu
EType Type;
bool Enabled;
// Hierarchy
UIMenuItem* Parent;
UIMenuItem* FirstChild; // child submenu for MI_Submenu and for MI_RadioGroup
UIMenuItem* NextChild; // next item belongs to the same parent
// Checkbox and radio group
bool bValue; // local bool value
int iValue; // local int value; used as radio constant for MI_RadioButton
void* pValue; // pointer to editable value (bool for MI_Checkbox and int for MI_RadioGroup)
void Init(EType type, const char* label);
void DestroyChildren();
UIMenu* GetOwner();
void FillMenuItems(HMENU parentMenu, int& nextId, int& position);
HMENU GetMenuHandle();
bool HandleCommand(int id);
bool GetCheckboxValue() const;
void SetCheckboxValue(bool newValue);
int GetMaxItemIdRecursive();
};
class UIMenu : public UIMenuItem // note: we're not using virtual functions in menu classes now
{
typedef UIMenu ThisClass;
DECLARE_CALLBACK(BeforePopup, UIElement*); // this callback is executed when menu is about to pop up
public:
UIMenu();
~UIMenu();
HMENU GetHandle(bool popup, bool forceCreate = false);
FORCEINLINE void Attach()
{
ReferenceCount++;
}
void AttachTo(HWND Wnd, bool updateRefCount = true);
void Detach();
void Redraw();
// make HandleCommand public for UIMenu
FORCEINLINE bool HandleCommand(int id)
{
return UIMenuItem::HandleCommand(id);
}
FORCEINLINE bool IsMainMenu() const
{
return MenuOwner != NULL;
}
int GetNextItemId();
void Popup(UIElement* Owner, int x, int y);
protected:
int ReferenceCount;
HWND MenuOwner;
HMENU MenuObject; // don't use UIMenuItem's hMenu here, see UIMenu::Create() for details
void Create(bool popup);
};
FORCEINLINE UIMenuItem& NewMenuItem(const char* label)
{
return *new UIMenuItem(label);
}
FORCEINLINE UIMenuItem& NewMenuHyperLink(const char* label, const char* link)
{
return *new UIMenuItem(label, link);
}
FORCEINLINE UIMenuItem& NewSubmenu(const char* label)
{
return *new UIMenuItem(UIMenuItem::MI_Submenu, label);
}
FORCEINLINE UIMenuItem& NewMenuSeparator()
{
return *new UIMenuItem(UIMenuItem::MI_Separator);
}
// Create a checkbox
// Checkbox which tracks provided value
FORCEINLINE UIMenuItem& NewMenuCheckbox(const char* label, bool* value)
{
return *new UIMenuItem(label, value);
}
// Checkbox with not automatic tracking
FORCEINLINE UIMenuItem& NewMenuCheckbox(const char* label, bool value)
{
return *new UIMenuItem(label, value);
}
FORCEINLINE UIMenuItem& NewMenuCheckbox(const char* label, int* value, int mask)
{
return *new UIMenuItem(label, value, mask);
}
FORCEINLINE UIMenuItem& NewMenuCheckbox(const char* label, unsigned int* value, int mask)
{
return *new UIMenuItem(label, (int*)value, mask);
}
// Create a radio group: RadioGroup holds a number of RadioItems
FORCEINLINE UIMenuItem& NewMenuRadioGroup(int* value)
{
return *new UIMenuItem(value);
}
FORCEINLINE UIMenuItem& NewMenuRadioGroup(int value)
{
return *new UIMenuItem(value);
}
FORCEINLINE UIMenuItem& NewMenuRadioButton(const char* label, int value)
{
return *new UIMenuItem(label, value);
}
/*-----------------------------------------------------------------------------
UI containers
-----------------------------------------------------------------------------*/
// Constants for setting some group properties (Flags)
#define GROUP_NO_BORDER 1
#define GROUP_NO_AUTO_LAYOUT 2
#define GROUP_HORIZONTAL_LAYOUT 4
#define GROUP_HORIZONTAL_SPACING 8 // for GROUP_HORIZONTAL_LAYOUT, evenly distribute controls
#define GROUP_CUSTOM_LAYOUT (GROUP_NO_BORDER|GROUP_NO_AUTO_LAYOUT)
class UIGroup : public UIElement
{
friend class UIPageControl;
DECLARE_UI_CLASS(UIGroup, UIElement);
DECLARE_CALLBACK(RadioCallback, int);
public:
UIGroup(const char* label, unsigned flags = 0);
UIGroup(unsigned flags = 0);
virtual ~UIGroup() override;