Skip to main content

egui/memory/
mod.rs

1#![warn(missing_docs)] // Let's keep this file well-documented.` to memory.rs
2
3use core::num::NonZeroUsize;
4
5use ahash::{HashMap, HashSet};
6use epaint::emath::TSTransform;
7
8use crate::{
9    EventFilter, Id, IdMap, LayerId, Order, Pos2, Rangef, RawInput, Rect, Style, Vec2, ViewportId,
10    ViewportIdMap, ViewportIdSet, area, vec2,
11};
12
13mod theme;
14pub use theme::{Theme, ThemePreference};
15
16// ----------------------------------------------------------------------------
17
18/// The data that egui persists between frames.
19///
20/// This includes window positions and sizes,
21/// how far the user has scrolled in a [`ScrollArea`](crate::ScrollArea) etc.
22///
23/// If you want this to persist when closing your app, you should serialize [`Memory`] and store it.
24/// For this you need to enable the `persistence` feature.
25///
26/// If you want to store data for your widgets, you should look at [`Memory::data`]
27#[derive(Clone, Debug)]
28#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
29#[cfg_attr(feature = "persistence", serde(default))]
30pub struct Memory {
31    /// Global egui options.
32    pub options: Options,
33
34    /// This map stores some superficial state for all widgets with custom [`Id`]s.
35    ///
36    /// This includes storing whether a [`crate::CollapsingHeader`] is open, how far scrolled a
37    /// [`crate::ScrollArea`] is, where the cursor in a [`crate::TextEdit`] is, etc.
38    ///
39    /// This is NOT meant to store any important data. Store that in your own structures!
40    ///
41    /// Each read clones the data, so keep your values cheap to clone.
42    /// If you want to store a lot of data, you should wrap it in `Arc<Mutex<…>>` so it is cheap to clone.
43    ///
44    /// This will be saved between different program runs if you use the `persistence` feature.
45    ///
46    /// To store a state common for all your widgets (a singleton), use [`Id::NULL`] as the key.
47    pub data: crate::util::IdTypeMap,
48
49    // ------------------------------------------
50    /// Can be used to cache computations from one frame to another.
51    ///
52    /// This is for saving CPU time when you have something that may take 1-100ms to compute.
53    /// Very slow operations (>100ms) should instead be done async (i.e. in another thread)
54    /// so as not to lock the UI thread.
55    ///
56    /// ```
57    /// use egui::cache::{ComputerMut, FrameCache};
58    ///
59    /// #[derive(Default)]
60    /// struct CharCounter {}
61    /// impl ComputerMut<&str, usize> for CharCounter {
62    ///     fn compute(&mut self, s: &str) -> usize {
63    ///         s.chars().count() // you probably want to cache something more expensive than this
64    ///     }
65    /// }
66    /// type CharCountCache<'a> = FrameCache<usize, CharCounter>;
67    ///
68    /// # let mut ctx = egui::Context::default();
69    /// ctx.memory_mut(|mem| {
70    ///     let cache = mem.caches.cache::<CharCountCache<'_>>();
71    ///     assert_eq!(*cache.get("hello"), 5);
72    /// });
73    /// ```
74    #[cfg_attr(feature = "persistence", serde(skip))]
75    pub caches: crate::cache::CacheStorage,
76
77    // ------------------------------------------
78    /// new fonts that will be applied at the start of the next frame
79    #[cfg_attr(feature = "persistence", serde(skip))]
80    pub(crate) new_font_definitions: Option<epaint::text::FontDefinitions>,
81
82    /// add new font that will be applied at the start of the next frame
83    #[cfg_attr(feature = "persistence", serde(skip))]
84    pub(crate) add_fonts: Vec<epaint::text::FontInsert>,
85
86    // Current active viewport
87    #[cfg_attr(feature = "persistence", serde(skip))]
88    pub(crate) viewport_id: ViewportId,
89
90    #[cfg_attr(feature = "persistence", serde(skip))]
91    everything_is_visible: bool,
92
93    /// Transforms per layer.
94    ///
95    /// Instead of using this directly, use:
96    /// * [`crate::Context::set_transform_layer`]
97    /// * [`crate::Context::layer_transform_to_global`]
98    /// * [`crate::Context::layer_transform_from_global`]
99    pub to_global: HashMap<LayerId, TSTransform>,
100
101    // -------------------------------------------------
102    // Per-viewport:
103    areas: ViewportIdMap<Areas>,
104
105    #[cfg_attr(feature = "persistence", serde(skip))]
106    pub(crate) interactions: ViewportIdMap<InteractionState>,
107
108    #[cfg_attr(feature = "persistence", serde(skip))]
109    pub(crate) focus: ViewportIdMap<Focus>,
110
111    /// Which popup-window is open on a viewport (if any)?
112    /// Could be a combo box, color picker, menu, etc.
113    /// Optionally stores the position of the popup (usually this would be the position where
114    /// the user clicked).
115    /// If position is [`None`], the popup position will be calculated based on some configuration
116    /// (e.g. relative to some other widget).
117    #[cfg_attr(feature = "persistence", serde(skip))]
118    popups: ViewportIdMap<OpenPopup>,
119
120    /// Whether to inform the backend to interrupt any ongoing IME composition
121    /// this pass.
122    #[cfg_attr(feature = "persistence", serde(skip))]
123    requested_interrupt_ime: bool,
124}
125
126impl Default for Memory {
127    fn default() -> Self {
128        let mut slf = Self {
129            options: Default::default(),
130            data: Default::default(),
131            caches: Default::default(),
132            new_font_definitions: Default::default(),
133            interactions: Default::default(),
134            focus: Default::default(),
135            viewport_id: Default::default(),
136            areas: Default::default(),
137            to_global: Default::default(),
138            popups: Default::default(),
139            everything_is_visible: Default::default(),
140            add_fonts: Default::default(),
141            requested_interrupt_ime: Default::default(),
142        };
143        slf.interactions.entry(slf.viewport_id).or_default();
144        slf.areas.entry(slf.viewport_id).or_default();
145        slf
146    }
147}
148
149/// A direction in which to move the keyboard focus.
150#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
151pub enum FocusDirection {
152    /// Select the widget closest above the current focused widget.
153    Up,
154
155    /// Select the widget to the right of the current focused widget.
156    Right,
157
158    /// Select the widget below the current focused widget.
159    Down,
160
161    /// Select the widget to the left of the current focused widget.
162    Left,
163
164    /// Select the previous widget that had focus.
165    Previous,
166
167    /// Select the next widget that wants focus.
168    Next,
169
170    /// Don't change focus.
171    #[default]
172    None,
173}
174
175impl FocusDirection {
176    fn is_cardinal(&self) -> bool {
177        match self {
178            Self::Up | Self::Right | Self::Down | Self::Left => true,
179
180            Self::Previous | Self::Next | Self::None => false,
181        }
182    }
183}
184
185// ----------------------------------------------------------------------------
186
187/// Some global options that you can read and write.
188///
189/// See also [`crate::style::DebugOptions`].
190#[derive(Clone, Debug, PartialEq)]
191#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
192#[cfg_attr(feature = "serde", serde(default))]
193pub struct Options {
194    /// The default style for new [`Ui`](crate::Ui):s in dark mode.
195    #[cfg_attr(feature = "serde", serde(skip))]
196    pub dark_style: std::sync::Arc<Style>,
197
198    /// The default style for new [`Ui`](crate::Ui):s in light mode.
199    #[cfg_attr(feature = "serde", serde(skip))]
200    pub light_style: std::sync::Arc<Style>,
201
202    /// Preference for selection between dark and light [`crate::Context::global_style`]
203    /// as the active style used by all subsequent windows, panels, etc.
204    ///
205    /// Default: `ThemePreference::System`.
206    pub theme_preference: ThemePreference,
207
208    /// Which theme to use in case [`Self::theme_preference`] is [`ThemePreference::System`]
209    /// and egui fails to detect the system theme.
210    ///
211    /// Default: [`crate::Theme::Dark`].
212    pub fallback_theme: Theme,
213
214    /// The current system theme, used to choose between
215    /// dark and light style in case [`Self::theme_preference`] is [`ThemePreference::System`].
216    #[cfg_attr(feature = "serde", serde(skip))]
217    pub(crate) system_theme: Option<Theme>,
218
219    /// If `true`, egui will keep the native window theme in sync with
220    /// [`Self::theme_preference`] by sending a [`crate::ViewportCommand::SetTheme`]
221    /// to the root viewport whenever the preference changes.
222    ///
223    /// This makes the native window decorations (title bar, borders, …) match the
224    /// theme selected inside egui.
225    ///
226    /// Set this to `false` if you want to manage the native window theme yourself.
227    ///
228    /// This is `true` by default.
229    pub sync_window_theme: bool,
230
231    /// Global zoom factor of the UI.
232    ///
233    /// This is used to calculate the `pixels_per_point`
234    /// for the UI as `pixels_per_point = zoom_fator * native_pixels_per_point`.
235    ///
236    /// The default is 1.0. Increase it to make all UI elements larger.
237    ///
238    /// You should call [`crate::Context::set_zoom_factor`]
239    /// instead of modifying this directly!
240    pub zoom_factor: f32,
241
242    /// If `true`, egui will change the scale of the ui ([`crate::Context::zoom_factor`]) when the user
243    /// presses Cmd+Plus, Cmd+Minus or Cmd+0, just like in a browser.
244    ///
245    /// This is `true` by default.
246    ///
247    /// On the web-backend of `eframe` this is set to false by default,
248    /// so that the zoom shortcuts are handled exclusively by the browser,
249    /// which will change the `native_pixels_per_point` (`devicePixelRatio`).
250    /// You can still zoom egui independently by calling [`crate::Context::set_zoom_factor`],
251    /// which will be applied on top of the browsers global zoom.
252    #[cfg_attr(feature = "serde", serde(skip))]
253    pub zoom_with_keyboard: bool,
254
255    /// Keyboard shortcuts to close the application.
256    ///
257    /// Pressing any of these will send [`crate::ViewportCommand::Close`]
258    /// to the root viewport.
259    ///
260    /// Defaults to `Cmd-Q` (which is Ctrl-Q on Linux/Windows, Cmd-Q on Mac).
261    /// Set to empty to disable.
262    #[cfg_attr(feature = "serde", serde(skip))]
263    pub quit_shortcuts: Vec<crate::KeyboardShortcut>,
264
265    /// Controls the tessellator.
266    pub tessellation_options: epaint::TessellationOptions,
267
268    /// If any widget moves or changes id, repaint everything.
269    ///
270    /// It is recommended you keep this OFF, as it may
271    /// lead to endless repaints for an unknown reason. See
272    /// (<https://github.com/rerun-io/rerun/issues/5018>).
273    pub repaint_on_widget_change: bool,
274
275    /// Maximum number of passes to run in one frame.
276    ///
277    /// Set to `1` for pure single-pass immediate mode.
278    /// Set to something larger than `1` to allow multi-pass when needed.
279    ///
280    /// Default is `2`. This means sometimes a frame will cost twice as much,
281    /// but usually only rarely (e.g. when showing a new panel for the first time).
282    ///
283    /// egui will usually only ever run one pass, even if `max_passes` is large.
284    ///
285    /// If this is `1`, [`crate::Context::request_discard`] will be ignored.
286    ///
287    /// Multi-pass is supported by [`crate::Context::run_ui`].
288    ///
289    /// See [`crate::Context::request_discard`] for more.
290    pub max_passes: NonZeroUsize,
291
292    /// This is a signal to any backend that we want the [`crate::PlatformOutput::events`] read out loud.
293    ///
294    /// The only change to egui is that labels can be focused by pressing tab.
295    ///
296    /// Screen readers are an experimental feature of egui, and not supported on all platforms.
297    /// `eframe` only supports it on web.
298    ///
299    /// Consider using [AccessKit](https://github.com/AccessKit/accesskit) instead,
300    /// which is supported by `eframe`.
301    pub screen_reader: bool,
302
303    /// Check reusing of [`Id`]s, and show a visual warning on screen when one is found.
304    ///
305    /// By default this is `true` in debug builds.
306    pub warn_on_id_clash: bool,
307
308    /// Options related to input state handling.
309    pub input_options: crate::input_state::InputOptions,
310
311    /// If `true`, `egui` will discard the loaded image data after
312    /// the texture is loaded onto the GPU to reduce memory usage.
313    ///
314    /// In modern GPU rendering, the texture data is not required after the texture is loaded.
315    ///
316    /// This is beneficial when using a large number or resolution of images and there is no need to
317    /// retain the image data, potentially saving a significant amount of memory.
318    ///
319    /// The drawback is that it becomes impossible to serialize the loaded images or render in non-GPU systems.
320    ///
321    /// Default is `false`.
322    pub reduce_texture_memory: bool,
323}
324
325impl Default for Options {
326    fn default() -> Self {
327        Self {
328            dark_style: std::sync::Arc::new(Theme::Dark.default_style()),
329            light_style: std::sync::Arc::new(Theme::Light.default_style()),
330            theme_preference: Default::default(),
331            fallback_theme: Theme::Dark,
332            system_theme: None,
333            sync_window_theme: true,
334            zoom_factor: 1.0,
335            zoom_with_keyboard: true,
336            quit_shortcuts: vec![crate::KeyboardShortcut::new(
337                crate::Modifiers::COMMAND,
338                crate::Key::Q,
339            )],
340            tessellation_options: Default::default(),
341            repaint_on_widget_change: false,
342
343            #[expect(clippy::unwrap_used)]
344            max_passes: NonZeroUsize::new(2).unwrap(),
345            screen_reader: false,
346            warn_on_id_clash: cfg!(debug_assertions),
347
348            // Input:
349            input_options: Default::default(),
350            reduce_texture_memory: false,
351        }
352    }
353}
354
355impl Options {
356    // Needs to be pub because we need to set the system_theme early in the eframe glow renderer.
357    #[doc(hidden)]
358    pub fn begin_pass(&mut self, new_raw_input: &RawInput) {
359        self.system_theme = new_raw_input.system_theme;
360    }
361
362    /// The currently active theme (may depend on the system theme).
363    pub(crate) fn theme(&self) -> Theme {
364        match self.theme_preference {
365            ThemePreference::Dark => Theme::Dark,
366            ThemePreference::Light => Theme::Light,
367            ThemePreference::System => self.system_theme.unwrap_or(self.fallback_theme),
368        }
369    }
370
371    pub(crate) fn style(&self) -> &std::sync::Arc<Style> {
372        match self.theme() {
373            Theme::Dark => &self.dark_style,
374            Theme::Light => &self.light_style,
375        }
376    }
377
378    pub(crate) fn style_mut(&mut self) -> &mut std::sync::Arc<Style> {
379        match self.theme() {
380            Theme::Dark => &mut self.dark_style,
381            Theme::Light => &mut self.light_style,
382        }
383    }
384}
385
386impl Options {
387    /// Show the options in the ui.
388    pub fn ui(&mut self, ui: &mut crate::Ui) {
389        let theme = self.theme();
390
391        let Self {
392            dark_style, // covered above
393            light_style,
394            theme_preference,
395            fallback_theme: _,
396            system_theme: _,
397            sync_window_theme,
398            zoom_factor,
399            zoom_with_keyboard,
400            quit_shortcuts: _, // not shown in ui
401            tessellation_options,
402            repaint_on_widget_change,
403            max_passes,
404            screen_reader: _, // needs to come from the integration
405            warn_on_id_clash,
406            input_options,
407            reduce_texture_memory,
408        } = self;
409
410        use crate::Widget as _;
411        use crate::containers::CollapsingHeader;
412
413        CollapsingHeader::new("⚙ Options")
414            .default_open(false)
415            .show(ui, |ui| {
416                ui.horizontal(|ui| {
417                    ui.label("Max passes:");
418                    ui.add(crate::DragValue::new(max_passes).range(0..=10));
419                });
420
421                ui.checkbox(
422                    repaint_on_widget_change,
423                    "Repaint if any widget moves or changes id",
424                );
425
426                ui.horizontal(|ui| {
427                    ui.label("Zoom factor:");
428                    ui.add(crate::DragValue::new(zoom_factor).range(0.10..=10.0));
429                });
430
431                ui.checkbox(
432                    zoom_with_keyboard,
433                    "Zoom with keyboard (Cmd +, Cmd -, Cmd 0)",
434                );
435
436                ui.checkbox(warn_on_id_clash, "Warn if two widgets have the same Id");
437
438                ui.checkbox(reduce_texture_memory, "Reduce texture memory");
439            });
440
441        CollapsingHeader::new("🎑 Style")
442            .default_open(true)
443            .show(ui, |ui| {
444                theme_preference.radio_buttons(ui);
445
446                ui.checkbox(sync_window_theme, "Sync window theme with egui theme");
447
448                let style = std::sync::Arc::make_mut(match theme {
449                    Theme::Dark => dark_style,
450                    Theme::Light => light_style,
451                });
452                style.ui(ui);
453            });
454
455        CollapsingHeader::new("✒ Painting")
456            .default_open(false)
457            .show(ui, |ui| {
458                tessellation_options.ui(ui);
459                ui.vertical_centered(|ui| {
460                    crate::reset_button(ui, tessellation_options, "Reset paint settings");
461                });
462            });
463
464        CollapsingHeader::new("🖱 Input")
465            .default_open(false)
466            .show(ui, |ui| {
467                input_options.ui(ui);
468            });
469
470        ui.vertical_centered(|ui| crate::reset_button(ui, self, "Reset all"));
471    }
472}
473
474// ----------------------------------------------------------------------------
475
476/// The state of the interaction in egui,
477/// i.e. what is being dragged.
478///
479/// Say there is a button in a scroll area.
480/// If the user clicks the button, the button should click.
481/// If the user drags the button we should scroll the scroll area.
482/// Therefore, when the mouse is pressed, we register both the button
483/// and the scroll area (as `click_id`/`drag_id`).
484/// If the user releases the button without moving the mouse, we register it as a click on `click_id`.
485/// If the cursor moves too much, we clear the `click_id` and start passing move events to `drag_id`.
486#[derive(Clone, Debug, Default)]
487pub(crate) struct InteractionState {
488    /// A widget interested in clicks that has a mouse press on it.
489    pub potential_click_id: Option<Id>,
490
491    /// A widget interested in drags that has a mouse press on it.
492    ///
493    /// Note that this is set as soon as the mouse is pressed,
494    /// so the widget may not yet be marked as "dragged"
495    /// as that can only happen after the mouse has moved a bit
496    /// (at least if the widget is interesated in both clicks and drags).
497    pub potential_drag_id: Option<Id>,
498}
499
500/// Keeps tracks of what widget has keyboard focus
501#[derive(Clone, Debug, Default)]
502pub(crate) struct Focus {
503    /// The widget with keyboard focus (i.e. a text input field).
504    focused_widget: Option<FocusWidget>,
505
506    /// The ID of a widget that had keyboard focus during the previous frame.
507    id_previous_frame: Option<Id>,
508
509    /// The ID of a widget that had keyboard focus *two* frames ago.
510    ///
511    /// Kept so `Response::lost_focus` can still fire after a mid-frame
512    /// focus transition (e.g. clicking a `TextEdit` that was added to
513    /// the UI later than the currently focused one).
514    id_two_frames_ago: Option<Id>,
515
516    /// The ID of a widget to give the focus to in the next frame.
517    id_next_frame: Option<Id>,
518
519    id_requested_by_accesskit: Option<accesskit::NodeId>,
520
521    /// If set, the next widget that is interested in focus will automatically get it.
522    /// Probably because the user pressed Tab.
523    give_to_next: bool,
524
525    /// The last widget interested in focus.
526    last_interested: Option<Id>,
527
528    /// Set when looking for widget with navigational keys like arrows, tab, shift+tab.
529    focus_direction: FocusDirection,
530
531    /// The top-most modal layer from the previous frame.
532    top_modal_layer: Option<LayerId>,
533
534    /// The top-most modal layer from the current frame.
535    top_modal_layer_current_frame: Option<LayerId>,
536
537    /// A cache of widget IDs that are interested in focus with their corresponding rectangles.
538    focus_widgets_cache: IdMap<Rect>,
539}
540
541/// The widget with focus.
542#[derive(Clone, Copy, Debug)]
543struct FocusWidget {
544    pub id: Id,
545    pub filter: EventFilter,
546}
547
548impl FocusWidget {
549    pub fn new(id: Id) -> Self {
550        Self {
551            id,
552            filter: Default::default(),
553        }
554    }
555}
556
557impl InteractionState {
558    /// Are we currently clicking or dragging an egui widget?
559    pub fn is_using_pointer(&self) -> bool {
560        self.potential_click_id.is_some() || self.potential_drag_id.is_some()
561    }
562}
563
564impl Focus {
565    /// Which widget currently has keyboard focus?
566    pub fn focused(&self) -> Option<Id> {
567        self.focused_widget.as_ref().map(|w| w.id)
568    }
569
570    fn begin_pass(&mut self, new_input: &crate::data::input::RawInput) {
571        self.id_two_frames_ago = self.id_previous_frame;
572        self.id_previous_frame = self.focused();
573        if let Some(id) = self.id_next_frame.take() {
574            self.focused_widget = Some(FocusWidget::new(id));
575        }
576        let event_filter = self.focused_widget.map(|w| w.filter).unwrap_or_default();
577
578        self.id_requested_by_accesskit = None;
579
580        self.focus_direction = FocusDirection::None;
581
582        for event in &new_input.events {
583            if !event_filter.matches(event)
584                && let crate::Event::Key {
585                    key,
586                    pressed: true,
587                    modifiers,
588                    ..
589                } = event
590                && let Some(cardinality) = match key {
591                    crate::Key::ArrowUp if !modifiers.any() => Some(FocusDirection::Up),
592                    crate::Key::ArrowRight if !modifiers.any() => Some(FocusDirection::Right),
593                    crate::Key::ArrowDown if !modifiers.any() => Some(FocusDirection::Down),
594                    crate::Key::ArrowLeft if !modifiers.any() => Some(FocusDirection::Left),
595
596                    crate::Key::Tab if !modifiers.any() => Some(FocusDirection::Next),
597                    crate::Key::Tab if modifiers.shift_only() => Some(FocusDirection::Previous),
598
599                    crate::Key::Escape if !modifiers.any() => {
600                        self.focused_widget = None;
601                        Some(FocusDirection::None)
602                    }
603
604                    _ => None,
605                }
606            {
607                self.focus_direction = cardinality;
608            }
609
610            if let crate::Event::AccessKitActionRequest(accesskit::ActionRequest {
611                action: accesskit::Action::Focus,
612                target_node,
613                target_tree,
614                data: None,
615            }) = event
616                && *target_tree == accesskit::TreeId::ROOT
617            {
618                self.id_requested_by_accesskit = Some(*target_node);
619            }
620        }
621    }
622
623    pub(crate) fn end_pass(&mut self, used_ids: &IdMap<Rect>) {
624        if self.focus_direction.is_cardinal()
625            && let Some(found_widget) = self.find_widget_in_direction(used_ids)
626        {
627            self.focused_widget = Some(FocusWidget::new(found_widget));
628        }
629
630        if let Some(focused_widget) = self.focused_widget {
631            // Allow calling `request_focus` one frame and not using it until next frame
632            let recently_gained_focus = self.id_previous_frame != Some(focused_widget.id);
633
634            if !recently_gained_focus && !used_ids.contains_key(&focused_widget.id) {
635                // Dead-mans-switch: the widget with focus has disappeared!
636                self.focused_widget = None;
637            }
638        }
639
640        self.top_modal_layer = self.top_modal_layer_current_frame.take();
641    }
642
643    pub(crate) fn had_focus_last_frame(&self, id: Id) -> bool {
644        self.id_previous_frame == Some(id)
645    }
646
647    fn interested_in_focus(&mut self, id: Id) {
648        if self.id_requested_by_accesskit == Some(id.accesskit_id()) {
649            self.focused_widget = Some(FocusWidget::new(id));
650            self.id_requested_by_accesskit = None;
651            self.give_to_next = false;
652            self.reset_focus();
653        }
654
655        // The rect is updated at the end of the frame.
656        self.focus_widgets_cache
657            .entry(id)
658            .or_insert(Rect::EVERYTHING);
659
660        if self.give_to_next && !self.had_focus_last_frame(id) {
661            self.focused_widget = Some(FocusWidget::new(id));
662            self.give_to_next = false;
663        } else if self.focused() == Some(id) {
664            if self.focus_direction == FocusDirection::Next {
665                self.focused_widget = None;
666                self.give_to_next = true;
667                self.reset_focus();
668            } else if self.focus_direction == FocusDirection::Previous {
669                self.id_next_frame = self.last_interested; // frame-delay so gained_focus works
670                self.reset_focus();
671            }
672        } else if self.focus_direction == FocusDirection::Next
673            && self.focused_widget.is_none()
674            && !self.give_to_next
675        {
676            // nothing has focus and the user pressed tab - give focus to the first widgets that wants it:
677            self.focused_widget = Some(FocusWidget::new(id));
678            self.reset_focus();
679        } else if self.focus_direction == FocusDirection::Previous
680            && self.focused_widget.is_none()
681            && !self.give_to_next
682        {
683            // nothing has focus and the user pressed Shift+Tab - give focus to the last widgets that wants it:
684            self.focused_widget = self.last_interested.map(FocusWidget::new);
685            self.reset_focus();
686        }
687
688        self.last_interested = Some(id);
689    }
690
691    fn set_modal_layer(&mut self, layer_id: LayerId) {
692        self.top_modal_layer_current_frame = Some(layer_id);
693    }
694
695    pub(crate) fn top_modal_layer(&self) -> Option<LayerId> {
696        self.top_modal_layer
697    }
698
699    fn reset_focus(&mut self) {
700        self.focus_direction = FocusDirection::None;
701    }
702
703    fn find_widget_in_direction(&mut self, new_rects: &IdMap<Rect>) -> Option<Id> {
704        // NOTE: `new_rects` here include some widgets _not_ interested in focus.
705
706        /// * negative if `a` is left of `b`
707        /// * positive if `a` is right of `b`
708        /// * zero if the ranges overlap significantly
709        fn range_diff(a: Rangef, b: Rangef) -> f32 {
710            let has_significant_overlap = a.intersection(b).span() >= 0.5 * b.span().min(a.span());
711            if has_significant_overlap {
712                0.0
713            } else {
714                a.center() - b.center()
715            }
716        }
717
718        let current_focused = self.focused_widget?;
719
720        // In what direction we are looking for the next widget.
721        let search_direction = match self.focus_direction {
722            FocusDirection::Up => Vec2::UP,
723            FocusDirection::Right => Vec2::RIGHT,
724            FocusDirection::Down => Vec2::DOWN,
725            FocusDirection::Left => Vec2::LEFT,
726            _ => {
727                return None;
728            }
729        };
730
731        // Update cache with new rects
732        self.focus_widgets_cache.retain(|id, old_rect| {
733            if let Some(new_rect) = new_rects.get(id) {
734                *old_rect = *new_rect;
735                true // Keep the item
736            } else {
737                false // Remove the item
738            }
739        });
740
741        let current_rect = self.focus_widgets_cache.get(&current_focused.id)?;
742
743        let mut best_score = f32::INFINITY;
744        let mut best_id = None;
745
746        // iteration order should only matter in case of a tie, and that should be very rare
747        #[expect(clippy::iter_over_hash_type)]
748        for (candidate_id, candidate_rect) in &self.focus_widgets_cache {
749            if *candidate_id == current_focused.id {
750                continue;
751            }
752
753            // There is a lot of room for improvement here.
754            let to_candidate = vec2(
755                range_diff(candidate_rect.x_range(), current_rect.x_range()),
756                range_diff(candidate_rect.y_range(), current_rect.y_range()),
757            );
758
759            let acos_angle = to_candidate.normalized().dot(search_direction);
760
761            // Only interested in widgets that fall in a 90° cone (±45°)
762            // of the search direction.
763            let is_in_search_cone = 0.5_f32.sqrt() <= acos_angle;
764            if is_in_search_cone {
765                let distance = to_candidate.length();
766
767                // There is a lot of room for improvement here.
768                let score = distance / (acos_angle * acos_angle);
769
770                if score < best_score {
771                    best_score = score;
772                    best_id = Some(*candidate_id);
773                }
774            }
775        }
776
777        best_id
778    }
779}
780
781impl Memory {
782    pub(crate) fn begin_pass(&mut self, new_raw_input: &RawInput, viewports: &ViewportIdSet) {
783        profiling::function_scope!();
784
785        self.viewport_id = new_raw_input.viewport_id;
786
787        // Cleanup
788        self.interactions.retain(|id, _| viewports.contains(id));
789        self.areas.retain(|id, _| viewports.contains(id));
790        self.popups.retain(|id, _| viewports.contains(id));
791
792        self.areas.entry(self.viewport_id).or_default();
793
794        self.requested_interrupt_ime = false;
795
796        // self.interactions  is handled elsewhere
797
798        self.options.begin_pass(new_raw_input);
799
800        self.focus
801            .entry(self.viewport_id)
802            .or_default()
803            .begin_pass(new_raw_input);
804    }
805
806    pub(crate) fn end_pass(&mut self, used_ids: &IdMap<Rect>) {
807        self.caches.update();
808        self.areas_mut().end_pass();
809        self.focus_mut().end_pass(used_ids);
810
811        // Clean up abandoned popups.
812        if let Some(popup) = self.popups.get_mut(&self.viewport_id) {
813            if popup.open_this_frame {
814                popup.open_this_frame = false;
815            } else {
816                self.popups.remove(&self.viewport_id);
817            }
818        }
819    }
820
821    pub(crate) fn set_viewport_id(&mut self, viewport_id: ViewportId) {
822        self.viewport_id = viewport_id;
823    }
824
825    /// Access memory of the [`Area`](crate::containers::area::Area)s, such as `Window`s.
826    pub fn areas(&self) -> &Areas {
827        self.areas
828            .get(&self.viewport_id)
829            .expect("Memory broken: no area for the current viewport")
830    }
831
832    /// Access memory of the [`Area`](crate::containers::area::Area)s, such as `Window`s.
833    pub fn areas_mut(&mut self) -> &mut Areas {
834        self.areas.entry(self.viewport_id).or_default()
835    }
836
837    /// Top-most layer at the given position.
838    pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
839        let layer_id = self.areas().layer_id_at(pos, &self.to_global)?;
840        if self.is_above_modal_layer(layer_id) {
841            Some(layer_id)
842        } else {
843            self.top_modal_layer()
844        }
845    }
846
847    /// An iterator over all layers. Back-to-front, top is last.
848    pub fn layer_ids(&self) -> impl ExactSizeIterator<Item = LayerId> + '_ {
849        self.areas().order().iter().copied()
850    }
851
852    /// Check if the layer had focus last frame.
853    /// returns `true` if the layer had focus last frame, but not this one.
854    pub fn had_focus_last_frame(&self, id: Id) -> bool {
855        self.focus().and_then(|f| f.id_previous_frame) == Some(id)
856    }
857
858    /// Check if the widget lost keyboard focus.
859    ///
860    /// Returns `true` when `id` was the focused widget at the start
861    /// of this frame *or* the start of the previous frame — but is
862    /// not focused now. The two-frame window matters when focus
863    /// transfers mid-frame: the previously-focused widget has
864    /// usually already been rendered by the time another widget
865    /// claims focus, so the loss signal can only reach it on its
866    /// next render pass.
867    pub(crate) fn lost_focus(&self, id: Id) -> bool {
868        let had_recent_focus = self
869            .focus()
870            .map(|f| f.id_previous_frame == Some(id) || f.id_two_frames_ago == Some(id))
871            .unwrap_or(false);
872        had_recent_focus && !self.has_focus(id)
873    }
874
875    /// Check if the layer gained focus this frame.
876    /// returns `true` if the layer gained focus this frame, but not last one.
877    pub(crate) fn gained_focus(&self, id: Id) -> bool {
878        !self.had_focus_last_frame(id) && self.has_focus(id)
879    }
880
881    /// Does this widget have keyboard focus?
882    ///
883    /// This function does not consider whether the UI as a whole (e.g. window)
884    /// has the keyboard focus. That makes this function suitable for deciding
885    /// widget state that should not be disrupted if the user moves away from
886    /// the window and back.
887    #[inline(always)]
888    pub fn has_focus(&self, id: Id) -> bool {
889        self.focused() == Some(id)
890    }
891
892    /// Which widget has keyboard focus?
893    pub fn focused(&self) -> Option<Id> {
894        self.focus()?.focused()
895    }
896
897    /// Set an event filter for a widget.
898    ///
899    /// This allows you to control whether the widget will loose focus
900    /// when the user presses tab, arrow keys, or escape.
901    ///
902    /// You must first give focus to the widget before calling this.
903    pub fn set_focus_lock_filter(&mut self, id: Id, event_filter: EventFilter) {
904        if self.had_focus_last_frame(id)
905            && self.has_focus(id)
906            && let Some(focused) = &mut self.focus_mut().focused_widget
907            && focused.id == id
908        {
909            focused.filter = event_filter;
910        }
911    }
912
913    /// Give keyboard focus to a specific widget.
914    /// See also [`crate::Response::request_focus`].
915    ///
916    /// Calling this will interrupt IME composition.
917    #[inline(always)]
918    pub fn request_focus(&mut self, id: Id) {
919        self.focus_mut().focused_widget = Some(FocusWidget::new(id));
920        self.interrupt_ime();
921    }
922
923    /// Surrender keyboard focus for a specific widget.
924    /// See also [`crate::Response::surrender_focus`].
925    #[inline(always)]
926    pub fn surrender_focus(&mut self, id: Id) {
927        let focus = self.focus_mut();
928        if focus.focused() == Some(id) {
929            focus.focused_widget = None;
930        }
931    }
932
933    /// Move keyboard focus in a specific direction.
934    pub fn move_focus(&mut self, direction: FocusDirection) {
935        self.focus_mut().focus_direction = direction;
936    }
937
938    /// Returns true if
939    /// - this layer is the top-most modal layer or above it
940    /// - there is no modal layer
941    pub fn is_above_modal_layer(&self, layer_id: LayerId) -> bool {
942        if let Some(modal_layer) = self.focus().and_then(|f| f.top_modal_layer) {
943            matches!(
944                self.areas().compare_order(layer_id, modal_layer),
945                core::cmp::Ordering::Equal | core::cmp::Ordering::Greater
946            )
947        } else {
948            true
949        }
950    }
951
952    /// Does this layer allow interaction?
953    /// Returns true if
954    ///  - the layer is not behind a modal layer
955    ///  - the [`Order`] allows interaction
956    pub fn allows_interaction(&self, layer_id: LayerId) -> bool {
957        let is_above_modal_layer = self.is_above_modal_layer(layer_id);
958        let ordering_allows_interaction = layer_id.order.allow_interaction();
959        is_above_modal_layer && ordering_allows_interaction
960    }
961
962    /// Register this widget as being interested in getting keyboard focus.
963    /// This will allow the user to select it with tab and shift-tab.
964    /// This is normally done automatically when handling interactions,
965    /// but it is sometimes useful to pre-register interest in focus,
966    /// e.g. before deciding which type of underlying widget to use,
967    /// as in the [`crate::DragValue`] widget, so a widget can be focused
968    /// and rendered correctly in a single frame.
969    ///
970    /// Pass in the `layer_id` of the layer that the widget is in.
971    #[inline(always)]
972    pub fn interested_in_focus(&mut self, id: Id, layer_id: LayerId) {
973        if !self.allows_interaction(layer_id) {
974            return;
975        }
976        self.focus_mut().interested_in_focus(id);
977    }
978
979    /// Limit focus to widgets on the given layer and above.
980    /// If this is called multiple times per frame, the top layer wins.
981    pub fn set_modal_layer(&mut self, layer_id: LayerId) {
982        if let Some(current) = self.focus().and_then(|f| f.top_modal_layer_current_frame)
983            && matches!(
984                self.areas().compare_order(layer_id, current),
985                core::cmp::Ordering::Less
986            )
987        {
988            return;
989        }
990
991        self.focus_mut().set_modal_layer(layer_id);
992    }
993
994    /// Get the top modal layer (from the previous frame).
995    pub fn top_modal_layer(&self) -> Option<LayerId> {
996        self.focus()?.top_modal_layer()
997    }
998
999    /// Stop editing the active [`TextEdit`](crate::TextEdit) (if any).
1000    #[inline(always)]
1001    pub fn stop_text_input(&mut self) {
1002        self.focus_mut().focused_widget = None;
1003    }
1004
1005    /// Forget window positions, sizes etc.
1006    /// Can be used to auto-layout windows.
1007    pub fn reset_areas(&mut self) {
1008        #[expect(clippy::iter_over_hash_type)]
1009        for area in self.areas.values_mut() {
1010            *area = Default::default();
1011        }
1012    }
1013
1014    /// Obtain the previous rectangle of an area.
1015    pub fn area_rect(&self, id: impl Into<Id>) -> Option<Rect> {
1016        self.areas().get(id.into()).map(|state| state.rect())
1017    }
1018
1019    pub(crate) fn interaction(&self) -> &InteractionState {
1020        self.interactions
1021            .get(&self.viewport_id)
1022            .expect("Failed to get interaction")
1023    }
1024
1025    pub(crate) fn interaction_mut(&mut self) -> &mut InteractionState {
1026        self.interactions.entry(self.viewport_id).or_default()
1027    }
1028
1029    pub(crate) fn focus(&self) -> Option<&Focus> {
1030        self.focus.get(&self.viewport_id)
1031    }
1032
1033    pub(crate) fn focus_mut(&mut self) -> &mut Focus {
1034        self.focus.entry(self.viewport_id).or_default()
1035    }
1036
1037    /// Check if the widget owns IME events.
1038    ///
1039    /// A widget should only consume IME events if this returns `true`. At most
1040    /// one widget can own IME events for each frame.
1041    #[inline(always)]
1042    pub fn owns_ime_events(&self, id: Id) -> bool {
1043        // Note: Even if the IME is being interrupted in the current frame, we
1044        // should not return `false` here, since we still need
1045        // `PlatformOutput::ime` to be set in such cases.
1046
1047        self.has_focus(id)
1048    }
1049
1050    /// Interrupt the current IME composition, if any.
1051    pub fn interrupt_ime(&mut self) {
1052        self.requested_interrupt_ime = true;
1053    }
1054
1055    pub(crate) fn should_interrupt_ime(&self) -> bool {
1056        self.requested_interrupt_ime
1057    }
1058}
1059
1060/// State of an open popup.
1061#[derive(Clone, Copy, Debug)]
1062struct OpenPopup {
1063    /// Id of the popup.
1064    id: Id,
1065
1066    /// Optional position of the popup.
1067    pos: Option<Pos2>,
1068
1069    /// Whether this popup was still open this frame. Otherwise it's considered abandoned and `Memory::popup` will be cleared.
1070    open_this_frame: bool,
1071}
1072
1073impl OpenPopup {
1074    /// Create a new `OpenPopup`.
1075    fn new(id: Id, pos: Option<Pos2>) -> Self {
1076        Self {
1077            id,
1078            pos,
1079            open_this_frame: true,
1080        }
1081    }
1082}
1083
1084/// ## Popup state (internal API)
1085///
1086/// Used by [`crate::Popup`].
1087impl Memory {
1088    pub(crate) fn is_popup_open(&self, popup_id: Id) -> bool {
1089        self.popups
1090            .get(&self.viewport_id)
1091            .is_some_and(|state| state.id == popup_id)
1092            || self.everything_is_visible()
1093    }
1094
1095    pub(crate) fn any_popup_open(&self) -> bool {
1096        self.popups.contains_key(&self.viewport_id) || self.everything_is_visible()
1097    }
1098
1099    pub(crate) fn open_popup(&mut self, popup_id: Id) {
1100        self.popups
1101            .insert(self.viewport_id, OpenPopup::new(popup_id, None));
1102    }
1103
1104    pub(crate) fn keep_popup_open(&mut self, popup_id: Id) {
1105        if let Some(state) = self.popups.get_mut(&self.viewport_id)
1106            && state.id == popup_id
1107        {
1108            state.open_this_frame = true;
1109        }
1110    }
1111
1112    pub(crate) fn open_popup_at(&mut self, popup_id: Id, pos: impl Into<Option<Pos2>>) {
1113        self.popups
1114            .insert(self.viewport_id, OpenPopup::new(popup_id, pos.into()));
1115    }
1116
1117    pub(crate) fn popup_position(&self, id: Id) -> Option<Pos2> {
1118        let state = self.popups.get(&self.viewport_id)?;
1119        if state.id == id { state.pos } else { None }
1120    }
1121
1122    pub(crate) fn close_all_popups(&mut self) {
1123        self.popups.clear();
1124    }
1125
1126    pub(crate) fn close_popup(&mut self, popup_id: Id) {
1127        if self.is_popup_open(popup_id) {
1128            self.popups.remove(&self.viewport_id);
1129        }
1130    }
1131
1132    pub(crate) fn toggle_popup(&mut self, popup_id: Id) {
1133        if self.is_popup_open(popup_id) {
1134            self.close_popup(popup_id);
1135        } else {
1136            self.open_popup(popup_id);
1137        }
1138    }
1139}
1140
1141impl Memory {
1142    /// If true, all windows, menus, tooltips, etc., will be visible at once.
1143    ///
1144    /// This is useful for testing, benchmarking, pre-caching, etc.
1145    ///
1146    /// Experimental feature!
1147    #[inline(always)]
1148    pub fn everything_is_visible(&self) -> bool {
1149        self.everything_is_visible
1150    }
1151
1152    /// If true, all windows, menus, tooltips etc are to be visible at once.
1153    ///
1154    /// This is useful for testing, benchmarking, pre-caching, etc.
1155    ///
1156    /// Experimental feature!
1157    pub fn set_everything_is_visible(&mut self, value: bool) {
1158        self.everything_is_visible = value;
1159    }
1160}
1161
1162// ----------------------------------------------------------------------------
1163
1164/// Map containing the index of each layer in the order list, for quick lookups.
1165type OrderMap = HashMap<LayerId, usize>;
1166
1167/// Keeps track of [`Area`](crate::containers::area::Area)s, which are free-floating [`Ui`](crate::Ui)s.
1168/// These [`Area`](crate::containers::area::Area)s can be in any [`Order`].
1169#[derive(Clone, Debug, Default)]
1170#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
1171#[cfg_attr(feature = "serde", serde(default))]
1172pub struct Areas {
1173    areas: IdMap<area::AreaState>,
1174
1175    visible_areas_last_frame: ahash::HashSet<LayerId>,
1176    visible_areas_current_frame: ahash::HashSet<LayerId>,
1177
1178    // ----------------------------
1179    // Everything below this is general to all layers, not just areas.
1180    // TODO(emilk): move this to a separate struct.
1181    /// Back-to-front,  top is last.
1182    order: Vec<LayerId>,
1183
1184    /// Inverse of [`Self::order`], calculated at the end of the frame.
1185    order_map: OrderMap,
1186
1187    /// When an area wants to be on top, it is assigned here.
1188    /// This is used to reorder the layers at the end of the frame.
1189    /// If several layers want to be on top, they will keep their relative order.
1190    /// This means closing three windows and then reopening them all in one frame
1191    /// results in them being sent to the top and keeping their previous internal order.
1192    wants_to_be_on_top: ahash::HashSet<LayerId>,
1193
1194    /// The sublayers that each layer has.
1195    ///
1196    /// The parent sublayer is moved directly above the child sublayers in the ordering.
1197    sublayers: ahash::HashMap<LayerId, HashSet<LayerId>>,
1198}
1199
1200impl Areas {
1201    pub(crate) fn count(&self) -> usize {
1202        self.areas.len()
1203    }
1204
1205    pub(crate) fn get(&self, id: Id) -> Option<&area::AreaState> {
1206        self.areas.get(&id)
1207    }
1208
1209    pub(crate) fn get_mut(&mut self, id: Id) -> Option<&mut area::AreaState> {
1210        self.areas.get_mut(&id)
1211    }
1212
1213    /// Can the user interact with this layer or it's widgets, or do clicks go straight through it?
1214    pub(crate) fn is_interactable(&self, layer_id: LayerId) -> bool {
1215        self.get(layer_id.id).is_none_or(|area| area.interactable)
1216    }
1217
1218    /// All layers back-to-front, top is last.
1219    pub(crate) fn order(&self) -> &[LayerId] {
1220        &self.order
1221    }
1222
1223    /// Compare the order of two layers, based on the order list from last frame.
1224    ///
1225    /// May return [`std::cmp::Ordering::Equal`] if the layers are not in the order list.
1226    pub(crate) fn compare_order(&self, a: LayerId, b: LayerId) -> core::cmp::Ordering {
1227        // Sort by layer `order` first and use `order_map` to resolve disputes.
1228        // If `order_map` only contains one layer ID, then the other one will be
1229        // lower because `None < Some(x)`.
1230        match a.order.cmp(&b.order) {
1231            core::cmp::Ordering::Equal => self.order_map.get(&a).cmp(&self.order_map.get(&b)),
1232            cmp => cmp,
1233        }
1234    }
1235
1236    pub(crate) fn set_state(&mut self, layer_id: LayerId, state: area::AreaState) {
1237        self.visible_areas_current_frame.insert(layer_id);
1238        self.areas.insert(layer_id.id, state);
1239        if !self.order.contains(&layer_id) {
1240            self.order.push(layer_id);
1241        }
1242    }
1243
1244    /// Top-most layer at the given position.
1245    pub fn layer_id_at(
1246        &self,
1247        pos: Pos2,
1248        layer_to_global: &HashMap<LayerId, TSTransform>,
1249    ) -> Option<LayerId> {
1250        for layer in self.order.iter().rev() {
1251            if self.is_visible(layer)
1252                && let Some(state) = self.areas.get(&layer.id)
1253            {
1254                let mut rect = state.rect();
1255                if state.interactable {
1256                    if let Some(to_global) = layer_to_global.get(layer) {
1257                        rect = *to_global * rect;
1258                    }
1259
1260                    if rect.contains(pos) {
1261                        return Some(*layer);
1262                    }
1263                }
1264            }
1265        }
1266        None
1267    }
1268
1269    pub fn visible_last_frame(&self, layer_id: &LayerId) -> bool {
1270        self.visible_areas_last_frame.contains(layer_id)
1271    }
1272
1273    pub fn is_visible(&self, layer_id: &LayerId) -> bool {
1274        self.visible_areas_last_frame.contains(layer_id)
1275            || self.visible_areas_current_frame.contains(layer_id)
1276    }
1277
1278    pub fn visible_layer_ids(&self) -> ahash::HashSet<LayerId> {
1279        core::iter::chain(
1280            &self.visible_areas_last_frame,
1281            &self.visible_areas_current_frame,
1282        )
1283        .copied()
1284        .collect()
1285    }
1286
1287    pub(crate) fn visible_windows(&self) -> impl Iterator<Item = (LayerId, &area::AreaState)> {
1288        self.visible_layer_ids()
1289            .into_iter()
1290            .filter(|layer| layer.order == crate::Order::Middle)
1291            .filter(|&layer| !self.is_sublayer(&layer))
1292            .filter_map(|layer| Some((layer, self.get(layer.id)?)))
1293    }
1294
1295    pub fn move_to_top(&mut self, layer_id: LayerId) {
1296        self.visible_areas_current_frame.insert(layer_id);
1297        self.wants_to_be_on_top.insert(layer_id);
1298
1299        if !self.order.contains(&layer_id) {
1300            self.order.push(layer_id);
1301        }
1302    }
1303
1304    /// Mark the `child` layer as a sublayer of `parent`.
1305    ///
1306    /// Sublayers are moved directly above the parent layer at the end of the frame. This is mainly
1307    /// intended for adding a new [Area](crate::Area) inside a [Window](crate::Window).
1308    ///
1309    /// This currently only supports one level of nesting. If `parent` is a sublayer of another
1310    /// layer, the behavior is unspecified.
1311    ///
1312    /// The two layers must have the same [`LayerId::order`].
1313    pub fn set_sublayer(&mut self, parent: LayerId, child: LayerId) {
1314        debug_assert_eq!(
1315            parent.order, child.order,
1316            "DEBUG ASSERT: Trying to set sublayers across layers of different order ({:?}, {:?}), which is currently undefined behavior in egui",
1317            parent.order, child.order
1318        );
1319
1320        self.sublayers.entry(parent).or_default().insert(child);
1321
1322        // Make sure the layers are in the order list:
1323        if !self.order.contains(&parent) {
1324            self.order.push(parent);
1325        }
1326        if !self.order.contains(&child) {
1327            self.order.push(child);
1328        }
1329    }
1330
1331    pub fn top_layer_id(&self, order: Order) -> Option<LayerId> {
1332        self.order
1333            .iter()
1334            .rfind(|layer| layer.order == order && !self.is_sublayer(layer))
1335            .copied()
1336    }
1337
1338    /// If this layer is the sublayer of another layer, return the parent.
1339    pub fn parent_layer(&self, layer_id: LayerId) -> Option<LayerId> {
1340        self.sublayers.iter().find_map(|(parent, children)| {
1341            if children.contains(&layer_id) {
1342                Some(*parent)
1343            } else {
1344                None
1345            }
1346        })
1347    }
1348
1349    /// All the child layers of this layer.
1350    pub fn child_layers(&self, layer_id: LayerId) -> impl Iterator<Item = LayerId> + '_ {
1351        self.sublayers.get(&layer_id).into_iter().flatten().copied()
1352    }
1353
1354    pub(crate) fn is_sublayer(&self, layer: &LayerId) -> bool {
1355        self.parent_layer(*layer).is_some()
1356    }
1357
1358    pub(crate) fn end_pass(&mut self) {
1359        let Self {
1360            visible_areas_last_frame,
1361            visible_areas_current_frame,
1362            order,
1363            wants_to_be_on_top,
1364            sublayers,
1365            ..
1366        } = self;
1367
1368        core::mem::swap(visible_areas_last_frame, visible_areas_current_frame);
1369        visible_areas_current_frame.clear();
1370
1371        order.sort_by_key(|layer| (layer.order, wants_to_be_on_top.contains(layer)));
1372        wants_to_be_on_top.clear();
1373
1374        // For all layers with sublayers, put the sublayers directly after the parent layer:
1375        // (it doesn't matter in which order we replace parents with their children)
1376        #[expect(clippy::iter_over_hash_type)]
1377        for (parent, children) in core::mem::take(sublayers) {
1378            let mut moved_layers = vec![parent]; // parent first…
1379
1380            order.retain(|l| {
1381                if children.contains(l) {
1382                    moved_layers.push(*l); // …followed by children
1383                    false
1384                } else {
1385                    true
1386                }
1387            });
1388            let Some(parent_pos) = order.iter().position(|l| l == &parent) else {
1389                continue;
1390            };
1391            order.splice(parent_pos..=parent_pos, moved_layers); // replace the parent with itself and its children
1392        }
1393
1394        self.order_map = self
1395            .order
1396            .iter()
1397            .enumerate()
1398            .map(|(i, id)| (*id, i))
1399            .collect();
1400    }
1401}
1402
1403// ----------------------------------------------------------------------------
1404
1405#[test]
1406fn memory_impl_send_sync() {
1407    fn assert_send_sync<T: Send + Sync>() {}
1408    assert_send_sync::<Memory>();
1409}
1410
1411// Regression test for https://github.com/emilk/egui/issues/2142.
1412#[test]
1413fn lost_focus_fires_after_mid_frame_focus_transfer() {
1414    use crate::data::input::RawInput;
1415    let a = Id::new("A");
1416    let b = Id::new("B");
1417    let mut focus = Focus::default();
1418    let raw = RawInput::default();
1419
1420    fn lost_focus_check(focus: &Focus, id: Id) -> bool {
1421        let was_focused =
1422            focus.id_previous_frame == Some(id) || focus.id_two_frames_ago == Some(id);
1423        was_focused && focus.focused() != Some(id)
1424    }
1425
1426    // Frame N-1
1427    {
1428        focus.begin_pass(&raw);
1429        focus.focused_widget = Some(FocusWidget::new(a));
1430    }
1431
1432    // Frame N: `A` is focused at start; user clicks `B` mid-frame
1433    {
1434        focus.begin_pass(&raw);
1435        assert_eq!(focus.id_previous_frame, Some(a));
1436        assert!(!lost_focus_check(&focus, a));
1437        focus.focused_widget = Some(FocusWidget::new(b));
1438    }
1439
1440    // Frame N+1: `A` deferred lost_focus signal must fire
1441    {
1442        focus.begin_pass(&raw);
1443        assert_eq!(focus.id_two_frames_ago, Some(a));
1444        assert_eq!(focus.id_previous_frame, Some(b));
1445        assert!(lost_focus_check(&focus, a), "`A` lost_focus must fire");
1446        assert!(!lost_focus_check(&focus, b));
1447    }
1448
1449    // Frame N+2
1450    {
1451        focus.begin_pass(&raw);
1452        assert!(
1453            !lost_focus_check(&focus, a),
1454            "A's lost_focus must stop firing once the two-frame window passes",
1455        );
1456    }
1457}
1458
1459#[test]
1460fn order_map_total_ordering() {
1461    let mut layers = [
1462        LayerId::new(Order::Tooltip, Id::new("a")),
1463        LayerId::new(Order::Background, Id::new("b")),
1464        LayerId::new(Order::Background, Id::new("c")),
1465        LayerId::new(Order::Tooltip, Id::new("d")),
1466        LayerId::new(Order::Background, Id::new("e")),
1467        LayerId::new(Order::Background, Id::new("f")),
1468        LayerId::new(Order::Tooltip, Id::new("g")),
1469    ];
1470    let mut areas = Areas::default();
1471
1472    // skip some of the layers
1473    for &layer in &layers[3..] {
1474        areas.set_state(layer, crate::AreaState::default());
1475    }
1476    areas.end_pass(); // sort layers
1477
1478    // Sort layers
1479    layers.sort_by(|&a, &b| areas.compare_order(a, b));
1480
1481    // Assert that `areas.compare_order()` forms a total ordering
1482    let mut equivalence_classes = vec![0];
1483    let mut i = 0;
1484    for &[a, b] in layers.array_windows() {
1485        assert!(a.order <= b.order, "does not follow LayerId.order");
1486        if areas.compare_order(a, b) != core::cmp::Ordering::Equal {
1487            i += 1;
1488        }
1489        equivalence_classes.push(i);
1490    }
1491    assert_eq!(layers.len(), equivalence_classes.len());
1492    for (&l1, c1) in core::iter::zip(&layers, &equivalence_classes) {
1493        for (&l2, c2) in core::iter::zip(&layers, &equivalence_classes) {
1494            assert_eq!(
1495                c1.cmp(c2),
1496                areas.compare_order(l1, l2),
1497                "not a total ordering",
1498            );
1499        }
1500    }
1501}