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.
|
/// Check if the [`ComboBox`] with the given id has its popup menu currently opened.
|
||||||
pub fn is_open(ctx: &Context, id: Id) -> bool {
|
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.
|
/// 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>> {
|
) -> InnerResponse<Option<R>> {
|
||||||
let popup_id = ComboBox::widget_to_popup_id(button_id);
|
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());
|
let wrap_mode = wrap_mode.unwrap_or_else(|| ui.wrap_mode());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
|
use emath::{Align2, Vec2};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Area, Color32, Context, Frame, Id, InnerResponse, Order, Response, Sense, Ui, UiBuilder, UiKind,
|
Area, Color32, Context, Frame, Id, InnerResponse, Order, Response, Sense, Ui, UiBuilder, UiKind,
|
||||||
};
|
};
|
||||||
use emath::{Align2, Vec2};
|
|
||||||
|
|
||||||
/// A modal dialog.
|
/// A modal dialog.
|
||||||
///
|
///
|
||||||
|
|
@ -80,13 +81,11 @@ impl Modal {
|
||||||
frame,
|
frame,
|
||||||
} = self;
|
} = 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.set_modal_layer(area.layer());
|
||||||
(
|
mem.top_modal_layer() == Some(area.layer())
|
||||||
mem.top_modal_layer() == Some(area.layer()),
|
|
||||||
mem.any_popup_open(),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
let any_popup_open = crate::Popup::is_any_open(ctx);
|
||||||
let InnerResponse {
|
let InnerResponse {
|
||||||
inner: (inner, backdrop_response),
|
inner: (inner, backdrop_response),
|
||||||
response,
|
response,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
use crate::containers::menu::{MenuConfig, MenuState, menu_style};
|
#![expect(deprecated)] // This is a new, safe wrapper around the old `Memory::popup` API.
|
||||||
use crate::style::StyleModifier;
|
|
||||||
|
use std::iter::once;
|
||||||
|
|
||||||
|
use emath::{Align, Pos2, Rect, RectAlign, Vec2, vec2};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Area, AreaState, Context, Frame, Id, InnerResponse, Key, LayerId, Layout, Order, Response,
|
Area, AreaState, Context, Frame, Id, InnerResponse, Key, LayerId, Layout, Order, Response,
|
||||||
Sense, Ui, UiKind, UiStackInfo,
|
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?
|
/// What should we anchor the popup to?
|
||||||
///
|
///
|
||||||
|
|
@ -64,9 +68,7 @@ impl PopupAnchor {
|
||||||
match self {
|
match self {
|
||||||
Self::ParentRect(rect) => Some(rect),
|
Self::ParentRect(rect) => Some(rect),
|
||||||
Self::Pointer => ctx.pointer_hover_pos().map(Rect::from_pos),
|
Self::Pointer => ctx.pointer_hover_pos().map(Rect::from_pos),
|
||||||
Self::PointerFixed => ctx
|
Self::PointerFixed => Popup::position_of_id(ctx, popup_id).map(Rect::from_pos),
|
||||||
.memory(|mem| mem.popup_position(popup_id))
|
|
||||||
.map(Rect::from_pos),
|
|
||||||
Self::Position(pos) => Some(Rect::from_pos(pos)),
|
Self::Position(pos) => Some(Rect::from_pos(pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -122,12 +124,12 @@ enum OpenKind<'a> {
|
||||||
|
|
||||||
impl OpenKind<'_> {
|
impl OpenKind<'_> {
|
||||||
/// Returns `true` if the popup should be open
|
/// 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 {
|
match self {
|
||||||
OpenKind::Open => true,
|
OpenKind::Open => true,
|
||||||
OpenKind::Closed => false,
|
OpenKind::Closed => false,
|
||||||
OpenKind::Bool(open) => **open,
|
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.
|
/// See [`Self::menu`] and [`Self::context_menu`] for common use cases.
|
||||||
pub fn from_response(response: &Response) -> Self {
|
pub fn from_response(response: &Response) -> Self {
|
||||||
let mut popup = Self::new(
|
let mut popup = Self::new(
|
||||||
response.id.with("popup"),
|
Self::default_response_id(response),
|
||||||
response.ctx.clone(),
|
response.ctx.clone(),
|
||||||
response,
|
response,
|
||||||
response.layer_id,
|
response.layer_id,
|
||||||
|
|
@ -455,7 +457,7 @@ impl<'a> Popup<'a> {
|
||||||
OpenKind::Open => true,
|
OpenKind::Open => true,
|
||||||
OpenKind::Closed => false,
|
OpenKind::Closed => false,
|
||||||
OpenKind::Bool(open) => **open,
|
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;
|
let id = self.id;
|
||||||
if let OpenKind::Memory { set } = self.open_kind {
|
if let OpenKind::Memory { set } = self.open_kind {
|
||||||
self.ctx.memory_mut(|mem| match set {
|
match set {
|
||||||
Some(SetOpenCommand::Bool(open)) => {
|
Some(SetOpenCommand::Bool(open)) => {
|
||||||
if open {
|
if open {
|
||||||
match self.anchor {
|
match self.anchor {
|
||||||
PopupAnchor::PointerFixed => {
|
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 {
|
} else {
|
||||||
mem.close_popup(id);
|
Self::close_id(&self.ctx, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(SetOpenCommand::Toggle) => {
|
Some(SetOpenCommand::Toggle) => {
|
||||||
mem.toggle_popup(id);
|
Self::toggle_id(&self.ctx, id);
|
||||||
}
|
}
|
||||||
None => {
|
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) {
|
if !self.open_kind.is_open(self.id, &self.ctx) {
|
||||||
|
|
@ -627,3 +629,65 @@ impl<'a> Popup<'a> {
|
||||||
Some(response)
|
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
|
/// ## Deprecated popup API
|
||||||
/// Popups are things like combo-boxes, color pickers, menus etc.
|
/// Use [`crate::Popup`] instead.
|
||||||
/// Only one can be open at a time.
|
|
||||||
impl Memory {
|
impl Memory {
|
||||||
/// Is the given popup open?
|
/// Is the given popup open?
|
||||||
|
#[deprecated = "Use Popup::is_id_open instead"]
|
||||||
pub fn is_popup_open(&self, popup_id: Id) -> bool {
|
pub fn is_popup_open(&self, popup_id: Id) -> bool {
|
||||||
self.popups
|
self.popups
|
||||||
.get(&self.viewport_id)
|
.get(&self.viewport_id)
|
||||||
|
|
@ -1025,6 +1025,7 @@ impl Memory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is any popup open?
|
/// Is any popup open?
|
||||||
|
#[deprecated = "Use Popup::is_any_open instead"]
|
||||||
pub fn any_popup_open(&self) -> bool {
|
pub fn any_popup_open(&self) -> bool {
|
||||||
self.popups.contains_key(&self.viewport_id) || self.everything_is_visible()
|
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.
|
/// 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.
|
/// 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) {
|
pub fn open_popup(&mut self, popup_id: Id) {
|
||||||
self.popups
|
self.popups
|
||||||
.insert(self.viewport_id, OpenPopup::new(popup_id, None));
|
.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
|
/// 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
|
/// called. For example, when a context menu is open and the underlying widget stops
|
||||||
/// being rendered.
|
/// being rendered.
|
||||||
|
#[deprecated = "Use Popup::show instead"]
|
||||||
pub fn keep_popup_open(&mut self, popup_id: Id) {
|
pub fn keep_popup_open(&mut self, popup_id: Id) {
|
||||||
if let Some(state) = self.popups.get_mut(&self.viewport_id) {
|
if let Some(state) = self.popups.get_mut(&self.viewport_id) {
|
||||||
if state.id == popup_id {
|
if state.id == popup_id {
|
||||||
|
|
@ -1051,12 +1054,14 @@ impl Memory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open the popup and remember its position.
|
/// 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>>) {
|
pub fn open_popup_at(&mut self, popup_id: Id, pos: impl Into<Option<Pos2>>) {
|
||||||
self.popups
|
self.popups
|
||||||
.insert(self.viewport_id, OpenPopup::new(popup_id, pos.into()));
|
.insert(self.viewport_id, OpenPopup::new(popup_id, pos.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the position for this popup.
|
/// Get the position for this popup.
|
||||||
|
#[deprecated = "Use Popup::position_of_id instead"]
|
||||||
pub fn popup_position(&self, id: Id) -> Option<Pos2> {
|
pub fn popup_position(&self, id: Id) -> Option<Pos2> {
|
||||||
self.popups
|
self.popups
|
||||||
.get(&self.viewport_id)
|
.get(&self.viewport_id)
|
||||||
|
|
@ -1064,6 +1069,7 @@ impl Memory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close any currently open popup.
|
/// Close any currently open popup.
|
||||||
|
#[deprecated = "Use Popup::close_all instead"]
|
||||||
pub fn close_all_popups(&mut self) {
|
pub fn close_all_popups(&mut self) {
|
||||||
self.popups.clear();
|
self.popups.clear();
|
||||||
}
|
}
|
||||||
|
|
@ -1071,7 +1077,9 @@ impl Memory {
|
||||||
/// Close the given popup, if it is open.
|
/// Close the given popup, if it is open.
|
||||||
///
|
///
|
||||||
/// See also [`Self::close_all_popups`] if you want to close any / all currently open popups.
|
/// 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) {
|
pub fn close_popup(&mut self, popup_id: Id) {
|
||||||
|
#[expect(deprecated)]
|
||||||
if self.is_popup_open(popup_id) {
|
if self.is_popup_open(popup_id) {
|
||||||
self.popups.remove(&self.viewport_id);
|
self.popups.remove(&self.viewport_id);
|
||||||
}
|
}
|
||||||
|
|
@ -1080,14 +1088,18 @@ impl Memory {
|
||||||
/// Toggle the given popup between closed and open.
|
/// Toggle the given popup between closed and open.
|
||||||
///
|
///
|
||||||
/// Note: At most, only one popup can be open at a time.
|
/// 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) {
|
pub fn toggle_popup(&mut self, popup_id: Id) {
|
||||||
|
#[expect(deprecated)]
|
||||||
if self.is_popup_open(popup_id) {
|
if self.is_popup_open(popup_id) {
|
||||||
self.close_popup(popup_id);
|
self.close_popup(popup_id);
|
||||||
} else {
|
} else {
|
||||||
self.open_popup(popup_id);
|
self.open_popup(popup_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Memory {
|
||||||
/// If true, all windows, menus, tooltips, etc., will be visible at once.
|
/// If true, all windows, menus, tooltips, etc., will be visible at once.
|
||||||
///
|
///
|
||||||
/// This is useful for testing, benchmarking, pre-caching, etc.
|
/// 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 {
|
pub fn color_edit_button_hsva(ui: &mut Ui, hsva: &mut Hsva, alpha: Alpha) -> Response {
|
||||||
let popup_id = ui.auto_id_with("popup");
|
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);
|
let mut button_response = color_button(ui, (*hsva).into(), open);
|
||||||
if ui.style().explanation_tooltips {
|
if ui.style().explanation_tooltips {
|
||||||
button_response = button_response.on_hover_text("Click to edit color");
|
button_response = button_response.on_hover_text("Click to edit color");
|
||||||
|
|
|
||||||
|
|
@ -164,8 +164,8 @@ impl crate::View for Modals {
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::Demo as _;
|
use crate::Demo as _;
|
||||||
use crate::demo::modals::Modals;
|
use crate::demo::modals::Modals;
|
||||||
use egui::Key;
|
|
||||||
use egui::accesskit::Role;
|
use egui::accesskit::Role;
|
||||||
|
use egui::{Key, Popup};
|
||||||
use egui_kittest::kittest::Queryable as _;
|
use egui_kittest::kittest::Queryable as _;
|
||||||
use egui_kittest::{Harness, SnapshotResults};
|
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 would fail because we keep requesting repaints to simulate progress.
|
||||||
harness.run_ok();
|
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);
|
assert!(harness.state().user_modal_open);
|
||||||
|
|
||||||
harness.key_press(Key::Escape);
|
harness.key_press(Key::Escape);
|
||||||
harness.run_ok();
|
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);
|
assert!(harness.state().user_modal_open);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue