Windows と Linux では、アプリのメニューを設定するとタイトルバーの下にメニューバーが付き、後から開いたウィンドウにも同じものが付きます。「表示 → メニューバーを隠す」で一時的に消したい、設定画面などのサブウィンドウには出したくない、という場面で使うのが Rust の hide_menu() / show_menu() / remove_menu() です。JS の Menu API には隠したり外したりするメソッドが無いので、フロントエンドからはコマンドを経由します。macOS のメニューバーは画面上部の OS のもので、これらの API は非対応です。
前提条件
プラグインも権限の追加も不要です。自分で定義して invoke_handler に登録したコマンドは、capability に書かなくてもページから呼べます。設定ファイル(tauri.conf.json)には、メニューバーの表示を切り替える項目はありません。
目的に応じて次のように使い分けます。
| やりたいこと | 使うもの |
|---|---|
| そもそもメニューバーを出さない | メニューを設定しない(Windows と Linux の既定)。macOS だけに設定するなら menu-015 |
| 1 つのウィンドウで隠す・戻す | ウィンドウの hide_menu() / show_menu() |
| アプリのメニューが付いた全ウィンドウでまとめて隠す | AppHandle の hide_menu() / show_menu() |
| 特定のウィンドウからメニューを外す | ウィンドウの remove_menu() |
| 表示されているか調べる | ウィンドウの is_menu_visible() |
hide_menu() はメニューを付けたまま見えなくするだけなので、show_menu() ですぐに戻せます。remove_menu() はウィンドウからメニューを外し、外したメニューを返します。
1. フロントエンドから実装する (TypeScript)
ページからは、2 章で定義する toggle_menu_bar コマンドを呼びます。メニューバーを隠すと、メニューから元に戻す操作はできなくなるので、戻す手段をページ側に必ず用意します。ここではボタンと、ページの keydown で受ける Ctrl+Shift+M で切り替えます。メニューのショートカット(menu-004)で受けないのは、隠れているメニューのキー操作に頼らないためです。右クリックメニュー(menu-008)に「メニューバーを表示」を入れておくのもよい方法です。
import { invoke } from '@tauri-apps/api/core';
const button = document.querySelector<HTMLButtonElement>('#toggle-menu-bar');
async function toggleMenuBar() {
try {
const visible = await invoke<boolean>('toggle_menu_bar'); // 切り替えた後の状態が返る
if (button) button.textContent = visible ? 'メニューバーを隠す' : 'メニューバーを表示';
console.log('menu bar visible:', visible);
} catch (e) {
// macOS とメニューの無いウィンドウではエラーを返すようにしてあるので、ボタンごと隠す
console.warn(e);
if (button) button.hidden = true;
}
}
button?.addEventListener('click', () => void toggleMenuBar());
window.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'm') {
e.preventDefault();
void toggleMenuBar();
}
});
隠した状態を次回の起動にも引き継ぐなら、状態を保存しておき、起動時に隠します。ただしページの JS は読み込み後に動くので、起動直後の一瞬はメニューバーが見えることがあります。確実に隠して起動したいなら、2 章のように Rust の setup で隠します。
2. バックエンドから実装する (Rust)
hide_menu() などは、ウィンドウにメニューが付いていないときや macOS では、何もせずに成功します。そのままでは「隠したつもりで何も起きない」ことに気付けないので、下の toggle_menu_bar では先に確かめてエラーを返しています。
サブウィンドウを作るときにメニューを指定しないと、Windows と Linux ではアプリのメニューが付きます。メニューバーを出したくないウィンドウは、visible(false) で作り、remove_menu() で外してから表示します。
use tauri::menu::{MenuBuilder, SubmenuBuilder};
use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindow, WebviewWindowBuilder};
/// true にすると、起動時からメニューバーを隠す
const START_HIDDEN: bool = false;
/// 呼び出し元ウィンドウのメニューバーを出し入れし、切り替えた後の状態を返す
#[tauri::command]
fn toggle_menu_bar(window: WebviewWindow) -> Result<bool, String> {
if cfg!(target_os = "macos") {
return Err("macOS ではメニューバーを隠せません".into());
}
if window.menu().is_none() {
return Err("このウィンドウにはメニューがありません".into());
}
let visible = window.is_menu_visible().map_err(|e| e.to_string())?;
if visible {
window.hide_menu().map_err(|e| e.to_string())?;
} else {
window.show_menu().map_err(|e| e.to_string())?;
}
Ok(!visible)
}
/// メニューバーの無いサブウィンドウを開く
#[tauri::command]
async fn open_palette(app: AppHandle) -> Result<(), String> {
let window = WebviewWindowBuilder::new(&app, "palette", WebviewUrl::App("palette.html".into()))
.title("パレット")
.inner_size(320.0, 480.0)
.visible(false) // メニューを外してから表示する
.build()
.map_err(|e| e.to_string())?;
window.remove_menu().map_err(|e| e.to_string())?;
window.show().map_err(|e| e.to_string())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.menu(|app| {
let view = SubmenuBuilder::new(app, "表示(&V)")
.text("view:hide-menu-bar", "メニューバーを隠す(&M)")
.build()?;
MenuBuilder::new(app).item(&view).build()
})
.setup(|app| {
if START_HIDDEN {
// 設定ファイルのウィンドウには、この時点でアプリのメニューが付いている
app.hide_menu()?;
}
Ok(())
})
.on_menu_event(|app, event| {
if event.id().as_ref() == "view:hide-menu-bar" {
// アプリのメニューが付いた全ウィンドウで隠す(戻すのはページ側の操作)
let _ = app.hide_menu();
}
})
.invoke_handler(tauri::generate_handler![toggle_menu_bar, open_palette])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
AppHandle の hide_menu() は、呼んだ時点で開いていて、アプリのメニューが付いているウィンドウだけが対象です。WebviewWindowBuilder::menu() や setAsWindowMenu() で専用のメニューを付けたウィンドウは含まれません。
動作確認
ページに <button id="toggle-menu-bar">メニューバーを隠す</button> を置いて npm run tauri dev で起動します。Windows では、タイトルバーの下に「表示(V)」があります。ボタンを押すとメニューバーが消え、もう一度押すか Ctrl+Shift+M で戻ります。コンソールには次のように出ます。
menu bar visible: false
menu bar visible: true
「表示 → メニューバーを隠す」を選んでも消え、ページのボタンで戻せます。invoke('open_palette') で開いたウィンドウにはメニューバーが無く、メインのウィンドウのメニューバーはそのままです。macOS ではボタンを押すとボタンが消え、コンソールに「macOS ではメニューバーを隠せません」と出ます。
よくあるエラーと対処法
- 「Command toggle_menu_bar not found」: コマンドを
invoke_handlerに登録していません。generate_handler!に足します。 - 呼んでも何も変わらない: そのウィンドウにメニューが付いていないか、macOS です。どちらもエラーにならないので、
menu()と OS を先に確かめます。 - 後から開いたウィンドウにはメニューバーが出る:
hide_menu()は呼んだ時点で開いているウィンドウにだけ効きます。新しいウィンドウは作るたびに隠すか、remove_menu()で外します。 remove_menu()で外したのに、また付いた: アプリのメニューを設定し直すと(ページの再読み込みでsetAsAppMenu()が再び呼ばれた場合を含む)、メニューの無いウィンドウには新しいメニューが付きます。設定し直した後にもう一度外します。app.hide_menu()で隠れないウィンドウがある: 専用のメニューを付けたウィンドウは対象外です。そのウィンドウのhide_menu()を呼びます。
OS ごとの違いと注意点
- Windows / Linux: メニューバーはウィンドウごとに付くので、隠す・外すもウィンドウ単位です。
- macOS:
hide_menu()・show_menu()・is_menu_visible()は非対応です。ウィンドウのremove_menu()も非対応で、代わりにAppHandleのremove_menu()を使うことになっています。画面上部のメニューバーはアプリから消せないので、項目を絞ったメニューにします(menu-015)。 - iOS / Android: メニューの API はありません。
- ショートカット: メニューを隠している間も同じキーを確実に効かせたいなら、ページの
keydownでも同じ処理を呼びます(menu-004)。 - サブウィンドウの作り方は 新しいサブウィンドウを開く (Vite Multi-page)、ネイティブのメニューバーを使わず HTML でタイトルバーとメニューを作る方法は menu-017 を参照してください。
