- 桌面应用
- 跨平台
【免费下载链接】react-nodegui
Build performant, native and cross-platform desktop applications with native React + powerful CSS like styling.🚀
导读
RNAction是 react-nodegui 中用于对接 NodeGui 底层QAction的 React 组件,它承担着菜单项、工具栏按钮、系统托盘菜单动作等"命令单元"的声明式表达职责。本文以 RNAction 类 API 文档 为主体,结合仓库源码(RNAction.ts、RNMenu.ts、RNMenuBar.ts、RNSystemTrayIcon.ts)与 ActionProps 接口文档,完整讲解它的构造方式、全部配置属性、事件模型、实例方法以及组件能力边界。读完本文,你将能够在 React JSX 中熟练编写带文字、图标、快捷键、复选状态与分隔线的原生菜单动作,并理解其背后从 React 属性到 Qt 原生调用的完整链路。
一、组件定位:菜单动作在 React 世界的化身
在 Qt 的组件体系中,QAction是一个"可被触发"的抽象动作单元,它本身不直接渲染界面,而是被添加到菜单(QMenu)、菜单栏(QMenuBar)或工具栏中呈现。react-nodegui 通过RNAction将这个能力带入 React 声明式编程模型:
- 它继承自 NodeGui 的
QAction,同时实现RNComponent接口,从而能被 react-nodegui 的 reconciler 统一管理(RNComponent抽象定义见 src/components/config.ts); - 它的静态属性
tagName = "action"是其注册到 reconciler 组件表中的标签名,通过registerComponent完成注册(见 src/components/Action/index.ts)。
从组件层级看,RNAction是典型的多层嵌套结构中的"叶子动作节点":
MenuBar (menubar) ── 只接受 Menu 作为子节点 └── Menu (menu) ── 只接受 Action 作为子节点 └── Action (action) ── 不再接受任何子节点这一约束在源码中有明确体现:RNMenuBar.ts 的appendInitialChild仅接受QMenu;RNMenu.ts 的appendChild只接受RNAction,且通过this.addAction(child)将动作真正挂载到原生菜单上。除此之外,RNAction也可以作为SystemTrayIcon的右键菜单动作使用,详见 RNSystemTrayIcon.ts 中的示例。
二、类层次与构造函数
2.1 继承与实现
QAction ↳ RNAction implements RNComponentRNAction直接继承QAction,这意味着 NodeGui 中QAction提供的全部原生能力(信号、属性系统、快捷键等)在 React 侧依然可用;同时它实现RNComponent接口,具备setProps、appendChild、removeChild等组件生命周期方法(抽象定义见 src/components/config.ts)。
2.2 三种构造方式
API 文档定义了三个构造函数重载(见 rnaction.md):
| 重载 | 说明 |
|---|---|
new RNAction() | 无参构造,创建空白动作实例,是最常见的用法 |
new RNAction(native: NativeElement) | 用已有的 NodeGui 原生元素包装,适用于接管既有原生对象的场景 |
new RNAction(parent: QWidget<any>) | 指定父级 QWidget 构造 |
在实际的 JSX 使用中,绝大多数场景无需手动new——react-nodegui 的组件配置会在createInstance阶段自动执行new RNAction()并调用setProps应用初始属性(见 src/components/Action/index.ts)。
三、实例属性速览
| 属性 | 类型 | 说明 |
|---|---|---|
icon? | QIcon | 可选,动作图标(对应setIcon) |
menu? | QMenu | 可选,与该动作关联的子菜单(对应setMenu,可实现"带子菜单的菜单项") |
native | NativeElement | 底层原生元素引用,由QAction继承而来 |
nodeChildren | Set<Component> | 组件树中的子节点集合(由RNComponent体系提供) |
nodeParent? | Component | 组件树中的父节点引用 |
tagName(静态) | string = "action" | 组件标签名,用于 reconciler 注册与查找 |
四、ActionProps:全部可配置属性
RNAction.setProps接收的参数类型是 ActionProps,其字段在源码 RNAction.ts 中逐一声明,本文汇总如下:
| 属性 | 类型 | 底层调用 | 说明 |
|---|---|---|---|
checkable | boolean | setCheckable | 是否是可复选(toggle)动作,常用于"显示/隐藏某面板"这类开关型菜单项 |
checked | boolean | setChecked | 是否处于选中状态(需先设置checkable才有意义) |
enabled | boolean | setEnabled | 是否启用,禁用后动作不可触发 |
font | QFont | setFont | 动作文字的字体 |
icon | QIcon | setIcon | 动作图标 |
id | string | setObjectName | Qt 对象名,类比 Web 世界的元素 id,可用于 Qt 样式表(stylesheet)中按 id 定向美化 |
on | Partial<QActionSignals> | addEventListener/removeEventListener | 事件监听器映射表,键为信号名或WidgetEventTypes,值为回调函数 |
separator | boolean | setSeparator | 是否作为菜单分隔线渲染 |
shortcut | QKeySequence | setShortcut | 动作的主快捷键 |
shortcutContext | ShortcutContext | setShortcutContext | 快捷键的生效上下文(如全局、仅窗口内等) |
text | string | setText | 动作显示的描述性文字 |
这些属性在源码中通过"setter 映射表"的方式统一落地(RNAction.ts):setActionProps把每个属性名映射为对widget上对应原生方法的调用,最后Object.assign(setter, newProps)一次性应用新属性。React 每次更新组件时,reconciler 都会调用setProps(newProps, oldProps)做新旧属性对比,从而精确地只更新变化的部分。
4.1 事件属性on的增量更新机制
值得注意的是on属性的处理并非简单的整体替换(RNAction.ts):
- 遍历
oldProps.on中的旧监听器:若新属性中不存在同名监听器或监听器引用已变化,则调用removeEventListener解绑旧回调;若引用完全相同,则保留并跳过; - 遍历剩余的新监听器,逐个
addEventListener注册。
这一机制保证了在 React 重渲染时不会重复注册或泄漏事件监听,也是 事件处理指南 中强调"尽量保持on对象引用稳定"的原因。
五、在 JSX 中组合使用:菜单、菜单栏与系统托盘
5.1 基础菜单:文字、图标与快捷键
import React from "react"; import { Renderer, Window, Menu, MenuBar, Action } from "@nodegui/react-nodegui"; import { QIcon, QKeySequence } from "@nodegui/nodegui"; import path from "path"; const copyIcon = new QIcon(path.join(__dirname, "copy.png")); const App = () => { return ( <Window> <MenuBar> <Menu title="编辑"> <Action text="复制" icon={copyIcon} shortcut={new QKeySequence("Ctrl+C")} /> <Action text="粘贴" shortcut={new QKeySequence("Ctrl+V")} /> </Menu> </MenuBar> </Window> ); }; Renderer.render(<App />);要点说明:
MenuBar只接受Menu作为子节点(RNMenuBar.ts);Menu只接受Action作为子节点,非RNAction的子组件会被拒绝并打印警告(RNMenu.ts);- 子菜单能力:可通过
menu属性(setMenu)为动作挂载二级菜单,实现多级菜单结构。
5.2 复选动作与分隔线
<Menu title="视图"> <Action text="显示状态栏" checkable checked /> <Action text="显示工具栏" checkable /> <Action separator /> <Action text="退出" enabled={false} /> </Menu>checkable让菜单项变为可切换的复选项,checked控制当前开关状态;separator渲染一条分隔线,用于逻辑分组;enabled={false}使动作变灰不可点击,适合展示尚不可用的功能。
5.3 系统托盘右键菜单
RNAction的另一典型场景是系统托盘菜单。RNSystemTrayIcon.ts 的源码示例展示了托盘图标 + 动作的组合写法:
import { QIcon, QAction } from "@nodegui/nodegui"; import { Menu, Renderer, SystemTrayIcon, Window } from "@nodegui/react-nodegui"; const icon = new QIcon(path.join(__dirname, "../extras/assets/nodegui.png")); const action = new QAction(); action.setText("Hello"); action.addEventListener("triggered", () => { console.log("hello"); }); const App = () => { return ( <Window> <SystemTrayIcon icon={icon} tooltip="Hello World" visible> <Menu actions={[action]} /> </SystemTrayIcon> </Window> ); }; Renderer.render(<App />);注意这里展示了QAction在 React 组件之外的命令式用法:直接new QAction()、setText、addEventListener("triggered", ...),再以actions属性传入Menu。这也是理解"声明式组件内部包着命令式原生对象"这一架构的好例子。
六、事件处理:信号与 QEvent
RNAction的事件能力继承自QAction,通过addEventListener提供两类事件的订阅(详见 事件处理指南):
6.1 信号(Signals)
信号是各控件特有的语义事件,QAction最重要的信号是triggered(动作被触发)。API 文档给出了通用范式:
const button = new QPushButton(); button.addEventListener('clicked', (checked) => console.log("clicked")); // clicked 来自 QPushButtonSignals 接口;对 QAction 则是 QActionSignals 的 triggered在 JSX 中,更推荐通过on属性声明式注册,并配合useEventHandler钩子保持监听器引用稳定(避免每次渲染都触发解绑/重绑):
import { QActionSignals } from "@nodegui/nodegui"; import { useEventHandler } from "@nodegui/react-nodegui"; const App = () => { const actionHandler = useEventHandler<QActionSignals>({ triggered: () => console.log("action triggered"), }, []); return ( <Menu title="文件"> <Action text="打开" on={actionHandler} /> </Menu> ); };useEventHandler本质是useMemo的封装,保证actionHandler在多次渲染间引用不变,从而避免on属性变化导致的重复监听(handle-events.md)。使用 TypeScript 时,QActionSignals接口还能为事件回调提供自动补全与类型检查。
6.2 通用 QEvent
QEvent 是所有 Qt 控件共享的底层事件(鼠标、键盘、焦点等),统一由WidgetEventTypes枚举表示:
import { WidgetEventTypes } from "@nodegui/nodegui"; const actionHandler = useEventHandler<QActionSignals>({ [WidgetEventTypes.HoverEnter]: () => console.log("hovered"), }, []);信号与 QEvent 可以通过同一个on属性混用,React NodeGui 内部会将二者分别路由到对应的原生监听通道。
七、实例方法速查
除事件 API 外,RNAction还继承了大量QAction/QObject级方法,API 文档逐一列出(rnaction.md):
7.1 状态查询类
| 方法 | 返回类型 | 说明 |
|---|---|---|
isCheckable() | boolean | 是否为可复选动作 |
isChecked() | boolean | 是否处于选中态 |
isSeparator() | boolean | 是否为分隔线动作 |
inherits(className) | boolean | 判断对象是否继承自指定 Qt 类 |
objectName() | string | 获取 Qt 对象名 |
property(name) | QVariant | 读取 Qt 动态属性 |
7.2 属性设置类
| 方法 | 参数 | 说明 |
|---|---|---|
setCheckable(isCheckable) | boolean | 设置可复选 |
setChecked(isChecked) | boolean | 设置选中态 |
setEnabled(enabled) | boolean | 设置可用性 |
setFont(font) | QFont | 设置字体 |
setIcon(icon) | QIcon | 设置图标 |
setMenu(menu) | QMenu | 挂载子菜单 |
setObjectName(objectName) | string | 设置 Qt 对象名(样式表定位) |
setProperty(name, value) | string+QVariantType | 写 Qt 动态属性,返回是否成功 |
setSeparator(isSeparator) | boolean | 设置分隔线 |
setShortcut(keysequence) | QKeySequence | 设置主快捷键 |
setShortcutContext(shortcutContext) | ShortcutContext | 设置快捷键生效上下文 |
setText(text) | string | 设置文字 |
setNodeParent(parent?) | Component | 设置组件树父节点 |
7.3 事件与树操作类
addEventListener/removeEventListener:均有信号版(SignalType: keyof QActionSignals)与 QEvent 版(WidgetEventTypes)两个重载,签名见 rnaction.md;appendChild、appendInitialChild、insertBefore、removeChild:这四个树操作方法在RNAction中一律调用throwUnsupported(this)抛出"不支持的操作"错误(RNAction.ts,错误实现见 src/utils/helpers.ts),因为动作节点本身不允许再嵌套子节点。
八、组件能力边界与注意事项
- Action 不接受子节点:若在
<Action>内嵌套其他组件,运行时会抛出Unsupported operation performed in RNAction错误。请把菜单结构组织为MenuBar → Menu → Action的扁平树。 - MenuBar 的动态更新暂不支持:
RNMenuBar的insertBefore与removeChild目前会打印提示并抛出异常(RNMenuBar.ts),即运行期增删菜单项尚未实现,应尽量在初始渲染时确定菜单结构。 - 子菜单通过属性而非子节点实现:二级菜单应通过
Action的menu属性(setMenu)挂接,而不是把Menu嵌套在Action内部。 - 保持
on监听器引用稳定:优先使用useEventHandler钩子,否则每次渲染都会因on对象变化而执行监听器的移除与重新注册。
总结
RNAction是 react-nodegui 中"动作"概念的完整封装:在类层次上继承 NodeGuiQAction、实现RNComponent,在声明层面提供覆盖文字、图标、快捷键、复选、分隔线、子菜单、事件监听等 11 个ActionProps配置项,在运行时通过 setter 映射表与事件 diff 机制将 React 属性变化精确同步到 Qt 原生对象。理解了RNAction,也就理解了 react-nodegui 菜单体系的骨架——MenuBar → Menu → Action的原生菜单链路,以及系统托盘等"命令触发"场景的声明式写法。相关代码可进一步研读 src/components/Action/RNAction.ts、src/components/Action/index.ts、src/components/Menu/RNMenu.ts 与配套的 ActionProps 文档、事件处理指南。
- 桌面应用
- 跨平台
【免费下载链接】react-nodegui
Build performant, native and cross-platform desktop applications with native React + powerful CSS like styling.🚀
相关推荐
NodeGui QDate 类完全指南:在 Node.js 中驾驭 Qt 日期核心 API
NodeGui QDate 类完全指南:在 Node.js 中驾驭 Qt 日期核心 API QDate 是 NodeGui 提供的日历日期值类型封装,对应 Qt
桌面应用跨平台react-nodegui 菜单组件深入指南:RNMenu 完整 API 解析与应用实践
react nodegui 菜单组件深入指南:RNMenu 完整 API 解析与应用实践 本文以 react nodegui 仓库中 RNMenu 类 API
桌面应用跨平台react-nodegui 菜单组件 MenuProps 完整指南:属性详解与源码级实现解析
react nodegui 菜单组件 MenuProps 完整指南:属性详解与源码级实现解析 本篇技术指南以 react nodegui(用原生 React 构
桌面应用跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考