Sur cette page

Compositeur QtShell

QtShell Compositor montre comment utiliser l'extension shell QtShell.

QtShell Compositor est un exemple de compositeur Wayland de type bureau qui met en œuvre une version complète de Qt Wayland Compositor qui utilise le protocole d'extension shell spécialisé appelé QtShell.

Le compositeur est implémenté avec Qt Quick et QML.

Établir la connexion

L'exemple indique que QtShell est la seule extension de l'objet WaylandCompositor. Cela signifie que tout client se connectant au serveur doit également prendre en charge cette extension, et qu'il doit donc s'agir d'applications Qt fonctionnant avec la même version de Qt que le compositeur.

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

Lorsqu'un client se connecte à l'interface QtShell, il crée un objet QtShellSurface. Le compositeur en est informé par l'émission du signal qtShellSurfaceCreated. L'exemple ajoute ensuite la surface de la coquille à un ListModel pour faciliter l'accès ultérieur.

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

Le ListModel est utilisé comme modèle pour un Repeater qui crée les éléments Qt Quick nécessaires pour afficher le contenu du client à l'écran.

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

Il utilise le type local Chrome, qui gère les états et les décorations de la fenêtre.

Chrome

Le type Chrome garantit que le contenu du client est visible et gère également l'état de la fenêtre, la position, la taille, etc. Il utilise comme base le type intégré QtShellChrome, qui gère automatiquement l'état de la fenêtre (maximisée, minimisée, plein écran) et l'activation de la fenêtre (en veillant à ce qu'une seule fenêtre soit active à la fois).

Son comportement peut être personnalisé dans une certaine mesure, mais il est également possible d'écrire la fonctionnalité Chrome à partir de zéro, en construisant à la place un type de base Item. QtShellChrome est une classe de commodité qui fournit le comportement typique d'un compositeur, et nous évite d'avoir à implémenter cette logique dans l'exemple.

Quelle que soit la façon dont Chrome est écrit, il doit avoir un ShellSurfaceItem pour contenir le contenu du client.

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

Le ShellSurfaceItem est la représentation visuelle du contenu du client dans la scène Qt Quick. Sa taille doit généralement correspondre à la taille de la mémoire tampon du client, sinon elle risque d'être étirée ou comprimée. QtShellChrome sera automatiquement dimensionné en fonction du windowGeometry de QtShellSurface, qui correspond à la taille de la mémoire tampon du client plus la taille des marges de la trame. Les marges du cadre sont des zones réservées sur les côtés de Chrome qui peuvent être utilisées pour contenir des décorations de fenêtre.

Le site ShellSurfaceItem est donc ancré aux décorations de la fenêtre pour remplir la zone réservée à la mémoire tampon du client.

Décorations de fenêtres

La décoration de fenêtre est généralement un cadre autour du contenu d'un client qui ajoute des informations (comme un titre de fenêtre) et la possibilité d'une interaction avec l'utilisateur (comme le redimensionnement, la fermeture, le déplacement de la fenêtre, etc.)

Avec QtShell, les décorations de fenêtre sont toujours dessinées par le compositeur et non par le client. Pour que les tailles et les positions soient communiquées correctement, QtShell doit également savoir quelle partie de la fenêtre est réservée à ces décorations. Cela peut être géré automatiquement par QtShellChrome, ou manuellement, en paramétrant frameMarginLeft, frameMarginRight, frameMarginTop et frameMarginBottom.

Dans les cas typiques où il y a des poignées de redimensionnement autour de la fenêtre et une barre de titre en haut, il est plus pratique de s'appuyer sur les marges de cadre par défaut. C'est ce que fait l'exemple du compositeur QtShell.

Tout d'abord, nous créons des éléments Qt Quick pour représenter les différentes parties des décorations de la fenêtre. Sur le côté gauche, par exemple, il doit y avoir une poignée de redimensionnement que l'utilisateur peut saisir et faire glisser pour redimensionner la fenêtre.

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
}

Dans l'exemple, il s'agit simplement d'un rectangle de cinq pixels de large, ancré en haut, en bas et sur le côté gauche de la page Chrome.

De même, nous ajoutons des éléments Qt Quick qui représentent les poignées de redimensionnement droite, supérieure, inférieure, supérieure-gauche, supérieure-droite, inférieure-gauche et inférieure-droite. Nous ajoutons également une barre de titre. Lorsque les décorations ont été créées et ancrées correctement sur les côtés de Chrome, nous définissons les propriétés correspondantes dans QtShellChrome.

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

Lorsque les propriétés des décorations sont définies, le comportement de redimensionnement et de repositionnement par défaut est ajouté automatiquement. L'utilisateur pourra interagir avec les poignées de redimensionnement pour redimensionner la fenêtre et faire glisser la barre de titre pour la repositionner. Les marges du cadre du site QtShellSurface seront également définies automatiquement pour tenir compte de la taille des décorations (à condition qu'aucune des propriétés de marge du cadre n'ait été définie explicitement).

La visibilité des décorations sera gérée automatiquement par QtShellChrome en fonction des drapeaux de fenêtre de QtShellSurface.

Gestion des fenêtres

Dans le cadre des décorations, il est courant d'avoir des boutons d'outils qui gèrent l'état et la durée de vie de la fenêtre. Dans l'exemple, ces boutons sont ajoutés à la barre de titre.

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

La visibilité de chaque bouton est conditionnée par l'indicateur de fenêtre de ce bouton, et lorsque l'on clique sur l'un d'eux, on appelle simplement la méthode correspondante dans QtShellChrome. L'exception est le bouton "close", qui appelle la méthode sendClose() dans QtShellSurface. Cette méthode demande au client de se fermer, ce qui garantit un arrêt en douceur de l'application.

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

Comme outil supplémentaire de gestion des fenêtres, l'exemple dispose d'une "barre des tâches". Il s'agit simplement d'une rangée de boutons d'outils en bas avec les titres des fenêtres. Il est possible de cliquer sur ces boutons pour réduire la taille des applications et les mettre en avant si elles sont masquées par d'autres fenêtres. Comme pour le site Chrome, nous utilisons un site Repeater pour créer les boutons d'outils et nous nous servons de la liste des surfaces de la coquille comme modèle. Pour des raisons de simplicité, l'exemple ne gère pas le débordement (lorsqu'il y a trop d'applications pour la barre des tâches), mais dans un compositeur approprié, c'est également un élément à prendre en compte.

Enfin, pour éviter que les applications maximisées ne remplissent la zone couverte par la barre des tâches, nous créons un élément spécial pour gérer les parties de la zone WaylandOutput qui sont disponibles pour les fenêtres clientes.

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

Il est simplement ancré sur les côtés de WaylandOutput, mais son ancrage inférieur se trouve en haut de la barre des tâches.

Dans Chrome, nous utilisons cette zone pour définir la maximizedRect de la fenêtre.

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

Par défaut, cette propriété correspondra à l'intégralité de WaylandOutput. Dans notre cas, cependant, nous ne voulons pas inclure la barre des tâches dans la zone disponible, c'est pourquoi nous remplaçons la valeur par défaut.

Exemple de projet @ code.qt.io

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