local state = runtime.import("state") local ui = runtime.import("ui") ---@type RuntimeUi local runtime_ui = runtime.import("runtime_ui") local examples = runtime.import("examples") local i18n = runtime.import("i18n") ---@type RuntimeCommands local commands = runtime.import("runtime_commands") ---@type RuntimeWidgets local widgets = runtime.import("runtime_widgets") local theme = runtime.import("theme") widgets.configure({ primary = theme.primary, secondary = "#ff475569", success = theme.success, overlay = "#99000000", surface = "#ff1e293b", surfaceAlt = theme.border, card = "#ee111827", text = theme.text, muted = theme.muted, progress = theme.success, transparent = "#00000000" }) function smoke_test(ctx) return ctx ~= nil and ctx.runtimeApiVersion ~= nil and examples.all ~= nil and i18n.t ~= nil and ui.create_nodes ~= nil and widgets.dialog ~= nil and commands.sequence ~= nil end local function apply_context(ctx) if ctx == nil then return end i18n.apply_context(ctx) if ctx.screen ~= nil then state.screen_width = ctx.screen.width or state.screen_width state.screen_height = ctx.screen.height or state.screen_height end if ctx.viewport ~= nil then state.viewport_width = ctx.viewport.width or state.viewport_width state.viewport_height = ctx.viewport.height or state.viewport_height state.viewport_scale = ctx.viewport.scaleX or state.viewport_scale end end function init(ctx) apply_context(ctx) state.status = i18n.t("status_ready") return { render = { creates = ui.create_nodes() }, ui = {}, commands = {} } end ---@param text string ---@return RuntimeDiff local function status_only(text) return { ui = { updates = ui.status_updates(text) } } end ---@return RuntimeDiff local function handle_anim() return { ui = { updates = ui.status_updates("执行 sequence + parallel:移动、缩放、旋转、淡入淡出。") }, commands = { commands.sequence({ commands.move_path("sample_circle", { { x = 104, y = 42 }, { x = 180, y = 44 }, { x = 248, y = 78 } }, { duration = 0.75, group = "demo_anim" }), commands.parallel({ commands.scale_to("sample_circle", 1.45, { duration = 0.35, group = "demo_anim" }), commands.rotate_to("sample_circle", 6.28, { duration = 0.35, group = "demo_anim" }), commands.fade_to("sample_circle", 0.45, { duration = 0.35, group = "demo_anim" }) }, { group = "demo_anim" }), commands.parallel({ commands.move_to("sample_circle", 104, 42, { duration = 0.55, group = "demo_anim" }), commands.scale_to("sample_circle", 1, { duration = 0.55, group = "demo_anim" }), commands.fade_to("sample_circle", 1, { duration = 0.55, group = "demo_anim" }) }, { group = "demo_anim" }) }, { id = "demo_anim_sequence", group = "demo_anim", onComplete = "demo_anim_done" }) } } end ---@return RuntimeDiff local function handle_cancel() return { ui = { updates = ui.status_updates("已发送 cancel_commands:group = demo_anim。") }, commands = { commands.cancel_group("demo_anim") } } end ---@return RuntimeDiff local function handle_progress() state.progress = state.progress + 0.15 if state.progress > 1 then state.progress = 0.05 end local updates = ui.progress_updates() local status_updates = ui.status_updates("通过 NodeDiff.update 更新 progress.value = " .. tostring(state.progress)) for _, update in ipairs(status_updates) do table.insert(updates, update) end return { render = { updates = updates } } end ---@return RuntimeDiff local function handle_visibility() state.visible = not state.visible local updates = ui.visibility_updates() local status_updates = ui.status_updates("通过 visible / alpha 更新节点显示状态。") for _, update in ipairs(status_updates) do table.insert(updates, update) end return { render = { updates = updates } } end ---@return RuntimeDiff local function handle_dialog() if state.dialog_open then return status_only("Dialog 已经打开;点击关闭按钮移除它。") end state.dialog_open = true return { ui = { creates = ui.dialog_nodes(), updates = ui.status_updates("创建 widgets.dialog:Lua 组合普通 RuntimeNode。") } } end ---@return RuntimeDiff local function handle_close_dialog() state.dialog_open = false return { ui = { removes = ui.dialog_removes(), updates = ui.status_updates("通过 GameDiff.removes 移除 Dialog 节点。") } } end ---@return RuntimeDiff local function handle_temp() if state.temp_node_visible then state.temp_node_visible = false return { render = { removes = { { id = "temp_node" }, { id = "temp_node_text" } } }, ui = { updates = ui.status_updates("通过 GameDiff.removes 移除临时节点。") } } end state.temp_node_visible = true return { render = { creates = ui.temp_nodes() }, ui = { updates = ui.status_updates("创建临时节点,并用 delay + remove_node 自动删除。") }, commands = { commands.sequence({ commands.delay(1.2, { id = "temp_delay", group = "temp" }), commands.remove_node("temp_node", { group = "temp" }), commands.remove_node("temp_node_text", { group = "temp", onComplete = "temp_removed" }) }, { group = "temp" }) } } end ---@return RuntimeDiff local function handle_sound() return { ui = { updates = ui.status_updates("播放 manifest 资源 click:commands.play_sound('click')。") }, commands = { commands.play_sound("click", { volume = 0.8, onComplete = "sound_done" }) } } end ---@return RuntimeDiff local function handle_bgm() if state.bgm_state == "stopped" then state.bgm_state = "playing" return { ui = { updates = ui.status_updates("启动 BGM channel=demo:play_bgm,可继续点击切换暂停/恢复/停止。") }, commands = { commands.play_bgm("click", { channel = "demo", loop = true, volume = 0.25 }) } } end if state.bgm_state == "playing" then state.bgm_state = "paused" return { ui = { updates = ui.status_updates("暂停 BGM:pause_bgm('demo')。") }, commands = { commands.pause_bgm("demo") } } end if state.bgm_state == "paused" then state.bgm_state = "resumed" return { ui = { updates = ui.status_updates("恢复 BGM:resume_bgm('demo')。") }, commands = { commands.resume_bgm("demo") } } end state.bgm_state = "stopped" return { ui = { updates = ui.status_updates("停止 BGM:stop_bgm('demo')。") }, commands = { commands.stop_bgm("demo") } } end ---@return RuntimeDiff local function handle_text_change() state.text_variant = state.text_variant == "plain" and "styled" or "plain" local updates = ui.text_updates() local status_updates = ui.status_updates("Text 当前是纯文本协议;富文本 richText/spans 尚未支持。") for _, update in ipairs(status_updates) do table.insert(updates, update) end return { ui = { updates = updates } } end ---@return RuntimeDiff local function handle_text_style() state.text_variant = state.text_variant == "plain" and "styled" or "plain" local updates = ui.text_updates() local status_updates = ui.status_updates("已切换 text 的 color/fontSize;不是富文本。") for _, update in ipairs(status_updates) do table.insert(updates, update) end return { ui = { updates = updates } } end ---@return RuntimeDiff local function handle_button_primary() return { ui = { updates = ui.status_updates("按钮点击:RuntimeEvent.tap -> Lua handler。") }, commands = { commands.play_sound("click", { volume = 0.55 }) } } end ---@return RuntimeDiff local function handle_button_toggle() state.button_active = not state.button_active local updates = ui.button_updates() local status_updates = ui.status_updates(state.button_active and "按钮恢复可点击状态。" or "按钮切换为置灰状态。") for _, update in ipairs(status_updates) do table.insert(updates, update) end return { ui = { updates = updates } } end ---@return RuntimeDiff local function handle_button_image_tap() return { ui = { updates = ui.status_updates("图片按钮点击:按下时显示 pressedAsset,松开回到 asset。") }, commands = { commands.play_sound("click", { volume = 0.45 }) } } end ---@return RuntimeDiff local function handle_button_image_toggle() state.button_image_enabled = not state.button_image_enabled local updates = ui.button_image_updates() local status_updates = ui.status_updates(state.button_image_enabled and "图片按钮恢复 interactive=true。" or "图片按钮设为 interactive=false,显示 disabledAsset。") for _, update in ipairs(status_updates) do table.insert(updates, update) end return { ui = { updates = updates } } end ---@return RuntimeDiff local function handle_sprite_anim() return { ui = { updates = ui.status_updates("精灵节点执行 move_to + rotate_to。") }, commands = { commands.parallel({ commands.move_to("sprite_sprite_demo", 154, 54, { duration = 0.35, group = "sprite_demo" }), commands.rotate_to("sprite_sprite_demo", 6.28, { duration = 0.35, group = "sprite_demo" }) }, { group = "sprite_demo", onComplete = "sprite_anim_done" }) } } end ---@return RuntimeDiff local function handle_sprite_style() state.sprite_variant = state.sprite_variant == "image" and "sprite" or "image" local updates = ui.sprite_updates() local status_updates = ui.status_updates("切换 sprite 示例样式:" .. state.sprite_variant) for _, update in ipairs(status_updates) do table.insert(updates, update) end return { ui = { updates = updates } } end ---@param value string ---@return RuntimeDiff local function handle_radio(value) state.radio_selected = value local updates = ui.radio_updates() local status_updates = ui.status_updates("RadioGroup 选择:" .. value) for _, update in ipairs(status_updates) do table.insert(updates, update) end return { ui = { updates = updates } } end ---@param index integer ---@return RuntimeDiff local function handle_list_pick(index) local items = { "Lua", "Runtime", "Flame", "Diff", "Command" } if index < 1 then index = #items elseif index > #items then index = 1 end state.list_selected = items[index] if state.list_axis == "horizontal" then state.list_scroll_x = math.min(math.max((index - 2) * 126, 0), 330) state.list_scroll_y = 0 else state.list_scroll_x = 0 state.list_scroll_y = math.min(math.max((index - 2) * 28, 0), 78) end local updates = ui.list_updates() local status_updates = ui.status_updates("ListView 选中:" .. state.list_selected .. ",双轴滚动已更新。") for _, update in ipairs(status_updates) do table.insert(updates, update) end return { ui = { updates = updates } } end ---@return RuntimeDiff local function handle_list_horizontal() state.list_axis = state.list_axis == "horizontal" and "vertical" or "horizontal" state.list_scroll_x = 0 state.list_scroll_y = 0 local updates = ui.list_updates() local status_updates = ui.status_updates("ListView 排列方向:" .. state.list_axis) for _, update in ipairs(status_updates) do table.insert(updates, update) end return { ui = { updates = updates } } end ---@return RuntimeDiff local function handle_list_reset() state.list_scroll_x = 0 state.list_scroll_y = 0 local updates = ui.list_updates() local status_updates = ui.status_updates("ListView 滚动已重置;滚轮/拖拽会触发 onScroll。") for _, update in ipairs(status_updates) do table.insert(updates, update) end return { ui = { updates = updates } } end ---@param event RuntimeEvent ---@return RuntimeDiff local function handle_list_scrolled(event) if event.data ~= nil then state.list_scroll_x = event.data.scrollX or state.list_scroll_x state.list_scroll_y = event.data.scrollY or state.list_scroll_y end return { ui = { updates = ui.status_updates("onScroll 回调:" .. tostring(state.list_scroll_x) .. ", " .. tostring(state.list_scroll_y)) } } end ---@param step integer ---@return RuntimeDiff ---@param preset string ---@return RuntimeDiff local function handle_particle(preset) state.particle_seed = (state.particle_seed or 0) + 1 local count = 42 + (state.particle_seed % 5) local updates = { runtime_ui.update("particle_burst", { preset = preset == "confetti" and "confetti" or "burst", count = count, duration = preset == "confetti" and 1.2 or 0.85, color = preset == "confetti" and "#ffff4d6d" or "#ffffcc33", colorTo = preset == "confetti" and "#fffacc15" or "#00ffcc33", gravityY = preset == "confetti" and 240 or 90, spread = preset == "confetti" and 90 or 360, visible = preset ~= "snow" }), runtime_ui.update("particle_trail", { visible = preset == "burst" }), runtime_ui.update("particle_snow", { count = 56 + (state.particle_seed % 7), visible = preset == "snow" }) } local status_updates = ui.status_updates("Particle 预设:" .. preset) for _, update in ipairs(status_updates) do table.insert(updates, update) end return { ui = { updates = updates } } end local function handle_list(step) local items = { "Lua", "Runtime", "Flame", "Diff", "Command" } local index = 1 for i, value in ipairs(items) do if value == state.list_selected then index = i break end end return handle_list_pick(index + step) end ---@param mode string ---@return RuntimeDiff local function handle_layout(mode) state.layout_mode = mode local updates = ui.layout_updates(mode) local status_updates = ui.status_updates("布局示例:" .. mode) for _, update in ipairs(status_updates) do table.insert(updates, update) end return { ui = { updates = updates } } end ---@return RuntimeDiff local function handle_resource() if state.resource_state == "ready" then state.resource_state = "evicted" return { ui = { updates = ui.status_updates("释放资源组 media:evict_resources。再次点击会 preload。") }, commands = { commands.evict_group("media") } } end state.resource_state = "ready" return { ui = { updates = ui.status_updates("预载资源组 media:preload_resources。") }, commands = { commands.preload_group("media", { failOnError = true }) } } end ---@return RuntimeDiff ---@return RuntimeDiff local function handle_i18n_toggle() local locale = i18n.toggle_locale() return { ui = { updates = ui.locale_updates() }, commands = { commands.toast(i18n.t("i18n_switched") .. locale) } } end ---@return RuntimeDiff local function handle_i18n_refresh() return { ui = { updates = ui.locale_updates() } } end ---@param mode string ---@return RuntimeDiff local function handle_responsive(mode) state.responsive_mode = mode local label = i18n.t("responsive_desktop") if mode == "phone" then label = i18n.t("responsive_phone") elseif mode == "tablet" then label = i18n.t("responsive_tablet") end local updates = ui.responsive_updates() local status_updates = ui.status_updates(label) for _, update in ipairs(status_updates) do table.insert(updates, update) end return { ui = { updates = updates } } end ---@param event RuntimeEvent ---@return RuntimeDiff local function handle_resize(event) if event.data ~= nil then apply_context(event.data) end return { ui = { updates = ui.responsive_updates() } } end ---@return RuntimeDiff local function handle_toast() return { ui = { updates = ui.status_updates("发送 toast 命令:Runtime 会显示临时 overlay 并自动移除。") }, commands = { commands.toast("Hello from Lua showcase") } } end ---@param target? string ---@return RuntimeDiff local function handle_select_example(target) local id = string.sub(target or "example_nodes", 9) local selected = examples.find(id) state.selected_example = selected.id state.detail_tab = "code" return { ui = { updates = ui.example_updates(selected) } } end ---@param tab string ---@return RuntimeDiff local function handle_detail_tab(tab) state.detail_tab = tab return { ui = { updates = ui.example_updates(examples.find(state.selected_example)) } } end ---@return RuntimeDiff local function handle_copy_detail() local selected = examples.find(state.selected_example) local text = selected.code local label = "代码" if state.detail_tab == "params" then text = i18n.example_params(selected) label = "参数说明" end return { ui = { updates = ui.status_updates("已复制" .. label .. "到剪贴板。") }, commands = { commands.copy_text(text) } } end function on_event(event) if event.handler == "select_example" then return handle_select_example(event.target) end if event.handler == "detail_tab_code" then return handle_detail_tab("code") end if event.handler == "detail_tab_params" then return handle_detail_tab("params") end if event.handler == "copy_detail" then return handle_copy_detail() end if event.handler == "demo_anim" then return handle_anim() end if event.handler == "demo_cancel" then return handle_cancel() end if event.handler == "demo_progress" then return handle_progress() end if event.handler == "demo_visibility" then return handle_visibility() end if event.handler == "demo_dialog" then return handle_dialog() end if event.handler == "close_dialog" then return handle_close_dialog() end if event.handler == "demo_temp" then return handle_temp() end if event.handler == "demo_text_change" then return handle_text_change() end if event.handler == "demo_text_style" then return handle_text_style() end if event.handler == "demo_button_primary" then return handle_button_primary() end if event.handler == "demo_button_toggle" then return handle_button_toggle() end if event.handler == "demo_button_image_tap" then return handle_button_image_tap() end if event.handler == "demo_button_image_toggle" then return handle_button_image_toggle() end if event.handler == "demo_sprite_anim" then return handle_sprite_anim() end if event.handler == "demo_sprite_style" then return handle_sprite_style() end if event.handler == "demo_radio_audio" then return handle_radio("audio") end if event.handler == "demo_radio_spine" then return handle_radio("spine") end if event.handler == "demo_radio_lua" then return handle_radio("Lua") end if event.handler == "demo_list_prev" then return handle_list(-1) end if event.handler == "demo_list_next" then return handle_list(1) end if event.handler == "demo_list_horizontal" then return handle_list_horizontal() end if event.handler == "demo_list_reset" then return handle_list_reset() end if event.handler == "demo_list_scrolled" then return handle_list_scrolled(event) end if event.handler == "demo_list_pick_1" then return handle_list_pick(1) end if event.handler == "demo_list_pick_2" then return handle_list_pick(2) end if event.handler == "demo_list_pick_3" then return handle_list_pick(3) end if event.handler == "demo_list_pick_4" then return handle_list_pick(4) end if event.handler == "demo_list_pick_5" then return handle_list_pick(5) end if event.handler == "demo_particle_burst" then return handle_particle("burst") end if event.handler == "demo_particle_confetti" then return handle_particle("confetti") end if event.handler == "demo_particle_snow" then return handle_particle("snow") end if event.handler == "demo_layout_row" then return handle_layout("row") end if event.handler == "demo_layout_column" then return handle_layout("column") end if event.handler == "demo_layout_box" then return handle_layout("box") end if event.handler == "demo_sound" then return handle_sound() end if event.handler == "demo_bgm" then return handle_bgm() end if event.handler == "demo_resource" then return handle_resource() end if event.handler == "demo_i18n_toggle" then return handle_i18n_toggle() end if event.handler == "demo_i18n_refresh" then return handle_i18n_refresh() end if event.handler == "demo_responsive_phone" then return handle_responsive("phone") end if event.handler == "demo_responsive_tablet" then return handle_responsive("tablet") end if event.handler == "demo_responsive_desktop" then return handle_responsive("desktop") end if event.handler == "demo_toast" then return handle_toast() end if event.type == "resize" then return handle_resize(event) end if event.handler == "demo_anim_done" then return status_only("动画完成事件:RuntimeCommand.onComplete -> RuntimeEvent -> Lua。") end if event.handler == "temp_removed" then state.temp_node_visible = false return status_only("临时节点已由 remove_node 命令删除。") end if event.handler == "sound_done" then return status_only("音效播放完成事件已回到 Lua。") end if event.handler == "sprite_anim_done" then return status_only("精灵动画完成。") end return {} end