1 module rpui.widgets.list_menu.widget;
2 
3 import std.container.array;
4 
5 import rpui.basic_rpdl_exts;
6 import rpui.events;
7 import rpui.input;
8 import rpui.math;
9 import rpui.primitives;
10 import rpui.widget;
11 import rpui.widgets.list_menu_item.widget;
12 import rpui.widgets.list_menu.renderer;
13 import rpui.widgets.stack_layout.stack_locator;
14 
15 class ListMenu : Widget {
16     @field bool transparent = false;
17     @field bool checkList = false;
18     @field bool isPopup = true;
19     @field bool isBindDataMode = false;
20     @field string listItemStyle = "ListMenuItem";
21 
22     private StackLocator stackLocator;
23 
24     package struct Measure {
25         float displayDelay = 0f;
26         FrameRect popupExtraPadding;
27         vec2 downPopupOffset;
28         vec2 rightPopupOffset;
29         FrameRect extraMenuVisibleBorder;
30     }
31 
32     Measure measure;
33 
34     this(in string style = "ListMenu") {
35         super(style);
36 
37         this.renderer = new ListMenuRenderer();
38         this.stackLocator.attach(this);
39         this.stackLocator.orientation = Orientation.vertical;
40         this.widthType = SizeType.value;
41         this.heightType = SizeType.wrapContent;
42     }
43 
44     override void onCreate() {
45         super.onCreate();
46         loadMeasure();
47     }
48 
49     private void loadMeasure() {
50         with (view.theme.tree) {
51             measure.popupExtraPadding = data.getFrameRect(style ~ ".popupExtraPadding");
52             measure.rightPopupOffset = data.getVec2f(style ~ ".rightPopupOffset");
53             measure.downPopupOffset = data.getVec2f(style ~ ".downPopupOffset");
54             measure.extraMenuVisibleBorder = data.getFrameRect(style ~ ".extraMenuVisibleBorder");
55             measure.displayDelay = data.getNumber(style ~ ".displayDelay.0");
56         }
57     }
58 
59     override void onProgress(in ProgressEvent event) {
60         super.onProgress(event);
61 
62         locator.updateAbsolutePosition();
63         locator.updateLocationAlign();
64         locator.updateVerticalLocationAlign();
65         locator.updateRegionAlign();
66 
67         updateSize();
68 
69         if (isPopup) {
70             extraInnerOffset = measure.popupExtraPadding;
71         }
72     }
73 
74     override void updateSize() {
75         super.updateSize();
76 
77         stackLocator.updateWidgetsPosition();
78         stackLocator.updateSize();
79     }
80 
81     void hideAllSubMenus() {
82         foreach (Widget widget; children) {
83             const row = widget.associatedWidget;
84 
85             if (auto item = cast(MenuActions) row) {
86                 item.hideMenu();
87             }
88         }
89     }
90 
91     // TODO(Andrey): visibility
92     void hideAllSubMenusExcept(Widget menuItem) {
93         foreach (Widget widget; children) {
94             const row = widget.associatedWidget;
95 
96             if (row == menuItem)
97                 continue;
98 
99             if (auto item = cast(MenuActions) row) {
100                 item.hideMenu();
101             }
102         }
103     }
104 
105     void bindData(T)(ref Array!(T) items, Widget delegate(T item) factory) {
106         children.clear();
107 
108         for (int i = 0; i < items.length; ++i) {
109             auto widget = factory(items[i]);
110             children.addWidget(widget);
111             widget.onPostCreate();
112         }
113 
114         if (parent == view.rootWidget) {
115             // NOTE(Andrey): reorder children
116             view.moveWidgetToFront(this);
117         }
118     }
119 }