Deprecate `Memory::popup` API in favor of new `Popup` API (#7317)
* Closes #7037 * Closes #7297 This deprecates all popup-related function in `Memory`, replacing them with the new `egui::Popup`. The new API is nicer in all ways, so we should encourage people to use it.
This commit is contained in:
parent
fbe0aadf63
commit
a7f14ca176
|
|
@ -293,7 +293,7 @@ impl ComboBox {
|
|||
|
||||
/// Check if the [`ComboBox`] with the given id has its popup menu currently opened.
|
||||
pub fn is_open(ctx: &Context, id: Id) -> bool {
|
||||
ctx.memory(|m| m.is_popup_open(Self::widget_to_popup_id(id)))
|
||||
Popup::is_id_open(ctx, Self::widget_to_popup_id(id))
|
||||
}
|
||||
|
||||
/// Convert a [`ComboBox`] id to the id used to store it's popup state.
|
||||
|
|
@ -315,7 +315,7 @@ fn combo_box_dyn<'c, R>(
|
|||
) -> InnerResponse<Option<R>> {
|
||||
let popup_id = ComboBox::widget_to_popup_id(button_id);
|
||||
|
||||
let is_popup_open = ui.memory(|m| m.is_popup_open(popup_id));
|
||||
let is_popup_open = Popup::is_id_open(ui.ctx(), popup_id);
|
||||
|
||||
let wrap_mode = wrap_mode.unwrap_or_else(|| ui.wrap_mode());
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use emath::{Align2, Vec2};
|
||||
|
||||
use crate::{
|
||||
Area, Color32, Context, Frame, Id, InnerResponse, Order, Response, Sense, Ui, UiBuilder, UiKind,
|
||||
};
|
||||
use emath::{Align2, Vec2};
|
||||
|
||||
/// A modal dialog.
|
||||
///
|
||||
|
|
@ -80,13 +81,11 @@ impl Modal {
|
|||
frame,
|
||||
} = self;
|
||||
|
||||
let (is_top_modal, any_popup_open) = ctx.memory_mut(|mem| {
|
||||
let is_top_modal = ctx.memory_mut(|mem| {
|
||||
mem.set_modal_layer(area.layer());
|
||||
(
|
||||
mem.top_modal_layer() == Some(area.layer()),
|
||||
mem.any_popup_open(),
|
||||
)
|
||||
mem.top_modal_layer() == Some(area.layer())
|
||||
});
|
||||
let any_popup_open = crate::Popup::is_any_open(ctx);
|
||||
let InnerResponse {
|
||||
inner: (inner, backdrop_response),
|
||||
response,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
use crate::containers::menu::{MenuConfig, MenuState, menu_style};
|
||||
use crate::style::StyleModifier;
|
||||
#![expect(deprecated)] // This is a new, safe wrapper around the old `Memory::popup` API.
|
||||
|
||||
use std::iter::once;
|
||||
|
||||
use emath::{Align, Pos2, Rect, RectAlign, Vec2, vec2};
|
||||
|
||||
use crate::{
|
||||
Area, AreaState, Context, Frame, Id, InnerResponse, Key, LayerId, Layout, Order, Response,
|
||||
Sense, Ui, UiKind, UiStackInfo,
|
||||
containers::menu::{MenuConfig, MenuState, menu_style},
|
||||
style::StyleModifier,
|
||||
};
|
||||
use emath::{Align, Pos2, Rect, RectAlign, Vec2, vec2};
|
||||
use std::iter::once;
|
||||
|
||||
/// What should we anchor the popup to?
|
||||
///
|
||||
|
|
@ -64,9 +68,7 @@ impl PopupAnchor {
|
|||
match self {
|
||||
Self::ParentRect(rect) => Some(rect),
|
||||
Self::Pointer => ctx.pointer_hover_pos().map(Rect::from_pos),
|
||||
Self::PointerFixed => ctx
|
||||
.memory(|mem| mem.popup_position(popup_id))
|
||||
.map(Rect::from_pos),
|
||||
Self::PointerFixed => Popup::position_of_id(ctx, popup_id).map(Rect::from_pos),
|
||||
Self::Position(pos) => Some(Rect::from_pos(pos)),
|
||||
}
|
||||
}
|
||||
|
|
@ -122,12 +124,12 @@ enum OpenKind<'a> {
|
|||
|
||||
impl OpenKind<'_> {
|
||||
/// Returns `true` if the popup should be open
|
||||
fn is_open(&self, id: Id, ctx: &Context) -> bool {
|
||||
fn is_open(&self, popup_id: Id, ctx: &Context) -> bool {
|
||||
match self {
|
||||
OpenKind::Open => true,
|
||||
OpenKind::Closed => false,
|
||||
OpenKind::Bool(open) => **open,
|
||||
OpenKind::Memory { .. } => ctx.memory(|mem| mem.is_popup_open(id)),
|
||||
OpenKind::Memory { .. } => Popup::is_id_open(ctx, popup_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -217,7 +219,7 @@ impl<'a> Popup<'a> {
|
|||
/// See [`Self::menu`] and [`Self::context_menu`] for common use cases.
|
||||
pub fn from_response(response: &Response) -> Self {
|
||||
let mut popup = Self::new(
|
||||
response.id.with("popup"),
|
||||
Self::default_response_id(response),
|
||||
response.ctx.clone(),
|
||||
response,
|
||||
response.layer_id,
|
||||
|
|
@ -455,7 +457,7 @@ impl<'a> Popup<'a> {
|
|||
OpenKind::Open => true,
|
||||
OpenKind::Closed => false,
|
||||
OpenKind::Bool(open) => **open,
|
||||
OpenKind::Memory { .. } => self.ctx.memory(|mem| mem.is_popup_open(self.id)),
|
||||
OpenKind::Memory { .. } => Self::is_id_open(&self.ctx, self.id),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -504,26 +506,26 @@ impl<'a> Popup<'a> {
|
|||
|
||||
let id = self.id;
|
||||
if let OpenKind::Memory { set } = self.open_kind {
|
||||
self.ctx.memory_mut(|mem| match set {
|
||||
match set {
|
||||
Some(SetOpenCommand::Bool(open)) => {
|
||||
if open {
|
||||
match self.anchor {
|
||||
PopupAnchor::PointerFixed => {
|
||||
mem.open_popup_at(id, hover_pos);
|
||||
self.ctx.memory_mut(|mem| mem.open_popup_at(id, hover_pos));
|
||||
}
|
||||
_ => mem.open_popup(id),
|
||||
_ => Popup::open_id(&self.ctx, id),
|
||||
}
|
||||
} else {
|
||||
mem.close_popup(id);
|
||||
Self::close_id(&self.ctx, id);
|
||||
}
|
||||
}
|
||||
Some(SetOpenCommand::Toggle) => {
|
||||
mem.toggle_popup(id);
|
||||
Self::toggle_id(&self.ctx, id);
|
||||
}
|
||||
None => {
|
||||
mem.keep_popup_open(id);
|
||||
self.ctx.memory_mut(|mem| mem.keep_popup_open(id));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if !self.open_kind.is_open(self.id, &self.ctx) {
|
||||
|
|
@ -627,3 +629,65 @@ impl<'a> Popup<'a> {
|
|||
Some(response)
|
||||
}
|
||||
}
|
||||
|
||||
/// ## Static methods
|
||||
impl Popup<'_> {
|
||||
/// The default ID when constructing a popup from the [`Response`] of e.g. a button.
|
||||
pub fn default_response_id(response: &Response) -> Id {
|
||||
response.id.with("popup")
|
||||
}
|
||||
|
||||
/// Is the given popup open?
|
||||
///
|
||||
/// This assumes the use of either:
|
||||
/// * [`Self::open_memory`]
|
||||
/// * [`Self::from_toggle_button_response`]
|
||||
/// * [`Self::menu`]
|
||||
/// * [`Self::context_menu`]
|
||||
///
|
||||
/// The popup id should be the same as either you set with [`Self::id`] or the
|
||||
/// default one from [`Self::default_response_id`].
|
||||
pub fn is_id_open(ctx: &Context, popup_id: Id) -> bool {
|
||||
ctx.memory(|mem| mem.is_popup_open(popup_id))
|
||||
}
|
||||
|
||||
/// Is any popup open?
|
||||
///
|
||||
/// This assumes the egui memory is being used to track the open state of popups.
|
||||
pub fn is_any_open(ctx: &Context) -> bool {
|
||||
ctx.memory(|mem| mem.any_popup_open())
|
||||
}
|
||||
|
||||
/// Open the given popup and close all others.
|
||||
///
|
||||
/// If you are NOT using [`Popup::show`], you must
|
||||
/// also call [`crate::Memory::keep_popup_open`] as long as
|
||||
/// you're showing the popup.
|
||||
pub fn open_id(ctx: &Context, popup_id: Id) {
|
||||
ctx.memory_mut(|mem| mem.open_popup(popup_id));
|
||||
}
|
||||
|
||||
/// Toggle the given popup between closed and open.
|
||||
///
|
||||
/// Note: At most, only one popup can be open at a time.
|
||||
pub fn toggle_id(ctx: &Context, popup_id: Id) {
|
||||
ctx.memory_mut(|mem| mem.toggle_popup(popup_id));
|
||||
}
|
||||
|
||||
/// Close all currently open popups.
|
||||
pub fn close_all(ctx: &Context) {
|
||||
ctx.memory_mut(|mem| mem.close_all_popups());
|
||||
}
|
||||
|
||||
/// Close the given popup, if it is open.
|
||||
///
|
||||
/// See also [`Self::close_all`] if you want to close any / all currently open popups.
|
||||
pub fn close_id(ctx: &Context, popup_id: Id) {
|
||||
ctx.memory_mut(|mem| mem.close_popup(popup_id));
|
||||
}
|
||||
|
||||
/// Get the position for this popup, if it is open.
|
||||
pub fn position_of_id(ctx: &Context, popup_id: Id) -> Option<Pos2> {
|
||||
ctx.memory(|mem| mem.popup_position(popup_id))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1012,11 +1012,11 @@ impl OpenPopup {
|
|||
}
|
||||
}
|
||||
|
||||
/// ## Popups
|
||||
/// Popups are things like combo-boxes, color pickers, menus etc.
|
||||
/// Only one can be open at a time.
|
||||
/// ## Deprecated popup API
|
||||
/// Use [`crate::Popup`] instead.
|
||||
impl Memory {
|
||||
/// Is the given popup open?
|
||||
#[deprecated = "Use Popup::is_id_open instead"]
|
||||
pub fn is_popup_open(&self, popup_id: Id) -> bool {
|
||||
self.popups
|
||||
.get(&self.viewport_id)
|
||||
|
|
@ -1025,6 +1025,7 @@ impl Memory {
|
|||
}
|
||||
|
||||
/// Is any popup open?
|
||||
#[deprecated = "Use Popup::is_any_open instead"]
|
||||
pub fn any_popup_open(&self) -> bool {
|
||||
self.popups.contains_key(&self.viewport_id) || self.everything_is_visible()
|
||||
}
|
||||
|
|
@ -1032,6 +1033,7 @@ 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.
|
||||
#[deprecated = "Use Popup::open_id instead"]
|
||||
pub fn open_popup(&mut self, popup_id: Id) {
|
||||
self.popups
|
||||
.insert(self.viewport_id, OpenPopup::new(popup_id, None));
|
||||
|
|
@ -1042,6 +1044,7 @@ impl Memory {
|
|||
/// 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.
|
||||
#[deprecated = "Use Popup::show instead"]
|
||||
pub fn keep_popup_open(&mut self, popup_id: Id) {
|
||||
if let Some(state) = self.popups.get_mut(&self.viewport_id) {
|
||||
if state.id == popup_id {
|
||||
|
|
@ -1051,12 +1054,14 @@ impl Memory {
|
|||
}
|
||||
|
||||
/// Open the popup and remember its position.
|
||||
#[deprecated = "Use Popup with PopupAnchor::Position instead"]
|
||||
pub fn open_popup_at(&mut self, popup_id: Id, pos: impl Into<Option<Pos2>>) {
|
||||
self.popups
|
||||
.insert(self.viewport_id, OpenPopup::new(popup_id, pos.into()));
|
||||
}
|
||||
|
||||
/// Get the position for this popup.
|
||||
#[deprecated = "Use Popup::position_of_id instead"]
|
||||
pub fn popup_position(&self, id: Id) -> Option<Pos2> {
|
||||
self.popups
|
||||
.get(&self.viewport_id)
|
||||
|
|
@ -1064,6 +1069,7 @@ impl Memory {
|
|||
}
|
||||
|
||||
/// Close any currently open popup.
|
||||
#[deprecated = "Use Popup::close_all instead"]
|
||||
pub fn close_all_popups(&mut self) {
|
||||
self.popups.clear();
|
||||
}
|
||||
|
|
@ -1071,7 +1077,9 @@ impl Memory {
|
|||
/// Close the given popup, if it is open.
|
||||
///
|
||||
/// See also [`Self::close_all_popups`] if you want to close any / all currently open popups.
|
||||
#[deprecated = "Use Popup::close_id instead"]
|
||||
pub fn close_popup(&mut self, popup_id: Id) {
|
||||
#[expect(deprecated)]
|
||||
if self.is_popup_open(popup_id) {
|
||||
self.popups.remove(&self.viewport_id);
|
||||
}
|
||||
|
|
@ -1080,14 +1088,18 @@ impl Memory {
|
|||
/// Toggle the given popup between closed and open.
|
||||
///
|
||||
/// Note: At most, only one popup can be open at a time.
|
||||
#[deprecated = "Use Popup::toggle_id instead"]
|
||||
pub fn toggle_popup(&mut self, popup_id: Id) {
|
||||
#[expect(deprecated)]
|
||||
if self.is_popup_open(popup_id) {
|
||||
self.close_popup(popup_id);
|
||||
} else {
|
||||
self.open_popup(popup_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
/// If true, all windows, menus, tooltips, etc., will be visible at once.
|
||||
///
|
||||
/// This is useful for testing, benchmarking, pre-caching, etc.
|
||||
|
|
|
|||
|
|
@ -491,7 +491,7 @@ pub fn color_picker_color32(ui: &mut Ui, srgba: &mut Color32, alpha: Alpha) -> b
|
|||
|
||||
pub fn color_edit_button_hsva(ui: &mut Ui, hsva: &mut Hsva, alpha: Alpha) -> Response {
|
||||
let popup_id = ui.auto_id_with("popup");
|
||||
let open = ui.memory(|mem| mem.is_popup_open(popup_id));
|
||||
let open = Popup::is_id_open(ui.ctx(), popup_id);
|
||||
let mut button_response = color_button(ui, (*hsva).into(), open);
|
||||
if ui.style().explanation_tooltips {
|
||||
button_response = button_response.on_hover_text("Click to edit color");
|
||||
|
|
|
|||
|
|
@ -164,8 +164,8 @@ impl crate::View for Modals {
|
|||
mod tests {
|
||||
use crate::Demo as _;
|
||||
use crate::demo::modals::Modals;
|
||||
use egui::Key;
|
||||
use egui::accesskit::Role;
|
||||
use egui::{Key, Popup};
|
||||
use egui_kittest::kittest::Queryable as _;
|
||||
use egui_kittest::{Harness, SnapshotResults};
|
||||
|
||||
|
|
@ -187,12 +187,12 @@ mod tests {
|
|||
|
||||
// Harness::run would fail because we keep requesting repaints to simulate progress.
|
||||
harness.run_ok();
|
||||
assert!(harness.ctx.memory(|mem| mem.any_popup_open()));
|
||||
assert!(Popup::is_any_open(&harness.ctx));
|
||||
assert!(harness.state().user_modal_open);
|
||||
|
||||
harness.key_press(Key::Escape);
|
||||
harness.run_ok();
|
||||
assert!(!harness.ctx.memory(|mem| mem.any_popup_open()));
|
||||
assert!(!Popup::is_any_open(&harness.ctx));
|
||||
assert!(harness.state().user_modal_open);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue