Sep 19, 2022 Lucas Nogueira
Tauri Co-Founder
The Tauri team is pleased to announce the first release of tauri-egui .
egui
is a GUI library written in Rust. It leverages an OpenGL context via glutin .
tauri-egui is a Tauri plugin that connects with the Tauri runtime event loop to allow you to create glutin windows via our glutin fork
and use egui through our egui-tao integration.
The first step is to add the crate to your dependencies in Cargo.toml:
Now you need to enable the plugin:
tauri :: Builder :: default ()
app . wry_plugin (tauri_egui :: EguiPluginBuilder :: new ( app . handle ()));
To use egui, all you need to do is implement the tauri_egui::eframe::App
trait to render elements using the egui API. In the following example, we will create a login layout.
Define the struct that will be used to render the layout:
use std :: sync :: mpsc :: {channel, Receiver, Sender};
use tauri_egui :: {eframe, egui};
password_checker : Box< dyn Fn( & str) -> bool + Send + 'static>,
texture : Option< egui :: TextureHandle>,
password_checker : Box< dyn Fn( & str) -> bool + Send + 'static>,
) -> ( Self , Receiver<String>) {
let ( tx , rx ) = channel ();
let initial_user = users . iter () . next () . cloned () . unwrap_or_else (String :: new );
heading : " Sign in " . into (),
Implement tauri_egui::eframe::App
to use the egui APIs:
impl eframe :: App for LoginLayout {
// Called each time the UI needs repainting
// see https://docs.rs/eframe/latest/eframe/trait.App.html#tymethod.update for more details
fn update ( & mut self , ctx : & egui :: Context, frame : & mut eframe :: Frame) {
let size = egui :: Vec2 { x : 320 . , y : 240 . };
frame . set_window_size ( size );
// adds a panel that covers the remainder of the screen
egui :: CentralPanel :: default () . show ( ctx , | ui | {
// our layout will be top-down and centered
ui . with_layout (egui :: Layout :: top_down (egui :: Align :: Center), | ui | {
// we will start adding elements here in the next sections
Define some helper functions we will use:
fn logo_and_heading ( ui : & mut egui :: Ui, logo : egui :: Image, heading : & str) {
let original_item_spacing_y = ui . style () . spacing . item_spacing . y;
ui . style_mut () . spacing . item_spacing . y = 8 . ;
ui . style_mut () . spacing . item_spacing . y = 16 . ;
ui . heading (egui :: RichText :: new ( heading ));
ui . style_mut () . spacing . item_spacing . y = original_item_spacing_y ;
fn control_label ( ui : & mut egui :: Ui, label : & str) {
let original_item_spacing_y = ui . style () . spacing . item_spacing . y;
ui . style_mut () . spacing . item_spacing . y = 8 . ;
ui . style_mut () . spacing . item_spacing . y = original_item_spacing_y ;
Load an image, allocate it as a texture and add it to the UI (requires the png
dependency):
let texture : & egui :: TextureHandle = self. texture . get_or_insert_with ( || {
let mut reader = png :: Decoder :: new (std :: io :: Cursor :: new ( include_bytes! ( " icons/32x32.png " )))
let mut buffer = Vec :: new ();
while let Ok(Some( row )) = reader . next_row () {
buffer . extend ( row . data ());
let icon_size = [ reader . info () . width as usize, reader . info () . height as usize];
// Load the texture only once.
egui :: ColorImage :: from_rgba_unmultiplied ( icon_size , & buffer ),
egui :: TextureFilter :: Linear,
egui :: Image :: new ( texture , texture . size_vec2 ()),
Add the user selection ComboBox:
ui . with_layout (egui :: Layout :: top_down (egui :: Align :: Min), | ui | {
control_label ( ui , " User " );
egui :: ComboBox :: from_id_source ( " user " )
. width ( ui . available_width () - 8 . )
. selected_text (egui :: RichText :: new ( user . clone ()) . family (egui :: FontFamily :: Monospace))
ui . selectable_value ( user , user_name . clone (), user_name . clone ());
Add an entry for password input:
ui . style_mut () . spacing . item_spacing . y = 20 . ;
. with_layout (egui :: Layout :: top_down (egui :: Align :: Min), | ui | {
ui . style_mut () . spacing . item_spacing . y = 0 . ;
control_label ( ui , " Password " );
ui . horizontal_wrapped ( | ui | {
let field = ui . add_sized (
[ ui . available_width (), 18 . ],
egui :: TextEdit :: singleline ( password ) . password ( true ),
let mut button = ui . add_enabled ( ! password . is_empty (), egui :: Button :: new ( " Unlock " ));
button . rect . min . x = 100 . ;
button . rect . max . x = 100 . ;
if ( textfield . lost_focus () && ui . input () . key_pressed (egui :: Key :: Enter)) || button . clicked ()
if password_checker ( & password ) {
let _ = tx . send ( password . clone ());
* heading = " Invalid password " . into ();
textfield . request_focus ();
Now that we have created the layout, let’s put it on a window and show it in the Tauri application:
tauri :: Builder :: default ()
app . wry_plugin (tauri_egui :: EguiPluginBuilder :: new ( app . handle ()));
// the closure that is called when the submit button is clicked - validate the password
let password_checker : Box< dyn Fn( & str) -> bool + Send> = Box :: new ( | s | s == " tauri-egui-released " );
let ( egui_app , rx ) = LoginLayout :: new (
vec! [ " John " . into (), " Jane " . into (), " Joe " . into ()],
let native_options = tauri_egui :: eframe :: NativeOptions {
. state :: <tauri_egui :: EguiPluginHandle>()
Box :: new ( | _cc | Box :: new ( egui_app )),
// wait for the window to be closed with the user data on another thread
// you don't need to spawn a thread when using e.g. an async command
std :: thread :: spawn ( move || {
if let Ok( signal ) = rx . recv () {
. run (tauri :: generate_context! ())
. expect ( " error while running tauri application " )
Here’s how it will look on all platforms:
To customize the look and feel of your egui application, check out the Context#set_style API .
© 2024 Tauri Contributors. CC-BY / MIT