Skip to content

Commit

Permalink
Add modal
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmerlin committed Nov 5, 2024
1 parent ad14bf2 commit e6d0237
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 13 deletions.
1 change: 1 addition & 0 deletions crates/egui/src/containers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub(crate) mod area;
pub mod collapsing_header;
mod combo_box;
pub mod frame;
pub mod modal;
pub mod panel;
pub mod popup;
pub(crate) mod resize;
Expand Down
79 changes: 79 additions & 0 deletions crates/egui/src/containers/modal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use crate::{
Area, Color32, Context, Frame, Id, InnerResponse, Order, Response, Sense, Ui, UiBuilder,
};
use emath::{Align2, Vec2};

pub struct Modal {
pub area: Area,
pub backdrop_color: Color32,
pub frame: Option<Frame>,
}

pub struct ModalResponse<T> {
pub response: Response,
pub backdrop_response: Response,
pub inner: T,
}

impl Modal {
pub fn new(id: Id) -> Self {
Self {
area: Area::new(id)
.sense(Sense::hover())
.anchor(Align2::CENTER_CENTER, Vec2::ZERO)
.order(Order::Foreground),
backdrop_color: Color32::from_black_alpha(100),
frame: None,
}
}

pub fn show<T>(self, ctx: &Context, content: impl FnOnce(&mut Ui) -> T) -> ModalResponse<T> {
let InnerResponse {
inner: (inner, backdrop_response),
response,
} = self.area.show(ctx, |ui| {
// TODO: Is screen_rect the right thing to use here?
let mut backdrop = ui.new_child(UiBuilder::new().max_rect(ui.ctx().screen_rect()));
let backdrop_response = backdrop_ui(&mut backdrop, self.backdrop_color);

let frame = self.frame.unwrap_or_else(|| Frame::popup(ui.style()));

// We need the extra scope with the sense since frame can't have a sense and since we
// need to prevent the clicks from passing through to the backdrop.
let inner = ui
.scope_builder(
UiBuilder::new().sense(Sense {
click: true,
drag: true,
focusable: false,
}),
|ui| frame.show(ui, content).inner,
)
.inner;

(inner, backdrop_response)
});

ModalResponse {
response,
backdrop_response,
inner,
}
}
}

fn backdrop_ui(ui: &mut Ui, color: Color32) -> Response {
// Ensure we capture any click and drag events
let response = ui.allocate_response(
ui.available_size(),
Sense {
click: true,
drag: true,
focusable: false,
},
);

ui.painter().rect_filled(response.rect, 0.0, color);

response
}
110 changes: 97 additions & 13 deletions examples/hello_world_simple/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#![allow(rustdoc::missing_crate_level_docs)] // it's an example

use eframe::egui;
use eframe::egui::modal::Modal;
use eframe::egui::{Align, ComboBox, Id, Layout, ProgressBar, Widget};

fn main() -> eframe::Result {
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
Expand All @@ -11,23 +13,105 @@ fn main() -> eframe::Result {
..Default::default()
};

// Our application state:
let mut name = "Arthur".to_owned();
let mut age = 42;
let mut save_modal_open = false;
let mut user_modal_open = false;
let mut save_progress = None;

let roles = ["user", "admin"];
let mut role = roles[0];

let mut name = "John Doe".to_string();

eframe::run_simple_native("My egui App", options, move |ctx, _frame| {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("My egui Application");
ui.horizontal(|ui| {
let name_label = ui.label("Your name: ");
ui.text_edit_singleline(&mut name)
.labelled_by(name_label.id);
});
ui.add(egui::Slider::new(&mut age, 0..=120).text("age"));
if ui.button("Increment").clicked() {
age += 1;
if ui.button("Open Modal A").clicked() {
save_modal_open = true;
}

if ui.button("Open Modal B").clicked() {
user_modal_open = true;
}

if save_modal_open {
let modal = Modal::new(Id::new("Modal A")).show(ui.ctx(), |ui| {
ui.set_width(250.0);

ui.heading("Edit User");

ui.label("Name:");
ui.text_edit_singleline(&mut name);

ComboBox::new("role", "Role")
.selected_text(role)
.show_ui(ui, |ui| {
for r in &roles {
ui.selectable_value(&mut role, r, *r);
}
});

ui.separator();

ui.with_layout(Layout::right_to_left(Align::Min), |ui| {
if ui.button("Save").clicked() {
user_modal_open = true;
}
if ui.button("Cancel").clicked() {
save_modal_open = false;
}
});
});

if modal.backdrop_response.clicked() {
save_modal_open = false;
}
}

if user_modal_open {
let modal = Modal::new(Id::new("Modal B")).show(ui.ctx(), |ui| {
ui.set_width(200.0);
ui.heading("Save? Are you sure?");

ui.add_space(32.0);

ui.with_layout(Layout::right_to_left(Align::Min), |ui| {
if ui.button("Yes Please").clicked() {
save_progress = Some(0.0);
}

if ui.button("No Thanks").clicked() {
user_modal_open = false;
}
});
});

if modal.backdrop_response.clicked() {
user_modal_open = false;
}
}

if let Some(progress) = save_progress {
let modal = Modal::new(Id::new("Modal C")).show(ui.ctx(), |ui| {
ui.set_width(70.0);
ui.heading("Saving...");

ProgressBar::new(progress).ui(ui);

if progress >= 1.0 {
save_progress = None;
user_modal_open = false;
save_modal_open = false;
} else {
save_progress = Some(progress + 0.003);
ui.ctx().request_repaint();
}
});
}
});

egui::Window::new("My Window").show(ctx, |ui| {
if ui.button("show modal").clicked() {
user_modal_open = true;
}
ui.label(format!("Hello '{name}', age {age}"));
});
})
}

0 comments on commit e6d0237

Please sign in to comment.