Add `PopupCloseBehavior` (#4636)
This PR adds `PopupCloseBehavior` to improve state of the <https://github.com/emilk/egui/issues/4607> `PopupCloseBehavior` determines when popup will be closed. - `CloseOnClick` popup will be closed if the click happens anywhere even in the popup's body - `CloseOnClickAway` popup will be closed if the click happens somewhere else but in the popup's body. It also adds a test in the demo app which contains several popups examples. --- My ideas about <https://github.com/emilk/egui/issues/4607> is to make every tooltip and popup a menu. So it will provide more control over popups and tooltips (you will be able to close a popup by calling something similar to the `ui.close_menu` if you need to). You won't need to manually handle it's opening. And also will allow to have multiple popups opened. That means you can have a popup inside a popup. And it will also lead to the easier creation of the popups. (should we create a tracking issue to track changes because to me it seems like a huge amount of changes to be done?) --- - Improvements on <https://github.com/emilk/egui/issues/4607>
This commit is contained in:
parent
ab861574f4
commit
5051e945e4
|
|
@ -2883,6 +2883,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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -253,11 +253,29 @@ pub fn was_tooltip_open_last_frame(ctx: &Context, widget_id: Id) -> bool {
|
|||
})
|
||||
}
|
||||
|
||||
/// Determines popup's close behavior
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PopupCloseBehavior {
|
||||
/// 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
|
||||
CloseOnClickOutside,
|
||||
|
||||
/// Clicks will be ignored. Popup might be closed manually by calling [`Memory::close_popup`]
|
||||
/// or by pressing the escape button
|
||||
IgnoreClicks,
|
||||
}
|
||||
|
||||
/// 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(
|
||||
|
|
@ -265,6 +283,7 @@ pub fn popup_below_widget<R>(
|
|||
popup_id,
|
||||
widget_response,
|
||||
AboveOrBelow::Below,
|
||||
close_behavior,
|
||||
add_contents,
|
||||
)
|
||||
}
|
||||
|
|
@ -287,7 +306,8 @@ pub fn popup_below_widget<R>(
|
|||
/// ui.memory_mut(|mem| mem.toggle_popup(popup_id));
|
||||
/// }
|
||||
/// let below = egui::AboveOrBelow::Below;
|
||||
/// egui::popup::popup_above_or_below_widget(ui, popup_id, &response, below, |ui| {
|
||||
/// let close_on_click_outside = egui::popup::PopupCloseBehavior::CloseOnClickOutside;
|
||||
/// egui::popup::popup_above_or_below_widget(ui, popup_id, &response, below, close_on_click_outside, |ui| {
|
||||
/// ui.set_min_width(200.0); // if you want to control the size
|
||||
/// ui.label("Some more info, or things you can select:");
|
||||
/// ui.label("…");
|
||||
|
|
@ -299,6 +319,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)) {
|
||||
|
|
@ -317,7 +338,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)
|
||||
|
|
@ -333,13 +354,20 @@ pub fn popup_above_or_below_widget<R>(
|
|||
.inner
|
||||
})
|
||||
.inner
|
||||
})
|
||||
.inner;
|
||||
});
|
||||
|
||||
if parent_ui.input(|i| i.key_pressed(Key::Escape)) || widget_response.clicked_elsewhere() {
|
||||
let should_close = match close_behavior {
|
||||
PopupCloseBehavior::CloseOnClick => widget_response.clicked_elsewhere(),
|
||||
PopupCloseBehavior::CloseOnClickOutside => {
|
||||
widget_response.clicked_elsewhere() && response.response.clicked_elsewhere()
|
||||
}
|
||||
PopupCloseBehavior::IgnoreClicks => false,
|
||||
};
|
||||
|
||||
if parent_ui.input(|i| i.key_pressed(Key::Escape)) || should_close {
|
||||
parent_ui.memory_mut(|mem| mem.close_popup());
|
||||
}
|
||||
Some(inner)
|
||||
Some(response.inner)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
Example of how to use menus, popups, context menus and tooltips.
|
||||
|
||||
```sh
|
||||
cargo run -p popups
|
||||
```
|
||||
|
|
@ -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::CloseOnClickOutside,
|
||||
|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}"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue