Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 3 additions & 1 deletion crates/egui/src/containers/popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,9 @@ impl<'a> Popup<'a> {
Some(SetOpenCommand::Toggle) => {
mem.toggle_popup(id);
}
None => {}
None => {
mem.keep_popup_open(id);
}
});
}

Expand Down
58 changes: 53 additions & 5 deletions crates/egui/src/memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pub struct Memory {
/// If position is [`None`], the popup position will be calculated based on some configuration
/// (e.g. relative to some other widget).
#[cfg_attr(feature = "persistence", serde(skip))]
popup: Option<(Id, Option<Pos2>)>,
popup: Option<OpenPopup>,

#[cfg_attr(feature = "persistence", serde(skip))]
everything_is_visible: bool,
Expand Down Expand Up @@ -807,6 +807,15 @@ impl Memory {
self.caches.update();
self.areas_mut().end_pass();
self.focus_mut().end_pass(used_ids);

// Clean up abandoned popups.
if let Some(popup) = &mut self.popup {
if popup.open_this_frame {
popup.open_this_frame = false;
} else {
self.popup = None;
}
}
}

pub(crate) fn set_viewport_id(&mut self, viewport_id: ViewportId) {
Expand Down Expand Up @@ -1068,13 +1077,37 @@ impl Memory {
}
}

/// State of an open popup.
#[derive(Clone, Copy, Debug)]
struct OpenPopup {
/// Id of the popup.
id: Id,

/// Optional position of the popup.
pos: Option<Pos2>,

/// Whether this popup was still open this frame. Otherwise it's considered abandoned and `Memory::popup` will be cleared.
open_this_frame: bool,
}

impl OpenPopup {
/// Create a new `OpenPopup`.
fn new(id: Id, pos: Option<Pos2>) -> Self {
Self {
id,
pos,
open_this_frame: true,
}
}
}

/// ## Popups
/// Popups are things like combo-boxes, color pickers, menus etc.
/// Only one can be open at a time.
impl Memory {
/// Is the given popup open?
pub fn is_popup_open(&self, popup_id: Id) -> bool {
self.popup.is_some_and(|(id, _)| id == popup_id) || self.everything_is_visible()
self.popup.is_some_and(|state| state.id == popup_id) || self.everything_is_visible()
}

/// Is any popup open?
Expand All @@ -1083,19 +1116,34 @@ impl Memory {
}

/// Open the given popup and close all others.
///
/// Note that you must call `keep_popup_open` on subsequent frames as long as the popup is open.
pub fn open_popup(&mut self, popup_id: Id) {
self.popup = Some((popup_id, None));
self.popup = Some(OpenPopup::new(popup_id, None));
}

/// Popups must call this every frame while open.
///
/// This is needed because in some cases popups can go away without `close_popup` being
/// called. For example, when a context menu is open and the underlying widget stops
/// being rendered.
pub fn keep_popup_open(&mut self, popup_id: Id) {
Copy link
Owner

Choose a reason for hiding this comment

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

I feel like there is a more elegant solution to this problem that I'm not seeing 😆

We could replace is_popup_open with show_popup_if_open(popup_id, closure), but that's a bit annoying too (especially when it comes to lifetimes and locks), so… no.

I'm also not in love with this function name, but again fail to come up with something better.

so… LGTM :)

if let Some(state) = self.popup.as_mut() {
if state.id == popup_id {
state.open_this_frame = true;
}
}
}

/// Open the popup and remember its position.
pub fn open_popup_at(&mut self, popup_id: Id, pos: impl Into<Option<Pos2>>) {
self.popup = Some((popup_id, pos.into()));
self.popup = Some(OpenPopup::new(popup_id, pos.into()));
}

/// Get the position for this popup.
pub fn popup_position(&self, id: Id) -> Option<Pos2> {
self.popup
.and_then(|(popup_id, pos)| if popup_id == id { pos } else { None })
.and_then(|state| if state.id == id { state.pos } else { None })
}

/// Close the open popup, if any.
Expand Down