Initial flame_lua_runtime package

This commit is contained in:
gem
2026-06-07 22:53:58 +08:00
commit 733b2fb798
262 changed files with 28439 additions and 0 deletions

View File

@@ -0,0 +1,698 @@
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_commandsgroup = 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.dialogLua 组合普通 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 资源 clickcommands.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=demoplay_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("暂停 BGMpause_bgm('demo')。") },
commands = { commands.pause_bgm("demo") }
}
end
if state.bgm_state == "paused" then
state.bgm_state = "resumed"
return {
ui = { updates = ui.status_updates("恢复 BGMresume_bgm('demo')。") },
commands = { commands.resume_bgm("demo") }
}
end
state.bgm_state = "stopped"
return {
ui = { updates = ui.status_updates("停止 BGMstop_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("释放资源组 mediaevict_resources。再次点击会 preload。") },
commands = { commands.evict_group("media") }
}
end
state.resource_state = "ready"
return {
ui = { updates = ui.status_updates("预载资源组 mediapreload_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