820 lines
25 KiB
Lua
820 lines
25 KiB
Lua
---@class (exact) ShowcaseAction
|
||
---@field text string
|
||
---@field text_en? string
|
||
---@field handler string
|
||
|
||
---@class (exact) ShowcaseExample
|
||
---@field id string
|
||
---@field group string
|
||
---@field group_en? string
|
||
---@field category string
|
||
---@field category_en? string
|
||
---@field menu string
|
||
---@field menu_en? string
|
||
---@field title string
|
||
---@field title_en? string
|
||
---@field summary string
|
||
---@field summary_en? string
|
||
---@field code string
|
||
---@field params string
|
||
---@field params_en? string
|
||
---@field actions ShowcaseAction[]
|
||
|
||
---@class ShowcaseExamples
|
||
local examples = {}
|
||
|
||
---@type ShowcaseExample[]
|
||
examples.items = {
|
||
{
|
||
id = "nodes",
|
||
group = "基础功能",
|
||
group_en = "Basics",
|
||
category = "节点",
|
||
category_en = "Nodes",
|
||
menu = "基础节点",
|
||
menu_en = "Nodes",
|
||
title = "RuntimeNode 基础组件",
|
||
title_en = "RuntimeNode primitives",
|
||
summary = "基础节点、交互和属性更新。",
|
||
summary_en = "Primitive nodes, interaction and property updates.",
|
||
code = [[local runtime_ui = runtime.import("runtime_ui")
|
||
|
||
runtime_ui.circle("hero", {
|
||
x = 80,
|
||
y = 64,
|
||
size = 48,
|
||
color = "#ff22c55e",
|
||
interactive = true,
|
||
handler = "demo_anim"
|
||
})]],
|
||
params = [[参数说明:type/circle/rect/line 定义节点类型;
|
||
x/y/width/height 定位尺寸;
|
||
w/h 是 width/height 的便利别名;
|
||
size 会同时设置 width 和 height;
|
||
handler/onClick 是 onTap 的便利别名;
|
||
color/alpha/scale/rotation 控制表现;
|
||
interactive/onTap 打开点击事件。
|
||
|
||
填写样例:
|
||
runtime_ui.circle("hero", {
|
||
x = 80,
|
||
y = 64,
|
||
size = 48,
|
||
color = "#ff22c55e",
|
||
alpha = 0.9,
|
||
scale = 1.0,
|
||
interactive = true,
|
||
handler = "demo_anim"
|
||
})]],
|
||
params_en = [[Params: type/circle/rect/line choose node kind;
|
||
x/y/width/height place it;
|
||
color/alpha/scale/rotation style it;
|
||
interactive/onTap enable taps.]],
|
||
actions = {
|
||
{ text = "更新进度", text_en = "Progress", handler = "demo_progress" },
|
||
{ text = "显隐/透明", text_en = "Visibility", handler = "demo_visibility" }
|
||
}
|
||
},
|
||
{
|
||
id = "text_demo",
|
||
group = "基础功能",
|
||
group_en = "Basics",
|
||
category = "文本",
|
||
category_en = "Text",
|
||
menu = "文本",
|
||
menu_en = "Text",
|
||
title = "Text 文本组件",
|
||
title_en = "Text component",
|
||
summary = "当前支持纯文本、颜色和字号;暂不支持富文本。",
|
||
summary_en = "Plain text, color and font size; rich text is not supported yet.",
|
||
code = [[runtime_ui.text("label", "Hello Lua", 20, 40, 220, 28, {
|
||
color = "#ffe2e8f0",
|
||
fontSize = 18
|
||
})
|
||
|
||
-- 当前协议没有 richText/spans 字段]],
|
||
params = [[参数说明:text 是纯文本;
|
||
color/fontSize/alpha 控制整体样式;
|
||
textAlign 可填 "left" / "center" / "right";
|
||
当前没有 richText/spans,不能做局部文字样式。
|
||
|
||
填写样例:
|
||
runtime_ui.text("label", "Hello Lua", 20, 40, 220, 28, {
|
||
color = "#ffe2e8f0",
|
||
fontSize = 18,
|
||
textAlign = "left"
|
||
})]],
|
||
params_en = [[Params: text is plain copy;
|
||
color/fontSize/alpha style the whole node;
|
||
richText/spans are not supported yet.]],
|
||
actions = {
|
||
{ text = "改文案", text_en = "Change", handler = "demo_text_change" },
|
||
{ text = "样式切换", text_en = "Style", handler = "demo_text_style" }
|
||
}
|
||
},
|
||
{
|
||
id = "buttons",
|
||
group = "基础功能",
|
||
group_en = "Basics",
|
||
category = "按钮",
|
||
category_en = "Buttons",
|
||
menu = "按钮交互",
|
||
menu_en = "Buttons",
|
||
title = "基础按钮和点击事件",
|
||
title_en = "Buttons and tap events",
|
||
summary = "button 节点、onTap handler、状态更新和禁用态。",
|
||
summary_en = "Button nodes, onTap handlers, state updates and disabled style.",
|
||
code = [[runtime_ui.button("ok", {
|
||
text = "确认",
|
||
x = 20,
|
||
y = 40,
|
||
w = 120,
|
||
h = 34,
|
||
handler = "submit",
|
||
color = theme.primary,
|
||
radius = 10,
|
||
fontSize = 13,
|
||
asset = "button_normal",
|
||
pressedAsset = "button_pressed",
|
||
disabledAsset = "button_disabled"
|
||
})
|
||
|
||
-- asset/pressedAsset/disabledAsset 都是 manifest image 资源 key。
|
||
-- event.handler == "submit" 时返回 Diff/Command]],
|
||
params = [[参数说明:button = 背景 + 文本 + onTap;
|
||
w/h 是 width/height 的便利别名;
|
||
handler/onClick 是 onTap 的便利别名;
|
||
color/radius/fontSize 控制样式;
|
||
asset/pressedAsset/disabledAsset 可配置 normal/pressed/disabled 三态图片;
|
||
interactive=false 可禁用命中并使用 disabledAsset。
|
||
|
||
填写样例:
|
||
runtime_ui.button("ok", {
|
||
text = "确认",
|
||
x = 20,
|
||
y = 40,
|
||
w = 120,
|
||
h = 34,
|
||
handler = "submit",
|
||
color = theme.primary,
|
||
radius = 10,
|
||
fontSize = 13,
|
||
asset = "button_normal",
|
||
pressedAsset = "button_pressed",
|
||
disabledAsset = "button_disabled",
|
||
interactive = true
|
||
})]],
|
||
params_en = [[Params: button combines background, label and onTap;
|
||
color/radius/fontSize style it;
|
||
interactive=false disables hit testing.]],
|
||
actions = {
|
||
{ text = "点击按钮", text_en = "Tap", handler = "demo_button_primary" },
|
||
{ text = "切换状态", text_en = "Toggle", handler = "demo_button_toggle" }
|
||
}
|
||
},
|
||
{
|
||
id = "button_images",
|
||
group = "基础功能",
|
||
group_en = "Basics",
|
||
category = "按钮",
|
||
category_en = "Buttons",
|
||
menu = "按钮三态图",
|
||
menu_en = "Button skins",
|
||
title = "Button 三态图片",
|
||
title_en = "Button state images",
|
||
summary = "button 使用 normal / pressed / disabled 三态图片资源。",
|
||
summary_en = "Buttons can render normal, pressed and disabled image assets.",
|
||
code = [[runtime_ui.button("start", {
|
||
text = "开始",
|
||
x = 20,
|
||
y = 40,
|
||
w = 132,
|
||
h = 40,
|
||
handler = "start",
|
||
asset = "button_normal",
|
||
pressedAsset = "button_pressed",
|
||
disabledAsset = "button_disabled"
|
||
})
|
||
|
||
runtime_ui.button("locked", {
|
||
text = "禁用",
|
||
x = 170,
|
||
y = 40,
|
||
w = 132,
|
||
h = 40,
|
||
handler = "noop",
|
||
asset = "button_normal",
|
||
pressedAsset = "button_pressed",
|
||
disabledAsset = "button_disabled",
|
||
interactive = false
|
||
})]],
|
||
params = [[参数说明:
|
||
asset = normal 状态图片资源 key;
|
||
pressedAsset = 按下状态图片资源 key;
|
||
disabledAsset = 禁用状态图片资源 key;
|
||
interactive=false 时 Runtime 自动选择 disabledAsset;
|
||
按住按钮时 Runtime 自动切换 pressedAsset;
|
||
未配置对应状态图片时回退到 asset,再回退到 color/radius 背景。
|
||
|
||
manifest 示例:
|
||
"button_normal": { "type": "image", "path": "assets/button_normal.png" },
|
||
"button_pressed": { "type": "image", "path": "assets/button_pressed.png" },
|
||
"button_disabled": { "type": "image", "path": "assets/button_disabled.png" }]],
|
||
params_en = [[Params:
|
||
asset is the normal-state image resource key;
|
||
pressedAsset is used while the button is pressed;
|
||
disabledAsset is used when interactive=false;
|
||
missing state images fall back to asset, then to color/radius background.]],
|
||
actions = {
|
||
{ text = "点击图片按钮", text_en = "Tap", handler = "demo_button_image_tap" },
|
||
{ text = "切换禁用", text_en = "Toggle", handler = "demo_button_image_toggle" }
|
||
}
|
||
},
|
||
{
|
||
id = "sprites",
|
||
group = "基础功能",
|
||
group_en = "Basics",
|
||
category = "精灵",
|
||
category_en = "Sprites",
|
||
menu = "图片精灵",
|
||
menu_en = "Sprites",
|
||
title = "图片和 Sprite 节点",
|
||
title_en = "Image and sprite nodes",
|
||
summary = "image/sprite 使用 manifest 资源 key,不暴露真实路径。",
|
||
summary_en = "Image/sprite nodes use manifest resource keys, not raw paths.",
|
||
code = [[runtime_ui.image("avatar", "sample_image", 24, 48, 56, 56)
|
||
runtime_ui.sprite("icon", "sample_image", 104, 48, 56, 56, {
|
||
layer = 20
|
||
})]],
|
||
params = [[参数说明:asset 必须是 manifest 资源 key;
|
||
image/sprite 不暴露真实路径;
|
||
layer 控制绘制顺序。
|
||
|
||
填写样例:
|
||
-- manifest.resources.sample_image.type = "image"
|
||
runtime_ui.image("avatar", "sample_image", 24, 48, 56, 56, {
|
||
layer = 20,
|
||
alpha = 1
|
||
})]],
|
||
params_en = [[Params: asset must be a manifest resource key;
|
||
image/sprite never expose raw paths;
|
||
layer controls draw order.]],
|
||
actions = {
|
||
{ text = "精灵动画", text_en = "Animate", handler = "demo_sprite_anim" },
|
||
{ text = "切换样式", text_en = "Style", handler = "demo_sprite_style" }
|
||
}
|
||
},
|
||
{
|
||
id = "radio_group",
|
||
group = "基础功能",
|
||
group_en = "Basics",
|
||
category = "选择",
|
||
category_en = "Selection",
|
||
menu = "RadioGroup",
|
||
menu_en = "Radio",
|
||
title = "RadioGroup 组合模式",
|
||
title_en = "RadioGroup composition",
|
||
summary = "Lua 用 circle/text/button 组合单选项。",
|
||
summary_en = "Lua composes radio options from circle/text/button nodes.",
|
||
code = [[-- RadioGroup 不是 Dart 原生控件
|
||
-- Lua 输出普通 RuntimeNode:circle + text + button
|
||
|
||
runtime_ui.circle("radio_dot", 24, 52, 14, { color = theme.primary })
|
||
runtime_ui.text("radio_label", "Audio", 46, 49, 120, 20)]],
|
||
params = [[参数说明:RadioGroup 是 Lua 组合,不是原生控件;
|
||
用 circle/text/button 组合,并由 Lua state 保存选中值。
|
||
|
||
填写样例:
|
||
local option = { key = "audio", label = "Audio", y = 72 }
|
||
runtime_ui.circle("radio_" .. option.key .. "_dot", 18, option.y, 14, {
|
||
color = selected and theme.primary or "#ff475569"
|
||
})
|
||
runtime_ui.button("radio_" .. option.key .. "_hit", "", 8, option.y - 4, 180, 24, "demo_radio_audio", {
|
||
color = "#00000000",
|
||
interactive = true
|
||
})]],
|
||
params_en = [[Params: RadioGroup is Lua composition, not a native control;
|
||
compose circle/text/button and keep selection in Lua state.]],
|
||
actions = {
|
||
{ text = "选 Audio", text_en = "Audio", handler = "demo_radio_audio" },
|
||
{ text = "选 Spine", text_en = "Spine", handler = "demo_radio_spine" },
|
||
{ text = "选 Lua", text_en = "Lua", handler = "demo_radio_lua" }
|
||
}
|
||
},
|
||
{
|
||
id = "list_view",
|
||
group = "基础功能",
|
||
group_en = "Basics",
|
||
category = "列表",
|
||
category_en = "List",
|
||
menu = "ListView",
|
||
menu_en = "List",
|
||
title = "原生 ListView 容器",
|
||
title_en = "Native ListView container",
|
||
summary = "双轴滚动、惯性、回调、滚动条样式和虚拟化。",
|
||
summary_en = "Two-axis scroll, inertia, callbacks, styled scrollbar and culling.",
|
||
code = [[runtime_ui.list_view("list", 16, 42, 260, 72, {
|
||
contentWidth = 420,
|
||
contentHeight = 150,
|
||
scrollX = state.list_scroll_x,
|
||
scrollY = state.list_scroll_y,
|
||
virtualized = true,
|
||
cacheExtent = 24,
|
||
inertia = true,
|
||
onScroll = "demo_list_scrolled"
|
||
})
|
||
|
||
runtime_ui.button("row_1", "Lua", 8, 8, 220, 24, "select", {
|
||
parent = "list"
|
||
})]],
|
||
params = [[参数说明:scrollX/scrollY 是双轴偏移;
|
||
contentWidth/contentHeight 是内容尺寸;
|
||
virtualized/cacheExtent 控制直接子节点裁剪;
|
||
inertia 开启拖动惯性;
|
||
onScroll 接收滚动回调;
|
||
scrollbarVisible=false 可隐藏滚动条;
|
||
scrollbarThumbColor/scrollbarTrackColor/scrollbarThickness 控制样式。
|
||
|
||
填写样例:
|
||
local opts = {
|
||
contentWidth = 420,
|
||
contentHeight = 150,
|
||
scrollX = 0,
|
||
scrollY = 0,
|
||
virtualized = true,
|
||
cacheExtent = 24,
|
||
inertia = true,
|
||
onScroll = "demo_list_scrolled",
|
||
scrollbarVisible = false
|
||
}
|
||
runtime_ui.list_view("list", 16, 42, 260, 72, opts)
|
||
|
||
-- 子节点必须把 parent 指向 listView
|
||
runtime_ui.button("row_1", "Lua", 8, 8, 220, 24, "select", {
|
||
parent = "list"
|
||
})]],
|
||
params_en = [[Params: scrollX/scrollY are two-axis offsets;
|
||
contentWidth/contentHeight define content size;
|
||
virtualized/cacheExtent cull children;
|
||
inertia enables momentum;
|
||
onScroll emits callbacks;
|
||
scrollbar* styles bars.]],
|
||
actions = {
|
||
{ text = "横/竖排列", text_en = "Axis", handler = "demo_list_horizontal" },
|
||
{ text = "下一项", text_en = "Next", handler = "demo_list_next" },
|
||
{ text = "重置滚动", text_en = "Reset", handler = "demo_list_reset" }
|
||
}
|
||
},
|
||
{
|
||
id = "particles",
|
||
group = "基础功能",
|
||
group_en = "Basics",
|
||
category = "特效",
|
||
category_en = "Effects",
|
||
menu = "粒子特效",
|
||
menu_en = "Particles",
|
||
title = "Particle 粒子特效",
|
||
title_en = "Particle effects",
|
||
summary = "Lua 描述粒子参数,Dart Runtime 创建 Flame 粒子组件。",
|
||
summary_en = "Lua describes particle params; Dart Runtime creates Flame particles.",
|
||
code = [[runtime_ui.particle("hit_fx", 220, 140, 160, 160, {
|
||
preset = "burst",
|
||
count = 40,
|
||
duration = 0.6,
|
||
color = "#ffffcc33",
|
||
colorTo = "#00ffcc33",
|
||
radius = 2.4,
|
||
radiusTo = 0,
|
||
speedMin = 60,
|
||
speedMax = 180,
|
||
gravityY = 120,
|
||
spread = 360,
|
||
autoRemove = true,
|
||
fadeOut = true,
|
||
layer = 80
|
||
})]],
|
||
params = [[参数说明:particle 是 Runtime 原生节点,Lua 不持有 Flame 粒子对象;
|
||
preset 可填 "burst" / "trail" / "snow" / "confetti";
|
||
count 是粒子数量;
|
||
duration 是生命周期秒数;
|
||
color/colorTo 控制颜色渐变;
|
||
radius/radiusTo 控制尺寸变化;
|
||
speedMin/speedMax 控制初速度范围;
|
||
gravityX/gravityY 控制加速度;
|
||
spread 控制发散角度;
|
||
autoRemove=true 表示生命周期结束后自动移除;
|
||
fadeOut=true 表示随进度淡出。
|
||
|
||
填写样例:
|
||
local opts = {
|
||
preset = "confetti",
|
||
count = 72,
|
||
duration = 1.2,
|
||
color = "#ffff4d6d",
|
||
colorTo = "#fffacc15",
|
||
radius = 2.6,
|
||
radiusTo = 0,
|
||
speedMin = 120,
|
||
speedMax = 260,
|
||
gravityY = 240,
|
||
spread = 90,
|
||
autoRemove = true,
|
||
fadeOut = true
|
||
}
|
||
runtime_ui.particle("win_fx", 280, 72, 220, 160, opts)]],
|
||
params_en = [[Params: particle is a native Runtime node; Lua never owns Flame particle objects;
|
||
preset: burst/trail/snow/confetti;
|
||
count controls particle amount;
|
||
duration is lifetime in seconds;
|
||
color/colorTo define gradient;
|
||
radius/radiusTo define size change;
|
||
speedMin/speedMax define initial speed;
|
||
gravityX/gravityY define acceleration;
|
||
spread defines emission angle;
|
||
autoRemove removes after lifetime;
|
||
fadeOut fades by progress.]],
|
||
actions = {
|
||
{ text = "爆发", text_en = "Burst", handler = "demo_particle_burst" },
|
||
{ text = "彩纸", text_en = "Confetti", handler = "demo_particle_confetti" },
|
||
{ text = "雪花", text_en = "Snow", handler = "demo_particle_snow" }
|
||
}
|
||
},
|
||
{
|
||
id = "layout_demo",
|
||
group = "基础功能",
|
||
group_en = "Basics",
|
||
category = "布局",
|
||
category_en = "Layout",
|
||
menu = "布局 Helper",
|
||
menu_en = "Layout",
|
||
title = "Lua layout helper",
|
||
title_en = "Lua layout helper",
|
||
summary = "layout.row/column/box 消费临时 margin,不污染协议。",
|
||
summary_en = "layout.row/column/box consume temporary margin without polluting protocol tables.",
|
||
code = [[local layout = runtime.import("layout")
|
||
|
||
layout.box("panel", {
|
||
runtime_ui.rect("a", 0, 0, 42, 28),
|
||
runtime_ui.rect("b", 0, 0, 42, 28),
|
||
runtime_ui.rect("c", 0, 0, 42, 28),
|
||
runtime_ui.rect("d", 0, 0, 42, 28)
|
||
}, {
|
||
x = 16,
|
||
y = 48,
|
||
rows = 2,
|
||
columns = 2,
|
||
cellWidth = 58,
|
||
cellHeight = 34,
|
||
gapX = 8,
|
||
gapY = 8,
|
||
align = "center",
|
||
valign = "center"
|
||
})]],
|
||
params = [[参数说明:layout.item 的 margin 只用于 Lua 布局计算,不会进入 RuntimeNode props;
|
||
row/column 处理单轴排列;
|
||
box 按 rows/columns 把节点排成几排几列;
|
||
cellWidth/cellHeight 是单元格尺寸;
|
||
gapX/gapY 是列间距和行间距;
|
||
align/valign 控制节点在单元格内的水平/垂直对齐。
|
||
|
||
填写样例:
|
||
layout.box("panel", {
|
||
layout.item(runtime_ui.rect("a", 0, 0, 42, 28), { marginRight = 4 }),
|
||
runtime_ui.rect("b", 0, 0, 42, 28),
|
||
runtime_ui.rect("c", 0, 0, 42, 28),
|
||
runtime_ui.rect("d", 0, 0, 42, 28)
|
||
}, {
|
||
x = 16,
|
||
y = 48,
|
||
rows = 2,
|
||
columns = 2,
|
||
cellWidth = 58,
|
||
cellHeight = 34,
|
||
gapX = 8,
|
||
gapY = 8,
|
||
align = "center",
|
||
valign = "center"
|
||
})]],
|
||
params_en = [[Params: layout.item margins are Lua-only layout metadata and never enter RuntimeNode props;
|
||
box uses rows/columns to place nodes in a grid;
|
||
cellWidth/cellHeight define cells;
|
||
gapX/gapY define column and row gaps;
|
||
align/valign align each node inside its cell.]],
|
||
actions = {
|
||
{ text = "横向布局", text_en = "Row", handler = "demo_layout_row" },
|
||
{ text = "纵向布局", text_en = "Column", handler = "demo_layout_column" },
|
||
{ text = "Box 网格", text_en = "Box", handler = "demo_layout_box" }
|
||
}
|
||
},
|
||
{
|
||
id = "diff",
|
||
group = "运行协议",
|
||
group_en = "Runtime Protocol",
|
||
category = "Diff",
|
||
category_en = "Diff",
|
||
menu = "Diff 更新",
|
||
menu_en = "Diff",
|
||
title = "GameDiff 创建、更新、移除",
|
||
title_en = "GameDiff create/update/remove",
|
||
summary = "Diff 创建、更新、移除节点。",
|
||
summary_en = "Create, update and remove nodes with GameDiff.",
|
||
code = [[return {
|
||
render = {
|
||
creates = { node },
|
||
updates = { { id = "node", props = { x = 120 } } },
|
||
removes = { { id = "node" } }
|
||
}
|
||
}]],
|
||
params = [[参数说明:creates 创建节点;
|
||
updates 只更新 props;
|
||
removes 只需要 id。
|
||
Runtime 会白名单校验未知字段。
|
||
|
||
填写样例:
|
||
return {
|
||
ui = {
|
||
creates = {
|
||
runtime_ui.text("tip", "Hello", 20, 20, 160, 24, { textAlign = "left" })
|
||
},
|
||
updates = {
|
||
{ id = "tip", props = { text = "Updated", color = "#ffffa000" } }
|
||
},
|
||
removes = {
|
||
{ id = "tip" }
|
||
}
|
||
}
|
||
}]],
|
||
params_en = [[Params: creates add nodes;
|
||
updates patch props;
|
||
removes only need ids.
|
||
Runtime rejects unknown fields by whitelist.]],
|
||
actions = {
|
||
{ text = "创建/删除", text_en = "Create/remove", handler = "demo_temp" },
|
||
{ text = "Toast", text_en = "Toast", handler = "demo_toast" }
|
||
}
|
||
},
|
||
{
|
||
id = "commands",
|
||
group = "运行协议",
|
||
group_en = "Runtime Protocol",
|
||
category = "命令",
|
||
category_en = "Commands",
|
||
menu = "命令动画",
|
||
menu_en = "Commands",
|
||
title = "RuntimeCommand 动作和组合",
|
||
title_en = "RuntimeCommand actions",
|
||
summary = "动作、组合、取消和完成回调。",
|
||
summary_en = "Actions, composition, cancellation and callbacks.",
|
||
code = [[commands.sequence({
|
||
commands.move_to("actor", 220, 104, { duration = 0.55 }),
|
||
commands.parallel({
|
||
commands.scale_to("actor", 1.45, { duration = 0.35 }),
|
||
commands.fade_to("actor", 0.45, { duration = 0.35 })
|
||
})
|
||
}, { group = "demo_anim", onComplete = "done" })]],
|
||
params = [[参数说明:target 指向节点;
|
||
duration 控制时长;
|
||
sequence 串行;
|
||
parallel 并行;
|
||
group/id/scope 可用于取消。
|
||
|
||
填写样例:
|
||
local move_opts = {
|
||
duration = 0.55,
|
||
id = "intro_move",
|
||
group = "intro",
|
||
scope = "dialog_1"
|
||
}
|
||
commands.move_to("actor", 220, 104, move_opts)
|
||
|
||
commands.cancel_group("intro")]],
|
||
params_en = [[Params: target points to a node;
|
||
duration controls timing;
|
||
sequence runs serially;
|
||
parallel runs together;
|
||
group/id/scope support cancellation.]],
|
||
actions = {
|
||
{ text = "动画序列", text_en = "Animate", handler = "demo_anim" },
|
||
{ text = "取消动画", text_en = "Cancel", handler = "demo_cancel" }
|
||
}
|
||
},
|
||
{
|
||
id = "widgets",
|
||
group = "Lua 表现层",
|
||
group_en = "Lua Layer",
|
||
category = "Widget",
|
||
category_en = "Widget",
|
||
menu = "组合组件",
|
||
menu_en = "Widgets",
|
||
title = "Lua Widget 组合组件",
|
||
title_en = "Lua widget composition",
|
||
summary = "Lua 组合组件仍输出普通 RuntimeNode。",
|
||
summary_en = "Lua widgets still output plain RuntimeNode tables.",
|
||
code = [[local widgets = runtime.import("runtime_widgets")
|
||
|
||
widgets.dialog("dialog", "标题", "内容", 150, 190, 420, 230, {
|
||
buttons = {
|
||
{ text = "确定", handler = "close_dialog" }
|
||
}
|
||
})]],
|
||
params = [[参数说明:widgets.dialog 是 Lua helper,输出普通 RuntimeNode;
|
||
buttons 数组定义文案和 handler。
|
||
|
||
填写样例:
|
||
local dialog_opts = {
|
||
buttons = {
|
||
{ text = "确定", handler = "close_dialog" },
|
||
{ text = "取消", handler = "cancel_dialog" }
|
||
},
|
||
modal = true
|
||
}
|
||
widgets.dialog("dialog", "标题", "内容", 150, 190, 420, 230, dialog_opts)]],
|
||
params_en = [[Params: widgets.dialog is a Lua helper that emits plain RuntimeNodes;
|
||
buttons define labels and handlers.]],
|
||
actions = {
|
||
{ text = "打开 Dialog", text_en = "Open Dialog", handler = "demo_dialog" },
|
||
{ text = "关闭 Dialog", text_en = "Close Dialog", handler = "close_dialog" }
|
||
}
|
||
},
|
||
{
|
||
id = "audio",
|
||
group = "资源能力",
|
||
group_en = "Assets",
|
||
category = "资源",
|
||
category_en = "Assets",
|
||
menu = "资源音频",
|
||
menu_en = "Audio",
|
||
title = "资源、音效和 BGM",
|
||
title_en = "Assets, SFX and BGM",
|
||
summary = "资源 key、图片、音效、BGM 和资源组。",
|
||
summary_en = "Resource keys, images, SFX, BGM and groups.",
|
||
code = [[commands.play_sound("click", { volume = 0.8 })
|
||
commands.play_bgm("click", { channel = "demo", loop = true })
|
||
commands.preload_group("media", { failOnError = true })]],
|
||
params = [[参数说明:asset/name 使用 manifest audio key;
|
||
volume 范围 0~1;
|
||
channel 管理 BGM;
|
||
group 管理资源预载/释放。
|
||
|
||
填写样例:
|
||
commands.play_sound("click", {
|
||
volume = 0.8,
|
||
id = "click_sfx"
|
||
})
|
||
commands.play_bgm("click", {
|
||
channel = "demo",
|
||
loop = true,
|
||
volume = 0.5
|
||
})
|
||
commands.preload_group("media", { failOnError = true })]],
|
||
params_en = [[Params: asset/name use manifest audio keys;
|
||
volume is 0..1;
|
||
channel manages BGM;
|
||
group manages resource preload/evict.]],
|
||
actions = {
|
||
{ text = "音效", text_en = "SFX", handler = "demo_sound" },
|
||
{ text = "BGM 循环", text_en = "BGM loop", handler = "demo_bgm" },
|
||
{ text = "预载/释放", text_en = "Preload/evict", handler = "demo_resource" }
|
||
}
|
||
},
|
||
{
|
||
id = "spine",
|
||
group = "资源能力",
|
||
group_en = "Assets",
|
||
category = "骨骼",
|
||
category_en = "Spine",
|
||
menu = "Spine 模板",
|
||
menu_en = "Spine",
|
||
title = "Spine 接入模板",
|
||
title_en = "Spine integration template",
|
||
summary = "Spine 资源、节点和动画命令模板。",
|
||
summary_en = "Template for Spine resources, nodes and animation commands.",
|
||
code = [[-- manifest.resources
|
||
"hero_spine": {
|
||
"type": "spine",
|
||
"atlas": "assets/hero.atlas",
|
||
"skeleton": "assets/hero.skel"
|
||
}
|
||
|
||
runtime_ui.spine("hero", "hero_spine", 100, 120, 160, 220, "idle")
|
||
commands.play_spine_animation("hero", "attack", { loop = false })]],
|
||
params = [[参数说明:spine 节点使用 manifest spine key;
|
||
animation/skin/loop 描述播放状态;
|
||
track/queue/delay 控制动画命令。
|
||
|
||
填写样例:
|
||
runtime_ui.spine("hero", "hero_spine", 100, 120, 160, 220, "idle", {
|
||
skin = "default",
|
||
loop = true
|
||
})
|
||
commands.play_spine_animation("hero", "attack", {
|
||
track = 0,
|
||
loop = false,
|
||
queue = false,
|
||
delay = 0
|
||
})]],
|
||
params_en = [[Params: spine nodes use manifest spine keys;
|
||
animation/skin/loop describe playback;
|
||
track/queue/delay control animation commands.]],
|
||
actions = {
|
||
{ text = "播放音效", text_en = "SFX", handler = "demo_sound" },
|
||
{ text = "Toast", text_en = "Toast", handler = "demo_toast" }
|
||
}
|
||
},
|
||
{
|
||
id = "i18n",
|
||
group = "平台能力",
|
||
group_en = "Platform",
|
||
category = "本地化",
|
||
category_en = "I18N",
|
||
menu = "多语言",
|
||
menu_en = "I18N",
|
||
title = "Lua 多语言 Showcase",
|
||
title_en = "Lua-owned localization",
|
||
summary = "Lua 管理文案表、回退和语言切换。",
|
||
summary_en = "Lua owns copy tables, fallback and locale switching.",
|
||
code = [[local i18n = runtime.import("i18n")
|
||
|
||
i18n.apply_context(ctx)
|
||
runtime_ui.text("title", i18n.t("app_title"), 24, 18, 360, 34)
|
||
|
||
-- 点击按钮:i18n.toggle_locale() 后更新所有文案]],
|
||
params = [[参数说明:Runtime 只传 ctx.locale;
|
||
Lua 自己管理 messages、fallback、刷新 Diff。
|
||
|
||
填写样例:
|
||
local messages = {
|
||
["zh-Hans"] = { app_title = "Lua Showcase" },
|
||
en = { app_title = "Lua Showcase" }
|
||
}
|
||
local locale = ctx.locale or "zh-Hans"
|
||
local text = (messages[locale] or messages["zh-Hans"]).app_title]],
|
||
params_en = [[Params: Runtime only passes ctx.locale;
|
||
Lua owns messages, fallback and refresh diffs.]],
|
||
actions = {
|
||
{ text = "切换语言", text_en = "Toggle locale", handler = "demo_i18n_toggle" },
|
||
{ text = "刷新文案", text_en = "Refresh copy", handler = "demo_i18n_refresh" }
|
||
}
|
||
},
|
||
{
|
||
id = "responsive",
|
||
group = "平台能力",
|
||
group_en = "Platform",
|
||
category = "适配",
|
||
category_en = "Layout",
|
||
menu = "分辨率适配",
|
||
menu_en = "Responsive",
|
||
title = "分辨率适配 Showcase",
|
||
title_en = "Responsive layout showcase",
|
||
summary = "设计分辨率、viewport 和布局模拟。",
|
||
summary_en = "Design size, viewport and layout simulation.",
|
||
code = [[-- manifest.display
|
||
{
|
||
"designWidth": 720,
|
||
"designHeight": 720,
|
||
"scaleMode": "fit"
|
||
}
|
||
|
||
-- ctx.screen / ctx.design / ctx.viewport
|
||
-- Lua 根据设计宽度选择 compact / desktop 布局]],
|
||
params = [[参数说明:manifest.display 声明设计分辨率;
|
||
ctx.screen/design/viewport 给 Lua 做布局决策。
|
||
|
||
填写样例:
|
||
-- manifest.display
|
||
{
|
||
designWidth = 720,
|
||
designHeight = 720,
|
||
scaleMode = "fit"
|
||
}
|
||
|
||
local compact = ctx.screen.width < 640
|
||
local menu_w = compact and 220 or 320]],
|
||
params_en = [[Params: manifest.display declares design size;
|
||
ctx.screen/design/viewport let Lua make layout decisions.]],
|
||
actions = {
|
||
{ text = "手机宽度", text_en = "Phone", handler = "demo_responsive_phone" },
|
||
{ text = "平板宽度", text_en = "Tablet", handler = "demo_responsive_tablet" },
|
||
{ text = "桌面宽度", text_en = "Desktop", handler = "demo_responsive_desktop" }
|
||
}
|
||
}
|
||
}
|
||
|
||
---@return ShowcaseExample[]
|
||
function examples.all()
|
||
return examples.items
|
||
end
|
||
|
||
---@param id string
|
||
---@return ShowcaseExample
|
||
function examples.find(id)
|
||
for _, example in ipairs(examples.items) do
|
||
if example.id == id then
|
||
return example
|
||
end
|
||
end
|
||
return examples.items[1]
|
||
end
|
||
|
||
return examples
|