QtShell 组合器
QtShell 组合器展示了如何使用 QtShell 壳扩展。
QtShell 组合器是一个桌面风格的 Wayland 组合器示例,它实现了一个完整的 Qt Wayland 组合器,该组合器使用了名为 QtShell 的专用 shell 扩展协议。
该组合器是用 Qt Quick 和 QML 实现的。
建立连接
此示例将 QtShell 列为 WaylandCompositor 对象的唯一扩展。这意味着任何连接到服务器的客户端也必须支持此扩展,因此它们应该是运行与组合器相同版本 Qt 的 Qt 应用程序。
QtShell { onQtShellSurfaceCreated: (qtShellSurface) => screen.handleShellSurface(qtShellSurface) }
当客户端连接到 QtShell 接口时,它会创建一个 QtShellSurface。组合器通过 qtShellSurfaceCreated 信号的发射被通知。然后示例将壳表面添加到一个 ListModel 中,以便以后轻松访问。
property ListModel shellSurfaces: ListModel {} function handleShellSurface(shellSurface) { shellSurfaces.append({shellSurface: shellSurface}); }
ListModel 被用作 Repeater 的模型,该模型创建了用于在屏幕上显示客户端内容的 Qt Quick 项目。
Repeater { id: chromeRepeater model: output.shellSurfaces // Chrome displays a shell surface on the screen (See Chrome.qml) Chrome { shellSurface: modelData onClientDestroyed: { output.shellSurfaces.remove(index) } } }
它使用本地的 Chrome
类型,该类型处理窗口状态和装饰。
Chrome
Chrome
类型确保客户端内容可见,并处理窗口状态、位置、大小等。它使用内置的 QtShellChrome 作为基础,该基础自动处理窗口状态(最大化、最小化、全屏)和窗口激活(确保在同一时间只有一个活动窗口)。
其行为可以在一定程度上进行定制,但也可以从头开始编写 Chrome
功能,从一个基本的 Item 类型构建。 QtShellChrome 是一个便利类,它提供了典型的组合器行为,并为我们节省了在示例中实现此逻辑的时间。
但是无论 Chrome
如何编写,它都应该有一个 ShellSurfaceItem 来容纳客户端内容。
ShellSurfaceItem { id: shellSurfaceItemId anchors.top: titleBar.bottom anchors.bottom: bottomResizeHandle.top anchors.left: leftResizeHandle.right anchors.right: rightResizeHandle.left moveItem: chrome staysOnBottom: shellSurface.windowFlags & Qt.WindowStaysOnBottomHint staysOnTop: !staysOnBottom && shellSurface.windowFlags & Qt.WindowStaysOnTopHint } shellSurfaceItem: shellSurfaceItemId
ShellSurfaceItem 是在 Qt Quick 场景中客户端内容的可视化表示。它的大小通常应该与客户端缓存的尺寸相匹配,否则可能会看起来拉伸或挤压。QtShellChrome 会自动调整大小以匹配 QtShellSurface 的 windowGeometry(客户端缓冲区的大小加上边框的尺寸)。边框的边距是 Chrome
两侧的保留区域,可以用作包含窗口装饰。
因此,ShellSurfaceItem 锚定到窗口装饰,以填充客户端缓存保留的区域。
窗口装饰
窗口装饰通常是围绕客户端内容的一个框架,它增加了信息(如窗口标题)和用户交互的可能性(如调整大小、关闭、移动窗口等。)
使用 QtShell,窗口装饰总是由合成器绘制,而不是由客户端绘制。为了正确传达尺寸和位置,QtShell 还需要知道多少窗口被用于这些装饰。这可以通过 QtShellChrome 自动处理,或者通过设置 frameMarginLeft、frameMarginRight、frameMarginTop 和 frameMarginBottom 手动处理。
在典型的窗口周围有调整大小控件和顶部标题栏的案例中,依赖默认的边框边距更方便。QtShell 合成器示例就是这样做的。
首先,我们创建 Qt Quick 元素来表示窗口装饰的不同部分。例如,在左侧,应该有一个用户可以抓取并拖动以调整窗口大小的调整大小控件。
Rectangle { id: leftResizeHandle color: "gray" width: visible ? 5 : 0 anchors.topMargin: 5 anchors.bottomMargin: 5 anchors.left: parent.left anchors.top: parent.top anchors.bottom: parent.bottom }
在示例中,我们简单地将其做成一个五像素宽的矩形,锚定到 Chrome
的顶部、底部和左侧。
类似地,我们添加代表右边、上边、下边、左上角、右上角、左下角和右下角的调整大小控件的 Qt Quick 元素。我们还添加了一个标题栏。当装饰已创建并正确锚定到 Chrome
的两侧时,我们在 QtShellChrome 中设置相应的属性。
leftResizeHandle: leftResizeHandle rightResizeHandle: rightResizeHandle topResizeHandle: topResizeHandle bottomResizeHandle: bottomResizeHandle bottomLeftResizeHandle: bottomLeftResizeHandle bottomRightResizeHandle: bottomRightResizeHandle topLeftResizeHandle: topLeftResizeHandle topRightResizeHandle: topRightResizeHandle titleBar: titleBar
设置装饰属性后,将自动添加默认的调整大小和重新定位行为。用户将能够与调整大小控件交互以调整窗口大小,并拖动标题栏以重新定位它。QtShellSurface 的边框边距也将自动设置,以考虑到装饰的大小(只要没有明确设置任何边框边距属性。)
装饰的可见性将由 QtShellChrome 根据 QtShellSurface 的窗口标志自动处理。
窗口管理
作为装饰的一部分,通常会有工具按钮来管理窗口的状态和生命周期。在示例中,这些按钮被添加到标题栏中。
RowLayout { id: rowLayout anchors.right: parent.right anchors.rightMargin: 5 ToolButton { text: "-" Layout.margins: 5 visible: (chrome.windowFlags & Qt.WindowMinimizeButtonHint) != 0 onClicked: { chrome.toggleMinimized() } } ToolButton { text: "+" Layout.margins: 5 visible: (chrome.windowFlags & Qt.WindowMaximizeButtonHint) != 0 onClicked: { chrome.toggleMaximized() } } ToolButton { id: xButton text: "X" Layout.margins: 5 visible: (chrome.windowFlags & Qt.WindowCloseButtonHint) != 0 onClicked: shellSurface.sendClose() } }
每个按钮的可见性取决于该按钮的窗口标志,当点击每个按钮时,我们只需调用QtShellChrome中相应的函数。例外是“关闭”按钮,它会在QtShellSurface中调用sendClose
()方法。这将指示客户端关闭自己,并确保应用程序的优雅关闭。
Row { id: taskbar height: 40 anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom Repeater { anchors.fill: parent model: output.shellSurfaces ToolButton { anchors.verticalCenter: parent.verticalCenter text: modelData.windowTitle onClicked: { var item = chromeRepeater.itemAt(index) if ((item.windowState & Qt.WindowMinimized) != 0) item.toggleMinimized() chromeRepeater.itemAt(index).activate() } } } }
作为额外的窗口管理工具,示例中有一个“任务栏”。这只是在底部具有窗口标题的工具按钮行。可以通过点击这些按钮来取消最小化应用程序并将它们带到前面,如果它们被其他窗口遮挡。同样地,与Chrome
一样,我们使用一个Repeater来创建工具按钮,并使用shell表面列表作为模型。为了简单起见,示例没有对溢出(当任务栏中的应用程序过多时)进行处理,但在一个合适的合成器中,这也应该是需要考虑的。
最后,为了避免最大化的应用程序扩展到填充任务栏覆盖的区域,我们创建一个特殊的项目来管理客户端窗口可用的WaylandOutput区域的一部分。
Item { id: usableArea anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.bottom: taskbar.top }
它只是锚点于WaylandOutput的两侧,但其底部锚点在任务栏的顶部。
在Chrome
中,我们使用这个区域来定义窗口的maximizedRect属性。
maximizedRect: Qt.rect(usableArea.x, usableArea.y, usableArea.width, usableArea.height)
默认情况下,此属性将匹配整个WaylandOutput。然而,在我们的案例中,我们不希望任务栏包含在可用区域中,因此我们覆盖了默认设置。
© 2024 The Qt Company Ltd. 本文档的贡献是各自所有者的版权。本文档是根据自由软件基金会发布的GNU自由文档许可证版本1.3的条款许可的。Qt及其相关标志是芬兰乃至全世界的商标,归The Qt Company Ltd.所有。所有其他商标均归其所有者所有。