メニューバーを非表示にする

Rust の hide_menu()・show_menu() で Windows・Linux のメニューバーを隠す・戻す。JS から切り替えるコマンド、サブウィンドウから外す remove_menu()、macOS で使えない点も示す。

メニュー・トレイ 対象: Tauri 2.x 更新日: 読了目安: 約8分 menu-016
目次
  1. 前提条件
  2. 1. フロントエンドから実装する (TypeScript)
  3. 2. バックエンドから実装する (Rust)
  4. 動作確認
  5. よくあるエラーと対処法
  6. OS ごとの違いと注意点
  7. 関連レシピ

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 を参照してください。

関連レシピ

参考リンク(公式ドキュメント)

Web Ninja

この記事を書いた人

Web Ninja ウェブエンジニア (Web Engineer)

会社員ネットワークエンジニアから独立してかれこれ 25 年以上 Web エンジニアとして活動中。普段は JavaScript と Node.js を自在に操り、時には C++ や Perl といった古流の技も嗜みます。近年は Tauri × Rust という新たな武器を手に、デスクトップアプリ開発の最前線を駆け抜けています。「作りたい」を「作れる」に変えるための、実践的な「技」をお届けします。

お問い合わせ: tauri.ninja@gmail.com

内容の誤り・動かないコードを報告する