En esta página

Compositor QtShell

QtShell Compositor muestra cómo utilizar la extensión de shell QtShell.

QtShell Compositor es un ejemplo de compositor Wayland de escritorio que implementa un completo Qt Wayland Compositor que utiliza el protocolo especializado de extensión de shell llamado QtShell.

El compositor está implementado con Qt Quick y QML.

Estableciendo la conexión

El ejemplo lista QtShell como la única extensión del objeto WaylandCompositor. Esto significa que cualquier cliente que se conecte al servidor también debe soportar esta extensión, por lo que deben ser aplicaciones Qt que se ejecuten con la misma versión de Qt que el compositor.

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

Cuando un cliente se conecta a la interfaz QtShell, crea un QtShellSurface. El compositor es notificado de ello mediante la emisión de la señal qtShellSurfaceCreated. A continuación, el ejemplo añade la superficie de la cáscara a un ListModel para facilitar el acceso posterior.

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

El ListModel se utiliza como modelo para un Repeater que crea los elementos Qt Quick necesarios para mostrar el contenido del cliente en pantalla.

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

Utiliza el tipo local Chrome, que maneja los estados y decoraciones de la ventana.

Chrome

El Chrome es el tipo que asegura que los contenidos del cliente sean visibles y también maneja el estado de la ventana, posición, tamaño, etc. Utiliza el QtShellChrome incorporado como base, que gestiona automáticamente el estado de la ventana (maximizada, minimizada, pantalla completa) y la activación de la ventana (garantizando que sólo haya una ventana activa en cada momento).

Su comportamiento puede personalizarse hasta cierto punto, pero también es posible escribir la funcionalidad de Chrome desde cero, construyendo en su lugar a partir de un tipo básico Item. QtShellChrome es una clase de conveniencia que proporciona el comportamiento típico del compositor, y nos ahorra el tiempo de implementar esta lógica en el ejemplo.

Independientemente de cómo se escriba Chrome, debería tener un ShellSurfaceItem para contener el contenido del cliente.

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

El ShellSurfaceItem es la representación visual de los contenidos del cliente en la escena Qt Quick. Por lo general, su tamaño debe coincidir con el tamaño de la memoria intermedia del cliente, de lo contrario puede parecer estirada o comprimida. QtShellChrome tendrá automáticamente el tamaño de QtShellSurface's windowGeometry, que es el tamaño de la memoria intermedia del cliente más el tamaño de los márgenes del marco. Los márgenes del marco son áreas reservadas a los lados de Chrome que pueden utilizarse para contener decoraciones de la ventana.

Por lo tanto, el ShellSurfaceItem se ancla a las decoraciones de la ventana para rellenar el área reservada para la memoria intermedia del cliente.

Decoraciones de ventana

La decoración de ventana suele ser un marco alrededor del contenido de un cliente que añade información (como un título de ventana) y la posibilidad de interacción del usuario (como cambiar el tamaño, cerrar, mover la ventana, etc.).

Con QtShell, las decoraciones de las ventanas siempre son dibujadas por el compositor y no por el cliente. Para que los tamaños y las posiciones se comuniquen correctamente, QtShell también necesita saber qué parte de la ventana está reservada para estas decoraciones. Esto puede ser manejado automáticamente por QtShellChrome, o manualmente, configurando frameMarginLeft, frameMarginRight, frameMarginTop y frameMarginBottom.

Para los casos típicos en los que hay tiradores de redimensionamiento alrededor de la ventana y una barra de título en la parte superior, es más conveniente confiar en los márgenes del marco por defecto. El ejemplo de QtShell Compositor hace esto.

Primero, creamos elementos Qt Quick para representar las diferentes partes de la decoración de la ventana. En el lado izquierdo, por ejemplo, debe haber un asa de redimensionamiento que el usuario pueda agarrar y arrastrar para redimensionar la ventana.

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
}

En el ejemplo, simplemente lo convertimos en un rectángulo de cinco píxeles de ancho, anclado a la parte superior, inferior e izquierda de Chrome.

Del mismo modo, añadimos elementos a Qt Quick que representan los tiradores de redimensionamiento derecho, superior, inferior, superior izquierdo, superior derecho, inferior izquierdo e inferior derecho. También añadimos una barra de título. Cuando las decoraciones han sido creadas y ancladas correctamente a los lados de Chrome, establecemos las propiedades correspondientes en QtShellChrome.

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

Una vez definidas las propiedades de la decoración, se añadirá automáticamente el comportamiento predeterminado de redimensionamiento y reposicionamiento. El usuario podrá interactuar con los tiradores de redimensionamiento para cambiar el tamaño de la ventana, y arrastrar la barra de título para reposicionarla. Los márgenes del marco de QtShellSurface también se ajustarán automáticamente para tener en cuenta el tamaño de las decoraciones (siempre que no se haya ajustado explícitamente ninguna de las propiedades de los márgenes del marco).

La visibilidad de las decoraciones será gestionada automáticamente por QtShellChrome basándose en los indicadores de ventana de QtShellSurface.

Gestión de ventanas

Como parte de las decoraciones, es común tener botones de herramientas que gestionan el estado de la ventana y su vida útil. En el ejemplo, éstos se añaden a la barra de título.

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 visibilidad de cada botón está condicionada a la bandera de ventana para ese botón, y cuando se hace clic en cada uno de ellos, simplemente llamamos al método correspondiente en QtShellChrome. La excepción es el botón "cerrar", que llama al método sendClose() en QtShellSurface. Esto indica al cliente que se cierre, y asegura un cierre elegante de la aplicación.

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

Como herramienta adicional de gestión de ventanas, el ejemplo tiene una "barra de tareas". Esto es sólo una fila de botones de herramientas en la parte inferior con los títulos de las ventanas. Los botones se pueden pulsar para minimizar las aplicaciones y traerlas al frente si están ocultas por otras ventanas. De forma similar a Chrome, utilizamos Repeater para crear los botones de herramientas y usamos la lista de superficies de la carcasa como modelo para ello. Por simplicidad, el ejemplo no tiene ningún manejo del desbordamiento (cuando hay demasiadas aplicaciones para la barra de tareas), pero en un compositor adecuado, esto también es algo que debería considerarse.

Por último, para evitar que las aplicaciones maximizadas se expandan hasta llenar el área cubierta por la barra de tareas, creamos un elemento especial para gestionar las partes del dominio WaylandOutput que están disponibles para las ventanas cliente.

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

Se ancla simplemente a los lados de WaylandOutput, pero su anclaje inferior está en la parte superior de la barra de tareas.

En Chrome, utilizamos esta área para definir el maximizedRect de la ventana.

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

Por defecto, esta propiedad coincidirá con el WaylandOutput completo. En nuestro caso, sin embargo, no queremos incluir la barra de tareas en el área disponible, por lo que anulamos el valor predeterminado.

Proyecto de ejemplo @ 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.