メニューの実行
これまでの連載ではJavaScript側からRustを呼び出していましたが、今回は図3のようにRust側からJavaScriptを呼び出します。それにはJavaScriptでRust側からのメッセージ呼び出しの受け取り状態をセット(listen)し、RustからのメッセージをJavaScriptで受け取ったときに処理を実行します。
ここではバックエンドのRust側で新たな機能として「メニューバー」を実装し、「メニュー」が実行された際にフロントエンドのWebページのJavaScriptにメッセージを送ります。
図3:JavaScript側でメッセージ受け取り状態の図
JavaScript側でメッセージを受け取る
まず、例として「New」メニューを実行したらJavaScriptに「new-file」メッセージを送り、Aceエディタの文字列を空(から)にして初期化します。そのためにはJavaScript側でTAURIのイベントの「listen」メソッドを使います。文字通りlistenメソッドで聞き耳を立てておくわけです。
なお、メッセージが呼ばれる前にメッセージの受け取り状態をセットしておかなければ、Rustからメッセージが送られてきても受け取れないため何も処理されません。
・「src」→「main.js」ファイルのサンプルコード
01 | const { invoke } = window.__TAURI__.tauri; |
05 | window.addEventListener("DOMContentLoaded", () => { |
06 | editor = ace.edit('text_edit'); |
08 | editor.setFontSize(16); |
11 | editor.session.setMode('ace/mode/html'); |
13 | //Newメニューから呼ばれるセットをする関数 |
14 | async function setNew() { |
15 | await window.__TAURI__.event.listen('new-file', () => { |
16 | editor.session.getDocument().setValue(""); |
【サンプルコードの解説】
DOMが全て読み込み完了したら「setNew」関数を呼び出します。
setNew関数で、Rustからの「new-file」メッセージを受け取った際に、Aceエディタのドキュメントの値を「""」(空)にするようにセットします。
Rustからメニューを実行
「New」と「Quit」のカスタムメニューを作成して「File」サブメニューに追加し、さらにメニューにも追加すればメニューバーの準備は完了です。TAURIビルダーの「menu」メソッドでメニューバーをウィンドウにセットします。「on_menu_event」メソッドでメニューがクリックされてメニューイベントを実行します。"new"メニューが実行されたらJavaScript側に"new-file"メッセージを送り(emit_all)ます。"quit"メニューが実行されたならプロセスから「exit」します。
お気づきかと思いますが、基本的にサンプルコードにコメントがある部分やその関数内、繋がりのあるコードなどが追記部分です。主にRust(main.rs)やJavaScript(main.js)のコードだけコメントしています(追記するコメントが抜けていたら申し訳ありません)。
・「src-tauri」→「src」→「main.rs」ファイルのサンプルコード
01 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] |
03 | use tauri::{CustomMenuItem,Menu,MenuItem,Submenu,Manager}; |
07 | let new_file = CustomMenuItem::new( |
08 | "new".to_string(), "New"); |
10 | let quit = CustomMenuItem::new( |
11 | "quit".to_string(), "Quit"); |
13 | let menu_file = Submenu::new( |
17 | .add_native_item(MenuItem::Separator) |
20 | let menu = Menu::new() |
21 | .add_submenu(menu_file); |
23 | tauri::Builder::default() |
24 | .invoke_handler(tauri::generate_handler![]) |
27 | .on_menu_event(|event| { |
28 | match event.menu_item_id() { |
33 | .emit_all("new-file", "") |
38 | std::process::exit(0); |
43 | .run(tauri::generate_context!()) |
44 | .expect("error while running tauri application"); |
【サンプルコードの解説】
「use」文で「CustomMenuItem」「Menu」「MenuItem」「Submenu」「Manager」構造体を使えるようにします。
「CustomMenuItem」構造体でサブメニュー内の「New」「Quit」カスタムメニューのインスタンスを作成します。
「Submenu」構造体でメニュー内の「File」サブメニューのインスタンスを作成し、カスタムメニューやセパレータを追加します。
「Menu」構造体でメニューバーに「File」サブメニューを追加したインスタンスを生成し「menu」変数に代入します。
TAURIビルダーの「menu」メソッドで「menu」変数をメニューバーにセットします。
TAURIビルダーの「on_menu_event」メソッドで「new」「quit」メニューを実行するメッセージの送信設定をします。
「Font」「Mode」「Theme」メニュー
先ほど「New」メニューを実装した要領で「Font」「Mode」「Theme」メニューも実装します。「Font」メニューにはフォントサイズ「12」「16」「24」ピクセルのメニュー、「Mode」メニューには「Text」「HTML」「JavaScript」メニュー、「Theme」メニューには「dracula」「terminal」「chrome」メニューがあります。
「New」メニューと同様のやり方なので、ここでは一気にまとめて「Font」「Mode」「Theme」メニューも実装します(図4)。たくさんコードを書きますが、簡単ですね。
図4:本文のサンプルコードを開いたmain.jsファイルをキャプチャしたフォントサイズ24、JavaScriptモード、draculaテーマのAceエディタ(図のソースコードにある「フォントサイズ16」や「ace/mode/html」は起動時の値)
「Font」「Mode」「Theme」メニューのJavaScript
「Font」メニューの「12」「16」「24」メニューの'font-size'メッセージを受け取ったら、Aceエディタの「setFontSize」メソッドでメニュー名の数値と同じフォントサイズをセットします。
「Mode」メニューの「Text」「HTML」「JavaScript」メニューの'mode'メッセージを受け取ったら、Aceエディタの「setMode」メソッドでシンタックスハイライトのモードをセットします。
「Theme」メニューの「dracula」「terminal」「chrome」メニューの'theme'メッセージを受け取ったら、Aceエディタの「setTheme」メソッドで背景色などの色の組み合わせのテーマをセットします。
・「src」→「main.js」ファイルのサンプルコード
01 | const { invoke } = window.__TAURI__.tauri; |
05 | window.addEventListener("DOMContentLoaded", () => { |
06 | editor = ace.edit('text_edit'); |
08 | editor.setFontSize(16); |
16 | editor.session.setMode('ace/mode/html'); |
19 | async function setNew() { |
20 | await window.__TAURI__.event.listen('new-file', () => { |
21 | editor.session.getDocument().setValue(""); |
24 | //Fontサイズメニューが呼ばれるセットをする関数 |
25 | async function setFontSize() { |
26 | await window.__TAURI__.event.listen('font-size', event => { |
27 | editor.setFontSize(event.payload); |
30 | //Modeメニューが呼ばれるセットをする関数 |
31 | async function setMode() { |
32 | await window.__TAURI__.event.listen('mode', event => { |
33 | editor.session.setMode('ace/mode/'+event.payload); |
36 | //Themeメニューが呼ばれるセットをする関数 |
37 | async function setTheme() { |
38 | await window.__TAURI__.event.listen('theme', event => { |
39 | editor.setTheme('ace/theme/'+event.payload); |
【サンプルコードの解説】
DOMが全て読み込み完了したら「setFontSize」「setMode」「setTheme」関数を呼び出します。
setFontSize関数でRustからの「font-size」メッセージを受け取った際に、Aceエディタのフォントサイズをメニューから受け取った引数の値(event.payload)をそのままセットします。
setMode関数でRustからの「mode」メッセージを受け取った際に、Aceエディタのモードを'ace/mode/'にメニューから受け取った引数の値(event.payload)をそのまま繋げてセットします。
setTheme関数でRustからの「font-size」メッセージを受け取った際に、Aceエディタのテーマを'ace/theme/'にメニューから受け取った引数の値(event.payload)をそのまま繋げてセットします。
「Font」「Mode」「Theme」メニューの実行
「New」メニューと同様に「Font」メニューの「12」「16」「24」メニュー、「Mode」メニューの「Text」「HTML」「JavaScript」メニュー、「Theme」メニューの「dracula」「terminal」「chrome」メニューを一気に実装します。
フォントサイズは任意の正の整数でもOKで、モードも「CSS」など様々なファイル形式に対応しており、テーマも「xcode」などいろいろあります。メニューを増やして改造してみると良いでしょう。
・「src-tauri」→「src」→「main.rs」ファイルのサンプルコード
001 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] |
003 | use tauri::{CustomMenuItem, Menu, MenuItem, Submenu,Manager}; |
006 | let new_file = CustomMenuItem::new( |
007 | "new".to_string(), "New"); |
008 | let quit = CustomMenuItem::new( |
009 | "quit".to_string(), "Quit"); |
011 | let font12 = CustomMenuItem::new( |
012 | "font12".to_string(), "12"); |
014 | let font16 = CustomMenuItem::new( |
015 | "font16".to_string(), "16"); |
017 | let font24 = CustomMenuItem::new( |
018 | "font24".to_string(), "24"); |
020 | let mode_text = CustomMenuItem::new( |
021 | "mode_text".to_string(), "Text"); |
023 | let mode_html = CustomMenuItem::new( |
024 | "mode_html".to_string(), "HTML"); |
026 | let mode_js = CustomMenuItem::new( |
027 | "mode_js".to_string(), "JavaScript"); |
029 | let theme_dracula = CustomMenuItem::new( |
030 | "theme_dracula".to_string(), "dracula"); |
032 | let theme_terminal = CustomMenuItem::new( |
033 | "theme_terminal".to_string(), "terminal"); |
035 | let theme_chrome = CustomMenuItem::new( |
036 | "theme_chrome".to_string(), "chrome"); |
037 | let menu_file = Submenu::new( |
041 | .add_native_item(MenuItem::Separator) |
044 | let menu_font = Submenu::new( |
051 | let menu_mode = Submenu::new( |
058 | let menu_theme = Submenu::new( |
061 | .add_item(theme_dracula) |
062 | .add_item(theme_terminal) |
063 | .add_item(theme_chrome)); |
064 | //メニューバーにFile・Font・Mode・Themeメニューを追加 |
065 | let menu = Menu::new() |
066 | .add_submenu(menu_file) |
067 | .add_submenu(menu_font) |
068 | .add_submenu(menu_mode) |
069 | .add_submenu(menu_theme); |
071 | tauri::Builder::default() |
072 | .invoke_handler(tauri::generate_handler![]) |
074 | .on_menu_event(|event| { |
075 | match event.menu_item_id() { |
079 | .emit_all("new-file", "") |
083 | std::process::exit(0); |
089 | .emit_all("font-size", 12) |
096 | .emit_all("font-size", 16) |
103 | .emit_all("font-size", 24) |
110 | .emit_all("mode", "text") |
117 | .emit_all("mode", "html") |
120 | //JavaScriptモードメニューを実行する時 |
124 | .emit_all("mode", "javascript") |
127 | //draculaテーマメニューを実行する時 |
131 | .emit_all("theme", "dracula") |
134 | //terminalテーマメニューを実行する時 |
135 | "theme_terminal" => { |
138 | .emit_all("theme", "terminal") |
141 | //chromeテーマメニューを実行する時 |
145 | .emit_all("theme", "chrome") |
151 | .run(tauri::generate_context!()) |
152 | .expect("error while running tauri application"); |
【サンプルコードの解説】
「CustomMenuItem」構造体でサブメニュー内の「12」「16」「24」「Text」「HTML」「JavaScript」「dracula」「terminal」「chrome」カスタムメニューのインスタンスを作成します。
「Submenu」構造体でメニュー内の「Font」「Mode」「Theme」サブメニューのインスタンスを作成し、カスタムメニューを追加します。
「Menu」構造体でメニューバーに「Font」「Mode」「Theme」サブメニューを追加するインスタンスを生成し「menu」変数に代入します。
TAURIビルダーの「menu」メソッドで「menu」変数をセットします。
TAURIビルダーの「on_menu_event」メソッドで「font12」「font16」「font24」「mode_text」「mode_html」「mode_js」「theme_dracula」「theme_terminal」「theme_chrome」メニューを実行した際のメッセージ送信設定をします。