This page is unfinished.
The following page is a Work In Progress and will suffer changes in the future.
Creating a cross-platform desktop app with Tauri+Vue
I wanted to explore a framework for desktop (and possibly mobile) applications.
Java and Rust are preferred but I am no stranger to JavaScript (or TypeScript for that matter).
Warning: I’m developing all of this in Ubuntu 22.04
Choosing a framework
JavaFX
Electron
Flutter
Qt
Tauri
Tauri basics
Installation is done following the website’s instructions. https://tauri.app/
Project structure is composed of a rust backend and the chosen frontend, Vue in this case.
invoke(<method>, <args>)
primitive to call Rust functions in the backend from the frontend.
Event structure shared between backend and frontend, with emit
and listen
primitives.
Vue basics
Vue’s way of programming is based in components.
<script setup>
procedure
Initializing components’s variables is done with ref(<inital value>)
.
Avoiding await
in the setup
step and opting with .then(..)
. It is doable using the Suspense
feature but it is still experimental. https://vuejs.org/guide/built-ins/suspense.html
Variables initialized with ref()
are accessed with .value
our .values
within the setup, but
everywhere else it is accessed directly (trust me, this is something to watch out as it may lead
to hours of useless debugging).
A fix for pinch zoom
The Tauri app window allows for pinch zoom, i.e. when you do a zoom gesture in a trackpad.
It doesn’t work like a normal page zoom where contents are scaled, only the view is scaled.
After many hours of search I found how to do it based on these links: https://github.com/tauri-apps/tauri/discussions/3843 https://docs.rs/tauri/latest/tauri/window/struct.Window.html
Warning: you need to add webkit2gtk
to your Cargo.toml
[dependencies]
...
webkit2gtk = "0.18"
And came up with this:
pub fn fix_pinch_zoom(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
let main_window = app.get_window("main").unwrap();
main_window.with_webview(|webview| {
#[cfg(target_os = "linux")]
{
unsafe {
if let Some(data) = webview.inner().data::<>("wk-view-zoom-gesture") {
gobject_ffi::g_signal_handlers_destroy(data.as_ptr());
}
}
}
}).expect("Something went wrong disabling pinch zoom.");
Ok(())
}
Applied with setup()
during the app creation:
fn main() {
tauri::Builder::default()
.invoke_handler(...)
...
.setup(fix_pinch_zoom)
...
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
A solution for zooming in and out
In rust we add two menu options Zoom In
and Zoom Out
.
We can add key shortcuts such as Ctrl++
and Ctrl+-
for each.
pub fn create_menu() -> Menu {
let options_menu =
Menu::new().add_item(CustomMenuItem::new("zoom_in".to_string(), "Zoom In").accelerator("CommandOrControl+-"))
.add_item(CustomMenuItem::new("zoom_out".to_string(), "Zoom Out").accelerator("CommandOrControl+Plus"));
let menu = Menu::new().add_submenu(Submenu::new("Options", options_menu));
return menu;
}
Warning: these do not seem to be working with Wayland, only with X11. https://github.com/tauri-apps/tauri/issues/3578
We add a listener to each menu item to get notified when they get pressed.
When they get pressed they emit
an event.
pub fn handle_menu_event(event: WindowMenuEvent) {
match event.menu_item_id() {
"zoom_in" => {
event.window().emit("zoom_in", {}).unwrap();
}
"zoom_out" => {
event.window().emit("zoom_out", {}).unwrap();
}
_ => {}
}
}
On the other side, the frontend, we listen
for these events and resize the window as needed.
const curr_zoom = ref(1.0);
async function resize(zoom:number) {
const total_height = window.outerHeight;
const total_width = window.outerWidth;
const vertical_padding = 25;
const horizontal_padding = 0;
// calculate new app size
// const new_app_height = zoom > 1 ? (total_height - 16) * zoom + 16 : (total_height + 16) * zoom - 16;
const new_app_height = (total_height - vertical_padding) * zoom + vertical_padding;
const new_app_width = total_width * zoom + horizontal_padding;
// create new logical size
const new_size = new LogicalSize(new_app_width, new_app_height);
// set size
await getCurrent().setSize(new_size);
// scale contents through rust
const new_zoom = curr_zoom.value * zoom;
// document.body.style.scale = new_zoom + "%";
curr_zoom.value = new_zoom;
}
listen('zoom_in', (_) => {
resize(0.625);
});
listen('zoom_out', (_) => {
resize(1.6);
});
curr_zoom
is a variable used to determine the container’s zoom
CSS style value.
References
https://scythe-studio.com/en/blog/4-best-frameworks-for-cross-platform-desktop-app-development https://medium.com/@maxel333/comparing-desktop-application-development-frameworks-electron-flutter-tauri-react-native-and-fd2712765377