時間のかかる書き出しが終わった、チャットにメッセージが届いた、というときに、別の作業をしているユーザーの手を止めずに「こちらを見て」と伝えるのが requestUserAttention() です。Windows ではタスクバーのボタンが点滅し、macOS では Dock のアイコンが跳ねます。手前に出してフォーカスを奪う setFocus() と違い、入力を横取りしません。JS の requestUserAttention() か Rust の request_user_attention() を使い、プラグインは不要です。
前提条件
権限 core:window:allow-request-user-attention は core:default に含まれないので追加します。呼ぶ前に確かめる isFocused() と、フォーカスの変化を受ける onFocusChanged() は core:default で使えます。Rust から呼ぶだけなら追加は要りません。
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
"core:window:allow-request-user-attention"
]
}
種類は UserAttentionType の 2 つで、見え方は OS ごとに決まっています。
| 指定 | Windows | macOS | Linux |
|---|---|---|---|
Critical | ウィンドウとタスクバーのボタンが、アプリにフォーカスが来るまで点滅 | Dock のアイコンが、アプリにフォーカスが来るまで跳ね続ける | 2 種類の違いはない |
Informational | タスクバーのボタンが、アプリにフォーカスが来るまで点滅 | Dock のアイコンが 1 回跳ねる | 同上 |
null | 取り消す | 効果なし | 取り消す |
どの OS でも、アプリにフォーカスがあるときに呼んでも何も起きません。
1. フロントエンドから実装する (TypeScript)
処理が終わった時点でユーザーが別のアプリを見ているときだけ知らせ、戻ってきたら取り消します。種類は数値の 1 / 2 ではなく UserAttentionType で指定します。フォーカスが戻っても点滅が自動で止まるとは限らないので、onFocusChanged() で null を渡して止めます。処理を中止した、別の端末で既読になった、など知らせる必要がなくなったときも同じ関数で取り消します。
import { getCurrentWindow, UserAttentionType } from '@tauri-apps/api/window';
const win = getCurrentWindow();
let requested = false;
// 見ていないときだけ知らせる
export async function notifyDone(critical = false) {
const focused = await win.isFocused();
console.log('job done, focused:', focused);
if (focused) return;
const kind = critical ? UserAttentionType.Critical : UserAttentionType.Informational;
await win.requestUserAttention(kind); // core:window:allow-request-user-attention が必要
requested = true;
console.log('attention requested:', UserAttentionType[kind]);
}
// 用が済んだら取り消す(macOS では効果なし)
export async function cancelAttention() {
if (!requested) return;
requested = false;
await win.requestUserAttention(null);
console.log('attention cleared');
}
// フォーカスが戻ったら止める
await win.onFocusChanged(({ payload: focused }) => {
if (focused) void cancelAttention();
});
// 例: 5 秒かかる処理のあとで知らせる
export async function runJob() {
await new Promise((resolve) => setTimeout(resolve, 5000));
await notifyDone();
}
2. バックエンドから実装する (Rust)
処理そのものを Rust で動かしているなら、終わった時点で Rust から知らせれば JS の権限も要りません。フォーカスが戻ったときの取り消しは、on_window_event で WindowEvent::Focused(true) を受けて行います。
use std::time::Duration;
use tauri::{UserAttentionType, WebviewWindow, WindowEvent};
/// アプリを見ていないときだけ知らせる
fn notify_done(window: &WebviewWindow, critical: bool) -> tauri::Result<()> {
if window.is_focused()? {
return Ok(());
}
let kind = if critical {
UserAttentionType::Critical
} else {
UserAttentionType::Informational
};
window.request_user_attention(Some(kind))
}
#[tauri::command]
async fn export_file(window: WebviewWindow) -> Result<(), String> {
// 実際の重い処理の代わりに 5 秒待つ(async のスレッドを直接ふさがない)
tauri::async_runtime::spawn_blocking(|| std::thread::sleep(Duration::from_secs(5)))
.await
.map_err(|e| e.to_string())?;
notify_done(&window, false).map_err(|e| e.to_string())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.on_window_event(|window, event| {
// フォーカスが戻ったら取り消す(自動で止まらない環境向け)
if let WindowEvent::Focused(true) = event {
let _ = window.request_user_attention(None);
}
})
.invoke_handler(tauri::generate_handler![export_file])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
import { invoke } from '@tauri-apps/api/core';
await invoke('export_file'); // 5 秒後、別のアプリを見ていれば点滅する
動作確認
npm run tauri dev で起動し、runJob() を呼んだらすぐに別のアプリをクリックします。5 秒後にタスクバーのボタンが点滅し(macOS は Dock のアイコンが 1 回跳ねる)、クリックして戻るとコンソールに次のように出ます。ウィンドウを見たまま待つと、フォーカスがあるので何も起きません。
job done, focused: false
attention requested: Informational
attention cleared
よくあるエラーと対処法
- 「window.request_user_attention not allowed. Permissions associated with this command: core:window:allow-request-user-attention」: 権限の追加漏れです。リリースビルドでは「Command plugin:window|request_user_attention not allowed by ACL」だけになります。
- 呼んでも何も起きない: アプリにフォーカスがあります。ボタンのクリックで直接呼ぶと必ずこの状態になるので、試すときはタイマーで遅らせて別のアプリへ切り替えます。
- 引数を省くと型エラーになる: 引数は必須で、省くと「Expected 1 arguments, but got 0.」になります。型チェックのない JS で省くと取り消し(
null)と同じ扱いになり、やはり何も起きません。 - 隠したウィンドウでは点滅が見えない:
hide()したウィンドウにはタスクバーのボタンがありません(ウィンドウを表示・非表示にする)。トレイに格納 している間は 通知 で知らせます。最小化ならボタンが残るので点滅は見えます。 - macOS で止まらない: macOS では
nullに効果がなく、Criticalはアプリにフォーカスが来るまで跳ね続けます。取り消す可能性がある知らせはInformationalにします。
OS ごとの違いと注意点
- Windows:
Criticalはタスクバーのボタンだけでなくウィンドウ自体も点滅します。どちらの種類もアプリにフォーカスが来ると止まります。 - macOS: 跳ねるのはアプリの Dock アイコンで、ウィンドウごとの区別はありません。
- Linux: 2 種類の見え方は同じです。
- ほかの知らせ方との使い分け: 点滅は「気付かせるだけ」です。内容まで伝える、隠れていても届ける、状態を出し続ける、といった目的には次を使います。
| 手段 | 向いている場面 |
|---|---|
requestUserAttention() | ウィンドウがタスクバーにある(最小化を含む)とき、気付かせるだけ |
| 通知(dlg-009) | 隠している・トレイ常駐中、内容も伝えたい |
| タイトルに件数を出す(win-002) | 未読数など、状態を出し続けたい |
setBadgeCount() / setOverlayIcon() | アイコンに数やマークを付けたい(前者は Windows 非対応、後者は Windows 専用) |
