#dialog #egui #customizable #standard #platform-independent #information #id

egui_dialogs

Platform-agnostic, customizable dialogs for egui library

15 releases

0.3.5 Oct 14, 2024
0.3.4 Oct 14, 2024
0.3.1 Sep 30, 2024
0.2.6 Sep 28, 2024
0.1.1 Sep 21, 2024

#425 in GUI

MIT/Apache

56KB
848 lines

egui_dialogs

Platform-agnostic, customizable dialogs for egui library.

Information Dialog Preview

Quick start

Run the example

# cd into the crate directory
cargo run --example dialogs

Basic usage

Install the crate:

cargo add egui_dialogs

Then add a Dialogs field to your App struct:

use egui_dialogs::Dialogs;

pub struct MyApp<'a> {
    // ... your other app states
    dialogs: Dialogs<'a>,
}

Then somewhere in your App::update function:

impl MyApp<'_> {
    // ... your other app logic
    
    pub fn update(&mut self, ctx: &egui::Context) {
        self.dialogs.show(ctx);
        
        // ... your other rendering logic
    }
}

And when you want to show a dialog:

self.dialogs.info("Information", "This is an info dialog");

Handle reply

Using callback functions

Use DialogDetails struct to build a dialog with custom attributes.

The following is an example to comfirm a window close request:

use std::{cell::RefCell, rc::Rc};

use egui_dialogs::{DialogDetails, StandardReply};

// in your app state
pub allow_to_close: Rc<RefCell<bool>>,
// and initialize it with false

// when received a close request in the update function
if ctx.input(|i| i.viewport().close_requested()) {
    let ctx = ctx.clone();
    let allow_to_close = Rc::clone(&self.allow_to_close);

    if *allow_to_close.borrow() {
        // run your close logic
    } else {
        // build and show a confirm dialog
        DialogDetails::confirm("Close", "Are you sure you want to close the window?")
            .on_reply(move |res| {
                if res == StandardReply::Ok {
                    *allow_to_close.borrow_mut() = true;
                    ctx.send_viewport_cmd(egui::ViewportCommand::Close);
                }
            })
            .show(&mut self.dialogs);
    }
}

Using IDs

As rust compiler thinks that your dialog callbacks may be called at any time, it limits your callback from visiting your app states.

To avoid filling your app state struct with Rc and RefCell, you can specify an ID for your dialog and use it to identify the dialog reply when a dialog is closed:

use egui::Id;
use egui_dialogs::{DialogDetails, StandardReply};

// in your app state
pub allow_to_close: bool,
// and initialize it with false

// define an ID for your dialog
const CLOSE_CONFIRM_DIALOG_ID: &str = "close_confirm_dialog";

// in your update function
if let Some(res) = self.dialogs.show(ctx) {
    // handle reply from close confirmation dialog
    if res.is_reply_of(CLOSE_CONFIRM_DIALOG_ID) {
        match res.reply() {
            Ok(StandardReply::Yes) => {
                self.allow_to_close = true;
                ctx.send_viewport_cmd(egui::ViewportCommand::Close);
            },
            _ => {},
        }
    }
}

// when you want to show the dialog
if ctx.input(|i| i.viewport().close_requested()) {
    if !self.allow_to_close {
        ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose);
        DialogDetails::confirm("Close", "Are you sure you want to close the window?")
            // specify the dialog ID
            .with_id(CLOSE_CONFIRM_DIALOG_ID)
            .show_if_absent(&mut self.dialogs);
    }
}

Customization

Customize standard dialog

You can show a customized dialog based on the standard dialogs:

use egui::include_image;
use egui_dialogs::{DialogDetails, StandardDialog, StandardReply};

let standard_dialog = StandardDialog::info("Information", "Now you can customize the dialog!")
    .buttons(vec![
        // use the standard buttons
        StandardReply::Yes.into(),
        // or add custom buttons with specific replies
        ("What?".into(), StandardReply::No)
    ])
    .image(include_image!("assets/info.svg"));

DialogDetails::new(standard_dialog)
    .on_reply(|res| {
        match res {
            StandardReply::Yes => println!("That's great!"),
            StandardReply::No => println!("Emm...maybe you can try to see the example?"),
            _ => panic!("I've never added such a reply!")
        }
    })
    .show(&mut self.dialogs);

Customize dialog appearance and behavior

To show a completely customized dialog, you can first design your dialog state struct like this:

pub struct NameConfirmDialog {
    name: String,
}

Then implement the Dialog trait to implement dialog logic with a generic type parameter to specify the dialog reply type:

use egui_dialogs::{Dialog, dialog_window, DialogContext};

impl Dialog<String> for NameConfirmDialog {
  fn show(&mut self, ctx: &egui::Context, dctx: &DialogContext) -> Option<String> {
    // return None if the user hasn't replied
    let mut res = None;

    // draw the dialog
    dialog_window(ctx, dctx, "Confirm name")
      .show(ctx, |ui| {
        ui.label("What's your name: ");
        ui.text_edit_singleline(&mut self.name);
        if ui.button("Done").clicked() {
          // set the reply and end the dialog
          res = Some(self.name.clone());
        }
      });
      
    res
  }
}

The dialog_window function is a helper function to draw a suggested dialog window with a title and a close button.

Now you can show your customized dialog:

DialogDetails::new(NameConfirmDialog { name: "".into() })
    .on_reply(|res| {
        println!("Your name is {}", res);
    })
    .show(&mut self.dialogs);

Dependencies

~4–9MB
~85K SLoC