LoginSignup
31
25

Tauri でフレームレスウィンドウのアプリケーションを作成する

Last updated at Posted at 2023-09-09

はじめに

Tauri でウィンドウを作るのは結構簡単にできる。
では、見た目をもっと製品っぽくリッチにするには?となるとやはりフレームレスウィンドウを作るという選択肢になってくると思う。
また、常駐系のアプリケーションを作りたい場合、「閉じる」ボタンはいらないし、許可したくないといった場合もカスタムが必要になってくる。

こうした条件に対してフレームレスウィンドウを作っていくことになるが、ちょこっと調べる必要があったので、それをまとめる。

image.png

Tauri は起動も速くて、軽量で素敵ですね。

output-palette.gif

前提: 環境

  • Windows 11
  • Rust v1.72.0 (rustc, cargo)
  • Tauri v1.4
  • Svelte v4.2

フレームレスウィンドウを作る

デフォルトの状態は次のような装飾になっている。

image.png

これを、こうする。

image.png

コンフィグを設定する

フレームレスウィンドウを設定する上で、以下のことを考慮する必要がある。

  • デフォルトの Window の装飾設定を切る。
  • フロントエンド側からウィンドウ操作を許可する。

これの設定には、 tauri.conf.json を編集する。

デフォルトの Window の装飾設定を切る

デフォルトでついているウィンドウ装飾を一切切ってしまう設定。
ウィンドウ上部のウィンドウコントロール部分もだが、影も消えている。

image.png

src-tauri/tauri.conf.json
{
    "tauri": {
        "windows": {
            "decorations": false
        }
    }
}

フロントエンド側からウィンドウ操作を許可する

特にドラッグ部分の startDragging: true に注意する。
他には、ウィンドウに行わせたい挙動に合わせてウィンドウ操作への権限を付与する。

src-tauri/tauri.conf.json
{
    "tauri": {
        "allowlist": {
            "window": {
                "maximize": true,
                "minimize": true,
                "hide": true,
                "startDragging": true,
                "show": true,
                "unmaximize": true,
                "unminimize": true
            }
        }
    }
}

もし、自らの管理が行き届く範囲での操作を行う場合は「すべて許可」する方法もある。

src-tauri/tauri.conf.json
{
    "tauri": {
        "allowlist": {
            "window": {
                "all": true,
            }
        }
    }
}

これらの許可項目については以下のドキュメントを参照。

フロントエンド側の処理を書く

画像は、フロントエンドに Svelte (SvelteKit) を使用している。普段は React 多め。
(Tauri は軽量・速度が魅力なのを謡っているので、どうせならフロントエンドでもその位置に立っている Svelte を使っていきたいのはある。)

また、以下のライブラリも併用している。ここら辺は好みなので、特に必須ではない。

pacakge.json
{
    "devDependencies": {
        "postcss": "^8.4.29",
        "tailwindcss": "^3.3.3",
		"lucide-svelte": "^0.274.0"
    }
}

まずは、全体のコード。

src/routes/+layout.svelte
<script lang="ts">
    import '../app.css'
    import { appWindow } from "@tauri-apps/api/window"
    import { X, Minus, Maximize, AlignHorizontalJustifyCenter } from 'lucide-svelte'

    function minimize() {
        appWindow.minimize()
    }

    async function maximize() {
        let maximizeState = await appWindow.isMaximized()

        if (!maximizeState) {
            appWindow.maximize()
        } else {
            appWindow.unmaximize()
        }
    }

    function hide() {
        appWindow.hide()
    }
</script>

<div>
    <header class="bg-zinc-800 h-12 select-none bg-fixe">
        <nav data-tauri-drag-region class="mx-auto flex items-center justify-between" aria-label="Global">
            <div class="mx-1 p-3">
                <AlignHorizontalJustifyCenter class="square-4" />
            </div>
            <ul class="flex gap-x-1">
                <li>
                    <button on:click={minimize} class="window-control-button">
                        <Minus size="16" />
                    </button>
                </li>
                <li>
                    <button on:click={maximize} class="window-control-button">
                        <Maximize size="16" />                         
                    </button>
                </li>
                <li>
                    <button on:click={hide} class="window-control-button">
                        <X size="16" />
                    </button>
                </li>
            </ul>
        </nav>
    </header>
    <div class="bg-zinc-900 h-[1px]"></div>
    <main class="bg-zinc-800 h-screen">
        <slot />
    </main>
</div>

<style lang="postcss">
    :global(body) {
        overflow: hidden;
        user-select: none;
        color: white;
    }

    .window-control-button {
        @apply hover:bg-zinc-600 p-4;
    }
</style>

data-tauri-drag-region 属性を適切につける

output-palette.gif

この属性がドラッグ可能エリアを示している。
上記コードでは <nav></nav> タグ部分をドラッグ可能部分に指定しているが、もし別の部分もまとめて行いたい場合は、以下のように設定する。

<div data-tauri-drag-region="input, a, textarea"> 特定のタグ要素をドラッグ対象に入れない </div>
// もしくは
<div data-tauri-drag-region="default"> デフォルトでいくつかのタグ要素をドラッグ対象に入れない </div>

例えば、上記のような場合、 <div></div> の中にウィンドウ操作のナビメニューを設定した <nav></nav> タグが用意されているが、これを誤って親の <div></div> タグの属性として付与してしまうと動作しない
一番上のレイヤーの要素が何かに注目して、 data-tauri-drag-region 属性を付けるように注意する。

Window のシャドウをつける

デフォルトでウィンドウ装飾を false にしてしまうと、Windows 側で用意されたシャドウが消えてしまう。
また、角ばってしまった印象になって格好悪い。

これをシャドウを付けつつ、丸みを帯びた感じの Windows らしい装飾デザインにしようというもの。
また、これは同様に MacOS 側のデザインにも対応する。

image.png

これを実現するには、window-shadows というプラグインを使用する。

src-tauri/Cargo.toml
[dependencies]
window-shadows = { git = "https://github.com/tauri-apps/window-shadows.git" }

そしてこれを Window 生成時のセットアップ処理に追加する。

src_tauri/src/main.rs
+ use tauri::Manager; // クロージャー内の型付けなどをサポートしてくれる。
+ use window_shadows::set_shadow;

fn main() {
    tauri::Builder::default()
+        .setup(|app| {
+            // "main" ウィンドウの取得
+            let main_window = app.get_window("main").unwrap();
+
+            // ウィンドウに window-shadows の装飾を適用
+            // Windows, macOS で有効
+            #[cfg(any(windows, target_os = "macos"))]
+            set_shadow(main_window, true).unwrap();
+
+            Ok(())
+        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

image.png

まとめ

少々、ここまでもっていくのにスタックがあったものの、おおむねスムーズに構築できたし、何より起動も速い。
Tauri + Rust + Svelte 環境はもう少し深堀していってみようと思う。

31
25
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
25