Initial flame_lua_runtime package
17
.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Dart / Flutter generated files
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
pubspec.lock
|
||||||
|
build/
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# IDE / OS
|
||||||
|
*.iml
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
4
.pubignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
build/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
pubspec.lock
|
||||||
9
CHANGELOG.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
|
- Initial extracted package skeleton for `flame_lua_runtime`.
|
||||||
|
- Added public API barrel with `LuaGameWidget`, `FlameLuaGame`, `RuntimeOptions`, package repositories, and script engine interfaces.
|
||||||
|
- Added shared Runtime Lua helper assets.
|
||||||
|
- Added manifest-driven package/resource/module loading support.
|
||||||
|
- Added Runtime commands, rendering nodes, package activation, and Lua bridge infrastructure.
|
||||||
29
LICENSE
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2026, Flame Lua Runtime contributors
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
103
README.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# flame_lua_runtime
|
||||||
|
|
||||||
|
A Flutter + Flame + Lua runtime kit for manifest-driven 2D game packages.
|
||||||
|
|
||||||
|
The package provides a reusable runtime boundary:
|
||||||
|
|
||||||
|
```text
|
||||||
|
RuntimeEvent -> Lua -> GameDiff / RuntimeCommand -> Flame
|
||||||
|
```
|
||||||
|
|
||||||
|
It is designed for Flutter apps that want to host Lua-authored 2D games or interactive showcases while keeping the Flutter/Flame side generic.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- `LuaGameWidget` for embedding a Lua game package in a Flutter app.
|
||||||
|
- Manifest-driven Lua module, resource, audio, and package loading.
|
||||||
|
- Controlled Lua modularization through `runtime.import(moduleName)`.
|
||||||
|
- Generic Runtime nodes for panels, text, sprites, buttons, particles, Spine, and list views.
|
||||||
|
- Runtime commands for movement, fading, scaling, rotation, sequencing, audio, resources, toast, clipboard, and Spine animation.
|
||||||
|
- Shared Lua helper modules under `assets/runtime/lua/`.
|
||||||
|
- Configurable Runtime Lua asset root via `RuntimeOptions.runtimeLuaRoot`.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
This package includes a runnable Flutter showcase app under `example/`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd example
|
||||||
|
flutter run --dart-define=LUA_GAME_ID=showcase
|
||||||
|
```
|
||||||
|
|
||||||
|
Other bundled example packages can be selected with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flutter run --dart-define=LUA_GAME_ID=template
|
||||||
|
flutter run --dart-define=LUA_GAME_ID=ludo
|
||||||
|
flutter run --dart-define=LUA_GAME_ID=flight
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
Add the package to your app:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
flame_lua_runtime: ^0.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Embed a game package:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:flame_lua_runtime/flame_lua_runtime.dart';
|
||||||
|
|
||||||
|
LuaGameWidget(
|
||||||
|
gameId: 'template',
|
||||||
|
runtimeOptions: const RuntimeOptions(
|
||||||
|
runtimeLuaRoot: 'packages/flame_lua_runtime/assets/runtime/lua',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Your app should provide game package assets such as:
|
||||||
|
|
||||||
|
```text
|
||||||
|
assets/games/template/manifest.json
|
||||||
|
assets/games/template/scripts/main.lua
|
||||||
|
assets/games/template/scripts/state.lua
|
||||||
|
assets/games/template/scripts/ui.lua
|
||||||
|
```
|
||||||
|
|
||||||
|
The game manifest declares package-local scripts and shared Runtime Lua modules:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"modules": {
|
||||||
|
"main": "scripts/main.lua",
|
||||||
|
"runtime_ui": "runtime:runtime_ui.lua",
|
||||||
|
"runtime_widgets": "runtime:runtime_widgets.lua",
|
||||||
|
"runtime_commands": "runtime:runtime_commands.lua",
|
||||||
|
"layout": "runtime:layout.lua"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Runtime asset path
|
||||||
|
|
||||||
|
When used as a published package, configure:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
const RuntimeOptions(
|
||||||
|
runtimeLuaRoot: 'packages/flame_lua_runtime/assets/runtime/lua',
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
For source-tree development, the default remains:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
RuntimeOptions.defaultRuntimeLuaRoot // assets/runtime/lua
|
||||||
|
```
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
This package is in early extraction stage. Public API is intentionally small and centered on `LuaGameWidget`, `FlameLuaGame`, `RuntimeOptions`, package repositories, and script engine interfaces.
|
||||||
28
analysis_options.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at https://dart.dev/lints.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
||||||
292
assets/runtime/lua/layout.lua
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
---@alias RuntimeLayoutAlign 'start'|'center'|'end'
|
||||||
|
|
||||||
|
---@class (exact) RuntimeLayoutItem
|
||||||
|
---@field node RuntimeNode
|
||||||
|
---@field marginLeft? number
|
||||||
|
---@field marginRight? number
|
||||||
|
---@field marginTop? number
|
||||||
|
---@field marginBottom? number
|
||||||
|
|
||||||
|
---@class RuntimeLayoutItemOpts
|
||||||
|
---@field margin? number
|
||||||
|
---@field mx? number
|
||||||
|
---@field my? number
|
||||||
|
---@field ml? number
|
||||||
|
---@field mr? number
|
||||||
|
---@field mt? number
|
||||||
|
---@field mb? number
|
||||||
|
---@field marginLeft? number
|
||||||
|
---@field marginRight? number
|
||||||
|
---@field marginTop? number
|
||||||
|
---@field marginBottom? number
|
||||||
|
|
||||||
|
---@class RuntimeLinearLayoutOpts
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field gap? number
|
||||||
|
---@field align? RuntimeLayoutAlign
|
||||||
|
---@field padding? number
|
||||||
|
---@field paddingX? number
|
||||||
|
---@field paddingY? number
|
||||||
|
---@field px? number
|
||||||
|
---@field py? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
|
||||||
|
---@class RuntimeBoxLayoutOpts: RuntimeLinearLayoutOpts
|
||||||
|
---@field rows? integer
|
||||||
|
---@field columns? integer
|
||||||
|
---@field cols? integer
|
||||||
|
---@field cellWidth? number
|
||||||
|
---@field cellHeight? number
|
||||||
|
---@field cellW? number
|
||||||
|
---@field cellH? number
|
||||||
|
---@field gapX? number
|
||||||
|
---@field gapY? number
|
||||||
|
---@field valign? RuntimeLayoutAlign
|
||||||
|
|
||||||
|
---@class RuntimeLayout
|
||||||
|
local layout = {}
|
||||||
|
|
||||||
|
---@param value any
|
||||||
|
---@param fallback number
|
||||||
|
---@return number
|
||||||
|
local function read_number(value, fallback)
|
||||||
|
if type(value) == "number" then
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
return fallback
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param opts? table
|
||||||
|
---@param key string
|
||||||
|
---@return number
|
||||||
|
local function spacing(opts, key)
|
||||||
|
opts = opts or {}
|
||||||
|
if key == "left" then
|
||||||
|
return read_number(opts.paddingLeft, read_number(opts.paddingX, read_number(opts.px, read_number(opts.padding, 0))))
|
||||||
|
end
|
||||||
|
if key == "top" then
|
||||||
|
return read_number(opts.paddingTop, read_number(opts.paddingY, read_number(opts.py, read_number(opts.padding, 0))))
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param node RuntimeNode
|
||||||
|
---@param parent? string
|
||||||
|
local function apply_parent(node, parent)
|
||||||
|
if parent ~= nil and parent ~= "" then
|
||||||
|
node.parent = parent
|
||||||
|
elseif parent == "" then
|
||||||
|
node.parent = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param axis_size number
|
||||||
|
---@param item_size number
|
||||||
|
---@param align RuntimeLayoutAlign
|
||||||
|
---@return number
|
||||||
|
local function align_cross(axis_size, item_size, align)
|
||||||
|
if align == "center" then
|
||||||
|
return (axis_size - item_size) / 2
|
||||||
|
end
|
||||||
|
if align == "end" then
|
||||||
|
return axis_size - item_size
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param value any
|
||||||
|
---@return RuntimeNode
|
||||||
|
local function node_of(value)
|
||||||
|
return value.node or value
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param value any
|
||||||
|
---@param key string
|
||||||
|
---@return number
|
||||||
|
local function margin_of(value, key)
|
||||||
|
local all = read_number(value.margin, 0)
|
||||||
|
if key == "marginLeft" then
|
||||||
|
return read_number(value.marginLeft, read_number(value.ml, read_number(value.mx, all)))
|
||||||
|
end
|
||||||
|
if key == "marginRight" then
|
||||||
|
return read_number(value.marginRight, read_number(value.mr, read_number(value.mx, all)))
|
||||||
|
end
|
||||||
|
if key == "marginTop" then
|
||||||
|
return read_number(value.marginTop, read_number(value.mt, read_number(value.my, all)))
|
||||||
|
end
|
||||||
|
if key == "marginBottom" then
|
||||||
|
return read_number(value.marginBottom, read_number(value.mb, read_number(value.my, all)))
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param node RuntimeNode
|
||||||
|
---@param opts? RuntimeLayoutItemOpts
|
||||||
|
---@return RuntimeLayoutItem
|
||||||
|
function layout.item(node, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
return {
|
||||||
|
node = node,
|
||||||
|
marginLeft = margin_of(opts, "marginLeft"),
|
||||||
|
marginRight = margin_of(opts, "marginRight"),
|
||||||
|
marginTop = margin_of(opts, "marginTop"),
|
||||||
|
marginBottom = margin_of(opts, "marginBottom")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param origin RuntimePoint
|
||||||
|
---@param position RuntimePoint
|
||||||
|
---@return RuntimePoint
|
||||||
|
function layout.local_position(origin, position)
|
||||||
|
return {
|
||||||
|
x = position.x - origin.x,
|
||||||
|
y = position.y - origin.y
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param parent? string
|
||||||
|
---@param items (RuntimeNode|RuntimeLayoutItem)[]
|
||||||
|
---@param opts? RuntimeLinearLayoutOpts
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function layout.row(parent, items, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local cursor = read_number(opts.x, 0) + spacing(opts, "left")
|
||||||
|
local base_y = read_number(opts.y, 0) + spacing(opts, "top")
|
||||||
|
local gap = read_number(opts.gap, 0)
|
||||||
|
local height = read_number(opts.height, 0)
|
||||||
|
local align = opts.align or "start"
|
||||||
|
local nodes = {}
|
||||||
|
|
||||||
|
for index, item in ipairs(items) do
|
||||||
|
local node = node_of(item)
|
||||||
|
cursor = cursor + margin_of(item, "marginLeft")
|
||||||
|
node.x = cursor
|
||||||
|
|
||||||
|
local item_height = read_number(node.height, 0)
|
||||||
|
node.y = base_y + align_cross(height, item_height, align)
|
||||||
|
apply_parent(node, parent)
|
||||||
|
|
||||||
|
cursor = cursor + read_number(node.width, 0) + margin_of(item, "marginRight") + gap
|
||||||
|
nodes[index] = node
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param parent? string
|
||||||
|
---@param items (RuntimeNode|RuntimeLayoutItem)[]
|
||||||
|
---@param opts? RuntimeLinearLayoutOpts
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function layout.column(parent, items, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local cursor = read_number(opts.y, 0) + spacing(opts, "top")
|
||||||
|
local base_x = read_number(opts.x, 0) + spacing(opts, "left")
|
||||||
|
local gap = read_number(opts.gap, 0)
|
||||||
|
local width = read_number(opts.width, 0)
|
||||||
|
local align = opts.align or "start"
|
||||||
|
local nodes = {}
|
||||||
|
|
||||||
|
for index, item in ipairs(items) do
|
||||||
|
local node = node_of(item)
|
||||||
|
cursor = cursor + margin_of(item, "marginTop")
|
||||||
|
node.y = cursor
|
||||||
|
|
||||||
|
local item_width = read_number(node.width, 0)
|
||||||
|
node.x = base_x + align_cross(width, item_width, align)
|
||||||
|
apply_parent(node, parent)
|
||||||
|
|
||||||
|
cursor = cursor + read_number(node.height, 0) + margin_of(item, "marginBottom") + gap
|
||||||
|
nodes[index] = node
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param parent? string
|
||||||
|
---@param items (RuntimeNode|RuntimeLayoutItem)[]
|
||||||
|
---@param opts? RuntimeLinearLayoutOpts
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function layout.stack(parent, items, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local offset_x = read_number(opts.x, 0) + spacing(opts, "left")
|
||||||
|
local offset_y = read_number(opts.y, 0) + spacing(opts, "top")
|
||||||
|
local nodes = {}
|
||||||
|
|
||||||
|
for index, item in ipairs(items) do
|
||||||
|
local node = node_of(item)
|
||||||
|
node.x = read_number(node.x, 0) + offset_x
|
||||||
|
node.y = read_number(node.y, 0) + offset_y
|
||||||
|
apply_parent(node, parent)
|
||||||
|
nodes[index] = node
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param parent? string
|
||||||
|
---@param items (RuntimeNode|RuntimeLayoutItem)[]
|
||||||
|
---@param opts? RuntimeBoxLayoutOpts
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function layout.box(parent, items, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local count = #items
|
||||||
|
local rows = math.floor(read_number(opts.rows, 0))
|
||||||
|
local columns = math.floor(read_number(opts.columns, read_number(opts.cols, 0)))
|
||||||
|
if columns <= 0 and rows > 0 then
|
||||||
|
columns = math.ceil(count / rows)
|
||||||
|
end
|
||||||
|
if columns <= 0 then
|
||||||
|
columns = count > 0 and count or 1
|
||||||
|
end
|
||||||
|
if rows <= 0 then
|
||||||
|
rows = math.ceil(count / columns)
|
||||||
|
end
|
||||||
|
|
||||||
|
local gap_x = read_number(opts.gapX, read_number(opts.gap, 0))
|
||||||
|
local gap_y = read_number(opts.gapY, read_number(opts.gap, 0))
|
||||||
|
local origin_x = read_number(opts.x, 0) + spacing(opts, "left")
|
||||||
|
local origin_y = read_number(opts.y, 0) + spacing(opts, "top")
|
||||||
|
local cell_w = read_number(opts.cellWidth, read_number(opts.cellW, 0))
|
||||||
|
local cell_h = read_number(opts.cellHeight, read_number(opts.cellH, 0))
|
||||||
|
local width = read_number(opts.width, 0)
|
||||||
|
local height = read_number(opts.height, 0)
|
||||||
|
if cell_w <= 0 and width > 0 then
|
||||||
|
cell_w = (width - gap_x * (columns - 1)) / columns
|
||||||
|
end
|
||||||
|
if cell_h <= 0 and height > 0 then
|
||||||
|
cell_h = (height - gap_y * (rows - 1)) / rows
|
||||||
|
end
|
||||||
|
|
||||||
|
local align = opts.align or "start"
|
||||||
|
local valign = opts.valign or "start"
|
||||||
|
local nodes = {}
|
||||||
|
|
||||||
|
for index, item in ipairs(items) do
|
||||||
|
local node = node_of(item)
|
||||||
|
local item_width = read_number(node.width, cell_w)
|
||||||
|
local item_height = read_number(node.height, cell_h)
|
||||||
|
local effective_cell_w = cell_w > 0 and cell_w or item_width
|
||||||
|
local effective_cell_h = cell_h > 0 and cell_h or item_height
|
||||||
|
local col = (index - 1) % columns
|
||||||
|
local row = math.floor((index - 1) / columns)
|
||||||
|
local margin_left = margin_of(item, "marginLeft")
|
||||||
|
local margin_right = margin_of(item, "marginRight")
|
||||||
|
local margin_top = margin_of(item, "marginTop")
|
||||||
|
local margin_bottom = margin_of(item, "marginBottom")
|
||||||
|
local inner_w = effective_cell_w - margin_left - margin_right
|
||||||
|
local inner_h = effective_cell_h - margin_top - margin_bottom
|
||||||
|
|
||||||
|
node.x = origin_x + col * (effective_cell_w + gap_x) + margin_left + align_cross(inner_w, item_width, align)
|
||||||
|
node.y = origin_y + row * (effective_cell_h + gap_y) + margin_top + align_cross(inner_h, item_height, valign)
|
||||||
|
apply_parent(node, parent)
|
||||||
|
nodes[index] = node
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
return layout
|
||||||
261
assets/runtime/lua/runtime_commands.lua
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
---@class (exact) RuntimeCommandOpts
|
||||||
|
---@field id? string
|
||||||
|
---@field group? string
|
||||||
|
---@field commandGroup? string
|
||||||
|
---@field scope? string
|
||||||
|
---@field onComplete? string
|
||||||
|
---@field duration? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeAudioCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field volume? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeBgmCommandOpts: RuntimeAudioCommandOpts
|
||||||
|
---@field channel? string
|
||||||
|
---@field loop? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimeSpineCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field track? integer
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field queue? boolean
|
||||||
|
---@field delay? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeResourceCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field failOnError? boolean
|
||||||
|
|
||||||
|
---@class RuntimeCommands
|
||||||
|
local commands = {}
|
||||||
|
|
||||||
|
---@param opts? table
|
||||||
|
---@return table
|
||||||
|
local function copy_opts(opts)
|
||||||
|
local result = {}
|
||||||
|
if opts ~= nil then
|
||||||
|
for key, value in pairs(opts) do
|
||||||
|
result[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param command_type RuntimeCommandType
|
||||||
|
---@param opts? table
|
||||||
|
---@return RuntimeCommand
|
||||||
|
local function with_type(command_type, opts)
|
||||||
|
local command = copy_opts(opts)
|
||||||
|
command.type = command_type
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param text string
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.toast(text, opts)
|
||||||
|
local command = with_type("toast", opts)
|
||||||
|
command.text = text
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param text string
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.copy_text(text, opts)
|
||||||
|
local command = with_type("copy_text", opts)
|
||||||
|
command.text = text
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param duration number
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.delay(duration, opts)
|
||||||
|
local command = with_type("delay", opts)
|
||||||
|
command.duration = duration
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param items RuntimeCommand[]
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.sequence(items, opts)
|
||||||
|
local command = with_type("sequence", opts)
|
||||||
|
command.commands = items or {}
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param items RuntimeCommand[]
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.parallel(items, opts)
|
||||||
|
local command = with_type("parallel", opts)
|
||||||
|
command.commands = items or {}
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param target string
|
||||||
|
---@param path RuntimePoint[]
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.move_path(target, path, opts)
|
||||||
|
local command = with_type("move_path", opts)
|
||||||
|
command.target = target
|
||||||
|
command.path = path or {}
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param target string
|
||||||
|
---@param x number
|
||||||
|
---@param y number
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.move_to(target, x, y, opts)
|
||||||
|
local command = with_type("move_to", opts)
|
||||||
|
command.target = target
|
||||||
|
command.x = x
|
||||||
|
command.y = y
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param target string
|
||||||
|
---@param alpha number
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.fade_to(target, alpha, opts)
|
||||||
|
local command = with_type("fade_to", opts)
|
||||||
|
command.target = target
|
||||||
|
command.alpha = alpha
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param target string
|
||||||
|
---@param scale number
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.scale_to(target, scale, opts)
|
||||||
|
local command = with_type("scale_to", opts)
|
||||||
|
command.target = target
|
||||||
|
command.scale = scale
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param target string
|
||||||
|
---@param angle number
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.rotate_to(target, angle, opts)
|
||||||
|
local command = with_type("rotate_to", opts)
|
||||||
|
command.target = target
|
||||||
|
command.angle = angle
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param target string
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.remove_node(target, opts)
|
||||||
|
local command = with_type("remove_node", opts)
|
||||||
|
command.target = target
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param target string
|
||||||
|
---@param animation string
|
||||||
|
---@param opts? RuntimeSpineCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.play_spine_animation(target, animation, opts)
|
||||||
|
local command = with_type("play_spine_animation", opts)
|
||||||
|
command.target = target
|
||||||
|
command.animation = animation
|
||||||
|
if command.loop == nil then
|
||||||
|
command.loop = true
|
||||||
|
end
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param asset string
|
||||||
|
---@param opts? RuntimeAudioCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.play_sound(asset, opts)
|
||||||
|
local command = with_type("play_sound", opts)
|
||||||
|
command.asset = asset
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param asset string
|
||||||
|
---@param opts? RuntimeBgmCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.play_bgm(asset, opts)
|
||||||
|
local command = with_type("play_bgm", opts)
|
||||||
|
command.asset = asset
|
||||||
|
if command.channel == nil then
|
||||||
|
command.channel = "bgm"
|
||||||
|
end
|
||||||
|
if command.loop == nil then
|
||||||
|
command.loop = true
|
||||||
|
end
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param channel? string
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.pause_bgm(channel, opts)
|
||||||
|
local command = with_type("pause_bgm", opts)
|
||||||
|
command.channel = channel or "bgm"
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param channel? string
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.resume_bgm(channel, opts)
|
||||||
|
local command = with_type("resume_bgm", opts)
|
||||||
|
command.channel = channel or "bgm"
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param channel? string
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.stop_bgm(channel, opts)
|
||||||
|
local command = with_type("stop_bgm", opts)
|
||||||
|
command.channel = channel or "bgm"
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param group string
|
||||||
|
---@param opts? RuntimeResourceCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.preload_group(group, opts)
|
||||||
|
local command = with_type("preload_resources", opts)
|
||||||
|
command.group = group
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param group string
|
||||||
|
---@param opts? RuntimeCommandOpts
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.evict_group(group, opts)
|
||||||
|
local command = with_type("evict_resources", opts)
|
||||||
|
command.group = group
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.cancel_id(id)
|
||||||
|
return { type = "cancel_commands", id = id }
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param group string
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.cancel_group(group)
|
||||||
|
return { type = "cancel_commands", group = group }
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param scope string
|
||||||
|
---@return RuntimeCommand
|
||||||
|
function commands.cancel_scope(scope)
|
||||||
|
return { type = "cancel_commands", scope = scope }
|
||||||
|
end
|
||||||
|
|
||||||
|
return commands
|
||||||
427
assets/runtime/lua/runtime_ui.lua
Normal file
@@ -0,0 +1,427 @@
|
|||||||
|
---@class RuntimeUi
|
||||||
|
local runtime_ui = {}
|
||||||
|
|
||||||
|
---@generic T: table
|
||||||
|
---@param base? T
|
||||||
|
---@param opts? table
|
||||||
|
---@return T
|
||||||
|
local function merge(base, opts)
|
||||||
|
local result = {}
|
||||||
|
for key, value in pairs(base or {}) do
|
||||||
|
result[key] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts ~= nil then
|
||||||
|
for key, value in pairs(opts) do
|
||||||
|
result[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param value any
|
||||||
|
---@return boolean
|
||||||
|
local function is_table(value)
|
||||||
|
return type(value) == "table"
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param opts? table
|
||||||
|
---@return table
|
||||||
|
local function normalize_opts(opts)
|
||||||
|
local source = opts or {}
|
||||||
|
local result = {}
|
||||||
|
for key, value in pairs(source) do
|
||||||
|
if key ~= "w" and key ~= "h" and key ~= "size" and key ~= "handler" and key ~= "onClick" then
|
||||||
|
result[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if result.width == nil then
|
||||||
|
result.width = source.w or source.size
|
||||||
|
end
|
||||||
|
if result.height == nil then
|
||||||
|
result.height = source.h or source.size
|
||||||
|
end
|
||||||
|
if result.onTap == nil then
|
||||||
|
result.onTap = source.handler or source.onClick
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param base table
|
||||||
|
---@param opts? table
|
||||||
|
---@return table
|
||||||
|
local function node_opts(base, opts)
|
||||||
|
return merge(base, normalize_opts(opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param base? RuntimeNodeProps
|
||||||
|
---@param opts? RuntimeNodeProps
|
||||||
|
---@return RuntimeNodeProps
|
||||||
|
function runtime_ui.style(base, opts)
|
||||||
|
return merge(base or {}, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param parent string
|
||||||
|
---@param opts? RuntimeNodeProps
|
||||||
|
---@return RuntimeNodeProps
|
||||||
|
function runtime_ui.with_parent(parent, opts)
|
||||||
|
return merge(opts or {}, { parent = parent })
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param node_type RuntimeNodeType
|
||||||
|
---@param id string
|
||||||
|
---@param opts? RuntimeNodeProps
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.node(node_type, id, opts)
|
||||||
|
return node_opts({ id = id, type = node_type }, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param x number|RuntimeNodeInit
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.panel(id, x, y, width, height, opts)
|
||||||
|
if is_table(x) then
|
||||||
|
return runtime_ui.node("panel", id, node_opts({ x = 0, y = 0, width = 0, height = 0 }, x))
|
||||||
|
end
|
||||||
|
return runtime_ui.node("panel", id, node_opts({
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = width,
|
||||||
|
height = height
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param x number|RuntimeNodeInit
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.rect(id, x, y, width, height, opts)
|
||||||
|
if is_table(x) then
|
||||||
|
return runtime_ui.node("rect", id, node_opts({ x = 0, y = 0, width = 0, height = 0 }, x))
|
||||||
|
end
|
||||||
|
return runtime_ui.node("rect", id, node_opts({
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = width,
|
||||||
|
height = height
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param x number|RuntimeNodeInit
|
||||||
|
---@param y? number
|
||||||
|
---@param size? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.circle(id, x, y, size, opts)
|
||||||
|
if is_table(x) then
|
||||||
|
return runtime_ui.node("circle", id, node_opts({ x = 0, y = 0, width = 0, height = 0 }, x))
|
||||||
|
end
|
||||||
|
return runtime_ui.node("circle", id, node_opts({
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = size,
|
||||||
|
height = size
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param x number|RuntimeNodeInit
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.line(id, x, y, width, height, opts)
|
||||||
|
if is_table(x) then
|
||||||
|
return runtime_ui.node("line", id, node_opts({ x = 0, y = 0, width = 0, height = 0 }, x))
|
||||||
|
end
|
||||||
|
return runtime_ui.node("line", id, node_opts({
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = width,
|
||||||
|
height = height
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param x number|RuntimeNodeInit
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param value? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.progress(id, x, y, width, height, value, opts)
|
||||||
|
if is_table(x) then
|
||||||
|
return runtime_ui.node("progress", id, node_opts({ x = 0, y = 0, width = 0, height = 0, value = 0 }, x))
|
||||||
|
end
|
||||||
|
return runtime_ui.node("progress", id, node_opts({
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = width,
|
||||||
|
height = height,
|
||||||
|
value = value
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param x number|RuntimeNodeInit
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.particle(id, x, y, width, height, opts)
|
||||||
|
if is_table(x) then
|
||||||
|
return runtime_ui.node("particle", id, node_opts({ x = 0, y = 0, width = 0, height = 0 }, x))
|
||||||
|
end
|
||||||
|
return runtime_ui.node("particle", id, node_opts({
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = width,
|
||||||
|
height = height
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param text string|RuntimeNodeInit
|
||||||
|
---@param x? number
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.text(id, text, x, y, width, height, opts)
|
||||||
|
if is_table(text) then
|
||||||
|
return runtime_ui.node("text", id, node_opts({ text = "", x = 0, y = 0, width = 0, height = 0 }, text))
|
||||||
|
end
|
||||||
|
return runtime_ui.node("text", id, node_opts({
|
||||||
|
text = text,
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = width,
|
||||||
|
height = height
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param text string|RuntimeNodeInit
|
||||||
|
---@param x? number
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param handler? string
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.button(id, text, x, y, width, height, handler, opts)
|
||||||
|
if is_table(text) then
|
||||||
|
return runtime_ui.node("button", id, node_opts({
|
||||||
|
text = "",
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 0,
|
||||||
|
height = 0,
|
||||||
|
interactive = true
|
||||||
|
}, text))
|
||||||
|
end
|
||||||
|
return runtime_ui.node("button", id, node_opts({
|
||||||
|
text = text,
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = width,
|
||||||
|
height = height,
|
||||||
|
interactive = true,
|
||||||
|
onTap = handler
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param x number|RuntimeNodeInit
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.list_view(id, x, y, width, height, opts)
|
||||||
|
if is_table(x) then
|
||||||
|
return runtime_ui.node("listView", id, node_opts({ x = 0, y = 0, width = 0, height = 0 }, x))
|
||||||
|
end
|
||||||
|
return runtime_ui.node("listView", id, node_opts({
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = width,
|
||||||
|
height = height
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param asset string|RuntimeNodeInit
|
||||||
|
---@param x? number
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.image(id, asset, x, y, width, height, opts)
|
||||||
|
if is_table(asset) then
|
||||||
|
return runtime_ui.node("image", id, node_opts({ asset = "", x = 0, y = 0, width = 0, height = 0 }, asset))
|
||||||
|
end
|
||||||
|
return runtime_ui.node("image", id, node_opts({
|
||||||
|
asset = asset,
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = width,
|
||||||
|
height = height
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param asset string|RuntimeNodeInit
|
||||||
|
---@param x? number
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.sprite(id, asset, x, y, width, height, opts)
|
||||||
|
if is_table(asset) then
|
||||||
|
return runtime_ui.node("sprite", id, node_opts({ asset = "", x = 0, y = 0, width = 0, height = 0 }, asset))
|
||||||
|
end
|
||||||
|
return runtime_ui.node("sprite", id, node_opts({
|
||||||
|
asset = asset,
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = width,
|
||||||
|
height = height
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param asset string|RuntimeNodeInit
|
||||||
|
---@param x? number
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param animation? string
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function runtime_ui.spine(id, asset, x, y, width, height, animation, opts)
|
||||||
|
if is_table(asset) then
|
||||||
|
return runtime_ui.node("spine", id, node_opts({ asset = "", x = 0, y = 0, width = 0, height = 0, loop = true }, asset))
|
||||||
|
end
|
||||||
|
return runtime_ui.node("spine", id, node_opts({
|
||||||
|
asset = asset,
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
width = width,
|
||||||
|
height = height,
|
||||||
|
animation = animation,
|
||||||
|
loop = true
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param props RuntimeNodeInit
|
||||||
|
---@return RuntimeNodeUpdate
|
||||||
|
function runtime_ui.update(id, props)
|
||||||
|
return { id = id, props = normalize_opts(props or {}) }
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param text string
|
||||||
|
---@return RuntimeNodeUpdate
|
||||||
|
function runtime_ui.text_update(id, text)
|
||||||
|
return runtime_ui.update(id, { text = text })
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param visible boolean
|
||||||
|
---@return RuntimeNodeUpdate
|
||||||
|
function runtime_ui.visible_update(id, visible)
|
||||||
|
return runtime_ui.update(id, { visible = visible })
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param alpha number
|
||||||
|
---@return RuntimeNodeUpdate
|
||||||
|
function runtime_ui.alpha_update(id, alpha)
|
||||||
|
return runtime_ui.update(id, { alpha = alpha })
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param scale number
|
||||||
|
---@return RuntimeNodeUpdate
|
||||||
|
function runtime_ui.scale_update(id, scale)
|
||||||
|
return runtime_ui.update(id, { scale = scale })
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param x number
|
||||||
|
---@param y number
|
||||||
|
---@return RuntimeNodeUpdate
|
||||||
|
function runtime_ui.position_update(id, x, y)
|
||||||
|
return runtime_ui.update(id, { x = x, y = y })
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param width number
|
||||||
|
---@param height number
|
||||||
|
---@return RuntimeNodeUpdate
|
||||||
|
function runtime_ui.size_update(id, width, height)
|
||||||
|
return runtime_ui.update(id, { width = width, height = height })
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param x number
|
||||||
|
---@param y number
|
||||||
|
---@param scale number
|
||||||
|
---@param rotation number
|
||||||
|
---@return RuntimeNodeUpdate
|
||||||
|
function runtime_ui.transform_update(id, x, y, scale, rotation)
|
||||||
|
return runtime_ui.update(id, {
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
scale = scale,
|
||||||
|
rotation = rotation
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param ids string[]
|
||||||
|
---@param props RuntimeNodeInit
|
||||||
|
---@return RuntimeNodeUpdate[]
|
||||||
|
function runtime_ui.batch_update(ids, props)
|
||||||
|
local updates = {}
|
||||||
|
for _, id in ipairs(ids) do
|
||||||
|
table.insert(updates, runtime_ui.update(id, props))
|
||||||
|
end
|
||||||
|
return updates
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param nodes RuntimeNode[]
|
||||||
|
---@param node RuntimeNode
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function runtime_ui.append(nodes, node)
|
||||||
|
table.insert(nodes, node)
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param nodes RuntimeNode[]
|
||||||
|
---@param extra_nodes RuntimeNode[]
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function runtime_ui.append_all(nodes, extra_nodes)
|
||||||
|
for _, node in ipairs(extra_nodes) do
|
||||||
|
table.insert(nodes, node)
|
||||||
|
end
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
return runtime_ui
|
||||||
773
assets/runtime/lua/runtime_widgets.lua
Normal file
@@ -0,0 +1,773 @@
|
|||||||
|
local runtime_ui = runtime.import("runtime_ui")
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDialogButton
|
||||||
|
---@field id? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler string
|
||||||
|
---@field color? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDialogOpts
|
||||||
|
---@field screenWidth? number
|
||||||
|
---@field screenHeight? number
|
||||||
|
---@field overlay? boolean
|
||||||
|
---@field overlayColor? string
|
||||||
|
---@field blockInput? boolean
|
||||||
|
---@field layer? integer
|
||||||
|
---@field color? string
|
||||||
|
---@field radius? number
|
||||||
|
---@field panelStyle? RuntimeNodeProps
|
||||||
|
---@field titleColor? string
|
||||||
|
---@field titleSize? number
|
||||||
|
---@field titleStyle? RuntimeNodeProps
|
||||||
|
---@field messageColor? string
|
||||||
|
---@field messageSize? number
|
||||||
|
---@field messageStyle? RuntimeNodeProps
|
||||||
|
---@field buttons? RuntimeDialogButton[]
|
||||||
|
---@field buttonGap? number
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class (exact) RuntimeLabeledProgressOpts: RuntimeNodeProps
|
||||||
|
---@field labelHeight? number
|
||||||
|
---@field labelStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimePillOpts: RuntimeNodeInit
|
||||||
|
---@field panelStyle? RuntimeNodeProps
|
||||||
|
---@field textStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeTextButtonOpts: RuntimeNodeInit
|
||||||
|
---@field variant? 'primary'|'secondary'|'ghost'
|
||||||
|
|
||||||
|
---@class RuntimeListItemOpts: RuntimeTextButtonOpts
|
||||||
|
---@field selected? boolean
|
||||||
|
---@field activeColor? string
|
||||||
|
---@field inactiveColor? string
|
||||||
|
|
||||||
|
---@class RuntimeTabItem
|
||||||
|
---@field id? string
|
||||||
|
---@field key? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler? string
|
||||||
|
---@field selected? boolean
|
||||||
|
|
||||||
|
---@class RuntimeTabsOpts: RuntimeNodeInit
|
||||||
|
---@field tabs RuntimeTabItem[]
|
||||||
|
---@field selected? string
|
||||||
|
---@field gap? number
|
||||||
|
---@field itemWidth? number
|
||||||
|
---@field itemHeight? number
|
||||||
|
---@field activeColor? string
|
||||||
|
---@field inactiveColor? string
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeActionItem
|
||||||
|
---@field id? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler? string
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field color? string
|
||||||
|
---@field style? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeActionRowOpts: RuntimeNodeInit
|
||||||
|
---@field actions RuntimeActionItem[]
|
||||||
|
---@field gap? number
|
||||||
|
---@field itemWidth? number
|
||||||
|
---@field itemHeight? number
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimePanelHeaderOpts: RuntimeNodeInit
|
||||||
|
---@field eyebrow? string
|
||||||
|
---@field title string
|
||||||
|
---@field summary? string
|
||||||
|
---@field gap? number
|
||||||
|
---@field eyebrowId? string
|
||||||
|
---@field titleId? string
|
||||||
|
---@field summaryId? string
|
||||||
|
---@field eyebrowHeight? number
|
||||||
|
---@field titleHeight? number
|
||||||
|
---@field summaryHeight? number
|
||||||
|
---@field eyebrowStyle? RuntimeNodeProps
|
||||||
|
---@field titleStyle? RuntimeNodeProps
|
||||||
|
---@field summaryStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeWidgetTheme
|
||||||
|
---@field primary? string
|
||||||
|
---@field secondary? string
|
||||||
|
---@field success? string
|
||||||
|
---@field overlay? string
|
||||||
|
---@field surface? string
|
||||||
|
---@field surfaceAlt? string
|
||||||
|
---@field card? string
|
||||||
|
---@field text? string
|
||||||
|
---@field muted? string
|
||||||
|
---@field progress? string
|
||||||
|
---@field transparent? string
|
||||||
|
|
||||||
|
---@class RuntimeWidgets
|
||||||
|
local widgets = {}
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- Theme/config
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
---@type RuntimeWidgetTheme
|
||||||
|
local widget_theme = {
|
||||||
|
primary = "#ff2563eb",
|
||||||
|
secondary = "#ff475569",
|
||||||
|
success = "#ff22c55e",
|
||||||
|
overlay = "#99000000",
|
||||||
|
surface = "#ff1e293b",
|
||||||
|
surfaceAlt = "#ff334155",
|
||||||
|
card = "#ee111827",
|
||||||
|
text = "#ffffffff",
|
||||||
|
muted = "#ffe5e7eb",
|
||||||
|
progress = "#ff22c55e",
|
||||||
|
transparent = "#00000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- Internal helpers
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
---@generic T: table
|
||||||
|
---@param base? T
|
||||||
|
---@param opts? table
|
||||||
|
---@return T
|
||||||
|
local function merge(base, opts)
|
||||||
|
local result = {}
|
||||||
|
for key, value in pairs(base or {}) do
|
||||||
|
result[key] = value
|
||||||
|
end
|
||||||
|
for key, value in pairs(opts or {}) do
|
||||||
|
result[key] = value
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param value any
|
||||||
|
---@return boolean
|
||||||
|
local function is_table(value)
|
||||||
|
return type(value) == "table"
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param source? table
|
||||||
|
---@param keys string[]
|
||||||
|
---@return table
|
||||||
|
local function without_keys(source, keys)
|
||||||
|
local result = merge(source or {}, nil)
|
||||||
|
for _, key in ipairs(keys) do
|
||||||
|
result[key] = nil
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNodeInit
|
||||||
|
local function normalize_box(opts)
|
||||||
|
local result = merge(opts or {}, nil)
|
||||||
|
if result.width == nil then
|
||||||
|
result.width = result.w or result.size
|
||||||
|
end
|
||||||
|
if result.height == nil then
|
||||||
|
result.height = result.h or result.size
|
||||||
|
end
|
||||||
|
result.w = nil
|
||||||
|
result.h = nil
|
||||||
|
result.size = nil
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@param fallback? table
|
||||||
|
---@return RuntimeNodeInit
|
||||||
|
local function with_box(opts, fallback)
|
||||||
|
return merge(fallback or {}, normalize_box(opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param target RuntimeNode[]
|
||||||
|
---@param source? RuntimeNode[]
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
local function append_all(target, source)
|
||||||
|
for _, node in ipairs(source or {}) do
|
||||||
|
table.insert(target, node)
|
||||||
|
end
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param parent string
|
||||||
|
---@param opts? RuntimeNodeProps
|
||||||
|
---@return RuntimeNodeProps
|
||||||
|
local function child_opts(parent, opts)
|
||||||
|
return merge(opts or {}, { parent = parent })
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param value table
|
||||||
|
---@param opts? table
|
||||||
|
---@param key string
|
||||||
|
---@return table, table
|
||||||
|
local function collection_args(value, opts, key)
|
||||||
|
local options = opts or {}
|
||||||
|
local items = value
|
||||||
|
if is_table(value) and value[key] ~= nil then
|
||||||
|
options = value
|
||||||
|
items = value[key]
|
||||||
|
end
|
||||||
|
return items or {}, options
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param tokens? RuntimeWidgetTheme
|
||||||
|
---@return RuntimeWidgets
|
||||||
|
function widgets.configure(tokens)
|
||||||
|
widget_theme = merge(widget_theme, tokens or {})
|
||||||
|
return widgets
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param name string
|
||||||
|
---@return string
|
||||||
|
local function theme_color(name)
|
||||||
|
return widget_theme[name] or "#ffffffff"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- Text helpers
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param text string|RuntimeNodeInit
|
||||||
|
---@param x? number
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function widgets.label(id, text, x, y, width, height, opts)
|
||||||
|
local fallback = { text = "", x = 0, y = 0, width = 0, height = 0 }
|
||||||
|
local options
|
||||||
|
if is_table(text) then
|
||||||
|
options = with_box(text, fallback)
|
||||||
|
else
|
||||||
|
options = with_box(merge({ text = text, x = x, y = y, width = width, height = height }, opts), fallback)
|
||||||
|
end
|
||||||
|
return runtime_ui.text(id, merge({
|
||||||
|
color = theme_color("muted"),
|
||||||
|
fontSize = 14,
|
||||||
|
textAlign = "left"
|
||||||
|
}, options))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param text string|RuntimeNodeInit
|
||||||
|
---@param x? number
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function widgets.section_title(id, text, x, y, width, height, opts)
|
||||||
|
local options
|
||||||
|
if is_table(text) then
|
||||||
|
options = text
|
||||||
|
else
|
||||||
|
options = merge({ text = text, x = x, y = y, width = width, height = height }, opts)
|
||||||
|
end
|
||||||
|
return widgets.label(id, merge({
|
||||||
|
color = theme_color("text"),
|
||||||
|
fontSize = 18,
|
||||||
|
textAlign = "left"
|
||||||
|
}, options))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- Badges and buttons
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param text string|RuntimePillOpts
|
||||||
|
---@param x? number
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimePillOpts
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function widgets.pill(id, text, x, y, width, height, opts)
|
||||||
|
local options
|
||||||
|
if is_table(text) then
|
||||||
|
options = with_box(text, { text = "", x = 0, y = 0, width = 0, height = 0 })
|
||||||
|
else
|
||||||
|
options = with_box(merge({ text = text, x = x, y = y, width = width, height = height }, opts), {
|
||||||
|
text = "",
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 0,
|
||||||
|
height = 0
|
||||||
|
})
|
||||||
|
end
|
||||||
|
local panel_opts = merge({
|
||||||
|
color = theme_color("surfaceAlt"),
|
||||||
|
radius = options.height / 2
|
||||||
|
}, options.panelStyle)
|
||||||
|
panel_opts = merge(panel_opts, without_keys(options, { "text", "panelStyle", "textStyle" }))
|
||||||
|
local text_opts = child_opts(id, merge({
|
||||||
|
color = theme_color("text"),
|
||||||
|
fontSize = 12,
|
||||||
|
textAlign = "center"
|
||||||
|
}, options.textStyle))
|
||||||
|
return {
|
||||||
|
runtime_ui.panel(id, panel_opts),
|
||||||
|
runtime_ui.text(id .. "_text", merge({
|
||||||
|
text = options.text or "",
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = options.width,
|
||||||
|
height = options.height
|
||||||
|
}, text_opts))
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param text string|RuntimeTextButtonOpts
|
||||||
|
---@param x? number
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param handler? string
|
||||||
|
---@param opts? RuntimeTextButtonOpts
|
||||||
|
---@return RuntimeNode
|
||||||
|
function widgets.text_button(id, text, x, y, width, height, handler, opts)
|
||||||
|
local options
|
||||||
|
if is_table(text) then
|
||||||
|
options = with_box(text, { text = "", x = 0, y = 0, width = 0, height = 0 })
|
||||||
|
else
|
||||||
|
options = with_box(merge({ text = text, x = x, y = y, width = width, height = height, handler = handler }, opts), {
|
||||||
|
text = "",
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 0,
|
||||||
|
height = 0
|
||||||
|
})
|
||||||
|
end
|
||||||
|
local variant = options.variant or "primary"
|
||||||
|
local color = theme_color("primary")
|
||||||
|
if variant == "secondary" then
|
||||||
|
color = theme_color("secondary")
|
||||||
|
elseif variant == "ghost" then
|
||||||
|
color = theme_color("transparent")
|
||||||
|
end
|
||||||
|
return runtime_ui.button(id, merge({
|
||||||
|
color = color,
|
||||||
|
radius = 10,
|
||||||
|
fontSize = 13,
|
||||||
|
textAlign = "center"
|
||||||
|
}, without_keys(options, { "variant" })))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param text string|RuntimeListItemOpts
|
||||||
|
---@param x? number
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param handler? string
|
||||||
|
---@param opts? RuntimeListItemOpts
|
||||||
|
---@return RuntimeNode
|
||||||
|
function widgets.list_item(id, text, x, y, width, height, handler, opts)
|
||||||
|
local options
|
||||||
|
if is_table(text) then
|
||||||
|
options = with_box(text, { text = "", x = 0, y = 0, width = 0, height = 0 })
|
||||||
|
else
|
||||||
|
options = with_box(merge({ text = text, x = x, y = y, width = width, height = height, handler = handler }, opts), {
|
||||||
|
text = "",
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 0,
|
||||||
|
height = 0
|
||||||
|
})
|
||||||
|
end
|
||||||
|
local selected = options.selected == true
|
||||||
|
local color = selected and (options.activeColor or theme_color("primary")) or (options.inactiveColor or theme_color("surface"))
|
||||||
|
return widgets.text_button(id, merge({
|
||||||
|
color = color,
|
||||||
|
radius = 10,
|
||||||
|
fontSize = 12
|
||||||
|
}, without_keys(options, { "selected", "activeColor", "inactiveColor" })))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param tabs RuntimeTabItem[]|RuntimeTabsOpts
|
||||||
|
---@param opts? RuntimeTabsOpts
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function widgets.tabs(id, tabs, opts)
|
||||||
|
local items, options = collection_args(tabs, opts, "tabs")
|
||||||
|
|
||||||
|
local origin_x = options.x or 0
|
||||||
|
local origin_y = options.y or 0
|
||||||
|
local gap = options.gap or 6
|
||||||
|
local item_w = options.itemWidth or options.width or options.w or 72
|
||||||
|
local item_h = options.itemHeight or options.height or options.h or 24
|
||||||
|
local nodes = {}
|
||||||
|
|
||||||
|
for index, tab in ipairs(items) do
|
||||||
|
local key = tab.key or tostring(index)
|
||||||
|
local selected = tab.selected == true or key == options.selected
|
||||||
|
local node_id = tab.id or (id .. "_" .. key)
|
||||||
|
local style = merge({
|
||||||
|
x = origin_x + (index - 1) * (item_w + gap),
|
||||||
|
y = origin_y,
|
||||||
|
w = item_w,
|
||||||
|
h = item_h,
|
||||||
|
text = tab.text or key,
|
||||||
|
handler = tab.handler or key,
|
||||||
|
selected = selected,
|
||||||
|
activeColor = options.activeColor,
|
||||||
|
inactiveColor = options.inactiveColor
|
||||||
|
}, options.buttonStyle)
|
||||||
|
style = merge(style, without_keys(options, {
|
||||||
|
"tabs",
|
||||||
|
"selected",
|
||||||
|
"gap",
|
||||||
|
"itemWidth",
|
||||||
|
"itemHeight",
|
||||||
|
"activeColor",
|
||||||
|
"inactiveColor",
|
||||||
|
"buttonStyle",
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
"width",
|
||||||
|
"height",
|
||||||
|
"w",
|
||||||
|
"h"
|
||||||
|
}))
|
||||||
|
nodes[index] = widgets.list_item(node_id, style)
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param actions RuntimeActionItem[]|RuntimeActionRowOpts
|
||||||
|
---@param opts? RuntimeActionRowOpts
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function widgets.action_row(id, actions, opts)
|
||||||
|
local items, options = collection_args(actions, opts, "actions")
|
||||||
|
|
||||||
|
local count = #items
|
||||||
|
local gap = options.gap or 8
|
||||||
|
local origin_x = options.x or 0
|
||||||
|
local origin_y = options.y or 0
|
||||||
|
local item_h = options.itemHeight or options.height or options.h or 32
|
||||||
|
local item_w = options.itemWidth
|
||||||
|
if item_w == nil and (options.width ~= nil or options.w ~= nil) and count > 0 then
|
||||||
|
item_w = ((options.width or options.w) - gap * (count - 1)) / count
|
||||||
|
end
|
||||||
|
item_w = item_w or 88
|
||||||
|
|
||||||
|
local nodes = {}
|
||||||
|
for index, action in ipairs(items) do
|
||||||
|
local visible = action.visible ~= false
|
||||||
|
local style = merge({
|
||||||
|
x = origin_x + (index - 1) * (item_w + gap),
|
||||||
|
y = origin_y,
|
||||||
|
w = item_w,
|
||||||
|
h = item_h,
|
||||||
|
text = visible and (action.text or "") or "",
|
||||||
|
handler = visible and (action.handler or "noop") or "noop",
|
||||||
|
color = action.color or (index == 1 and theme_color("primary") or theme_color("surfaceAlt")),
|
||||||
|
visible = visible
|
||||||
|
}, options.buttonStyle)
|
||||||
|
style = merge(style, action.style)
|
||||||
|
style = merge(style, without_keys(options, {
|
||||||
|
"actions",
|
||||||
|
"gap",
|
||||||
|
"itemWidth",
|
||||||
|
"itemHeight",
|
||||||
|
"buttonStyle",
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
"width",
|
||||||
|
"height",
|
||||||
|
"w",
|
||||||
|
"h"
|
||||||
|
}))
|
||||||
|
nodes[index] = widgets.text_button(action.id or (id .. "_" .. tostring(index)), style)
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- Panels and headers
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param opts RuntimePanelHeaderOpts
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function widgets.panel_header(id, opts)
|
||||||
|
local options = with_box(opts or {}, { x = 0, y = 0, width = 0, height = 0, title = "" })
|
||||||
|
local nodes = {}
|
||||||
|
local cursor = options.y
|
||||||
|
local gap = options.gap or 4
|
||||||
|
local layer = options.layer
|
||||||
|
local parent = options.parent
|
||||||
|
|
||||||
|
if options.eyebrow ~= nil and options.eyebrow ~= "" then
|
||||||
|
local h = options.eyebrowHeight or 20
|
||||||
|
table.insert(nodes, widgets.label(options.eyebrowId or (id .. "_eyebrow"), merge({
|
||||||
|
text = options.eyebrow,
|
||||||
|
x = options.x,
|
||||||
|
y = cursor,
|
||||||
|
width = options.width,
|
||||||
|
height = h,
|
||||||
|
parent = parent,
|
||||||
|
layer = layer,
|
||||||
|
color = theme_color("primary"),
|
||||||
|
fontSize = 13
|
||||||
|
}, options.eyebrowStyle)))
|
||||||
|
cursor = cursor + h + gap
|
||||||
|
end
|
||||||
|
|
||||||
|
local title_h = options.titleHeight or 28
|
||||||
|
table.insert(nodes, widgets.section_title(options.titleId or (id .. "_title"), merge({
|
||||||
|
text = options.title or "",
|
||||||
|
x = options.x,
|
||||||
|
y = cursor,
|
||||||
|
width = options.width,
|
||||||
|
height = title_h,
|
||||||
|
parent = parent,
|
||||||
|
layer = layer,
|
||||||
|
fontSize = 20
|
||||||
|
}, options.titleStyle)))
|
||||||
|
cursor = cursor + title_h + gap
|
||||||
|
|
||||||
|
if options.summary ~= nil and options.summary ~= "" then
|
||||||
|
local h = options.summaryHeight or 22
|
||||||
|
table.insert(nodes, widgets.label(options.summaryId or (id .. "_summary"), merge({
|
||||||
|
text = options.summary,
|
||||||
|
x = options.x,
|
||||||
|
y = cursor,
|
||||||
|
width = options.width,
|
||||||
|
height = h,
|
||||||
|
parent = parent,
|
||||||
|
layer = layer,
|
||||||
|
color = theme_color("muted"),
|
||||||
|
fontSize = 12
|
||||||
|
}, options.summaryStyle)))
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param width number|RuntimeNodeInit
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function widgets.overlay(id, width, height, opts)
|
||||||
|
if is_table(width) then
|
||||||
|
return runtime_ui.rect(id, merge({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
color = theme_color("overlay"),
|
||||||
|
layer = 900
|
||||||
|
}, width))
|
||||||
|
end
|
||||||
|
return runtime_ui.rect(id, 0, 0, width, height, merge({
|
||||||
|
color = theme_color("overlay"),
|
||||||
|
layer = 900
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param x number|RuntimeNodeInit
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function widgets.card(id, x, y, width, height, opts)
|
||||||
|
if is_table(x) then
|
||||||
|
return runtime_ui.panel(id, merge({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 0,
|
||||||
|
height = 0,
|
||||||
|
color = theme_color("card"),
|
||||||
|
radius = 14,
|
||||||
|
layer = 910
|
||||||
|
}, x))
|
||||||
|
end
|
||||||
|
return runtime_ui.panel(id, x, y, width, height, merge({
|
||||||
|
color = theme_color("card"),
|
||||||
|
radius = 14,
|
||||||
|
layer = 910
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- Progress
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param x number|RuntimeNodeInit
|
||||||
|
---@param y? number
|
||||||
|
---@param width? number
|
||||||
|
---@param height? number
|
||||||
|
---@param value? number
|
||||||
|
---@param opts? RuntimeNodeInit
|
||||||
|
---@return RuntimeNode
|
||||||
|
function widgets.progress_bar(id, x, y, width, height, value, opts)
|
||||||
|
if is_table(x) then
|
||||||
|
return runtime_ui.progress(id, merge({
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
width = 0,
|
||||||
|
height = 0,
|
||||||
|
value = 0,
|
||||||
|
color = theme_color("progress"),
|
||||||
|
radius = 6
|
||||||
|
}, x))
|
||||||
|
end
|
||||||
|
return runtime_ui.progress(id, x, y, width, height, value, merge({
|
||||||
|
color = theme_color("progress"),
|
||||||
|
radius = 6
|
||||||
|
}, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param label string
|
||||||
|
---@param x number
|
||||||
|
---@param y number
|
||||||
|
---@param width number
|
||||||
|
---@param height number
|
||||||
|
---@param value number
|
||||||
|
---@param opts? RuntimeLabeledProgressOpts
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function widgets.labeled_progress(id, label, x, y, width, height, value, opts)
|
||||||
|
local label_height = opts ~= nil and opts.labelHeight or 24
|
||||||
|
local progress_opts = merge(opts or {}, nil)
|
||||||
|
progress_opts.labelHeight = nil
|
||||||
|
progress_opts.labelStyle = nil
|
||||||
|
return {
|
||||||
|
runtime_ui.text(id .. "_label", label, x, y, width, label_height, merge({
|
||||||
|
color = theme_color("text"),
|
||||||
|
fontSize = 16
|
||||||
|
}, opts ~= nil and opts.labelStyle or nil)),
|
||||||
|
widgets.progress_bar(id, x, y + label_height + 6, width, height, value, progress_opts)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
-- Dialogs
|
||||||
|
-- -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
---@param parent string
|
||||||
|
---@param id string
|
||||||
|
---@param buttons RuntimeDialogButton[]
|
||||||
|
---@param x number
|
||||||
|
---@param y number
|
||||||
|
---@param width number
|
||||||
|
---@param height number
|
||||||
|
---@param gap? number
|
||||||
|
---@param opts? RuntimeNodeProps
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function widgets.button_row(parent, id, buttons, x, y, width, height, gap, opts)
|
||||||
|
local nodes = {}
|
||||||
|
local count = #buttons
|
||||||
|
if count == 0 then
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
local actual_gap = gap or 8
|
||||||
|
local button_width = (width - actual_gap * (count - 1)) / count
|
||||||
|
for index, button in ipairs(buttons) do
|
||||||
|
local button_id = button.id or (id .. "_button_" .. index)
|
||||||
|
table.insert(nodes, runtime_ui.button(
|
||||||
|
button_id,
|
||||||
|
button.text or "OK",
|
||||||
|
x + (index - 1) * (button_width + actual_gap),
|
||||||
|
y,
|
||||||
|
button_width,
|
||||||
|
height,
|
||||||
|
button.handler,
|
||||||
|
child_opts(parent, merge({
|
||||||
|
layer = 920,
|
||||||
|
color = button.color or theme_color("primary"),
|
||||||
|
radius = 10
|
||||||
|
}, opts))
|
||||||
|
))
|
||||||
|
end
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param id string
|
||||||
|
---@param title string
|
||||||
|
---@param message string
|
||||||
|
---@param x number
|
||||||
|
---@param y number
|
||||||
|
---@param width number
|
||||||
|
---@param height number
|
||||||
|
---@param opts? RuntimeDialogOpts
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function widgets.dialog(id, title, message, x, y, width, height, opts)
|
||||||
|
local options = opts or {}
|
||||||
|
local nodes = {}
|
||||||
|
local layer = options.layer or 900
|
||||||
|
local screen_width = options.screenWidth or 720
|
||||||
|
local screen_height = options.screenHeight or 720
|
||||||
|
|
||||||
|
if options.overlay ~= false then
|
||||||
|
table.insert(nodes, widgets.overlay(id .. "_overlay", screen_width, screen_height, {
|
||||||
|
layer = layer,
|
||||||
|
color = options.overlayColor or theme_color("overlay"),
|
||||||
|
interactive = options.blockInput == true
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(nodes, widgets.card(id, x, y, width, height, merge({
|
||||||
|
layer = layer + 1,
|
||||||
|
color = options.color or theme_color("card"),
|
||||||
|
radius = options.radius or 16
|
||||||
|
}, options.panelStyle)))
|
||||||
|
|
||||||
|
table.insert(nodes, runtime_ui.text(
|
||||||
|
id .. "_title",
|
||||||
|
title or "",
|
||||||
|
20,
|
||||||
|
18,
|
||||||
|
width - 40,
|
||||||
|
32,
|
||||||
|
child_opts(id, merge({
|
||||||
|
layer = layer + 2,
|
||||||
|
color = options.titleColor or theme_color("text"),
|
||||||
|
fontSize = options.titleSize or 22
|
||||||
|
}, options.titleStyle))
|
||||||
|
))
|
||||||
|
|
||||||
|
table.insert(nodes, runtime_ui.text(
|
||||||
|
id .. "_message",
|
||||||
|
message or "",
|
||||||
|
20,
|
||||||
|
60,
|
||||||
|
width - 40,
|
||||||
|
height - 116,
|
||||||
|
child_opts(id, merge({
|
||||||
|
layer = layer + 2,
|
||||||
|
color = options.messageColor or theme_color("muted"),
|
||||||
|
fontSize = options.messageSize or 16
|
||||||
|
}, options.messageStyle))
|
||||||
|
))
|
||||||
|
|
||||||
|
append_all(nodes, widgets.button_row(
|
||||||
|
id,
|
||||||
|
id,
|
||||||
|
options.buttons or {},
|
||||||
|
20,
|
||||||
|
height - 48,
|
||||||
|
width - 40,
|
||||||
|
34,
|
||||||
|
options.buttonGap or 8,
|
||||||
|
options.buttonStyle
|
||||||
|
))
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
return widgets
|
||||||
45
example/.gitignore
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.build/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
.swiftpm/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
/coverage/
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
||||||
17
example/.luarc.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
||||||
|
"runtime.version": "Lua 5.4",
|
||||||
|
"workspace.checkThirdParty": false,
|
||||||
|
"diagnostics.globals": ["runtime"],
|
||||||
|
"diagnostics.neededFileStatus": {
|
||||||
|
"undefined-field": "Any",
|
||||||
|
"inject-field": "Any",
|
||||||
|
"assign-type-mismatch": "Any",
|
||||||
|
"param-type-mismatch": "Any",
|
||||||
|
"return-type-mismatch": "Any",
|
||||||
|
"missing-fields": "Any",
|
||||||
|
"undefined-global": "Any"
|
||||||
|
},
|
||||||
|
"type.weakUnionCheck": true,
|
||||||
|
"type.weakNilCheck": true
|
||||||
|
}
|
||||||
45
example/.metadata
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
|
||||||
|
channel: "stable"
|
||||||
|
|
||||||
|
project_type: app
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
- platform: android
|
||||||
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
- platform: ios
|
||||||
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
- platform: linux
|
||||||
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
- platform: macos
|
||||||
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
- platform: web
|
||||||
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
- platform: windows
|
||||||
|
create_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
base_revision: adc901062556672b4138e18a4dc62a4be8f4b3c2
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||||
16
example/README.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# flame_lua_ludo
|
||||||
|
|
||||||
|
A new Flutter project.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter application.
|
||||||
|
|
||||||
|
A few resources to get you started if this is your first Flutter project:
|
||||||
|
|
||||||
|
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||||
|
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||||
|
|
||||||
|
For help getting started with Flutter development, view the
|
||||||
|
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
||||||
28
example/analysis_options.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at https://dart.dev/lints.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
||||||
14
example/android/.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
gradle-wrapper.jar
|
||||||
|
/.gradle
|
||||||
|
/captures/
|
||||||
|
/gradlew
|
||||||
|
/gradlew.bat
|
||||||
|
/local.properties
|
||||||
|
GeneratedPluginRegistrant.java
|
||||||
|
.cxx/
|
||||||
|
|
||||||
|
# Remember to never publicly share your keystore.
|
||||||
|
# See https://flutter.dev/to/reference-keystore
|
||||||
|
key.properties
|
||||||
|
**/*.keystore
|
||||||
|
**/*.jks
|
||||||
44
example/android/app/build.gradle.kts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
plugins {
|
||||||
|
id("com.android.application")
|
||||||
|
id("kotlin-android")
|
||||||
|
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||||
|
id("dev.flutter.flutter-gradle-plugin")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "dev.flame_lua.runtime_showcase"
|
||||||
|
compileSdk = flutter.compileSdkVersion
|
||||||
|
ndkVersion = flutter.ndkVersion
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
|
applicationId = "dev.flame_lua.runtime_showcase"
|
||||||
|
// You can update the following values to match your application needs.
|
||||||
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
|
minSdk = flutter.minSdkVersion
|
||||||
|
targetSdk = flutter.targetSdkVersion
|
||||||
|
versionCode = flutter.versionCode
|
||||||
|
versionName = flutter.versionName
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
// TODO: Add your own signing config for the release build.
|
||||||
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source = "../.."
|
||||||
|
}
|
||||||
7
example/android/app/src/debug/AndroidManifest.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
45
example/android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<application
|
||||||
|
android:label="flame_lua_runtime_showcase"
|
||||||
|
android:name="${applicationName}"
|
||||||
|
android:icon="@mipmap/ic_launcher">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:taskAffinity=""
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
|
the Android process has started. This theme is visible to the user
|
||||||
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
|
to determine the Window background behind the Flutter UI. -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
|
android:resource="@style/NormalTheme"
|
||||||
|
/>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
</application>
|
||||||
|
<!-- Required to query activities that can process text, see:
|
||||||
|
https://developer.android.com/training/package-visibility and
|
||||||
|
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||||
|
|
||||||
|
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||||
|
<data android:mimeType="text/plain"/>
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package dev.flame_lua.runtime_showcase
|
||||||
|
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
class MainActivity : FlutterActivity()
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="?android:colorBackground" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@android:color/white" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
BIN
example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 544 B |
BIN
example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 442 B |
BIN
example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 721 B |
BIN
example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
18
example/android/app/src/main/res/values-night/styles.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
18
example/android/app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
7
example/android/app/src/profile/AndroidManifest.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
24
example/android/build.gradle.kts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val newBuildDir: Directory =
|
||||||
|
rootProject.layout.buildDirectory
|
||||||
|
.dir("../../build")
|
||||||
|
.get()
|
||||||
|
rootProject.layout.buildDirectory.value(newBuildDir)
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
|
||||||
|
project.layout.buildDirectory.value(newSubprojectBuildDir)
|
||||||
|
}
|
||||||
|
subprojects {
|
||||||
|
project.evaluationDependsOn(":app")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<Delete>("clean") {
|
||||||
|
delete(rootProject.layout.buildDirectory)
|
||||||
|
}
|
||||||
3
example/android/gradle.properties
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
5
example/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
|
||||||
26
example/android/settings.gradle.kts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
pluginManagement {
|
||||||
|
val flutterSdkPath =
|
||||||
|
run {
|
||||||
|
val properties = java.util.Properties()
|
||||||
|
file("local.properties").inputStream().use { properties.load(it) }
|
||||||
|
val flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
|
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
|
||||||
|
flutterSdkPath
|
||||||
|
}
|
||||||
|
|
||||||
|
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||||
|
id("com.android.application") version "8.9.1" apply false
|
||||||
|
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
|
||||||
|
}
|
||||||
|
|
||||||
|
include(":app")
|
||||||
22
example/assets/games/flight/manifest.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"gameId": "flight",
|
||||||
|
"name": "Lua 飞行棋基础版",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"runtimeApiVersion": 1,
|
||||||
|
"entry": "scripts/main.lua",
|
||||||
|
"assetsBase": "assets",
|
||||||
|
"modules": {
|
||||||
|
"runtime_ui": "runtime:runtime_ui.lua",
|
||||||
|
"runtime_widgets": "runtime:runtime_widgets.lua",
|
||||||
|
"runtime_commands": "runtime:runtime_commands.lua",
|
||||||
|
"layout": "runtime:layout.lua",
|
||||||
|
"theme": "scripts/theme.lua",
|
||||||
|
"styles": "scripts/styles.lua",
|
||||||
|
"state": "scripts/state.lua",
|
||||||
|
"board": "scripts/board.lua",
|
||||||
|
"rules": "scripts/rules.lua",
|
||||||
|
"ui": "scripts/ui.lua",
|
||||||
|
"animation": "scripts/animation.lua"
|
||||||
|
},
|
||||||
|
"resources": {}
|
||||||
|
}
|
||||||
17
example/assets/games/flight/scripts/animation.lua
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---@type RuntimeCommands
|
||||||
|
local commands = runtime.import("runtime_commands")
|
||||||
|
|
||||||
|
local animation = {}
|
||||||
|
|
||||||
|
function animation.move_piece(piece_id, path)
|
||||||
|
return commands.move_path(piece_id, path, {
|
||||||
|
duration = 0.55,
|
||||||
|
onComplete = "piece_move_done"
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function animation.toast(text)
|
||||||
|
return commands.toast(text)
|
||||||
|
end
|
||||||
|
|
||||||
|
return animation
|
||||||
91
example/assets/games/flight/scripts/board.lua
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
local board = {
|
||||||
|
route_length = 40,
|
||||||
|
finish_progress = 40,
|
||||||
|
start = {
|
||||||
|
red = 1,
|
||||||
|
yellow = 11,
|
||||||
|
blue = 21,
|
||||||
|
green = 31
|
||||||
|
},
|
||||||
|
homes = {
|
||||||
|
red = {
|
||||||
|
{ x = 92, y = 132 },
|
||||||
|
{ x = 142, y = 132 }
|
||||||
|
},
|
||||||
|
yellow = {
|
||||||
|
{ x = 518, y = 132 },
|
||||||
|
{ x = 568, y = 132 }
|
||||||
|
},
|
||||||
|
blue = {
|
||||||
|
{ x = 518, y = 548 },
|
||||||
|
{ x = 568, y = 548 }
|
||||||
|
},
|
||||||
|
green = {
|
||||||
|
{ x = 92, y = 548 },
|
||||||
|
{ x = 142, y = 548 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
finish = {
|
||||||
|
red = {
|
||||||
|
{ x = 290, y = 330 },
|
||||||
|
{ x = 250, y = 330 }
|
||||||
|
},
|
||||||
|
yellow = {
|
||||||
|
{ x = 350, y = 330 },
|
||||||
|
{ x = 390, y = 330 }
|
||||||
|
},
|
||||||
|
blue = {
|
||||||
|
{ x = 350, y = 390 },
|
||||||
|
{ x = 390, y = 390 }
|
||||||
|
},
|
||||||
|
green = {
|
||||||
|
{ x = 290, y = 390 },
|
||||||
|
{ x = 250, y = 390 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
route = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
local function add(x, y)
|
||||||
|
table.insert(board.route, { x = x, y = y })
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 0, 9 do add(200 + i * 32, 104) end
|
||||||
|
for i = 0, 9 do add(520, 136 + i * 32) end
|
||||||
|
for i = 0, 9 do add(488 - i * 32, 456) end
|
||||||
|
for i = 0, 9 do add(168, 424 - i * 32) end
|
||||||
|
|
||||||
|
function board.home_position(piece)
|
||||||
|
local index = piece.id:sub(-1) == "2" and 2 or 1
|
||||||
|
return board.homes[piece.owner][index]
|
||||||
|
end
|
||||||
|
|
||||||
|
function board.finish_position(piece)
|
||||||
|
local index = piece.id:sub(-1) == "2" and 2 or 1
|
||||||
|
return board.finish[piece.owner][index]
|
||||||
|
end
|
||||||
|
|
||||||
|
function board.route_index(owner, progress)
|
||||||
|
local start = board.start[owner]
|
||||||
|
local index = start + progress - 1
|
||||||
|
while index > board.route_length do
|
||||||
|
index = index - board.route_length
|
||||||
|
end
|
||||||
|
return index
|
||||||
|
end
|
||||||
|
|
||||||
|
function board.route_position(owner, progress)
|
||||||
|
return board.route[board.route_index(owner, progress)]
|
||||||
|
end
|
||||||
|
|
||||||
|
function board.position(piece)
|
||||||
|
if piece.status == "home" then
|
||||||
|
return board.home_position(piece)
|
||||||
|
end
|
||||||
|
if piece.status == "finished" then
|
||||||
|
return board.finish_position(piece)
|
||||||
|
end
|
||||||
|
return board.route_position(piece.owner, piece.progress)
|
||||||
|
end
|
||||||
|
|
||||||
|
return board
|
||||||
154
example/assets/games/flight/scripts/main.lua
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
local state = runtime.import("state")
|
||||||
|
local rules = runtime.import("rules")
|
||||||
|
local ui = runtime.import("ui")
|
||||||
|
local animation = runtime.import("animation")
|
||||||
|
---@type RuntimeWidgets
|
||||||
|
local widgets = runtime.import("runtime_widgets")
|
||||||
|
local theme = runtime.import("theme")
|
||||||
|
|
||||||
|
widgets.configure({
|
||||||
|
primary = theme.colors.dice_button,
|
||||||
|
secondary = theme.colors.board_inner,
|
||||||
|
success = theme.colors.green,
|
||||||
|
overlay = "#99000000",
|
||||||
|
surface = theme.colors.top_bar,
|
||||||
|
surfaceAlt = theme.colors.board_inner,
|
||||||
|
card = theme.colors.top_bar,
|
||||||
|
text = theme.colors.text,
|
||||||
|
muted = theme.colors.muted_text,
|
||||||
|
progress = theme.colors.green,
|
||||||
|
transparent = "#00000000"
|
||||||
|
})
|
||||||
|
|
||||||
|
function smoke_test(ctx)
|
||||||
|
return ctx ~= nil
|
||||||
|
and ctx.runtimeApiVersion ~= nil
|
||||||
|
and state.current_player ~= nil
|
||||||
|
and rules.plan_move ~= nil
|
||||||
|
and ui.create_board_nodes ~= nil
|
||||||
|
and animation.move_piece ~= nil
|
||||||
|
and widgets.dialog ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function init(ctx)
|
||||||
|
return {
|
||||||
|
render = { creates = ui.create_board_nodes() },
|
||||||
|
ui = { creates = ui.create_ui_nodes() },
|
||||||
|
commands = {}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handle_roll_dice()
|
||||||
|
if state.phase == "game_over" then
|
||||||
|
return { commands = { animation.toast("游戏已结束") } }
|
||||||
|
end
|
||||||
|
|
||||||
|
if state.phase ~= "waiting_roll" then
|
||||||
|
return { commands = { animation.toast("请先完成当前移动") } }
|
||||||
|
end
|
||||||
|
|
||||||
|
state.dice = rules.next_dice()
|
||||||
|
local movable = rules.movable_pieces()
|
||||||
|
|
||||||
|
if #movable == 0 then
|
||||||
|
local message = state.current_player .. " 无可移动飞机"
|
||||||
|
if state.dice ~= 6 then
|
||||||
|
state.current_player = rules.next_player()
|
||||||
|
end
|
||||||
|
state.phase = "waiting_roll"
|
||||||
|
return {
|
||||||
|
ui = { updates = ui.dice_and_status_updates() },
|
||||||
|
render = { updates = ui.highlight_updates(rules.all_piece_ids(), false) },
|
||||||
|
commands = { animation.toast(message) }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
state.phase = "waiting_piece"
|
||||||
|
return {
|
||||||
|
ui = { updates = ui.dice_and_status_updates() },
|
||||||
|
render = { updates = ui.highlight_updates(movable, true) }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handle_piece_tap(piece_id)
|
||||||
|
if state.phase ~= "waiting_piece" then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local piece = state.pieces[piece_id]
|
||||||
|
if piece == nil or piece.owner ~= state.current_player or not rules.can_move(piece, state.dice) then
|
||||||
|
return { commands = { animation.toast("该飞机不能移动") } }
|
||||||
|
end
|
||||||
|
|
||||||
|
local plan = rules.plan_move(piece, state.dice)
|
||||||
|
state.phase = "animating"
|
||||||
|
state.selected_piece = piece_id
|
||||||
|
state.pending_move = plan
|
||||||
|
|
||||||
|
return {
|
||||||
|
ui = { updates = ui.dice_and_status_updates() },
|
||||||
|
render = { updates = ui.highlight_updates(rules.all_piece_ids(), false) },
|
||||||
|
commands = { animation.move_piece(piece_id, plan.path) }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handle_move_done()
|
||||||
|
local plan = state.pending_move
|
||||||
|
if plan == nil then
|
||||||
|
state.phase = "waiting_roll"
|
||||||
|
return { ui = { updates = ui.dice_and_status_updates() } }
|
||||||
|
end
|
||||||
|
|
||||||
|
rules.commit_move(plan)
|
||||||
|
local moved_piece = state.pieces[plan.piece_id]
|
||||||
|
local updates = ui.piece_position_updates(plan.captures)
|
||||||
|
table.insert(updates, ui.piece_position_update(plan.piece_id))
|
||||||
|
|
||||||
|
local messages = {}
|
||||||
|
if #plan.captures > 0 then
|
||||||
|
table.insert(messages, "撞回 " .. tostring(#plan.captures) .. " 架飞机")
|
||||||
|
end
|
||||||
|
|
||||||
|
if rules.player_finished(moved_piece.owner) then
|
||||||
|
state.winner = moved_piece.owner
|
||||||
|
state.phase = "game_over"
|
||||||
|
table.insert(messages, moved_piece.owner .. " 获胜")
|
||||||
|
else
|
||||||
|
if state.dice ~= 6 then
|
||||||
|
state.current_player = rules.next_player()
|
||||||
|
else
|
||||||
|
table.insert(messages, "掷出 6,额外回合")
|
||||||
|
end
|
||||||
|
state.phase = "waiting_roll"
|
||||||
|
end
|
||||||
|
|
||||||
|
state.selected_piece = nil
|
||||||
|
state.pending_move = nil
|
||||||
|
|
||||||
|
local commands = {}
|
||||||
|
if #messages > 0 then
|
||||||
|
commands = { animation.toast(table.concat(messages, ";")) }
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
ui = { updates = ui.dice_and_status_updates() },
|
||||||
|
render = { updates = updates },
|
||||||
|
commands = commands
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_event(event)
|
||||||
|
if event.handler == "roll_dice" then
|
||||||
|
return handle_roll_dice()
|
||||||
|
end
|
||||||
|
|
||||||
|
if event.handler == "piece_tap" then
|
||||||
|
return handle_piece_tap(event.target)
|
||||||
|
end
|
||||||
|
|
||||||
|
if event.handler == "piece_move_done" then
|
||||||
|
return handle_move_done()
|
||||||
|
end
|
||||||
|
|
||||||
|
return {}
|
||||||
|
end
|
||||||
153
example/assets/games/flight/scripts/rules.lua
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
local state = runtime.import("state")
|
||||||
|
local board = runtime.import("board")
|
||||||
|
|
||||||
|
local rules = {}
|
||||||
|
|
||||||
|
local function piece_index(piece_id)
|
||||||
|
return piece_id:sub(-1) == "2" and 2 or 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.next_player()
|
||||||
|
for index, player in ipairs(state.players) do
|
||||||
|
if player == state.current_player then
|
||||||
|
local next_index = index + 1
|
||||||
|
if next_index > #state.players then
|
||||||
|
next_index = 1
|
||||||
|
end
|
||||||
|
return state.players[next_index]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return state.players[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.next_dice()
|
||||||
|
state.dice_index = state.dice_index + 1
|
||||||
|
if state.dice_index > #state.dice_values then
|
||||||
|
state.dice_index = 1
|
||||||
|
end
|
||||||
|
return state.dice_values[state.dice_index]
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.can_move(piece, dice)
|
||||||
|
if piece == nil or piece.status == "finished" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if piece.status == "home" then
|
||||||
|
return dice == 6
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.movable_pieces()
|
||||||
|
local result = {}
|
||||||
|
for id, piece in pairs(state.pieces) do
|
||||||
|
if piece.owner == state.current_player and rules.can_move(piece, state.dice) then
|
||||||
|
table.insert(result, id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.all_piece_ids()
|
||||||
|
local result = {}
|
||||||
|
for id, _ in pairs(state.pieces) do
|
||||||
|
table.insert(result, id)
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.piece_position(piece)
|
||||||
|
return board.position(piece)
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.plan_move(piece, dice)
|
||||||
|
local path = {}
|
||||||
|
local final_status = "route"
|
||||||
|
local final_progress = piece.progress
|
||||||
|
|
||||||
|
if piece.status == "home" then
|
||||||
|
final_progress = 1
|
||||||
|
table.insert(path, board.route_position(piece.owner, final_progress))
|
||||||
|
else
|
||||||
|
for step = 1, dice do
|
||||||
|
local progress = piece.progress + step
|
||||||
|
if progress >= board.finish_progress then
|
||||||
|
final_status = "finished"
|
||||||
|
final_progress = board.finish_progress
|
||||||
|
table.insert(path, board.finish_position(piece))
|
||||||
|
break
|
||||||
|
end
|
||||||
|
table.insert(path, board.route_position(piece.owner, progress))
|
||||||
|
final_progress = progress
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local captures = {}
|
||||||
|
if final_status == "route" then
|
||||||
|
local landing_index = board.route_index(piece.owner, final_progress)
|
||||||
|
for id, other in pairs(state.pieces) do
|
||||||
|
if id ~= piece.id and other.owner ~= piece.owner and other.status == "route" then
|
||||||
|
if board.route_index(other.owner, other.progress) == landing_index then
|
||||||
|
table.insert(captures, id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
piece_id = piece.id,
|
||||||
|
final_status = final_status,
|
||||||
|
final_progress = final_progress,
|
||||||
|
path = path,
|
||||||
|
captures = captures
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.commit_move(plan)
|
||||||
|
local piece = state.pieces[plan.piece_id]
|
||||||
|
piece.status = plan.final_status
|
||||||
|
piece.progress = plan.final_progress
|
||||||
|
|
||||||
|
for _, captured_id in ipairs(plan.captures) do
|
||||||
|
local captured = state.pieces[captured_id]
|
||||||
|
captured.status = "home"
|
||||||
|
captured.progress = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.player_finished(owner)
|
||||||
|
for _, piece in pairs(state.pieces) do
|
||||||
|
if piece.owner == owner and piece.status ~= "finished" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.status_text()
|
||||||
|
if state.winner ~= nil then
|
||||||
|
return "胜利者: " .. state.winner
|
||||||
|
end
|
||||||
|
if state.phase == "waiting_roll" then
|
||||||
|
return "请 " .. state.current_player .. " 掷骰子"
|
||||||
|
end
|
||||||
|
if state.phase == "waiting_piece" then
|
||||||
|
return "请选择 " .. state.current_player .. " 的飞机"
|
||||||
|
end
|
||||||
|
if state.phase == "animating" then
|
||||||
|
return "飞机移动中..."
|
||||||
|
end
|
||||||
|
return "准备中"
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.piece_label(piece)
|
||||||
|
if piece.status == "home" then
|
||||||
|
return "待飞"
|
||||||
|
end
|
||||||
|
if piece.status == "finished" then
|
||||||
|
return "到达"
|
||||||
|
end
|
||||||
|
return tostring(piece.progress)
|
||||||
|
end
|
||||||
|
|
||||||
|
return rules
|
||||||
589
example/assets/games/flight/scripts/runtime_defs.lua
Normal file
@@ -0,0 +1,589 @@
|
|||||||
|
---@meta
|
||||||
|
--- COMMON RUNTIME TYPES SECTION.
|
||||||
|
--- Source of truth: tool/lua_runtime_defs_common.lua
|
||||||
|
--- After editing this common section, run:
|
||||||
|
--- dart run tool/generate_lua_runtime_defs.dart
|
||||||
|
|
||||||
|
|
||||||
|
---@alias RuntimeNodeType
|
||||||
|
---| 'panel'
|
||||||
|
---| 'button'
|
||||||
|
---| 'text'
|
||||||
|
---| 'circle'
|
||||||
|
---| 'rect'
|
||||||
|
---| 'line'
|
||||||
|
---| 'progress'
|
||||||
|
---| 'listView'
|
||||||
|
---| 'sprite'
|
||||||
|
---| 'image'
|
||||||
|
---| 'spine'
|
||||||
|
---| 'particle'
|
||||||
|
|
||||||
|
---@alias RuntimeAnchor
|
||||||
|
---| 'center'
|
||||||
|
---| 'topLeft'
|
||||||
|
---| 'topRight'
|
||||||
|
---| 'bottomLeft'
|
||||||
|
---| 'bottomRight'
|
||||||
|
|
||||||
|
---@alias RuntimeTextAlign
|
||||||
|
---| 'left'
|
||||||
|
---| 'center'
|
||||||
|
---| 'right'
|
||||||
|
|
||||||
|
---@alias RuntimeParticlePreset
|
||||||
|
---| 'burst'
|
||||||
|
---| 'trail'
|
||||||
|
---| 'snow'
|
||||||
|
---| 'confetti'
|
||||||
|
|
||||||
|
---@alias RuntimeCommandType
|
||||||
|
---| 'move_path'
|
||||||
|
---| 'move_to'
|
||||||
|
---| 'fade_to'
|
||||||
|
---| 'scale_to'
|
||||||
|
---| 'rotate_to'
|
||||||
|
---| 'remove_node'
|
||||||
|
---| 'sequence'
|
||||||
|
---| 'parallel'
|
||||||
|
---| 'delay'
|
||||||
|
---| 'toast'
|
||||||
|
---| 'play_sound'
|
||||||
|
---| 'play_bgm'
|
||||||
|
---| 'pause_bgm'
|
||||||
|
---| 'resume_bgm'
|
||||||
|
---| 'stop_bgm'
|
||||||
|
---| 'preload_resources'
|
||||||
|
---| 'evict_resources'
|
||||||
|
---| 'cancel_commands'
|
||||||
|
---| 'play_spine_animation'
|
||||||
|
---| 'copy_text'
|
||||||
|
|
||||||
|
---@alias RuntimeEventType
|
||||||
|
---| 'tap'
|
||||||
|
---| 'animation_done'
|
||||||
|
---| 'resize'
|
||||||
|
---| 'scroll'
|
||||||
|
|
||||||
|
---@alias RuntimeScaleMode
|
||||||
|
---| 'fit'
|
||||||
|
---| 'fill'
|
||||||
|
---| 'stretch'
|
||||||
|
---| 'none'
|
||||||
|
|
||||||
|
---@alias RuntimeLayoutAlign
|
||||||
|
---| 'start'
|
||||||
|
---| 'center'
|
||||||
|
---| 'end'
|
||||||
|
|
||||||
|
---@alias RuntimeButtonVariant
|
||||||
|
---| 'primary'
|
||||||
|
---| 'secondary'
|
||||||
|
---| 'ghost'
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNode
|
||||||
|
---@field id string
|
||||||
|
---@field type RuntimeNodeType
|
||||||
|
---@field parent? string
|
||||||
|
---@field asset? string Normal image/sprite/spine asset key. For button nodes this is the normal-state image.
|
||||||
|
---@field pressedAsset? string Button pressed-state image asset key.
|
||||||
|
---@field disabledAsset? string Button disabled-state image asset key.
|
||||||
|
---@field animation? string
|
||||||
|
---@field skin? string
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field text? string
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
---@field paddingRight? number
|
||||||
|
---@field paddingBottom? number
|
||||||
|
---@field anchor? RuntimeAnchor
|
||||||
|
---@field layer? integer
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field alpha? number
|
||||||
|
---@field scale? number
|
||||||
|
---@field rotation? number
|
||||||
|
---@field color? string
|
||||||
|
---@field fontSize? number
|
||||||
|
---@field textAlign? RuntimeTextAlign
|
||||||
|
---@field radius? number
|
||||||
|
---@field strokeWidth? number
|
||||||
|
---@field value? number
|
||||||
|
---@field scrollX? number
|
||||||
|
---@field scrollY? number
|
||||||
|
---@field contentWidth? number
|
||||||
|
---@field contentHeight? number
|
||||||
|
---@field virtualized? boolean
|
||||||
|
---@field cacheExtent? number
|
||||||
|
---@field inertia? boolean
|
||||||
|
---@field scrollbarThumbColor? string
|
||||||
|
---@field scrollbarTrackColor? string
|
||||||
|
---@field scrollbarThickness? number
|
||||||
|
---@field scrollbarVisible? boolean
|
||||||
|
---@field interactive? boolean
|
||||||
|
---@field onTap? string
|
||||||
|
---@field onScroll? string
|
||||||
|
---@field preset? RuntimeParticlePreset
|
||||||
|
---@field count? integer
|
||||||
|
---@field duration? number
|
||||||
|
---@field speedMin? number
|
||||||
|
---@field speedMax? number
|
||||||
|
---@field gravityX? number
|
||||||
|
---@field gravityY? number
|
||||||
|
---@field spread? number
|
||||||
|
---@field colorTo? string
|
||||||
|
---@field radiusTo? number
|
||||||
|
---@field autoRemove? boolean
|
||||||
|
---@field fadeOut? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNodeProps
|
||||||
|
---@field type? RuntimeNodeType
|
||||||
|
---@field parent? string
|
||||||
|
---@field asset? string Normal image/sprite/spine asset key. For button nodes this is the normal-state image.
|
||||||
|
---@field pressedAsset? string Button pressed-state image asset key.
|
||||||
|
---@field disabledAsset? string Button disabled-state image asset key.
|
||||||
|
---@field animation? string
|
||||||
|
---@field skin? string
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field text? string
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
---@field paddingRight? number
|
||||||
|
---@field paddingBottom? number
|
||||||
|
---@field anchor? RuntimeAnchor
|
||||||
|
---@field layer? integer
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field alpha? number
|
||||||
|
---@field scale? number
|
||||||
|
---@field rotation? number
|
||||||
|
---@field color? string
|
||||||
|
---@field fontSize? number
|
||||||
|
---@field textAlign? RuntimeTextAlign
|
||||||
|
---@field radius? number
|
||||||
|
---@field strokeWidth? number
|
||||||
|
---@field value? number
|
||||||
|
---@field scrollX? number
|
||||||
|
---@field scrollY? number
|
||||||
|
---@field contentWidth? number
|
||||||
|
---@field contentHeight? number
|
||||||
|
---@field virtualized? boolean
|
||||||
|
---@field cacheExtent? number
|
||||||
|
---@field inertia? boolean
|
||||||
|
---@field scrollbarThumbColor? string
|
||||||
|
---@field scrollbarTrackColor? string
|
||||||
|
---@field scrollbarThickness? number
|
||||||
|
---@field scrollbarVisible? boolean
|
||||||
|
---@field interactive? boolean
|
||||||
|
---@field onTap? string
|
||||||
|
---@field onScroll? string
|
||||||
|
---@field preset? RuntimeParticlePreset
|
||||||
|
---@field count? integer
|
||||||
|
---@field duration? number
|
||||||
|
---@field speedMin? number
|
||||||
|
---@field speedMax? number
|
||||||
|
---@field gravityX? number
|
||||||
|
---@field gravityY? number
|
||||||
|
---@field spread? number
|
||||||
|
---@field colorTo? string
|
||||||
|
---@field radiusTo? number
|
||||||
|
---@field autoRemove? boolean
|
||||||
|
---@field fadeOut? boolean
|
||||||
|
|
||||||
|
---Helper-only fields accepted by runtime_ui/runtime_widgets. They are normalized
|
||||||
|
---before the node/update crosses the Dart Runtime protocol boundary.
|
||||||
|
---@class RuntimeNodeInit: RuntimeNodeProps
|
||||||
|
---@field w? number Alias for width.
|
||||||
|
---@field h? number Alias for height.
|
||||||
|
---@field size? number Alias for both width and height.
|
||||||
|
---@field handler? string Alias for onTap.
|
||||||
|
---@field onClick? string Alias for onTap.
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNodeUpdate
|
||||||
|
---@field id string
|
||||||
|
---@field props RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNodeRemove
|
||||||
|
---@field id string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDiffSection
|
||||||
|
---@field creates? RuntimeNode[]
|
||||||
|
---@field updates? RuntimeNodeUpdate[]
|
||||||
|
---@field removes? (string|RuntimeNodeRemove)[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDiff
|
||||||
|
---@field render? RuntimeDiffSection
|
||||||
|
---@field ui? RuntimeDiffSection
|
||||||
|
---@field commands? RuntimeCommand[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeEvent
|
||||||
|
---@field type RuntimeEventType|string
|
||||||
|
---@field target? string
|
||||||
|
---@field handler? string
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field data? table
|
||||||
|
|
||||||
|
---@class (exact) RuntimeCommand
|
||||||
|
---@field type RuntimeCommandType
|
||||||
|
---@field target? string
|
||||||
|
---@field scope? string
|
||||||
|
---@field id? string
|
||||||
|
---@field group? string
|
||||||
|
---@field commandGroup? string
|
||||||
|
---@field onComplete? string
|
||||||
|
---@field duration? number
|
||||||
|
---@field commands? RuntimeCommand[]
|
||||||
|
---@field path? RuntimePoint[]
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field alpha? number
|
||||||
|
---@field scale? number
|
||||||
|
---@field angle? number
|
||||||
|
---@field text? string
|
||||||
|
---@field message? string
|
||||||
|
---@field asset? string
|
||||||
|
---@field name? string
|
||||||
|
---@field volume? number
|
||||||
|
---@field channel? string
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field failOnError? boolean
|
||||||
|
---@field animation? string
|
||||||
|
---@field track? integer
|
||||||
|
---@field queue? boolean
|
||||||
|
---@field delay? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeCommandOpts
|
||||||
|
---@field id? string
|
||||||
|
---@field group? string
|
||||||
|
---@field commandGroup? string
|
||||||
|
---@field scope? string
|
||||||
|
---@field onComplete? string
|
||||||
|
---@field duration? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeAudioCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field volume? number
|
||||||
|
---@field name? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeBgmCommandOpts: RuntimeAudioCommandOpts
|
||||||
|
---@field channel? string
|
||||||
|
---@field loop? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimeSpineCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field track? integer
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field queue? boolean
|
||||||
|
---@field delay? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeResourceCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field failOnError? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimePoint
|
||||||
|
---@field x number
|
||||||
|
---@field y number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeLocaleContext
|
||||||
|
---@field requested string
|
||||||
|
---@field resolved string
|
||||||
|
---@field default string
|
||||||
|
---@field supported string[]
|
||||||
|
---@field languageCode string
|
||||||
|
---@field scriptCode? string
|
||||||
|
---@field countryCode? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeScreenContext
|
||||||
|
---@field width number
|
||||||
|
---@field height number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDesignContext
|
||||||
|
---@field width number
|
||||||
|
---@field height number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeViewportContext
|
||||||
|
---@field x number
|
||||||
|
---@field y number
|
||||||
|
---@field width number
|
||||||
|
---@field height number
|
||||||
|
---@field scaleX number
|
||||||
|
---@field scaleY number
|
||||||
|
---@field scaleMode RuntimeScaleMode|string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeContext
|
||||||
|
---@field screen RuntimeScreenContext
|
||||||
|
---@field design RuntimeDesignContext
|
||||||
|
---@field viewport RuntimeViewportContext
|
||||||
|
---@field seed integer
|
||||||
|
---@field runtimeApiVersion integer
|
||||||
|
---@field gameId string
|
||||||
|
---@field gameVersion string
|
||||||
|
---@field locale? RuntimeLocaleContext
|
||||||
|
|
||||||
|
---@class RuntimeUi
|
||||||
|
---@field style fun(base?: RuntimeNodeProps, opts?: RuntimeNodeProps): RuntimeNodeProps
|
||||||
|
---@field with_parent fun(parent: string, opts?: RuntimeNodeProps): RuntimeNodeProps
|
||||||
|
---@field node fun(node_type: RuntimeNodeType, id: string, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field panel fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field rect fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field circle fun(id: string, x: number|RuntimeNodeInit, y?: number, size?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field line fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field progress fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, value?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field particle fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field text fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field button fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, handler?: string, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field list_view fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field image fun(id: string, asset: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field sprite fun(id: string, asset: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field spine fun(id: string, asset: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, animation?: string, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field update fun(id: string, props: RuntimeNodeInit): RuntimeNodeUpdate
|
||||||
|
---@field text_update fun(id: string, text: string): RuntimeNodeUpdate
|
||||||
|
---@field visible_update fun(id: string, visible: boolean): RuntimeNodeUpdate
|
||||||
|
---@field alpha_update fun(id: string, alpha: number): RuntimeNodeUpdate
|
||||||
|
---@field scale_update fun(id: string, scale: number): RuntimeNodeUpdate
|
||||||
|
---@field position_update fun(id: string, x: number, y: number): RuntimeNodeUpdate
|
||||||
|
---@field size_update fun(id: string, width: number, height: number): RuntimeNodeUpdate
|
||||||
|
---@field transform_update fun(id: string, x: number, y: number, scale: number, rotation: number): RuntimeNodeUpdate
|
||||||
|
---@field batch_update fun(ids: string[], props: RuntimeNodeInit): RuntimeNodeUpdate[]
|
||||||
|
---@field append fun(nodes: RuntimeNode[], node: RuntimeNode): RuntimeNode[]
|
||||||
|
---@field append_all fun(nodes: RuntimeNode[], extra_nodes: RuntimeNode[]): RuntimeNode[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDialogButton
|
||||||
|
---@field id? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler string
|
||||||
|
---@field color? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDialogOpts
|
||||||
|
---@field screenWidth? number
|
||||||
|
---@field screenHeight? number
|
||||||
|
---@field overlay? boolean
|
||||||
|
---@field overlayColor? string
|
||||||
|
---@field blockInput? boolean
|
||||||
|
---@field layer? integer
|
||||||
|
---@field color? string
|
||||||
|
---@field radius? number
|
||||||
|
---@field panelStyle? RuntimeNodeProps
|
||||||
|
---@field titleColor? string
|
||||||
|
---@field titleSize? number
|
||||||
|
---@field titleStyle? RuntimeNodeProps
|
||||||
|
---@field messageColor? string
|
||||||
|
---@field messageSize? number
|
||||||
|
---@field messageStyle? RuntimeNodeProps
|
||||||
|
---@field buttons? RuntimeDialogButton[]
|
||||||
|
---@field buttonGap? number
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeLabeledProgressOpts: RuntimeNodeInit
|
||||||
|
---@field labelHeight? number
|
||||||
|
---@field labelStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimePillOpts: RuntimeNodeInit
|
||||||
|
---@field panelStyle? RuntimeNodeProps
|
||||||
|
---@field textStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeTextButtonOpts: RuntimeNodeInit
|
||||||
|
---@field variant? RuntimeButtonVariant
|
||||||
|
|
||||||
|
---@class RuntimeListItemOpts: RuntimeTextButtonOpts
|
||||||
|
---@field selected? boolean
|
||||||
|
---@field activeColor? string
|
||||||
|
---@field inactiveColor? string
|
||||||
|
|
||||||
|
---@class RuntimeTabItem
|
||||||
|
---@field id? string
|
||||||
|
---@field key? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler? string
|
||||||
|
---@field selected? boolean
|
||||||
|
|
||||||
|
---@class RuntimeTabsOpts: RuntimeNodeInit
|
||||||
|
---@field tabs? RuntimeTabItem[]
|
||||||
|
---@field selected? string
|
||||||
|
---@field gap? number
|
||||||
|
---@field itemWidth? number
|
||||||
|
---@field itemHeight? number
|
||||||
|
---@field activeColor? string
|
||||||
|
---@field inactiveColor? string
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeActionItem
|
||||||
|
---@field id? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler? string
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field color? string
|
||||||
|
---@field style? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeActionRowOpts: RuntimeNodeInit
|
||||||
|
---@field actions? RuntimeActionItem[]
|
||||||
|
---@field gap? number
|
||||||
|
---@field itemWidth? number
|
||||||
|
---@field itemHeight? number
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimePanelHeaderOpts: RuntimeNodeInit
|
||||||
|
---@field eyebrow? string
|
||||||
|
---@field title string
|
||||||
|
---@field summary? string
|
||||||
|
---@field gap? number
|
||||||
|
---@field eyebrowId? string
|
||||||
|
---@field titleId? string
|
||||||
|
---@field summaryId? string
|
||||||
|
---@field eyebrowHeight? number
|
||||||
|
---@field titleHeight? number
|
||||||
|
---@field summaryHeight? number
|
||||||
|
---@field eyebrowStyle? RuntimeNodeProps
|
||||||
|
---@field titleStyle? RuntimeNodeProps
|
||||||
|
---@field summaryStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeWidgetTheme
|
||||||
|
---@field primary? string
|
||||||
|
---@field secondary? string
|
||||||
|
---@field success? string
|
||||||
|
---@field overlay? string
|
||||||
|
---@field surface? string
|
||||||
|
---@field surfaceAlt? string
|
||||||
|
---@field card? string
|
||||||
|
---@field text? string
|
||||||
|
---@field muted? string
|
||||||
|
---@field progress? string
|
||||||
|
---@field transparent? string
|
||||||
|
|
||||||
|
---@class RuntimeWidgets
|
||||||
|
---@field configure fun(tokens?: RuntimeWidgetTheme): RuntimeWidgets
|
||||||
|
---@field label fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field section_title fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field pill fun(id: string, text: string|RuntimePillOpts, x?: number, y?: number, width?: number, height?: number, opts?: RuntimePillOpts): RuntimeNode[]
|
||||||
|
---@field text_button fun(id: string, text: string|RuntimeTextButtonOpts, x?: number, y?: number, width?: number, height?: number, handler?: string, opts?: RuntimeTextButtonOpts): RuntimeNode
|
||||||
|
---@field list_item fun(id: string, text: string|RuntimeListItemOpts, x?: number, y?: number, width?: number, height?: number, handler?: string, opts?: RuntimeListItemOpts): RuntimeNode
|
||||||
|
---@field tabs fun(id: string, tabs: RuntimeTabItem[]|RuntimeTabsOpts, opts?: RuntimeTabsOpts): RuntimeNode[]
|
||||||
|
---@field action_row fun(id: string, actions: RuntimeActionItem[]|RuntimeActionRowOpts, opts?: RuntimeActionRowOpts): RuntimeNode[]
|
||||||
|
---@field panel_header fun(id: string, opts: RuntimePanelHeaderOpts): RuntimeNode[]
|
||||||
|
---@field overlay fun(id: string, width: number|RuntimeNodeInit, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field card fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field progress_bar fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, value?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field labeled_progress fun(id: string, label: string, x: number, y: number, width: number, height: number, value: number, opts?: RuntimeLabeledProgressOpts): RuntimeNode[]
|
||||||
|
---@field button_row fun(parent: string, id: string, buttons: RuntimeDialogButton[], x: number, y: number, width: number, height: number, gap?: number, opts?: RuntimeNodeProps): RuntimeNode[]
|
||||||
|
---@field dialog fun(id: string, title: string, message: string, x: number, y: number, width: number, height: number, opts?: RuntimeDialogOpts): RuntimeNode[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeLayoutItem
|
||||||
|
---@field node RuntimeNode
|
||||||
|
---@field marginLeft? number
|
||||||
|
---@field marginRight? number
|
||||||
|
---@field marginTop? number
|
||||||
|
---@field marginBottom? number
|
||||||
|
|
||||||
|
---@class RuntimeLayoutItemOpts
|
||||||
|
---@field margin? number
|
||||||
|
---@field mx? number
|
||||||
|
---@field my? number
|
||||||
|
---@field ml? number
|
||||||
|
---@field mr? number
|
||||||
|
---@field mt? number
|
||||||
|
---@field mb? number
|
||||||
|
---@field marginLeft? number
|
||||||
|
---@field marginRight? number
|
||||||
|
---@field marginTop? number
|
||||||
|
---@field marginBottom? number
|
||||||
|
|
||||||
|
---@class RuntimeLinearLayoutOpts
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field gap? number
|
||||||
|
---@field align? RuntimeLayoutAlign
|
||||||
|
---@field padding? number
|
||||||
|
---@field paddingX? number
|
||||||
|
---@field paddingY? number
|
||||||
|
---@field px? number
|
||||||
|
---@field py? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
|
||||||
|
---@class RuntimeBoxLayoutOpts: RuntimeLinearLayoutOpts
|
||||||
|
---@field rows? integer
|
||||||
|
---@field columns? integer
|
||||||
|
---@field cols? integer
|
||||||
|
---@field cellWidth? number
|
||||||
|
---@field cellHeight? number
|
||||||
|
---@field cellW? number
|
||||||
|
---@field cellH? number
|
||||||
|
---@field gapX? number
|
||||||
|
---@field gapY? number
|
||||||
|
---@field valign? RuntimeLayoutAlign
|
||||||
|
|
||||||
|
---@class RuntimeLayout
|
||||||
|
---@field item fun(node: RuntimeNode, opts?: RuntimeLayoutItemOpts): RuntimeLayoutItem
|
||||||
|
---@field local_position fun(origin: RuntimePoint, position: RuntimePoint): RuntimePoint
|
||||||
|
---@field row fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeLinearLayoutOpts): RuntimeNode[]
|
||||||
|
---@field column fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeLinearLayoutOpts): RuntimeNode[]
|
||||||
|
---@field stack fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeLinearLayoutOpts): RuntimeNode[]
|
||||||
|
---@field box fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeBoxLayoutOpts): RuntimeNode[]
|
||||||
|
|
||||||
|
---@class RuntimeCommands
|
||||||
|
---@field toast fun(text: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field copy_text fun(text: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field delay fun(duration: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field sequence fun(items: RuntimeCommand[], opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field parallel fun(items: RuntimeCommand[], opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field move_path fun(target: string, path: RuntimePoint[], opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field move_to fun(target: string, x: number, y: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field fade_to fun(target: string, alpha: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field scale_to fun(target: string, scale: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field rotate_to fun(target: string, angle: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field remove_node fun(target: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field play_spine_animation fun(target: string, animation: string, opts?: RuntimeSpineCommandOpts): RuntimeCommand
|
||||||
|
---@field play_sound fun(asset: string, opts?: RuntimeAudioCommandOpts): RuntimeCommand
|
||||||
|
---@field play_bgm fun(asset: string, opts?: RuntimeBgmCommandOpts): RuntimeCommand
|
||||||
|
---@field pause_bgm fun(channel?: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field resume_bgm fun(channel?: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field stop_bgm fun(channel?: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field preload_group fun(group: string, opts?: RuntimeResourceCommandOpts): RuntimeCommand
|
||||||
|
---@field evict_group fun(group: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field cancel_id fun(id: string): RuntimeCommand
|
||||||
|
---@field cancel_group fun(group: string): RuntimeCommand
|
||||||
|
---@field cancel_scope fun(scope: string): RuntimeCommand
|
||||||
|
|
||||||
|
---@class RuntimeImportApi
|
||||||
|
---@field import fun(moduleName: string): table
|
||||||
|
|
||||||
|
---@type RuntimeImportApi
|
||||||
|
runtime = runtime
|
||||||
|
|
||||||
|
---@alias PlayerColor 'red'|'yellow'|'blue'|'green'
|
||||||
|
---@alias GamePhase 'waiting_roll'|'waiting_piece'|'animating'|'game_over'
|
||||||
|
---@alias PieceStatus 'home'|'route'|'finished'
|
||||||
|
|
||||||
|
---@class (exact) PieceState
|
||||||
|
---@field id string
|
||||||
|
---@field owner PlayerColor
|
||||||
|
---@field progress integer
|
||||||
|
---@field status PieceStatus
|
||||||
|
|
||||||
|
---@class (exact) LudoState
|
||||||
|
---@field current_player PlayerColor
|
||||||
|
---@field phase GamePhase
|
||||||
|
---@field dice_index integer
|
||||||
|
---@field dice_values integer[]
|
||||||
|
---@field dice? integer
|
||||||
|
---@field selected_piece? string
|
||||||
|
---@field pending_move? table
|
||||||
|
---@field winner? PlayerColor
|
||||||
|
---@field players PlayerColor[]
|
||||||
|
---@field pieces table<string, PieceState>
|
||||||
|
|
||||||
|
---@class (exact) BoardPoint
|
||||||
|
---@field x number
|
||||||
|
---@field y number
|
||||||
|
|
||||||
|
---@class (exact) BoardData
|
||||||
|
---@field red_home BoardPoint[]
|
||||||
|
---@field blue_home BoardPoint[]
|
||||||
|
---@field path BoardPoint[]
|
||||||
|
---@field start table<PlayerColor, integer>
|
||||||
22
example/assets/games/flight/scripts/state.lua
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---@type table
|
||||||
|
return {
|
||||||
|
players = { "red", "yellow", "blue", "green" },
|
||||||
|
current_player = "red",
|
||||||
|
phase = "waiting_roll",
|
||||||
|
dice_index = 0,
|
||||||
|
dice_values = { 6, 3, 4, 6, 2, 5, 1, 6, 4, 2, 6, 5 },
|
||||||
|
dice = nil,
|
||||||
|
selected_piece = nil,
|
||||||
|
pending_move = nil,
|
||||||
|
winner = nil,
|
||||||
|
pieces = {
|
||||||
|
red_1 = { id = "red_1", owner = "red", status = "home", progress = 0 },
|
||||||
|
red_2 = { id = "red_2", owner = "red", status = "home", progress = 0 },
|
||||||
|
yellow_1 = { id = "yellow_1", owner = "yellow", status = "home", progress = 0 },
|
||||||
|
yellow_2 = { id = "yellow_2", owner = "yellow", status = "home", progress = 0 },
|
||||||
|
blue_1 = { id = "blue_1", owner = "blue", status = "home", progress = 0 },
|
||||||
|
blue_2 = { id = "blue_2", owner = "blue", status = "home", progress = 0 },
|
||||||
|
green_1 = { id = "green_1", owner = "green", status = "home", progress = 0 },
|
||||||
|
green_2 = { id = "green_2", owner = "green", status = "home", progress = 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
81
example/assets/games/flight/scripts/styles.lua
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
local theme = runtime.import("theme")
|
||||||
|
local colors = theme.colors
|
||||||
|
local styles = {}
|
||||||
|
|
||||||
|
styles.layers = {
|
||||||
|
board = 1,
|
||||||
|
cell = 2,
|
||||||
|
home = 3,
|
||||||
|
plane = 10,
|
||||||
|
hud = 100,
|
||||||
|
hud_content = 101
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.board = {
|
||||||
|
color = colors.board,
|
||||||
|
layer = styles.layers.board
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.center = {
|
||||||
|
color = colors.board_inner,
|
||||||
|
layer = styles.layers.board
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.route_cell = {
|
||||||
|
anchor = "center",
|
||||||
|
color = colors.route_cell,
|
||||||
|
layer = styles.layers.cell
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.home_panel = function(owner)
|
||||||
|
return {
|
||||||
|
color = colors[owner .. "_home"],
|
||||||
|
layer = styles.layers.home
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
styles.hud = {
|
||||||
|
color = colors.top_bar,
|
||||||
|
layer = styles.layers.hud
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.hud_text = {
|
||||||
|
fontSize = 20,
|
||||||
|
color = colors.text,
|
||||||
|
layer = styles.layers.hud_content
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.small_text = {
|
||||||
|
fontSize = 14,
|
||||||
|
color = colors.muted_text,
|
||||||
|
layer = styles.layers.hud_content
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.dice_button = {
|
||||||
|
color = colors.dice_button,
|
||||||
|
fontSize = 20,
|
||||||
|
layer = styles.layers.hud_content
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.plane = function(owner)
|
||||||
|
return {
|
||||||
|
anchor = "center",
|
||||||
|
color = colors[owner],
|
||||||
|
layer = styles.layers.plane,
|
||||||
|
interactive = true,
|
||||||
|
onTap = "piece_tap",
|
||||||
|
alpha = 0.95
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
styles.plane_highlight = {
|
||||||
|
scale = 1.25,
|
||||||
|
alpha = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.plane_normal = {
|
||||||
|
scale = 1,
|
||||||
|
alpha = 0.95
|
||||||
|
}
|
||||||
|
|
||||||
|
return styles
|
||||||
22
example/assets/games/flight/scripts/theme.lua
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
return {
|
||||||
|
title = "Lua 飞行棋",
|
||||||
|
colors = {
|
||||||
|
background = "#0f172a",
|
||||||
|
board = "#1e293b",
|
||||||
|
board_inner = "#334155",
|
||||||
|
route_cell = "#f8fafc",
|
||||||
|
route_text = "#0f172a",
|
||||||
|
top_bar = "#020617",
|
||||||
|
dice_button = "#2563eb",
|
||||||
|
text = "#ffffff",
|
||||||
|
muted_text = "#cbd5e1",
|
||||||
|
red = "#ef4444",
|
||||||
|
yellow = "#eab308",
|
||||||
|
blue = "#3b82f6",
|
||||||
|
green = "#22c55e",
|
||||||
|
red_home = "#7f1d1d",
|
||||||
|
yellow_home = "#713f12",
|
||||||
|
blue_home = "#1e3a8a",
|
||||||
|
green_home = "#14532d"
|
||||||
|
}
|
||||||
|
}
|
||||||
130
example/assets/games/flight/scripts/ui.lua
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
---@type RuntimeUi
|
||||||
|
local runtime_ui = runtime.import("runtime_ui")
|
||||||
|
---@type RuntimeLayout
|
||||||
|
local layout = runtime.import("layout")
|
||||||
|
local theme = runtime.import("theme")
|
||||||
|
local styles = runtime.import("styles")
|
||||||
|
local state = runtime.import("state")
|
||||||
|
local board = runtime.import("board")
|
||||||
|
local rules = runtime.import("rules")
|
||||||
|
|
||||||
|
local ui = {}
|
||||||
|
|
||||||
|
local function color_name(owner)
|
||||||
|
if owner == "red" then return "红" end
|
||||||
|
if owner == "yellow" then return "黄" end
|
||||||
|
if owner == "blue" then return "蓝" end
|
||||||
|
if owner == "green" then return "绿" end
|
||||||
|
return owner
|
||||||
|
end
|
||||||
|
|
||||||
|
local function piece_size(piece)
|
||||||
|
if piece.status == "finished" then
|
||||||
|
return 30
|
||||||
|
end
|
||||||
|
return 34
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.highlight_updates(ids, enabled)
|
||||||
|
return runtime_ui.batch_update(ids, enabled and styles.plane_highlight or styles.plane_normal)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.piece_position_update(piece_id)
|
||||||
|
local piece = state.pieces[piece_id]
|
||||||
|
local pos = rules.piece_position(piece)
|
||||||
|
return runtime_ui.update(piece_id, {
|
||||||
|
x = pos.x,
|
||||||
|
y = pos.y,
|
||||||
|
width = piece_size(piece),
|
||||||
|
height = piece_size(piece),
|
||||||
|
scale = 1,
|
||||||
|
alpha = 0.95
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.piece_position_updates(ids)
|
||||||
|
local updates = {}
|
||||||
|
for _, id in ipairs(ids) do
|
||||||
|
table.insert(updates, ui.piece_position_update(id))
|
||||||
|
end
|
||||||
|
return updates
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.all_piece_position_updates()
|
||||||
|
return ui.piece_position_updates(rules.all_piece_ids())
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.create_board_nodes()
|
||||||
|
local nodes = {
|
||||||
|
runtime_ui.panel("board_panel", 28, 82, 664, 610, styles.board),
|
||||||
|
runtime_ui.panel("center_airport", 252, 292, 168, 136, styles.center),
|
||||||
|
runtime_ui.text("board_title", theme.title, 268, 318, 140, 32, styles.hud_text),
|
||||||
|
runtime_ui.text("board_tip", "掷 6 起飞 · 撞子回家 · 全部到达获胜", 206, 360, 320, 28, styles.small_text),
|
||||||
|
runtime_ui.panel("home_red", 62, 98, 128, 92, styles.home_panel("red")),
|
||||||
|
runtime_ui.panel("home_yellow", 498, 98, 128, 92, styles.home_panel("yellow")),
|
||||||
|
runtime_ui.panel("home_blue", 498, 516, 128, 92, styles.home_panel("blue")),
|
||||||
|
runtime_ui.panel("home_green", 62, 516, 128, 92, styles.home_panel("green")),
|
||||||
|
runtime_ui.text("label_red", "红方", 96, 102, 60, 24, styles.small_text),
|
||||||
|
runtime_ui.text("label_yellow", "黄方", 532, 102, 60, 24, styles.small_text),
|
||||||
|
runtime_ui.text("label_blue", "蓝方", 532, 520, 60, 24, styles.small_text),
|
||||||
|
runtime_ui.text("label_green", "绿方", 96, 520, 60, 24, styles.small_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, cell in ipairs(board.route) do
|
||||||
|
runtime_ui.append(nodes, runtime_ui.circle("route_" .. tostring(index), cell.x, cell.y, 24, styles.route_cell))
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, owner in ipairs(state.players) do
|
||||||
|
local start = board.route[board.start[owner]]
|
||||||
|
runtime_ui.append(nodes, runtime_ui.text("start_" .. owner, color_name(owner), start.x - 14, start.y - 14, 36, 24, {
|
||||||
|
fontSize = 14,
|
||||||
|
color = theme.colors[owner],
|
||||||
|
layer = styles.layers.cell + 1
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
|
||||||
|
for id, piece in pairs(state.pieces) do
|
||||||
|
local pos = rules.piece_position(piece)
|
||||||
|
runtime_ui.append(nodes, runtime_ui.circle(id, pos.x, pos.y, piece_size(piece), styles.plane(piece.owner)))
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.create_ui_nodes()
|
||||||
|
local hud_items = layout.row("top_bar", {
|
||||||
|
runtime_ui.text("turn_text", "当前: 红方", 0, 0, 120, 32, styles.hud_text),
|
||||||
|
runtime_ui.text("dice_text", "骰子: -", 0, 0, 100, 32, styles.hud_text),
|
||||||
|
runtime_ui.text("status_text", rules.status_text(), 0, 0, 260, 32, styles.hud_text),
|
||||||
|
layout.item(
|
||||||
|
runtime_ui.button("dice_button", "掷骰子", 0, 0, 120, 48, "roll_dice", styles.dice_button),
|
||||||
|
{ marginLeft = 18 }
|
||||||
|
)
|
||||||
|
}, {
|
||||||
|
x = 24,
|
||||||
|
height = 76,
|
||||||
|
gap = 16,
|
||||||
|
align = "center"
|
||||||
|
})
|
||||||
|
|
||||||
|
local nodes = { runtime_ui.panel("top_bar", 0, 0, 720, 76, styles.hud) }
|
||||||
|
return runtime_ui.append_all(nodes, hud_items)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.turn_name(owner)
|
||||||
|
return "当前: " .. color_name(owner) .. "方"
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.dice_and_status_updates()
|
||||||
|
return {
|
||||||
|
runtime_ui.text_update("dice_text", "骰子: " .. tostring(state.dice or "-")),
|
||||||
|
runtime_ui.text_update("turn_text", ui.turn_name(state.current_player)),
|
||||||
|
runtime_ui.text_update("status_text", rules.status_text())
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.status_update()
|
||||||
|
return { runtime_ui.text_update("status_text", rules.status_text()) }
|
||||||
|
end
|
||||||
|
|
||||||
|
return ui
|
||||||
BIN
example/assets/games/ludo/assets/board.png
Normal file
|
After Width: | Height: | Size: 68 B |
BIN
example/assets/games/ludo/assets/dice.wav
Normal file
BIN
example/assets/games/ludo/assets/piece_blue.png
Normal file
|
After Width: | Height: | Size: 68 B |
BIN
example/assets/games/ludo/assets/piece_red.png
Normal file
|
After Width: | Height: | Size: 68 B |
55
example/assets/games/ludo/manifest.json
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"gameId": "ludo",
|
||||||
|
"name": "Lua Ludo 示例",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"runtimeApiVersion": 1,
|
||||||
|
"entry": "scripts/main.lua",
|
||||||
|
"assetsBase": "assets",
|
||||||
|
"defaultLocale": "zh-Hans",
|
||||||
|
"supportedLocales": [
|
||||||
|
"zh-Hans",
|
||||||
|
"en"
|
||||||
|
],
|
||||||
|
"display": {
|
||||||
|
"designWidth": 720,
|
||||||
|
"designHeight": 720,
|
||||||
|
"scaleMode": "fit"
|
||||||
|
},
|
||||||
|
"modules": {
|
||||||
|
"runtime_ui": "runtime:runtime_ui.lua",
|
||||||
|
"runtime_widgets": "runtime:runtime_widgets.lua",
|
||||||
|
"runtime_commands": "runtime:runtime_commands.lua",
|
||||||
|
"layout": "runtime:layout.lua",
|
||||||
|
"i18n": "scripts/i18n.lua",
|
||||||
|
"theme": "scripts/theme.lua",
|
||||||
|
"styles": "scripts/styles.lua",
|
||||||
|
"state": "scripts/state.lua",
|
||||||
|
"board": "scripts/board.lua",
|
||||||
|
"rules": "scripts/rules.lua",
|
||||||
|
"ui": "scripts/ui.lua",
|
||||||
|
"animation": "scripts/animation.lua"
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"board": {
|
||||||
|
"type": "image",
|
||||||
|
"path": "assets/board.png",
|
||||||
|
"group": "board"
|
||||||
|
},
|
||||||
|
"piece_red": {
|
||||||
|
"type": "image",
|
||||||
|
"path": "assets/piece_red.png",
|
||||||
|
"group": "pieces"
|
||||||
|
},
|
||||||
|
"piece_blue": {
|
||||||
|
"type": "image",
|
||||||
|
"path": "assets/piece_blue.png",
|
||||||
|
"group": "pieces"
|
||||||
|
},
|
||||||
|
"dice": {
|
||||||
|
"type": "audio",
|
||||||
|
"path": "assets/dice.wav",
|
||||||
|
"preload": "lazy",
|
||||||
|
"group": "sfx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
example/assets/games/ludo/scripts/animation.lua
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---@type RuntimeCommands
|
||||||
|
local commands = runtime.import("runtime_commands")
|
||||||
|
|
||||||
|
local animation = {}
|
||||||
|
|
||||||
|
function animation.move_piece(piece_id, path)
|
||||||
|
return commands.move_path(piece_id, path, {
|
||||||
|
duration = 0.7,
|
||||||
|
onComplete = "piece_move_done"
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function animation.toast(text)
|
||||||
|
return commands.toast(text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function animation.play_sound(name)
|
||||||
|
return commands.play_sound(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
function animation.play_bgm(name, channel)
|
||||||
|
return commands.play_bgm(name, { channel = channel or "bgm" })
|
||||||
|
end
|
||||||
|
|
||||||
|
function animation.pause_bgm(channel)
|
||||||
|
return commands.pause_bgm(channel)
|
||||||
|
end
|
||||||
|
|
||||||
|
function animation.resume_bgm(channel)
|
||||||
|
return commands.resume_bgm(channel)
|
||||||
|
end
|
||||||
|
|
||||||
|
function animation.stop_bgm(channel)
|
||||||
|
return commands.stop_bgm(channel)
|
||||||
|
end
|
||||||
|
|
||||||
|
return animation
|
||||||
20
example/assets/games/ludo/scripts/board.lua
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---@type BoardData
|
||||||
|
return {
|
||||||
|
red_home = {
|
||||||
|
{x = 80, y = 520},
|
||||||
|
{x = 140, y = 520}
|
||||||
|
},
|
||||||
|
blue_home = {
|
||||||
|
{x = 520, y = 120},
|
||||||
|
{x = 580, y = 120}
|
||||||
|
},
|
||||||
|
path = {
|
||||||
|
{x = 120, y = 420}, {x = 180, y = 420}, {x = 240, y = 420},
|
||||||
|
{x = 300, y = 420}, {x = 360, y = 420}, {x = 420, y = 420},
|
||||||
|
{x = 480, y = 420}, {x = 480, y = 360}, {x = 480, y = 300},
|
||||||
|
{x = 480, y = 240}, {x = 420, y = 240}, {x = 360, y = 240},
|
||||||
|
{x = 300, y = 240}, {x = 240, y = 240}, {x = 180, y = 240},
|
||||||
|
{x = 120, y = 240}, {x = 120, y = 300}, {x = 120, y = 360}
|
||||||
|
},
|
||||||
|
start = { red = 1, blue = 10 }
|
||||||
|
}
|
||||||
103
example/assets/games/ludo/scripts/i18n.lua
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
---@class RuntimeI18n
|
||||||
|
local i18n = {}
|
||||||
|
|
||||||
|
local default_locale = "zh-Hans"
|
||||||
|
local current_locale = default_locale
|
||||||
|
|
||||||
|
local messages = {
|
||||||
|
["zh-Hans"] = {
|
||||||
|
title = "Lua 飞行棋",
|
||||||
|
roll_button = "掷骰子",
|
||||||
|
dice_empty = "骰子: -",
|
||||||
|
dice_value = "骰子: {value}",
|
||||||
|
current_player = "当前玩家: {player}",
|
||||||
|
["player.red"] = "红方",
|
||||||
|
["player.blue"] = "蓝方",
|
||||||
|
["toast.move_first"] = "请先移动棋子",
|
||||||
|
["toast.no_movable_piece"] = "无可移动棋子",
|
||||||
|
["toast.invalid_piece"] = "该棋子不能移动"
|
||||||
|
},
|
||||||
|
en = {
|
||||||
|
title = "Lua Ludo",
|
||||||
|
roll_button = "Roll",
|
||||||
|
dice_empty = "Dice: -",
|
||||||
|
dice_value = "Dice: {value}",
|
||||||
|
current_player = "Current player: {player}",
|
||||||
|
["player.red"] = "Red",
|
||||||
|
["player.blue"] = "Blue",
|
||||||
|
["toast.move_first"] = "Move a piece first",
|
||||||
|
["toast.no_movable_piece"] = "No movable pieces",
|
||||||
|
["toast.invalid_piece"] = "This piece cannot move"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
---@param locale? string
|
||||||
|
---@return string
|
||||||
|
local function normalize_locale(locale)
|
||||||
|
if type(locale) ~= "string" or locale == "" then
|
||||||
|
return default_locale
|
||||||
|
end
|
||||||
|
locale = string.gsub(locale, "_", "-")
|
||||||
|
if messages[locale] ~= nil then
|
||||||
|
return locale
|
||||||
|
end
|
||||||
|
|
||||||
|
local language = string.match(locale, "^([A-Za-z]+)")
|
||||||
|
if language ~= nil and messages[language] ~= nil then
|
||||||
|
return language
|
||||||
|
end
|
||||||
|
return default_locale
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param value string
|
||||||
|
---@return string
|
||||||
|
local function escape_pattern(value)
|
||||||
|
return string.gsub(value, "([%(%)%.%%%+%-%*%?%[%]%^%$])", "%%%1")
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param template string
|
||||||
|
---@param vars? table<string, string|number>
|
||||||
|
---@return string
|
||||||
|
local function interpolate(template, vars)
|
||||||
|
if vars == nil then
|
||||||
|
return template
|
||||||
|
end
|
||||||
|
|
||||||
|
local result = template
|
||||||
|
for key, value in pairs(vars) do
|
||||||
|
result = string.gsub(result, "{" .. escape_pattern(key) .. "}", tostring(value))
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param ctx? RuntimeContext
|
||||||
|
function i18n.configure(ctx)
|
||||||
|
local locale = nil
|
||||||
|
if ctx ~= nil and ctx.locale ~= nil then
|
||||||
|
locale = ctx.locale.resolved or ctx.locale.requested or ctx.locale.default
|
||||||
|
end
|
||||||
|
current_locale = normalize_locale(locale)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return string
|
||||||
|
function i18n.locale()
|
||||||
|
return current_locale
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param key string
|
||||||
|
---@param vars? table<string, string|number>
|
||||||
|
---@return string
|
||||||
|
function i18n.t(key, vars)
|
||||||
|
local bundle = messages[current_locale] or messages[default_locale]
|
||||||
|
local fallback = messages[default_locale]
|
||||||
|
local value = bundle[key] or fallback[key] or key
|
||||||
|
return interpolate(value, vars)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param color PlayerColor
|
||||||
|
---@return string
|
||||||
|
function i18n.player(color)
|
||||||
|
return i18n.t("player." .. color)
|
||||||
|
end
|
||||||
|
|
||||||
|
return i18n
|
||||||
117
example/assets/games/ludo/scripts/main.lua
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
local state = runtime.import("state")
|
||||||
|
local rules = runtime.import("rules")
|
||||||
|
local ui = runtime.import("ui")
|
||||||
|
local animation = runtime.import("animation")
|
||||||
|
local i18n = runtime.import("i18n")
|
||||||
|
---@type RuntimeWidgets
|
||||||
|
local widgets = runtime.import("runtime_widgets")
|
||||||
|
local theme = runtime.import("theme")
|
||||||
|
|
||||||
|
widgets.configure({
|
||||||
|
primary = theme.colors.dice_button,
|
||||||
|
secondary = theme.colors.board,
|
||||||
|
success = theme.colors.blue,
|
||||||
|
overlay = "#99000000",
|
||||||
|
surface = theme.colors.top_bar,
|
||||||
|
surfaceAlt = theme.colors.board,
|
||||||
|
card = theme.colors.top_bar,
|
||||||
|
text = theme.colors.text,
|
||||||
|
muted = "#ffcbd5e1",
|
||||||
|
progress = theme.colors.blue,
|
||||||
|
transparent = "#00000000"
|
||||||
|
})
|
||||||
|
|
||||||
|
function smoke_test(ctx)
|
||||||
|
i18n.configure(ctx)
|
||||||
|
return ctx ~= nil
|
||||||
|
and ctx.runtimeApiVersion ~= nil
|
||||||
|
and state.current_player ~= nil
|
||||||
|
and rules.next_player ~= nil
|
||||||
|
and ui.create_board_nodes ~= nil
|
||||||
|
and animation.move_piece ~= nil
|
||||||
|
and widgets.dialog ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function init(ctx)
|
||||||
|
i18n.configure(ctx)
|
||||||
|
return {
|
||||||
|
render = { creates = ui.create_board_nodes() },
|
||||||
|
ui = { creates = ui.create_ui_nodes() },
|
||||||
|
commands = {}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handle_roll_dice()
|
||||||
|
if state.phase ~= "waiting_roll" then
|
||||||
|
return { commands = { animation.toast(i18n.t("toast.move_first")) } }
|
||||||
|
end
|
||||||
|
|
||||||
|
state.dice = rules.next_dice()
|
||||||
|
local movable = rules.movable_pieces()
|
||||||
|
|
||||||
|
if #movable == 0 then
|
||||||
|
state.current_player = rules.next_player()
|
||||||
|
state.phase = "waiting_roll"
|
||||||
|
return {
|
||||||
|
ui = { updates = ui.dice_and_turn_updates(state.dice, state.current_player) },
|
||||||
|
render = { updates = ui.highlight_updates(rules.all_piece_ids(), false) },
|
||||||
|
commands = { animation.toast(i18n.t("toast.no_movable_piece")) }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
state.phase = "waiting_piece"
|
||||||
|
return {
|
||||||
|
ui = { updates = ui.dice_update(state.dice) },
|
||||||
|
render = { updates = ui.highlight_updates(movable, true) },
|
||||||
|
commands = { animation.play_sound("dice") }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handle_piece_tap(piece_id)
|
||||||
|
if state.phase ~= "waiting_piece" then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local piece = state.pieces[piece_id]
|
||||||
|
if piece == nil or piece.owner ~= state.current_player or not rules.can_move(piece, state.dice) then
|
||||||
|
return { commands = { animation.toast(i18n.t("toast.invalid_piece")) } }
|
||||||
|
end
|
||||||
|
|
||||||
|
local path = rules.calculate_path(piece, state.dice)
|
||||||
|
rules.apply_move(piece, state.dice)
|
||||||
|
state.phase = "animating"
|
||||||
|
state.selected_piece = piece_id
|
||||||
|
|
||||||
|
return {
|
||||||
|
render = { updates = ui.highlight_updates(rules.all_piece_ids(), false) },
|
||||||
|
commands = { animation.move_piece(piece_id, path) }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handle_move_done()
|
||||||
|
if state.dice ~= 6 then
|
||||||
|
state.current_player = rules.next_player()
|
||||||
|
end
|
||||||
|
state.phase = "waiting_roll"
|
||||||
|
state.selected_piece = nil
|
||||||
|
|
||||||
|
return {
|
||||||
|
ui = { updates = ui.turn_update(state.current_player) }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_event(event)
|
||||||
|
if event.handler == "roll_dice" then
|
||||||
|
return handle_roll_dice()
|
||||||
|
end
|
||||||
|
|
||||||
|
if event.handler == "piece_tap" then
|
||||||
|
return handle_piece_tap(event.target)
|
||||||
|
end
|
||||||
|
|
||||||
|
if event.handler == "piece_move_done" then
|
||||||
|
return handle_move_done()
|
||||||
|
end
|
||||||
|
|
||||||
|
return {}
|
||||||
|
end
|
||||||
88
example/assets/games/ludo/scripts/rules.lua
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
local state = runtime.import("state")
|
||||||
|
local board = runtime.import("board")
|
||||||
|
|
||||||
|
local rules = {}
|
||||||
|
|
||||||
|
function rules.next_player()
|
||||||
|
if state.current_player == "red" then
|
||||||
|
return "blue"
|
||||||
|
end
|
||||||
|
return "red"
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.next_dice()
|
||||||
|
state.dice_index = state.dice_index + 1
|
||||||
|
if state.dice_index > #state.dice_values then
|
||||||
|
state.dice_index = 1
|
||||||
|
end
|
||||||
|
return state.dice_values[state.dice_index]
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.piece_home_position(piece)
|
||||||
|
if piece.owner == "red" then
|
||||||
|
if piece.id == "red_1" then return board.red_home[1] end
|
||||||
|
return board.red_home[2]
|
||||||
|
end
|
||||||
|
if piece.id == "blue_1" then return board.blue_home[1] end
|
||||||
|
return board.blue_home[2]
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.can_move(piece, dice)
|
||||||
|
if piece.status == "home" then
|
||||||
|
return dice == 6
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.movable_pieces()
|
||||||
|
local result = {}
|
||||||
|
for id, piece in pairs(state.pieces) do
|
||||||
|
if piece.owner == state.current_player and rules.can_move(piece, state.dice) then
|
||||||
|
table.insert(result, id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.all_piece_ids()
|
||||||
|
local result = {}
|
||||||
|
for id, _ in pairs(state.pieces) do
|
||||||
|
table.insert(result, id)
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.calculate_path(piece, dice)
|
||||||
|
local path = {}
|
||||||
|
if piece.status == "home" then
|
||||||
|
local start_index = board.start[piece.owner]
|
||||||
|
local pos = board.path[start_index]
|
||||||
|
table.insert(path, {x = pos.x, y = pos.y})
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, dice do
|
||||||
|
local index = piece.path_index + i
|
||||||
|
while index > #board.path do
|
||||||
|
index = index - #board.path
|
||||||
|
end
|
||||||
|
local pos = board.path[index]
|
||||||
|
table.insert(path, {x = pos.x, y = pos.y})
|
||||||
|
end
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
|
function rules.apply_move(piece, dice)
|
||||||
|
if piece.status == "home" then
|
||||||
|
piece.status = "path"
|
||||||
|
piece.path_index = board.start[piece.owner]
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
piece.path_index = piece.path_index + dice
|
||||||
|
while piece.path_index > #board.path do
|
||||||
|
piece.path_index = piece.path_index - #board.path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return rules
|
||||||
587
example/assets/games/ludo/scripts/runtime_defs.lua
Normal file
@@ -0,0 +1,587 @@
|
|||||||
|
---@meta
|
||||||
|
--- COMMON RUNTIME TYPES SECTION.
|
||||||
|
--- Source of truth: tool/lua_runtime_defs_common.lua
|
||||||
|
--- After editing this common section, run:
|
||||||
|
--- dart run tool/generate_lua_runtime_defs.dart
|
||||||
|
|
||||||
|
|
||||||
|
---@alias RuntimeNodeType
|
||||||
|
---| 'panel'
|
||||||
|
---| 'button'
|
||||||
|
---| 'text'
|
||||||
|
---| 'circle'
|
||||||
|
---| 'rect'
|
||||||
|
---| 'line'
|
||||||
|
---| 'progress'
|
||||||
|
---| 'listView'
|
||||||
|
---| 'sprite'
|
||||||
|
---| 'image'
|
||||||
|
---| 'spine'
|
||||||
|
---| 'particle'
|
||||||
|
|
||||||
|
---@alias RuntimeAnchor
|
||||||
|
---| 'center'
|
||||||
|
---| 'topLeft'
|
||||||
|
---| 'topRight'
|
||||||
|
---| 'bottomLeft'
|
||||||
|
---| 'bottomRight'
|
||||||
|
|
||||||
|
---@alias RuntimeTextAlign
|
||||||
|
---| 'left'
|
||||||
|
---| 'center'
|
||||||
|
---| 'right'
|
||||||
|
|
||||||
|
---@alias RuntimeParticlePreset
|
||||||
|
---| 'burst'
|
||||||
|
---| 'trail'
|
||||||
|
---| 'snow'
|
||||||
|
---| 'confetti'
|
||||||
|
|
||||||
|
---@alias RuntimeCommandType
|
||||||
|
---| 'move_path'
|
||||||
|
---| 'move_to'
|
||||||
|
---| 'fade_to'
|
||||||
|
---| 'scale_to'
|
||||||
|
---| 'rotate_to'
|
||||||
|
---| 'remove_node'
|
||||||
|
---| 'sequence'
|
||||||
|
---| 'parallel'
|
||||||
|
---| 'delay'
|
||||||
|
---| 'toast'
|
||||||
|
---| 'play_sound'
|
||||||
|
---| 'play_bgm'
|
||||||
|
---| 'pause_bgm'
|
||||||
|
---| 'resume_bgm'
|
||||||
|
---| 'stop_bgm'
|
||||||
|
---| 'preload_resources'
|
||||||
|
---| 'evict_resources'
|
||||||
|
---| 'cancel_commands'
|
||||||
|
---| 'play_spine_animation'
|
||||||
|
---| 'copy_text'
|
||||||
|
|
||||||
|
---@alias RuntimeEventType
|
||||||
|
---| 'tap'
|
||||||
|
---| 'animation_done'
|
||||||
|
---| 'resize'
|
||||||
|
---| 'scroll'
|
||||||
|
|
||||||
|
---@alias RuntimeScaleMode
|
||||||
|
---| 'fit'
|
||||||
|
---| 'fill'
|
||||||
|
---| 'stretch'
|
||||||
|
---| 'none'
|
||||||
|
|
||||||
|
---@alias RuntimeLayoutAlign
|
||||||
|
---| 'start'
|
||||||
|
---| 'center'
|
||||||
|
---| 'end'
|
||||||
|
|
||||||
|
---@alias RuntimeButtonVariant
|
||||||
|
---| 'primary'
|
||||||
|
---| 'secondary'
|
||||||
|
---| 'ghost'
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNode
|
||||||
|
---@field id string
|
||||||
|
---@field type RuntimeNodeType
|
||||||
|
---@field parent? string
|
||||||
|
---@field asset? string Normal image/sprite/spine asset key. For button nodes this is the normal-state image.
|
||||||
|
---@field pressedAsset? string Button pressed-state image asset key.
|
||||||
|
---@field disabledAsset? string Button disabled-state image asset key.
|
||||||
|
---@field animation? string
|
||||||
|
---@field skin? string
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field text? string
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
---@field paddingRight? number
|
||||||
|
---@field paddingBottom? number
|
||||||
|
---@field anchor? RuntimeAnchor
|
||||||
|
---@field layer? integer
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field alpha? number
|
||||||
|
---@field scale? number
|
||||||
|
---@field rotation? number
|
||||||
|
---@field color? string
|
||||||
|
---@field fontSize? number
|
||||||
|
---@field textAlign? RuntimeTextAlign
|
||||||
|
---@field radius? number
|
||||||
|
---@field strokeWidth? number
|
||||||
|
---@field value? number
|
||||||
|
---@field scrollX? number
|
||||||
|
---@field scrollY? number
|
||||||
|
---@field contentWidth? number
|
||||||
|
---@field contentHeight? number
|
||||||
|
---@field virtualized? boolean
|
||||||
|
---@field cacheExtent? number
|
||||||
|
---@field inertia? boolean
|
||||||
|
---@field scrollbarThumbColor? string
|
||||||
|
---@field scrollbarTrackColor? string
|
||||||
|
---@field scrollbarThickness? number
|
||||||
|
---@field scrollbarVisible? boolean
|
||||||
|
---@field interactive? boolean
|
||||||
|
---@field onTap? string
|
||||||
|
---@field onScroll? string
|
||||||
|
---@field preset? RuntimeParticlePreset
|
||||||
|
---@field count? integer
|
||||||
|
---@field duration? number
|
||||||
|
---@field speedMin? number
|
||||||
|
---@field speedMax? number
|
||||||
|
---@field gravityX? number
|
||||||
|
---@field gravityY? number
|
||||||
|
---@field spread? number
|
||||||
|
---@field colorTo? string
|
||||||
|
---@field radiusTo? number
|
||||||
|
---@field autoRemove? boolean
|
||||||
|
---@field fadeOut? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNodeProps
|
||||||
|
---@field type? RuntimeNodeType
|
||||||
|
---@field parent? string
|
||||||
|
---@field asset? string Normal image/sprite/spine asset key. For button nodes this is the normal-state image.
|
||||||
|
---@field pressedAsset? string Button pressed-state image asset key.
|
||||||
|
---@field disabledAsset? string Button disabled-state image asset key.
|
||||||
|
---@field animation? string
|
||||||
|
---@field skin? string
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field text? string
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
---@field paddingRight? number
|
||||||
|
---@field paddingBottom? number
|
||||||
|
---@field anchor? RuntimeAnchor
|
||||||
|
---@field layer? integer
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field alpha? number
|
||||||
|
---@field scale? number
|
||||||
|
---@field rotation? number
|
||||||
|
---@field color? string
|
||||||
|
---@field fontSize? number
|
||||||
|
---@field textAlign? RuntimeTextAlign
|
||||||
|
---@field radius? number
|
||||||
|
---@field strokeWidth? number
|
||||||
|
---@field value? number
|
||||||
|
---@field scrollX? number
|
||||||
|
---@field scrollY? number
|
||||||
|
---@field contentWidth? number
|
||||||
|
---@field contentHeight? number
|
||||||
|
---@field virtualized? boolean
|
||||||
|
---@field cacheExtent? number
|
||||||
|
---@field inertia? boolean
|
||||||
|
---@field scrollbarThumbColor? string
|
||||||
|
---@field scrollbarTrackColor? string
|
||||||
|
---@field scrollbarThickness? number
|
||||||
|
---@field scrollbarVisible? boolean
|
||||||
|
---@field interactive? boolean
|
||||||
|
---@field onTap? string
|
||||||
|
---@field onScroll? string
|
||||||
|
---@field preset? RuntimeParticlePreset
|
||||||
|
---@field count? integer
|
||||||
|
---@field duration? number
|
||||||
|
---@field speedMin? number
|
||||||
|
---@field speedMax? number
|
||||||
|
---@field gravityX? number
|
||||||
|
---@field gravityY? number
|
||||||
|
---@field spread? number
|
||||||
|
---@field colorTo? string
|
||||||
|
---@field radiusTo? number
|
||||||
|
---@field autoRemove? boolean
|
||||||
|
---@field fadeOut? boolean
|
||||||
|
|
||||||
|
---Helper-only fields accepted by runtime_ui/runtime_widgets. They are normalized
|
||||||
|
---before the node/update crosses the Dart Runtime protocol boundary.
|
||||||
|
---@class RuntimeNodeInit: RuntimeNodeProps
|
||||||
|
---@field w? number Alias for width.
|
||||||
|
---@field h? number Alias for height.
|
||||||
|
---@field size? number Alias for both width and height.
|
||||||
|
---@field handler? string Alias for onTap.
|
||||||
|
---@field onClick? string Alias for onTap.
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNodeUpdate
|
||||||
|
---@field id string
|
||||||
|
---@field props RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNodeRemove
|
||||||
|
---@field id string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDiffSection
|
||||||
|
---@field creates? RuntimeNode[]
|
||||||
|
---@field updates? RuntimeNodeUpdate[]
|
||||||
|
---@field removes? (string|RuntimeNodeRemove)[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDiff
|
||||||
|
---@field render? RuntimeDiffSection
|
||||||
|
---@field ui? RuntimeDiffSection
|
||||||
|
---@field commands? RuntimeCommand[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeEvent
|
||||||
|
---@field type RuntimeEventType|string
|
||||||
|
---@field target? string
|
||||||
|
---@field handler? string
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field data? table
|
||||||
|
|
||||||
|
---@class (exact) RuntimeCommand
|
||||||
|
---@field type RuntimeCommandType
|
||||||
|
---@field target? string
|
||||||
|
---@field scope? string
|
||||||
|
---@field id? string
|
||||||
|
---@field group? string
|
||||||
|
---@field commandGroup? string
|
||||||
|
---@field onComplete? string
|
||||||
|
---@field duration? number
|
||||||
|
---@field commands? RuntimeCommand[]
|
||||||
|
---@field path? RuntimePoint[]
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field alpha? number
|
||||||
|
---@field scale? number
|
||||||
|
---@field angle? number
|
||||||
|
---@field text? string
|
||||||
|
---@field message? string
|
||||||
|
---@field asset? string
|
||||||
|
---@field name? string
|
||||||
|
---@field volume? number
|
||||||
|
---@field channel? string
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field failOnError? boolean
|
||||||
|
---@field animation? string
|
||||||
|
---@field track? integer
|
||||||
|
---@field queue? boolean
|
||||||
|
---@field delay? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeCommandOpts
|
||||||
|
---@field id? string
|
||||||
|
---@field group? string
|
||||||
|
---@field commandGroup? string
|
||||||
|
---@field scope? string
|
||||||
|
---@field onComplete? string
|
||||||
|
---@field duration? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeAudioCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field volume? number
|
||||||
|
---@field name? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeBgmCommandOpts: RuntimeAudioCommandOpts
|
||||||
|
---@field channel? string
|
||||||
|
---@field loop? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimeSpineCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field track? integer
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field queue? boolean
|
||||||
|
---@field delay? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeResourceCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field failOnError? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimePoint
|
||||||
|
---@field x number
|
||||||
|
---@field y number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeLocaleContext
|
||||||
|
---@field requested string
|
||||||
|
---@field resolved string
|
||||||
|
---@field default string
|
||||||
|
---@field supported string[]
|
||||||
|
---@field languageCode string
|
||||||
|
---@field scriptCode? string
|
||||||
|
---@field countryCode? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeScreenContext
|
||||||
|
---@field width number
|
||||||
|
---@field height number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDesignContext
|
||||||
|
---@field width number
|
||||||
|
---@field height number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeViewportContext
|
||||||
|
---@field x number
|
||||||
|
---@field y number
|
||||||
|
---@field width number
|
||||||
|
---@field height number
|
||||||
|
---@field scaleX number
|
||||||
|
---@field scaleY number
|
||||||
|
---@field scaleMode RuntimeScaleMode|string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeContext
|
||||||
|
---@field screen RuntimeScreenContext
|
||||||
|
---@field design RuntimeDesignContext
|
||||||
|
---@field viewport RuntimeViewportContext
|
||||||
|
---@field seed integer
|
||||||
|
---@field runtimeApiVersion integer
|
||||||
|
---@field gameId string
|
||||||
|
---@field gameVersion string
|
||||||
|
---@field locale? RuntimeLocaleContext
|
||||||
|
|
||||||
|
---@class RuntimeUi
|
||||||
|
---@field style fun(base?: RuntimeNodeProps, opts?: RuntimeNodeProps): RuntimeNodeProps
|
||||||
|
---@field with_parent fun(parent: string, opts?: RuntimeNodeProps): RuntimeNodeProps
|
||||||
|
---@field node fun(node_type: RuntimeNodeType, id: string, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field panel fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field rect fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field circle fun(id: string, x: number|RuntimeNodeInit, y?: number, size?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field line fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field progress fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, value?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field particle fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field text fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field button fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, handler?: string, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field list_view fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field image fun(id: string, asset: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field sprite fun(id: string, asset: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field spine fun(id: string, asset: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, animation?: string, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field update fun(id: string, props: RuntimeNodeInit): RuntimeNodeUpdate
|
||||||
|
---@field text_update fun(id: string, text: string): RuntimeNodeUpdate
|
||||||
|
---@field visible_update fun(id: string, visible: boolean): RuntimeNodeUpdate
|
||||||
|
---@field alpha_update fun(id: string, alpha: number): RuntimeNodeUpdate
|
||||||
|
---@field scale_update fun(id: string, scale: number): RuntimeNodeUpdate
|
||||||
|
---@field position_update fun(id: string, x: number, y: number): RuntimeNodeUpdate
|
||||||
|
---@field size_update fun(id: string, width: number, height: number): RuntimeNodeUpdate
|
||||||
|
---@field transform_update fun(id: string, x: number, y: number, scale: number, rotation: number): RuntimeNodeUpdate
|
||||||
|
---@field batch_update fun(ids: string[], props: RuntimeNodeInit): RuntimeNodeUpdate[]
|
||||||
|
---@field append fun(nodes: RuntimeNode[], node: RuntimeNode): RuntimeNode[]
|
||||||
|
---@field append_all fun(nodes: RuntimeNode[], extra_nodes: RuntimeNode[]): RuntimeNode[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDialogButton
|
||||||
|
---@field id? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler string
|
||||||
|
---@field color? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDialogOpts
|
||||||
|
---@field screenWidth? number
|
||||||
|
---@field screenHeight? number
|
||||||
|
---@field overlay? boolean
|
||||||
|
---@field overlayColor? string
|
||||||
|
---@field blockInput? boolean
|
||||||
|
---@field layer? integer
|
||||||
|
---@field color? string
|
||||||
|
---@field radius? number
|
||||||
|
---@field panelStyle? RuntimeNodeProps
|
||||||
|
---@field titleColor? string
|
||||||
|
---@field titleSize? number
|
||||||
|
---@field titleStyle? RuntimeNodeProps
|
||||||
|
---@field messageColor? string
|
||||||
|
---@field messageSize? number
|
||||||
|
---@field messageStyle? RuntimeNodeProps
|
||||||
|
---@field buttons? RuntimeDialogButton[]
|
||||||
|
---@field buttonGap? number
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeLabeledProgressOpts: RuntimeNodeInit
|
||||||
|
---@field labelHeight? number
|
||||||
|
---@field labelStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimePillOpts: RuntimeNodeInit
|
||||||
|
---@field panelStyle? RuntimeNodeProps
|
||||||
|
---@field textStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeTextButtonOpts: RuntimeNodeInit
|
||||||
|
---@field variant? RuntimeButtonVariant
|
||||||
|
|
||||||
|
---@class RuntimeListItemOpts: RuntimeTextButtonOpts
|
||||||
|
---@field selected? boolean
|
||||||
|
---@field activeColor? string
|
||||||
|
---@field inactiveColor? string
|
||||||
|
|
||||||
|
---@class RuntimeTabItem
|
||||||
|
---@field id? string
|
||||||
|
---@field key? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler? string
|
||||||
|
---@field selected? boolean
|
||||||
|
|
||||||
|
---@class RuntimeTabsOpts: RuntimeNodeInit
|
||||||
|
---@field tabs? RuntimeTabItem[]
|
||||||
|
---@field selected? string
|
||||||
|
---@field gap? number
|
||||||
|
---@field itemWidth? number
|
||||||
|
---@field itemHeight? number
|
||||||
|
---@field activeColor? string
|
||||||
|
---@field inactiveColor? string
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeActionItem
|
||||||
|
---@field id? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler? string
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field color? string
|
||||||
|
---@field style? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeActionRowOpts: RuntimeNodeInit
|
||||||
|
---@field actions? RuntimeActionItem[]
|
||||||
|
---@field gap? number
|
||||||
|
---@field itemWidth? number
|
||||||
|
---@field itemHeight? number
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimePanelHeaderOpts: RuntimeNodeInit
|
||||||
|
---@field eyebrow? string
|
||||||
|
---@field title string
|
||||||
|
---@field summary? string
|
||||||
|
---@field gap? number
|
||||||
|
---@field eyebrowId? string
|
||||||
|
---@field titleId? string
|
||||||
|
---@field summaryId? string
|
||||||
|
---@field eyebrowHeight? number
|
||||||
|
---@field titleHeight? number
|
||||||
|
---@field summaryHeight? number
|
||||||
|
---@field eyebrowStyle? RuntimeNodeProps
|
||||||
|
---@field titleStyle? RuntimeNodeProps
|
||||||
|
---@field summaryStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeWidgetTheme
|
||||||
|
---@field primary? string
|
||||||
|
---@field secondary? string
|
||||||
|
---@field success? string
|
||||||
|
---@field overlay? string
|
||||||
|
---@field surface? string
|
||||||
|
---@field surfaceAlt? string
|
||||||
|
---@field card? string
|
||||||
|
---@field text? string
|
||||||
|
---@field muted? string
|
||||||
|
---@field progress? string
|
||||||
|
---@field transparent? string
|
||||||
|
|
||||||
|
---@class RuntimeWidgets
|
||||||
|
---@field configure fun(tokens?: RuntimeWidgetTheme): RuntimeWidgets
|
||||||
|
---@field label fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field section_title fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field pill fun(id: string, text: string|RuntimePillOpts, x?: number, y?: number, width?: number, height?: number, opts?: RuntimePillOpts): RuntimeNode[]
|
||||||
|
---@field text_button fun(id: string, text: string|RuntimeTextButtonOpts, x?: number, y?: number, width?: number, height?: number, handler?: string, opts?: RuntimeTextButtonOpts): RuntimeNode
|
||||||
|
---@field list_item fun(id: string, text: string|RuntimeListItemOpts, x?: number, y?: number, width?: number, height?: number, handler?: string, opts?: RuntimeListItemOpts): RuntimeNode
|
||||||
|
---@field tabs fun(id: string, tabs: RuntimeTabItem[]|RuntimeTabsOpts, opts?: RuntimeTabsOpts): RuntimeNode[]
|
||||||
|
---@field action_row fun(id: string, actions: RuntimeActionItem[]|RuntimeActionRowOpts, opts?: RuntimeActionRowOpts): RuntimeNode[]
|
||||||
|
---@field panel_header fun(id: string, opts: RuntimePanelHeaderOpts): RuntimeNode[]
|
||||||
|
---@field overlay fun(id: string, width: number|RuntimeNodeInit, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field card fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field progress_bar fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, value?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field labeled_progress fun(id: string, label: string, x: number, y: number, width: number, height: number, value: number, opts?: RuntimeLabeledProgressOpts): RuntimeNode[]
|
||||||
|
---@field button_row fun(parent: string, id: string, buttons: RuntimeDialogButton[], x: number, y: number, width: number, height: number, gap?: number, opts?: RuntimeNodeProps): RuntimeNode[]
|
||||||
|
---@field dialog fun(id: string, title: string, message: string, x: number, y: number, width: number, height: number, opts?: RuntimeDialogOpts): RuntimeNode[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeLayoutItem
|
||||||
|
---@field node RuntimeNode
|
||||||
|
---@field marginLeft? number
|
||||||
|
---@field marginRight? number
|
||||||
|
---@field marginTop? number
|
||||||
|
---@field marginBottom? number
|
||||||
|
|
||||||
|
---@class RuntimeLayoutItemOpts
|
||||||
|
---@field margin? number
|
||||||
|
---@field mx? number
|
||||||
|
---@field my? number
|
||||||
|
---@field ml? number
|
||||||
|
---@field mr? number
|
||||||
|
---@field mt? number
|
||||||
|
---@field mb? number
|
||||||
|
---@field marginLeft? number
|
||||||
|
---@field marginRight? number
|
||||||
|
---@field marginTop? number
|
||||||
|
---@field marginBottom? number
|
||||||
|
|
||||||
|
---@class RuntimeLinearLayoutOpts
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field gap? number
|
||||||
|
---@field align? RuntimeLayoutAlign
|
||||||
|
---@field padding? number
|
||||||
|
---@field paddingX? number
|
||||||
|
---@field paddingY? number
|
||||||
|
---@field px? number
|
||||||
|
---@field py? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
|
||||||
|
---@class RuntimeBoxLayoutOpts: RuntimeLinearLayoutOpts
|
||||||
|
---@field rows? integer
|
||||||
|
---@field columns? integer
|
||||||
|
---@field cols? integer
|
||||||
|
---@field cellWidth? number
|
||||||
|
---@field cellHeight? number
|
||||||
|
---@field cellW? number
|
||||||
|
---@field cellH? number
|
||||||
|
---@field gapX? number
|
||||||
|
---@field gapY? number
|
||||||
|
---@field valign? RuntimeLayoutAlign
|
||||||
|
|
||||||
|
---@class RuntimeLayout
|
||||||
|
---@field item fun(node: RuntimeNode, opts?: RuntimeLayoutItemOpts): RuntimeLayoutItem
|
||||||
|
---@field local_position fun(origin: RuntimePoint, position: RuntimePoint): RuntimePoint
|
||||||
|
---@field row fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeLinearLayoutOpts): RuntimeNode[]
|
||||||
|
---@field column fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeLinearLayoutOpts): RuntimeNode[]
|
||||||
|
---@field stack fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeLinearLayoutOpts): RuntimeNode[]
|
||||||
|
---@field box fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeBoxLayoutOpts): RuntimeNode[]
|
||||||
|
|
||||||
|
---@class RuntimeCommands
|
||||||
|
---@field toast fun(text: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field copy_text fun(text: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field delay fun(duration: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field sequence fun(items: RuntimeCommand[], opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field parallel fun(items: RuntimeCommand[], opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field move_path fun(target: string, path: RuntimePoint[], opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field move_to fun(target: string, x: number, y: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field fade_to fun(target: string, alpha: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field scale_to fun(target: string, scale: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field rotate_to fun(target: string, angle: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field remove_node fun(target: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field play_spine_animation fun(target: string, animation: string, opts?: RuntimeSpineCommandOpts): RuntimeCommand
|
||||||
|
---@field play_sound fun(asset: string, opts?: RuntimeAudioCommandOpts): RuntimeCommand
|
||||||
|
---@field play_bgm fun(asset: string, opts?: RuntimeBgmCommandOpts): RuntimeCommand
|
||||||
|
---@field pause_bgm fun(channel?: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field resume_bgm fun(channel?: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field stop_bgm fun(channel?: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field preload_group fun(group: string, opts?: RuntimeResourceCommandOpts): RuntimeCommand
|
||||||
|
---@field evict_group fun(group: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field cancel_id fun(id: string): RuntimeCommand
|
||||||
|
---@field cancel_group fun(group: string): RuntimeCommand
|
||||||
|
---@field cancel_scope fun(scope: string): RuntimeCommand
|
||||||
|
|
||||||
|
---@class RuntimeImportApi
|
||||||
|
---@field import fun(moduleName: string): table
|
||||||
|
|
||||||
|
---@type RuntimeImportApi
|
||||||
|
runtime = runtime
|
||||||
|
|
||||||
|
---@alias PlayerColor 'red'|'blue'
|
||||||
|
---@alias GamePhase 'waiting_roll'|'waiting_piece'|'animating'
|
||||||
|
---@alias PieceStatus 'home'|'path'|'finished'
|
||||||
|
|
||||||
|
---@class (exact) PieceState
|
||||||
|
---@field id string
|
||||||
|
---@field owner PlayerColor
|
||||||
|
---@field path_index integer
|
||||||
|
---@field status PieceStatus
|
||||||
|
|
||||||
|
---@class (exact) LudoState
|
||||||
|
---@field current_player PlayerColor
|
||||||
|
---@field phase GamePhase
|
||||||
|
---@field dice_index integer
|
||||||
|
---@field dice_values integer[]
|
||||||
|
---@field dice? integer
|
||||||
|
---@field selected_piece? string
|
||||||
|
---@field players PlayerColor[]
|
||||||
|
---@field pieces table<string, PieceState>
|
||||||
|
|
||||||
|
---@class (exact) BoardPoint
|
||||||
|
---@field x number
|
||||||
|
---@field y number
|
||||||
|
|
||||||
|
---@class (exact) BoardData
|
||||||
|
---@field red_home BoardPoint[]
|
||||||
|
---@field blue_home BoardPoint[]
|
||||||
|
---@field path BoardPoint[]
|
||||||
|
---@field start table<PlayerColor, integer>
|
||||||
15
example/assets/games/ludo/scripts/state.lua
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---@type LudoState
|
||||||
|
return {
|
||||||
|
current_player = "red",
|
||||||
|
phase = "waiting_roll",
|
||||||
|
dice_index = 0,
|
||||||
|
dice_values = {6, 3, 5, 2, 6, 4},
|
||||||
|
selected_piece = nil,
|
||||||
|
players = {"red", "blue"},
|
||||||
|
pieces = {
|
||||||
|
red_1 = { id = "red_1", owner = "red", path_index = 0, status = "home" },
|
||||||
|
red_2 = { id = "red_2", owner = "red", path_index = 0, status = "home" },
|
||||||
|
blue_1 = { id = "blue_1", owner = "blue", path_index = 0, status = "home" },
|
||||||
|
blue_2 = { id = "blue_2", owner = "blue", path_index = 0, status = "home" }
|
||||||
|
}
|
||||||
|
}
|
||||||
80
example/assets/games/ludo/scripts/styles.lua
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
local theme = runtime.import("theme")
|
||||||
|
|
||||||
|
local colors = theme.colors
|
||||||
|
local styles = {}
|
||||||
|
|
||||||
|
styles.layers = {
|
||||||
|
background = 0,
|
||||||
|
board = 1,
|
||||||
|
board_content = 2,
|
||||||
|
piece = 10,
|
||||||
|
hud = 100,
|
||||||
|
hud_content = 101
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.board_panel = {
|
||||||
|
color = colors.board,
|
||||||
|
layer = styles.layers.background
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.board_title = {
|
||||||
|
fontSize = 24,
|
||||||
|
color = colors.text,
|
||||||
|
layer = styles.layers.board_content
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.red_home = {
|
||||||
|
color = colors.red_home,
|
||||||
|
layer = styles.layers.board
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.blue_home = {
|
||||||
|
color = colors.blue_home,
|
||||||
|
layer = styles.layers.board
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.path_cell = {
|
||||||
|
anchor = "center",
|
||||||
|
color = colors.path_cell,
|
||||||
|
layer = styles.layers.board_content
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.top_bar = {
|
||||||
|
color = colors.top_bar,
|
||||||
|
layer = styles.layers.hud
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.hud_text = {
|
||||||
|
fontSize = 22,
|
||||||
|
color = colors.text,
|
||||||
|
layer = styles.layers.hud_content
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.dice_button = {
|
||||||
|
color = colors.dice_button,
|
||||||
|
fontSize = 20,
|
||||||
|
layer = styles.layers.hud_content
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.piece_highlight = {
|
||||||
|
scale = 1.25,
|
||||||
|
alpha = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.piece_normal = {
|
||||||
|
scale = 1,
|
||||||
|
alpha = 0.95
|
||||||
|
}
|
||||||
|
|
||||||
|
function styles.piece(owner)
|
||||||
|
return {
|
||||||
|
anchor = "center",
|
||||||
|
asset = "piece_" .. owner,
|
||||||
|
color = colors[owner],
|
||||||
|
layer = styles.layers.piece,
|
||||||
|
interactive = true,
|
||||||
|
onTap = "piece_tap"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return styles
|
||||||
18
example/assets/games/ludo/scripts/theme.lua
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
local i18n = runtime.import("i18n")
|
||||||
|
|
||||||
|
return {
|
||||||
|
title = function()
|
||||||
|
return i18n.t("title")
|
||||||
|
end,
|
||||||
|
colors = {
|
||||||
|
red = "#ef4444",
|
||||||
|
blue = "#3b82f6",
|
||||||
|
top_bar = "#020617",
|
||||||
|
dice_button = "#2563eb",
|
||||||
|
board = "#1e293b",
|
||||||
|
red_home = "#7f1d1d",
|
||||||
|
blue_home = "#1e3a8a",
|
||||||
|
path_cell = "#f8fafc",
|
||||||
|
text = "#ffffff"
|
||||||
|
}
|
||||||
|
}
|
||||||
74
example/assets/games/ludo/scripts/ui.lua
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
---@type RuntimeUi
|
||||||
|
local runtime_ui = runtime.import("runtime_ui")
|
||||||
|
---@type RuntimeLayout
|
||||||
|
local layout = runtime.import("layout")
|
||||||
|
local theme = runtime.import("theme")
|
||||||
|
local i18n = runtime.import("i18n")
|
||||||
|
local styles = runtime.import("styles")
|
||||||
|
local state = runtime.import("state")
|
||||||
|
local board = runtime.import("board")
|
||||||
|
local rules = runtime.import("rules")
|
||||||
|
|
||||||
|
local ui = {}
|
||||||
|
local board_origin = { x = 40, y = 90 }
|
||||||
|
|
||||||
|
function ui.highlight_updates(ids, enabled)
|
||||||
|
return runtime_ui.batch_update(ids, enabled and styles.piece_highlight or styles.piece_normal)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.create_board_nodes()
|
||||||
|
local nodes = {
|
||||||
|
runtime_ui.sprite("board_panel", "board", 40, 90, 640, 520, styles.board_panel),
|
||||||
|
runtime_ui.text("board_title", theme.title(), 240, 15, 180, 40, runtime_ui.with_parent("board_panel", styles.board_title)),
|
||||||
|
runtime_ui.panel("red_home", 20, 390, 130, 90, runtime_ui.with_parent("board_panel", styles.red_home)),
|
||||||
|
runtime_ui.panel("blue_home", 460, -10, 130, 90, runtime_ui.with_parent("board_panel", styles.blue_home))
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, cell in ipairs(board.path) do
|
||||||
|
local pos = layout.local_position(board_origin, cell)
|
||||||
|
runtime_ui.append(nodes, runtime_ui.circle("cell_" .. tostring(index), pos.x, pos.y, 28, runtime_ui.with_parent("board_panel", styles.path_cell)))
|
||||||
|
end
|
||||||
|
|
||||||
|
for id, piece in pairs(state.pieces) do
|
||||||
|
local pos = rules.piece_home_position(piece)
|
||||||
|
runtime_ui.append(nodes, runtime_ui.circle(id, pos.x, pos.y, 44, styles.piece(piece.owner)))
|
||||||
|
end
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.create_ui_nodes()
|
||||||
|
local hud_items = layout.row("top_bar", {
|
||||||
|
runtime_ui.text("turn_text", i18n.t("current_player", { player = i18n.player(state.current_player) }), 0, 0, 230, 32, styles.hud_text),
|
||||||
|
runtime_ui.text("dice_text", i18n.t("dice_empty"), 0, 0, 120, 32, styles.hud_text),
|
||||||
|
layout.item(
|
||||||
|
runtime_ui.button("dice_button", i18n.t("roll_button"), 0, 0, 130, 48, "roll_dice", styles.dice_button),
|
||||||
|
{ marginLeft = 134 }
|
||||||
|
)
|
||||||
|
}, {
|
||||||
|
x = 24,
|
||||||
|
height = 76,
|
||||||
|
gap = 16,
|
||||||
|
align = "center"
|
||||||
|
})
|
||||||
|
|
||||||
|
local nodes = { runtime_ui.panel("top_bar", 0, 0, 720, 76, styles.top_bar) }
|
||||||
|
return runtime_ui.append_all(nodes, hud_items)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.dice_and_turn_updates(dice, current_player)
|
||||||
|
return {
|
||||||
|
runtime_ui.text_update("dice_text", i18n.t("dice_value", { value = dice })),
|
||||||
|
runtime_ui.text_update("turn_text", i18n.t("current_player", { player = i18n.player(current_player) }))
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.dice_update(dice)
|
||||||
|
return { runtime_ui.text_update("dice_text", i18n.t("dice_value", { value = dice })) }
|
||||||
|
end
|
||||||
|
|
||||||
|
function ui.turn_update(current_player)
|
||||||
|
return { runtime_ui.text_update("turn_text", i18n.t("current_player", { player = i18n.player(current_player) })) }
|
||||||
|
end
|
||||||
|
|
||||||
|
return ui
|
||||||
BIN
example/assets/games/showcase/assets/button_disabled.png
Normal file
|
After Width: | Height: | Size: 138 B |
BIN
example/assets/games/showcase/assets/button_normal.png
Normal file
|
After Width: | Height: | Size: 136 B |
BIN
example/assets/games/showcase/assets/button_pressed.png
Normal file
|
After Width: | Height: | Size: 136 B |
BIN
example/assets/games/showcase/assets/click.wav
Normal file
BIN
example/assets/games/showcase/assets/sample.png
Normal file
|
After Width: | Height: | Size: 70 B |
62
example/assets/games/showcase/manifest.json
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"gameId": "showcase",
|
||||||
|
"name": "Lua Runtime Showcase",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"runtimeApiVersion": 1,
|
||||||
|
"entry": "scripts/main.lua",
|
||||||
|
"assetsBase": "assets",
|
||||||
|
"defaultLocale": "zh-Hans",
|
||||||
|
"supportedLocales": [
|
||||||
|
"zh-Hans",
|
||||||
|
"en"
|
||||||
|
],
|
||||||
|
"display": {
|
||||||
|
"designWidth": 720,
|
||||||
|
"designHeight": 720,
|
||||||
|
"scaleMode": "fit"
|
||||||
|
},
|
||||||
|
"modules": {
|
||||||
|
"runtime_ui": "runtime:runtime_ui.lua",
|
||||||
|
"runtime_widgets": "runtime:runtime_widgets.lua",
|
||||||
|
"runtime_commands": "runtime:runtime_commands.lua",
|
||||||
|
"layout": "runtime:layout.lua",
|
||||||
|
"theme": "scripts/theme.lua",
|
||||||
|
"styles": "scripts/styles.lua",
|
||||||
|
"state": "scripts/state.lua",
|
||||||
|
"i18n": "scripts/i18n.lua",
|
||||||
|
"examples": "scripts/examples.lua",
|
||||||
|
"ui": "scripts/ui.lua"
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"sample_image": {
|
||||||
|
"type": "image",
|
||||||
|
"path": "assets/sample.png",
|
||||||
|
"preload": "optional",
|
||||||
|
"group": "media"
|
||||||
|
},
|
||||||
|
"click": {
|
||||||
|
"type": "audio",
|
||||||
|
"path": "assets/click.wav",
|
||||||
|
"preload": "lazy",
|
||||||
|
"group": "media"
|
||||||
|
},
|
||||||
|
"button_normal": {
|
||||||
|
"type": "image",
|
||||||
|
"path": "assets/button_normal.png",
|
||||||
|
"preload": "optional",
|
||||||
|
"group": "media"
|
||||||
|
},
|
||||||
|
"button_pressed": {
|
||||||
|
"type": "image",
|
||||||
|
"path": "assets/button_pressed.png",
|
||||||
|
"preload": "optional",
|
||||||
|
"group": "media"
|
||||||
|
},
|
||||||
|
"button_disabled": {
|
||||||
|
"type": "image",
|
||||||
|
"path": "assets/button_disabled.png",
|
||||||
|
"preload": "optional",
|
||||||
|
"group": "media"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
819
example/assets/games/showcase/scripts/examples.lua
Normal file
@@ -0,0 +1,819 @@
|
|||||||
|
---@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
|
||||||
151
example/assets/games/showcase/scripts/i18n.lua
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
local state = runtime.import("state")
|
||||||
|
|
||||||
|
---@class ShowcaseI18n
|
||||||
|
local i18n = {}
|
||||||
|
|
||||||
|
local fallback_locale = "zh-Hans"
|
||||||
|
|
||||||
|
local messages = {
|
||||||
|
["zh-Hans"] = {
|
||||||
|
app_title = "Lua Runtime Showcase",
|
||||||
|
app_subtitle = "仿 Cocos Showcase:左侧管理所有示例,右侧查看说明并直接运行。",
|
||||||
|
examples_title = "Examples",
|
||||||
|
preview_title = "Preview / Runtime Stage",
|
||||||
|
tab_code = "代码",
|
||||||
|
tab_params = "参数",
|
||||||
|
copy_code = "复制代码",
|
||||||
|
copy_params = "复制参数",
|
||||||
|
selected_prefix = "已选择示例:",
|
||||||
|
status_ready = "点击按钮查看 RuntimeEvent -> Lua -> Diff / Command 示例",
|
||||||
|
i18n_current = "当前语言",
|
||||||
|
i18n_zh = "中文文案:运行时只传 locale,翻译由 Lua 包管理。",
|
||||||
|
i18n_en = "English copy: Lua owns text resources and fallback policy.",
|
||||||
|
i18n_switched = "已切换语言:",
|
||||||
|
responsive_title = "分辨率适配",
|
||||||
|
responsive_design = "设计分辨率",
|
||||||
|
responsive_screen = "屏幕",
|
||||||
|
responsive_viewport = "视口",
|
||||||
|
responsive_phone = "模拟手机宽度",
|
||||||
|
responsive_tablet = "模拟平板宽度",
|
||||||
|
responsive_desktop = "模拟桌面宽度"
|
||||||
|
},
|
||||||
|
en = {
|
||||||
|
app_title = "Lua Runtime Showcase",
|
||||||
|
app_subtitle = "Cocos-style showcase: select examples on the left, inspect and run them on the right.",
|
||||||
|
examples_title = "Examples",
|
||||||
|
preview_title = "Preview / Runtime Stage",
|
||||||
|
tab_code = "Code",
|
||||||
|
tab_params = "Params",
|
||||||
|
copy_code = "Copy code",
|
||||||
|
copy_params = "Copy params",
|
||||||
|
selected_prefix = "Selected example: ",
|
||||||
|
status_ready = "Tap actions to inspect RuntimeEvent -> Lua -> Diff / Command.",
|
||||||
|
i18n_current = "Current locale",
|
||||||
|
i18n_zh = "中文文案:运行时只传 locale,翻译由 Lua 包管理。",
|
||||||
|
i18n_en = "English copy: Lua owns text resources and fallback policy.",
|
||||||
|
i18n_switched = "Locale switched: ",
|
||||||
|
responsive_title = "Responsive Layout",
|
||||||
|
responsive_design = "Design",
|
||||||
|
responsive_screen = "Screen",
|
||||||
|
responsive_viewport = "Viewport",
|
||||||
|
responsive_phone = "Simulate phone width",
|
||||||
|
responsive_tablet = "Simulate tablet width",
|
||||||
|
responsive_desktop = "Simulate desktop width"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
---@param locale? string
|
||||||
|
---@return string
|
||||||
|
local function normalize(locale)
|
||||||
|
if locale == nil or locale == "" then
|
||||||
|
return fallback_locale
|
||||||
|
end
|
||||||
|
if messages[locale] ~= nil then
|
||||||
|
return locale
|
||||||
|
end
|
||||||
|
|
||||||
|
local language = string.match(locale, "^([A-Za-z]+)")
|
||||||
|
if language ~= nil and messages[language] ~= nil then
|
||||||
|
return language
|
||||||
|
end
|
||||||
|
return fallback_locale
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param ctx? RuntimeContext
|
||||||
|
function i18n.apply_context(ctx)
|
||||||
|
local locale = fallback_locale
|
||||||
|
if ctx ~= nil and ctx.locale ~= nil and ctx.locale.resolved ~= nil then
|
||||||
|
locale = ctx.locale.resolved
|
||||||
|
end
|
||||||
|
state.locale = normalize(locale)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param locale string
|
||||||
|
function i18n.set_locale(locale)
|
||||||
|
state.locale = normalize(locale)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return string
|
||||||
|
function i18n.toggle_locale()
|
||||||
|
if state.locale == "en" then
|
||||||
|
state.locale = "zh-Hans"
|
||||||
|
else
|
||||||
|
state.locale = "en"
|
||||||
|
end
|
||||||
|
return state.locale
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return string
|
||||||
|
function i18n.current_locale()
|
||||||
|
return normalize(state.locale)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param key string
|
||||||
|
---@return string
|
||||||
|
function i18n.t(key)
|
||||||
|
local locale = normalize(state.locale)
|
||||||
|
local bundle = messages[locale] or messages[fallback_locale]
|
||||||
|
return bundle[key] or messages[fallback_locale][key] or key
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param example ShowcaseExample
|
||||||
|
---@param field string
|
||||||
|
---@return string
|
||||||
|
function i18n.example_field(example, field)
|
||||||
|
if i18n.current_locale() == "en" then
|
||||||
|
local en_value = example[field .. "_en"]
|
||||||
|
if en_value ~= nil and en_value ~= "" then
|
||||||
|
return en_value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return example[field]
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param example ShowcaseExample
|
||||||
|
---@return string
|
||||||
|
function i18n.example_label(example)
|
||||||
|
return i18n.example_field(example, "menu")
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param example ShowcaseExample
|
||||||
|
---@return string
|
||||||
|
function i18n.example_params(example)
|
||||||
|
return i18n.example_field(example, "params")
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param example ShowcaseExample
|
||||||
|
---@return string
|
||||||
|
function i18n.example_group(example)
|
||||||
|
return i18n.example_field(example, "group")
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param action ShowcaseAction
|
||||||
|
---@return string
|
||||||
|
function i18n.action_text(action)
|
||||||
|
if i18n.current_locale() == "en" and action.text_en ~= nil and action.text_en ~= "" then
|
||||||
|
return action.text_en
|
||||||
|
end
|
||||||
|
return action.text
|
||||||
|
end
|
||||||
|
|
||||||
|
return i18n
|
||||||
698
example/assets/games/showcase/scripts/main.lua
Normal 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_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
|
||||||
606
example/assets/games/showcase/scripts/runtime_defs.lua
Normal file
@@ -0,0 +1,606 @@
|
|||||||
|
---@meta
|
||||||
|
--- COMMON RUNTIME TYPES SECTION.
|
||||||
|
--- Source of truth: tool/lua_runtime_defs_common.lua
|
||||||
|
--- After editing this common section, run:
|
||||||
|
--- dart run tool/generate_lua_runtime_defs.dart
|
||||||
|
|
||||||
|
|
||||||
|
---@alias RuntimeNodeType
|
||||||
|
---| 'panel'
|
||||||
|
---| 'button'
|
||||||
|
---| 'text'
|
||||||
|
---| 'circle'
|
||||||
|
---| 'rect'
|
||||||
|
---| 'line'
|
||||||
|
---| 'progress'
|
||||||
|
---| 'listView'
|
||||||
|
---| 'sprite'
|
||||||
|
---| 'image'
|
||||||
|
---| 'spine'
|
||||||
|
---| 'particle'
|
||||||
|
|
||||||
|
---@alias RuntimeAnchor
|
||||||
|
---| 'center'
|
||||||
|
---| 'topLeft'
|
||||||
|
---| 'topRight'
|
||||||
|
---| 'bottomLeft'
|
||||||
|
---| 'bottomRight'
|
||||||
|
|
||||||
|
---@alias RuntimeTextAlign
|
||||||
|
---| 'left'
|
||||||
|
---| 'center'
|
||||||
|
---| 'right'
|
||||||
|
|
||||||
|
---@alias RuntimeParticlePreset
|
||||||
|
---| 'burst'
|
||||||
|
---| 'trail'
|
||||||
|
---| 'snow'
|
||||||
|
---| 'confetti'
|
||||||
|
|
||||||
|
---@alias RuntimeCommandType
|
||||||
|
---| 'move_path'
|
||||||
|
---| 'move_to'
|
||||||
|
---| 'fade_to'
|
||||||
|
---| 'scale_to'
|
||||||
|
---| 'rotate_to'
|
||||||
|
---| 'remove_node'
|
||||||
|
---| 'sequence'
|
||||||
|
---| 'parallel'
|
||||||
|
---| 'delay'
|
||||||
|
---| 'toast'
|
||||||
|
---| 'play_sound'
|
||||||
|
---| 'play_bgm'
|
||||||
|
---| 'pause_bgm'
|
||||||
|
---| 'resume_bgm'
|
||||||
|
---| 'stop_bgm'
|
||||||
|
---| 'preload_resources'
|
||||||
|
---| 'evict_resources'
|
||||||
|
---| 'cancel_commands'
|
||||||
|
---| 'play_spine_animation'
|
||||||
|
---| 'copy_text'
|
||||||
|
|
||||||
|
---@alias RuntimeEventType
|
||||||
|
---| 'tap'
|
||||||
|
---| 'animation_done'
|
||||||
|
---| 'resize'
|
||||||
|
---| 'scroll'
|
||||||
|
|
||||||
|
---@alias RuntimeScaleMode
|
||||||
|
---| 'fit'
|
||||||
|
---| 'fill'
|
||||||
|
---| 'stretch'
|
||||||
|
---| 'none'
|
||||||
|
|
||||||
|
---@alias RuntimeLayoutAlign
|
||||||
|
---| 'start'
|
||||||
|
---| 'center'
|
||||||
|
---| 'end'
|
||||||
|
|
||||||
|
---@alias RuntimeButtonVariant
|
||||||
|
---| 'primary'
|
||||||
|
---| 'secondary'
|
||||||
|
---| 'ghost'
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNode
|
||||||
|
---@field id string
|
||||||
|
---@field type RuntimeNodeType
|
||||||
|
---@field parent? string
|
||||||
|
---@field asset? string Normal image/sprite/spine asset key. For button nodes this is the normal-state image.
|
||||||
|
---@field pressedAsset? string Button pressed-state image asset key.
|
||||||
|
---@field disabledAsset? string Button disabled-state image asset key.
|
||||||
|
---@field animation? string
|
||||||
|
---@field skin? string
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field text? string
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
---@field paddingRight? number
|
||||||
|
---@field paddingBottom? number
|
||||||
|
---@field anchor? RuntimeAnchor
|
||||||
|
---@field layer? integer
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field alpha? number
|
||||||
|
---@field scale? number
|
||||||
|
---@field rotation? number
|
||||||
|
---@field color? string
|
||||||
|
---@field fontSize? number
|
||||||
|
---@field textAlign? RuntimeTextAlign
|
||||||
|
---@field radius? number
|
||||||
|
---@field strokeWidth? number
|
||||||
|
---@field value? number
|
||||||
|
---@field scrollX? number
|
||||||
|
---@field scrollY? number
|
||||||
|
---@field contentWidth? number
|
||||||
|
---@field contentHeight? number
|
||||||
|
---@field virtualized? boolean
|
||||||
|
---@field cacheExtent? number
|
||||||
|
---@field inertia? boolean
|
||||||
|
---@field scrollbarThumbColor? string
|
||||||
|
---@field scrollbarTrackColor? string
|
||||||
|
---@field scrollbarThickness? number
|
||||||
|
---@field scrollbarVisible? boolean
|
||||||
|
---@field interactive? boolean
|
||||||
|
---@field onTap? string
|
||||||
|
---@field onScroll? string
|
||||||
|
---@field preset? RuntimeParticlePreset
|
||||||
|
---@field count? integer
|
||||||
|
---@field duration? number
|
||||||
|
---@field speedMin? number
|
||||||
|
---@field speedMax? number
|
||||||
|
---@field gravityX? number
|
||||||
|
---@field gravityY? number
|
||||||
|
---@field spread? number
|
||||||
|
---@field colorTo? string
|
||||||
|
---@field radiusTo? number
|
||||||
|
---@field autoRemove? boolean
|
||||||
|
---@field fadeOut? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNodeProps
|
||||||
|
---@field type? RuntimeNodeType
|
||||||
|
---@field parent? string
|
||||||
|
---@field asset? string Normal image/sprite/spine asset key. For button nodes this is the normal-state image.
|
||||||
|
---@field pressedAsset? string Button pressed-state image asset key.
|
||||||
|
---@field disabledAsset? string Button disabled-state image asset key.
|
||||||
|
---@field animation? string
|
||||||
|
---@field skin? string
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field text? string
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
---@field paddingRight? number
|
||||||
|
---@field paddingBottom? number
|
||||||
|
---@field anchor? RuntimeAnchor
|
||||||
|
---@field layer? integer
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field alpha? number
|
||||||
|
---@field scale? number
|
||||||
|
---@field rotation? number
|
||||||
|
---@field color? string
|
||||||
|
---@field fontSize? number
|
||||||
|
---@field textAlign? RuntimeTextAlign
|
||||||
|
---@field radius? number
|
||||||
|
---@field strokeWidth? number
|
||||||
|
---@field value? number
|
||||||
|
---@field scrollX? number
|
||||||
|
---@field scrollY? number
|
||||||
|
---@field contentWidth? number
|
||||||
|
---@field contentHeight? number
|
||||||
|
---@field virtualized? boolean
|
||||||
|
---@field cacheExtent? number
|
||||||
|
---@field inertia? boolean
|
||||||
|
---@field scrollbarThumbColor? string
|
||||||
|
---@field scrollbarTrackColor? string
|
||||||
|
---@field scrollbarThickness? number
|
||||||
|
---@field scrollbarVisible? boolean
|
||||||
|
---@field interactive? boolean
|
||||||
|
---@field onTap? string
|
||||||
|
---@field onScroll? string
|
||||||
|
---@field preset? RuntimeParticlePreset
|
||||||
|
---@field count? integer
|
||||||
|
---@field duration? number
|
||||||
|
---@field speedMin? number
|
||||||
|
---@field speedMax? number
|
||||||
|
---@field gravityX? number
|
||||||
|
---@field gravityY? number
|
||||||
|
---@field spread? number
|
||||||
|
---@field colorTo? string
|
||||||
|
---@field radiusTo? number
|
||||||
|
---@field autoRemove? boolean
|
||||||
|
---@field fadeOut? boolean
|
||||||
|
|
||||||
|
---Helper-only fields accepted by runtime_ui/runtime_widgets. They are normalized
|
||||||
|
---before the node/update crosses the Dart Runtime protocol boundary.
|
||||||
|
---@class RuntimeNodeInit: RuntimeNodeProps
|
||||||
|
---@field w? number Alias for width.
|
||||||
|
---@field h? number Alias for height.
|
||||||
|
---@field size? number Alias for both width and height.
|
||||||
|
---@field handler? string Alias for onTap.
|
||||||
|
---@field onClick? string Alias for onTap.
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNodeUpdate
|
||||||
|
---@field id string
|
||||||
|
---@field props RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNodeRemove
|
||||||
|
---@field id string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDiffSection
|
||||||
|
---@field creates? RuntimeNode[]
|
||||||
|
---@field updates? RuntimeNodeUpdate[]
|
||||||
|
---@field removes? (string|RuntimeNodeRemove)[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDiff
|
||||||
|
---@field render? RuntimeDiffSection
|
||||||
|
---@field ui? RuntimeDiffSection
|
||||||
|
---@field commands? RuntimeCommand[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeEvent
|
||||||
|
---@field type RuntimeEventType|string
|
||||||
|
---@field target? string
|
||||||
|
---@field handler? string
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field data? table
|
||||||
|
|
||||||
|
---@class (exact) RuntimeCommand
|
||||||
|
---@field type RuntimeCommandType
|
||||||
|
---@field target? string
|
||||||
|
---@field scope? string
|
||||||
|
---@field id? string
|
||||||
|
---@field group? string
|
||||||
|
---@field commandGroup? string
|
||||||
|
---@field onComplete? string
|
||||||
|
---@field duration? number
|
||||||
|
---@field commands? RuntimeCommand[]
|
||||||
|
---@field path? RuntimePoint[]
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field alpha? number
|
||||||
|
---@field scale? number
|
||||||
|
---@field angle? number
|
||||||
|
---@field text? string
|
||||||
|
---@field message? string
|
||||||
|
---@field asset? string
|
||||||
|
---@field name? string
|
||||||
|
---@field volume? number
|
||||||
|
---@field channel? string
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field failOnError? boolean
|
||||||
|
---@field animation? string
|
||||||
|
---@field track? integer
|
||||||
|
---@field queue? boolean
|
||||||
|
---@field delay? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeCommandOpts
|
||||||
|
---@field id? string
|
||||||
|
---@field group? string
|
||||||
|
---@field commandGroup? string
|
||||||
|
---@field scope? string
|
||||||
|
---@field onComplete? string
|
||||||
|
---@field duration? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeAudioCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field volume? number
|
||||||
|
---@field name? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeBgmCommandOpts: RuntimeAudioCommandOpts
|
||||||
|
---@field channel? string
|
||||||
|
---@field loop? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimeSpineCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field track? integer
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field queue? boolean
|
||||||
|
---@field delay? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeResourceCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field failOnError? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimePoint
|
||||||
|
---@field x number
|
||||||
|
---@field y number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeLocaleContext
|
||||||
|
---@field requested string
|
||||||
|
---@field resolved string
|
||||||
|
---@field default string
|
||||||
|
---@field supported string[]
|
||||||
|
---@field languageCode string
|
||||||
|
---@field scriptCode? string
|
||||||
|
---@field countryCode? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeScreenContext
|
||||||
|
---@field width number
|
||||||
|
---@field height number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDesignContext
|
||||||
|
---@field width number
|
||||||
|
---@field height number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeViewportContext
|
||||||
|
---@field x number
|
||||||
|
---@field y number
|
||||||
|
---@field width number
|
||||||
|
---@field height number
|
||||||
|
---@field scaleX number
|
||||||
|
---@field scaleY number
|
||||||
|
---@field scaleMode RuntimeScaleMode|string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeContext
|
||||||
|
---@field screen RuntimeScreenContext
|
||||||
|
---@field design RuntimeDesignContext
|
||||||
|
---@field viewport RuntimeViewportContext
|
||||||
|
---@field seed integer
|
||||||
|
---@field runtimeApiVersion integer
|
||||||
|
---@field gameId string
|
||||||
|
---@field gameVersion string
|
||||||
|
---@field locale? RuntimeLocaleContext
|
||||||
|
|
||||||
|
---@class RuntimeUi
|
||||||
|
---@field style fun(base?: RuntimeNodeProps, opts?: RuntimeNodeProps): RuntimeNodeProps
|
||||||
|
---@field with_parent fun(parent: string, opts?: RuntimeNodeProps): RuntimeNodeProps
|
||||||
|
---@field node fun(node_type: RuntimeNodeType, id: string, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field panel fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field rect fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field circle fun(id: string, x: number|RuntimeNodeInit, y?: number, size?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field line fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field progress fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, value?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field particle fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field text fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field button fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, handler?: string, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field list_view fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field image fun(id: string, asset: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field sprite fun(id: string, asset: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field spine fun(id: string, asset: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, animation?: string, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field update fun(id: string, props: RuntimeNodeInit): RuntimeNodeUpdate
|
||||||
|
---@field text_update fun(id: string, text: string): RuntimeNodeUpdate
|
||||||
|
---@field visible_update fun(id: string, visible: boolean): RuntimeNodeUpdate
|
||||||
|
---@field alpha_update fun(id: string, alpha: number): RuntimeNodeUpdate
|
||||||
|
---@field scale_update fun(id: string, scale: number): RuntimeNodeUpdate
|
||||||
|
---@field position_update fun(id: string, x: number, y: number): RuntimeNodeUpdate
|
||||||
|
---@field size_update fun(id: string, width: number, height: number): RuntimeNodeUpdate
|
||||||
|
---@field transform_update fun(id: string, x: number, y: number, scale: number, rotation: number): RuntimeNodeUpdate
|
||||||
|
---@field batch_update fun(ids: string[], props: RuntimeNodeInit): RuntimeNodeUpdate[]
|
||||||
|
---@field append fun(nodes: RuntimeNode[], node: RuntimeNode): RuntimeNode[]
|
||||||
|
---@field append_all fun(nodes: RuntimeNode[], extra_nodes: RuntimeNode[]): RuntimeNode[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDialogButton
|
||||||
|
---@field id? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler string
|
||||||
|
---@field color? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDialogOpts
|
||||||
|
---@field screenWidth? number
|
||||||
|
---@field screenHeight? number
|
||||||
|
---@field overlay? boolean
|
||||||
|
---@field overlayColor? string
|
||||||
|
---@field blockInput? boolean
|
||||||
|
---@field layer? integer
|
||||||
|
---@field color? string
|
||||||
|
---@field radius? number
|
||||||
|
---@field panelStyle? RuntimeNodeProps
|
||||||
|
---@field titleColor? string
|
||||||
|
---@field titleSize? number
|
||||||
|
---@field titleStyle? RuntimeNodeProps
|
||||||
|
---@field messageColor? string
|
||||||
|
---@field messageSize? number
|
||||||
|
---@field messageStyle? RuntimeNodeProps
|
||||||
|
---@field buttons? RuntimeDialogButton[]
|
||||||
|
---@field buttonGap? number
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeLabeledProgressOpts: RuntimeNodeInit
|
||||||
|
---@field labelHeight? number
|
||||||
|
---@field labelStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimePillOpts: RuntimeNodeInit
|
||||||
|
---@field panelStyle? RuntimeNodeProps
|
||||||
|
---@field textStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeTextButtonOpts: RuntimeNodeInit
|
||||||
|
---@field variant? RuntimeButtonVariant
|
||||||
|
|
||||||
|
---@class RuntimeListItemOpts: RuntimeTextButtonOpts
|
||||||
|
---@field selected? boolean
|
||||||
|
---@field activeColor? string
|
||||||
|
---@field inactiveColor? string
|
||||||
|
|
||||||
|
---@class RuntimeTabItem
|
||||||
|
---@field id? string
|
||||||
|
---@field key? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler? string
|
||||||
|
---@field selected? boolean
|
||||||
|
|
||||||
|
---@class RuntimeTabsOpts: RuntimeNodeInit
|
||||||
|
---@field tabs? RuntimeTabItem[]
|
||||||
|
---@field selected? string
|
||||||
|
---@field gap? number
|
||||||
|
---@field itemWidth? number
|
||||||
|
---@field itemHeight? number
|
||||||
|
---@field activeColor? string
|
||||||
|
---@field inactiveColor? string
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeActionItem
|
||||||
|
---@field id? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler? string
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field color? string
|
||||||
|
---@field style? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeActionRowOpts: RuntimeNodeInit
|
||||||
|
---@field actions? RuntimeActionItem[]
|
||||||
|
---@field gap? number
|
||||||
|
---@field itemWidth? number
|
||||||
|
---@field itemHeight? number
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimePanelHeaderOpts: RuntimeNodeInit
|
||||||
|
---@field eyebrow? string
|
||||||
|
---@field title string
|
||||||
|
---@field summary? string
|
||||||
|
---@field gap? number
|
||||||
|
---@field eyebrowId? string
|
||||||
|
---@field titleId? string
|
||||||
|
---@field summaryId? string
|
||||||
|
---@field eyebrowHeight? number
|
||||||
|
---@field titleHeight? number
|
||||||
|
---@field summaryHeight? number
|
||||||
|
---@field eyebrowStyle? RuntimeNodeProps
|
||||||
|
---@field titleStyle? RuntimeNodeProps
|
||||||
|
---@field summaryStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeWidgetTheme
|
||||||
|
---@field primary? string
|
||||||
|
---@field secondary? string
|
||||||
|
---@field success? string
|
||||||
|
---@field overlay? string
|
||||||
|
---@field surface? string
|
||||||
|
---@field surfaceAlt? string
|
||||||
|
---@field card? string
|
||||||
|
---@field text? string
|
||||||
|
---@field muted? string
|
||||||
|
---@field progress? string
|
||||||
|
---@field transparent? string
|
||||||
|
|
||||||
|
---@class RuntimeWidgets
|
||||||
|
---@field configure fun(tokens?: RuntimeWidgetTheme): RuntimeWidgets
|
||||||
|
---@field label fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field section_title fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field pill fun(id: string, text: string|RuntimePillOpts, x?: number, y?: number, width?: number, height?: number, opts?: RuntimePillOpts): RuntimeNode[]
|
||||||
|
---@field text_button fun(id: string, text: string|RuntimeTextButtonOpts, x?: number, y?: number, width?: number, height?: number, handler?: string, opts?: RuntimeTextButtonOpts): RuntimeNode
|
||||||
|
---@field list_item fun(id: string, text: string|RuntimeListItemOpts, x?: number, y?: number, width?: number, height?: number, handler?: string, opts?: RuntimeListItemOpts): RuntimeNode
|
||||||
|
---@field tabs fun(id: string, tabs: RuntimeTabItem[]|RuntimeTabsOpts, opts?: RuntimeTabsOpts): RuntimeNode[]
|
||||||
|
---@field action_row fun(id: string, actions: RuntimeActionItem[]|RuntimeActionRowOpts, opts?: RuntimeActionRowOpts): RuntimeNode[]
|
||||||
|
---@field panel_header fun(id: string, opts: RuntimePanelHeaderOpts): RuntimeNode[]
|
||||||
|
---@field overlay fun(id: string, width: number|RuntimeNodeInit, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field card fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field progress_bar fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, value?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field labeled_progress fun(id: string, label: string, x: number, y: number, width: number, height: number, value: number, opts?: RuntimeLabeledProgressOpts): RuntimeNode[]
|
||||||
|
---@field button_row fun(parent: string, id: string, buttons: RuntimeDialogButton[], x: number, y: number, width: number, height: number, gap?: number, opts?: RuntimeNodeProps): RuntimeNode[]
|
||||||
|
---@field dialog fun(id: string, title: string, message: string, x: number, y: number, width: number, height: number, opts?: RuntimeDialogOpts): RuntimeNode[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeLayoutItem
|
||||||
|
---@field node RuntimeNode
|
||||||
|
---@field marginLeft? number
|
||||||
|
---@field marginRight? number
|
||||||
|
---@field marginTop? number
|
||||||
|
---@field marginBottom? number
|
||||||
|
|
||||||
|
---@class RuntimeLayoutItemOpts
|
||||||
|
---@field margin? number
|
||||||
|
---@field mx? number
|
||||||
|
---@field my? number
|
||||||
|
---@field ml? number
|
||||||
|
---@field mr? number
|
||||||
|
---@field mt? number
|
||||||
|
---@field mb? number
|
||||||
|
---@field marginLeft? number
|
||||||
|
---@field marginRight? number
|
||||||
|
---@field marginTop? number
|
||||||
|
---@field marginBottom? number
|
||||||
|
|
||||||
|
---@class RuntimeLinearLayoutOpts
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field gap? number
|
||||||
|
---@field align? RuntimeLayoutAlign
|
||||||
|
---@field padding? number
|
||||||
|
---@field paddingX? number
|
||||||
|
---@field paddingY? number
|
||||||
|
---@field px? number
|
||||||
|
---@field py? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
|
||||||
|
---@class RuntimeBoxLayoutOpts: RuntimeLinearLayoutOpts
|
||||||
|
---@field rows? integer
|
||||||
|
---@field columns? integer
|
||||||
|
---@field cols? integer
|
||||||
|
---@field cellWidth? number
|
||||||
|
---@field cellHeight? number
|
||||||
|
---@field cellW? number
|
||||||
|
---@field cellH? number
|
||||||
|
---@field gapX? number
|
||||||
|
---@field gapY? number
|
||||||
|
---@field valign? RuntimeLayoutAlign
|
||||||
|
|
||||||
|
---@class RuntimeLayout
|
||||||
|
---@field item fun(node: RuntimeNode, opts?: RuntimeLayoutItemOpts): RuntimeLayoutItem
|
||||||
|
---@field local_position fun(origin: RuntimePoint, position: RuntimePoint): RuntimePoint
|
||||||
|
---@field row fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeLinearLayoutOpts): RuntimeNode[]
|
||||||
|
---@field column fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeLinearLayoutOpts): RuntimeNode[]
|
||||||
|
---@field stack fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeLinearLayoutOpts): RuntimeNode[]
|
||||||
|
---@field box fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeBoxLayoutOpts): RuntimeNode[]
|
||||||
|
|
||||||
|
---@class RuntimeCommands
|
||||||
|
---@field toast fun(text: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field copy_text fun(text: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field delay fun(duration: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field sequence fun(items: RuntimeCommand[], opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field parallel fun(items: RuntimeCommand[], opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field move_path fun(target: string, path: RuntimePoint[], opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field move_to fun(target: string, x: number, y: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field fade_to fun(target: string, alpha: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field scale_to fun(target: string, scale: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field rotate_to fun(target: string, angle: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field remove_node fun(target: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field play_spine_animation fun(target: string, animation: string, opts?: RuntimeSpineCommandOpts): RuntimeCommand
|
||||||
|
---@field play_sound fun(asset: string, opts?: RuntimeAudioCommandOpts): RuntimeCommand
|
||||||
|
---@field play_bgm fun(asset: string, opts?: RuntimeBgmCommandOpts): RuntimeCommand
|
||||||
|
---@field pause_bgm fun(channel?: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field resume_bgm fun(channel?: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field stop_bgm fun(channel?: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field preload_group fun(group: string, opts?: RuntimeResourceCommandOpts): RuntimeCommand
|
||||||
|
---@field evict_group fun(group: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field cancel_id fun(id: string): RuntimeCommand
|
||||||
|
---@field cancel_group fun(group: string): RuntimeCommand
|
||||||
|
---@field cancel_scope fun(scope: string): RuntimeCommand
|
||||||
|
|
||||||
|
---@class RuntimeImportApi
|
||||||
|
---@field import fun(moduleName: string): table
|
||||||
|
|
||||||
|
---@type RuntimeImportApi
|
||||||
|
runtime = runtime
|
||||||
|
|
||||||
|
---@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 (exact) ShowcaseState
|
||||||
|
---@field selected_example string
|
||||||
|
---@field detail_tab string
|
||||||
|
---@field progress number
|
||||||
|
---@field visible boolean
|
||||||
|
---@field dialog_open boolean
|
||||||
|
---@field temp_node_visible boolean
|
||||||
|
---@field bgm_state string
|
||||||
|
---@field resource_state string
|
||||||
|
---@field text_variant string
|
||||||
|
---@field button_active boolean
|
||||||
|
---@field sprite_variant string
|
||||||
|
---@field layout_mode string
|
||||||
|
---@field radio_selected string
|
||||||
|
---@field list_selected string
|
||||||
|
---@field list_axis string
|
||||||
|
---@field list_scroll_x number
|
||||||
|
---@field list_scroll_y number
|
||||||
|
---@field locale string
|
||||||
|
---@field screen_width number
|
||||||
|
---@field screen_height number
|
||||||
|
---@field viewport_width number
|
||||||
|
---@field viewport_height number
|
||||||
|
---@field viewport_scale number
|
||||||
|
---@field responsive_mode string
|
||||||
|
---@field status string
|
||||||
32
example/assets/games/showcase/scripts/state.lua
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---@class ShowcaseState
|
||||||
|
local state = {
|
||||||
|
selected_example = "nodes",
|
||||||
|
detail_tab = "code",
|
||||||
|
progress = 0.35,
|
||||||
|
visible = true,
|
||||||
|
dialog_open = false,
|
||||||
|
temp_node_visible = false,
|
||||||
|
bgm_state = "stopped",
|
||||||
|
resource_state = "ready",
|
||||||
|
text_variant = "plain",
|
||||||
|
button_active = true,
|
||||||
|
button_image_enabled = true,
|
||||||
|
sprite_variant = "image",
|
||||||
|
layout_mode = "row",
|
||||||
|
radio_selected = "audio",
|
||||||
|
list_selected = "Lua",
|
||||||
|
list_axis = "vertical",
|
||||||
|
list_scroll_x = 0,
|
||||||
|
list_scroll_y = 0,
|
||||||
|
locale = "zh-Hans",
|
||||||
|
screen_width = 720,
|
||||||
|
screen_height = 720,
|
||||||
|
viewport_width = 720,
|
||||||
|
viewport_height = 720,
|
||||||
|
viewport_scale = 1,
|
||||||
|
responsive_mode = "desktop",
|
||||||
|
particle_seed = 0,
|
||||||
|
status = "点击按钮查看 RuntimeEvent -> Lua -> Diff / Command 示例"
|
||||||
|
}
|
||||||
|
|
||||||
|
return state
|
||||||
44
example/assets/games/showcase/scripts/styles.lua
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
local theme = runtime.import("theme")
|
||||||
|
|
||||||
|
---@class ShowcaseStyles
|
||||||
|
local styles = {}
|
||||||
|
|
||||||
|
styles.title = {
|
||||||
|
color = theme.text,
|
||||||
|
fontSize = 24,
|
||||||
|
layer = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.label = {
|
||||||
|
color = theme.muted,
|
||||||
|
fontSize = 14,
|
||||||
|
layer = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.value = {
|
||||||
|
color = theme.text,
|
||||||
|
fontSize = 16,
|
||||||
|
layer = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.button = {
|
||||||
|
color = "#ff2563eb",
|
||||||
|
radius = 10,
|
||||||
|
fontSize = 14,
|
||||||
|
layer = 30
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.small_button = {
|
||||||
|
color = "#ff334155",
|
||||||
|
radius = 8,
|
||||||
|
fontSize = 12,
|
||||||
|
layer = 30
|
||||||
|
}
|
||||||
|
|
||||||
|
styles.card = {
|
||||||
|
color = theme.card,
|
||||||
|
radius = 14,
|
||||||
|
layer = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
return styles
|
||||||
18
example/assets/games/showcase/scripts/theme.lua
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---@class ShowcaseTheme
|
||||||
|
local theme = {
|
||||||
|
screen_width = 720,
|
||||||
|
screen_height = 720,
|
||||||
|
background = "#ff0f172a",
|
||||||
|
panel = "#ff111827",
|
||||||
|
card = "#ff1f2937",
|
||||||
|
border = "#ff334155",
|
||||||
|
text = "#fff8fafc",
|
||||||
|
muted = "#ff94a3b8",
|
||||||
|
primary = "#ff38bdf8",
|
||||||
|
success = "#ff22c55e",
|
||||||
|
warning = "#fff59e0b",
|
||||||
|
danger = "#ffef4444",
|
||||||
|
purple = "#ffa855f7"
|
||||||
|
}
|
||||||
|
|
||||||
|
return theme
|
||||||
1300
example/assets/games/showcase/scripts/ui.lua
Normal file
27
example/assets/games/template/manifest.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"gameId": "template",
|
||||||
|
"name": "Lua Runtime Template",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"runtimeApiVersion": 1,
|
||||||
|
"entry": "scripts/main.lua",
|
||||||
|
"assetsBase": "assets",
|
||||||
|
"defaultLocale": "zh-Hans",
|
||||||
|
"supportedLocales": [
|
||||||
|
"zh-Hans",
|
||||||
|
"en"
|
||||||
|
],
|
||||||
|
"display": {
|
||||||
|
"designWidth": 720,
|
||||||
|
"designHeight": 720,
|
||||||
|
"scaleMode": "fit"
|
||||||
|
},
|
||||||
|
"modules": {
|
||||||
|
"runtime_ui": "runtime:runtime_ui.lua",
|
||||||
|
"runtime_commands": "runtime:runtime_commands.lua",
|
||||||
|
"runtime_widgets": "runtime:runtime_widgets.lua",
|
||||||
|
"layout": "runtime:layout.lua",
|
||||||
|
"state": "scripts/state.lua",
|
||||||
|
"ui": "scripts/ui.lua"
|
||||||
|
},
|
||||||
|
"resources": {}
|
||||||
|
}
|
||||||
35
example/assets/games/template/scripts/main.lua
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
local state = runtime.import("state")
|
||||||
|
local ui = runtime.import("ui")
|
||||||
|
---@type RuntimeCommands
|
||||||
|
local commands = runtime.import("runtime_commands")
|
||||||
|
|
||||||
|
function smoke_test(ctx)
|
||||||
|
return ctx ~= nil
|
||||||
|
and ctx.runtimeApiVersion ~= nil
|
||||||
|
and ui.create_nodes ~= nil
|
||||||
|
and commands.toast ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function init(ctx)
|
||||||
|
state.status = "Template ready: " .. tostring(ctx.gameId or "template")
|
||||||
|
return {
|
||||||
|
render = { creates = ui.create_nodes() },
|
||||||
|
ui = {},
|
||||||
|
commands = {}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_event(event)
|
||||||
|
if event.handler == "template_start" then
|
||||||
|
state.started = true
|
||||||
|
state.clicks = state.clicks + 1
|
||||||
|
state.status = "收到 tap 事件,Lua 已返回 Diff 和 toast command。"
|
||||||
|
return {
|
||||||
|
ui = { updates = ui.state_updates() },
|
||||||
|
commands = {
|
||||||
|
commands.toast("Template event handled in Lua", { duration = 1.2 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return {}
|
||||||
|
end
|
||||||
557
example/assets/games/template/scripts/runtime_defs.lua
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
---@meta
|
||||||
|
--- COMMON RUNTIME TYPES SECTION.
|
||||||
|
--- Source of truth: tool/lua_runtime_defs_common.lua
|
||||||
|
--- After editing this common section, run:
|
||||||
|
--- dart run tool/generate_lua_runtime_defs.dart
|
||||||
|
|
||||||
|
|
||||||
|
---@alias RuntimeNodeType
|
||||||
|
---| 'panel'
|
||||||
|
---| 'button'
|
||||||
|
---| 'text'
|
||||||
|
---| 'circle'
|
||||||
|
---| 'rect'
|
||||||
|
---| 'line'
|
||||||
|
---| 'progress'
|
||||||
|
---| 'listView'
|
||||||
|
---| 'sprite'
|
||||||
|
---| 'image'
|
||||||
|
---| 'spine'
|
||||||
|
---| 'particle'
|
||||||
|
|
||||||
|
---@alias RuntimeAnchor
|
||||||
|
---| 'center'
|
||||||
|
---| 'topLeft'
|
||||||
|
---| 'topRight'
|
||||||
|
---| 'bottomLeft'
|
||||||
|
---| 'bottomRight'
|
||||||
|
|
||||||
|
---@alias RuntimeTextAlign
|
||||||
|
---| 'left'
|
||||||
|
---| 'center'
|
||||||
|
---| 'right'
|
||||||
|
|
||||||
|
---@alias RuntimeParticlePreset
|
||||||
|
---| 'burst'
|
||||||
|
---| 'trail'
|
||||||
|
---| 'snow'
|
||||||
|
---| 'confetti'
|
||||||
|
|
||||||
|
---@alias RuntimeCommandType
|
||||||
|
---| 'move_path'
|
||||||
|
---| 'move_to'
|
||||||
|
---| 'fade_to'
|
||||||
|
---| 'scale_to'
|
||||||
|
---| 'rotate_to'
|
||||||
|
---| 'remove_node'
|
||||||
|
---| 'sequence'
|
||||||
|
---| 'parallel'
|
||||||
|
---| 'delay'
|
||||||
|
---| 'toast'
|
||||||
|
---| 'play_sound'
|
||||||
|
---| 'play_bgm'
|
||||||
|
---| 'pause_bgm'
|
||||||
|
---| 'resume_bgm'
|
||||||
|
---| 'stop_bgm'
|
||||||
|
---| 'preload_resources'
|
||||||
|
---| 'evict_resources'
|
||||||
|
---| 'cancel_commands'
|
||||||
|
---| 'play_spine_animation'
|
||||||
|
---| 'copy_text'
|
||||||
|
|
||||||
|
---@alias RuntimeEventType
|
||||||
|
---| 'tap'
|
||||||
|
---| 'animation_done'
|
||||||
|
---| 'resize'
|
||||||
|
---| 'scroll'
|
||||||
|
|
||||||
|
---@alias RuntimeScaleMode
|
||||||
|
---| 'fit'
|
||||||
|
---| 'fill'
|
||||||
|
---| 'stretch'
|
||||||
|
---| 'none'
|
||||||
|
|
||||||
|
---@alias RuntimeLayoutAlign
|
||||||
|
---| 'start'
|
||||||
|
---| 'center'
|
||||||
|
---| 'end'
|
||||||
|
|
||||||
|
---@alias RuntimeButtonVariant
|
||||||
|
---| 'primary'
|
||||||
|
---| 'secondary'
|
||||||
|
---| 'ghost'
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNode
|
||||||
|
---@field id string
|
||||||
|
---@field type RuntimeNodeType
|
||||||
|
---@field parent? string
|
||||||
|
---@field asset? string Normal image/sprite/spine asset key. For button nodes this is the normal-state image.
|
||||||
|
---@field pressedAsset? string Button pressed-state image asset key.
|
||||||
|
---@field disabledAsset? string Button disabled-state image asset key.
|
||||||
|
---@field animation? string
|
||||||
|
---@field skin? string
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field text? string
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
---@field paddingRight? number
|
||||||
|
---@field paddingBottom? number
|
||||||
|
---@field anchor? RuntimeAnchor
|
||||||
|
---@field layer? integer
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field alpha? number
|
||||||
|
---@field scale? number
|
||||||
|
---@field rotation? number
|
||||||
|
---@field color? string
|
||||||
|
---@field fontSize? number
|
||||||
|
---@field textAlign? RuntimeTextAlign
|
||||||
|
---@field radius? number
|
||||||
|
---@field strokeWidth? number
|
||||||
|
---@field value? number
|
||||||
|
---@field scrollX? number
|
||||||
|
---@field scrollY? number
|
||||||
|
---@field contentWidth? number
|
||||||
|
---@field contentHeight? number
|
||||||
|
---@field virtualized? boolean
|
||||||
|
---@field cacheExtent? number
|
||||||
|
---@field inertia? boolean
|
||||||
|
---@field scrollbarThumbColor? string
|
||||||
|
---@field scrollbarTrackColor? string
|
||||||
|
---@field scrollbarThickness? number
|
||||||
|
---@field scrollbarVisible? boolean
|
||||||
|
---@field interactive? boolean
|
||||||
|
---@field onTap? string
|
||||||
|
---@field onScroll? string
|
||||||
|
---@field preset? RuntimeParticlePreset
|
||||||
|
---@field count? integer
|
||||||
|
---@field duration? number
|
||||||
|
---@field speedMin? number
|
||||||
|
---@field speedMax? number
|
||||||
|
---@field gravityX? number
|
||||||
|
---@field gravityY? number
|
||||||
|
---@field spread? number
|
||||||
|
---@field colorTo? string
|
||||||
|
---@field radiusTo? number
|
||||||
|
---@field autoRemove? boolean
|
||||||
|
---@field fadeOut? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNodeProps
|
||||||
|
---@field type? RuntimeNodeType
|
||||||
|
---@field parent? string
|
||||||
|
---@field asset? string Normal image/sprite/spine asset key. For button nodes this is the normal-state image.
|
||||||
|
---@field pressedAsset? string Button pressed-state image asset key.
|
||||||
|
---@field disabledAsset? string Button disabled-state image asset key.
|
||||||
|
---@field animation? string
|
||||||
|
---@field skin? string
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field text? string
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
---@field paddingRight? number
|
||||||
|
---@field paddingBottom? number
|
||||||
|
---@field anchor? RuntimeAnchor
|
||||||
|
---@field layer? integer
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field alpha? number
|
||||||
|
---@field scale? number
|
||||||
|
---@field rotation? number
|
||||||
|
---@field color? string
|
||||||
|
---@field fontSize? number
|
||||||
|
---@field textAlign? RuntimeTextAlign
|
||||||
|
---@field radius? number
|
||||||
|
---@field strokeWidth? number
|
||||||
|
---@field value? number
|
||||||
|
---@field scrollX? number
|
||||||
|
---@field scrollY? number
|
||||||
|
---@field contentWidth? number
|
||||||
|
---@field contentHeight? number
|
||||||
|
---@field virtualized? boolean
|
||||||
|
---@field cacheExtent? number
|
||||||
|
---@field inertia? boolean
|
||||||
|
---@field scrollbarThumbColor? string
|
||||||
|
---@field scrollbarTrackColor? string
|
||||||
|
---@field scrollbarThickness? number
|
||||||
|
---@field scrollbarVisible? boolean
|
||||||
|
---@field interactive? boolean
|
||||||
|
---@field onTap? string
|
||||||
|
---@field onScroll? string
|
||||||
|
---@field preset? RuntimeParticlePreset
|
||||||
|
---@field count? integer
|
||||||
|
---@field duration? number
|
||||||
|
---@field speedMin? number
|
||||||
|
---@field speedMax? number
|
||||||
|
---@field gravityX? number
|
||||||
|
---@field gravityY? number
|
||||||
|
---@field spread? number
|
||||||
|
---@field colorTo? string
|
||||||
|
---@field radiusTo? number
|
||||||
|
---@field autoRemove? boolean
|
||||||
|
---@field fadeOut? boolean
|
||||||
|
|
||||||
|
---Helper-only fields accepted by runtime_ui/runtime_widgets. They are normalized
|
||||||
|
---before the node/update crosses the Dart Runtime protocol boundary.
|
||||||
|
---@class RuntimeNodeInit: RuntimeNodeProps
|
||||||
|
---@field w? number Alias for width.
|
||||||
|
---@field h? number Alias for height.
|
||||||
|
---@field size? number Alias for both width and height.
|
||||||
|
---@field handler? string Alias for onTap.
|
||||||
|
---@field onClick? string Alias for onTap.
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNodeUpdate
|
||||||
|
---@field id string
|
||||||
|
---@field props RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class (exact) RuntimeNodeRemove
|
||||||
|
---@field id string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDiffSection
|
||||||
|
---@field creates? RuntimeNode[]
|
||||||
|
---@field updates? RuntimeNodeUpdate[]
|
||||||
|
---@field removes? (string|RuntimeNodeRemove)[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDiff
|
||||||
|
---@field render? RuntimeDiffSection
|
||||||
|
---@field ui? RuntimeDiffSection
|
||||||
|
---@field commands? RuntimeCommand[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeEvent
|
||||||
|
---@field type RuntimeEventType|string
|
||||||
|
---@field target? string
|
||||||
|
---@field handler? string
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field data? table
|
||||||
|
|
||||||
|
---@class (exact) RuntimeCommand
|
||||||
|
---@field type RuntimeCommandType
|
||||||
|
---@field target? string
|
||||||
|
---@field scope? string
|
||||||
|
---@field id? string
|
||||||
|
---@field group? string
|
||||||
|
---@field commandGroup? string
|
||||||
|
---@field onComplete? string
|
||||||
|
---@field duration? number
|
||||||
|
---@field commands? RuntimeCommand[]
|
||||||
|
---@field path? RuntimePoint[]
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field alpha? number
|
||||||
|
---@field scale? number
|
||||||
|
---@field angle? number
|
||||||
|
---@field text? string
|
||||||
|
---@field message? string
|
||||||
|
---@field asset? string
|
||||||
|
---@field name? string
|
||||||
|
---@field volume? number
|
||||||
|
---@field channel? string
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field failOnError? boolean
|
||||||
|
---@field animation? string
|
||||||
|
---@field track? integer
|
||||||
|
---@field queue? boolean
|
||||||
|
---@field delay? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeCommandOpts
|
||||||
|
---@field id? string
|
||||||
|
---@field group? string
|
||||||
|
---@field commandGroup? string
|
||||||
|
---@field scope? string
|
||||||
|
---@field onComplete? string
|
||||||
|
---@field duration? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeAudioCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field volume? number
|
||||||
|
---@field name? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeBgmCommandOpts: RuntimeAudioCommandOpts
|
||||||
|
---@field channel? string
|
||||||
|
---@field loop? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimeSpineCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field track? integer
|
||||||
|
---@field loop? boolean
|
||||||
|
---@field queue? boolean
|
||||||
|
---@field delay? number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeResourceCommandOpts: RuntimeCommandOpts
|
||||||
|
---@field failOnError? boolean
|
||||||
|
|
||||||
|
---@class (exact) RuntimePoint
|
||||||
|
---@field x number
|
||||||
|
---@field y number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeLocaleContext
|
||||||
|
---@field requested string
|
||||||
|
---@field resolved string
|
||||||
|
---@field default string
|
||||||
|
---@field supported string[]
|
||||||
|
---@field languageCode string
|
||||||
|
---@field scriptCode? string
|
||||||
|
---@field countryCode? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeScreenContext
|
||||||
|
---@field width number
|
||||||
|
---@field height number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDesignContext
|
||||||
|
---@field width number
|
||||||
|
---@field height number
|
||||||
|
|
||||||
|
---@class (exact) RuntimeViewportContext
|
||||||
|
---@field x number
|
||||||
|
---@field y number
|
||||||
|
---@field width number
|
||||||
|
---@field height number
|
||||||
|
---@field scaleX number
|
||||||
|
---@field scaleY number
|
||||||
|
---@field scaleMode RuntimeScaleMode|string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeContext
|
||||||
|
---@field screen RuntimeScreenContext
|
||||||
|
---@field design RuntimeDesignContext
|
||||||
|
---@field viewport RuntimeViewportContext
|
||||||
|
---@field seed integer
|
||||||
|
---@field runtimeApiVersion integer
|
||||||
|
---@field gameId string
|
||||||
|
---@field gameVersion string
|
||||||
|
---@field locale? RuntimeLocaleContext
|
||||||
|
|
||||||
|
---@class RuntimeUi
|
||||||
|
---@field style fun(base?: RuntimeNodeProps, opts?: RuntimeNodeProps): RuntimeNodeProps
|
||||||
|
---@field with_parent fun(parent: string, opts?: RuntimeNodeProps): RuntimeNodeProps
|
||||||
|
---@field node fun(node_type: RuntimeNodeType, id: string, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field panel fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field rect fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field circle fun(id: string, x: number|RuntimeNodeInit, y?: number, size?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field line fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field progress fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, value?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field particle fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field text fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field button fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, handler?: string, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field list_view fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field image fun(id: string, asset: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field sprite fun(id: string, asset: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field spine fun(id: string, asset: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, animation?: string, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field update fun(id: string, props: RuntimeNodeInit): RuntimeNodeUpdate
|
||||||
|
---@field text_update fun(id: string, text: string): RuntimeNodeUpdate
|
||||||
|
---@field visible_update fun(id: string, visible: boolean): RuntimeNodeUpdate
|
||||||
|
---@field alpha_update fun(id: string, alpha: number): RuntimeNodeUpdate
|
||||||
|
---@field scale_update fun(id: string, scale: number): RuntimeNodeUpdate
|
||||||
|
---@field position_update fun(id: string, x: number, y: number): RuntimeNodeUpdate
|
||||||
|
---@field size_update fun(id: string, width: number, height: number): RuntimeNodeUpdate
|
||||||
|
---@field transform_update fun(id: string, x: number, y: number, scale: number, rotation: number): RuntimeNodeUpdate
|
||||||
|
---@field batch_update fun(ids: string[], props: RuntimeNodeInit): RuntimeNodeUpdate[]
|
||||||
|
---@field append fun(nodes: RuntimeNode[], node: RuntimeNode): RuntimeNode[]
|
||||||
|
---@field append_all fun(nodes: RuntimeNode[], extra_nodes: RuntimeNode[]): RuntimeNode[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDialogButton
|
||||||
|
---@field id? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler string
|
||||||
|
---@field color? string
|
||||||
|
|
||||||
|
---@class (exact) RuntimeDialogOpts
|
||||||
|
---@field screenWidth? number
|
||||||
|
---@field screenHeight? number
|
||||||
|
---@field overlay? boolean
|
||||||
|
---@field overlayColor? string
|
||||||
|
---@field blockInput? boolean
|
||||||
|
---@field layer? integer
|
||||||
|
---@field color? string
|
||||||
|
---@field radius? number
|
||||||
|
---@field panelStyle? RuntimeNodeProps
|
||||||
|
---@field titleColor? string
|
||||||
|
---@field titleSize? number
|
||||||
|
---@field titleStyle? RuntimeNodeProps
|
||||||
|
---@field messageColor? string
|
||||||
|
---@field messageSize? number
|
||||||
|
---@field messageStyle? RuntimeNodeProps
|
||||||
|
---@field buttons? RuntimeDialogButton[]
|
||||||
|
---@field buttonGap? number
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeLabeledProgressOpts: RuntimeNodeInit
|
||||||
|
---@field labelHeight? number
|
||||||
|
---@field labelStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimePillOpts: RuntimeNodeInit
|
||||||
|
---@field panelStyle? RuntimeNodeProps
|
||||||
|
---@field textStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeTextButtonOpts: RuntimeNodeInit
|
||||||
|
---@field variant? RuntimeButtonVariant
|
||||||
|
|
||||||
|
---@class RuntimeListItemOpts: RuntimeTextButtonOpts
|
||||||
|
---@field selected? boolean
|
||||||
|
---@field activeColor? string
|
||||||
|
---@field inactiveColor? string
|
||||||
|
|
||||||
|
---@class RuntimeTabItem
|
||||||
|
---@field id? string
|
||||||
|
---@field key? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler? string
|
||||||
|
---@field selected? boolean
|
||||||
|
|
||||||
|
---@class RuntimeTabsOpts: RuntimeNodeInit
|
||||||
|
---@field tabs? RuntimeTabItem[]
|
||||||
|
---@field selected? string
|
||||||
|
---@field gap? number
|
||||||
|
---@field itemWidth? number
|
||||||
|
---@field itemHeight? number
|
||||||
|
---@field activeColor? string
|
||||||
|
---@field inactiveColor? string
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeActionItem
|
||||||
|
---@field id? string
|
||||||
|
---@field text string
|
||||||
|
---@field handler? string
|
||||||
|
---@field visible? boolean
|
||||||
|
---@field color? string
|
||||||
|
---@field style? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeActionRowOpts: RuntimeNodeInit
|
||||||
|
---@field actions? RuntimeActionItem[]
|
||||||
|
---@field gap? number
|
||||||
|
---@field itemWidth? number
|
||||||
|
---@field itemHeight? number
|
||||||
|
---@field buttonStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimePanelHeaderOpts: RuntimeNodeInit
|
||||||
|
---@field eyebrow? string
|
||||||
|
---@field title string
|
||||||
|
---@field summary? string
|
||||||
|
---@field gap? number
|
||||||
|
---@field eyebrowId? string
|
||||||
|
---@field titleId? string
|
||||||
|
---@field summaryId? string
|
||||||
|
---@field eyebrowHeight? number
|
||||||
|
---@field titleHeight? number
|
||||||
|
---@field summaryHeight? number
|
||||||
|
---@field eyebrowStyle? RuntimeNodeProps
|
||||||
|
---@field titleStyle? RuntimeNodeProps
|
||||||
|
---@field summaryStyle? RuntimeNodeProps
|
||||||
|
|
||||||
|
---@class RuntimeWidgetTheme
|
||||||
|
---@field primary? string
|
||||||
|
---@field secondary? string
|
||||||
|
---@field success? string
|
||||||
|
---@field overlay? string
|
||||||
|
---@field surface? string
|
||||||
|
---@field surfaceAlt? string
|
||||||
|
---@field card? string
|
||||||
|
---@field text? string
|
||||||
|
---@field muted? string
|
||||||
|
---@field progress? string
|
||||||
|
---@field transparent? string
|
||||||
|
|
||||||
|
---@class RuntimeWidgets
|
||||||
|
---@field configure fun(tokens?: RuntimeWidgetTheme): RuntimeWidgets
|
||||||
|
---@field label fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field section_title fun(id: string, text: string|RuntimeNodeInit, x?: number, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field pill fun(id: string, text: string|RuntimePillOpts, x?: number, y?: number, width?: number, height?: number, opts?: RuntimePillOpts): RuntimeNode[]
|
||||||
|
---@field text_button fun(id: string, text: string|RuntimeTextButtonOpts, x?: number, y?: number, width?: number, height?: number, handler?: string, opts?: RuntimeTextButtonOpts): RuntimeNode
|
||||||
|
---@field list_item fun(id: string, text: string|RuntimeListItemOpts, x?: number, y?: number, width?: number, height?: number, handler?: string, opts?: RuntimeListItemOpts): RuntimeNode
|
||||||
|
---@field tabs fun(id: string, tabs: RuntimeTabItem[]|RuntimeTabsOpts, opts?: RuntimeTabsOpts): RuntimeNode[]
|
||||||
|
---@field action_row fun(id: string, actions: RuntimeActionItem[]|RuntimeActionRowOpts, opts?: RuntimeActionRowOpts): RuntimeNode[]
|
||||||
|
---@field panel_header fun(id: string, opts: RuntimePanelHeaderOpts): RuntimeNode[]
|
||||||
|
---@field overlay fun(id: string, width: number|RuntimeNodeInit, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field card fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field progress_bar fun(id: string, x: number|RuntimeNodeInit, y?: number, width?: number, height?: number, value?: number, opts?: RuntimeNodeInit): RuntimeNode
|
||||||
|
---@field labeled_progress fun(id: string, label: string, x: number, y: number, width: number, height: number, value: number, opts?: RuntimeLabeledProgressOpts): RuntimeNode[]
|
||||||
|
---@field button_row fun(parent: string, id: string, buttons: RuntimeDialogButton[], x: number, y: number, width: number, height: number, gap?: number, opts?: RuntimeNodeProps): RuntimeNode[]
|
||||||
|
---@field dialog fun(id: string, title: string, message: string, x: number, y: number, width: number, height: number, opts?: RuntimeDialogOpts): RuntimeNode[]
|
||||||
|
|
||||||
|
---@class (exact) RuntimeLayoutItem
|
||||||
|
---@field node RuntimeNode
|
||||||
|
---@field marginLeft? number
|
||||||
|
---@field marginRight? number
|
||||||
|
---@field marginTop? number
|
||||||
|
---@field marginBottom? number
|
||||||
|
|
||||||
|
---@class RuntimeLayoutItemOpts
|
||||||
|
---@field margin? number
|
||||||
|
---@field mx? number
|
||||||
|
---@field my? number
|
||||||
|
---@field ml? number
|
||||||
|
---@field mr? number
|
||||||
|
---@field mt? number
|
||||||
|
---@field mb? number
|
||||||
|
---@field marginLeft? number
|
||||||
|
---@field marginRight? number
|
||||||
|
---@field marginTop? number
|
||||||
|
---@field marginBottom? number
|
||||||
|
|
||||||
|
---@class RuntimeLinearLayoutOpts
|
||||||
|
---@field x? number
|
||||||
|
---@field y? number
|
||||||
|
---@field width? number
|
||||||
|
---@field height? number
|
||||||
|
---@field gap? number
|
||||||
|
---@field align? RuntimeLayoutAlign
|
||||||
|
---@field padding? number
|
||||||
|
---@field paddingX? number
|
||||||
|
---@field paddingY? number
|
||||||
|
---@field px? number
|
||||||
|
---@field py? number
|
||||||
|
---@field paddingLeft? number
|
||||||
|
---@field paddingTop? number
|
||||||
|
|
||||||
|
---@class RuntimeBoxLayoutOpts: RuntimeLinearLayoutOpts
|
||||||
|
---@field rows? integer
|
||||||
|
---@field columns? integer
|
||||||
|
---@field cols? integer
|
||||||
|
---@field cellWidth? number
|
||||||
|
---@field cellHeight? number
|
||||||
|
---@field cellW? number
|
||||||
|
---@field cellH? number
|
||||||
|
---@field gapX? number
|
||||||
|
---@field gapY? number
|
||||||
|
---@field valign? RuntimeLayoutAlign
|
||||||
|
|
||||||
|
---@class RuntimeLayout
|
||||||
|
---@field item fun(node: RuntimeNode, opts?: RuntimeLayoutItemOpts): RuntimeLayoutItem
|
||||||
|
---@field local_position fun(origin: RuntimePoint, position: RuntimePoint): RuntimePoint
|
||||||
|
---@field row fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeLinearLayoutOpts): RuntimeNode[]
|
||||||
|
---@field column fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeLinearLayoutOpts): RuntimeNode[]
|
||||||
|
---@field stack fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeLinearLayoutOpts): RuntimeNode[]
|
||||||
|
---@field box fun(parent?: string, items: (RuntimeNode|RuntimeLayoutItem)[], opts?: RuntimeBoxLayoutOpts): RuntimeNode[]
|
||||||
|
|
||||||
|
---@class RuntimeCommands
|
||||||
|
---@field toast fun(text: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field copy_text fun(text: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field delay fun(duration: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field sequence fun(items: RuntimeCommand[], opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field parallel fun(items: RuntimeCommand[], opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field move_path fun(target: string, path: RuntimePoint[], opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field move_to fun(target: string, x: number, y: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field fade_to fun(target: string, alpha: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field scale_to fun(target: string, scale: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field rotate_to fun(target: string, angle: number, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field remove_node fun(target: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field play_spine_animation fun(target: string, animation: string, opts?: RuntimeSpineCommandOpts): RuntimeCommand
|
||||||
|
---@field play_sound fun(asset: string, opts?: RuntimeAudioCommandOpts): RuntimeCommand
|
||||||
|
---@field play_bgm fun(asset: string, opts?: RuntimeBgmCommandOpts): RuntimeCommand
|
||||||
|
---@field pause_bgm fun(channel?: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field resume_bgm fun(channel?: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field stop_bgm fun(channel?: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field preload_group fun(group: string, opts?: RuntimeResourceCommandOpts): RuntimeCommand
|
||||||
|
---@field evict_group fun(group: string, opts?: RuntimeCommandOpts): RuntimeCommand
|
||||||
|
---@field cancel_id fun(id: string): RuntimeCommand
|
||||||
|
---@field cancel_group fun(group: string): RuntimeCommand
|
||||||
|
---@field cancel_scope fun(scope: string): RuntimeCommand
|
||||||
|
|
||||||
|
---@class RuntimeImportApi
|
||||||
|
---@field import fun(moduleName: string): table
|
||||||
|
|
||||||
|
---@type RuntimeImportApi
|
||||||
|
runtime = runtime
|
||||||
8
example/assets/games/template/scripts/state.lua
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---@class TemplateState
|
||||||
|
local state = {
|
||||||
|
started = false,
|
||||||
|
clicks = 0,
|
||||||
|
status = "点击 Start 验证 RuntimeEvent -> Lua -> Diff / Command"
|
||||||
|
}
|
||||||
|
|
||||||
|
return state
|
||||||
100
example/assets/games/template/scripts/ui.lua
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
---@type RuntimeUi
|
||||||
|
local runtime_ui = runtime.import("runtime_ui")
|
||||||
|
---@type RuntimeWidgets
|
||||||
|
local widgets = runtime.import("runtime_widgets")
|
||||||
|
local state = runtime.import("state")
|
||||||
|
|
||||||
|
widgets.configure({
|
||||||
|
primary = "#ff2563eb",
|
||||||
|
secondary = "#ff475569",
|
||||||
|
success = "#ff22c55e",
|
||||||
|
overlay = "#99000000",
|
||||||
|
surface = "#ff1e293b",
|
||||||
|
surfaceAlt = "#ff334155",
|
||||||
|
card = "#ee111827",
|
||||||
|
text = "#ffe2e8f0",
|
||||||
|
muted = "#ff94a3b8",
|
||||||
|
progress = "#ff22c55e",
|
||||||
|
transparent = "#00000000"
|
||||||
|
})
|
||||||
|
|
||||||
|
---@class TemplateUi
|
||||||
|
local ui = {}
|
||||||
|
|
||||||
|
---@return RuntimeNode[]
|
||||||
|
function ui.create_nodes()
|
||||||
|
return {
|
||||||
|
runtime_ui.rect("template_bg", {
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
w = 720,
|
||||||
|
h = 720,
|
||||||
|
color = "#ff0f172a"
|
||||||
|
}),
|
||||||
|
runtime_ui.panel("template_card", {
|
||||||
|
x = 72,
|
||||||
|
y = 96,
|
||||||
|
w = 576,
|
||||||
|
h = 360,
|
||||||
|
color = "#ee111827",
|
||||||
|
radius = 18,
|
||||||
|
layer = 5
|
||||||
|
}),
|
||||||
|
widgets.section_title("template_title", {
|
||||||
|
text = "Lua Runtime Template",
|
||||||
|
x = 104,
|
||||||
|
y = 132,
|
||||||
|
w = 420,
|
||||||
|
h = 34,
|
||||||
|
color = "#ffe2e8f0",
|
||||||
|
fontSize = 24,
|
||||||
|
layer = 10
|
||||||
|
}),
|
||||||
|
widgets.label("template_desc", {
|
||||||
|
text = "最小接入示例:一个 manifest、一个 main.lua、一组 RuntimeNode。",
|
||||||
|
x = 104,
|
||||||
|
y = 178,
|
||||||
|
w = 500,
|
||||||
|
h = 24,
|
||||||
|
color = "#ff94a3b8",
|
||||||
|
fontSize = 14,
|
||||||
|
layer = 10
|
||||||
|
}),
|
||||||
|
runtime_ui.button("template_start", {
|
||||||
|
text = state.started and "Started" or "Start",
|
||||||
|
x = 104,
|
||||||
|
y = 236,
|
||||||
|
w = 144,
|
||||||
|
h = 44,
|
||||||
|
handler = "template_start",
|
||||||
|
color = state.started and "#ff16a34a" or "#ff2563eb",
|
||||||
|
radius = 12,
|
||||||
|
fontSize = 15,
|
||||||
|
layer = 10
|
||||||
|
}),
|
||||||
|
runtime_ui.text("template_counter", "Clicks: " .. tostring(state.clicks), 272, 246, 180, 24, {
|
||||||
|
color = "#ffe2e8f0",
|
||||||
|
fontSize = 15,
|
||||||
|
layer = 10
|
||||||
|
}),
|
||||||
|
runtime_ui.text("template_status", state.status, 104, 320, 500, 24, {
|
||||||
|
color = "#fffacc15",
|
||||||
|
fontSize = 13,
|
||||||
|
layer = 10
|
||||||
|
})
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return RuntimeNodeUpdate[]
|
||||||
|
function ui.state_updates()
|
||||||
|
return {
|
||||||
|
runtime_ui.update("template_start", {
|
||||||
|
text = state.started and "Started" or "Start",
|
||||||
|
color = state.started and "#ff16a34a" or "#ff2563eb"
|
||||||
|
}),
|
||||||
|
runtime_ui.text_update("template_counter", "Clicks: " .. tostring(state.clicks)),
|
||||||
|
runtime_ui.text_update("template_status", state.status)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return ui
|
||||||
34
example/ios/.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
**/dgph
|
||||||
|
*.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
*.moved-aside
|
||||||
|
*.pbxuser
|
||||||
|
*.perspectivev3
|
||||||
|
**/*sync/
|
||||||
|
.sconsign.dblite
|
||||||
|
.tags*
|
||||||
|
**/.vagrant/
|
||||||
|
**/DerivedData/
|
||||||
|
Icon?
|
||||||
|
**/Pods/
|
||||||
|
**/.symlinks/
|
||||||
|
profile
|
||||||
|
xcuserdata
|
||||||
|
**/.generated/
|
||||||
|
Flutter/App.framework
|
||||||
|
Flutter/Flutter.framework
|
||||||
|
Flutter/Flutter.podspec
|
||||||
|
Flutter/Generated.xcconfig
|
||||||
|
Flutter/ephemeral/
|
||||||
|
Flutter/app.flx
|
||||||
|
Flutter/app.zip
|
||||||
|
Flutter/flutter_assets/
|
||||||
|
Flutter/flutter_export_environment.sh
|
||||||
|
ServiceDefinitions.json
|
||||||
|
Runner/GeneratedPluginRegistrant.*
|
||||||
|
|
||||||
|
# Exceptions to above rules.
|
||||||
|
!default.mode1v3
|
||||||
|
!default.mode2v3
|
||||||
|
!default.pbxuser
|
||||||
|
!default.perspectivev3
|
||||||
26
example/ios/Flutter/AppFrameworkInfo.plist
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>App</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>io.flutter.flutter.app</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>App</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>MinimumOSVersion</key>
|
||||||
|
<string>13.0</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
1
example/ios/Flutter/Debug.xcconfig
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "Generated.xcconfig"
|
||||||
1
example/ios/Flutter/Release.xcconfig
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "Generated.xcconfig"
|
||||||
616
example/ios/Runner.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,616 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 54;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
|
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
|
||||||
|
remoteInfo = Runner;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 10;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
name = "Embed Frameworks";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
|
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||||
|
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
331C8082294A63A400263BE5 /* RunnerTests */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
331C807B294A618700263BE5 /* RunnerTests.swift */,
|
||||||
|
);
|
||||||
|
path = RunnerTests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||||
|
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||||
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||||
|
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Flutter;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146E51CF9000F007C117D = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
|
331C8082294A63A400263BE5 /* RunnerTests */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146EF1CF9000F007C117D /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||||
|
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146F01CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||||
|
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||||
|
97C147021CF9000F007C117D /* Info.plist */,
|
||||||
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||||
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||||
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||||
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||||
|
);
|
||||||
|
path = Runner;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
331C8080294A63A400263BE5 /* RunnerTests */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||||
|
buildPhases = (
|
||||||
|
331C807D294A63A400263BE5 /* Sources */,
|
||||||
|
331C807F294A63A400263BE5 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
331C8086294A63A400263BE5 /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = RunnerTests;
|
||||||
|
productName = RunnerTests;
|
||||||
|
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
|
||||||
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
|
};
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
|
buildPhases = (
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = Runner;
|
||||||
|
productName = Runner;
|
||||||
|
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = YES;
|
||||||
|
LastUpgradeCheck = 1510;
|
||||||
|
ORGANIZATIONNAME = "";
|
||||||
|
TargetAttributes = {
|
||||||
|
331C8080294A63A400263BE5 = {
|
||||||
|
CreatedOnToolsVersion = 14.0;
|
||||||
|
TestTargetID = 97C146ED1CF9000F007C117D;
|
||||||
|
};
|
||||||
|
97C146ED1CF9000F007C117D = {
|
||||||
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
|
LastSwiftMigration = 1100;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||||
|
compatibilityVersion = "Xcode 9.3";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 97C146E51CF9000F007C117D;
|
||||||
|
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
97C146ED1CF9000F007C117D /* Runner */,
|
||||||
|
331C8080294A63A400263BE5 /* RunnerTests */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
331C807F294A63A400263BE5 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||||
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||||
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||||
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
|
||||||
|
);
|
||||||
|
name = "Thin Binary";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
|
};
|
||||||
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script";
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
331C807D294A63A400263BE5 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||||
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 97C146ED1CF9000F007C117D /* Runner */;
|
||||||
|
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
|
/* Begin PBXVariantGroup section */
|
||||||
|
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C146FB1CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = Main.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
97C147001CF9000F007C117D /* Base */,
|
||||||
|
);
|
||||||
|
name = LaunchScreen.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.flameLuaLudo;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
331C8088294A63A400263BE5 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.flameLuaLudo.RunnerTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
331C8089294A63A400263BE5 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.flameLuaLudo.RunnerTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
331C808A294A63A400263BE5 /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.flameLuaLudo.RunnerTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
|
97C147031CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147041CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
97C147061CF9000F007C117D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.flameLuaLudo;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
97C147071CF9000F007C117D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.flameLuaLudo;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
331C8088294A63A400263BE5 /* Debug */,
|
||||||
|
331C8089294A63A400263BE5 /* Release */,
|
||||||
|
331C808A294A63A400263BE5 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147031CF9000F007C117D /* Debug */,
|
||||||
|
97C147041CF9000F007C117D /* Release */,
|
||||||
|
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
97C147061CF9000F007C117D /* Debug */,
|
||||||
|
97C147071CF9000F007C117D /* Release */,
|
||||||
|
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
}
|
||||||
7
example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PreviewsEnabled</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1510"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO"
|
||||||
|
parallelizable = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "331C8080294A63A400263BE5"
|
||||||
|
BuildableName = "RunnerTests.xctest"
|
||||||
|
BlueprintName = "RunnerTests"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
enableGPUValidationMode = "1"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Profile"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||||
|
BuildableName = "Runner.app"
|
||||||
|
BlueprintName = "Runner"
|
||||||
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
7
example/ios/Runner.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Runner.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PreviewsEnabled</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
13
example/ios/Runner/AppDelegate.swift
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import Flutter
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
@main
|
||||||
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
|
override func application(
|
||||||
|
_ application: UIApplication,
|
||||||
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
|
) -> Bool {
|
||||||
|
GeneratedPluginRegistrant.register(with: self)
|
||||||
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-20x20@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-20x20@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-29x29@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-40x40@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-40x40@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "60x60",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-60x60@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "60x60",
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"filename" : "Icon-App-60x60@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-20x20@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "20x20",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-20x20@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-29x29@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "29x29",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-29x29@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-40x40@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "40x40",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-40x40@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "76x76",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-76x76@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "76x76",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-76x76@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "83.5x83.5",
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size" : "1024x1024",
|
||||||
|
"idiom" : "ios-marketing",
|
||||||
|
"filename" : "Icon-App-1024x1024@1x.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 295 B |
|
After Width: | Height: | Size: 406 B |
|
After Width: | Height: | Size: 450 B |
|
After Width: | Height: | Size: 282 B |
|
After Width: | Height: | Size: 462 B |
|
After Width: | Height: | Size: 704 B |
|
After Width: | Height: | Size: 406 B |