花哨的合成器

花哨的合成器是一个示例,展示了如何使用纯 QML 编写一个花哨的 Wayland 合成器。

简介

花哨的合成器是一个小的桌面风格 Wayland 合成器示例,展示了 Qt Wayland Compositor QML API 的强大和易用性。

花哨的合成器示例类似于 最小 QML 示例,因为它是一个完整的 Wayland 合成器,仅使用 QML 代码实现。

初始化合成器

最小 QML 示例一样,花哨的合成器支持 Qt 支持的常用 shell 扩展。

// Shell surface extension. Needed to provide a window concept for Wayland clients.
// I.e. requests and events for maximization, minimization, resizing, closing etc.
XdgShell {
    onToplevelCreated: (toplevel, xdgSurface) => screen.handleShellSurface(xdgSurface)
}

// Minimalistic shell extension. Mainly used for embedded applications.
IviApplication {
    onIviSurfaceCreated: (iviSurface) => screen.handleShellSurface(iviSurface)
}

// Deprecated shell extension, still used by some clients
WlShell {
    onWlShellSurfaceCreated: (shellSurface) => screen.handleShellSurface(shellSurface)
}

这些扩展作为 WaylandCompositor 的子项而被实例化,并自动将它们添加到从服务器广播给客户端的受支持接口列表。

当连接的客户端创建一个表面并将其绑定到某个 shell 扩展时,就会发出相应的信号。这会调用我们自定义的 WaylandOutput 类中的一个方法,该方法将 ShellSurface 添加到一个 ListModel 中。

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

这个模型被用作 Repeater 的源,在合成器的 WaylandOutput 中创建 ShellSurfaceItems。这向 Qt Quick 场景添加了表面的视图。由于它是一个 ShellSurfaceItem,它也向合成器的用户提供了某些交互选项,具体取决于正在使用的哪个 shell 扩展。

Repeater {
    model: output.shellSurfaces
    // Chrome displays a shell surface on the screen (See Chrome.qml)
    Chrome {
        shellSurface: modelData
        onDestroyAnimationFinished: output.shellSurfaces.remove(index)
    }
}

键盘

除了基本的窗口系统功能外,花哨的合成器还支持可选的运行在进程中的屏幕键盘。它使用 Qt 虚拟键盘 模块,如果该模块可用,则将启用。

import QtQuick
import QtQuick.VirtualKeyboard

InputPanel {
    visible: active
    y: active ? parent.height - height : parent.height
    anchors.left: parent.left
    anchors.right: parent.right
}

代码很简单。我们在输出的底部实例化一个 InputPanel,并确保仅在其当前活动时才可见。

Loader {
    anchors.fill: parent
    source: "Keyboard.qml"
}

然后使用一个 Loader 元素将键盘添加到 WaylandOutput。在这里使用 Loader 以避免对 Qt 虚拟键盘 模块的硬依赖。如果加载失败,那么合成器将继续正常操作,但将不支持屏幕键盘。

最后,我们需要一种方式让合成器将文本输入传达给其客户端。这通过一个text-input扩展来实现。Fancy Compositor 示例支持text_input_unstable_v2协议以及 Qt 的qt_text_input_method_unstable_v1协议。

通过将qt_text_input_method_unstable_v1扩展作为WaylandCompositor的子对象实例化,将其添加到合成器中,并通过TextInputManager添加text_input_unstable_v2

较新的 Qt 应用将选择可用的qt_text_input_method_unstable_v1,而其他客户端则可以使用text_input_unstable_v2

过渡

除了基本功能外,Fancy Compositor 示例还演示了状态之间的动画过渡。

其中第一个是激活过渡。这仅在XdgShell上受支持,因为这是唯一具有激活状态的 Shell 扩展。

Connections {
    target: shellSurface.toplevel !== undefined ? shellSurface.toplevel : null

    // some signals are not available on wl_shell, so let's ignore them
    ignoreUnknownSignals: true

    function onActivatedChanged() { // xdg_shell only
        if (shellSurface.toplevel.activated) {
            receivedFocusAnimation.start();
        }
    }
}

SequentialAnimation {
    id: receivedFocusAnimation

    ParallelAnimation {
        NumberAnimation { target: scaleTransform; property: "yScale"; to: 1.02; duration: 100; easing.type: Easing.OutQuad }
        NumberAnimation { target: scaleTransform; property: "xScale"; to: 1.02; duration: 100; easing.type: Easing.OutQuad }
    }
    ParallelAnimation {
        NumberAnimation { target: scaleTransform; property: "yScale"; to: 1; duration: 100; easing.type: Easing.InOutQuad }
        NumberAnimation { target: scaleTransform; property: "xScale"; to: 1; duration: 100; easing.type: Easing.InOutQuad }
    }
}

当客户端窗口在XdgShell协议下激活时,我们触发动画,使窗口“弹出”200毫秒。

Fancy Compositor 还支持销毁动画。这会在窗口关闭并销毁表面时触发,无论是客户端优雅地关闭其窗口,还是它崩溃。

onSurfaceDestroyed: {
    bufferLocked = true;
    destroyAnimation.start();
}

SequentialAnimation {
    id: destroyAnimation

    ParallelAnimation {
        NumberAnimation { target: scaleTransform; property: "yScale"; to: 2/height; duration: 150 }
        NumberAnimation { target: scaleTransform; property: "xScale"; to: 0.4; duration: 150 }
        NumberAnimation { target: chrome; property: "opacity"; to: chrome.isChild ? 0 : 1; duration: 150 }
    }
    NumberAnimation { target: scaleTransform; property: "xScale"; to: 0; duration: 150 }
    ScriptAction { script: destroyAnimationFinished() }
}

为了确保动画持续时间内有内容存在,我们首先锁定缓冲区。这意味着客户端渲染的最后一帧将保留在内存中,直到我们完成它。

同样,我们在项目的缩放比例上触发动画。相关的动画模仿关闭CRT屏幕,向用户提供视觉提示,表示窗口正在关闭,而不仅仅是从空气中消失。

可以采用任何类型的动画效果来进行这种状态变化,Qt Quick 提供了完整的工具。

示例项目 @ code.qt.io

© 2024 Qt 公司有限公司。本文档的贡献包含在此处的是其各自所有者的版权。本文档提供的文档根据自由软件基金会发布的条款和使用GNU自由文档许可证版本1.3进行许可。Qt以及相应的标志是芬兰和/或世界其他国家的Qt公司商标。所有其他商标均为其各自所有者的财产。