在 UI 开发的世界里,交互的流畅度与直观性直接决定着用户体验的上限 ------ 而上下文菜单、对话框、菜单栏、弹出窗口与核心窗体,正是构建这些关键交互的 "基石组件"。它们既是用户与应用沟通的 "入口",也是应用反馈操作结果的 "窗口",从右键唤起的快捷功能,到确认操作的弹窗,再到顶部导航的菜单栏,每一个组件的合理运用,都能让应用更贴合用户习惯、更具专业性。
我在本章,带您聚焦 Slint 框架中这五类核心交互组件,从基础定义、触发逻辑、属性配置,到实际代码示例,层层拆解它们的使用方法。无论你是想实现灵活的右键菜单、适配多平台的对话框,还是搭建规范的菜单栏与轻量弹出窗口,亦或是定制化核心窗体的样式与行为,都能在这里找到清晰的指引。跟着示例动手实践,你将快速掌握这些组件的核心用法,让你的 UI 交互设计既高效又易用。
一、上下文菜单(ContextMenuArea)
使用非视觉元素 ContextMenuArea 可以声明一个区域,用户在该区域内可触发上下文菜单。
上下文菜单的触发方式如下:
- 用户右键点击 ContextMenuArea 覆盖的区域;
- 用户在 ContextMenuArea 内的 FocusScope 获得焦点时,按下键盘上的 "Menu" 键;
- 在 Android 设备上,长按该区域;
- 编程调用 ContextMenuArea 元素的 show () 方法。
ContextMenuArea 的子元素中必须包含一个 Menu 元素(用于定义要显示的菜单),且最多只能有一个 Menu 子元素;其他子元素需为不同类型,会作为常规视觉元素显示。在 Menu 内部可通过放置 MenuItem 或嵌套 Menu 元素来定义菜单结构。
函数
- show (Point):通过编程方式在相对于 ContextMenuArea 元素的指定位置显示上下文菜单。
- close ():如果上下文菜单当前处于打开状态,调用此函数可将其关闭。
属性
- enabled(bool 类型,默认值为 true):当设为禁用状态时,菜单不会显示。
Menu
Menu 元素可放置在 MenuBar、ContextMenuArea 中,或嵌套在另一个 Menu 内部。其可包含以下子元素:
- MenuItem:用于定义单个菜单项;
- 嵌套的 Menu:用于创建子菜单;
- MenuSeparator:用于创建分隔线。
属性
- title(string 类型,默认值为 ""):在菜单栏或父菜单中显示的菜单标签。
- enabled(bool 类型,默认值为 true):禁用状态下,菜单可被选中但无法激活。
- icon(image 类型,默认值为空图像):在父菜单中,显示在标题旁的图标。
MenuItem
MenuItem 代表单个菜单项,必须作为 Menu 元素的子元素存在。
属性
- title(string 类型,默认值为 ""):菜单项显示的标题。
- enabled(bool 类型,默认值为 true):禁用状态下,菜单项可被选中但无法激活。
- checkable(bool 类型,默认值为 true):设为 true 时,菜单项可被勾选;用户激活菜单项时,checked 属性的值会切换。
- checked(bool 类型,默认值为 true):设为 true 时,菜单项标题旁会显示勾选标记。
- icon(image 类型,默认值为空图像):显示在标题旁的图标。
回调
- activated ():菜单项被激活时调用。
MenuSeparator
MenuSeparator 代表菜单中的分隔线,它不能有子元素,也没有属性和回调。位于菜单开头或结尾的 MenuSeparator 不会显示;连续的多个 MenuSeparator 会合并为一个。
示例
qml
export component Example {
ContextMenuArea {
Menu {
MenuItem {
title: @tr("Cut");
activated => { debug("Cut"); }
}
MenuItem {
title: @tr("Copy");
activated => { debug("Copy"); }
}
MenuItem {
title: @tr("Paste");
activated => { debug("Paste"); }
}
MenuSeparator {}
Menu {
title: @tr("Find");
MenuItem {
title: @tr("Find Next");
}
MenuItem {
title: @tr("Find Previous");
}
}
}
}
}
二、对话框(Dialog)
slint
import { StandardButton, Button } from "std-widgets.slint";
export component Example inherits Dialog {
Text {
text: "This is a dialog box";
}
StandardButton { kind: ok; }
StandardButton { kind: cancel; }
Button {
text: "More Info";
dialog-button-role: action;
}
}
slint 对话框示例
Dialog 类似窗口,但它包含的按钮会自动进行布局。
Dialog 应包含一个非按钮的主元素作为子元素。对话框可以有任意数量的 StandardButton 组件,或者其他设置了 dialog-button-role 属性的按钮。这些按钮的排列顺序会根据运行时的目标平台自动调整。
StandardButton 的 kind 属性和按钮的 dialog-button-role 属性需要设置为常量值,不能是任意变量表达式。而且,不允许存在多个同 kind 的 StandardButton。
对于每个没有显式设置回调处理函数的 StandardButton,会自动添加一个<kind>_clicked 回调,方便从原生代码中处理:比如,若有一个 kind 为 cancel 的按钮,就会自动添加 cancel_clicked 回调。这些自动生成的回调都是对应 StandardButton 的 clicked 回调的别名。
属性:
- icon(image 类型,默认值为空图像):在支持的窗口管理器中,显示在标题栏或任务栏上的窗口图标。
- title(string 类型,默认值为 ""):显示在标题栏上的窗口标题。
三、MenuBar
在 Window 中使用 MenuBar 元素可以声明菜单栏的结构,包括实际的菜单和子菜单。
注意事项:
- 一个 Window 中只能有一个 MenuBar 元素,且它不能位于 for 或 if 语句中。
MenuBar 没有属性,但它必须包含 Menu 作为子元素,这些 Menu 代表菜单栏中的顶级条目。
根据平台不同,菜单栏可能是系统原生的,也可能由 Slint 渲染。例如,在 macOS 上,菜单栏会显示在屏幕顶部。Window 的 width 和 height 属性定义的是客户区(不包含菜单栏),Window 子元素的 x 和 y 坐标也是相对于客户区的。
示例:
slint
export component Example inherits Window {
MenuBar {
Menu {
title: @tr("File");
MenuItem {
title: @tr("New");
activated => { file-new(); }
}
MenuItem {
title: @tr("Open");
activated => { file-open(); }
}
}
Menu {
title: @tr("Edit");
MenuItem {
title: @tr("Copy");
}
MenuItem {
title: @tr("Paste");
}
MenuSeparator {}
Menu {
title: @tr("Find");
MenuItem {
title: @tr("Find in document...");
}
MenuItem {
title: @tr("Find Next");
}
MenuItem {
title: @tr("Find Previous");
}
}
}
}
callback file-new();
callback file-open();
// ... 实际窗口内容放在这里
}
四、弹出窗口(PopupWindow)
slint
export component Example inherits Window {
width: 100px;
height: 100px;
popup := PopupWindow {
Rectangle { height:100%; width: 100%; background: yellow; }
x: 20px; y: 20px; height: 50px; width: 50px;
}
TouchArea {
height:100%; width: 100%;
clicked => { popup.show(); }
}
}
使用该元素可以显示类似工具提示或弹出菜单的弹出窗口。
注意:不允许从 PopupWindow 外部访问其内部元素的属性。详见 #4438↗。
属性:
- close-policy(枚举类型 PopupClosePolicy,默认值为 close-on-click)
PopupClosePolicy 包含以下值:
- close-on-click:用户点击或按 ESC 键时,关闭 PopupWindow。
- close-on-click-outside:用户点击弹出窗口外部或按 ESC 键时,关闭 PopupWindow。
- no-auto-close:用户点击时,不会自动关闭 PopupWindow。
默认情况下,点击时 PopupWindow 会关闭。若要阻止这种行为,可将 close-policy 设为 no-auto-close,然后通过 close () 函数手动关闭。
函数:
- show ():在屏幕上显示弹出窗口。
- close ():关闭弹出窗口。当 close-policy 设为 no-auto-close 时,可使用此函数手动关闭。
四、窗体(Window)
Window 是屏幕上可见元素树的根节点。
窗口的几何尺寸会受其布局约束限制:设置 width 会固定窗口宽度;窗口管理器会遵循 min-width 和 max-width,因此窗口不能被调整到超出这两个值的大小。初始宽度可通过 preferred-width 属性控制,高度的相关逻辑与此相同。
可使用 MenuBar 元素为窗口声明菜单栏。
属性:
-
always-on-top(bool 类型,默认值为 false):在支持的窗口管理器中,窗口是否始终显示在其他所有窗口之上。
-
full-screen(bool 类型,默认值:若设置了 'SLINT_FULLSCREEN' 环境变量则为 true,否则为 false):窗口是否以全屏模式显示。全屏模式下,窗口会占据整个屏幕,不可调整大小,且不显示标题栏。
-
background(brush 类型,默认值:取决于样式):窗口的背景画刷。
-
default-font-family(string 类型,默认值为 ""):窗口内未设置 font-family 属性的文本元素,会使用此属性作为默认字体家族。
-
default-font-size(length 类型,默认值为 0):窗口内未设置 font-size 属性的文本元素,会使用此属性作为默认字体大小。该值也是相对字体大小的基准。
-
default-font-weight(int 类型,默认值为 0):窗口内未设置 font-weight 属性的文本元素,会使用此属性作为默认字体粗细。取值范围为 100(最细)到 900(最粗),400 为常规粗细。
-
icon(image 类型,默认值为空图像):在支持的窗口管理器中,显示在标题栏或任务栏上的窗口图标。
-
no-frame(bool 类型,默认值为 false):窗口是否为无边框 / 无框架样式。
-
resize-border-width(length 类型,默认值为 0):注意:目前该属性仅支持 winit。无边框 / 无框架窗口中,可调整大小的边框宽度。
-
title(string 类型,默认值为 ""):显示在标题栏上的窗口标题。