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 }