---@class RuntimeUi local runtime_ui = {} ---@generic T: table ---@param base? T ---@param opts? table ---@return T local function merge(base, opts) local result = {} for key, value in pairs(base or {}) do result[key] = value end if opts ~= nil then for key, value in pairs(opts) do result[key] = value end end return result end ---@param value any ---@return boolean local function is_table(value) return type(value) == "table" end ---@param opts? table ---@return table local function normalize_opts(opts) local source = opts or {} local result = {} for key, value in pairs(source) do if key ~= "w" and key ~= "h" and key ~= "size" and key ~= "handler" and key ~= "onClick" then result[key] = value end end if result.width == nil then result.width = source.w or source.size end if result.height == nil then result.height = source.h or source.size end if result.onTap == nil then result.onTap = source.handler or source.onClick end return result end ---@param base table ---@param opts? table ---@return table local function node_opts(base, opts) return merge(base, normalize_opts(opts)) end ---@param base? RuntimeNodeProps ---@param opts? RuntimeNodeProps ---@return RuntimeNodeProps function runtime_ui.style(base, opts) return merge(base or {}, opts) end ---@param parent string ---@param opts? RuntimeNodeProps ---@return RuntimeNodeProps function runtime_ui.with_parent(parent, opts) return merge(opts or {}, { parent = parent }) end ---@param node_type RuntimeNodeType ---@param id string ---@param opts? RuntimeNodeProps ---@return RuntimeNode function runtime_ui.node(node_type, id, opts) return node_opts({ id = id, type = node_type }, opts) end ---@param id string ---@param x number|RuntimeNodeInit ---@param y? number ---@param width? number ---@param height? number ---@param opts? RuntimeNodeInit ---@return RuntimeNode function runtime_ui.panel(id, x, y, width, height, opts) if is_table(x) then return runtime_ui.node("panel", id, node_opts({ x = 0, y = 0, width = 0, height = 0 }, x)) end return runtime_ui.node("panel", id, node_opts({ x = x, y = y, width = width, height = height }, opts)) end ---@param id string ---@param x number|RuntimeNodeInit ---@param y? number ---@param width? number ---@param height? number ---@param opts? RuntimeNodeInit ---@return RuntimeNode function runtime_ui.rect(id, x, y, width, height, opts) if is_table(x) then return runtime_ui.node("rect", id, node_opts({ x = 0, y = 0, width = 0, height = 0 }, x)) end return runtime_ui.node("rect", id, node_opts({ x = x, y = y, width = width, height = height }, opts)) end ---@param id string ---@param x number|RuntimeNodeInit ---@param y? number ---@param size? number ---@param opts? RuntimeNodeInit ---@return RuntimeNode function runtime_ui.circle(id, x, y, size, opts) if is_table(x) then return runtime_ui.node("circle", id, node_opts({ x = 0, y = 0, width = 0, height = 0 }, x)) end return runtime_ui.node("circle", id, node_opts({ x = x, y = y, width = size, height = size }, opts)) end ---@param id string ---@param x number|RuntimeNodeInit ---@param y? number ---@param width? number ---@param height? number ---@param opts? RuntimeNodeInit ---@return RuntimeNode function runtime_ui.line(id, x, y, width, height, opts) if is_table(x) then return runtime_ui.node("line", id, node_opts({ x = 0, y = 0, width = 0, height = 0 }, x)) end return runtime_ui.node("line", id, node_opts({ x = x, y = y, width = width, height = height }, opts)) end ---@param id string ---@param x number|RuntimeNodeInit ---@param y? number ---@param width? number ---@param height? number ---@param value? number ---@param opts? RuntimeNodeInit ---@return RuntimeNode function runtime_ui.progress(id, x, y, width, height, value, opts) if is_table(x) then return runtime_ui.node("progress", id, node_opts({ x = 0, y = 0, width = 0, height = 0, value = 0 }, x)) end return runtime_ui.node("progress", id, node_opts({ x = x, y = y, width = width, height = height, value = value }, opts)) end ---@param id string ---@param x number|RuntimeNodeInit ---@param y? number ---@param width? number ---@param height? number ---@param opts? RuntimeNodeInit ---@return RuntimeNode function runtime_ui.particle(id, x, y, width, height, opts) if is_table(x) then return runtime_ui.node("particle", id, node_opts({ x = 0, y = 0, width = 0, height = 0 }, x)) end return runtime_ui.node("particle", id, node_opts({ x = x, y = y, width = width, height = height }, opts)) end ---@param id string ---@param text string|RuntimeNodeInit ---@param x? number ---@param y? number ---@param width? number ---@param height? number ---@param opts? RuntimeNodeInit ---@return RuntimeNode function runtime_ui.text(id, text, x, y, width, height, opts) if is_table(text) then return runtime_ui.node("text", id, node_opts({ text = "", x = 0, y = 0, width = 0, height = 0 }, text)) end return runtime_ui.node("text", id, node_opts({ text = text, x = x, y = y, width = width, height = height }, opts)) end ---@param id string ---@param text string|RuntimeNodeInit ---@param x? number ---@param y? number ---@param width? number ---@param height? number ---@param handler? string ---@param opts? RuntimeNodeInit ---@return RuntimeNode function runtime_ui.button(id, text, x, y, width, height, handler, opts) if is_table(text) then return runtime_ui.node("button", id, node_opts({ text = "", x = 0, y = 0, width = 0, height = 0, interactive = true }, text)) end return runtime_ui.node("button", id, node_opts({ text = text, x = x, y = y, width = width, height = height, interactive = true, onTap = handler }, opts)) end ---@param id string ---@param x number|RuntimeNodeInit ---@param y? number ---@param width? number ---@param height? number ---@param opts? RuntimeNodeInit ---@return RuntimeNode function runtime_ui.list_view(id, x, y, width, height, opts) if is_table(x) then return runtime_ui.node("listView", id, node_opts({ x = 0, y = 0, width = 0, height = 0 }, x)) end return runtime_ui.node("listView", id, node_opts({ x = x, y = y, width = width, height = height }, opts)) end ---@param id string ---@param asset string|RuntimeNodeInit ---@param x? number ---@param y? number ---@param width? number ---@param height? number ---@param opts? RuntimeNodeInit ---@return RuntimeNode function runtime_ui.image(id, asset, x, y, width, height, opts) if is_table(asset) then return runtime_ui.node("image", id, node_opts({ asset = "", x = 0, y = 0, width = 0, height = 0 }, asset)) end return runtime_ui.node("image", id, node_opts({ asset = asset, x = x, y = y, width = width, height = height }, opts)) end ---@param id string ---@param asset string|RuntimeNodeInit ---@param x? number ---@param y? number ---@param width? number ---@param height? number ---@param opts? RuntimeNodeInit ---@return RuntimeNode function runtime_ui.sprite(id, asset, x, y, width, height, opts) if is_table(asset) then return runtime_ui.node("sprite", id, node_opts({ asset = "", x = 0, y = 0, width = 0, height = 0 }, asset)) end return runtime_ui.node("sprite", id, node_opts({ asset = asset, x = x, y = y, width = width, height = height }, opts)) end ---@param id string ---@param asset string|RuntimeNodeInit ---@param x? number ---@param y? number ---@param width? number ---@param height? number ---@param animation? string ---@param opts? RuntimeNodeInit ---@return RuntimeNode function runtime_ui.spine(id, asset, x, y, width, height, animation, opts) if is_table(asset) then return runtime_ui.node("spine", id, node_opts({ asset = "", x = 0, y = 0, width = 0, height = 0, loop = true }, asset)) end return runtime_ui.node("spine", id, node_opts({ asset = asset, x = x, y = y, width = width, height = height, animation = animation, loop = true }, opts)) end ---@param id string ---@param props RuntimeNodeInit ---@return RuntimeNodeUpdate function runtime_ui.update(id, props) return { id = id, props = normalize_opts(props or {}) } end ---@param id string ---@param text string ---@return RuntimeNodeUpdate function runtime_ui.text_update(id, text) return runtime_ui.update(id, { text = text }) end ---@param id string ---@param visible boolean ---@return RuntimeNodeUpdate function runtime_ui.visible_update(id, visible) return runtime_ui.update(id, { visible = visible }) end ---@param id string ---@param alpha number ---@return RuntimeNodeUpdate function runtime_ui.alpha_update(id, alpha) return runtime_ui.update(id, { alpha = alpha }) end ---@param id string ---@param scale number ---@return RuntimeNodeUpdate function runtime_ui.scale_update(id, scale) return runtime_ui.update(id, { scale = scale }) end ---@param id string ---@param x number ---@param y number ---@return RuntimeNodeUpdate function runtime_ui.position_update(id, x, y) return runtime_ui.update(id, { x = x, y = y }) end ---@param id string ---@param width number ---@param height number ---@return RuntimeNodeUpdate function runtime_ui.size_update(id, width, height) return runtime_ui.update(id, { width = width, height = height }) end ---@param id string ---@param x number ---@param y number ---@param scale number ---@param rotation number ---@return RuntimeNodeUpdate function runtime_ui.transform_update(id, x, y, scale, rotation) return runtime_ui.update(id, { x = x, y = y, scale = scale, rotation = rotation }) end ---@param ids string[] ---@param props RuntimeNodeInit ---@return RuntimeNodeUpdate[] function runtime_ui.batch_update(ids, props) local updates = {} for _, id in ipairs(ids) do table.insert(updates, runtime_ui.update(id, props)) end return updates end ---@param nodes RuntimeNode[] ---@param node RuntimeNode ---@return RuntimeNode[] function runtime_ui.append(nodes, node) table.insert(nodes, node) return nodes end ---@param nodes RuntimeNode[] ---@param extra_nodes RuntimeNode[] ---@return RuntimeNode[] function runtime_ui.append_all(nodes, extra_nodes) for _, node in ipairs(extra_nodes) do table.insert(nodes, node) end return nodes end return runtime_ui