1301 lines
38 KiB
Lua
1301 lines
38 KiB
Lua
---@type RuntimeUi
|
||
local runtime_ui = runtime.import("runtime_ui")
|
||
---@type RuntimeWidgets
|
||
local widgets = runtime.import("runtime_widgets")
|
||
local theme = runtime.import("theme")
|
||
local styles = runtime.import("styles")
|
||
local state = runtime.import("state")
|
||
local examples = runtime.import("examples")
|
||
local i18n = runtime.import("i18n")
|
||
|
||
---@class ShowcaseUi
|
||
local ui = {}
|
||
|
||
local base_preview_ids = {
|
||
"sample_rect",
|
||
"sample_circle",
|
||
"sample_line",
|
||
"sample_image_node",
|
||
"sample_sprite_node",
|
||
"sample_progress",
|
||
"widget_progress_label",
|
||
"widget_progress"
|
||
}
|
||
|
||
local text_preview_ids = {
|
||
"text_plain_title",
|
||
"text_plain_body",
|
||
"text_style_badge",
|
||
"text_rich_note"
|
||
}
|
||
|
||
local button_preview_ids = {
|
||
"button_hint",
|
||
"button_primary",
|
||
"button_secondary",
|
||
"button_disabled",
|
||
"button_state_text"
|
||
}
|
||
|
||
local button_image_preview_ids = {
|
||
"image_button_hint",
|
||
"image_button_normal",
|
||
"image_button_toggle",
|
||
"image_button_disabled",
|
||
"image_button_state_text"
|
||
}
|
||
|
||
local sprite_preview_ids = {
|
||
"sprite_image_demo",
|
||
"sprite_sprite_demo",
|
||
"sprite_frame_demo",
|
||
"sprite_label_demo"
|
||
}
|
||
|
||
local layout_preview_ids = {
|
||
"layout_canvas",
|
||
"layout_chip_1",
|
||
"layout_chip_2",
|
||
"layout_chip_3",
|
||
"layout_chip_4",
|
||
"layout_label"
|
||
}
|
||
|
||
local radio_preview_ids = {
|
||
"radio_title",
|
||
"radio_audio_dot",
|
||
"radio_audio_label",
|
||
"radio_spine_dot",
|
||
"radio_spine_label",
|
||
"radio_lua_dot",
|
||
"radio_lua_label",
|
||
"radio_value_text"
|
||
}
|
||
|
||
local list_preview_ids = {
|
||
"list_panel",
|
||
"list_row_1",
|
||
"list_row_2",
|
||
"list_row_3",
|
||
"list_row_4",
|
||
"list_row_5",
|
||
"list_row_1_text",
|
||
"list_row_2_text",
|
||
"list_row_3_text",
|
||
"list_row_4_text",
|
||
"list_row_5_text",
|
||
"list_value_text"
|
||
}
|
||
|
||
local particle_preview_ids = {
|
||
"particle_burst",
|
||
"particle_trail",
|
||
"particle_snow",
|
||
"particle_label"
|
||
}
|
||
|
||
local responsive_preview_ids = {
|
||
"responsive_info",
|
||
"responsive_device",
|
||
"responsive_sidebar",
|
||
"responsive_content"
|
||
}
|
||
|
||
---@return table
|
||
local function metrics()
|
||
local screen_w = theme.screen_width
|
||
local screen_h = theme.screen_height
|
||
local margin = 20
|
||
local gap = 16
|
||
local header_h = 82
|
||
local status_h = 30
|
||
local content_h = screen_h - header_h - status_h - margin
|
||
local compact = screen_w < 640
|
||
local menu_w = compact and 188 or 220
|
||
local detail_w = screen_w - margin * 2 - gap - menu_w
|
||
local content_y = header_h
|
||
local preview_h = 148
|
||
local action_y = content_y + content_h - preview_h - 54
|
||
local preview_y = content_y + content_h - preview_h - 10
|
||
local code_h = action_y - content_y - 150
|
||
if code_h < 128 then
|
||
code_h = 128
|
||
end
|
||
|
||
return {
|
||
screen_w = screen_w,
|
||
screen_h = screen_h,
|
||
margin = margin,
|
||
gap = gap,
|
||
compact = compact,
|
||
menu_x = margin,
|
||
menu_y = content_y,
|
||
menu_w = menu_w,
|
||
detail_x = margin + menu_w + gap,
|
||
detail_y = content_y,
|
||
detail_w = detail_w,
|
||
content_h = content_h,
|
||
action_y = action_y,
|
||
preview_y = preview_y,
|
||
preview_h = preview_h,
|
||
code_h = code_h,
|
||
status_y = screen_h - status_h - 8
|
||
}
|
||
end
|
||
|
||
---@param example ShowcaseExample
|
||
---@return boolean
|
||
local function preview_mode(example)
|
||
if example.id == "text_demo" then
|
||
return "text"
|
||
end
|
||
if example.id == "buttons" then
|
||
return "buttons"
|
||
end
|
||
if example.id == "button_images" then
|
||
return "button_images"
|
||
end
|
||
if example.id == "sprites" then
|
||
return "sprites"
|
||
end
|
||
if example.id == "layout_demo" then
|
||
return "layout"
|
||
end
|
||
if example.id == "radio_group" then
|
||
return "radio"
|
||
end
|
||
if example.id == "list_view" then
|
||
return "list"
|
||
end
|
||
if example.id == "particles" then
|
||
return "particle"
|
||
end
|
||
if example.id == "responsive" then
|
||
return "responsive"
|
||
end
|
||
return "base"
|
||
end
|
||
|
||
---@param nodes RuntimeNode[]
|
||
---@param text string
|
||
---@param y number
|
||
---@param m table
|
||
local function append_menu_group(nodes, text, y, m)
|
||
table.insert(nodes, widgets.label("group_" .. tostring(y), {
|
||
text = text,
|
||
x = 16,
|
||
y = y,
|
||
w = m.menu_w - 32,
|
||
h = 18,
|
||
parent = "example_list_panel",
|
||
color = theme.muted,
|
||
fontSize = 11,
|
||
layer = 30
|
||
}))
|
||
end
|
||
|
||
---@param nodes RuntimeNode[]
|
||
---@param example ShowcaseExample
|
||
---@param y number
|
||
---@param m table
|
||
local function append_menu_item(nodes, example, y, m)
|
||
local selected = example.id == state.selected_example
|
||
table.insert(nodes, widgets.list_item("example_" .. example.id, {
|
||
text = i18n.example_label(example),
|
||
x = 14,
|
||
y = y,
|
||
w = m.menu_w - 28,
|
||
h = 27,
|
||
handler = "select_example",
|
||
parent = "example_list_panel",
|
||
selected = selected,
|
||
activeColor = theme.primary,
|
||
inactiveColor = "#ff1e293b",
|
||
radius = 10,
|
||
fontSize = 11,
|
||
layer = 31
|
||
}))
|
||
end
|
||
|
||
---@param selected ShowcaseExample
|
||
---@param m table
|
||
---@return RuntimeNode[]
|
||
local function detail_action_nodes(selected, m)
|
||
local actions = {}
|
||
for index = 1, 3 do
|
||
local action = selected.actions[index]
|
||
actions[index] = {
|
||
id = "detail_action_" .. index,
|
||
text = action ~= nil and i18n.action_text(action) or "",
|
||
handler = action ~= nil and action.handler or "noop",
|
||
visible = action ~= nil,
|
||
color = index == 1 and "#ff2563eb" or "#ff334155"
|
||
}
|
||
end
|
||
return widgets.action_row("detail_action", actions, {
|
||
x = m.detail_x + 20,
|
||
y = m.action_y,
|
||
width = m.detail_w - 40,
|
||
itemHeight = 34,
|
||
gap = 12,
|
||
radius = 10,
|
||
fontSize = 12,
|
||
layer = 35
|
||
})
|
||
end
|
||
|
||
---@param example ShowcaseExample
|
||
---@return string
|
||
local function detail_body(example)
|
||
if state.detail_tab == "params" then
|
||
return i18n.example_params(example)
|
||
end
|
||
return example.code
|
||
end
|
||
|
||
---@return string
|
||
local function detail_copy_text()
|
||
if state.detail_tab == "params" then
|
||
return i18n.t("copy_params")
|
||
end
|
||
return i18n.t("copy_code")
|
||
end
|
||
|
||
---@param text string
|
||
---@return integer
|
||
local function line_count(text)
|
||
local value = text or ""
|
||
local count = 1
|
||
local start = 1
|
||
while true do
|
||
local index = string.find(value, "\n", start, true)
|
||
if index == nil then
|
||
return count
|
||
end
|
||
count = count + 1
|
||
start = index + 1
|
||
end
|
||
end
|
||
|
||
local detail_text_y
|
||
|
||
local detail_padding_left = 14
|
||
local detail_padding_top = 12
|
||
local detail_padding_right = 14
|
||
local detail_padding_bottom = 12
|
||
|
||
---@param m table
|
||
---@return number
|
||
local function detail_viewport_width(m)
|
||
return m.detail_w - 40 - detail_padding_left - detail_padding_right
|
||
end
|
||
|
||
---@param m table
|
||
---@return number
|
||
local function detail_viewport_height(m)
|
||
return m.code_h + 18 - detail_padding_top - detail_padding_bottom
|
||
end
|
||
|
||
---@param example ShowcaseExample
|
||
---@return number
|
||
local function detail_text_height(example)
|
||
return line_count(detail_body(example)) * 16 + 8
|
||
end
|
||
|
||
---@param example ShowcaseExample
|
||
---@param m table
|
||
---@return number
|
||
local function detail_content_height(example, m)
|
||
local estimated = detail_text_y(example, m) + detail_text_height(example) + 18
|
||
local viewport_h = detail_viewport_height(m)
|
||
if estimated < viewport_h then
|
||
return viewport_h
|
||
end
|
||
return estimated
|
||
end
|
||
|
||
---@param m table
|
||
---@return number
|
||
local function detail_content_width(m)
|
||
local viewport_w = detail_viewport_width(m)
|
||
if state.detail_tab == "params" then
|
||
return viewport_w
|
||
end
|
||
if line_count(detail_body(examples.find(state.selected_example))) <= 7 then
|
||
return viewport_w
|
||
end
|
||
return viewport_w * 1.45
|
||
end
|
||
|
||
---@param example ShowcaseExample
|
||
---@param m table
|
||
---@return number
|
||
local function detail_text_width(example, m)
|
||
local viewport_w = detail_viewport_width(m)
|
||
if state.detail_tab == "params" then
|
||
return viewport_w * 0.78
|
||
end
|
||
local content_w = detail_content_width(m) - 28
|
||
local centered_w = viewport_w * 0.78
|
||
if line_count(detail_body(example)) <= 7 then
|
||
return centered_w
|
||
end
|
||
return content_w
|
||
end
|
||
|
||
---@param example ShowcaseExample
|
||
---@param m table
|
||
---@return number
|
||
local function detail_text_x(example, m)
|
||
local viewport_w = detail_viewport_width(m)
|
||
local width = detail_text_width(example, m)
|
||
if state.detail_tab == "code" and width > viewport_w then
|
||
return 14
|
||
end
|
||
local x = (viewport_w - width) / 2
|
||
if x < 0 then
|
||
return 0
|
||
end
|
||
return x
|
||
end
|
||
|
||
---@param example ShowcaseExample
|
||
---@param m table
|
||
---@return number
|
||
detail_text_y = function(example, m)
|
||
return 0
|
||
end
|
||
|
||
---@return string
|
||
local function responsive_text()
|
||
local scale = state.viewport_scale or 1
|
||
local width = state.screen_width or theme.screen_width
|
||
local height = state.screen_height or theme.screen_height
|
||
local viewport_w = state.viewport_width or theme.screen_width
|
||
local viewport_h = state.viewport_height or theme.screen_height
|
||
return i18n.t("responsive_design") .. ": " .. tostring(theme.screen_width) .. "x" .. tostring(theme.screen_height)
|
||
.. " | " .. i18n.t("responsive_screen") .. ": " .. tostring(math.floor(width)) .. "x" .. tostring(math.floor(height))
|
||
.. " | " .. i18n.t("responsive_viewport") .. ": " .. tostring(math.floor(viewport_w)) .. "x" .. tostring(math.floor(viewport_h))
|
||
.. " @" .. string.format("%.2f", scale)
|
||
end
|
||
|
||
---@return number
|
||
local function responsive_device_width()
|
||
if state.responsive_mode == "phone" then
|
||
return 92
|
||
end
|
||
if state.responsive_mode == "tablet" then
|
||
return 124
|
||
end
|
||
return 148
|
||
end
|
||
|
||
---@param nodes RuntimeNode[]
|
||
---@param selected ShowcaseExample
|
||
local function append_preview_nodes(nodes, selected)
|
||
local mode = preview_mode(selected)
|
||
local base_visible = mode == "base"
|
||
local text_visible = mode == "text"
|
||
local button_visible = mode == "buttons"
|
||
local button_image_visible = mode == "button_images"
|
||
local sprite_visible = mode == "sprites"
|
||
local layout_visible = mode == "layout"
|
||
local radio_visible = mode == "radio"
|
||
local list_visible = mode == "list"
|
||
local particle_visible = mode == "particle"
|
||
local responsive_visible = mode == "responsive"
|
||
|
||
table.insert(nodes, runtime_ui.rect("sample_rect", 16, 48, 70, 44, {
|
||
parent = "preview_panel",
|
||
color = theme.primary,
|
||
radius = 8,
|
||
layer = 25,
|
||
visible = base_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.circle("sample_circle", 104, 42, 54, {
|
||
parent = "preview_panel",
|
||
color = theme.success,
|
||
interactive = true,
|
||
onTap = "demo_anim",
|
||
layer = 25,
|
||
visible = base_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.line("sample_line", 176, 70, 82, 0, {
|
||
parent = "preview_panel",
|
||
color = theme.warning,
|
||
strokeWidth = 4,
|
||
layer = 25,
|
||
visible = base_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.image("sample_image_node", "sample_image", 278, 42, 48, 48, {
|
||
parent = "preview_panel",
|
||
layer = 25,
|
||
visible = base_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.sprite("sample_sprite_node", "sample_image", 340, 42, 48, 48, {
|
||
parent = "preview_panel",
|
||
layer = 25,
|
||
visible = base_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.progress("sample_progress", 16, 112, 180, 16, state.progress, {
|
||
parent = "preview_panel",
|
||
color = theme.success,
|
||
radius = 8,
|
||
layer = 25,
|
||
visible = base_visible
|
||
}))
|
||
local progress_nodes = widgets.labeled_progress("widget_progress", "Widget Progress", 220, 98, 170, 12, state.progress, {
|
||
parent = "preview_panel",
|
||
labelStyle = { parent = "preview_panel", color = theme.muted, fontSize = 12, layer = 25, visible = base_visible },
|
||
color = theme.primary,
|
||
layer = 25,
|
||
visible = base_visible
|
||
})
|
||
runtime_ui.append_all(nodes, progress_nodes)
|
||
|
||
table.insert(nodes, runtime_ui.text("text_plain_title", state.text_variant == "plain" and "Text: 纯文本组件" or "Text: 样式已切换", 16, 46, 260, 24, {
|
||
parent = "preview_panel",
|
||
color = state.text_variant == "plain" and theme.text or theme.warning,
|
||
fontSize = state.text_variant == "plain" and 18 or 20,
|
||
layer = 25,
|
||
visible = text_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.text("text_plain_body", "字段:text / color / fontSize / alpha", 16, 78, 310, 20, {
|
||
parent = "preview_panel",
|
||
color = theme.muted,
|
||
fontSize = 12,
|
||
layer = 25,
|
||
visible = text_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.rect("text_style_badge", 16, 108, 92, 24, {
|
||
parent = "preview_panel",
|
||
color = state.text_variant == "plain" and theme.primary or theme.purple,
|
||
radius = 8,
|
||
layer = 25,
|
||
visible = text_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.text("text_rich_note", "富文本:当前未支持 richText/spans", 124, 111, 240, 18, {
|
||
parent = "preview_panel",
|
||
color = theme.warning,
|
||
fontSize = 12,
|
||
layer = 26,
|
||
visible = text_visible
|
||
}))
|
||
|
||
table.insert(nodes, runtime_ui.text("button_hint", "button: text + background + onTap", 16, 44, 240, 20, {
|
||
parent = "preview_panel",
|
||
color = theme.muted,
|
||
fontSize = 12,
|
||
layer = 25,
|
||
visible = button_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.button("button_primary", "主按钮", 16, 76, 108, 34, "demo_button_primary", {
|
||
parent = "preview_panel",
|
||
color = state.button_active and theme.primary or "#ff475569",
|
||
radius = 10,
|
||
fontSize = 13,
|
||
layer = 26,
|
||
visible = button_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.button("button_secondary", "次按钮", 138, 76, 108, 34, "demo_button_primary", {
|
||
parent = "preview_panel",
|
||
color = "#ff334155",
|
||
radius = 10,
|
||
fontSize = 13,
|
||
layer = 26,
|
||
visible = button_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.button("button_disabled", "禁用态", 260, 76, 108, 34, "noop", {
|
||
parent = "preview_panel",
|
||
color = "#ff1f2937",
|
||
radius = 10,
|
||
fontSize = 13,
|
||
alpha = 0.55,
|
||
interactive = false,
|
||
layer = 26,
|
||
visible = button_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.text("button_state_text", state.button_active and "状态:可点击" or "状态:已置灰", 16, 118, 220, 18, {
|
||
parent = "preview_panel",
|
||
color = theme.warning,
|
||
fontSize = 12,
|
||
layer = 26,
|
||
visible = button_visible
|
||
}))
|
||
|
||
table.insert(nodes, runtime_ui.text("image_button_hint", "button image: normal / pressed / disabled", 16, 44, 300, 20, {
|
||
parent = "preview_panel",
|
||
color = theme.muted,
|
||
fontSize = 12,
|
||
layer = 25,
|
||
visible = button_image_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.button("image_button_normal", {
|
||
text = "按住 Pressed",
|
||
x = 16,
|
||
y = 76,
|
||
w = 116,
|
||
h = 40,
|
||
handler = "demo_button_image_tap",
|
||
parent = "preview_panel",
|
||
asset = "button_normal",
|
||
pressedAsset = "button_pressed",
|
||
disabledAsset = "button_disabled",
|
||
radius = 10,
|
||
fontSize = 12,
|
||
layer = 26,
|
||
visible = button_image_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.button("image_button_toggle", {
|
||
text = state.button_image_enabled and "可切换" or "已禁用",
|
||
x = 144,
|
||
y = 76,
|
||
w = 116,
|
||
h = 40,
|
||
handler = "demo_button_image_tap",
|
||
parent = "preview_panel",
|
||
asset = "button_normal",
|
||
pressedAsset = "button_pressed",
|
||
disabledAsset = "button_disabled",
|
||
radius = 10,
|
||
fontSize = 12,
|
||
interactive = state.button_image_enabled,
|
||
layer = 26,
|
||
visible = button_image_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.button("image_button_disabled", {
|
||
text = "Disabled",
|
||
x = 272,
|
||
y = 76,
|
||
w = 116,
|
||
h = 40,
|
||
handler = "noop",
|
||
parent = "preview_panel",
|
||
asset = "button_normal",
|
||
pressedAsset = "button_pressed",
|
||
disabledAsset = "button_disabled",
|
||
radius = 10,
|
||
fontSize = 12,
|
||
interactive = false,
|
||
layer = 26,
|
||
visible = button_image_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.text("image_button_state_text", state.button_image_enabled and "中间按钮:interactive=true" or "中间按钮:interactive=false,显示 disabledAsset", 16, 122, 360, 18, {
|
||
parent = "preview_panel",
|
||
color = theme.warning,
|
||
fontSize = 12,
|
||
layer = 26,
|
||
visible = button_image_visible
|
||
}))
|
||
|
||
table.insert(nodes, runtime_ui.image("sprite_image_demo", "sample_image", 24, 54, 64, 64, {
|
||
parent = "preview_panel",
|
||
layer = 25,
|
||
visible = sprite_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.sprite("sprite_sprite_demo", "sample_image", 118, 54, 64, 64, {
|
||
parent = "preview_panel",
|
||
layer = 26,
|
||
visible = sprite_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.rect("sprite_frame_demo", 212, 54, 64, 64, {
|
||
parent = "preview_panel",
|
||
color = state.sprite_variant == "image" and theme.primary or theme.purple,
|
||
radius = 8,
|
||
layer = 25,
|
||
visible = sprite_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.text("sprite_label_demo", "image / sprite 都通过 manifest key 加载", 20, 120, 300, 18, {
|
||
parent = "preview_panel",
|
||
color = theme.muted,
|
||
fontSize = 12,
|
||
layer = 26,
|
||
visible = sprite_visible
|
||
}))
|
||
|
||
table.insert(nodes, runtime_ui.panel("layout_canvas", 16, 42, 360, 86, {
|
||
parent = "preview_panel",
|
||
color = "#ff111827",
|
||
radius = 10,
|
||
layer = 24,
|
||
visible = layout_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.rect("layout_chip_1", 34, 70, 58, 28, {
|
||
parent = "preview_panel",
|
||
color = theme.primary,
|
||
radius = 8,
|
||
layer = 26,
|
||
visible = layout_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.rect("layout_chip_2", 108, 70, 58, 28, {
|
||
parent = "preview_panel",
|
||
color = theme.success,
|
||
radius = 8,
|
||
layer = 26,
|
||
visible = layout_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.rect("layout_chip_3", 182, 70, 58, 28, {
|
||
parent = "preview_panel",
|
||
color = theme.warning,
|
||
radius = 8,
|
||
layer = 26,
|
||
visible = layout_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.rect("layout_chip_4", 256, 70, 58, 28, {
|
||
parent = "preview_panel",
|
||
color = theme.purple,
|
||
radius = 8,
|
||
layer = 26,
|
||
visible = layout_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.text("layout_label", "layout.row:gap + align + margin", 28, 112, 280, 18, {
|
||
parent = "preview_panel",
|
||
color = theme.muted,
|
||
fontSize = 12,
|
||
layer = 26,
|
||
visible = layout_visible
|
||
}))
|
||
|
||
table.insert(nodes, runtime_ui.text("radio_title", "RadioGroup: Lua 组合单选项", 16, 44, 260, 20, {
|
||
parent = "preview_panel",
|
||
color = theme.muted,
|
||
fontSize = 12,
|
||
layer = 25,
|
||
visible = radio_visible
|
||
}))
|
||
local radio_options = {
|
||
{ key = "audio", label = "Audio", y = 72 },
|
||
{ key = "spine", label = "Spine", y = 96 },
|
||
{ key = "Lua", label = "Lua", y = 120 }
|
||
}
|
||
for _, option in ipairs(radio_options) do
|
||
local selected_radio = state.radio_selected == option.key
|
||
table.insert(nodes, runtime_ui.circle("radio_" .. string.lower(option.key) .. "_dot", 18, option.y, 14, {
|
||
parent = "preview_panel",
|
||
color = selected_radio and theme.primary or "#ff475569",
|
||
layer = 25,
|
||
visible = radio_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.text("radio_" .. string.lower(option.key) .. "_label", option.label, 42, option.y - 2, 120, 18, {
|
||
parent = "preview_panel",
|
||
color = selected_radio and theme.text or theme.muted,
|
||
fontSize = 12,
|
||
layer = 25,
|
||
visible = radio_visible
|
||
}))
|
||
end
|
||
table.insert(nodes, runtime_ui.text("radio_value_text", "当前选择:" .. state.radio_selected, 190, 96, 160, 18, {
|
||
parent = "preview_panel",
|
||
color = theme.warning,
|
||
fontSize = 12,
|
||
layer = 25,
|
||
visible = radio_visible
|
||
}))
|
||
|
||
local list_horizontal = state.list_axis == "horizontal"
|
||
table.insert(nodes, runtime_ui.list_view("list_panel", 16, 42, 320, 72, {
|
||
parent = "preview_panel",
|
||
color = "#ff111827",
|
||
radius = 10,
|
||
contentWidth = list_horizontal and 650 or 430,
|
||
contentHeight = list_horizontal and 90 or 150,
|
||
scrollX = state.list_scroll_x,
|
||
scrollY = state.list_scroll_y,
|
||
virtualized = true,
|
||
cacheExtent = 24,
|
||
inertia = true,
|
||
onScroll = "demo_list_scrolled",
|
||
scrollbarThumbColor = theme.warning,
|
||
scrollbarTrackColor = "#33475569",
|
||
scrollbarThickness = 6,
|
||
layer = 24,
|
||
visible = list_visible
|
||
}))
|
||
local list_items = {
|
||
{ key = "Lua", label = "Lua 脚本层" },
|
||
{ key = "Runtime", label = "Runtime 协议层" },
|
||
{ key = "Flame", label = "Flame 渲染层" },
|
||
{ key = "Diff", label = "Diff 更新流" },
|
||
{ key = "Command", label = "Command 动作流" }
|
||
}
|
||
for index, item in ipairs(list_items) do
|
||
local selected_item = state.list_selected == item.key
|
||
local row_x = list_horizontal and (8 + (index - 1) * 126) or 8
|
||
local row_y = list_horizontal and 18 or (8 + (index - 1) * 28)
|
||
local row_w = list_horizontal and 116 or 292
|
||
table.insert(nodes, runtime_ui.button("list_row_" .. index, "", row_x, row_y, row_w, 24, "demo_list_pick_" .. index, {
|
||
parent = "list_panel",
|
||
color = selected_item and theme.primary or "#ff1e293b",
|
||
radius = 7,
|
||
layer = 25,
|
||
visible = list_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.text("list_row_" .. index .. "_text", item.label, 12, 4, 180, 16, {
|
||
parent = "list_row_" .. index,
|
||
color = theme.text,
|
||
fontSize = 11,
|
||
textAlign = "left",
|
||
layer = 26,
|
||
visible = list_visible
|
||
}))
|
||
end
|
||
table.insert(nodes, runtime_ui.text("list_value_text", state.list_axis .. " scrollX=" .. tostring(state.list_scroll_x) .. " scrollY=" .. tostring(state.list_scroll_y), 16, 120, 300, 16, {
|
||
parent = "preview_panel",
|
||
color = theme.warning,
|
||
fontSize = 11,
|
||
layer = 26,
|
||
visible = list_visible
|
||
}))
|
||
|
||
table.insert(nodes, runtime_ui.particle("particle_burst", 32, 42, 120, 92, {
|
||
parent = "preview_panel",
|
||
preset = "burst",
|
||
count = 42,
|
||
duration = 0.85,
|
||
color = "#ffffcc33",
|
||
colorTo = "#00ffcc33",
|
||
radius = 2.8,
|
||
speedMin = 60,
|
||
speedMax = 180,
|
||
gravityY = 90,
|
||
spread = 360,
|
||
autoRemove = false,
|
||
layer = 26,
|
||
visible = particle_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.particle("particle_trail", 168, 46, 120, 86, {
|
||
parent = "preview_panel",
|
||
preset = "trail",
|
||
count = 24,
|
||
duration = 0.7,
|
||
color = "#ff38bdf8",
|
||
radius = 2.2,
|
||
speedMin = 20,
|
||
speedMax = 80,
|
||
autoRemove = false,
|
||
layer = 26,
|
||
visible = particle_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.particle("particle_snow", 304, 34, 120, 104, {
|
||
parent = "preview_panel",
|
||
preset = "snow",
|
||
count = 56,
|
||
duration = 8,
|
||
color = "#ccffffff",
|
||
radius = 1.5,
|
||
autoRemove = false,
|
||
fadeOut = false,
|
||
layer = 26,
|
||
visible = particle_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.text("particle_label", "particle: burst / trail / snow", 16, 120, 280, 18, {
|
||
parent = "preview_panel",
|
||
color = theme.warning,
|
||
fontSize = 11,
|
||
layer = 27,
|
||
visible = particle_visible
|
||
}))
|
||
|
||
local width = responsive_device_width()
|
||
table.insert(nodes, runtime_ui.text("responsive_info", responsive_text(), 16, 112, 390, 18, {
|
||
parent = "preview_panel",
|
||
color = theme.muted,
|
||
fontSize = 10,
|
||
layer = 26,
|
||
visible = responsive_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.rect("responsive_device", 172, 44, width, 62, {
|
||
parent = "preview_panel",
|
||
color = "#ff1d4ed8",
|
||
radius = 10,
|
||
layer = 26,
|
||
visible = responsive_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.rect("responsive_sidebar", 184, 56, width * 0.26, 38, {
|
||
parent = "preview_panel",
|
||
color = theme.primary,
|
||
radius = 6,
|
||
layer = 27,
|
||
visible = responsive_visible
|
||
}))
|
||
table.insert(nodes, runtime_ui.rect("responsive_content", 192 + width * 0.26, 56, width * 0.56, 38, {
|
||
parent = "preview_panel",
|
||
color = theme.success,
|
||
radius = 6,
|
||
layer = 27,
|
||
visible = responsive_visible
|
||
}))
|
||
end
|
||
|
||
---@return RuntimeNode[]
|
||
function ui.create_nodes()
|
||
local nodes = {}
|
||
local selected = examples.find(state.selected_example)
|
||
local m = metrics()
|
||
|
||
table.insert(nodes, runtime_ui.rect("app_bg", 0, 0, theme.screen_width, theme.screen_height, {
|
||
color = theme.background,
|
||
layer = 0
|
||
}))
|
||
table.insert(nodes, runtime_ui.text("app_title", i18n.t("app_title"), 24, 18, 360, 34, styles.title))
|
||
table.insert(nodes, runtime_ui.text("app_subtitle", i18n.t("app_subtitle"), 24, 52, 660, 24, styles.label))
|
||
|
||
table.insert(nodes, runtime_ui.panel("example_list_panel", m.menu_x, m.menu_y, m.menu_w, m.content_h, styles.card))
|
||
table.insert(nodes, widgets.section_title("example_list_title", {
|
||
text = i18n.t("examples_title"),
|
||
x = 16,
|
||
y = 14,
|
||
w = m.menu_w - 32,
|
||
h = 24,
|
||
parent = "example_list_panel",
|
||
color = theme.text,
|
||
fontSize = 20,
|
||
layer = 25
|
||
}))
|
||
local menu_y = 44
|
||
local last_group = nil
|
||
for _, example in ipairs(examples.all()) do
|
||
local group = i18n.example_group(example)
|
||
if group ~= last_group then
|
||
append_menu_group(nodes, group, menu_y, m)
|
||
menu_y = menu_y + 15
|
||
last_group = group
|
||
end
|
||
append_menu_item(nodes, example, menu_y, m)
|
||
menu_y = menu_y + 32
|
||
end
|
||
|
||
table.insert(nodes, runtime_ui.panel("detail_panel", m.detail_x, m.detail_y, m.detail_w, m.content_h, styles.card))
|
||
runtime_ui.append_all(nodes, widgets.panel_header("detail", {
|
||
eyebrow = i18n.example_group(selected) .. " / " .. i18n.example_field(selected, "category"),
|
||
title = i18n.example_field(selected, "title"),
|
||
summary = i18n.example_field(selected, "summary"),
|
||
x = 20,
|
||
y = 16,
|
||
w = m.detail_w - 40,
|
||
parent = "detail_panel",
|
||
layer = 25,
|
||
gap = 4,
|
||
eyebrowHeight = 20,
|
||
titleHeight = 30,
|
||
summaryHeight = 24,
|
||
eyebrowId = "detail_category",
|
||
titleId = "detail_title",
|
||
summaryId = "detail_summary",
|
||
eyebrowStyle = { color = theme.primary, fontSize = 13 },
|
||
titleStyle = { color = theme.text, fontSize = 21 },
|
||
summaryStyle = { color = theme.muted, fontSize = 12 }
|
||
}))
|
||
|
||
runtime_ui.append_all(nodes, widgets.tabs("detail_tab", {
|
||
{ id = "detail_tab_code", key = "code", text = i18n.t("tab_code"), handler = "detail_tab_code" },
|
||
{ id = "detail_tab_params", key = "params", text = i18n.t("tab_params"), handler = "detail_tab_params" }
|
||
}, {
|
||
x = m.detail_x + 20,
|
||
y = m.detail_y + 104,
|
||
itemWidth = 72,
|
||
itemHeight = 24,
|
||
gap = 6,
|
||
selected = state.detail_tab,
|
||
activeColor = theme.primary,
|
||
inactiveColor = "#ff334155",
|
||
radius = 8,
|
||
fontSize = 11,
|
||
interactive = true,
|
||
layer = 90
|
||
}))
|
||
table.insert(nodes, widgets.text_button("detail_copy", {
|
||
text = detail_copy_text(),
|
||
x = m.detail_x + m.detail_w - 116,
|
||
y = m.detail_y + 104,
|
||
w = 96,
|
||
h = 24,
|
||
handler = "copy_detail",
|
||
color = "#ff475569",
|
||
radius = 8,
|
||
fontSize = 11,
|
||
interactive = true,
|
||
layer = 90
|
||
}))
|
||
|
||
table.insert(nodes, runtime_ui.list_view("code_panel", m.detail_x + 20, m.detail_y + 132, m.detail_w - 40, m.code_h + 18, {
|
||
color = "#ff020617",
|
||
radius = 12,
|
||
contentWidth = detail_content_width(m),
|
||
contentHeight = detail_content_height(selected, m),
|
||
scrollX = 0,
|
||
scrollY = 0,
|
||
paddingLeft = detail_padding_left,
|
||
paddingTop = detail_padding_top,
|
||
paddingRight = detail_padding_right,
|
||
paddingBottom = detail_padding_bottom,
|
||
virtualized = false,
|
||
inertia = true,
|
||
scrollbarThumbColor = theme.primary,
|
||
scrollbarTrackColor = "#33475569",
|
||
scrollbarThickness = 5,
|
||
layer = 20
|
||
}))
|
||
table.insert(nodes, runtime_ui.text("detail_code", detail_body(selected), detail_text_x(selected, m), detail_text_y(selected, m), detail_text_width(selected, m), detail_text_height(selected), {
|
||
parent = "code_panel",
|
||
color = "#ffe2e8f0",
|
||
fontSize = 11,
|
||
textAlign = "left",
|
||
layer = 25
|
||
}))
|
||
|
||
runtime_ui.append_all(nodes, detail_action_nodes(selected, m))
|
||
|
||
table.insert(nodes, runtime_ui.panel("preview_panel", m.detail_x + 20, m.preview_y, m.detail_w - 40, m.preview_h, {
|
||
color = "#ff0b1120",
|
||
radius = 12,
|
||
layer = 20
|
||
}))
|
||
table.insert(nodes, widgets.section_title("preview_title", {
|
||
text = i18n.t("preview_title"),
|
||
x = 14,
|
||
y = 10,
|
||
w = 220,
|
||
h = 22,
|
||
parent = "preview_panel",
|
||
color = theme.text,
|
||
fontSize = 15,
|
||
layer = 25
|
||
}))
|
||
append_preview_nodes(nodes, selected)
|
||
|
||
table.insert(nodes, runtime_ui.text("status_text", state.status, 24, m.status_y, theme.screen_width - 48, 26, {
|
||
color = theme.warning,
|
||
fontSize = 14,
|
||
layer = 50
|
||
}))
|
||
|
||
return nodes
|
||
end
|
||
|
||
---@param text string
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.status_updates(text)
|
||
state.status = text
|
||
return { runtime_ui.text_update("status_text", text) }
|
||
end
|
||
|
||
---@param selected ShowcaseExample
|
||
---@return RuntimeNodeUpdate[]
|
||
local function preview_visibility_updates(selected)
|
||
local updates = {}
|
||
local mode = preview_mode(selected)
|
||
local groups = {
|
||
{ ids = base_preview_ids, visible = mode == "base" },
|
||
{ ids = text_preview_ids, visible = mode == "text" },
|
||
{ ids = button_preview_ids, visible = mode == "buttons" },
|
||
{ ids = button_image_preview_ids, visible = mode == "button_images" },
|
||
{ ids = sprite_preview_ids, visible = mode == "sprites" },
|
||
{ ids = layout_preview_ids, visible = mode == "layout" },
|
||
{ ids = radio_preview_ids, visible = mode == "radio" },
|
||
{ ids = list_preview_ids, visible = mode == "list" },
|
||
{ ids = particle_preview_ids, visible = mode == "particle" },
|
||
{ ids = responsive_preview_ids, visible = mode == "responsive" }
|
||
}
|
||
for _, group in ipairs(groups) do
|
||
for _, id in ipairs(group.ids) do
|
||
table.insert(updates, runtime_ui.visible_update(id, group.visible))
|
||
end
|
||
end
|
||
return updates
|
||
end
|
||
|
||
---@param selected ShowcaseExample
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.example_updates(selected)
|
||
local m = metrics()
|
||
local updates = {
|
||
runtime_ui.text_update("detail_category", i18n.example_group(selected) .. " / " .. i18n.example_field(selected, "category")),
|
||
runtime_ui.text_update("detail_title", i18n.example_field(selected, "title")),
|
||
runtime_ui.text_update("detail_summary", i18n.example_field(selected, "summary")),
|
||
runtime_ui.text_update("detail_code", detail_body(selected)),
|
||
runtime_ui.update("detail_code", {
|
||
x = detail_text_x(selected, m),
|
||
y = detail_text_y(selected, m),
|
||
width = detail_text_width(selected, m),
|
||
height = detail_text_height(selected),
|
||
textAlign = "left"
|
||
}),
|
||
runtime_ui.update("code_panel", {
|
||
contentWidth = detail_content_width(m),
|
||
contentHeight = detail_content_height(selected, m),
|
||
scrollX = 0,
|
||
scrollY = 0
|
||
}),
|
||
runtime_ui.update("detail_tab_code", {
|
||
text = i18n.t("tab_code"),
|
||
onTap = "detail_tab_code",
|
||
interactive = true,
|
||
layer = 90,
|
||
color = state.detail_tab == "code" and theme.primary or "#ff334155"
|
||
}),
|
||
runtime_ui.update("detail_tab_params", {
|
||
text = i18n.t("tab_params"),
|
||
onTap = "detail_tab_params",
|
||
interactive = true,
|
||
layer = 90,
|
||
color = state.detail_tab == "params" and theme.primary or "#ff334155"
|
||
}),
|
||
runtime_ui.update("detail_copy", {
|
||
text = detail_copy_text(),
|
||
onTap = "copy_detail",
|
||
interactive = true,
|
||
layer = 90
|
||
}),
|
||
runtime_ui.text_update("status_text", i18n.t("selected_prefix") .. i18n.example_field(selected, "title"))
|
||
}
|
||
|
||
for _, example in ipairs(examples.all()) do
|
||
table.insert(updates, runtime_ui.update("example_" .. example.id, {
|
||
text = i18n.example_label(example),
|
||
color = example.id == selected.id and theme.primary or "#ff1e293b"
|
||
}))
|
||
end
|
||
|
||
for index = 1, 3 do
|
||
local action = selected.actions[index]
|
||
if action ~= nil then
|
||
table.insert(updates, runtime_ui.update("detail_action_" .. index, {
|
||
text = i18n.action_text(action),
|
||
onTap = action.handler,
|
||
visible = true,
|
||
color = index == 1 and "#ff2563eb" or "#ff334155"
|
||
}))
|
||
else
|
||
table.insert(updates, runtime_ui.update("detail_action_" .. index, {
|
||
text = "",
|
||
onTap = "noop",
|
||
visible = false
|
||
}))
|
||
end
|
||
end
|
||
|
||
local preview_updates = preview_visibility_updates(selected)
|
||
for _, update in ipairs(preview_updates) do
|
||
table.insert(updates, update)
|
||
end
|
||
table.insert(updates, runtime_ui.update("status_text", { width = m.screen_w - 48 }))
|
||
return updates
|
||
end
|
||
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.locale_updates()
|
||
local selected = examples.find(state.selected_example)
|
||
local updates = {
|
||
runtime_ui.text_update("app_title", i18n.t("app_title")),
|
||
runtime_ui.text_update("app_subtitle", i18n.t("app_subtitle")),
|
||
runtime_ui.text_update("example_list_title", i18n.t("examples_title")),
|
||
runtime_ui.text_update("preview_title", i18n.t("preview_title")),
|
||
runtime_ui.text_update("responsive_info", responsive_text())
|
||
}
|
||
local detail_updates = ui.example_updates(selected)
|
||
for _, update in ipairs(detail_updates) do
|
||
table.insert(updates, update)
|
||
end
|
||
return updates
|
||
end
|
||
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.responsive_updates()
|
||
local width = responsive_device_width()
|
||
return {
|
||
runtime_ui.text_update("responsive_info", responsive_text()),
|
||
runtime_ui.update("responsive_device", { width = width }),
|
||
runtime_ui.update("responsive_sidebar", { width = width * 0.26 }),
|
||
runtime_ui.update("responsive_content", {
|
||
x = 192 + width * 0.26,
|
||
width = width * 0.56
|
||
})
|
||
}
|
||
end
|
||
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.text_updates()
|
||
return {
|
||
runtime_ui.update("text_plain_title", {
|
||
text = state.text_variant == "plain" and "Text: 纯文本组件" or "Text: 样式已切换",
|
||
color = state.text_variant == "plain" and theme.text or theme.warning,
|
||
fontSize = state.text_variant == "plain" and 18 or 20
|
||
}),
|
||
runtime_ui.update("text_style_badge", {
|
||
color = state.text_variant == "plain" and theme.primary or theme.purple
|
||
})
|
||
}
|
||
end
|
||
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.radio_updates()
|
||
local updates = {
|
||
runtime_ui.text_update("radio_value_text", "当前选择:" .. state.radio_selected)
|
||
}
|
||
local options = { "audio", "spine", "lua" }
|
||
for _, key in ipairs(options) do
|
||
local value = key == "lua" and "Lua" or key
|
||
local selected = state.radio_selected == value
|
||
table.insert(updates, runtime_ui.update("radio_" .. key .. "_dot", {
|
||
color = selected and theme.primary or "#ff475569"
|
||
}))
|
||
table.insert(updates, runtime_ui.update("radio_" .. key .. "_label", {
|
||
color = selected and theme.text or theme.muted
|
||
}))
|
||
end
|
||
return updates
|
||
end
|
||
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.list_updates()
|
||
local horizontal = state.list_axis == "horizontal"
|
||
local updates = {
|
||
runtime_ui.update("list_panel", {
|
||
contentWidth = horizontal and 650 or 430,
|
||
contentHeight = horizontal and 90 or 150,
|
||
scrollX = state.list_scroll_x,
|
||
scrollY = state.list_scroll_y
|
||
}),
|
||
runtime_ui.text_update("list_value_text", state.list_axis .. " scrollX=" .. tostring(state.list_scroll_x) .. " scrollY=" .. tostring(state.list_scroll_y))
|
||
}
|
||
local items = { "Lua", "Runtime", "Flame", "Diff", "Command" }
|
||
for index, key in ipairs(items) do
|
||
local row_x = horizontal and (8 + (index - 1) * 126) or 8
|
||
local row_y = horizontal and 18 or (8 + (index - 1) * 28)
|
||
local row_w = horizontal and 116 or 292
|
||
table.insert(updates, runtime_ui.update("list_row_" .. index, {
|
||
x = row_x,
|
||
y = row_y,
|
||
width = row_w,
|
||
color = state.list_selected == key and theme.primary or "#ff1e293b"
|
||
}))
|
||
table.insert(updates, runtime_ui.update("list_row_" .. index .. "_text", {
|
||
textAlign = "left"
|
||
}))
|
||
end
|
||
return updates
|
||
end
|
||
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.button_updates()
|
||
return {
|
||
runtime_ui.update("button_primary", {
|
||
color = state.button_active and theme.primary or "#ff475569",
|
||
alpha = state.button_active and 1 or 0.65
|
||
}),
|
||
runtime_ui.text_update("button_state_text", state.button_active and "状态:可点击" or "状态:已置灰")
|
||
}
|
||
end
|
||
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.button_image_updates()
|
||
return {
|
||
runtime_ui.update("image_button_toggle", {
|
||
text = state.button_image_enabled and "可切换" or "已禁用",
|
||
interactive = state.button_image_enabled
|
||
}),
|
||
runtime_ui.text_update("image_button_state_text", state.button_image_enabled and "中间按钮:interactive=true" or "中间按钮:interactive=false,显示 disabledAsset")
|
||
}
|
||
end
|
||
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.sprite_updates()
|
||
return {
|
||
runtime_ui.update("sprite_frame_demo", {
|
||
color = state.sprite_variant == "image" and theme.primary or theme.purple,
|
||
rotation = state.sprite_variant == "image" and 0 or 0.12
|
||
}),
|
||
runtime_ui.text_update("sprite_label_demo", state.sprite_variant == "image" and "当前:image 原图节点" or "当前:sprite 可动画节点")
|
||
}
|
||
end
|
||
|
||
---@param mode string
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.layout_updates(mode)
|
||
if mode == "column" then
|
||
return {
|
||
runtime_ui.update("layout_chip_1", { x = 44, y = 46 }),
|
||
runtime_ui.update("layout_chip_2", { x = 44, y = 74 }),
|
||
runtime_ui.update("layout_chip_3", { x = 44, y = 102 }),
|
||
runtime_ui.update("layout_chip_4", { x = 44, y = 130 }),
|
||
runtime_ui.text_update("layout_label", "layout.column:纵向排列 + gap")
|
||
}
|
||
end
|
||
if mode == "box" then
|
||
return {
|
||
runtime_ui.update("layout_chip_1", { x = 44, y = 54 }),
|
||
runtime_ui.update("layout_chip_2", { x = 116, y = 54 }),
|
||
runtime_ui.update("layout_chip_3", { x = 44, y = 90 }),
|
||
runtime_ui.update("layout_chip_4", { x = 116, y = 90 }),
|
||
runtime_ui.text_update("layout_label", "layout.box:2 行 × 2 列网格")
|
||
}
|
||
end
|
||
return {
|
||
runtime_ui.update("layout_chip_1", { x = 34, y = 70 }),
|
||
runtime_ui.update("layout_chip_2", { x = 108, y = 70 }),
|
||
runtime_ui.update("layout_chip_3", { x = 182, y = 70 }),
|
||
runtime_ui.update("layout_chip_4", { x = 256, y = 70 }),
|
||
runtime_ui.text_update("layout_label", "layout.row:横向排列 + gap")
|
||
}
|
||
end
|
||
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.progress_updates()
|
||
return {
|
||
runtime_ui.update("sample_progress", { value = state.progress }),
|
||
runtime_ui.update("widget_progress", { value = state.progress })
|
||
}
|
||
end
|
||
|
||
---@return RuntimeNodeUpdate[]
|
||
function ui.visibility_updates()
|
||
return {
|
||
runtime_ui.visible_update("sample_rect", state.visible),
|
||
runtime_ui.alpha_update("sample_circle", state.visible and 1 or 0.35)
|
||
}
|
||
end
|
||
|
||
---@return RuntimeNode[]
|
||
function ui.dialog_nodes()
|
||
return widgets.dialog("sample_dialog", "Widget Dialog", "这是 Lua 侧组合组件:overlay + card + text + button。输出仍然只是普通 RuntimeNode。", 150, 190, 420, 230, {
|
||
screenWidth = theme.screen_width,
|
||
screenHeight = theme.screen_height,
|
||
buttons = {
|
||
{ text = "播放音效", handler = "demo_sound" },
|
||
{ text = "关闭", handler = "close_dialog", color = theme.danger }
|
||
}
|
||
})
|
||
end
|
||
|
||
---@return { id: string }[]
|
||
function ui.dialog_removes()
|
||
return {
|
||
{ id = "sample_dialog_overlay" },
|
||
{ id = "sample_dialog" },
|
||
{ id = "sample_dialog_title" },
|
||
{ id = "sample_dialog_message" },
|
||
{ id = "sample_dialog_button_1" },
|
||
{ id = "sample_dialog_button_2" }
|
||
}
|
||
end
|
||
|
||
---@return RuntimeNode[]
|
||
function ui.temp_nodes()
|
||
local m = metrics()
|
||
return {
|
||
runtime_ui.rect("temp_node", m.detail_x + 270, m.preview_y + 98, 90, 28, {
|
||
color = theme.warning,
|
||
radius = 8,
|
||
layer = 45
|
||
}),
|
||
runtime_ui.text("temp_node_text", "临时节点", m.detail_x + 284, m.preview_y + 103, 72, 18, {
|
||
color = "#ff111827",
|
||
fontSize = 13,
|
||
layer = 46
|
||
})
|
||
}
|
||
end
|
||
|
||
return ui
|