egui/memory/
mod.rs

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