- 桌面应用
- 跨平台
【免费下载链接】nodegui
A library for building cross-platform native desktop applications with Node.js and CSS 🚀. React NodeGui : https://react.nodegui.org and Vue NodeGui: https://vue.nodegui.org
QWindow 是 NodeGui 中面向底层原生窗口句柄的封装类,负责窗口的状态管理(全屏、最大化、最小化、常规显示)、可见性控制、系统级拖动与缩放,以及屏幕信息查询。本文以官方 API 文档为主体,结合仓库源码与原生实现,完整讲解 QWindow 的构造约束、全部方法签名、配套枚举取值,并给出可直接运行的实战示例,帮助读者在自己的 NodeGui 应用中精确控制窗口行为。
QWindow 在 NodeGui 中的定位
在 NodeGui 的对象体系中,QWindow继承自QObject<QWindowSignals>,其完整继承链为:
QObject<QWindowSignals> ↳ QWindow这一继承关系可以在 QWindow API 文档 的 Hierarchy 一节中直接看到,同时反映在 TypeScript 源码export class QWindow extends QObject<QWindowSignals>这一行(见 src/lib/QtGui/QWindow.ts)。
需要特别注意的是,QWindow与QWidget属于不同抽象层次:
QWidget是高级窗口部件,承载界面渲染与布局(FlexLayout、样式等);QWindow是底层平台窗口句柄,聚焦于窗口状态、可见性和系统级交互。
在 NodeGui 中,QWindow实例通常不是直接创建的,而是通过QWidget.windowHandle()获取。该方法返回当前 widget 对应的原生窗口句柄包装(若句柄尚未创建则返回null),实现位于 src/lib/QtWidgets/QWidget.ts:
windowHandle(): QWindow | null { const handle = this.native.windowHandle(); if (handle != null) { return wrapperCache.get<QWindow>(QWindow, handle); } return null; }构造函数与 native 属性
constructor(native: NativeElement)
new QWindow(native: NativeElement): QWindow该构造函数接收一个 NativeElement 类型的原生对象引用,不接受普通参数。TypeScript 封装层在构造时做了严格校验(src/lib/QtGui/QWindow.ts):
constructor(native: NativeElement) { if (!checkIfNativeElement(native)) { throw new Error('QWindow cannot be initialised this way.'); } super(native); }在 C++ 原生层,QWindowWrap的构造函数只接受一个Napi::External<QWindow>类型的外部指针,否则会抛出"NodeGui: QWindowWrap: Bad arguments to constructor."的类型错误(见 qwindow_wrap.cpp)。这意味着不要试图用new QWindow(...)凭空创建窗口——正确做法是通过widget.windowHandle()拿到已有的原生窗口句柄包装。
native
native: NativeElement | null该属性继承自 Component,保存着底层 C++ 对象的引用,是所有原生方法调用的入口。Component 是 NodeGui 世界所有 widget 与 layout 的根基类,其职责包括维护对原生 C++ 实例的引用以及防止子元素被垃圾回收(src/lib/core/Component.ts)。
窗口状态控制
QWindow 提供了最常用的窗口状态切换方法,均为void返回:
| 方法 | 作用 |
|---|---|
showFullScreen() | 以全屏模式显示窗口 |
showMaximized() | 以最大化模式显示窗口 |
showMinimized() | 以最小化模式显示窗口 |
showNormal() | 恢复为正常(非全屏、非最大化)模式显示窗口 |
这些方法直接透传给原生层调用(qwindow_wrap.cpp 中对应的this->instance->showFullScreen()等)。示例:
const window = widget.windowHandle(); if (window) { window.showMaximized(); // 启动即最大化 window.showFullScreen(); // 或直接全屏 window.showNormal(); // 恢复正常窗口 }setWindowState 与 windowState
除了一次性切换,QWindow 还提供基于枚举的状态设置与查询:
setWindowState(state: WindowState): void windowState(): WindowStateWindowState枚举定义于 src/lib/QtEnums/WindowState/index.ts,取值与 Qt 原生一致(均为位标志,可组合):
| 枚举值 | 数值 | 含义 |
|---|---|---|
WindowNoState | 0x00000000 | 无特殊状态 |
WindowMinimized | 0x00000001 | 最小化 |
WindowMaximized | 0x00000002 | 最大化 |
WindowFullScreen | 0x00000004 | 全屏 |
WindowActive | 0x00000008 | 活动窗口 |
在原生层,setWindowState通过static_cast<Qt::WindowState>转换后调用QWindow::setWindowState,windowState则读取并返回Qt::WindowState的无符号整型值(qwindow_wrap.cpp)。
window.setWindowState(WindowState.WindowMinimized); // 最小化 console.log(window.windowState()); // 查询当前状态可见性控制
setVisibility(visibility: Visibility): void visibility(): VisibilityVisibility枚举定义于 src/lib/QtEnums/Visibility/index.ts,对应 Qt 的QWindow::Visibility:
| 枚举值 | 数值 | 含义 |
|---|---|---|
Hidden | 0 | 窗口隐藏 |
AutomaticVisibility | 1 | 可见性由系统自动决定 |
Windowed | 2 | 窗口模式显示 |
Minimized | 3 | 最小化 |
Maximized | 4 | 最大化 |
FullScreen | 5 | 全屏 |
setVisibility在原生层被转换为QWindow::Visibility枚举后调用(qwindow_wrap.cpp)。
window.setVisibility(Visibility.Hidden); // 隐藏窗口 if (window.visibility() === Visibility.Windowed) { console.log('窗口正处于普通窗口模式'); }系统级窗口操作
startSystemMove
startSystemMove(): boolean启动系统级窗口拖动,即从 JavaScript 侧发起与用户按住标题栏拖动等价的操作。返回boolean表示是否成功开始拖动。原生实现为this->instance->startSystemMove()(qwindow_wrap.cpp)。
一个典型场景是自定义标题栏:把普通 widget 伪装成标题栏,通过MouseButtonPress事件触发系统拖动。
startSystemResize
startSystemResize(edges: Edge): boolean启动系统级窗口缩放,edges指定从哪些边缘/角落进行缩放。Edge枚举定义于 src/lib/QtEnums/Edge/index.ts,取值同样为 Qt 位标志:
| 枚举值 | 数值 | 含义 |
|---|---|---|
TopEdge | 0x00001 | 顶部边缘 |
LeftEdge | 0x00002 | 左侧边缘 |
RightEdge | 0x00004 | 右侧边缘 |
BottomEdge | 0x00008 | 底部边缘 |
原生层将参数按static_cast<Qt::Edges>(edge)转换后调用QWindow::startSystemResize,返回boolean表示操作是否成功发起(qwindow_wrap.cpp)。由于Edge是位标志,可以使用|组合多个边缘:
// 从右下角发起系统级缩放 const ok = window.startSystemResize(Edge.RightEdge | Edge.BottomEdge);屏幕信息查询
screen(): QScreen返回该窗口当前所在屏幕的 QScreen 包装对象,可用于获取分辨率、DPI、刷新率、可用几何区域等信息。原生层在 screen 指针为空时返回env.Null()(即 JavaScript 的null),否则通过WrapperCache包装后返回(qwindow_wrap.cpp)。
QScreen的 TypeScript 封装(src/lib/QtGui/QScreen.ts)提供了大量屏幕属性查询方法,包括:
geometry(): QRect、availableGeometry(): QRect—— 屏幕几何区域与可用区域;size(): QSize、availableSize(): QSize—— 屏幕尺寸;devicePixelRatio(): number—— 设备像素比(高分屏适配关键);refreshRate(): number—— 刷新率;physicalDotsPerInch() / logicalDotsPerInch(): number—— 物理/逻辑 DPI;grabWindow(...): QPixmap—— 截取屏幕内容。
const screen = window.screen(); console.log(`DPI: ${screen.logicalDotsPerInch()}`); console.log(`刷新率: ${screen.refreshRate()} Hz`);信号与事件机制
QWindowSignals 信号接口
QWindow的信号接口为QWindowSignals(src/lib/QtGui/QWindow.ts 中的接口定义,完整文档见 qwindowsignals.md):
| 信号 | 回调签名 | 触发时机 |
|---|---|---|
screenChanged | (screen: QScreen) => void | 窗口所在屏幕发生变化时 |
visibilityChanged | (visibility: Visibility) => void | 窗口可见性改变时 |
windowStateChanged | (windowState: WindowState) => void | 窗口状态改变时 |
objectNameChanged | (objectName: string) => void | 继承自 QObjectSignals,objectName 改变时 |
这些信号在原生层由connectSignalsToEventEmitter通过QObject::connect与 Qt 原生信号连接,再由 N-API 回调投递到 JavaScript 的 EventEmitter(qwindow_wrap.cpp)。
addEventListener 与 removeEventListener
addEventListener有两个重载:
- 信号监听:
addEventListener<SignalType extends keyof QWindowSignals>( signalType: SignalType, callback: QWindowSignals[SignalType], options?: EventListenerOptions ): void- QEvent 监听:
addEventListener( eventType: WidgetEventTypes, callback: (event?: NativeRawPointer<"QEvent">) => void, options?: EventListenerOptions ): voidremoveEventListener具有相同结构的两个重载,用于注销监听。EventListenerOptions(eventlisteneroptions.md)中的afterDefault选项仅对 QEvent 生效:设为true时回调会在基类默认处理之后被调用(对应内部_after事件名);默认在默认处理之前调用。底层的订阅/退订逻辑通过native.subscribeToQtEvent与native.unSubscribeToQtEvent完成,实现在 src/lib/core/EventWidget.ts。
// 监听信号 window.addEventListener('windowStateChanged', (state) => { console.log('窗口状态变为:', state); }); window.addEventListener('screenChanged', (screen) => { console.log('窗口切换到了新屏幕'); }); // 注销信号监听 window.removeEventListener('windowStateChanged', handler);eventProcessed 与 setEventProcessed
eventProcessed(): boolean setEventProcessed(isProcessed: boolean): void这一对方法(继承自 EventWidget)用于控制事件是否继续向上传递:在事件处理器中调用setEventProcessed(true)会把当前事件标记为"已处理",此后 NodeGui 的QObject::event()会直接返回true而不调用父类event(),从而阻止该事件的进一步处理;eventProcessed()用于查询当前标记状态(src/lib/core/EventWidget.ts)。
继承自 QObject 的通用能力
QWindow的大部分方法与QObject完全一致(由 qobject.md 继承而来),它们都在 src/lib/QtCore/QObject.ts 中实现:
| 方法 | 签名 | 说明 |
|---|---|---|
_id() | (): number | 返回标识底层 C++ 对象的唯一编号(内存地址哈希),配合setLogCreateQObject()/setLogDestroyQObject()可排查内存问题 |
objectName() / setObjectName(name) | (): string/(name: string): void | 读写对象名 |
property(name)/setProperty(name, value) | (name): QVariant/(name, value): boolean | 通过动态属性读写元数据 |
inherits(className) | (className: string): boolean | 判断对象是否继承自指定类 |
setParent(parent)/parent() | (parent: QObject): void/(): QObject | 设置/获取父对象(注意原生层 QWindow 的 setParent 仅接受 QWindow,见 qwindow_wrap.h 中的特殊实现) |
children() | (): QObject[] | 返回子对象列表 |
startTimer(intervalMS, timerType) | (intervalMS: number, timerType?): number | 启动定时器,返回 timerId;timerType默认TimerType.CoarseTimer |
killTimer(timerId) | (timerId: number): void | 停止定时器 |
delete() | (): void | 立即删除原生对象 |
deleteLater() | (): void | 事件循环空闲时删除原生对象(更安全) |
dumpObjectTree()/dumpObjectInfo() | (): void | 向 stderr 输出对象树/对象信息,用于调试 |
startTimer的timerType参数接受 TimerType 枚举:
| 枚举值 | 数值 | 含义 |
|---|---|---|
PreciseTimer | 0 | 精确定时器(尽量按毫秒触发) |
CoarseTimer | 1 | 粗粒度定时器(默认,可合并以省电) |
VeryCoarseTimer | 2 | 极粗粒度定时器(秒级) |
底层实现:从 TypeScript 到原生 C++
QWindow 的完整调用链可以概括为三层:
- TypeScript 封装层(src/lib/QtGui/QWindow.ts):定义类、签名与信号接口,并通过
wrapperCache.registerWrapper('QWindowWrap', QWindow)与registerNativeWrapFunction注册包装映射; - N-API 原生层(qwindow_wrap.cpp):
QWindowWrap继承自Napi::ObjectWrap<QWindowWrap>、EventWidget与QObject,通过DefineClass注册screen、showFullScreen、startSystemResize等实例方法,并通过QOBJECT_REGISTER_WRAPPER(QWindow, QWindowWrap)登记包装器; - Qt 层:所有调用最终落到
QPointer<QWindow>指向的 Qt 原生QWindow实例上。
值得留意的是 qwindow_wrap.h 中的注释:QWindow 不处理任何 QEvents("We don't use EVENTWIDGET_IMPLEMENTATIONS() here because this class doesn't handle any QEvents"),但它仍通过installEventFilter安装了事件过滤器,将事件转发给EventWidget::event处理;同时它在对象析构时会自动removeEventFilter,避免悬挂引用。screenChanged等信号在回调中通过WrapperCache::instance.getWrapper(env, screen, true)将原生QScreen*转为 JS 包装对象后投递。
由于QWindow只能由windowHandle()等路径产生,原生层构造函数对非法参数直接抛错,这保证了 JavaScript 侧拿到的每一个 QWindow 都对应一个真实存在的原生窗口句柄。
实战示例:利用 QWindow 实现窗口控制
下面是一个完整的 NodeGui 示例,演示如何从QWidget.windowHandle()获取 QWindow,并组合使用状态切换、可见性查询与信号监听:
import { QMainWindow, QPushButton, WindowState, Visibility } from '@nodegui/nodegui'; const win = new QMainWindow(); win.setWindowTitle('QWindow 实战示例'); win.resize(800, 600); const button = new QPushButton(); button.setText('最大化 / 还原'); win.setCentralWidget(button); button.addEventListener('clicked', () => { const handle = win.windowHandle(); if (!handle) return; // 当前不是最大化则最大化,否则还原 if (handle.windowState() !== WindowState.WindowMaximized) { handle.setWindowState(WindowState.WindowMaximized); } else { handle.setWindowState(WindowState.WindowNoState); } }); // 监听窗口状态变化 win.windowHandle()?.addEventListener('windowStateChanged', (state) => { console.log('窗口状态:', state); }); // 全屏切换:按 F 进入全屏,再按退出 const toggleFullScreen = () => { const handle = win.windowHandle(); if (!handle) return; if (handle.visibility() !== Visibility.FullScreen) { handle.showFullScreen(); } else { handle.showNormal(); } }; // 挂载后执行 win.addEventListener('show', toggleFullScreen); // 示例:窗口显示时进入全屏(可按需调整) win.show(); (globalThis as any).win = win; // 保持引用,避免被 GC提示:
windowHandle()可能返回null(原生窗口句柄尚未创建),因此在使用前务必判空。窗口显示后再获取句柄通常是更稳妥的方式。
小结
QWindow 是 NodeGui 中连接 JavaScript 世界与 Qt 原生窗口系统的关键桥梁:通过showFullScreen/showMaximized/showMinimized/showNormal与setWindowState完成状态控制,通过setVisibility/visibility管理可见性,通过startSystemMove/startSystemResize实现系统级拖动与缩放,通过screen()查询屏幕信息,并通过QWindowSignals与addEventListener感知窗口变化。结合 API 文档、TypeScript 封装(src/lib/QtGui/QWindow.ts)与原生实现(src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp),你可以在 NodeGui 应用中精确、可靠地控制窗口行为。
- 桌面应用
- 跨平台
【免费下载链接】nodegui
A library for building cross-platform native desktop applications with Node.js and CSS 🚀. React NodeGui : https://react.nodegui.org and Vue NodeGui: https://vue.nodegui.org
相关推荐
Azul窗口管理:多窗口应用和窗口状态控制的完整教程
Azul窗口管理:多窗口应用和窗口状态控制的完整教程 想要构建现代化的跨平台桌面应用? Azul GUI框架 的 窗口管理 功能让多窗口应用开发变得简单高效!无
桌面应用前端跨平台Polybar窗口管理终极指南:X11窗口属性与操作完全解析
Polybar窗口管理终极指南:X11窗口属性与操作完全解析 Polybar 是一款快速且易用的状态栏工具,专为 X11 窗口系统设计,能够帮助用户高效管理和监
桌面应用GlazeWM窗口管理器终极指南:从基础操作到高级窗口控制
GlazeWM窗口管理器终极指南:从基础操作到高级窗口控制 GlazeWM是一款专为Windows系统设计的平铺式窗口管理器,灵感来源于i3wm和Polybar
桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考