ui
pkg.go.dev · import "github.com/chhongzh/lilt/ui"
节点树、布局、层栈、焦点、输入、光标和主题。除了标注“任何 goroutine”的函数,其余都只能在 UI goroutine 上调用:lilt.Start 之前、ui.Do 里、或者组件的方法里。
节点与 Box
| 签名 |
说明 |
详见 |
type Node interface |
树上的任何节点;只有一个未导出方法,只有嵌入了 Box 的类型满足它 |
节点树 |
type Box struct |
每个节点都嵌入的基础:父子链接、尺寸、内边距、动画偏移 |
节点树 |
func BoxOf(d Node) *Box |
取 d 嵌入的 Box |
节点树 |
func (b *Box) Parent() Node |
父节点 |
节点树 |
func (b *Box) Children() []Node |
子节点,不要直接修改,用 Add、Insert、Remove |
节点树 |
func (b *Box) Width() Size、SetWidth(s Size) |
宽度 |
尺寸 |
func (b *Box) Height() Size、SetHeight(s Size) |
高度 |
尺寸 |
func (b *Box) Anchor() Vec2、SetAnchor(v Vec2) |
节点挂在父节点内容区的哪一点 |
锚点 |
func (b *Box) Origin() Vec2、SetOrigin(v Vec2) |
节点自己的哪一点放在锚点上 |
锚点 |
func (b *Box) Position() Vec2、SetPosition(v Vec2) |
锚定之后再加的偏移,像素 |
锚点 |
func (b *Box) Margin() Insets、SetMargin(i Insets) |
外边距,单位 |
尺寸 |
func (b *Box) Padding() Insets、SetPadding(i Insets) |
内边距,单位 |
尺寸 |
func (b *Box) Grow() float32、SetGrow(g float32) |
在 Flex 里分剩余空间的份数 |
Flex |
func (b *Box) Visible() bool、SetVisible(v bool) |
隐藏的节点不占空间、不绘制、不能有焦点 |
节点树 |
func (b *Box) Focusable() bool、SetFocusable(f bool) |
能否获得焦点 |
焦点 |
func (b *Box) SetClip(c bool) |
把自己和子节点的绘制限制在自己的矩形里 |
能力接口 |
func (b *Box) SetAnimateLayout(a bool) |
布局引起的位置变化改为滑过去 |
布局动画 |
func (b *Box) SetBackground(c gfx.Color) |
用 c 填充矩形,按圆角 |
主题 |
func (b *Box) SetBorder(c gfx.Color) |
一像素的边框 |
主题 |
func (b *Box) Bordered() bool |
是否画边框 |
主题 |
func (b *Box) SetRadius(r int16) |
背景和边框的圆角,单位 |
单位 |
func (b *Box) Rect() Rect |
布局得到的矩形,相对父节点,不含动画偏移 |
测量与绘制 |
func (b *Box) ContentRect() Rect |
内边距以内的区域,像素,自己的坐标系 |
测量与绘制 |
func (b *Box) OffsetX() *anim.Value、OffsetY() *anim.Value |
叠加在布局位置上的动画平移,子节点跟着动 |
动画 |
构造选项
| 签名 |
说明 |
详见 |
type Option func(Node) |
构造时配置节点,让整棵树能写成一个嵌套表达式 |
选项 |
func Apply(d Node, opts []Option) |
依次应用选项;组件的构造函数最后调用它 |
自定义组件 |
func Children(nodes ...Node) Option |
添加子节点 |
选项 |
func Ref[T Node](p *T) Option |
把正在构造的节点存进 *p,在嵌套的树里留个句柄 |
Ref |
func Width(s Size) Option、Height(s Size) Option |
宽、高 |
尺寸 |
func Fill() Option |
宽高都等于父节点内容区 |
尺寸 |
func FillWidth() Option、FillHeight() Option |
只铺满一个方向 |
尺寸 |
func Anchor(v Vec2) Option、Origin(v Vec2) Option |
锚点、原点 |
锚点 |
func Align(v Vec2) Option |
锚点和原点设成同一点,把节点钉到父节点的边或角 |
锚点 |
func Position(x, y float32) Option |
附加偏移 |
锚点 |
func Margin(i Insets) Option、Padding(i Insets) Option |
外边距、内边距 |
尺寸 |
func Grow(g float32) Option |
Flex 里的伸展份数 |
Flex |
func Gap(g float32) Option |
Flex 或 Grid 子节点间距,单位;用在别的节点上 panic |
Flex |
func Main(m MainAlign) Option、Cross(c CrossAlign) Option |
Flex 主轴、交叉轴对齐 |
Flex |
func Columns(cols ...Track) Option |
Grid 的列 |
Grid |
func Focusable() Option |
可获得焦点 |
焦点 |
func Hidden() Option |
一开始就隐藏 |
节点树 |
func Clip() Option |
裁剪到自己的矩形 |
能力接口 |
func AnimateLayout() Option |
布局变化时滑动 |
布局动画 |
func Background(c gfx.Color) Option、Border(c gfx.Color) Option |
背景色、边框色 |
主题 |
func Radius(r int16) Option |
圆角,单位 |
单位 |
树操作
| 签名 |
说明 |
详见 |
func Add(parent Node, children ...Node) |
追加子节点;parent 实现了 ContentHost 时加到它的内部节点 |
节点树 |
func Insert(parent Node, i int, child Node) |
插入到第 i 个位置 |
节点树 |
func Attach(parent, child Node) |
直接追加,忽略 ContentHost;组件搭内部结构用 |
节点树 |
func Remove(d Node) |
从父节点摘下并通知父节点;焦点在里面时移到最近的剩余节点 |
节点树 |
func RemoveChildren(parent Node) |
摘下所有子节点(或 Content 的) |
节点树 |
func IsAncestor(a, d Node) bool |
a 是 d 本身或包含 d |
节点树 |
func Attached(d Node) bool |
d 是否在活动的树上 |
节点树 |
线程与刷新
| 签名 |
说明 |
详见 |
func Do(fn func()) |
在两帧之间运行 fn,其他 goroutine 借此改树;任何 goroutine,fn 里不能再调用 Do |
ui.Do |
func Relayout() |
安排一次整树布局和重绘 |
布局 |
func Invalidate() |
安排一次重绘 |
布局 |
尺寸与几何
| 签名 |
说明 |
详见 |
type Size struct |
一个方向的尺寸:固定像素、父节点内容区的比例,或 Auto |
尺寸 |
func Px(v float32) Size |
固定像素 |
尺寸 |
func Rel(f float32) Size |
父节点内容区的比例 |
尺寸 |
var Auto = Size{} |
按内容;Size 的零值 |
尺寸 |
const Unbounded float32 = 1 << 24 |
滚动方向上的可用空间;这里的比例尺寸退回 Auto |
尺寸 |
type Vec2 struct{ X, Y float32 } |
布局空间的点或尺寸 |
锚点 |
TopLeft、TopCenter、TopRight、CenterLeft、Center、CenterRight、BottomLeft、BottomCenter、BottomRight |
九个对齐点,Vec2{0, 0} 到 Vec2{1, 1} |
锚点 |
type Rect struct{ X, Y, W, H float32 } |
布局矩形 |
测量与绘制 |
func (r Rect) Right() float32、Bottom() float32 |
右边、下边 |
测量与绘制 |
func (r Rect) Center() Vec2 |
中心点 |
测量与绘制 |
func (r Rect) Pixels() gfx.Rect |
四条边分别取整,相邻矩形取整后仍相邻 |
测量与绘制 |
type Insets struct{ Left, Top, Right, Bottom float32 } |
外边距或内边距,单位 |
单位 |
func All(v float32) Insets |
四边相同 |
单位 |
func XY(x, y float32) Insets |
左右 x、上下 y |
单位 |
func LTRB(l, t, r, b float32) Insets |
分别指定 |
单位 |
func (i Insets) Horizontal() float32、Vertical() float32 |
左右之和、上下之和 |
单位 |
func (i Insets) Scale(f float32) Insets |
四边乘 f,乘 Unit() 得到像素 |
单位 |
func Unit() float32 |
一个单位等于多少像素:主题字体行高除以默认字体行高 |
单位 |
func Round(x float32) int16 |
四舍五入,.5 远离零 |
测量与绘制 |
func ScreenSize() (w, h int16) |
绑定的屏幕尺寸,像素 |
屏幕接口 |
容器
| 签名 |
说明 |
详见 |
type Flex struct |
沿一个方向排列子节点 |
Flex |
func NewRow(opts ...Option) *Flex、NewColumn(opts ...Option) *Flex |
横向、纵向的 Flex |
Flex |
func (f *Flex) Axis() Axis、SetAxis(a Axis) |
方向;嵌入 Flex 的组件用 SetAxis 变成一列 |
Flex |
func (f *Flex) Gap() float32、SetGap(g float32) |
子节点间距,单位 |
Flex |
func (f *Flex) SetMainAlign(m MainAlign)、SetCrossAlign(c CrossAlign) |
主轴、交叉轴对齐 |
Flex |
type Axis uint8:Horizontal、Vertical |
Flex 的方向 |
Flex |
type MainAlign uint8:MainStart、MainCenter、MainEnd、MainSpaceBetween |
没有子节点伸展时,主轴上的剩余空间怎么分 |
Flex |
type CrossAlign uint8:CrossStretch、CrossStart、CrossCenter、CrossEnd |
交叉轴上的位置;CrossStretch 拉满该方向为 Auto 的子节点 |
Flex |
type Grid struct |
按列逐行摆放,行高取该行最高的子节点,Auto 尺寸的子节点填满格子 |
Grid |
func NewGrid(opts ...Option) *Grid |
没设置列时为一列 Fr(1) |
Grid |
func (g *Grid) SetColumns(cols ...Track) |
设置列 |
Grid |
func (g *Grid) SetGap(gap float32) |
行列间距,单位 |
Grid |
type Track struct |
一列的宽度 |
Grid |
func Fixed(px float32) Track |
固定像素 |
Grid |
func Fr(n float32) Track |
剩余宽度的份数 |
Grid |
type Stack struct |
普通容器,子节点按锚点和原点摆放 |
Stack |
func NewStack(opts ...Option) *Stack |
新建 Stack |
Stack |
Flex、Grid 的 MeasureChildren、ArrangeChildren |
Layouter 的实现,由布局调用 |
能力接口 |
布局函数
自己实现 Layouter 时用来测量、摆放子节点。
| 签名 |
说明 |
详见 |
func Measure(d Node, max Vec2) Vec2 |
d 在 max(扣除外边距后)里想要的尺寸 |
能力接口 |
func Arrange(d Node, r Rect) |
给 d 最终矩形(相对父节点)并布局它的子节点 |
能力接口 |
func ArrangeFree(c Node, area Rect) |
按锚点、原点和偏移把 c 放进 area;非 Layouter 节点的默认摆法 |
Stack |
func RestRect(d Node) Rect |
所有动画停下后 d 在屏幕上的矩形,像素 |
焦点 |
页面与弹窗
| 签名 |
说明 |
详见 |
func Push(p Node) |
把 p 作为新的整屏页面从右侧推入 |
页面 |
func Pop() bool |
关闭最上层的页面或弹窗;根页面不关 |
Back |
func Open(n Node, p Popup) |
以弹窗打开 n;抽屉和对话框关闭前独占焦点和按键 |
弹窗 |
func Close(n Node) bool |
关闭显示 n 的页面或弹窗 |
弹窗 |
type Popup uint8 |
弹窗的位置和进出方向 |
弹窗 |
DrawerBottom、DrawerTop |
从下、上边缘滑入,高度按内容 |
Drawer |
DrawerLeft、DrawerRight |
从左、右边缘滑入,宽度按内容 |
Drawer |
Dialog |
落到屏幕中央 |
Dialog |
Toast |
从底部浮起,不抢焦点、不接按键 |
Toast |
焦点
| 签名 |
说明 |
详见 |
func SetFocus(d Node) |
把焦点移到 d;隐藏或不可聚焦的节点被忽略 |
焦点 |
func Focused() Node |
当前焦点节点,没有时为 nil |
焦点 |
func FindFocus(scope, from Node, dir Key) Node |
按 Android FocusFinder 的规则找 dir 方向的下一个焦点 |
焦点 |
func FirstFocusable(d Node) Node |
按树序第一个可聚焦节点 |
焦点 |
按键与触摸
| 签名 |
说明 |
详见 |
type Key uint8 |
逻辑键,引脚到键的映射由板子代码负责 |
按键 |
KeyNone、KeyUp、KeyDown、KeyLeft、KeyRight、KeyOK、KeyBack、KeyMenu、KeyBackspace、KeyChar |
所有逻辑键;KeyChar 的字符在 KeyEvent.Rune 里 |
按键 |
func (k Key) Arrow() bool |
是否方向键 |
按键 |
type Phase uint8:Press、Repeat、Release |
按下、连发、松开 |
激活 |
type KeyEvent struct{ Key; Phase; Rune; Canceled; Repeated } |
一个按键事件;Canceled 是触摸变拖动后的松开,Repeated 是连发之后的松开 |
激活 |
func (e *KeyEvent) Fires() bool |
是否该执行动作:未取消且前面没有连发的 Release,或每个 Repeat |
激活 |
func Emit(k Key, p Phase) |
发一个按键事件;任何 goroutine |
按键 |
func EmitRune(r rune) |
发一个键入的字符;任何 goroutine |
物理键盘 |
func EmitPointer(x, y int16, p PointerPhase) |
发一个触摸或鼠标事件,屏幕像素;任何 goroutine |
触摸屏 |
type PointerEvent struct{ X, Y int16; Phase PointerPhase } |
一个指针事件 |
分发 |
type PointerPhase uint8:PointerDown、PointerMove、PointerUp |
按下、移动、抬起 |
触摸屏 |
type KeyPoller struct |
把按键电平变成消抖后的按下、连发、松开,连发会加速 |
KeyPoller |
func NewKeyPoller() *KeyPoller |
默认 Debounce 2、RepeatDelay 0.2、RepeatInterval 0.07、MinInterval 0.03、Accel 0.9 |
KeyPoller |
func (p *KeyPoller) Poll(k Key, down bool, dt float32) |
喂一次采样,dt 是距上次的秒数 |
按键 |
KeyPoller.Debounce、RepeatDelay、RepeatInterval、MinInterval、Accel |
消抖采样数、连发延迟、首次间隔、最小间隔、间隔倍率 |
按键 |
文字输入
| 签名 |
说明 |
详见 |
type TextTarget interface{ Text() string; SetText(s string) } |
输入法编辑的对象 |
文字输入 |
type TextInputMethod interface{ Open(t TextTarget) } |
为 t 打开一个编辑器 |
文字输入 |
var TextInput TextInputMethod |
Input 激活时打开的编辑器;nil 时用 widget.Keyboard |
文字输入 |
能力接口
组件实现这些接口获得对应能力,框架通过类型断言调用。
| 签名 |
说明 |
详见 |
type Painter interface{ Draw(c gfx.Canvas) } |
画自己,在子节点之前 |
能力接口 |
type OverPainter interface{ DrawOver(c gfx.Canvas) } |
在子节点之后再画 |
能力接口 |
type Updater interface{ Update(dt float32) } |
节点在树上且可见时每帧一次 |
能力接口 |
type Measurer interface{ Measure(max Vec2) Vec2 } |
叶子节点的内容尺寸,不含内边距 |
测量与绘制 |
type Layouter interface{ MeasureChildren; ArrangeChildren } |
自己测量、摆放子节点;content 是内边距以内的区域 |
能力接口 |
type KeyHandler interface{ HandleKey(e *KeyEvent) bool } |
按键从焦点节点向上冒泡;Press 时返回 true 就认领这个键 |
按键分发 |
type KeyPreviewer interface{ PreviewKey(e *KeyEvent) bool } |
按键从根向焦点节点下行时先过目,容器借此拦截 |
按键分发 |
type PointerHandler interface{ HandlePointer(e *PointerEvent) bool } |
指针事件从指下节点向上冒泡;移动时返回 true 就接管指针 |
按键分发 |
type Focuser interface{ FocusChanged(focused bool) } |
获得或失去焦点时通知 |
焦点 |
type Revealer interface{ Reveal(d Node) } |
后代获得焦点时通知,用来滚动到它 |
焦点 |
type ArrowScroller interface{ ArrowScroll(e *KeyEvent, next Node) bool } |
焦点移动前祖先先拿到方向键;next 是焦点将去的节点,边缘时为 nil |
嵌套滚动 |
type CursorShaper interface{ CursorShape(r Rect) Rect } |
焦点节点改变光标的矩形,屏幕像素 |
改变光标 |
type ContentHost interface{ Content() Node } |
让 Add、RemoveChildren 转到内部节点 |
能力接口 |
type Leaf interface{ Leaf() } |
不接受子节点 |
能力接口 |
type ChildRemover interface{ ChildRemoved(child Node) } |
子节点摘下时通知,包括子节点移除自己 |
能力接口 |
光标
| 签名 |
说明 |
详见 |
type Cursor struct |
标示焦点的唯一高亮,四条边各自运动,生活在屏幕坐标里 |
光标 |
func (c *Cursor) Lean(dx, dy float32) |
把光标偏移并保持,按键按下时的预备动作;Lean(0, 0) 放开 |
光标 |
func (c *Cursor) Leap() |
跳一下:抬起、变大、落地压扁;切换屏幕时调用 |
光标 |
func (c *Cursor) Nudge(px float32) |
横向甩出约 px 再弹回 |
光标 |
var CursorLead = &anim.Spring{Response: 0.22, Ease: true} |
光标朝运动方向那条边的缓动 |
光标 |
var CursorTrail = &anim.Spring{Response: 0.52, Ease: true} |
光标后面那条边的缓动,更慢,于是先拉长再收拢 |
光标 |
var CursorRelease = &anim.Spring{Response: 0.57, Damping: 0.4} |
松开 OK 后光标带着晃动恢复;按下时用 anim.Interactive |
光标 |
var CursorLeap = &anim.Spring{Response: 0.63, Damping: 0.45} |
跨屏幕跳跃 |
光标 |
var CursorLean = &anim.Spring{Response: 0.35, Damping: 0.7} |
朝按住的键偏过去再回来 |
光标 |
Cursor 的实例是包内的单例,没有导出的访问方式;框架自己在切换屏幕、按 Back、移动焦点时调用这三个方法。组件要改光标的形状,用 CursorShaper。
主题
| 签名 |
说明 |
详见 |
type Theme struct |
组件绘制用的颜色、字体和间距 |
主题 |
Theme.Background、Foreground |
背景色、前景色 |
主题 |
Theme.Accent |
屏幕不能反色时光标的颜色 |
反色光标 |
Theme.Muted |
提示和禁用文字 |
主题 |
Theme.Font、SmallFont |
正文字体、小字体;Font 的行高决定 Unit() |
单位 |
Theme.Radius |
带边框控件的圆角,单位 |
主题 |
Theme.RowPadding |
列表行、输入框、标题的文字内边距 |
主题 |
Theme.ButtonPadding |
按钮文字和边框之间的距离 |
主题 |
Theme.Gap |
抽屉、对话框、带内边距的列表里控件之间的间距 |
主题 |
Theme.PanelPadding |
控件和外框之间的距离:每个弹窗、每个带边框的面板 |
主题 |
var CurrentTheme = &Theme{...} |
当前主题,默认黑底白字,适合 OLED;组件在构造时读取 |
主题 |