Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PopupCloseBehavior #4636

Merged
merged 6 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2867,6 +2867,14 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"

[[package]]
name = "popups"
version = "0.27.2"
dependencies = [
"eframe",
"env_logger",
]

[[package]]
name = "powerfmt"
version = "0.2.0"
Expand Down
1 change: 1 addition & 0 deletions crates/egui/src/containers/combo_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ fn combo_box_dyn<'c, R>(
popup_id,
&button_response,
above_or_below,
PopupCloseBehavior::CloseOnClick,
|ui| {
ScrollArea::vertical()
.max_height(height)
Expand Down
31 changes: 26 additions & 5 deletions crates/egui/src/containers/popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,18 +253,32 @@ pub fn was_tooltip_open_last_frame(ctx: &Context, widget_id: Id) -> bool {
})
}

/// Determines popup's close behavior
#[derive(Clone, Copy)]
pub enum PopupCloseBehavior {
emilk marked this conversation as resolved.
Show resolved Hide resolved
/// Popup will be closed on click
/// It is used in the [`ComboBox`]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Popup will be closed on click
/// It is used in the [`ComboBox`]
/// Popup will be closed on click anywhere, inside or outside the popup.
///
/// It is used in [`ComboBox`].

CloseOnClick,

/// Popup will be closed if the click happened somewhere else
/// but in the popup's body
CloseOnClickAway,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
CloseOnClickAway,
CloseOnClickOutside,

}

/// Helper for [`popup_above_or_below_widget`].
pub fn popup_below_widget<R>(
ui: &Ui,
popup_id: Id,
widget_response: &Response,
close_behavior: PopupCloseBehavior,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> Option<R> {
popup_above_or_below_widget(
ui,
popup_id,
widget_response,
AboveOrBelow::Below,
close_behavior,
add_contents,
)
}
Expand Down Expand Up @@ -299,6 +313,7 @@ pub fn popup_above_or_below_widget<R>(
popup_id: Id,
widget_response: &Response,
above_or_below: AboveOrBelow,
close_behavior: PopupCloseBehavior,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> Option<R> {
if parent_ui.memory(|mem| mem.is_popup_open(popup_id)) {
Expand All @@ -311,7 +326,7 @@ pub fn popup_above_or_below_widget<R>(
let frame_margin = frame.total_margin();
let inner_width = widget_response.rect.width() - frame_margin.sum().x;

let inner = Area::new(popup_id)
let response = Area::new(popup_id)
.kind(UiKind::Popup)
.order(Order::Foreground)
.fixed_pos(pos)
Expand All @@ -327,13 +342,19 @@ pub fn popup_above_or_below_widget<R>(
.inner
})
.inner
})
.inner;
});

let is_close = match close_behavior {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let is_close = match close_behavior {
let should_close = match close_behavior {

("is close" sounds like it is about proximity)

PopupCloseBehavior::CloseOnClick => widget_response.clicked_elsewhere(),
PopupCloseBehavior::CloseOnClickAway => {
widget_response.clicked_elsewhere() && response.response.clicked_elsewhere()
}
};

if parent_ui.input(|i| i.key_pressed(Key::Escape)) || widget_response.clicked_elsewhere() {
if parent_ui.input(|i| i.key_pressed(Key::Escape)) || is_close {
parent_ui.memory_mut(|mem| mem.close_popup());
}
Some(inner)
Some(response.inner)
} else {
None
}
Expand Down
22 changes: 22 additions & 0 deletions examples/popups/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "popups"
edition.workspace = true
license.workspace = true
rust-version.workspace = true
version.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
eframe = { workspace = true, features = [
"default",
"__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO
] }
env_logger = { version = "0.10", default-features = false, features = [
"auto-color",
"humantime",
] }


[lints]
workspace = true
5 changes: 5 additions & 0 deletions examples/popups/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Example of how to use menus, popups, context menus and tooltips.

```sh
cargo run -p popups
```
52 changes: 52 additions & 0 deletions examples/popups/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
#![allow(rustdoc::missing_crate_level_docs)] // it's an example

use eframe::egui::*;

fn main() -> Result<(), eframe::Error> {
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
let options = eframe::NativeOptions::default();

eframe::run_native("Popups", options, Box::new(|_| Ok(Box::<MyApp>::default())))
}

#[derive(Default)]
struct MyApp {
checkbox: bool,
number: u8,
}

impl eframe::App for MyApp {
fn update(&mut self, ctx: &eframe::egui::Context, _frame: &mut eframe::Frame) {
CentralPanel::default().show(ctx, |ui| {
ui.label("PopupCloseBehavior::CloseOnClickAway popup");
let response = ui.button("Open");
let popup_id = Id::new("popup_id");

if response.clicked() {
ui.memory_mut(|mem| mem.toggle_popup(popup_id));
}

popup_below_widget(
ui,
popup_id,
&response,
PopupCloseBehavior::CloseOnClickAway,
|ui| {
ui.set_min_width(300.0);
ui.label("This popup will be open even if you click the checkbox");
ui.checkbox(&mut self.checkbox, "Checkbox");
},
);

ui.label("PopupCloseBehavior::CloseOnClick popup");
ComboBox::from_label("ComboBox")
.selected_text(format!("{}", self.number))
.show_ui(ui, |ui| {
for num in 0..10 {
ui.selectable_value(&mut self.number, num, format!("{num}"));
}
});
});
}
}