🧸
Tauri2.0デスクトップアプリの基本カスタム実装メモ
Tauri2.0でデスクトップアプリを作成する前に、よく使うであろう基本的なカスタマイズをどのように実装すればよいか下調べした結果メモです。最初はTauri独自の書き方に慣れませんでしたが、慣れてくれば特に問題なく使用できそうでした。ただ、まだ2.0がリリースされて日が浅いからか英語でも情報が断片的にしか見つからなかったりで、結局doc.rsやGitHubのソースを確認する場面がそこそこありなかなか苦労しました。フロントエンドはSvelte5を使用しています。下調べの内容は色々実装した後に追記するかもしれません。
前提
環境
$ cargo tauri info
[✔] Environment
- OS: Debian 12.0.0 x86_64 (X64)
✔ webkit2gtk-4.1: 2.44.3
✔ rsvg2: 2.54.7
✔ rustc: 1.81.0 (eeb90cda1 2024-09-04)
✔ cargo: 1.81.0 (2dbb1af80 2024-08-20)
✔ rustup: 1.27.1 (54dd3d00f 2024-04-24)
✔ Rust toolchain: stable-x86_64-unknown-linux-gnu (environment override by RUSTUP_TOOLCHAIN)
- node: 20.18.0
- pnpm: 9.12.0
- yarn: 1.22.22
- npm: 10.8.2
[-] Packages
- tauri 🦀: 2.0.2
- tauri-build 🦀: 2.0.1
- wry 🦀: 0.44.1
- tao 🦀: 0.30.3
- tauri-cli 🦀: 2.0.2
- @tauri-apps/api : 2.0.2
- @tauri-apps/cli : 2.0.2
[-] Plugins
- tauri-plugin-window-state 🦀: 2.0.1
- @tauri-apps/plugin-window-state : 2.0.0
- tauri-plugin-fs 🦀: 2.0.1
- @tauri-apps/plugin-fs : 2.0.0
- tauri-plugin-dialog 🦀: 2.0.1
- @tauri-apps/plugin-dialog : 2.0.0
- tauri-plugin-log 🦀: 2.0.1
- @tauri-apps/plugin-log : not installed!
- tauri-plugin-global-shortcut 🦀: 2.0.1
- @tauri-apps/plugin-global-shortcut : 2.0.0
...
表記
- フロントエンドとバックエンド
- "View" : フロントエンドのJavaScript側
- "Core" : バックエンドのRust側
- カレントディレクトリはプロジェクトディレクトリ
各種基本カスタムの実装
ViewからCore機能呼出
実装機能
実装コード
./src/routes/+page.svelte
<script lang="ts">
import { invoke } from "@tauri-apps/api/core";
let coreMsg = $state("");
function invokeGreeting() {
invoke<string>("greeting", { argName: "Svelte" })
.then(v => coreMsg = v)
.catch(e => coreMsg = e);
}
</script>
<h3>server message</h3>
<input type="text" bind:value={coreMsg} />
<h3>invoke core function</h3>
<button type="button" onclick={invokeGreeting}>greeting</button>
./src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greeting])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
#[tauri::command]
async fn greeting(arg_name: String) -> String {
format!("Hello, {}", arg_name)
}
メモ
- 色々なパターンを盛り込んだ例
- 上記の
Invoke
を用いる方法はCommandと呼ばれている - ViewからCoreへの通信は他に
emitTo
等を用いる方法もある- Eventと呼ばれている仕組み
- Webview Event
CoreからViewに情報送出
実装機能
実装コード
./src/routes/+page.svelte
<script lang="ts">
import { onDestroy } from "svelte";
import { invoke } from "@tauri-apps/api/core";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
let coreMsg = $state("");
function invokeGreeting() {
invoke<string>("greeting").then(null);
}
const appWebview = getCurrentWebviewWindow();
const unlisten = appWebview.listen<string>("core-msg", (ev: { payload: string; }) => {
coreMsg = ev.payload;
});
onDestroy(() => { unlisten.then(null); });
</script>
<h3>server message</h3>
<input type="text" bind:value={coreMsg} />
<h3>invoke core function</h3>
<button type="button" onclick={invokeGreeting}>greeting</button>
./src-tauri/src/lib.rs
use tauri::AppHandle;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greeting])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
#[tauri::command]
async fn greeting(app_handle: AppHandle) {
use tauri::Emitter;
let _ = app_handle.emit_to("main", "core-msg", "Hello!!");
}
メモ
- 上記の
Emitter::emit_to
を用いる方法はEventと呼ばれている- EventはViewからCoreへの伝送も可能
- CoreからViewへの通信は他に
Channel::send
を用いる方法もある- 順序データを高速伝送可能らしい
- Channels
- unmount時に
unlisten
するコードを書く必要がある- 書かないと適切に後処理が実施されない
-
emit_to
の"main"
は以下ファイルの値(のはず)./src-tauri/capabilities/default.json{ "windows": [ "main" ], }
メニューバー
実装機能
実装コード
./src/routes/+page.svelte
<script lang="ts">
import { onDestroy } from "svelte";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
let coreMsg = $state("");
const appWebview = getCurrentWebviewWindow();
const showMenuMsg = (ev: { payload: string; }) => coreMsg = ev.payload;
const evNew = appWebview.listen<string>("ev-new", showMenuMsg);
const evOpen = appWebview.listen<string>("ev-open", showMenuMsg);
const evSp = appWebview.listen<string>("ev-sp", showMenuMsg);
onDestroy(() => {
evNew.then(null);
evOpen.then(null);
evSp.then(null);
});
</script>
<p>core message: <input type="text" bind:value={coreMsg} /></p>
./src-tauri/src/lib.rs
use tauri::{menu::*, AppHandle, Wry, Error};
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
let _ = app.set_menu(get_menu(app.handle())?);
app.on_menu_event(|handle, ev| { run_menu_process(handle, ev.id().as_ref()) });
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
fn get_menu(handle: &AppHandle) -> Result<Menu<Wry>, Error> {
let menu = Menu::new(handle)?;
let _ = menu.append(&get_menu_file(handle)?)?;
let _ = menu.append(&get_menu_edit(handle)?)?;
Ok(menu)
}
fn get_menu_file(handle: &AppHandle) -> Result<Submenu<Wry>, Error> {
SubmenuBuilder::new(handle, "File")
.items(&[
&MenuItem::with_id(handle, MenuId::new("id_new"), "New", true, Some("Ctrl+N"))?,
&MenuItem::with_id(handle, MenuId::new("id_open"), "Open", true, Some("Ctrl+O"))?,
])
.separator()
.text(MenuId::new("id_quit"), "Quit")
.build()
}
fn get_menu_edit(handle: &AppHandle) -> Result<Submenu<Wry>, Error> {
SubmenuBuilder::new(handle, "Edit")
.cut()
.copy()
.paste()
.separator()
.item(&MenuItem::with_id(handle,MenuId::new("id_sp"), "Special Edit", true, None::<&str>)?)
.build()
}
fn run_menu_process(handle: &AppHandle, id: &str) {
use tauri::Emitter;
match id {
"id_new" => { let _ = handle.emit_to("main", "ev-new", "trigger new menu process"); },
"id_open" => { let _ = handle.emit_to("main", "ev-open", "trigger open menu process"); },
"id_sp" => { let _ = handle.emit_to("main", "ev-sp", "trigger sp menu process"); },
"id_quit" => {
handle.cleanup_before_exit();
std::process::exit(0);
},
_ => {},
}
}
メモ
- 各メニュー内容をサブメニューとして構成してから表示メニューに登録するイメージ
- メニュー構成時の
Some("Ctrl+N")
等は単なる表記指定のみ- 機能させるには別途ショートカットキーとして実装する必要がある
- メニュー構成時の
- 基本的なメニューは
cut()
等の指定だけで追加可能- これらはそれだけで機能し、かつショートカットキーも登録不要
- ただしOSによりサポート状況が異なる
- サポート状況はdoc.rs参照
- 自前実装の中でプロセス終了させる場合は
cleanup_before_exit()
を実行する
右クリックメニュー
実装機能
実装コード
./src/routes/+page.svelte
<script lang="ts">
import { emitTo } from '@tauri-apps/api/event';
let coreMsg = $state("");
function oncontextmenu(ev: Event) {
ev.preventDefault();
emitTo("main", "right-click");
}
</script>
<svelte:document {oncontextmenu} />
<p>core message: <input type="text" bind:value={coreMsg} /></p>
./src-tauri/src/lib.rs
use tauri::{menu::*, AppHandle, Manager, Listener,Wry, Error};
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
use std::sync::Arc;
let context_menu = get_menu_context(app.handle())?;
let main_view = Arc::new(app.get_webview_window("main").ok_or("could not find main window")?);
let mv_for_listen = main_view.clone();
main_view.listen("right-click", move |_ev| {
let _ = mv_for_listen.popup_menu(&context_menu);
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
fn get_menu_context(handle: &AppHandle) -> Result<Menu<Wry>, Error> {
MenuBuilder::new(handle).cut().copy().paste().build()
}
メモ
- 右クリックメニュー(コンテキストメニュー)も基本はメニューと同じ
-
app.set_menu
のような右クリックメニュー専用登録メソッドは無い - ViewからのEventを受けて
popup_menu
メソッドで表示させる
-
ウィンドウ状態保存
実装機能
実装コード
./src-tauri/capabilities/default.json
{
"permissions": [
"window-state:default"
]
}
./src-tauri/tauri.conf.json
{
"app": {
"windows": [
{
"visible": false
}
]
}
}
./src/routes/+page.svelte
<script lang="ts">
let coreMsg = $state("");
</script>
<p>core message: <input type="text" bind:value={coreMsg} /></p>
./src-tauri/src/lib.rs
use tauri::Manager;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_window_state::Builder::new().build())
.setup(|app| {
let main_view = app.get_webview_window("main")
.ok_or("could not find main window")?;
let _ = main_view.show();
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
メモ
- Window Stateプラグインを用いる
-
Automatic
でインストールするとpermissions
がdesktop.json
に追記される- 概ねうまくいくが
default.json
にも追記した方が無難- プラグインによってはなぜか
desktop.json
の記載が効かないものがあった - プラグインのplatform対応状況によって自動で切り替わっているように思える
-
default.json
に追記することで自身の責任において確実に制御可能になる
- プラグインによってはなぜか
- 概ねうまくいくが
- プラグインを使用しただけだと微妙にうまくいかない
- 不定位置に描画されてから前回の状態に復元される
-
tauri.conf.json
で標準非表示にして描画タイミングを制御して回避する
- ウィンドウ状態の情報は以下に保存される
$env:AppData\[identifier of tauri.conf.json]
- 例:
C:\Users\scirexs\AppData\Roaming\dev.scirexs.sandbox
ショートカットキー
実装機能
実装コード
./src-tauri/capabilities/default.json
{
"permissions": [
"global-shortcut:default"
]
}
./src/routes/+page.svelte
<script lang="ts">
import { onDestroy } from "svelte";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
let coreMsg = $state("");
const appWebview = getCurrentWebviewWindow();
const showMsg = (ev: { payload: string; }) => coreMsg = ev.payload;
const evCN = appWebview.listen<string>("ev-cn", showMsg);
const evAB = appWebview.listen<string>("ev-ab", showMsg);
onDestroy(() => {
evCN.then(null);
evAB.then(null);
});
</script>
<p>core message: <input type="text" bind:value={coreMsg} /></p>
./src-tauri/src/lib.rs
use tauri::AppHandle;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
let handle = app.handle();
use tauri_plugin_global_shortcut::{Shortcut, ShortcutState};
// let ctrl_n = Shortcut::new(Some(Modifiers::CONTROL), Code::KeyN); // strict define
let ctrl_n = Shortcut::try_from("ctrl+n")?;
let alt_b = Shortcut::try_from("alt+b")?;
handle.plugin(
tauri_plugin_global_shortcut::Builder::new()
// .with_shortcuts(["ctrl+n", "alt+b"])? // possible to pass str directly
.with_shortcuts([ctrl_n, alt_b])?
.with_handler(move |handle, shortcut, event| {
if event.state() == ShortcutState::Released { return; } // on keyup
// if ShortcutState::Pressed (on keydown), run process
run_shortcut_process(handle, shortcut.into_string().as_str());
})
.build(),
)?;
// app.global_shortcut().register(ctrl_n)?; // if need to add key later
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
fn run_shortcut_process(handle: &AppHandle, id: &str) {
use tauri::Emitter;
match id {
// Shortcut::new(Some(Modifiers::CONTROL), Code::KeyN).into_string().as_str() => {},
"control+KeyN" => { let _ = handle.emit_to("main", "ev-cn", "trigger ctrl+n"); },
"alt+KeyB" => { let _ = handle.emit_to("main", "ev-ab", "trigger alt+b"); },
_ => {},
}
}
メモ
- Global Shortcutプラグインを用いる
-
Automatic
でインストールするとlib.rs
に以下が自動で追記されるので削除する.plugin(tauri_plugin_global_shortcut::Builder::new().build())
-
.plugin()
内で処理を記述することも可能だが?
が使用できないため不便
- 構造体
Shortcut
とHotKey
は同じなのか不明- doc.rsでは"Struct Shortcut"と表示されている
- しかし同ページのsourceリンクで辿ると
pub struct HotKey
がハイライトされる - rust-analyzerでは
Shortcut::new()
はHotKey
で型表示される -
use
はuse tauri_plugin_global_shortcut::Shortcut
になる - aliasでもないようだ?
- 簡単に使えないプラグインは意味がないので深く考えないことにした
- 文字列による有効なキー表現
- 文字列パース処理
- 文字列で指定する場合、各キーのデリミタは
+
- doc.rs source
- 文字列で指定する場合、各キーのデリミタは
ダイアログ表示
実装機能
実装コード
./src-tauri/capabilities/default.json
{
"permissions": [
"dialog:default"
]
}
./src/routes/+page.svelte
<script lang="ts">
import { ask, open, save, type ConfirmDialogOptions, type DialogFilter, type OpenDialogOptions, type SaveDialogOptions } from "@tauri-apps/plugin-dialog";
// confirm => Ok/Cancel dialog
// message => only Ok dialog
let coreMsg = $state("");
const filters: DialogFilter[] = [
{ name: "my image filter png", extensions: ["png"] },
{ name: "my image filter jpg", extensions: ["jpg", "jpeg"] },
];
function openAskDialog() {
const optionDialog: ConfirmDialogOptions = {
title: "ask dialog",
kind: "info",
}
ask("Appear Yes/No dialog as shown", optionDialog)
.then(v => coreMsg = v.toString())
.catch(_ => coreMsg = "error")
}
function openFileDialog() {
const optionDialog: OpenDialogOptions = {
title: "file dialog",
filters,
multiple: false,
directory: false,
defaultPath: coreMsg,
};
open(optionDialog)
.then(v => coreMsg = v === null ? "null" : v)
.catch(_ => coreMsg = "error");
}
function openSaveDialog() {
const optionDialog: SaveDialogOptions = {
title: "",
filters,
defaultPath: coreMsg,
}
save(optionDialog)
.then(v => coreMsg = v === null ? "null": v)
.catch(_ => coreMsg = "error");
}
</script>
<p>core message: <input type="text" bind:value={coreMsg} /></p>
<ul>
<li><button type="button" onclick={openAskDialog}>yesno dialog</button></li>
<li><button type="button" onclick={openFileDialog}>file dialog</button></li>
<li><button type="button" onclick={openSaveDialog}>save dialog</button></li>
</ul>
./src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
メモ
- Dialogプラグインを用いる
- 右上の閉じるボタンを押すと
null
が返る-
ask
の場合はfalse
が返る
-
-
save
で既存のファイルパスを選択すると自動で上書き確認が出る- これだけでは実際の上書きはされない
- 実際の保存処理の中で適切に処理する必要がある
画像読み込み
実装機能
実装コード
./src-tauri/Cargo.toml
[dependencies]
tauri = { version = "2.0.2", features = ["protocol-asset"]}
./src-tauri/tauri.conf.json
{
"app": {
"security": {
"assetProtocol": {
"enable": true,
"scope": ["**"]
},
"csp": "default-src 'self'; img-src 'self' asset: http://asset.localhost"
}
},
}
./src/routes/+page.svelte
<script lang="ts">
import { convertFileSrc } from "@tauri-apps/api/core";
let coreMsg = $state("");
const imgPath = convertFileSrc("C:/sandbox/img_png.png".replaceAll("/","\\"));
coreMsg = imgPath;
</script>
<p>core message: <input type="text" bind:value={coreMsg} size="50" /></p>
<img src={imgPath} alt="sample" />
./src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
メモ
- Coreの処理を通さず直接ファイルを読み込む
- 画像や動画等のコンテンツはこの手法で読み込むのが最速(のはず)
テキスト読み込み
実装機能
実装コード
./src/routes/+page.svelte
<script lang="ts">
import { invoke } from "@tauri-apps/api/core";
let coreMsg = $state("");
const txtPath = "C:/sandbox/sample.txt".replaceAll("/","\\");
invoke<string>("read_text_file", { path: txtPath })
.then(v => coreMsg = v)
.catch(e => coreMsg = e);
</script>
<p>core message: <input type="text" bind:value={coreMsg} size="50" /></p>
./src-tauri/src/lib.rs
use std::path::PathBuf;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![read_text_file])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
#[tauri::command]
fn read_text_file(path: String) -> Result<String, String> {
use std::fs::File;
use std::io::Read;
let file_path = get_valid_file_path(&path)?;
let mut file = File::open(file_path).map_err(|e| format!("unable to open file as readonly: {e}"))?;
let mut whole_string = String::new();
file.read_to_string(&mut whole_string).map_err(|e| format!("unable to read file as text: {e}"))?;
Ok(whole_string)
}
fn get_valid_file_path(path: &str) -> Result<PathBuf, String> {
let file_path = PathBuf::from(path);
let _ = file_path.try_exists().map_err(|e| format!("file not exists: {e}"))?;
if !file_path.is_file() { return Err("the path is not a file".to_string()); }
Ok(file_path)
}
plugin version
./src-tauri/capabilities/default.json
{
"permissions": [
"fs:allow-appconfig-read"
]
}
./src/routes/+page.svelte
<script lang="ts">
import { readTextFile, BaseDirectory, type ReadFileOptions } from "@tauri-apps/plugin-fs"
let coreMsg = $state("");
const options: ReadFileOptions = {
baseDir: BaseDirectory.AppConfig // $env:AppData\[identifier of tauri.conf.json]
}
readTextFile("sample.txt", options)
.then(v => coreMsg = v)
.catch(e => coreMsg = e)
</script>
<p>core message: <input type="text" bind:value={coreMsg} size="50" /></p>
./src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_fs::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
メモ
- 使用する場合はFile Systemプラグインを用いる
- このプラグインを用いると取り扱えるディレクトリに強力な制限がかかる
- 決まった位置にあるファイルを使用する場合は基本プラグインを用いる方が良い
- ファイル位置によって
permissions
を細かく追加する必要がある
- ファイル位置によって
クリップボード読み書き
実装機能
実装コード
./src-tauri/capabilities/default.json
{
"permissions": [
"clipboard-manager:allow-read-text",
"clipboard-manager:allow-write-text",
"clipboard-manager:allow-clear"
]
}
./src/routes/+page.svelte
<script lang="ts">
import { readText, writeText, clear } from "@tauri-apps/plugin-clipboard-manager";
let coreMsg = $state("");
function readClipboard() {
readText()
.then(v => coreMsg = v)
.catch(e => coreMsg = e);
}
function writeClipboard() {
writeText("write to clipboard by view")
.then(null)
.catch(e => coreMsg = e);
}
function clearClipboard() {
clear()
.then(null)
.catch(e => coreMsg = e);
}
</script>
<p>core message: <input type="text" bind:value={coreMsg} size="50" /></p>
<ul>
<li><button type="button" onclick={readClipboard}>read</button></li>
<li><button type="button" onclick={writeClipboard}>write</button></li>
<li><button type="button" onclick={clearClipboard}>clear</button></li>
</ul>
./src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_clipboard_manager::init())
.setup(|app| {
use tauri_plugin_clipboard_manager::ClipboardExt;
let _ = app.clipboard().write_text("write to clipboard by core");
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
メモ
- Clipboardプラグインを用いる
- 使用する機能に応じて
permissions
を細かく追加する必要がある
動的ウィンドウ設定
実装機能
実装コード
./src/routes/+page.svelte
<script lang="ts">
import { invoke } from "@tauri-apps/api/core";
let coreMsg = $state("");
function toggleDecoration() {
invoke<string>("toggle_decoration").then(null);
}
</script>
<p>core message: <input type="text" bind:value={coreMsg} size="50" /></p>
<button type="button" onclick={toggleDecoration}>toggle</button>
./src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![toggle_decoration])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
#[tauri::command]
async fn toggle_decoration(window: tauri::Window) {
if let Ok(is_decorated) = window.is_decorated() {
let _ = window.set_decorations(!is_decorated);
}
}
メモ
-
tauri::Window
を操作することでウィンドウ設定を動的に変更可能
ドラッグ&ドロップ
実装機能
実装コード
./src/routes/+page.svelte
<script lang="ts">
import { onDestroy } from "svelte";
import { getCurrentWebview } from "@tauri-apps/api/webview";
import { type UnlistenFn } from "@tauri-apps/api/event";
let coreMsg = $state("");
let color = $state("text-black");
async function startListenDragDrop(): Promise<UnlistenFn> {
return await getCurrentWebview().onDragDropEvent((ev) => {
if (ev.payload.type === "over") {
coreMsg = "trigger drag over";
} else if (ev.payload.type === "enter") {
coreMsg = "trigger drag enter";
color = "text-red-500";
} else if (ev.payload.type === "drop") {
coreMsg = "trigger drag drop: " + ev.payload.paths.join(";");
color = "text-black";
} else if (ev.payload.type === "leave") {
coreMsg = "trigger drag leave";
color = "text-black";
}
});
}
const unlisten = startListenDragDrop().then(null);
onDestroy(() => {
unlisten.then(null);
});
</script>
<p class={color}>core message: <input type="text" bind:value={coreMsg} size="50" /></p>
./src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
メモ
-
公式のサンプル参考
- 実際は取り扱う
event.payload.type
の値がサンプルの記載と異なるため注意- とはいえ型からのコード補完で選択するだけでよい
- 実際は取り扱う
- Tauri機能を使用せずWeb技術だけで対応する場合、以下設定が必要
-
tauri.conf.json
のWindowConfigのdragDropEnabled
にfalse
設定
-
CLI引数取得
実装機能
実装コード
./src-tauri/capabilities/default.json
{
"permissions": [
"cli:default"
]
}
./src-tauri/tauri.conf.json
{
"plugins": {
"cli": {
"description": "CLI test",
"args": [
{
"name": "source",
"index": 1,
"takesValue": true
}
]
}
}
}
<script lang="ts">
import { getMatches } from "@tauri-apps/plugin-cli";
let coreMsg = $state("");
getArgValueByName("source").then(v => coreMsg = v);
async function getArgValueByName(name: string): Promise<string> {
const source = (await getMatches().catch(() => undefined))?.args[name]?.value;
if (typeof source === "string") {
return source;
} else if (typeof source === "boolean") {
return source.toString();
} else if (Array.isArray(source)) {
return source.join(",");
} else {
return "";
}
}
</script>
<p>core message: <input type="text" bind:value={coreMsg} size="50" /></p>
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_cli::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
メモ
-
Command Line Interface (CLI)プラグインを用いる
- 上記コード以外の例は公式サンプル参照
雑記
右クリックメニューのカスタマイズが英語ですらgoogle検索で情報がほぼ出てこなかったので特に苦労しました。みんな普通にカスタマイズするだろうに、ドンピシャの例を公式に載せておいてほしかったです・・・。
Discussion