Skip to content

自定义控件 ​

每个控件都构建在一个基座(base)之上,即同一个控件去掉外观后的样子: 基座处理指针、键盘和焦点,告诉辅助技术它是什么,而把颜色、尺寸和形状 完全交给它返回的元素,你可以像对其他元素一样为它们设置样式。在它们 之上构建你自己的设计,就像 Web 上的无头(headless)组件库那样。

基座作用
ButtonBase一个可获得焦点的行,并在指针、Enter 和 Space 时报告 Clicked
CheckboxBase、SwitchBase、ToggleBase一个切换 *bool 值的行
RadioBase一个把选中值写入 *T 的行
SliderBase根据指针在其内容盒(含内边距)上的横向位置设置 *float64,Vertical 时为纵向;并可用方向键、Page Up 和 Page Down、Home 和 End 按其 Step 调整
TabsBase标签 List,其 Tab 选择一个 *int,方向键移动选择项和焦点
SegmentedBase由 Segment 组成的 Track,一个选择 *int 的单选组
CollapsibleBase一个 Trigger 显示和隐藏 Panel 构建的内容,用 Progress 使其增长和收缩,取值从 0(收起)到 1(展开)
SelectBase一个 Trigger 打开由 Item 组成的 Popup,选择 *T;方向键高亮(Highlighted),Enter 选中
ComboboxBase一个 Input,其文本过滤下方 Popup 的 Item;方向键高亮,Enter 或点击选择(Chosen)
PopoverBase、DialogBase位于锚点旁边的面板,或覆盖整个窗口背景之上的面板,点击外部或按 Escape 关闭
TooltipBase当指针停留或键盘焦点位于锚点上时,在其旁边显示提示;点击或按 Escape 隐藏(工具提示)
ToastViewportBase、ToastBase在你自己的视口里显示窗口的提示条,每条是一个带有 ActionButton 和 CloseButton 的状态;当指针或焦点位于其上时计时暂停(提示条)
TextInputBase、TextAreaBase没有内边距、背景、边框或圆角的文本输入,可设为 ReadOnly 或不可

图表也有自己的基座:包 ui/chart提供没有外观的标尺、 坐标轴、标记和选择。

基座构造函数返回值形式的部件,其构建周期与其他元素相同。对有状态的 基座,在构造前用 Context.Key 给它一个键。

基于 SegmentedBase 的分段控件,以及基于 SelectBase 的选择器:

go
t := c.Theme()
view := ui.SegmentedBase(c.Key("period"), &app.view, 3)
view.Track.Padding(3).Radius(999).Background(t.Surface).Children(func() {
	for i, name := range []string{"Day", "Week", "Month"} {
		seg := view.Segment(i).Padding(5, 14).Radius(999)
		if i == app.view {
			seg.Background(t.Background).Shadow(0, 1, 2, 0, ui.RGBA(0, 0, 0, 0.15))
		}
		seg.Children(func() { ui.Text(c, name) })
	}
})

sel := ui.SelectBase(c.Key("size"), &app.size)
sel.Trigger.Gap(6).Padding(6, 10).Radius(8).Border(1, t.Border).Children(func() {
	ui.Text(c, app.size)
	ui.Icon(c, chevron)
})
sel.Popup(func(panel ui.Element) {
	panel.Margin(4, 0, 0, 0).Padding(4).Radius(10).Background(t.Background).Border(1, t.Border)
	for _, size := range sizes {
		item := sel.Item(size).Padding(6, 10).Radius(6)
		if item.Highlighted() {
			item.Background(t.Accent).TextColor(t.AccentText)
		}
		item.Children(func() { ui.Text(c, size) })
	}
})

基座会环绕获得键盘焦点的元素画出焦点环,与其他可聚焦元素一致;对于 自己绘制焦点环的控件,用 FocusRing(false) 关闭它,并在 FocusVisible 时用 Painter.FocusRing 自行绘制。

你自己的控件 ​

对于带有自己的缓冲区或选区模型的文本控件,使用 HandleTextInput 与保留的文本几何。

从元素构建你自己的控件。元素用 ui.Local 在帧与帧之间保持自己的状态:

go
// Spoiler 隐藏文本,直到被点击。
func Spoiler(c *ui.Context, text string) {
	t := c.Theme()
	box := ui.Box(c).Padding(2, 6).Radius(4).Focusable()
	shown := ui.Local(box, "shown", func() bool { return false })
	if box.Clicked() {
		*shown = !*shown
	}
	if *shown {
		box.Background(t.Surface)
	} else {
		box.Background(t.Text) // 用文本的颜色,把它遮住
	}
	box.Children(func() { ui.Text(c, text) })
}

当控件的输入改变了它所编辑的内容时,它在构建过程中通过其元素上的 SetChanged 报告这一点:此后 Changed 对它返回 true,与内置控件一致。 富文本编辑器就是这样做的。

为辅助技术给控件设置角色、名称和 状态;用其元素上的 Shortcut 获取它需要的 按键,或用 HandleInput 接收每一个 按键;元素没有绘制的内容用 Draw 来画。你自己的覆盖层, 如抽屉、悬停卡片和菜单,用 AttachTo 定位,用 PressedOutside 和 OverlayShortcut 关闭;Modal 把焦点保持在其中一个之内: 参见覆盖层。