QtShell Kompositor

QtShell Compositor zeigt, wie man die QtShell Shell-Erweiterung verwendet.

QtShell Compositor ist ein Wayland-Compositor-Beispiel im Desktop-Stil, das eine vollständige Qt Wayland Compositor implementiert, die das spezielle Shell-Erweiterungsprotokoll namens QtShell verwendet.

Der Compositor ist mit Qt Quick und QML implementiert.

Herstellen der Verbindung

Im Beispiel wird QtShell als einzige Erweiterung des WaylandCompositor Objekts aufgeführt. Das bedeutet, dass jeder Client, der eine Verbindung zum Server herstellt, diese Erweiterung ebenfalls unterstützen muss, d.h. es sollte sich um Qt-Anwendungen handeln, die mit der gleichen Qt-Version wie der Compositor laufen.

QtShell {
    onQtShellSurfaceCreated: (qtShellSurface) => screen.handleShellSurface(qtShellSurface)
}

Wenn ein Client eine Verbindung zur Schnittstelle QtShell herstellt, erzeugt er ein QtShellSurface. Der Compositor wird darüber durch die Ausgabe des Signals qtShellSurfaceCreated informiert. Das Beispiel fügt dann die Shell-Oberfläche zu einem ListModel hinzu, um später einen einfachen Zugriff zu ermöglichen.

property ListModel shellSurfaces: ListModel {}
function handleShellSurface(shellSurface) {
    shellSurfaces.append({shellSurface: shellSurface});
}

Die ListModel wird als Modell für eine Repeater verwendet, die die Qt Quick Elemente erstellt, die für die Anzeige der Client-Inhalte auf dem Bildschirm erforderlich sind.

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)
        }
    }
}

Es wird der lokale Typ Chrome verwendet, der Fensterzustände und -dekorationen verwaltet.

Chrome

Chrome ist der Typ, der sicherstellt, dass der Client-Inhalt sichtbar ist und der auch den Fensterstatus, die Position, die Größe usw. verwaltet. Er verwendet den eingebauten Typ QtShellChrome als Grundlage, der automatisch den Fensterzustand (maximiert, minimiert, Vollbild) und die Fensteraktivierung (Sicherstellung, dass nur ein einziges Fenster aktiv ist) handhabt.

Sein Verhalten kann bis zu einem gewissen Grad angepasst werden, aber es ist auch möglich, die Chrome -Funktionalität von Grund auf neu zu schreiben, indem man stattdessen auf einem grundlegenden Item -Typ aufbaut. QtShellChrome ist eine Komfortklasse, die typisches Compositor-Verhalten bereitstellt und uns die Zeit erspart, diese Logik im Beispiel zu implementieren.

Wie auch immer die Chrome geschrieben wird, sie sollte eine ShellSurfaceItem haben, um die Client-Inhalte zu speichern.

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

Die ShellSurfaceItem ist die visuelle Darstellung des Client-Inhalts in der Qt Quick Szene. Seine Größe sollte in der Regel mit der Größe des Client-Puffers übereinstimmen, andernfalls könnte er gestreckt oder gequetscht aussehen. QtShellChrome wird automatisch auf die Größe von QtShellSurface's windowGeometry angepasst, was der Größe des Client-Puffers plus der Größe der Frame-Ränder entspricht. Die Rahmenränder sind reservierte Bereiche an den Seiten von Chrome, die zur Aufnahme von Fensterdekorationen verwendet werden können.

Die ShellSurfaceItem wird daher an den Fensterdekorationen verankert, um den für den Client-Puffer reservierten Bereich zu füllen.

Fensterdekorationen

Die Fensterdekoration ist in der Regel ein Rahmen um den Inhalt eines Clients, der Informationen (z. B. einen Fenstertitel) und die Möglichkeit der Benutzerinteraktion (wie Größenänderung, Schließen, Verschieben des Fensters usw.) hinzufügt.

Bei QtShell werden Fensterdekorationen immer vom Compositor und nicht vom Client gezeichnet. Damit die Größen und Positionen korrekt kommuniziert werden können, muss QtShell auch wissen, wie viel des Fensters für diese Dekorationen reserviert ist. Dies kann automatisch durch QtShellChrome oder manuell durch Setzen von frameMarginLeft, frameMarginRight, frameMarginTop und frameMarginBottom erfolgen.

Für typische Fälle, in denen es Griffe zur Größenänderung um das Fenster herum und eine Titelleiste am oberen Rand gibt, ist es bequemer, sich auf die Standard-Rahmenränder zu verlassen. Das QtShell Compositor-Beispiel tut dies.

Zunächst erstellen wir Qt Quick Elemente, um die verschiedenen Teile der Fensterdekoration darzustellen. Auf der linken Seite sollte es beispielsweise einen Griff zur Größenänderung geben, den der Benutzer greifen und ziehen kann, um die Größe des Fensters zu ändern.

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
}

Im Beispiel ist dies einfach ein fünf Pixel breites Rechteck, das an der oberen, unteren und linken Seite des Chrome verankert ist.

In ähnlicher Weise fügen wir Qt Quick Elemente hinzu, die die Griffe für die Größenänderung rechts, oben, unten, oben links, oben rechts, unten links und unten rechts darstellen. Wir fügen auch eine Titelleiste hinzu. Wenn die Dekorationen erstellt und korrekt an den Seiten von Chrome verankert wurden, setzen wir die entsprechenden Eigenschaften in QtShellChrome.

leftResizeHandle: leftResizeHandle
rightResizeHandle: rightResizeHandle
topResizeHandle: topResizeHandle
bottomResizeHandle: bottomResizeHandle
bottomLeftResizeHandle: bottomLeftResizeHandle
bottomRightResizeHandle: bottomRightResizeHandle
topLeftResizeHandle: topLeftResizeHandle
topRightResizeHandle: topRightResizeHandle
titleBar: titleBar

Wenn die Dekorationseigenschaften festgelegt sind, wird das Standardverhalten zur Größenänderung und Neupositionierung automatisch hinzugefügt. Der Benutzer kann mit den Griffen zur Größenänderung interagieren, um die Größe des Fensters zu ändern, und die Titelleiste ziehen, um sie neu zu positionieren. Die Rahmenränder von QtShellSurface werden ebenfalls automatisch eingestellt, um die Größe der Dekorationen zu berücksichtigen (solange keine der Rahmenrandeigenschaften explizit eingestellt wurde).

Die Sichtbarkeit der Dekorationen wird von QtShellChrome automatisch auf der Grundlage der Fensterflags von QtShellSurface geregelt.

Fenster-Verwaltung

Als Teil der Dekorationen ist es üblich, Werkzeugschaltflächen zu haben, die den Zustand und die Lebensdauer des Fensters verwalten. Im Beispiel werden diese in die Titelleiste eingefügt.

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()
    }
}

Die Sichtbarkeit jeder Schaltfläche hängt vom Fenster-Flag für diese Schaltfläche ab, und wenn eine Schaltfläche angeklickt wird, rufen wir einfach die entsprechende Methode in QtShellChrome auf. Die Ausnahme ist die Schaltfläche "Schließen", die die Methode sendClose() in QtShellSurface aufruft. Dadurch wird der Client angewiesen, sich selbst zu schließen, und es wird ein geordnetes Herunterfahren der Anwendung gewährleistet.

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()
            }
        }
    }
}

Als zusätzliches Werkzeug zur Fensterverwaltung verfügt das Beispiel über eine "Taskleiste". Dabei handelt es sich lediglich um eine Reihe von Schaltflächen am unteren Rand mit den Fenstertiteln. Die Schaltflächen können angeklickt werden, um Anwendungen zu minimieren und sie in den Vordergrund zu bringen, wenn sie von anderen Fenstern verdeckt werden. Ähnlich wie bei Chrome verwenden wir eine Repeater für die Erstellung der Werkzeugschaltflächen und nutzen die Shell-Oberflächenliste als Vorlage dafür. Der Einfachheit halber enthält das Beispiel keine Behandlung des Überlaufs (wenn es zu viele Anwendungen für die Taskleiste gibt), aber in einem richtigen Compositor ist dies auch etwas, das berücksichtigt werden sollte.

Um schließlich zu verhindern, dass maximierte Anwendungen den von der Taskleiste abgedeckten Bereich ausfüllen, erstellen wir ein spezielles Element zur Verwaltung der Teile des WaylandOutput -Bereichs, die für Client-Fenster zur Verfügung stehen.

Item {
    id: usableArea
    anchors.top: parent.top
    anchors.left: parent.left
    anchors.right: parent.right
    anchors.bottom: taskbar.top
}

Es ist einfach an den Seiten des WaylandOutput verankert, aber sein unterer Anker befindet sich am oberen Rand der Taskleiste.

In Chrome verwenden wir diesen Bereich, um die maximizedRect des Fensters zu definieren.

maximizedRect: Qt.rect(usableArea.x,
                       usableArea.y,
                       usableArea.width,
                       usableArea.height)

Standardmäßig entspricht diese Eigenschaft der gesamten WaylandOutput. In unserem Fall wollen wir jedoch die Taskleiste nicht in den verfügbaren Bereich einbeziehen, also überschreiben wir die Vorgabe.

Beispielprojekt @ code.qt.io

© 2025 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.