QtShell Compositor
QtShell Compositor 演示了如何使用QtShell shell扩展。
QtShell Compositor 是一个桌面风格的 Wayland 合成器示例,它实现了一个完整的Qt Wayland Compositor ,该系统使用名为QtShell 的专用shell 扩展协议。

该合成器采用Qt Quick 和QML实现。
建立连接
该示例将QtShell 列为WaylandCompositor 对象的唯一扩展。这意味着任何连接到服务器的客户端都必须支持此扩展,因此它们应为与合成器运行在同一版本Qt上的Qt应用程序。
QtShell {
onQtShellSurfaceCreated: (qtShellSurface) => screen.handleShellSurface(qtShellSurface)
}当客户端连接到 `QtShell ` 接口时,它会创建一个 `QtShellSurface` 对象。通过发射 `qtShellSurfaceCreated ` 信号,将此情况通知给合成器。随后,示例将 shell 表面添加到 `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
onShellSurfaceChanged: {
if (!shellSurface)
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: shellSurfaceItemIdShellSurfaceItem 是Qt Quick 场景中客户端内容的视觉呈现。其大小通常应与客户端缓冲区的大小一致,否则可能会出现拉伸或压缩的现象。QtShellChrome 将自动调整大小以适应QtShellSurface 的windowGeometry ,即客户端缓冲区大小加上框架边距的大小。 帧边距是Chrome 两侧预留的区域,可用于容纳窗口装饰。
因此,ShellSurfaceItem 会锚定在窗口装饰上,以填满为客户端缓冲区预留的区域。
窗口装饰
窗口装饰通常是围绕客户端内容的一个边框,它不仅提供信息(如窗口标题),还支持用户交互(如调整大小、关闭、移动窗口等)。
在QtShell 中,窗口装饰总是由合成器绘制,而不是由客户端绘制。为了正确传递大小和位置信息,QtShell 还需要知道窗口中为这些装饰预留了多少空间。这可以通过QtShellChrome 自动处理,也可以通过手动设置frameMarginLeft 、frameMarginRight 、frameMarginTop 和frameMarginBottom 来实现。
对于典型的窗口场景——即窗口四周带有调整大小的控点,顶部设有标题栏——使用默认的框架边距更为便捷。QtShell 中的 Compositor 示例便是如此实现的。
首先,我们创建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
}在示例中,我们只需将其设为一个宽度为 5 像素的矩形,并将其锚定在 `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 来创建工具按钮,并以此作为模型。为简化起见,本示例未处理溢出情况(即任务栏中应用程序过多时),但在一个规范的合成器中,这也应予以考虑。
最后,为了避免最大化应用程序扩展到填满任务栏所占区域,我们创建了一个特殊项来管理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 区域一致。但在本例中,我们不希望将任务栏包含在可用区域内,因此需要覆盖默认值。
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.