前回閉じたときの位置と大きさで開くのは、デスクトップアプリに当然期待される動きです。公式の Window State プラグインを登録すると、ウィンドウの位置・サイズ・最大化などが終了時にファイルへ保存され、次にそのウィンドウが作られたときに復元されます。保存される項目と絞り込み方、記憶させないウィンドウの指定、ファイルに書かれるタイミング、手動で保存する方法を説明します。
前提条件
npm run tauri add window-state
Windows / macOS / Linux 用で、iOS / Android では使えません。tauri add はクレートをデスクトップ向けだけに追加し、lib.rs に登録を足し、capabilities/desktop.json に window-state:default を書き込みます。自動の保存と復元に権限は要らず、この権限(saveWindowState() / restoreState() / filename() を許可。core:default には含まれない)が要るのは 2 章の JS の API を呼ぶときだけです。
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "desktop-capability",
"platforms": ["macOS", "windows", "linux"],
"windows": ["main"],
"permissions": [
"window-state:default"
]
}
保存される項目と保存先
記憶する項目は StateFlags の 6 つで、既定はすべてです。
StateFlags | 記憶するもの |
|---|---|
SIZE | 内側のサイズ(物理ピクセル) |
POSITION | 外側の位置(物理ピクセル) |
MAXIMIZED | 最大化しているか |
VISIBLE | 表示しているか(復元の後に表示する) |
DECORATIONS | タイトルバーと枠の有無 |
FULLSCREEN | フルスクリーンか |
最大化・最小化している間はサイズと位置を記録し直さないので、最大化したまま終了すると次回は最大化で開き、解除すると最大化する前の大きさと位置に戻ります。最小化の状態は記憶しません。
記録はウィンドウのラベルごとに、アプリの設定フォルダ(appConfigDir())の .window-state.json へ書かれます。フォルダは Windows なら %APPDATA%\<識別子>、macOS なら ~/Library/Application Support/<識別子>、Linux なら ~/.config/<識別子> で、<識別子> は tauri.conf.json の identifier です。
1. 登録して、記憶する項目とウィンドウを決める (Rust)
プラグインはウィンドウが作られた時点で復元するので、設定ファイルのウィンドウより先に登録されるよう Builder に直接つなぎます。tauri add が足す行は条件なしなので、モバイル向けにもビルドするなら #[cfg(desktop)] で囲みます。
use tauri::Manager;
// デスクトップ専用なので、プラグインを使うコードには cfg(desktop) を付ける
#[cfg(desktop)]
fn window_state_plugin() -> tauri::plugin::TauriPlugin<tauri::Wry> {
use tauri_plugin_window_state::{Builder, StateFlags};
Builder::new()
// 枠の有無は記憶しない(tauri.conf.json の decorations を変えたらそれが効くように)
.with_state_flags(StateFlags::all() - StateFlags::DECORATIONS)
// スプラッシュ画面は対象外
.with_denylist(&["splash"])
// "popup-" で始まる一時的なウィンドウも対象外(false を返すと記憶しない)
.with_filter(|label| !label.starts_with("popup-"))
.build()
}
// フォーカスが外れたらファイルに書いておく(強制終了やクラッシュに備える)
#[cfg(desktop)]
fn save_on_blur(window: &tauri::Window, event: &tauri::WindowEvent) {
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
if let tauri::WindowEvent::Focused(false) = event {
let _ = window.app_handle().save_window_state(StateFlags::all());
}
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let builder = tauri::Builder::default();
#[cfg(desktop)]
let builder = builder
.plugin(window_state_plugin())
.on_window_event(save_on_blur);
builder
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
| メソッド | 働き |
|---|---|
with_state_flags() | 記憶する項目を絞る |
with_denylist() | 指定したラベルを対象外にする |
with_filter() | ラベルごとに判定し、false なら対象外 |
skip_initial_state() | 記憶はするが、作成時の自動復元はしない |
map_label() | ラベルを読み替え、複数のウィンドウで記録を共有する |
記録はラベルで引くので、開くたびに違うラベル(UUID など)を付けるウィンドウは、記録が増えるだけで復元されません。map_label(|l| if l.starts_with("editor-") { "editor" } else { l }) のようにまとめます。
保存と復元のタイミング
- 復元: ウィンドウが作られたときです。実行中に作ったウィンドウも、同じラベルなら前回の状態で開きます。
- 記録: 移動・リサイズのたびにメモリ上で更新し、ウィンドウを閉じるときにもその時点の状態を控えます。
- ファイルへの書き込み: アプリの終了時だけです。最後のウィンドウを閉じたときや
exit()/relaunch()(Process プラグイン)、Rust のapp.exit()では書かれますが、クラッシュや強制終了、std::process::exit()では書かれません。1 章のsave_on_blurはその備えです。
tauri.conf.json でウィンドウに "visible": false を付けておくと、プラグインが位置を戻してから表示するので、既定の位置で一瞬表示されるチラつきが出ません。VISIBLE を外した場合は自分で show() します。
2. 手動で保存する (TypeScript)
saveWindowState() は開いているすべてのウィンドウの状態をファイルに書きます。引数を省くと 1 章の with_state_flags() の項目で保存します。トレイに隠す作り(最小化ボタンでトレイに格納するようにする)ではアプリがなかなか終了しないので、隠す前に呼んでおきます。filename() で保存先のフルパスも案内できます。
import { appConfigDir, join } from '@tauri-apps/api/path';
import { filename, saveWindowState } from '@tauri-apps/plugin-window-state';
// 今の配置をファイルに書き、保存先のフルパスを返す
export async function saveLayoutNow(): Promise<string> {
await saveWindowState(); // window-state:allow-save-window-state が必要
return join(await appConfigDir(), await filename());
}
動作確認
npm run tauri dev で起動し、ウィンドウを動かして大きさを変えてから × ボタンで閉じます。ターミナルの Ctrl+C や、Rust の変更による自動再起動では正常に終了しないので、プラグインだけでは保存されません(save_on_blur があれば、ターミナルに切り替えた時点で保存されます)。再び起動すると前回の位置と大きさで開きます。拡大率 150% の Windows で論理ピクセル 1024 x 768 のウィンドウなら、ファイルは次のようになります。
{
"main": {
"width": 1536,
"height": 1152,
"x": 412,
"y": 180,
"prev_x": 398,
"prev_y": 176,
"maximized": false,
"visible": true,
"decorated": true,
"fullscreen": false
}
}
prev_x / prev_y は、最大化する前の位置に戻すための値です。
よくあるエラーと対処法
- 「window-state.save_window_state not allowed. Permissions associated with this command: window-state:allow-save-window-state, window-state:default」:
window-state:defaultがありません。リリースビルドでは「Command plugin:window-state|save_window_state not allowed by ACL」になります。 - main ウィンドウが前回の位置に戻らない:
setupの中でapp.handle().plugin()として登録すると、設定ファイルのウィンドウはそれより先に作られるので対象から外れます。Builderにつなぎます。 tauri.conf.jsonのwidthやdecorationsを変えても効かない: 記録が優先されます。アプリを終了してから.window-state.jsonを消します(起動中に消しても終了時に書き直されます)。- 起動してもウィンドウが出てこない:
"visible": falseのウィンドウを隠したまま終了すると、次回も表示されません。トレイから表示できるようにするか、VISIBLEを外して自分でshow()します。 - モバイル向けのビルドだけ
tauri_plugin_window_stateが見つからない趣旨のエラーになる: クレートはデスクトップ向けにしか入っていないので、#[cfg(desktop)]で囲みます。
OS ごとの違いと注意点
- iOS / Android: 非対応です。
- モニターの抜き差し: 記録した範囲の四隅のどれかがいずれかのモニターに入っていれば位置を戻し、どこにも入らなければ位置だけ戻さずに開きます。外部モニターを外した後に、画面外に開いて見失うことはありません。
- 設定ファイルの位置:
x/yやcenterを指定していても記録が優先され、設定ファイルの位置は初回だけ使われます(ウィンドウの位置を指定する)。 - 自前の保存との併用: ウィンドウのサイズを変更する のように
onResized()で自分で保存・復元する処理とはどちらか一方にします。表示と非表示の切り替えは ウィンドウを表示・非表示にする を参照してください。
