跳转至

核心概念

lilt 的界面是一棵节点树,由一个 UI goroutine 独占:它每帧处理按键、推进动画、在属性变化后整树重排,再把整屏画一遍。下面几页分别讲这条流水线上的一环,写复杂界面之前建议按顺序读一遍。

页面 讲什么
节点树 Node 和 Box、用选项嵌套建树、增删节点、节点靠哪些接口获得能力
布局 尺寸、锚点、Stack、Flex、Grid,以及位置变化时的滑动
主题与单位 Theme 的字段和默认值、单位如何随字体缩放、光标的弹簧
线程模型 回调为什么不在 UI goroutine 上、ui.Do 和 widget.Control
输入与焦点 按键的阶段、激活规则、焦点怎么移动、按键怎么分发
动画 anim.Value、anim.Spring 和几个预设
页面与弹窗 层栈、ui.Push 的页面、ui.Open 的抽屉、对话框和 Toast

一帧里发生什么

lilt.Start 起的 goroutine 循环调用帧,帧率上限是 lilt.FPS(默认 60),传给各处的 dt 是距上一帧的秒数,最多按 0.1 秒算,卡顿之后动画不会一步跳到终点。一帧从头到尾持有一把锁,ui.Do 就是在两帧之间拿这把锁,所以它里面的代码不会和帧交错执行。

flowchart TD
    A["取出输入队列<br/>按键交给焦点节点"] --> B["Update<br/>可见节点逐个调用"]
    B --> C["层栈<br/>移除已退出的层"]
    C --> D["anim.Step<br/>推进所有弹簧"]
    D --> E{"需要布局?"}
    E -- 是 --> F["整树 Arrange<br/>刚打开的层取得焦点"]
    E -- 否 --> G
    F --> G["光标追向焦点"]
    G --> H{"需要重画?"}
    H -- 是 --> I["清屏,画各层<br/>画光标,再画 Toast"]
    I --> J["screen.Flush"]
    H -- 否 --> K["本帧结束"]
  • 收到按键、有弹簧还在动,都会把这一帧标成需要重画;什么都没变的帧不画也不 Flush,屏幕总线是空闲的。
  • 布局只在调用过 ui.Relayout() 之后做,任何影响尺寸和位置的 Set 方法都会调用它,见布局。
  • 光标画在页面和弹窗之上、Toast 之下,所以聚焦的行不会把 Toast 反色。