egui/
memory.rs

1#![warn(missing_docs)] // Let's keep this file well-documented.` to memory.rs
2
3use ahash::{HashMap, HashSet};
4use epaint::emath::TSTransform;
5
6use crate::{
7    area, vec2, EventFilter, Id, IdMap, LayerId, Order, Pos2, Rangef, RawInput, Rect, Style, Vec2,
8    ViewportId, ViewportIdMap, ViewportIdSet,
9};
10
11// ----------------------------------------------------------------------------
12
13/// The data that egui persists between frames.
14///
15/// This includes window positions and sizes,
16/// how far the user has scrolled in a [`ScrollArea`](crate::ScrollArea) etc.
17///
18/// If you want this to persist when closing your app you should serialize [`Memory`] and store it.
19/// For this you need to enable the `persistence`.
20///
21/// If you want to store data for your widgets, you should look at [`Memory::data`]
22#[derive(Clone, Debug)]
23#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
24#[cfg_attr(feature = "persistence", serde(default))]
25pub struct Memory {
26    /// Global egui options.
27    pub options: Options,
28
29    /// This map stores some superficial state for all widgets with custom [`Id`]s.
30    ///
31    /// This includes storing if a [`crate::CollapsingHeader`] is open, how far scrolled a
32    /// [`crate::ScrollArea`] is, where the cursor in a [`crate::TextEdit`] is, etc.
33    ///
34    /// This is NOT meant to store any important data. Store that in your own structures!
35    ///
36    /// Each read clones the data, so keep your values cheap to clone.
37    /// If you want to store a lot of data you should wrap it in `Arc<Mutex<…>>` so it is cheap to clone.
38    ///
39    /// This will be saved between different program runs if you use the `persistence` feature.
40    ///
41    /// To store a state common for all your widgets (a singleton), use [`Id::NULL`] as the key.
42    pub data: crate::util::IdTypeMap,
43
44    // ------------------------------------------
45    /// Can be used to cache computations from one frame to another.
46    ///
47    /// This is for saving CPU when you have something that may take 1-100ms to compute.
48    /// Things that are very slow (>100ms) should instead be done async (i.e. in another thread)
49    /// so as not to lock the UI thread.
50    ///
51    /// ```
52    /// use egui::util::cache::{ComputerMut, FrameCache};
53    ///
54    /// #[derive(Default)]
55    /// struct CharCounter {}
56    /// impl ComputerMut<&str, usize> for CharCounter {
57    ///     fn compute(&mut self, s: &str) -> usize {
58    ///         s.chars().count() // you probably want to cache something more expensive than this
59    ///     }
60    /// }
61    /// type CharCountCache<'a> = FrameCache<usize, CharCounter>;
62    ///
63    /// # let mut ctx = egui::Context::default();
64    /// ctx.memory_mut(|mem| {
65    ///     let cache = mem.caches.cache::<CharCountCache<'_>>();
66    ///     assert_eq!(cache.get("hello"), 5);
67    /// });
68    /// ```
69    #[cfg_attr(feature = "persistence", serde(skip))]
70    pub caches: crate::util::cache::CacheStorage,
71
72    // ------------------------------------------
73    /// new fonts that will be applied at the start of the next frame
74    #[cfg_attr(feature = "persistence", serde(skip))]
75    pub(crate) new_font_definitions: Option<epaint::text::FontDefinitions>,
76
77    // Current active viewport
78    #[cfg_attr(feature = "persistence", serde(skip))]
79    pub(crate) viewport_id: ViewportId,
80
81    /// Which popup-window is open (if any)?
82    /// Could be a combo box, color picker, menu etc.
83    #[cfg_attr(feature = "persistence", serde(skip))]
84    popup: Option<Id>,
85
86    #[cfg_attr(feature = "persistence", serde(skip))]
87    everything_is_visible: bool,
88
89    /// Transforms per layer
90    pub layer_transforms: HashMap<LayerId, TSTransform>,
91
92    // -------------------------------------------------
93    // Per-viewport:
94    areas: ViewportIdMap<Areas>,
95
96    #[cfg_attr(feature = "persistence", serde(skip))]
97    pub(crate) interactions: ViewportIdMap<InteractionState>,
98
99    #[cfg_attr(feature = "persistence", serde(skip))]
100    pub(crate) focus: ViewportIdMap<Focus>,
101}
102
103impl Default for Memory {
104    fn default() -> Self {
105        let mut slf = Self {
106            options: Default::default(),
107            data: Default::default(),
108            caches: Default::default(),
109            new_font_definitions: Default::default(),
110            interactions: Default::default(),
111            focus: Default::default(),
112            viewport_id: Default::default(),
113            areas: Default::default(),
114            layer_transforms: Default::default(),
115            popup: Default::default(),
116            everything_is_visible: Default::default(),
117        };
118        slf.interactions.entry(slf.viewport_id).or_default();
119        slf.areas.entry(slf.viewport_id).or_default();
120        slf
121    }
122}
123
124#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
125enum FocusDirection {
126    /// Select the widget closest above the current focused widget.
127    Up,
128
129    /// Select the widget to the right of the current focused widget.
130    Right,
131
132    /// Select the widget below the current focused widget.
133    Down,
134
135    /// Select the widget to the left of the current focused widget.
136    Left,
137
138    /// Select the previous widget that had focus.
139    Previous,
140
141    /// Select the next widget that wants focus.
142    Next,
143
144    /// Don't change focus.
145    #[default]
146    None,
147}
148
149impl FocusDirection {
150    fn is_cardinal(&self) -> bool {
151        match self {
152            Self::Up | Self::Right | Self::Down | Self::Left => true,
153
154            Self::Previous | Self::Next | Self::None => false,
155        }
156    }
157}
158
159// ----------------------------------------------------------------------------
160
161/// Some global options that you can read and write.
162///
163/// See also [`crate::style::DebugOptions`].
164#[derive(Clone, Debug, PartialEq)]
165#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
166#[cfg_attr(feature = "serde", serde(default))]
167pub struct Options {
168    /// The default style for new [`Ui`](crate::Ui):s.
169    #[cfg_attr(feature = "serde", serde(skip))]
170    pub(crate) style: std::sync::Arc<Style>,
171
172    /// Global zoom factor of the UI.
173    ///
174    /// This is used to calculate the `pixels_per_point`
175    /// for the UI as `pixels_per_point = zoom_fator * native_pixels_per_point`.
176    ///
177    /// The default is 1.0.
178    /// Make larger to make everything larger.
179    ///
180    /// Please call [`crate::Context::set_zoom_factor`]
181    /// instead of modifying this directly!
182    pub zoom_factor: f32,
183
184    /// If `true`, egui will change the scale of the ui ([`crate::Context::zoom_factor`]) when the user
185    /// presses Cmd+Plus, Cmd+Minus or Cmd+0, just like in a browser.
186    ///
187    /// This is `true` by default.
188    ///
189    /// On the web-backend of `eframe` this is set to false by default,
190    /// so that the zoom shortcuts are handled exclusively by the browser,
191    /// which will change the `native_pixels_per_point` (`devicePixelRatio`).
192    /// You can still zoom egui independently by calling [`crate::Context::set_zoom_factor`],
193    /// which will be applied on top of the browsers global zoom.
194    #[cfg_attr(feature = "serde", serde(skip))]
195    pub zoom_with_keyboard: bool,
196
197    /// Controls the tessellator.
198    pub tessellation_options: epaint::TessellationOptions,
199
200    /// If any widget moves or changes id, repaint everything.
201    ///
202    /// It is recommended you keep this OFF, because
203    /// it is know to cause endless repaints, for unknown reasons
204    /// (<https://github.com/rerun-io/rerun/issues/5018>).
205    pub repaint_on_widget_change: bool,
206
207    /// This is a signal to any backend that we want the [`crate::PlatformOutput::events`] read out loud.
208    ///
209    /// The only change to egui is that labels can be focused by pressing tab.
210    ///
211    /// Screen readers is an experimental feature of egui, and not supported on all platforms.
212    ///
213    /// `eframe` supports it only on web,
214    /// but you should consider using [AccessKit](https://github.com/AccessKit/accesskit) instead,
215    /// which `eframe` supports.
216    pub screen_reader: bool,
217
218    /// If true, the most common glyphs (ASCII) are pre-rendered to the texture atlas.
219    ///
220    /// Only the fonts in [`Style::text_styles`] will be pre-cached.
221    ///
222    /// This can lead to fewer texture operations, but may use up the texture atlas quicker
223    /// if you are changing [`Style::text_styles`], of have a lot of text styles.
224    pub preload_font_glyphs: bool,
225
226    /// Check reusing of [`Id`]s, and show a visual warning on screen when one is found.
227    ///
228    /// By default this is `true` in debug builds.
229    pub warn_on_id_clash: bool,
230
231    // ------------------------------
232    // Input:
233    /// Multiplier for the scroll speed when reported in [`crate::MouseWheelUnit::Line`]s.
234    pub line_scroll_speed: f32,
235
236    /// Controls the speed at which we zoom in when doing ctrl/cmd + scroll.
237    pub scroll_zoom_speed: f32,
238
239    /// If `true`, `egui` will discard the loaded image data after
240    /// the texture is loaded onto the GPU to reduce memory usage.
241    ///
242    /// In modern GPU rendering, the texture data is not required after the texture is loaded.
243    ///
244    /// This is beneficial when using a large number or resolution of images and there is no need to
245    /// retain the image data, potentially saving a significant amount of memory.
246    ///
247    /// The drawback is that it becomes impossible to serialize the loaded images or render in non-GPU systems.
248    ///
249    /// Default is `false`.
250    pub reduce_texture_memory: bool,
251}
252
253impl Default for Options {
254    fn default() -> Self {
255        // TODO(emilk): figure out why these constants need to be different on web and on native (winit).
256        let is_web = cfg!(target_arch = "wasm32");
257        let line_scroll_speed = if is_web {
258            8.0
259        } else {
260            40.0 // Scroll speed decided by consensus: https://github.com/emilk/egui/issues/461
261        };
262
263        Self {
264            style: Default::default(),
265            zoom_factor: 1.0,
266            zoom_with_keyboard: true,
267            tessellation_options: Default::default(),
268            repaint_on_widget_change: false,
269            screen_reader: false,
270            preload_font_glyphs: true,
271            warn_on_id_clash: cfg!(debug_assertions),
272
273            // Input:
274            line_scroll_speed,
275            scroll_zoom_speed: 1.0 / 200.0,
276            reduce_texture_memory: false,
277        }
278    }
279}
280
281impl Options {
282    /// Show the options in the ui.
283    pub fn ui(&mut self, ui: &mut crate::Ui) {
284        let Self {
285            style,          // covered above
286            zoom_factor: _, // TODO(emilk)
287            zoom_with_keyboard,
288            tessellation_options,
289            repaint_on_widget_change,
290            screen_reader: _, // needs to come from the integration
291            preload_font_glyphs: _,
292            warn_on_id_clash,
293
294            line_scroll_speed,
295            scroll_zoom_speed,
296            reduce_texture_memory,
297        } = self;
298
299        use crate::Widget as _;
300
301        CollapsingHeader::new("⚙ Options")
302            .default_open(false)
303            .show(ui, |ui| {
304                ui.checkbox(
305                    repaint_on_widget_change,
306                    "Repaint if any widget moves or changes id",
307                );
308
309                ui.checkbox(
310                    zoom_with_keyboard,
311                    "Zoom with keyboard (Cmd +, Cmd -, Cmd 0)",
312                );
313
314                ui.checkbox(warn_on_id_clash, "Warn if two widgets have the same Id");
315
316                ui.checkbox(reduce_texture_memory, "Reduce texture memory");
317            });
318
319        use crate::containers::*;
320        CollapsingHeader::new("🎑 Style")
321            .default_open(true)
322            .show(ui, |ui| {
323                std::sync::Arc::make_mut(style).ui(ui);
324            });
325
326        CollapsingHeader::new("✒ Painting")
327            .default_open(false)
328            .show(ui, |ui| {
329                tessellation_options.ui(ui);
330                ui.vertical_centered(|ui| {
331                    crate::reset_button(ui, tessellation_options, "Reset paint settings");
332                });
333            });
334
335        CollapsingHeader::new("🖱 Input")
336            .default_open(false)
337            .show(ui, |ui| {
338                ui.horizontal(|ui| {
339                    ui.label("Line scroll speed");
340                    ui.add(crate::DragValue::new(line_scroll_speed).range(0.0..=f32::INFINITY))
341                        .on_hover_text(
342                            "How many lines to scroll with each tick of the mouse wheel",
343                        );
344                });
345                ui.horizontal(|ui| {
346                    ui.label("Scroll zoom speed");
347                    ui.add(
348                        crate::DragValue::new(scroll_zoom_speed)
349                            .range(0.0..=f32::INFINITY)
350                            .speed(0.001),
351                    )
352                    .on_hover_text("How fast to zoom with ctrl/cmd + scroll");
353                });
354            });
355
356        ui.vertical_centered(|ui| crate::reset_button(ui, self, "Reset all"));
357    }
358}
359
360// ----------------------------------------------------------------------------
361
362/// The state of the interaction in egui,
363/// i.e. what is being dragged.
364///
365/// Say there is a button in a scroll area.
366/// If the user clicks the button, the button should click.
367/// If the user drags the button we should scroll the scroll area.
368/// So what we do is that when the mouse is pressed we register both the button
369/// and the scroll area (as `click_id`/`drag_id`).
370/// If the user releases the button without moving the mouse we register it as a click on `click_id`.
371/// If the cursor moves too much we clear the `click_id` and start passing move events to `drag_id`.
372#[derive(Clone, Debug, Default)]
373pub(crate) struct InteractionState {
374    /// A widget interested in clicks that has a mouse press on it.
375    pub potential_click_id: Option<Id>,
376
377    /// A widget interested in drags that has a mouse press on it.
378    ///
379    /// Note that this is set as soon as the mouse is pressed,
380    /// so the widget may not yet be marked as "dragged",
381    /// as that can only happen after the mouse has moved a bit
382    /// (at least if the widget is interesated in both clicks and drags).
383    pub potential_drag_id: Option<Id>,
384}
385
386/// Keeps tracks of what widget has keyboard focus
387#[derive(Clone, Debug, Default)]
388pub(crate) struct Focus {
389    /// The widget with keyboard focus (i.e. a text input field).
390    focused_widget: Option<FocusWidget>,
391
392    /// What had keyboard focus previous frame?
393    id_previous_frame: Option<Id>,
394
395    /// Give focus to this widget next frame
396    id_next_frame: Option<Id>,
397
398    #[cfg(feature = "accesskit")]
399    id_requested_by_accesskit: Option<accesskit::NodeId>,
400
401    /// If set, the next widget that is interested in focus will automatically get it.
402    /// Probably because the user pressed Tab.
403    give_to_next: bool,
404
405    /// The last widget interested in focus.
406    last_interested: Option<Id>,
407
408    /// Set when looking for widget with navigational keys like arrows, tab, shift+tab
409    focus_direction: FocusDirection,
410
411    /// A cache of widget ids that are interested in focus with their corresponding rectangles.
412    focus_widgets_cache: IdMap<Rect>,
413}
414
415/// The widget with focus.
416#[derive(Clone, Copy, Debug)]
417struct FocusWidget {
418    pub id: Id,
419    pub filter: EventFilter,
420}
421
422impl FocusWidget {
423    pub fn new(id: Id) -> Self {
424        Self {
425            id,
426            filter: Default::default(),
427        }
428    }
429}
430
431impl InteractionState {
432    /// Are we currently clicking or dragging an egui widget?
433    pub fn is_using_pointer(&self) -> bool {
434        self.potential_click_id.is_some() || self.potential_drag_id.is_some()
435    }
436}
437
438impl Focus {
439    /// Which widget currently has keyboard focus?
440    pub fn focused(&self) -> Option<Id> {
441        self.focused_widget.as_ref().map(|w| w.id)
442    }
443
444    fn begin_frame(&mut self, new_input: &crate::data::input::RawInput) {
445        self.id_previous_frame = self.focused();
446        if let Some(id) = self.id_next_frame.take() {
447            self.focused_widget = Some(FocusWidget::new(id));
448        }
449        let event_filter = self.focused_widget.map(|w| w.filter).unwrap_or_default();
450
451        #[cfg(feature = "accesskit")]
452        {
453            self.id_requested_by_accesskit = None;
454        }
455
456        self.focus_direction = FocusDirection::None;
457
458        for event in &new_input.events {
459            if !event_filter.matches(event) {
460                if let crate::Event::Key {
461                    key,
462                    pressed: true,
463                    modifiers,
464                    ..
465                } = event
466                {
467                    if let Some(cardinality) = match key {
468                        crate::Key::ArrowUp => Some(FocusDirection::Up),
469                        crate::Key::ArrowRight => Some(FocusDirection::Right),
470                        crate::Key::ArrowDown => Some(FocusDirection::Down),
471                        crate::Key::ArrowLeft => Some(FocusDirection::Left),
472
473                        crate::Key::Tab => {
474                            if modifiers.shift {
475                                Some(FocusDirection::Previous)
476                            } else {
477                                Some(FocusDirection::Next)
478                            }
479                        }
480                        crate::Key::Escape => {
481                            self.focused_widget = None;
482                            Some(FocusDirection::None)
483                        }
484                        _ => None,
485                    } {
486                        self.focus_direction = cardinality;
487                    }
488                }
489            }
490
491            #[cfg(feature = "accesskit")]
492            {
493                if let crate::Event::AccessKitActionRequest(accesskit::ActionRequest {
494                    action: accesskit::Action::Focus,
495                    target,
496                    data: None,
497                }) = event
498                {
499                    self.id_requested_by_accesskit = Some(*target);
500                }
501            }
502        }
503    }
504
505    pub(crate) fn end_frame(&mut self, used_ids: &IdMap<Rect>) {
506        if self.focus_direction.is_cardinal() {
507            if let Some(found_widget) = self.find_widget_in_direction(used_ids) {
508                self.focused_widget = Some(FocusWidget::new(found_widget));
509            }
510        }
511
512        if let Some(focused_widget) = self.focused_widget {
513            // Allow calling `request_focus` one frame and not using it until next frame
514            let recently_gained_focus = self.id_previous_frame != Some(focused_widget.id);
515
516            if !recently_gained_focus && !used_ids.contains_key(&focused_widget.id) {
517                // Dead-mans-switch: the widget with focus has disappeared!
518                self.focused_widget = None;
519            }
520        }
521    }
522
523    pub(crate) fn had_focus_last_frame(&self, id: Id) -> bool {
524        self.id_previous_frame == Some(id)
525    }
526
527    fn interested_in_focus(&mut self, id: Id) {
528        #[cfg(feature = "accesskit")]
529        {
530            if self.id_requested_by_accesskit == Some(id.accesskit_id()) {
531                self.focused_widget = Some(FocusWidget::new(id));
532                self.id_requested_by_accesskit = None;
533                self.give_to_next = false;
534                self.reset_focus();
535            }
536        }
537
538        // The rect is updated at the end of the frame.
539        self.focus_widgets_cache
540            .entry(id)
541            .or_insert(Rect::EVERYTHING);
542
543        if self.give_to_next && !self.had_focus_last_frame(id) {
544            self.focused_widget = Some(FocusWidget::new(id));
545            self.give_to_next = false;
546        } else if self.focused() == Some(id) {
547            if self.focus_direction == FocusDirection::Next {
548                self.focused_widget = None;
549                self.give_to_next = true;
550                self.reset_focus();
551            } else if self.focus_direction == FocusDirection::Previous {
552                self.id_next_frame = self.last_interested; // frame-delay so gained_focus works
553                self.reset_focus();
554            }
555        } else if self.focus_direction == FocusDirection::Next
556            && self.focused_widget.is_none()
557            && !self.give_to_next
558        {
559            // nothing has focus and the user pressed tab - give focus to the first widgets that wants it:
560            self.focused_widget = Some(FocusWidget::new(id));
561            self.reset_focus();
562        } else if self.focus_direction == FocusDirection::Previous
563            && self.focused_widget.is_none()
564            && !self.give_to_next
565        {
566            // nothing has focus and the user pressed Shift+Tab - give focus to the last widgets that wants it:
567            self.focused_widget = self.last_interested.map(FocusWidget::new);
568            self.reset_focus();
569        }
570
571        self.last_interested = Some(id);
572    }
573
574    fn reset_focus(&mut self) {
575        self.focus_direction = FocusDirection::None;
576    }
577
578    fn find_widget_in_direction(&mut self, new_rects: &IdMap<Rect>) -> Option<Id> {
579        // NOTE: `new_rects` here include some widgets _not_ interested in focus.
580
581        /// * negative if `a` is left of `b`
582        /// * positive if `a` is right of `b`
583        /// * zero if the ranges overlap significantly
584        fn range_diff(a: Rangef, b: Rangef) -> f32 {
585            let has_significant_overlap = a.intersection(b).span() >= 0.5 * b.span().min(a.span());
586            if has_significant_overlap {
587                0.0
588            } else {
589                a.center() - b.center()
590            }
591        }
592
593        let current_focused = self.focused_widget?;
594
595        // In what direction we are looking for the next widget.
596        let search_direction = match self.focus_direction {
597            FocusDirection::Up => Vec2::UP,
598            FocusDirection::Right => Vec2::RIGHT,
599            FocusDirection::Down => Vec2::DOWN,
600            FocusDirection::Left => Vec2::LEFT,
601            _ => {
602                return None;
603            }
604        };
605
606        // Update cache with new rects
607        self.focus_widgets_cache.retain(|id, old_rect| {
608            if let Some(new_rect) = new_rects.get(id) {
609                *old_rect = *new_rect;
610                true // Keep the item
611            } else {
612                false // Remove the item
613            }
614        });
615
616        let current_rect = self.focus_widgets_cache.get(&current_focused.id)?;
617
618        let mut best_score = std::f32::INFINITY;
619        let mut best_id = None;
620
621        for (candidate_id, candidate_rect) in &self.focus_widgets_cache {
622            if *candidate_id == current_focused.id {
623                continue;
624            }
625
626            // There is a lot of room for improvement here.
627            let to_candidate = vec2(
628                range_diff(candidate_rect.x_range(), current_rect.x_range()),
629                range_diff(candidate_rect.y_range(), current_rect.y_range()),
630            );
631
632            let acos_angle = to_candidate.normalized().dot(search_direction);
633
634            // Only interested in widgets that fall in a 90° cone (±45°)
635            // of the search direction.
636            let is_in_search_cone = 0.5_f32.sqrt() <= acos_angle;
637            if is_in_search_cone {
638                let distance = to_candidate.length();
639
640                // There is a lot of room for improvement here.
641                let score = distance / (acos_angle * acos_angle);
642
643                if score < best_score {
644                    best_score = score;
645                    best_id = Some(*candidate_id);
646                }
647            }
648        }
649
650        best_id
651    }
652}
653
654impl Memory {
655    pub(crate) fn begin_frame(&mut self, new_raw_input: &RawInput, viewports: &ViewportIdSet) {
656        crate::profile_function!();
657
658        self.viewport_id = new_raw_input.viewport_id;
659
660        // Cleanup
661        self.interactions.retain(|id, _| viewports.contains(id));
662        self.areas.retain(|id, _| viewports.contains(id));
663
664        self.areas.entry(self.viewport_id).or_default();
665
666        // self.interactions  is handled elsewhere
667
668        self.focus
669            .entry(self.viewport_id)
670            .or_default()
671            .begin_frame(new_raw_input);
672    }
673
674    pub(crate) fn end_frame(&mut self, used_ids: &IdMap<Rect>) {
675        self.caches.update();
676        self.areas_mut().end_frame();
677        self.focus_mut().end_frame(used_ids);
678    }
679
680    pub(crate) fn set_viewport_id(&mut self, viewport_id: ViewportId) {
681        self.viewport_id = viewport_id;
682    }
683
684    /// Access memory of the [`Area`](crate::containers::area::Area)s, such as `Window`s.
685    pub fn areas(&self) -> &Areas {
686        self.areas
687            .get(&self.viewport_id)
688            .expect("Memory broken: no area for the current viewport")
689    }
690
691    /// Access memory of the [`Area`](crate::containers::area::Area)s, such as `Window`s.
692    pub fn areas_mut(&mut self) -> &mut Areas {
693        self.areas.entry(self.viewport_id).or_default()
694    }
695
696    /// Top-most layer at the given position.
697    pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
698        self.areas().layer_id_at(pos, &self.layer_transforms)
699    }
700
701    /// An iterator over all layers. Back-to-front. Top is last.
702    pub fn layer_ids(&self) -> impl ExactSizeIterator<Item = LayerId> + '_ {
703        self.areas().order().iter().copied()
704    }
705
706    pub(crate) fn had_focus_last_frame(&self, id: Id) -> bool {
707        self.focus().and_then(|f| f.id_previous_frame) == Some(id)
708    }
709
710    /// True if the given widget had keyboard focus last frame, but not this one.
711    pub(crate) fn lost_focus(&self, id: Id) -> bool {
712        self.had_focus_last_frame(id) && !self.has_focus(id)
713    }
714
715    /// True if the given widget has keyboard focus this frame, but didn't last frame.
716    pub(crate) fn gained_focus(&self, id: Id) -> bool {
717        !self.had_focus_last_frame(id) && self.has_focus(id)
718    }
719
720    /// Does this widget have keyboard focus?
721    ///
722    /// This function does not consider whether the UI as a whole (e.g. window)
723    /// has the keyboard focus. That makes this function suitable for deciding
724    /// widget state that should not be disrupted if the user moves away
725    /// from the window and back.
726    #[inline(always)]
727    pub fn has_focus(&self, id: Id) -> bool {
728        self.focused() == Some(id)
729    }
730
731    /// Which widget has keyboard focus?
732    pub fn focused(&self) -> Option<Id> {
733        self.focus().and_then(|f| f.focused())
734    }
735
736    /// Set an event filter for a widget.
737    ///
738    /// This allows you to control whether the widget will loose focus
739    /// when the user presses tab, arrow keys, or escape.
740    ///
741    /// You must first give focus to the widget before calling this.
742    pub fn set_focus_lock_filter(&mut self, id: Id, event_filter: EventFilter) {
743        if self.had_focus_last_frame(id) && self.has_focus(id) {
744            if let Some(focused) = &mut self.focus_mut().focused_widget {
745                if focused.id == id {
746                    focused.filter = event_filter;
747                }
748            }
749        }
750    }
751
752    /// Give keyboard focus to a specific widget.
753    /// See also [`crate::Response::request_focus`].
754    #[inline(always)]
755    pub fn request_focus(&mut self, id: Id) {
756        self.focus_mut().focused_widget = Some(FocusWidget::new(id));
757    }
758
759    /// Surrender keyboard focus for a specific widget.
760    /// See also [`crate::Response::surrender_focus`].
761    #[inline(always)]
762    pub fn surrender_focus(&mut self, id: Id) {
763        let focus = self.focus_mut();
764        if focus.focused() == Some(id) {
765            focus.focused_widget = None;
766        }
767    }
768
769    /// Register this widget as being interested in getting keyboard focus.
770    /// This will allow the user to select it with tab and shift-tab.
771    /// This is normally done automatically when handling interactions,
772    /// but it is sometimes useful to pre-register interest in focus,
773    /// e.g. before deciding which type of underlying widget to use,
774    /// as in the [`crate::DragValue`] widget, so a widget can be focused
775    /// and rendered correctly in a single frame.
776    #[inline(always)]
777    pub fn interested_in_focus(&mut self, id: Id) {
778        self.focus_mut().interested_in_focus(id);
779    }
780
781    /// Stop editing of active [`TextEdit`](crate::TextEdit) (if any).
782    #[inline(always)]
783    pub fn stop_text_input(&mut self) {
784        self.focus_mut().focused_widget = None;
785    }
786
787    /// Is any widget being dragged?
788    #[deprecated = "Use `Context::dragged_id` instead"]
789    #[inline(always)]
790    pub fn is_anything_being_dragged(&self) -> bool {
791        self.interaction().potential_drag_id.is_some()
792    }
793
794    /// Is this specific widget being dragged?
795    ///
796    /// Usually it is better to use [`crate::Response::dragged`].
797    ///
798    /// A widget that sense both clicks and drags is only marked as "dragged"
799    /// when the mouse has moved a bit, but `is_being_dragged` will return true immediately.
800    #[deprecated = "Use `Context::is_being_dragged` instead"]
801    #[inline(always)]
802    pub fn is_being_dragged(&self, id: Id) -> bool {
803        self.interaction().potential_drag_id == Some(id)
804    }
805
806    /// Get the id of the widget being dragged, if any.
807    ///
808    /// Note that this is set as soon as the mouse is pressed,
809    /// so the widget may not yet be marked as "dragged",
810    /// as that can only happen after the mouse has moved a bit
811    /// (at least if the widget is interesated in both clicks and drags).
812    #[deprecated = "Use `Context::dragged_id` instead"]
813    #[inline(always)]
814    pub fn dragged_id(&self) -> Option<Id> {
815        self.interaction().potential_drag_id
816    }
817
818    /// Set which widget is being dragged.
819    #[inline(always)]
820    #[deprecated = "Use `Context::set_dragged_id` instead"]
821    pub fn set_dragged_id(&mut self, id: Id) {
822        self.interaction_mut().potential_drag_id = Some(id);
823    }
824
825    /// Stop dragging any widget.
826    #[inline(always)]
827    #[deprecated = "Use `Context::stop_dragging` instead"]
828    pub fn stop_dragging(&mut self) {
829        self.interaction_mut().potential_drag_id = None;
830    }
831
832    /// Is something else being dragged?
833    ///
834    /// Returns true if we are dragging something, but not the given widget.
835    #[inline(always)]
836    #[deprecated = "Use `Context::dragging_something_else` instead"]
837    pub fn dragging_something_else(&self, not_this: Id) -> bool {
838        let drag_id = self.interaction().potential_drag_id;
839        drag_id.is_some() && drag_id != Some(not_this)
840    }
841
842    /// Forget window positions, sizes etc.
843    /// Can be used to auto-layout windows.
844    pub fn reset_areas(&mut self) {
845        for area in self.areas.values_mut() {
846            *area = Default::default();
847        }
848    }
849
850    /// Obtain the previous rectangle of an area.
851    pub fn area_rect(&self, id: impl Into<Id>) -> Option<Rect> {
852        self.areas().get(id.into()).map(|state| state.rect())
853    }
854
855    pub(crate) fn interaction(&self) -> &InteractionState {
856        self.interactions
857            .get(&self.viewport_id)
858            .expect("Failed to get interaction")
859    }
860
861    pub(crate) fn interaction_mut(&mut self) -> &mut InteractionState {
862        self.interactions.entry(self.viewport_id).or_default()
863    }
864
865    pub(crate) fn focus(&self) -> Option<&Focus> {
866        self.focus.get(&self.viewport_id)
867    }
868
869    pub(crate) fn focus_mut(&mut self) -> &mut Focus {
870        self.focus.entry(self.viewport_id).or_default()
871    }
872}
873
874/// ## Popups
875/// Popups are things like combo-boxes, color pickers, menus etc.
876/// Only one can be open at a time.
877impl Memory {
878    /// Is the given popup open?
879    pub fn is_popup_open(&self, popup_id: Id) -> bool {
880        self.popup == Some(popup_id) || self.everything_is_visible()
881    }
882
883    /// Is any popup open?
884    pub fn any_popup_open(&self) -> bool {
885        self.popup.is_some() || self.everything_is_visible()
886    }
887
888    /// Open the given popup, and close all other.
889    pub fn open_popup(&mut self, popup_id: Id) {
890        self.popup = Some(popup_id);
891    }
892
893    /// Close the open popup, if any.
894    pub fn close_popup(&mut self) {
895        self.popup = None;
896    }
897
898    /// Toggle the given popup between closed and open.
899    ///
900    /// Note: at most one popup can be open at one time.
901    pub fn toggle_popup(&mut self, popup_id: Id) {
902        if self.is_popup_open(popup_id) {
903            self.close_popup();
904        } else {
905            self.open_popup(popup_id);
906        }
907    }
908
909    /// If true, all windows, menus, tooltips etc are to be visible at once.
910    ///
911    /// This is useful for testing, benchmarking, pre-caching, etc.
912    ///
913    /// Experimental feature!
914    #[inline(always)]
915    pub fn everything_is_visible(&self) -> bool {
916        self.everything_is_visible
917    }
918
919    /// If true, all windows, menus, tooltips etc are to be visible at once.
920    ///
921    /// This is useful for testing, benchmarking, pre-caching, etc.
922    ///
923    /// Experimental feature!
924    pub fn set_everything_is_visible(&mut self, value: bool) {
925        self.everything_is_visible = value;
926    }
927}
928
929// ----------------------------------------------------------------------------
930
931/// Keeps track of [`Area`](crate::containers::area::Area)s, which are free-floating [`Ui`](crate::Ui)s.
932/// These [`Area`](crate::containers::area::Area)s can be in any [`Order`].
933#[derive(Clone, Debug, Default)]
934#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
935#[cfg_attr(feature = "serde", serde(default))]
936pub struct Areas {
937    areas: IdMap<area::AreaState>,
938
939    /// Back-to-front. Top is last.
940    order: Vec<LayerId>,
941
942    visible_last_frame: ahash::HashSet<LayerId>,
943    visible_current_frame: ahash::HashSet<LayerId>,
944
945    /// When an area want to be on top, it is put in here.
946    /// At the end of the frame, this is used to reorder the layers.
947    /// This means if several layers want to be on top, they will keep their relative order.
948    /// So if you close three windows and then reopen them all in one frame,
949    /// they will all be sent to the top, but keep their previous internal order.
950    wants_to_be_on_top: ahash::HashSet<LayerId>,
951
952    /// List of sublayers for each layer
953    ///
954    /// When a layer has sublayers, they are moved directly above it in the ordering.
955    sublayers: ahash::HashMap<LayerId, HashSet<LayerId>>,
956}
957
958impl Areas {
959    pub(crate) fn count(&self) -> usize {
960        self.areas.len()
961    }
962
963    pub(crate) fn get(&self, id: Id) -> Option<&area::AreaState> {
964        self.areas.get(&id)
965    }
966
967    /// Back-to-front. Top is last.
968    pub(crate) fn order(&self) -> &[LayerId] {
969        &self.order
970    }
971
972    /// For each layer, which order is it in [`Self::order`]?
973    pub(crate) fn order_map(&self) -> HashMap<LayerId, usize> {
974        self.order
975            .iter()
976            .enumerate()
977            .map(|(i, id)| (*id, i))
978            .collect()
979    }
980
981    pub(crate) fn set_state(&mut self, layer_id: LayerId, state: area::AreaState) {
982        self.visible_current_frame.insert(layer_id);
983        self.areas.insert(layer_id.id, state);
984        if !self.order.iter().any(|x| *x == layer_id) {
985            self.order.push(layer_id);
986        }
987    }
988
989    /// Top-most layer at the given position.
990    pub fn layer_id_at(
991        &self,
992        pos: Pos2,
993        layer_transforms: &HashMap<LayerId, TSTransform>,
994    ) -> Option<LayerId> {
995        for layer in self.order.iter().rev() {
996            if self.is_visible(layer) {
997                if let Some(state) = self.areas.get(&layer.id) {
998                    let mut rect = state.rect();
999                    if state.interactable {
1000                        if let Some(transform) = layer_transforms.get(layer) {
1001                            rect = *transform * rect;
1002                        }
1003
1004                        if rect.contains(pos) {
1005                            return Some(*layer);
1006                        }
1007                    }
1008                }
1009            }
1010        }
1011        None
1012    }
1013
1014    pub fn visible_last_frame(&self, layer_id: &LayerId) -> bool {
1015        self.visible_last_frame.contains(layer_id)
1016    }
1017
1018    pub fn is_visible(&self, layer_id: &LayerId) -> bool {
1019        self.visible_last_frame.contains(layer_id) || self.visible_current_frame.contains(layer_id)
1020    }
1021
1022    pub fn visible_layer_ids(&self) -> ahash::HashSet<LayerId> {
1023        self.visible_last_frame
1024            .iter()
1025            .copied()
1026            .chain(self.visible_current_frame.iter().copied())
1027            .collect()
1028    }
1029
1030    pub(crate) fn visible_windows(&self) -> impl Iterator<Item = (LayerId, &area::AreaState)> {
1031        self.visible_layer_ids()
1032            .into_iter()
1033            .filter(|layer| layer.order == crate::Order::Middle)
1034            .filter(|&layer| !self.is_sublayer(&layer))
1035            .filter_map(|layer| Some((layer, self.get(layer.id)?)))
1036    }
1037
1038    pub fn move_to_top(&mut self, layer_id: LayerId) {
1039        self.visible_current_frame.insert(layer_id);
1040        self.wants_to_be_on_top.insert(layer_id);
1041
1042        if !self.order.iter().any(|x| *x == layer_id) {
1043            self.order.push(layer_id);
1044        }
1045    }
1046
1047    /// Mark the `child` layer as a sublayer of `parent`.
1048    ///
1049    /// Sublayers are moved directly above the parent layer at the end of the frame. This is mainly
1050    /// intended for adding a new [Area](crate::Area) inside a [Window](crate::Window).
1051    ///
1052    /// This currently only supports one level of nesting. If `parent` is a sublayer of another
1053    /// layer, the behavior is unspecified.
1054    pub fn set_sublayer(&mut self, parent: LayerId, child: LayerId) {
1055        self.sublayers.entry(parent).or_default().insert(child);
1056    }
1057
1058    pub fn top_layer_id(&self, order: Order) -> Option<LayerId> {
1059        self.order
1060            .iter()
1061            .filter(|layer| layer.order == order && !self.is_sublayer(layer))
1062            .last()
1063            .copied()
1064    }
1065
1066    pub(crate) fn is_sublayer(&self, layer: &LayerId) -> bool {
1067        self.sublayers
1068            .iter()
1069            .any(|(_, children)| children.contains(layer))
1070    }
1071
1072    pub(crate) fn end_frame(&mut self) {
1073        let Self {
1074            visible_last_frame,
1075            visible_current_frame,
1076            order,
1077            wants_to_be_on_top,
1078            sublayers,
1079            ..
1080        } = self;
1081
1082        std::mem::swap(visible_last_frame, visible_current_frame);
1083        visible_current_frame.clear();
1084        order.sort_by_key(|layer| (layer.order, wants_to_be_on_top.contains(layer)));
1085        wants_to_be_on_top.clear();
1086        // For all layers with sublayers, put the sublayers directly after the parent layer:
1087        let sublayers = std::mem::take(sublayers);
1088        for (parent, children) in sublayers {
1089            let mut moved_layers = vec![parent];
1090            order.retain(|l| {
1091                if children.contains(l) {
1092                    moved_layers.push(*l);
1093                    false
1094                } else {
1095                    true
1096                }
1097            });
1098            let Some(parent_pos) = order.iter().position(|l| l == &parent) else {
1099                continue;
1100            };
1101            order.splice(parent_pos..=parent_pos, moved_layers);
1102        }
1103    }
1104}
1105
1106// ----------------------------------------------------------------------------
1107
1108#[test]
1109fn memory_impl_send_sync() {
1110    fn assert_send_sync<T: Send + Sync>() {}
1111    assert_send_sync::<Memory>();
1112}