z_mirror/flib

git clone https://git.raiguard.me/z_mirror/flib

Caleb HeuerRemove require guardsa9f2be7

trunk
9.3 KiB254 linesraw
1--- Utilities for building GUIs and handling GUI events.
2--- ```lua
3--- local flib_gui = require("__flib__.gui")
4--- ```
5--- @class flib_gui : event_handler
6local flib_gui = {}
7
8local handler_tag_key = "__" .. script.mod_name .. "_handler"
9
10--- @type table<flib.GuiElemHandler, string>
11local handlers = {}
12--- @type table<string, flib.GuiElemHandler>
13local handlers_lookup = {}
14
15--- Add a new child or children to the given GUI element.
16--- @param parent LuaGuiElement The parent GUI element.
17--- @param def flib.GuiElemDef|flib.GuiElemDef[] The element definition, or an array of element definitions.
18--- @param elems table<string, LuaGuiElement>? Optional initial `elems` table.
19--- @return table<string, LuaGuiElement> elems Elements with names will be collected into this table.
20--- @return LuaGuiElement first The element that was created first;  the "top level" element.
21function flib_gui.add(parent, def, elems)
22  if not parent or not parent.valid then
23    error("Parent element is missing or invalid")
24  end
25  if not elems then
26    elems = {}
27  end
28  -- If a single def was passed, wrap it in an array
29  if def.type or (def.tab and def.content) then
30    def = { def }
31  end
32  local first
33  for i = 1, #def do
34    local def = def[i]
35    if def.type then
36      -- Remove custom attributes from the def so the game doesn't serialize them
37      local children = def.children
38      local elem_mods = def.elem_mods
39      local handler = def.handler
40      local style_mods = def.style_mods
41      local drag_target = def.drag_target
42      -- If children were defined in the array portion, remove and collect them
43      local has_array_children = false
44      if def[1] then
45        if children then
46          error("Cannot define children in array portion and subtable simultaneously")
47        end
48        has_array_children = true
49        children = {}
50        for i = 1, #def do
51          children[i] = def[i]
52          def[i] = nil
53        end
54      end
55      def.children = nil
56      def.elem_mods = nil
57      def.handler = nil
58      def.style_mods = nil
59      def.drag_target = nil
60
61      local elem = parent.add(def)
62
63      if not first then
64        first = elem
65      end
66      if def.name then
67        elems[def.name] = elem
68      end
69      if style_mods then
70        for key, value in pairs(style_mods) do
71          elem.style[key] = value
72        end
73      end
74      if elem_mods then
75        for key, value in pairs(elem_mods) do
76          elem[key] = value
77        end
78      end
79      if drag_target then
80        local target = elems[drag_target]
81        if not target then
82          error("Drag target '" .. drag_target .. "' not found.")
83        end
84        elem.drag_target = target
85      end
86      if handler then
87        local out
88        if type(handler) == "table" then
89          out = {}
90          for name, handler in pairs(handler) do
91            out[tostring(name)] = handlers[handler]
92          end
93        else
94          out = handlers[handler]
95        end
96        local tags = elem.tags
97        tags[handler_tag_key] = out
98        elem.tags = tags
99      end
100      if children then
101        flib_gui.add(elem, children, elems)
102      end
103
104      -- Re-add custom attributes
105      if children and has_array_children then
106        for i = 1, #children do
107          def[i] = children[i]
108        end
109      else
110        def.children = children
111      end
112      def.elem_mods = elem_mods
113      def.handler = handler
114      def.style_mods = style_mods
115      def.drag_target = drag_target
116    elseif def.tab and def.content then
117      local _, tab = flib_gui.add(parent, def.tab, elems)
118      local _, content = flib_gui.add(parent, def.content, elems)
119      parent.add_tab(tab, content)
120    end
121  end
122  return elems, first
123end
124
125--- Add the given handler functions to the registry for use with `flib_gui.add`. Each handler must have a unique name. If a
126--- `wrapper` function is provided, it will be called instead, and will receive the event data and handler. The wrapper
127--- can be used to execute logic or gather data common to all handler functions for this GUI.
128--- @param new_handlers table<string, fun(e: flib.GuiEventData)>
129--- @param wrapper fun(e: flib.GuiEventData, handler: function)?
130--- @param prefix string?
131function flib_gui.add_handlers(new_handlers, wrapper, prefix)
132  for name, handler in pairs(new_handlers) do
133    if prefix then
134      name = prefix .. "/" .. name
135    end
136    if type(handler) == "function" then
137      if handlers_lookup[name] then
138        error("Attempted to register two GUI event handlers with the same name: " .. name)
139      end
140      handlers[handler] = name
141      if wrapper then
142        handlers_lookup[name] = function(e)
143          wrapper(e, handler)
144        end
145      else
146        handlers_lookup[name] = handler
147      end
148    end
149  end
150end
151
152--- Dispatch the handler associated with this event and GUI element. The handler must have been added using
153--- `flib_gui.add_handlers`.
154--- @param e flib.GuiEventData
155--- @return boolean handled True if an event handler was called.
156function flib_gui.dispatch(e)
157  local elem = e.element
158  if not elem then
159    return false
160  end
161  local tags = elem.tags --[[@as Tags]]
162  local handler_def = tags[handler_tag_key]
163  if not handler_def then
164    return false
165  end
166  local handler_type = type(handler_def)
167  if handler_type == "table" then
168    handler_def = handler_def[tostring(e.name)]
169  end
170  if handler_def then
171    local handler = handlers_lookup[handler_def]
172    if handler then
173      handler(e)
174      return true
175    end
176  end
177  return false
178end
179
180--- For use with `__core__/lualib/event_handler`. Pass `flib_gui` into `handler.add_lib` to handle
181--- all GUI events automatically.
182flib_gui.events = {}
183for name, id in pairs(defines.events) do
184  if string.find(name, "on_gui_") then
185    flib_gui.events[id] = flib_gui.dispatch
186  end
187end
188
189--- Handle all GUI events with `flib_gui.dispatch`. Will not overwrite any existing event handlers.
190function flib_gui.handle_events()
191  for id in pairs(flib_gui.events) do
192    if not script.get_event_handler(id) then
193      script.on_event(id, flib_gui.dispatch)
194    end
195  end
196end
197
198--- Format the given handlers for use in a GUI element's tags. An alternative to using `flib_gui.add` if event handling
199--- is the only desired feature.
200---
201--- ### Example
202---
203--- ```lua
204--- --- @param e EventData.on_gui_click
205--- local function on_button_clicked(e)
206---   game.print("You clicked it!")
207--- end
208---
209--- player.gui.screen.add({
210---   type = "button",
211---   caption = "Click me!",
212---   tags = flib_gui.format_handlers({ [defines.events.on_gui_click] = on_button_clicked }),
213--- })
214---
215--- flib_gui.handle_events({ on_button_clicked = on_button_clicked })
216--- ```
217--- @param input flib.GuiElemHandler|table<defines.events, flib.GuiElemHandler?>
218--- @param existing Tags?
219--- @return Tags
220function flib_gui.format_handlers(input, existing)
221  local out
222  if type(input) == "table" then
223    out = {}
224    for name, handler in pairs(input) do
225      out[tostring(name)] = handlers[handler]
226    end
227  else
228    out = handlers[input]
229  end
230  if existing then
231    existing[handler_tag_key] = out
232    return existing
233  end
234  return { [handler_tag_key] = out }
235end
236
237--- A GUI element definition. This extends `LuaGuiElement.add_param` with several new attributes.
238--- Children may be defined in the array portion as an alternative to the `children` subtable.
239--- @class flib.GuiElemDef: LuaGuiElement.add_param.button|LuaGuiElement.add_param.camera|LuaGuiElement.add_param.checkbox|LuaGuiElement.add_param.choose_elem_button|LuaGuiElement.add_param.drop_down|LuaGuiElement.add_param.flow|LuaGuiElement.add_param.frame|LuaGuiElement.add_param.line|LuaGuiElement.add_param.list_box|LuaGuiElement.add_param.minimap|LuaGuiElement.add_param.progressbar|LuaGuiElement.add_param.radiobutton|LuaGuiElement.add_param.scroll_pane|LuaGuiElement.add_param.slider|LuaGuiElement.add_param.sprite|LuaGuiElement.add_param.sprite_button|LuaGuiElement.add_param.switch|LuaGuiElement.add_param.tab|LuaGuiElement.add_param.table|LuaGuiElement.add_param.text_box|LuaGuiElement.add_param.textfield
240--- @field style_mods LuaStyle? Modifications to make to the element's style.
241--- @field elem_mods LuaGuiElement? Modifications to make to the element itself.
242--- @field drag_target string? Set the element's drag target to the element whose name matches this string. The drag target must be present in the `elems` table.
243--- @field handler (flib.GuiElemHandler|table<defines.events, flib.GuiElemHandler>)? Handler(s) to assign to this element. If assigned to a function, that function will be called for any GUI event on this element.
244--- @field children flib.GuiElemDef[]? Children to add to this element.
245--- @field tab flib.GuiElemDef? To add a tab, specify `tab` and `content` and leave all other fields unset.
246--- @field content flib.GuiElemDef? To add a tab, specify `tab` and `content` and leave all other fields unset.
247
248--- A handler function to invoke when receiving GUI events for this element.
249--- @alias flib.GuiElemHandler fun(e: flib.GuiEventData)
250
251--- Aggregate type of all possible GUI events.
252--- @alias flib.GuiEventData EventData.on_gui_checked_state_changed|EventData.on_gui_click|EventData.on_gui_closed|EventData.on_gui_confirmed|EventData.on_gui_elem_changed|EventData.on_gui_location_changed|EventData.on_gui_opened|EventData.on_gui_selected_tab_changed|EventData.on_gui_selection_state_changed|EventData.on_gui_switch_state_changed|EventData.on_gui_text_changed|EventData.on_gui_value_changed
253
254return flib_gui