Давайте воспользуемся фреймворком Rust
Состояние результатов JS за 2022 год побудило меня присмотреться к Таури как к альтернативе Электрону. Одним из главных преимуществ фреймворка, несомненно, является размер конечного пакета приложений.
Основным препятствием, которое остановило меня от создания/конвертации приложения с Electron на Tauri, было отсутствие у меня знаний о Rust. Но просматривая список приложений в репозитории awesome-tauri, я увидел пару приложений, живущих в строке меню, и, наконец, решил создать что-то свое.
Поскольку я не смог найти ничего похожего на модуль менюбар для Tauri, я хочу поделиться в этой статье своим опытом создания простого приложения меню/трея с нуля. Часть кода JavaScript трогать не будем, так как весь необходимый код мы добавим в файлы Rust.
Для тех, кто хочет сразу взглянуть на весь код, проверьте этот репозиторий.
1. Создание нового приложения
Как предлагает документация по быстрому запуску Tauri, давайте возьмем команду npm create
и создадим новое приложение:
$ npm create tauri-app
Введите имя приложения, выберите любой менеджер пакетов и базовый фреймворк. Я взял pnpm
и vue-ts
.
Команда создаст новую папку с базовой структурой приложения Tauri.
Теперь запустите приложение с помощью команды $ pnpm tauri dev
и перейдите к коду.
Кстати, вот некоторые рекомендуемые расширения VS Code:
- Tauri — добавляет автозаполнение и проверку конфигурационных файлов, а также команды для запуска, сборки и т.д.
- rust-analyzer — поддержка Rust
2. Добавление функциональности панели меню, также известной как System Tray API.
Как и в Electron, System Tray API отображает значки в строке меню/трее. Что мне нравится в Tauri на данный момент, так это то, что базовая конфигурация приложения хранится в отдельном файле JSON.
Откройте tauri.conf.json
и добавьте в него конфигурацию для systemTray
:
"systemTray": { "iconPath": "icons/icon.png", "iconAsTemplate": true }
Теперь давайте перейдем к коду Rust. Для тех, кто, как и я, новичок в Rust, в официальной документации есть базовые примеры обработчиков лотков, которые мы возьмем для начала. Откройте src-tauri/main.ts
и добавьте код, отвечающий за событие щелчка левой кнопкой мыши. Вот код для этого:
use tauri::{Manager, SystemTray, SystemTrayEvent, SystemTrayMenu}; fn main() { let system_tray_menu = SystemTrayMenu::new(); tauri::Builder::default() .system_tray(SystemTray::new().with_menu(system_tray_menu)) .on_system_tray_event(|app, event| match event { SystemTrayEvent::LeftClick { position: _, size: _, .. } => { let window = app.get_window("main").unwrap(); // toggle application window if window.is_visible().unwrap() { window.hide().unwrap(); } else { window.show().unwrap(); window.set_focus().unwrap(); } }, _ => {} }) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
Теперь в строке меню должен появиться значок по умолчанию, и вы можете переключать окно приложения, щелкнув по нему.
3. Установка правильного положения окна приложения
Это неплохо, но нам нужно изменить положение окна, чтобы оно было рядом с самим значком. Вы можете попробовать рассчитать позицию самостоятельно, но для этого мы пойдем другим путем и воспользуемся для этого пакетом tauri_plugin_positioner
. Заодно разберемся, как добавлять плагины в Tauri/Rust.
Это действительно просто, просто откройте cargo.toml
и добавьте эту строку с зависимостью:
[dependencies] tauri-plugin-positioner = { version = "1.0.4", features = ["system-tray"] }
Теперь вернитесь к main.rs
и используйте зависимость в нашем коде:
use tauri::{Manager, SystemTray, SystemTrayEvent, SystemTrayMenu}; use tauri_plugin_positioner::{Position, WindowExt}; fn main() { let system_tray_menu = SystemTrayMenu::new(); tauri::Builder::default() .plugin(tauri_plugin_positioner::init()) .system_tray(SystemTray::new().with_menu(system_tray_menu)) .on_system_tray_event(|app, event| { tauri_plugin_positioner::on_tray_event(app, &event); match event { SystemTrayEvent::LeftClick { position: _, size: _, .. } => { let window = app.get_window("main").unwrap(); // use TrayCenter as initial window position let _ = window.move_window(Position::TrayCenter); if window.is_visible().unwrap() { window.hide().unwrap(); } else { window.show().unwrap(); window.set_focus().unwrap(); } } _ => {} } }) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
4. Изменение внешнего вида окна
Отлично, теперь наше окно отображается в нужном нам месте. Теперь нам нужно исправить некоторые свойства окна. Например, нам нужно удалить titlebar
с кнопками, расположенными слишком близко, и развернуть и свернуть приложение. Для этого снова откройте tauri.conf.json
и найдите его window
настройки объекта.
Я использовал следующую конфигурацию:
{ "fullscreen": false, "height": 600, "resizable": false, "title": "menubar", "width": 600, "visible": false, "hiddenTitle": true, "decorations": false, "focus": false, "transparent": false }
Полный список поддерживаемых свойств доступен в официальной документации.
5. Скройте окно, нажав «Снаружи»
В нашем приложении отсутствует еще одна деталь: возможность скрыть приложение, когда вы щелкаете за пределами окна. Это легко сделать, потому что вы можете проверить состояние фокуса окна, используя on_window_event()
:
fn main() { ... tauri::Builder::default() .plugin(tauri_plugin_positioner::init()) .system_tray(SystemTray::new().with_menu(system_tray_menu)) .on_system_tray_event(|app, event| { ... }) .on_window_event(|event| match event.event() { tauri::WindowEvent::Focused(is_focused) => { // detect click outside of the focused window and hide the app if !is_focused { event.window().hide().unwrap(); } } _ => {} }) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
6. Добавление контекстного меню
Конфигурация API системного трея содержит еще один полезный параметр — menuOnLeftClick
со значением по умолчанию true
. Поскольку левый щелчок уже используется для переключения окна, в нашем случае давайте деактивируем это свойство с помощью следующего кода:
"systemTray": { "iconPath": "icons/icon.png", "iconAsTemplate": true, "menuOnLeftClick": false }
А теперь мы будем использовать следующий код для создания простого контекстного меню с одним пунктом меню, который открывается при щелчке правой кнопкой мыши по значку menubar
:
fn main() { let quit = CustomMenuItem::new("quit".to_string(), "Quit").accelerator("Cmd+Q"); let system_tray_menu = SystemTrayMenu::new().add_item(quit); tauri::Builder::default() ... .on_system_tray_event(|app, event| { tauri_plugin_positioner::on_tray_event(app, &event); match event { ... SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() { "quit" => { std::process::exit(0); } _ => {} }, _ => {} } })
Кроме id
и title
можно также указать горячую клавишу клавиатуры, которая автоматически появится в контекстном меню рядом с заголовком.
7. Смотри и чувствуй
На последнем шаге мы снова позаботимся о внешнем виде нашего окна, а именно добавим стрелку поверх приложения, чтобы придать ему раскрывающийся вид, и закруглим углы окна.
Для этого нам нужно будет активировать свойство прозрачности и свойство macOSPrivateApi
, чтобы оно работало в macOS. Снова откройте tauri.conf.json
и добавьте следующие две строки:
..., "macOSPrivateApi": true, "windows": [ { ..., "transparent": true }
Для отображения стрелки воспользуемся одним из примеров из menubar
module Electron и добавим (адаптируем) в style.css
следующий код:
body { margin: 0; } .arrow { position: relative; padding: 12px 0; } .arrow:before { content: ''; height: 0; width: 0; border-width: 0 8px 12px 8px; border-style: solid; border-color: transparent transparent #2f2f2f transparent; position: absolute; top: 0px; left: 50%; transform: translateX(-50%); } @media (prefers-color-scheme: dark) { .container { border-radius: 7px; padding: 10px; color: #f6f6f6; background-color: #2f2f2f; } }
Наконец, добавьте класс arrow
в тег body в index.html
. Вот как это выглядит:
... <body class="arrow"> ... </body>
Вот и все. Я буду рад вашим отзывам и надеюсь, что эти несколько шагов помогут вам приступить к созданию приложения menubar
с помощью Tauri :-)