774 lines
20 KiB
Lua
774 lines
20 KiB
Lua
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
|