ファンシー・コンポジター

Fancy Compositorは純粋なQMLでWaylandコンポジタを書く方法を示すサンプルです。

はじめに

Fancy Compositorは小さなデスクトップスタイルのWaylandコンポジターのサンプルで、Qt Wayland CompositorQML APIのパワーと使いやすさを示すものです。

Fancy Compositorの例はMinimal QMLの例と似ていますが、QMLコードのみで実装された本格的なWaylandコンポジターです。

コンポジターの初期化

最小限のQMLの例と同様に、Fancy CompositorはQtがサポートしている主なシェル拡張をサポートしています。

// 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 の子としてインスタンス化され、サーバからクライアントにブロードキャストされるサポートインターフェースのリストに自動的に追加されます。

接続されたクライアントがサーフェスを作成し、シェルエクステンションの1つにバインドすると、対応するシグナルが発せられます。そして、カスタムWaylandOutput クラスのメソッドが呼び出され、ShellSurfaceListModel に追加される。

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

このモデルはRepeater のソースとして使用され、コンポジターのWaylandOutput の中にShellSurfaceItems を作成します。これにより、Qt Quick シーンにサーフェスのビューが追加されます。これはShellSurfaceItem であるため、どのシェル拡張が使用されているかによって、コンポジターのユーザーに対する特定のインタラクションオプションも持っています。

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

キーボード

基本的なウィンドウシステム機能に加えて、Fancy Compositor はオプションでインプロセスで動作するオンスクリーンキーボードもサポートしています。これはQt Virtual Keyboardモジュールを使用しており、モジュールが利用可能であれば有効になります。

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 Virtual Keyboardモジュールへの依存を避けるために使用しています。読み込みに失敗した場合、コンポジターは正常に動作し続けますが、オンスクリーンキーボードのサポートはありません。

最後に、コンポジターがテキスト入力をクライアントに伝える方法が必要です。これはtext-input 拡張機能を使って行います。Fancy Compositor の例では、text_input_unstable_v2 プロトコルと Qt のqt_text_input_method_unstable_v1 プロトコルの両方をサポートしています。

WaylandCompositor の子としてQtTextInputMethodManager をインスタンス化することでqt_text_input_method_unstable_v1 拡張をコンポジターに追加し、TextInputManagertext_input_unstable_v2 を追加します。

新しいQtアプリケーションは、qt_text_input_method_unstable_v1 が利用可能であればそれを選びますが、他のクライアントはtext_input_unstable_v2 を使うことができます。

トランジション

基本的な機能に加え、Fancy Compositorの例では、状態間の遷移をアニメーションで表現しています。

その1つ目がアクティベーションのトランジションです。これはactivated ステートを持つ唯一のシェルエクステンションであるため、XdgShell でのみサポートされています。

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 The Qt Company Ltd. ここに含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 ここで提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。