swift/ui/demo_ui.swift · 12453 字节 · 下载原文
// demo_ui.swift —— **最新 UI 接口**示范(Swift)
// demo_ui.swift — a demo of the **newest UI interface** (Swift)
//
// 四层示范的第四层:开 `ui_*` 绘图窗口 + **查屏幕方向** + 画图元 + `ui_2d_present` +
// Layer 4 of the four-layer demo: open a `ui_*` drawing window + **query the screen orientation** + draw primitives + `ui_2d_present` +
// 处理触摸/按键,**有界**(收到任意键就退出,否则画满 60 帧自动退出),正常结束。
// handle touch/keys, **bounded** (exit on any key, otherwise auto-exit after 60 frames), terminating normally.
//
// 四条关键差别(与第三层 BGI 相比):① 开窗显式(宽高来自 `ui_scr_get_w/h`,不写死分辨率);
// Four key differences (compared with layer 3, BGI): ① opening the window is explicit (width/height come from `ui_scr_get_w/h`, no hardcoded resolution);
// ② 方向要问 `ui_scr_get_orientation()`(0=竖屏 / 1=横屏,**开窗前就能问**);
// ② Ask the orientation with `ui_scr_get_orientation()` (0=portrait / 1=landscape, **askable before opening the window**);
// ③ `ui_2d_present()` 才是"这一帧画完了"的帧边界;④ 输入是**统一消息队列**
// ③ `ui_2d_present()` is what marks the frame boundary of "this frame is finished"; ④ input is a **single unified message queue**
// (键盘/触摸/定时器/窗口事件同一个队列,`ui_msg_fetch_wait` / `ui_msg_get_a` / `ui_win_is_closed`)。
// (keyboard/touch/timer/window events all in one queue: `ui_msg_fetch_wait` / `ui_msg_get_a` / `ui_win_is_closed`).
//
// ◆ 跑法与"画面怎么看"(桌面 vmlcli)
// ◆ How to run it and "how to look at the picture" (desktop vmlcli)
//
// dotnet scripts/vmlcli/bin/Release/net10.0/vmlcli.dll \
// third_party/vml/Examples/swift/demo_ui.swift --screen 480x640 --frames /tmp/out_frames/
//
// ⚠ **要看画面请用 `--frames 目录`,不要用 `--frame 单个文件`**:`ui_win_close()`(#521)
// ⚠ **To see the picture use `--frames <dir>`, not `--frame <single file>`**: `ui_win_close()` (#521)
// 在宿主侧把**整个场景置空**,程序跑完之后 `--frame` 那一刀已经无场景可取,只会打一行
// blanks out **the whole scene** on the host side, so once the program has finished there is no scene left for `--frame` to take; it only prints a line
// 「没有可导出的帧(场景是空的)」。`--frames` 是每帧 present 当场拍快照,照常出图。
// "no frame to export (the scene is empty)". `--frames` snapshots at each present and exports as usual.
// 这是桌面脚手架的行为,不是本程序写错了。
// This is the behavior of the desktop scaffold, not a mistake in this program.
//
// ◆ 写法(Swift 前端的实测约束)
// ◆ Style (measured constraints of the Swift frontend)
//
// · 顶层 `var` 可变标量**可用**(实测 `while` 里累加正确),这一份全部写在顶层;
// · Top-level `var` mutable scalars **do work** (measured: accumulation inside a `while` is correct), and this copy writes everything at the top level;
// · `print` 多个实参之间不加分隔符 —— 与绘图无关,仅作记录;
// · `print` adds no separator between multiple arguments — unrelated to drawing, recorded just for the record;
// · 整数转字符串用 `int_to_str`(`Lib/shared/src/conv.c`,靠 `int_to_str` 前缀自动链进来)。
// · Integer-to-string uses `int_to_str` (`Lib/shared/src/conv.c`, auto-linked by the `int_to_str` prefix).
//
// 跑法:命令行页输入 vml run examples/swift/demo_ui.swift
// How to run: on the command-line page type vml run examples/swift/demo_ui.swift
//
// 颜色:0xAARRGGBB 写**负数十进制**。
// Colors: 0xAARRGGBB is written as a **negative decimal**.
// ── 开窗之前就问方向(这一层与 BGI 最"新"的一条)────────────
// ── Ask the orientation before opening the window (the most "new-style" thing about this layer vs BGI) ────────────
#include "../../../Lib/swift/vml_ui.swift"
var ori = ui_scr_get_orientation()
// 界面语言:开局查一次(`ui_scr_get_language` 是 syscall,别每帧调)
// UI language: queried once at start (`ui_scr_get_language` is a syscall, not once per frame)
var lang = ui_scr_get_language()
// 开窗:尺寸传 0 ⇒ **交给宿主用真实可用区**。开窗**之前** `ui_scr_get_w/h` 只能估算,而估算与
// Open the window: pass size 0 ⇒ **the host uses the real available area**. **Before** the open,
// 实际总有出入(这就是"内容偏上"的根因)⇒ 所以尺寸改在**开窗之后**问(那时才是真实画布)。
// `ui_scr_get_w/h` can only estimate and the estimate differs from reality (exactly why the content sat high) ⇒ the size is now queried **after** the open, when it is the real canvas.
ui_win_open(lang == 0 ? "UI 接口演示" : "UI interface demo", 0, 0)
ui_scr_set_keep_on(1)
// 尺寸:开窗之后再问一次(拿不到就退到竖屏默认值)
// Size: re-queried after the open (falling back to the portrait default if unavailable)
var w = ui_scr_get_w()
var h = ui_scr_get_h()
if w <= 0 { w = 360 }
if h <= 0 { h = 620 }
var tid = ui_timer_set(30, 1)
var frames = 0
var keys = 0
var tx = 0
var ty = 0
var touched = 0
var running = 1
while running != 0 {
if ui_win_is_closed() != 0 { running = 0 }
if frames >= 60 { running = 0 }
if running != 0 {
// ── ① 底色 + 标题(居中锚点 = 1)────────────────────────
// ── ① Background + title (center anchor = 1) ────────────────────────
ui_2d_clear(-15724520)
ui_2d_text(w / 2, 26, lang == 0 ? "Dolaima ui_* 接口演示 (Swift)" : "Dolaima ui_* interface demo (Swift)", -6643536, 16, VML_ANCHOR_CENTER)
ui_2d_text(w / 2, 50, lang == 0 ? "开窗 / 方向 / 图元 / present / 消息队列" : "window / orientation / primitives / present / message queue", -6643536, 12, VML_ANCHOR_CENTER)
// ── ② 屏幕方向(0=竖屏 1=横屏)──────────────────────────
// ── ② Screen orientation (0=portrait 1=landscape) ──────────────────────────
ui_2d_text(14, 80, lang == 0 ? "屏幕方向 =" : "orientation =", -11409298, 13, VML_ANCHOR_LEFT)
ui_2d_text(96, 80, int_to_str(ori), -11409298, 13, VML_ANCHOR_LEFT)
if ori == 1 { ui_2d_text(126, 80, lang == 0 ? "(横屏)" : "(landscape)", -11409298, 13, VML_ANCHOR_LEFT) }
else { ui_2d_text(126, 80, lang == 0 ? "(竖屏)" : "(portrait)", -11409298, 13, VML_ANCHOR_LEFT) }
// ── ③ 图元:矩形(实心 / 空心 / 圆角)──────────────────
// ── ③ Primitives: rectangles (filled / outlined / rounded) ──────────────────
ui_2d_text(14, 108, lang == 0 ? "图元:矩形" : "primitives: rectangles", -6643536, 13, VML_ANCHOR_LEFT)
ui_2d_rect(14, 126, 84, 46, -11409298, 1, 0, 0)
ui_2d_rect(108, 126, 84, 46, -131246, 0, 2, 0)
ui_2d_rect(202, 126, 84, 46, -63488, 1, 0, 12)
// ── ④ 图元:圆 / 椭圆 / 线 ──────────────────────────────
// ── ④ Primitives: circle / ellipse / line ─────────────────────────────
ui_2d_text(14, 196, lang == 0 ? "图元:圆 / 椭圆 / 线" : "primitives: circle / ellipse / line", -6643536, 13, VML_ANCHOR_LEFT)
ui_2d_circle(46, 250, 30, -131246, 1, 0)
ui_2d_circle(120, 250, 30, -11409298, 0, 3)
ui_2d_ellipse(210, 250, 44, 26, -63488, 1, 0)
var i = 0
while i < 8 {
ui_2d_line(14, 300 + i * 6, w - 14, 300 + i * 6, -15263713, 2)
i = i + 1
}
// ── ⑤ 8 级灰度色带(0xAARRGGBB 真彩)───────────────────
// ── ⑤ 8-step grayscale bar (0xAARRGGBB true color) ───────────────────
ui_2d_text(14, 358, lang == 0 ? "真彩(0xAARRGGBB):" : "true color (0xAARRGGBB):", -6643536, 13, VML_ANCHOR_LEFT)
var s = 0
while s < 8 {
if s == 0 { ui_2d_rect(14 + s * 40, 376, 36, 26, -16777216, 1, 0, 0) }
if s == 1 { ui_2d_rect(14 + s * 40, 376, 36, 26, -14671840, 1, 0, 0) }
if s == 2 { ui_2d_rect(14 + s * 40, 376, 36, 26, -12566464, 1, 0, 0) }
if s == 3 { ui_2d_rect(14 + s * 40, 376, 36, 26, -10066330, 1, 0, 0) }
if s == 4 { ui_2d_rect(14 + s * 40, 376, 36, 26, -8355712, 1, 0, 0) }
if s == 5 { ui_2d_rect(14 + s * 40, 376, 36, 26, -6250336, 1, 0, 0) }
if s == 6 { ui_2d_rect(14 + s * 40, 376, 36, 26, -4210753, 1, 0, 0) }
if s == 7 { ui_2d_rect(14 + s * 40, 376, 36, 26, -1, 1, 0, 0) }
s = s + 1
}
// ── ⑥ 动画 + 帧进度条(证明每帧确实在重画)─────────────
// ── ⑥ Animation + frame progress bar (proof that every frame really redraws) ─────────────
var col = 14 + frames - (frames / 20) * 20
col = 14 + col * 10
if col > w - 46 { col = 14 }
ui_2d_circle(col, 448, 18, -63488, 1, 0)
ui_2d_rect(14, 480, (w - 28) * frames / 60, 10, -11409298, 1, 0, 4)
// ── ⑦ 消息队列:帧数 / 最后一次按键 / 最后一次触摸 ──────
// ── ⑦ Message queue: frame count / last key / last touch ──────
ui_2d_text(14, 506, lang == 0 ? "已画帧数 =" : "frames drawn =", -6643536, 13, VML_ANCHOR_LEFT)
ui_2d_text(112, 506, int_to_str(frames), -6643536, 13, VML_ANCHOR_LEFT)
ui_2d_text(14, 528, lang == 0 ? "最后一次按键 =" : "last key =", -6643536, 13, VML_ANCHOR_LEFT)
ui_2d_text(140, 528, int_to_str(keys), -6643536, 13, VML_ANCHOR_LEFT)
if touched == 1 {
ui_2d_text(14, 550, lang == 0 ? "最后一次触摸 =" : "last touch =", -6643536, 13, VML_ANCHOR_LEFT)
ui_2d_text(140, 550, int_to_str(tx), -6643536, 13, VML_ANCHOR_LEFT)
ui_2d_text(190, 550, int_to_str(ty), -6643536, 13, VML_ANCHOR_LEFT)
ui_2d_circle(tx, ty, 26, -131246, 0, 3)
} else {
ui_2d_text(14, 550, lang == 0 ? "最后一次触摸 = 无(点一下试试)" : "last touch = none (tap to try)", -6643536, 13, VML_ANCHOR_LEFT)
}
ui_2d_text(w / 2, h - 24, lang == 0 ? "按任意键退出,或画满 60 帧自动退出" : "press any key to exit, or 60 frames ends it", -6643536, 12, VML_ANCHOR_CENTER)
// ── ⑧ 帧边界 ────────────────────────────────────────────
// ── ⑧ Frame boundary ────────────────────────────────────────────
ui_2d_present()
}
// ── ⑨ 收一条消息(最多等 30ms,保证循环有界)────────────────
// ── ⑨ Take one message (wait at most 30ms, keeping the loop bounded) ────────────────
var t = ui_msg_fetch_wait(30)
if t == 10 { running = 0 }
else if t == 1 { keys = ui_msg_get_a(); running = 0 }
else if t == 6 { tx = ui_msg_get_a(); ty = ui_msg_get_b(); touched = 1 }
else if t == 4 { tx = ui_msg_get_a(); ty = ui_msg_get_b(); touched = 1 }
frames = frames + 1
}
// ── 收尾:关定时器、放开屏幕常亮、关窗 ──────────────────────
// ── Wrap-up: kill the timer, release keep-screen-on, close the window ──────────────────────
ui_timer_kill(tid)
ui_scr_set_keep_on(0)
ui_win_close()