Initial flame_lua_runtime package
This commit is contained in:
773
assets/runtime/lua/runtime_widgets.lua
Normal file
773
assets/runtime/lua/runtime_widgets.lua
Normal file
@@ -0,0 +1,773 @@
|
||||
local runtime_ui = runtime.import("runtime_ui")
|
||||
|
||||
---@class (exact) RuntimeDialogButton
|
||||
---@field id? string
|
||||
---@field text string
|
||||
---@field handler string
|
||||
---@field color? string
|
||||
|
||||
---@class (exact) RuntimeDialogOpts
|
||||
---@field screenWidth? number
|
||||
---@field screenHeight? number
|
||||
---@field overlay? boolean
|
||||
---@field overlayColor? string
|
||||
---@field blockInput? boolean
|
||||
---@field layer? integer
|
||||
---@field color? string
|
||||
---@field radius? number
|
||||
---@field panelStyle? RuntimeNodeProps
|
||||
---@field titleColor? string
|
||||
---@field titleSize? number
|
||||
---@field titleStyle? RuntimeNodeProps
|
||||
---@field messageColor? string
|
||||
---@field messageSize? number
|
||||
---@field messageStyle? RuntimeNodeProps
|
||||
---@field buttons? RuntimeDialogButton[]
|
||||
---@field buttonGap? number
|
||||
---@field buttonStyle? RuntimeNodeProps
|
||||
|
||||
---@class (exact) RuntimeLabeledProgressOpts: RuntimeNodeProps
|
||||
---@field labelHeight? number
|
||||
---@field labelStyle? RuntimeNodeProps
|
||||
|
||||
---@class RuntimePillOpts: RuntimeNodeInit
|
||||
---@field panelStyle? RuntimeNodeProps
|
||||
---@field textStyle? RuntimeNodeProps
|
||||
|
||||
---@class RuntimeTextButtonOpts: RuntimeNodeInit
|
||||
---@field variant? 'primary'|'secondary'|'ghost'
|
||||
|
||||
---@class RuntimeListItemOpts: RuntimeTextButtonOpts
|
||||
---@field selected? boolean
|
||||
---@field activeColor? string
|
||||
---@field inactiveColor? string
|
||||
|
||||
---@class RuntimeTabItem
|
||||
---@field id? string
|
||||
---@field key? string
|
||||
---@field text string
|
||||
---@field handler? string
|
||||
---@field selected? boolean
|
||||
|
||||
---@class RuntimeTabsOpts: RuntimeNodeInit
|
||||
---@field tabs RuntimeTabItem[]
|
||||
---@field selected? string
|
||||
---@field gap? number
|
||||
---@field itemWidth? number
|
||||
---@field itemHeight? number
|
||||
---@field activeColor? string
|
||||
---@field inactiveColor? string
|
||||
---@field buttonStyle? RuntimeNodeProps
|
||||
|
||||
---@class RuntimeActionItem
|
||||
---@field id? string
|
||||
---@field text string
|
||||
---@field handler? string
|
||||
---@field visible? boolean
|
||||
---@field color? string
|
||||
---@field style? RuntimeNodeProps
|
||||
|
||||
---@class RuntimeActionRowOpts: RuntimeNodeInit
|
||||
---@field actions RuntimeActionItem[]
|
||||
---@field gap? number
|
||||
---@field itemWidth? number
|
||||
---@field itemHeight? number
|
||||
---@field buttonStyle? RuntimeNodeProps
|
||||
|
||||
---@class RuntimePanelHeaderOpts: RuntimeNodeInit
|
||||
---@field eyebrow? string
|
||||
---@field title string
|
||||
---@field summary? string
|
||||
---@field gap? number
|
||||
---@field eyebrowId? string
|
||||
---@field titleId? string
|
||||
---@field summaryId? string
|
||||
---@field eyebrowHeight? number
|
||||
---@field titleHeight? number
|
||||
---@field summaryHeight? number
|
||||
---@field eyebrowStyle? RuntimeNodeProps
|
||||
---@field titleStyle? RuntimeNodeProps
|
||||
---@field summaryStyle? RuntimeNodeProps
|
||||
|
||||
---@class RuntimeWidgetTheme
|
||||
---@field primary? string
|
||||
---@field secondary? string
|
||||
---@field success? string
|
||||
---@field overlay? string
|
||||
---@field surface? string
|
||||
---@field surfaceAlt? string
|
||||
---@field card? string
|
||||
---@field text? string
|
||||
---@field muted? string
|
||||
---@field progress? string
|
||||
---@field transparent? string
|
||||
|
||||
---@class RuntimeWidgets
|
||||
local widgets = {}
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- Theme/config
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
---@type RuntimeWidgetTheme
|
||||
local widget_theme = {
|
||||
primary = "#ff2563eb",
|
||||
secondary = "#ff475569",
|
||||
success = "#ff22c55e",
|
||||
overlay = "#99000000",
|
||||
surface = "#ff1e293b",
|
||||
surfaceAlt = "#ff334155",
|
||||
card = "#ee111827",
|
||||
text = "#ffffffff",
|
||||
muted = "#ffe5e7eb",
|
||||
progress = "#ff22c55e",
|
||||
transparent = "#00000000"
|
||||
}
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- Internal helpers
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
---@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
|
||||
for key, value in pairs(opts or {}) do
|
||||
result[key] = value
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
---@param value any
|
||||
---@return boolean
|
||||
local function is_table(value)
|
||||
return type(value) == "table"
|
||||
end
|
||||
|
||||
---@param source? table
|
||||
---@param keys string[]
|
||||
---@return table
|
||||
local function without_keys(source, keys)
|
||||
local result = merge(source or {}, nil)
|
||||
for _, key in ipairs(keys) do
|
||||
result[key] = nil
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
---@param opts? RuntimeNodeInit
|
||||
---@return RuntimeNodeInit
|
||||
local function normalize_box(opts)
|
||||
local result = merge(opts or {}, nil)
|
||||
if result.width == nil then
|
||||
result.width = result.w or result.size
|
||||
end
|
||||
if result.height == nil then
|
||||
result.height = result.h or result.size
|
||||
end
|
||||
result.w = nil
|
||||
result.h = nil
|
||||
result.size = nil
|
||||
return result
|
||||
end
|
||||
|
||||
---@param opts? RuntimeNodeInit
|
||||
---@param fallback? table
|
||||
---@return RuntimeNodeInit
|
||||
local function with_box(opts, fallback)
|
||||
return merge(fallback or {}, normalize_box(opts))
|
||||
end
|
||||
|
||||
---@param target RuntimeNode[]
|
||||
---@param source? RuntimeNode[]
|
||||
---@return RuntimeNode[]
|
||||
local function append_all(target, source)
|
||||
for _, node in ipairs(source or {}) do
|
||||
table.insert(target, node)
|
||||
end
|
||||
return target
|
||||
end
|
||||
|
||||
---@param parent string
|
||||
---@param opts? RuntimeNodeProps
|
||||
---@return RuntimeNodeProps
|
||||
local function child_opts(parent, opts)
|
||||
return merge(opts or {}, { parent = parent })
|
||||
end
|
||||
|
||||
---@param value table
|
||||
---@param opts? table
|
||||
---@param key string
|
||||
---@return table, table
|
||||
local function collection_args(value, opts, key)
|
||||
local options = opts or {}
|
||||
local items = value
|
||||
if is_table(value) and value[key] ~= nil then
|
||||
options = value
|
||||
items = value[key]
|
||||
end
|
||||
return items or {}, options
|
||||
end
|
||||
|
||||
---@param tokens? RuntimeWidgetTheme
|
||||
---@return RuntimeWidgets
|
||||
function widgets.configure(tokens)
|
||||
widget_theme = merge(widget_theme, tokens or {})
|
||||
return widgets
|
||||
end
|
||||
|
||||
---@param name string
|
||||
---@return string
|
||||
local function theme_color(name)
|
||||
return widget_theme[name] or "#ffffffff"
|
||||
end
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- Text helpers
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
---@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 widgets.label(id, text, x, y, width, height, opts)
|
||||
local fallback = { text = "", x = 0, y = 0, width = 0, height = 0 }
|
||||
local options
|
||||
if is_table(text) then
|
||||
options = with_box(text, fallback)
|
||||
else
|
||||
options = with_box(merge({ text = text, x = x, y = y, width = width, height = height }, opts), fallback)
|
||||
end
|
||||
return runtime_ui.text(id, merge({
|
||||
color = theme_color("muted"),
|
||||
fontSize = 14,
|
||||
textAlign = "left"
|
||||
}, options))
|
||||
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 widgets.section_title(id, text, x, y, width, height, opts)
|
||||
local options
|
||||
if is_table(text) then
|
||||
options = text
|
||||
else
|
||||
options = merge({ text = text, x = x, y = y, width = width, height = height }, opts)
|
||||
end
|
||||
return widgets.label(id, merge({
|
||||
color = theme_color("text"),
|
||||
fontSize = 18,
|
||||
textAlign = "left"
|
||||
}, options))
|
||||
end
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- Badges and buttons
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
---@param id string
|
||||
---@param text string|RuntimePillOpts
|
||||
---@param x? number
|
||||
---@param y? number
|
||||
---@param width? number
|
||||
---@param height? number
|
||||
---@param opts? RuntimePillOpts
|
||||
---@return RuntimeNode[]
|
||||
function widgets.pill(id, text, x, y, width, height, opts)
|
||||
local options
|
||||
if is_table(text) then
|
||||
options = with_box(text, { text = "", x = 0, y = 0, width = 0, height = 0 })
|
||||
else
|
||||
options = with_box(merge({ text = text, x = x, y = y, width = width, height = height }, opts), {
|
||||
text = "",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 0,
|
||||
height = 0
|
||||
})
|
||||
end
|
||||
local panel_opts = merge({
|
||||
color = theme_color("surfaceAlt"),
|
||||
radius = options.height / 2
|
||||
}, options.panelStyle)
|
||||
panel_opts = merge(panel_opts, without_keys(options, { "text", "panelStyle", "textStyle" }))
|
||||
local text_opts = child_opts(id, merge({
|
||||
color = theme_color("text"),
|
||||
fontSize = 12,
|
||||
textAlign = "center"
|
||||
}, options.textStyle))
|
||||
return {
|
||||
runtime_ui.panel(id, panel_opts),
|
||||
runtime_ui.text(id .. "_text", merge({
|
||||
text = options.text or "",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = options.width,
|
||||
height = options.height
|
||||
}, text_opts))
|
||||
}
|
||||
end
|
||||
|
||||
---@param id string
|
||||
---@param text string|RuntimeTextButtonOpts
|
||||
---@param x? number
|
||||
---@param y? number
|
||||
---@param width? number
|
||||
---@param height? number
|
||||
---@param handler? string
|
||||
---@param opts? RuntimeTextButtonOpts
|
||||
---@return RuntimeNode
|
||||
function widgets.text_button(id, text, x, y, width, height, handler, opts)
|
||||
local options
|
||||
if is_table(text) then
|
||||
options = with_box(text, { text = "", x = 0, y = 0, width = 0, height = 0 })
|
||||
else
|
||||
options = with_box(merge({ text = text, x = x, y = y, width = width, height = height, handler = handler }, opts), {
|
||||
text = "",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 0,
|
||||
height = 0
|
||||
})
|
||||
end
|
||||
local variant = options.variant or "primary"
|
||||
local color = theme_color("primary")
|
||||
if variant == "secondary" then
|
||||
color = theme_color("secondary")
|
||||
elseif variant == "ghost" then
|
||||
color = theme_color("transparent")
|
||||
end
|
||||
return runtime_ui.button(id, merge({
|
||||
color = color,
|
||||
radius = 10,
|
||||
fontSize = 13,
|
||||
textAlign = "center"
|
||||
}, without_keys(options, { "variant" })))
|
||||
end
|
||||
|
||||
---@param id string
|
||||
---@param text string|RuntimeListItemOpts
|
||||
---@param x? number
|
||||
---@param y? number
|
||||
---@param width? number
|
||||
---@param height? number
|
||||
---@param handler? string
|
||||
---@param opts? RuntimeListItemOpts
|
||||
---@return RuntimeNode
|
||||
function widgets.list_item(id, text, x, y, width, height, handler, opts)
|
||||
local options
|
||||
if is_table(text) then
|
||||
options = with_box(text, { text = "", x = 0, y = 0, width = 0, height = 0 })
|
||||
else
|
||||
options = with_box(merge({ text = text, x = x, y = y, width = width, height = height, handler = handler }, opts), {
|
||||
text = "",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 0,
|
||||
height = 0
|
||||
})
|
||||
end
|
||||
local selected = options.selected == true
|
||||
local color = selected and (options.activeColor or theme_color("primary")) or (options.inactiveColor or theme_color("surface"))
|
||||
return widgets.text_button(id, merge({
|
||||
color = color,
|
||||
radius = 10,
|
||||
fontSize = 12
|
||||
}, without_keys(options, { "selected", "activeColor", "inactiveColor" })))
|
||||
end
|
||||
|
||||
---@param id string
|
||||
---@param tabs RuntimeTabItem[]|RuntimeTabsOpts
|
||||
---@param opts? RuntimeTabsOpts
|
||||
---@return RuntimeNode[]
|
||||
function widgets.tabs(id, tabs, opts)
|
||||
local items, options = collection_args(tabs, opts, "tabs")
|
||||
|
||||
local origin_x = options.x or 0
|
||||
local origin_y = options.y or 0
|
||||
local gap = options.gap or 6
|
||||
local item_w = options.itemWidth or options.width or options.w or 72
|
||||
local item_h = options.itemHeight or options.height or options.h or 24
|
||||
local nodes = {}
|
||||
|
||||
for index, tab in ipairs(items) do
|
||||
local key = tab.key or tostring(index)
|
||||
local selected = tab.selected == true or key == options.selected
|
||||
local node_id = tab.id or (id .. "_" .. key)
|
||||
local style = merge({
|
||||
x = origin_x + (index - 1) * (item_w + gap),
|
||||
y = origin_y,
|
||||
w = item_w,
|
||||
h = item_h,
|
||||
text = tab.text or key,
|
||||
handler = tab.handler or key,
|
||||
selected = selected,
|
||||
activeColor = options.activeColor,
|
||||
inactiveColor = options.inactiveColor
|
||||
}, options.buttonStyle)
|
||||
style = merge(style, without_keys(options, {
|
||||
"tabs",
|
||||
"selected",
|
||||
"gap",
|
||||
"itemWidth",
|
||||
"itemHeight",
|
||||
"activeColor",
|
||||
"inactiveColor",
|
||||
"buttonStyle",
|
||||
"x",
|
||||
"y",
|
||||
"width",
|
||||
"height",
|
||||
"w",
|
||||
"h"
|
||||
}))
|
||||
nodes[index] = widgets.list_item(node_id, style)
|
||||
end
|
||||
|
||||
return nodes
|
||||
end
|
||||
|
||||
---@param id string
|
||||
---@param actions RuntimeActionItem[]|RuntimeActionRowOpts
|
||||
---@param opts? RuntimeActionRowOpts
|
||||
---@return RuntimeNode[]
|
||||
function widgets.action_row(id, actions, opts)
|
||||
local items, options = collection_args(actions, opts, "actions")
|
||||
|
||||
local count = #items
|
||||
local gap = options.gap or 8
|
||||
local origin_x = options.x or 0
|
||||
local origin_y = options.y or 0
|
||||
local item_h = options.itemHeight or options.height or options.h or 32
|
||||
local item_w = options.itemWidth
|
||||
if item_w == nil and (options.width ~= nil or options.w ~= nil) and count > 0 then
|
||||
item_w = ((options.width or options.w) - gap * (count - 1)) / count
|
||||
end
|
||||
item_w = item_w or 88
|
||||
|
||||
local nodes = {}
|
||||
for index, action in ipairs(items) do
|
||||
local visible = action.visible ~= false
|
||||
local style = merge({
|
||||
x = origin_x + (index - 1) * (item_w + gap),
|
||||
y = origin_y,
|
||||
w = item_w,
|
||||
h = item_h,
|
||||
text = visible and (action.text or "") or "",
|
||||
handler = visible and (action.handler or "noop") or "noop",
|
||||
color = action.color or (index == 1 and theme_color("primary") or theme_color("surfaceAlt")),
|
||||
visible = visible
|
||||
}, options.buttonStyle)
|
||||
style = merge(style, action.style)
|
||||
style = merge(style, without_keys(options, {
|
||||
"actions",
|
||||
"gap",
|
||||
"itemWidth",
|
||||
"itemHeight",
|
||||
"buttonStyle",
|
||||
"x",
|
||||
"y",
|
||||
"width",
|
||||
"height",
|
||||
"w",
|
||||
"h"
|
||||
}))
|
||||
nodes[index] = widgets.text_button(action.id or (id .. "_" .. tostring(index)), style)
|
||||
end
|
||||
|
||||
return nodes
|
||||
end
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- Panels and headers
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
---@param id string
|
||||
---@param opts RuntimePanelHeaderOpts
|
||||
---@return RuntimeNode[]
|
||||
function widgets.panel_header(id, opts)
|
||||
local options = with_box(opts or {}, { x = 0, y = 0, width = 0, height = 0, title = "" })
|
||||
local nodes = {}
|
||||
local cursor = options.y
|
||||
local gap = options.gap or 4
|
||||
local layer = options.layer
|
||||
local parent = options.parent
|
||||
|
||||
if options.eyebrow ~= nil and options.eyebrow ~= "" then
|
||||
local h = options.eyebrowHeight or 20
|
||||
table.insert(nodes, widgets.label(options.eyebrowId or (id .. "_eyebrow"), merge({
|
||||
text = options.eyebrow,
|
||||
x = options.x,
|
||||
y = cursor,
|
||||
width = options.width,
|
||||
height = h,
|
||||
parent = parent,
|
||||
layer = layer,
|
||||
color = theme_color("primary"),
|
||||
fontSize = 13
|
||||
}, options.eyebrowStyle)))
|
||||
cursor = cursor + h + gap
|
||||
end
|
||||
|
||||
local title_h = options.titleHeight or 28
|
||||
table.insert(nodes, widgets.section_title(options.titleId or (id .. "_title"), merge({
|
||||
text = options.title or "",
|
||||
x = options.x,
|
||||
y = cursor,
|
||||
width = options.width,
|
||||
height = title_h,
|
||||
parent = parent,
|
||||
layer = layer,
|
||||
fontSize = 20
|
||||
}, options.titleStyle)))
|
||||
cursor = cursor + title_h + gap
|
||||
|
||||
if options.summary ~= nil and options.summary ~= "" then
|
||||
local h = options.summaryHeight or 22
|
||||
table.insert(nodes, widgets.label(options.summaryId or (id .. "_summary"), merge({
|
||||
text = options.summary,
|
||||
x = options.x,
|
||||
y = cursor,
|
||||
width = options.width,
|
||||
height = h,
|
||||
parent = parent,
|
||||
layer = layer,
|
||||
color = theme_color("muted"),
|
||||
fontSize = 12
|
||||
}, options.summaryStyle)))
|
||||
end
|
||||
|
||||
return nodes
|
||||
end
|
||||
|
||||
---@param id string
|
||||
---@param width number|RuntimeNodeInit
|
||||
---@param height? number
|
||||
---@param opts? RuntimeNodeInit
|
||||
---@return RuntimeNode
|
||||
function widgets.overlay(id, width, height, opts)
|
||||
if is_table(width) then
|
||||
return runtime_ui.rect(id, merge({
|
||||
x = 0,
|
||||
y = 0,
|
||||
color = theme_color("overlay"),
|
||||
layer = 900
|
||||
}, width))
|
||||
end
|
||||
return runtime_ui.rect(id, 0, 0, width, height, merge({
|
||||
color = theme_color("overlay"),
|
||||
layer = 900
|
||||
}, opts))
|
||||
end
|
||||
|
||||
---@param id string
|
||||
---@param x number|RuntimeNodeInit
|
||||
---@param y? number
|
||||
---@param width? number
|
||||
---@param height? number
|
||||
---@param opts? RuntimeNodeInit
|
||||
---@return RuntimeNode
|
||||
function widgets.card(id, x, y, width, height, opts)
|
||||
if is_table(x) then
|
||||
return runtime_ui.panel(id, merge({
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 0,
|
||||
height = 0,
|
||||
color = theme_color("card"),
|
||||
radius = 14,
|
||||
layer = 910
|
||||
}, x))
|
||||
end
|
||||
return runtime_ui.panel(id, x, y, width, height, merge({
|
||||
color = theme_color("card"),
|
||||
radius = 14,
|
||||
layer = 910
|
||||
}, opts))
|
||||
end
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- Progress
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
---@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 widgets.progress_bar(id, x, y, width, height, value, opts)
|
||||
if is_table(x) then
|
||||
return runtime_ui.progress(id, merge({
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 0,
|
||||
height = 0,
|
||||
value = 0,
|
||||
color = theme_color("progress"),
|
||||
radius = 6
|
||||
}, x))
|
||||
end
|
||||
return runtime_ui.progress(id, x, y, width, height, value, merge({
|
||||
color = theme_color("progress"),
|
||||
radius = 6
|
||||
}, opts))
|
||||
end
|
||||
|
||||
---@param id string
|
||||
---@param label string
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param width number
|
||||
---@param height number
|
||||
---@param value number
|
||||
---@param opts? RuntimeLabeledProgressOpts
|
||||
---@return RuntimeNode[]
|
||||
function widgets.labeled_progress(id, label, x, y, width, height, value, opts)
|
||||
local label_height = opts ~= nil and opts.labelHeight or 24
|
||||
local progress_opts = merge(opts or {}, nil)
|
||||
progress_opts.labelHeight = nil
|
||||
progress_opts.labelStyle = nil
|
||||
return {
|
||||
runtime_ui.text(id .. "_label", label, x, y, width, label_height, merge({
|
||||
color = theme_color("text"),
|
||||
fontSize = 16
|
||||
}, opts ~= nil and opts.labelStyle or nil)),
|
||||
widgets.progress_bar(id, x, y + label_height + 6, width, height, value, progress_opts)
|
||||
}
|
||||
end
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- Dialogs
|
||||
-- -----------------------------------------------------------------------------
|
||||
|
||||
---@param parent string
|
||||
---@param id string
|
||||
---@param buttons RuntimeDialogButton[]
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param width number
|
||||
---@param height number
|
||||
---@param gap? number
|
||||
---@param opts? RuntimeNodeProps
|
||||
---@return RuntimeNode[]
|
||||
function widgets.button_row(parent, id, buttons, x, y, width, height, gap, opts)
|
||||
local nodes = {}
|
||||
local count = #buttons
|
||||
if count == 0 then
|
||||
return nodes
|
||||
end
|
||||
|
||||
local actual_gap = gap or 8
|
||||
local button_width = (width - actual_gap * (count - 1)) / count
|
||||
for index, button in ipairs(buttons) do
|
||||
local button_id = button.id or (id .. "_button_" .. index)
|
||||
table.insert(nodes, runtime_ui.button(
|
||||
button_id,
|
||||
button.text or "OK",
|
||||
x + (index - 1) * (button_width + actual_gap),
|
||||
y,
|
||||
button_width,
|
||||
height,
|
||||
button.handler,
|
||||
child_opts(parent, merge({
|
||||
layer = 920,
|
||||
color = button.color or theme_color("primary"),
|
||||
radius = 10
|
||||
}, opts))
|
||||
))
|
||||
end
|
||||
return nodes
|
||||
end
|
||||
|
||||
---@param id string
|
||||
---@param title string
|
||||
---@param message string
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param width number
|
||||
---@param height number
|
||||
---@param opts? RuntimeDialogOpts
|
||||
---@return RuntimeNode[]
|
||||
function widgets.dialog(id, title, message, x, y, width, height, opts)
|
||||
local options = opts or {}
|
||||
local nodes = {}
|
||||
local layer = options.layer or 900
|
||||
local screen_width = options.screenWidth or 720
|
||||
local screen_height = options.screenHeight or 720
|
||||
|
||||
if options.overlay ~= false then
|
||||
table.insert(nodes, widgets.overlay(id .. "_overlay", screen_width, screen_height, {
|
||||
layer = layer,
|
||||
color = options.overlayColor or theme_color("overlay"),
|
||||
interactive = options.blockInput == true
|
||||
}))
|
||||
end
|
||||
|
||||
table.insert(nodes, widgets.card(id, x, y, width, height, merge({
|
||||
layer = layer + 1,
|
||||
color = options.color or theme_color("card"),
|
||||
radius = options.radius or 16
|
||||
}, options.panelStyle)))
|
||||
|
||||
table.insert(nodes, runtime_ui.text(
|
||||
id .. "_title",
|
||||
title or "",
|
||||
20,
|
||||
18,
|
||||
width - 40,
|
||||
32,
|
||||
child_opts(id, merge({
|
||||
layer = layer + 2,
|
||||
color = options.titleColor or theme_color("text"),
|
||||
fontSize = options.titleSize or 22
|
||||
}, options.titleStyle))
|
||||
))
|
||||
|
||||
table.insert(nodes, runtime_ui.text(
|
||||
id .. "_message",
|
||||
message or "",
|
||||
20,
|
||||
60,
|
||||
width - 40,
|
||||
height - 116,
|
||||
child_opts(id, merge({
|
||||
layer = layer + 2,
|
||||
color = options.messageColor or theme_color("muted"),
|
||||
fontSize = options.messageSize or 16
|
||||
}, options.messageStyle))
|
||||
))
|
||||
|
||||
append_all(nodes, widgets.button_row(
|
||||
id,
|
||||
id,
|
||||
options.buttons or {},
|
||||
20,
|
||||
height - 48,
|
||||
width - 40,
|
||||
34,
|
||||
options.buttonGap or 8,
|
||||
options.buttonStyle
|
||||
))
|
||||
|
||||
return nodes
|
||||
end
|
||||
|
||||
return widgets
|
||||
Reference in New Issue
Block a user