1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TauriAdvent Calendar 2024

Day 21

Tauriとvueでリマインドアプリを作ってみた[2]: メニューバーからアプリを表示

Posted at

今回作るもの

以下記事に続いてリマインドアプリを作っていきます。

なぜウェブアプリでなくてtauriなの?

私は普段Gitifyというアプリを使っていて、Macの画面上段にあるメニューバーから操作できるアプリを作りたいと思いました。

画面にずっと出しておく必要はなく、ちょっとした変化に気づくことができるからです。

今回作ったものは以下のようなイメージです。

Screen Recording 2024-12-21 at 18.18.54.gif

tl;dr

今回書いたコードを先に貼っておきます。
説明が必要な方は引き続き読んでいただければ!

src-tauri/src/main.rs

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use tauri::ActivationPolicy;

mod tray;

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_positioner::init())
        .setup(|app| {
            #[cfg(target_os = "macos")]
            {
                tray::init_macos_menu_extra(app.handle())?;
                // Dockアイコンを非表示に
                app.set_activation_policy(ActivationPolicy::Accessory);
            }
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

src-tauri/src/tray.rs

use tauri::{
    menu::{Menu, MenuItem},
    tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
    Manager, Runtime,
};
use tauri_plugin_positioner::WindowExt;

pub fn init_macos_menu_extra<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
    // メニューアイテムの作成
    let quit_item = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
    let menu = Menu::with_items(app, &[&quit_item])?;

    // トレイアイコンの設定
    let _ = TrayIconBuilder::with_id("menu_extra")
        .icon(app.default_window_icon().unwrap().clone())
        .icon_as_template(true)
        .menu(&menu)
        .menu_on_left_click(false)
        // メニューイベントのハンドリング
        .on_menu_event(move |app, event| match event.id.as_ref() {
            "quit" => {
                app.exit(0);
            }
            _ => {}
        })
        // トレイアイコンクリックイベントのハンドリング
        .on_tray_icon_event(|tray, event| {
            let app = tray.app_handle();

            tauri_plugin_positioner::on_tray_event(app.app_handle(), &event);

            if let TrayIconEvent::Click {
                button: MouseButton::Left,
                button_state: MouseButtonState::Up,
                ..
            } = event
            {
                if let Some(window) = app.get_webview_window("main") {
                    if !window.is_visible().unwrap_or(false) {
                        let _ = window.move_window(tauri_plugin_positioner::Position::TrayBottomCenter);
                        let _ = window.show();
                        let _ = window.set_focus();
                    } else {
                        let _ = window.hide();
                    }
                }
            }
        })
        .build(app);

    Ok(())
}
src-tauri/Cargo.toml
[build-dependencies]
tauri-build = { version = "2", features = [] }

[dependencies]
tauri = { version = "2", features = [ "macos-private-api", "tray-icon"] }
tauri-plugin-shell = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri-plugin-positioner = { version = "2.0.0-rc", features = ["tray-icon"] }

コード分析

前編でも書いていましたが、思い通りに動くメニューバーアプリを作ることに色々なところで苦戦していました。
tauri v2自体の開発をしている人が少なく、情報が限られているので調べた情報がv1のものだったりしてました。

今回の実現したかったものは以下のようなものです。

  1. メニューバーにアイコンが追加される

  2. アイコンを左クリックしたらvue.jsのwebviewが表示される

  3. アイコンを右クリックしたらtrayが表示される
    trayの例
    Screenshot 2024-12-21 at 18.32.51.png

  4. Webview以外のところをクリックするとアプリが非表示になる

  5. Dockにはアプリアイコンが表示されない

1. メニューバーにアイコンが追加される

TrayIconBuilder::with_id("menu_extra")
    .icon(app.default_window_icon().unwrap().clone())
    .icon_as_template(true)

まずはTrayIconBuilderを使用してメニューバーアイコンを作成する必要があります。
そのあとicon_as_template(true)を書いてあげるとmacOSのメニューバーに適したモノクロアイコンを表示します。

ここでapp.default_window_icon()はアプリケーションのデフォルトアイコンを使用するという意味です。

2. アイコンを左クリックしたらvue.jsのwebviewが表示される

.on_tray_icon_event(|tray, event| {
    if let TrayIconEvent::Click {
        button: MouseButton::Left,
        button_state: MouseButtonState::Up,
        ..
    } = event
    {
        if let Some(window) = app.get_webview_window("main") {
            if !window.is_visible().unwrap_or(false) {
                let _ = window.move_window(tauri_plugin_positioner::Position::TrayBottomCenter);
                let _ = window.show();
                let _ = window.set_focus();
            } else {
                let _ = window.hide();
            }
        }
    }
})

私はここで公式ドキュメントで書かれているon_menu_eventを使ってハマりました。
右クリックと左クリックで異なったものを見せるためには、trayメニューではなく、アイコンに対してイベントを検知するon_tray_icon_eventを使う必要がありました。

次は左クリックイベントを検知する必要があります。TrayIconEventのClickでどんなイベントであったかを判断できるようになっています。

あとはget_webview_window("main")でVue.jsのWebViewウィンドウを出してあげます。

ウィンドウが非表示の場合は以下の記述で解決しました。

トレイアイコンの真下に移動(TrayBottomCenter)
ウィンドウを表示(show())
フォーカスを設定(set_focus())

3. アイコンを右クリックしたらtrayが表示される

let quit_item = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
let menu = Menu::with_items(app, &[&quit_item])?;

TrayIconBuilder::with_id("menu_extra")
    .menu(&menu)
    .menu_on_left_click(false)

MenuItemを使ってアプリを終了する機能を持つquitメニューを作りました。

ここでは右クリックの時のみtrayメニューを出すのでmenu_on_left_click(false)を書いています。

4. Webview以外のところをクリックするとアプリが非表示になる

if !window.is_visible().unwrap_or(false) {
    // ...
} else {
    let _ = window.hide();
}

ここでは2.のところのelseになっています。

Webview外をクリックするとウィンドウを非表示にする処理が実行されます。

5. Dockにはアプリアイコンが表示されない

// main.rs
.setup(|app| {
    #[cfg(target_os = "macos")]
    {
        tray::init_macos_menu_extra(app.handle())?;
        app.set_activation_policy(ActivationPolicy::Accessory);
    }
    Ok(())
})

main.rsにActivationPolicy::Accessoryを設定して、macOSのDockにアプリケーションアイコンを表示しないようにしています。

おわりに

v1とv2の情報が混じってる中で、tauriで開発をするのって本当に大変ですね。
私はRustにも慣れていなくとても苦労しました。

一旦基本的な動きができたことで、後はvueでの開発に集中できそうでワクワクです!

出典

Tauri v2 ドキュメント

Mastering Menu Bar Apps: Using Rust and Tauri for macOS Development!

1
0
0

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?