1#![warn(missing_docs)] use core::num::NonZeroUsize;
4
5use ahash::{HashMap, HashSet};
6use epaint::emath::TSTransform;
7
8use crate::{
9 EventFilter, Id, IdMap, LayerId, Order, Pos2, Rangef, RawInput, Rect, Style, Vec2, ViewportId,
10 ViewportIdMap, ViewportIdSet, area, vec2,
11};
12
13mod theme;
14pub use theme::{Theme, ThemePreference};
15
16#[derive(Clone, Debug)]
28#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
29#[cfg_attr(feature = "persistence", serde(default))]
30pub struct Memory {
31 pub options: Options,
33
34 pub data: crate::util::IdTypeMap,
48
49 #[cfg_attr(feature = "persistence", serde(skip))]
75 pub caches: crate::cache::CacheStorage,
76
77 #[cfg_attr(feature = "persistence", serde(skip))]
80 pub(crate) new_font_definitions: Option<epaint::text::FontDefinitions>,
81
82 #[cfg_attr(feature = "persistence", serde(skip))]
84 pub(crate) add_fonts: Vec<epaint::text::FontInsert>,
85
86 #[cfg_attr(feature = "persistence", serde(skip))]
88 pub(crate) viewport_id: ViewportId,
89
90 #[cfg_attr(feature = "persistence", serde(skip))]
91 everything_is_visible: bool,
92
93 pub to_global: HashMap<LayerId, TSTransform>,
100
101 areas: ViewportIdMap<Areas>,
104
105 #[cfg_attr(feature = "persistence", serde(skip))]
106 pub(crate) interactions: ViewportIdMap<InteractionState>,
107
108 #[cfg_attr(feature = "persistence", serde(skip))]
109 pub(crate) focus: ViewportIdMap<Focus>,
110
111 #[cfg_attr(feature = "persistence", serde(skip))]
118 popups: ViewportIdMap<OpenPopup>,
119
120 #[cfg_attr(feature = "persistence", serde(skip))]
123 requested_interrupt_ime: bool,
124}
125
126impl Default for Memory {
127 fn default() -> Self {
128 let mut slf = Self {
129 options: Default::default(),
130 data: Default::default(),
131 caches: Default::default(),
132 new_font_definitions: Default::default(),
133 interactions: Default::default(),
134 focus: Default::default(),
135 viewport_id: Default::default(),
136 areas: Default::default(),
137 to_global: Default::default(),
138 popups: Default::default(),
139 everything_is_visible: Default::default(),
140 add_fonts: Default::default(),
141 requested_interrupt_ime: Default::default(),
142 };
143 slf.interactions.entry(slf.viewport_id).or_default();
144 slf.areas.entry(slf.viewport_id).or_default();
145 slf
146 }
147}
148
149#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
151pub enum FocusDirection {
152 Up,
154
155 Right,
157
158 Down,
160
161 Left,
163
164 Previous,
166
167 Next,
169
170 #[default]
172 None,
173}
174
175impl FocusDirection {
176 fn is_cardinal(&self) -> bool {
177 match self {
178 Self::Up | Self::Right | Self::Down | Self::Left => true,
179
180 Self::Previous | Self::Next | Self::None => false,
181 }
182 }
183}
184
185#[derive(Clone, Debug, PartialEq)]
191#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
192#[cfg_attr(feature = "serde", serde(default))]
193pub struct Options {
194 #[cfg_attr(feature = "serde", serde(skip))]
196 pub dark_style: std::sync::Arc<Style>,
197
198 #[cfg_attr(feature = "serde", serde(skip))]
200 pub light_style: std::sync::Arc<Style>,
201
202 pub theme_preference: ThemePreference,
207
208 pub fallback_theme: Theme,
213
214 #[cfg_attr(feature = "serde", serde(skip))]
217 pub(crate) system_theme: Option<Theme>,
218
219 pub sync_window_theme: bool,
230
231 pub zoom_factor: f32,
241
242 #[cfg_attr(feature = "serde", serde(skip))]
253 pub zoom_with_keyboard: bool,
254
255 #[cfg_attr(feature = "serde", serde(skip))]
263 pub quit_shortcuts: Vec<crate::KeyboardShortcut>,
264
265 pub tessellation_options: epaint::TessellationOptions,
267
268 pub repaint_on_widget_change: bool,
274
275 pub max_passes: NonZeroUsize,
291
292 pub screen_reader: bool,
302
303 pub warn_on_id_clash: bool,
307
308 pub input_options: crate::input_state::InputOptions,
310
311 pub reduce_texture_memory: bool,
323}
324
325impl Default for Options {
326 fn default() -> Self {
327 Self {
328 dark_style: std::sync::Arc::new(Theme::Dark.default_style()),
329 light_style: std::sync::Arc::new(Theme::Light.default_style()),
330 theme_preference: Default::default(),
331 fallback_theme: Theme::Dark,
332 system_theme: None,
333 sync_window_theme: true,
334 zoom_factor: 1.0,
335 zoom_with_keyboard: true,
336 quit_shortcuts: vec![crate::KeyboardShortcut::new(
337 crate::Modifiers::COMMAND,
338 crate::Key::Q,
339 )],
340 tessellation_options: Default::default(),
341 repaint_on_widget_change: false,
342
343 #[expect(clippy::unwrap_used)]
344 max_passes: NonZeroUsize::new(2).unwrap(),
345 screen_reader: false,
346 warn_on_id_clash: cfg!(debug_assertions),
347
348 input_options: Default::default(),
350 reduce_texture_memory: false,
351 }
352 }
353}
354
355impl Options {
356 #[doc(hidden)]
358 pub fn begin_pass(&mut self, new_raw_input: &RawInput) {
359 self.system_theme = new_raw_input.system_theme;
360 }
361
362 pub(crate) fn theme(&self) -> Theme {
364 match self.theme_preference {
365 ThemePreference::Dark => Theme::Dark,
366 ThemePreference::Light => Theme::Light,
367 ThemePreference::System => self.system_theme.unwrap_or(self.fallback_theme),
368 }
369 }
370
371 pub(crate) fn style(&self) -> &std::sync::Arc<Style> {
372 match self.theme() {
373 Theme::Dark => &self.dark_style,
374 Theme::Light => &self.light_style,
375 }
376 }
377
378 pub(crate) fn style_mut(&mut self) -> &mut std::sync::Arc<Style> {
379 match self.theme() {
380 Theme::Dark => &mut self.dark_style,
381 Theme::Light => &mut self.light_style,
382 }
383 }
384}
385
386impl Options {
387 pub fn ui(&mut self, ui: &mut crate::Ui) {
389 let theme = self.theme();
390
391 let Self {
392 dark_style, light_style,
394 theme_preference,
395 fallback_theme: _,
396 system_theme: _,
397 sync_window_theme,
398 zoom_factor,
399 zoom_with_keyboard,
400 quit_shortcuts: _, tessellation_options,
402 repaint_on_widget_change,
403 max_passes,
404 screen_reader: _, warn_on_id_clash,
406 input_options,
407 reduce_texture_memory,
408 } = self;
409
410 use crate::Widget as _;
411 use crate::containers::CollapsingHeader;
412
413 CollapsingHeader::new("⚙ Options")
414 .default_open(false)
415 .show(ui, |ui| {
416 ui.horizontal(|ui| {
417 ui.label("Max passes:");
418 ui.add(crate::DragValue::new(max_passes).range(0..=10));
419 });
420
421 ui.checkbox(
422 repaint_on_widget_change,
423 "Repaint if any widget moves or changes id",
424 );
425
426 ui.horizontal(|ui| {
427 ui.label("Zoom factor:");
428 ui.add(crate::DragValue::new(zoom_factor).range(0.10..=10.0));
429 });
430
431 ui.checkbox(
432 zoom_with_keyboard,
433 "Zoom with keyboard (Cmd +, Cmd -, Cmd 0)",
434 );
435
436 ui.checkbox(warn_on_id_clash, "Warn if two widgets have the same Id");
437
438 ui.checkbox(reduce_texture_memory, "Reduce texture memory");
439 });
440
441 CollapsingHeader::new("🎑 Style")
442 .default_open(true)
443 .show(ui, |ui| {
444 theme_preference.radio_buttons(ui);
445
446 ui.checkbox(sync_window_theme, "Sync window theme with egui theme");
447
448 let style = std::sync::Arc::make_mut(match theme {
449 Theme::Dark => dark_style,
450 Theme::Light => light_style,
451 });
452 style.ui(ui);
453 });
454
455 CollapsingHeader::new("✒ Painting")
456 .default_open(false)
457 .show(ui, |ui| {
458 tessellation_options.ui(ui);
459 ui.vertical_centered(|ui| {
460 crate::reset_button(ui, tessellation_options, "Reset paint settings");
461 });
462 });
463
464 CollapsingHeader::new("🖱 Input")
465 .default_open(false)
466 .show(ui, |ui| {
467 input_options.ui(ui);
468 });
469
470 ui.vertical_centered(|ui| crate::reset_button(ui, self, "Reset all"));
471 }
472}
473
474#[derive(Clone, Debug, Default)]
487pub(crate) struct InteractionState {
488 pub potential_click_id: Option<Id>,
490
491 pub potential_drag_id: Option<Id>,
498}
499
500#[derive(Clone, Debug, Default)]
502pub(crate) struct Focus {
503 focused_widget: Option<FocusWidget>,
505
506 id_previous_frame: Option<Id>,
508
509 id_two_frames_ago: Option<Id>,
515
516 id_next_frame: Option<Id>,
518
519 id_requested_by_accesskit: Option<accesskit::NodeId>,
520
521 give_to_next: bool,
524
525 last_interested: Option<Id>,
527
528 focus_direction: FocusDirection,
530
531 top_modal_layer: Option<LayerId>,
533
534 top_modal_layer_current_frame: Option<LayerId>,
536
537 focus_widgets_cache: IdMap<Rect>,
539}
540
541#[derive(Clone, Copy, Debug)]
543struct FocusWidget {
544 pub id: Id,
545 pub filter: EventFilter,
546}
547
548impl FocusWidget {
549 pub fn new(id: Id) -> Self {
550 Self {
551 id,
552 filter: Default::default(),
553 }
554 }
555}
556
557impl InteractionState {
558 pub fn is_using_pointer(&self) -> bool {
560 self.potential_click_id.is_some() || self.potential_drag_id.is_some()
561 }
562}
563
564impl Focus {
565 pub fn focused(&self) -> Option<Id> {
567 self.focused_widget.as_ref().map(|w| w.id)
568 }
569
570 fn begin_pass(&mut self, new_input: &crate::data::input::RawInput) {
571 self.id_two_frames_ago = self.id_previous_frame;
572 self.id_previous_frame = self.focused();
573 if let Some(id) = self.id_next_frame.take() {
574 self.focused_widget = Some(FocusWidget::new(id));
575 }
576 let event_filter = self.focused_widget.map(|w| w.filter).unwrap_or_default();
577
578 self.id_requested_by_accesskit = None;
579
580 self.focus_direction = FocusDirection::None;
581
582 for event in &new_input.events {
583 if !event_filter.matches(event)
584 && let crate::Event::Key {
585 key,
586 pressed: true,
587 modifiers,
588 ..
589 } = event
590 && let Some(cardinality) = match key {
591 crate::Key::ArrowUp if !modifiers.any() => Some(FocusDirection::Up),
592 crate::Key::ArrowRight if !modifiers.any() => Some(FocusDirection::Right),
593 crate::Key::ArrowDown if !modifiers.any() => Some(FocusDirection::Down),
594 crate::Key::ArrowLeft if !modifiers.any() => Some(FocusDirection::Left),
595
596 crate::Key::Tab if !modifiers.any() => Some(FocusDirection::Next),
597 crate::Key::Tab if modifiers.shift_only() => Some(FocusDirection::Previous),
598
599 crate::Key::Escape if !modifiers.any() => {
600 self.focused_widget = None;
601 Some(FocusDirection::None)
602 }
603
604 _ => None,
605 }
606 {
607 self.focus_direction = cardinality;
608 }
609
610 if let crate::Event::AccessKitActionRequest(accesskit::ActionRequest {
611 action: accesskit::Action::Focus,
612 target_node,
613 target_tree,
614 data: None,
615 }) = event
616 && *target_tree == accesskit::TreeId::ROOT
617 {
618 self.id_requested_by_accesskit = Some(*target_node);
619 }
620 }
621 }
622
623 pub(crate) fn end_pass(&mut self, used_ids: &IdMap<Rect>) {
624 if self.focus_direction.is_cardinal()
625 && let Some(found_widget) = self.find_widget_in_direction(used_ids)
626 {
627 self.focused_widget = Some(FocusWidget::new(found_widget));
628 }
629
630 if let Some(focused_widget) = self.focused_widget {
631 let recently_gained_focus = self.id_previous_frame != Some(focused_widget.id);
633
634 if !recently_gained_focus && !used_ids.contains_key(&focused_widget.id) {
635 self.focused_widget = None;
637 }
638 }
639
640 self.top_modal_layer = self.top_modal_layer_current_frame.take();
641 }
642
643 pub(crate) fn had_focus_last_frame(&self, id: Id) -> bool {
644 self.id_previous_frame == Some(id)
645 }
646
647 fn interested_in_focus(&mut self, id: Id) {
648 if self.id_requested_by_accesskit == Some(id.accesskit_id()) {
649 self.focused_widget = Some(FocusWidget::new(id));
650 self.id_requested_by_accesskit = None;
651 self.give_to_next = false;
652 self.reset_focus();
653 }
654
655 self.focus_widgets_cache
657 .entry(id)
658 .or_insert(Rect::EVERYTHING);
659
660 if self.give_to_next && !self.had_focus_last_frame(id) {
661 self.focused_widget = Some(FocusWidget::new(id));
662 self.give_to_next = false;
663 } else if self.focused() == Some(id) {
664 if self.focus_direction == FocusDirection::Next {
665 self.focused_widget = None;
666 self.give_to_next = true;
667 self.reset_focus();
668 } else if self.focus_direction == FocusDirection::Previous {
669 self.id_next_frame = self.last_interested; self.reset_focus();
671 }
672 } else if self.focus_direction == FocusDirection::Next
673 && self.focused_widget.is_none()
674 && !self.give_to_next
675 {
676 self.focused_widget = Some(FocusWidget::new(id));
678 self.reset_focus();
679 } else if self.focus_direction == FocusDirection::Previous
680 && self.focused_widget.is_none()
681 && !self.give_to_next
682 {
683 self.focused_widget = self.last_interested.map(FocusWidget::new);
685 self.reset_focus();
686 }
687
688 self.last_interested = Some(id);
689 }
690
691 fn set_modal_layer(&mut self, layer_id: LayerId) {
692 self.top_modal_layer_current_frame = Some(layer_id);
693 }
694
695 pub(crate) fn top_modal_layer(&self) -> Option<LayerId> {
696 self.top_modal_layer
697 }
698
699 fn reset_focus(&mut self) {
700 self.focus_direction = FocusDirection::None;
701 }
702
703 fn find_widget_in_direction(&mut self, new_rects: &IdMap<Rect>) -> Option<Id> {
704 fn range_diff(a: Rangef, b: Rangef) -> f32 {
710 let has_significant_overlap = a.intersection(b).span() >= 0.5 * b.span().min(a.span());
711 if has_significant_overlap {
712 0.0
713 } else {
714 a.center() - b.center()
715 }
716 }
717
718 let current_focused = self.focused_widget?;
719
720 let search_direction = match self.focus_direction {
722 FocusDirection::Up => Vec2::UP,
723 FocusDirection::Right => Vec2::RIGHT,
724 FocusDirection::Down => Vec2::DOWN,
725 FocusDirection::Left => Vec2::LEFT,
726 _ => {
727 return None;
728 }
729 };
730
731 self.focus_widgets_cache.retain(|id, old_rect| {
733 if let Some(new_rect) = new_rects.get(id) {
734 *old_rect = *new_rect;
735 true } else {
737 false }
739 });
740
741 let current_rect = self.focus_widgets_cache.get(¤t_focused.id)?;
742
743 let mut best_score = f32::INFINITY;
744 let mut best_id = None;
745
746 #[expect(clippy::iter_over_hash_type)]
748 for (candidate_id, candidate_rect) in &self.focus_widgets_cache {
749 if *candidate_id == current_focused.id {
750 continue;
751 }
752
753 let to_candidate = vec2(
755 range_diff(candidate_rect.x_range(), current_rect.x_range()),
756 range_diff(candidate_rect.y_range(), current_rect.y_range()),
757 );
758
759 let acos_angle = to_candidate.normalized().dot(search_direction);
760
761 let is_in_search_cone = 0.5_f32.sqrt() <= acos_angle;
764 if is_in_search_cone {
765 let distance = to_candidate.length();
766
767 let score = distance / (acos_angle * acos_angle);
769
770 if score < best_score {
771 best_score = score;
772 best_id = Some(*candidate_id);
773 }
774 }
775 }
776
777 best_id
778 }
779}
780
781impl Memory {
782 pub(crate) fn begin_pass(&mut self, new_raw_input: &RawInput, viewports: &ViewportIdSet) {
783 profiling::function_scope!();
784
785 self.viewport_id = new_raw_input.viewport_id;
786
787 self.interactions.retain(|id, _| viewports.contains(id));
789 self.areas.retain(|id, _| viewports.contains(id));
790 self.popups.retain(|id, _| viewports.contains(id));
791
792 self.areas.entry(self.viewport_id).or_default();
793
794 self.requested_interrupt_ime = false;
795
796 self.options.begin_pass(new_raw_input);
799
800 self.focus
801 .entry(self.viewport_id)
802 .or_default()
803 .begin_pass(new_raw_input);
804 }
805
806 pub(crate) fn end_pass(&mut self, used_ids: &IdMap<Rect>) {
807 self.caches.update();
808 self.areas_mut().end_pass();
809 self.focus_mut().end_pass(used_ids);
810
811 if let Some(popup) = self.popups.get_mut(&self.viewport_id) {
813 if popup.open_this_frame {
814 popup.open_this_frame = false;
815 } else {
816 self.popups.remove(&self.viewport_id);
817 }
818 }
819 }
820
821 pub(crate) fn set_viewport_id(&mut self, viewport_id: ViewportId) {
822 self.viewport_id = viewport_id;
823 }
824
825 pub fn areas(&self) -> &Areas {
827 self.areas
828 .get(&self.viewport_id)
829 .expect("Memory broken: no area for the current viewport")
830 }
831
832 pub fn areas_mut(&mut self) -> &mut Areas {
834 self.areas.entry(self.viewport_id).or_default()
835 }
836
837 pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
839 let layer_id = self.areas().layer_id_at(pos, &self.to_global)?;
840 if self.is_above_modal_layer(layer_id) {
841 Some(layer_id)
842 } else {
843 self.top_modal_layer()
844 }
845 }
846
847 pub fn layer_ids(&self) -> impl ExactSizeIterator<Item = LayerId> + '_ {
849 self.areas().order().iter().copied()
850 }
851
852 pub fn had_focus_last_frame(&self, id: Id) -> bool {
855 self.focus().and_then(|f| f.id_previous_frame) == Some(id)
856 }
857
858 pub(crate) fn lost_focus(&self, id: Id) -> bool {
868 let had_recent_focus = self
869 .focus()
870 .map(|f| f.id_previous_frame == Some(id) || f.id_two_frames_ago == Some(id))
871 .unwrap_or(false);
872 had_recent_focus && !self.has_focus(id)
873 }
874
875 pub(crate) fn gained_focus(&self, id: Id) -> bool {
878 !self.had_focus_last_frame(id) && self.has_focus(id)
879 }
880
881 #[inline(always)]
888 pub fn has_focus(&self, id: Id) -> bool {
889 self.focused() == Some(id)
890 }
891
892 pub fn focused(&self) -> Option<Id> {
894 self.focus()?.focused()
895 }
896
897 pub fn set_focus_lock_filter(&mut self, id: Id, event_filter: EventFilter) {
904 if self.had_focus_last_frame(id)
905 && self.has_focus(id)
906 && let Some(focused) = &mut self.focus_mut().focused_widget
907 && focused.id == id
908 {
909 focused.filter = event_filter;
910 }
911 }
912
913 #[inline(always)]
918 pub fn request_focus(&mut self, id: Id) {
919 self.focus_mut().focused_widget = Some(FocusWidget::new(id));
920 self.interrupt_ime();
921 }
922
923 #[inline(always)]
926 pub fn surrender_focus(&mut self, id: Id) {
927 let focus = self.focus_mut();
928 if focus.focused() == Some(id) {
929 focus.focused_widget = None;
930 }
931 }
932
933 pub fn move_focus(&mut self, direction: FocusDirection) {
935 self.focus_mut().focus_direction = direction;
936 }
937
938 pub fn is_above_modal_layer(&self, layer_id: LayerId) -> bool {
942 if let Some(modal_layer) = self.focus().and_then(|f| f.top_modal_layer) {
943 matches!(
944 self.areas().compare_order(layer_id, modal_layer),
945 core::cmp::Ordering::Equal | core::cmp::Ordering::Greater
946 )
947 } else {
948 true
949 }
950 }
951
952 pub fn allows_interaction(&self, layer_id: LayerId) -> bool {
957 let is_above_modal_layer = self.is_above_modal_layer(layer_id);
958 let ordering_allows_interaction = layer_id.order.allow_interaction();
959 is_above_modal_layer && ordering_allows_interaction
960 }
961
962 #[inline(always)]
972 pub fn interested_in_focus(&mut self, id: Id, layer_id: LayerId) {
973 if !self.allows_interaction(layer_id) {
974 return;
975 }
976 self.focus_mut().interested_in_focus(id);
977 }
978
979 pub fn set_modal_layer(&mut self, layer_id: LayerId) {
982 if let Some(current) = self.focus().and_then(|f| f.top_modal_layer_current_frame)
983 && matches!(
984 self.areas().compare_order(layer_id, current),
985 core::cmp::Ordering::Less
986 )
987 {
988 return;
989 }
990
991 self.focus_mut().set_modal_layer(layer_id);
992 }
993
994 pub fn top_modal_layer(&self) -> Option<LayerId> {
996 self.focus()?.top_modal_layer()
997 }
998
999 #[inline(always)]
1001 pub fn stop_text_input(&mut self) {
1002 self.focus_mut().focused_widget = None;
1003 }
1004
1005 pub fn reset_areas(&mut self) {
1008 #[expect(clippy::iter_over_hash_type)]
1009 for area in self.areas.values_mut() {
1010 *area = Default::default();
1011 }
1012 }
1013
1014 pub fn area_rect(&self, id: impl Into<Id>) -> Option<Rect> {
1016 self.areas().get(id.into()).map(|state| state.rect())
1017 }
1018
1019 pub(crate) fn interaction(&self) -> &InteractionState {
1020 self.interactions
1021 .get(&self.viewport_id)
1022 .expect("Failed to get interaction")
1023 }
1024
1025 pub(crate) fn interaction_mut(&mut self) -> &mut InteractionState {
1026 self.interactions.entry(self.viewport_id).or_default()
1027 }
1028
1029 pub(crate) fn focus(&self) -> Option<&Focus> {
1030 self.focus.get(&self.viewport_id)
1031 }
1032
1033 pub(crate) fn focus_mut(&mut self) -> &mut Focus {
1034 self.focus.entry(self.viewport_id).or_default()
1035 }
1036
1037 #[inline(always)]
1042 pub fn owns_ime_events(&self, id: Id) -> bool {
1043 self.has_focus(id)
1048 }
1049
1050 pub fn interrupt_ime(&mut self) {
1052 self.requested_interrupt_ime = true;
1053 }
1054
1055 pub(crate) fn should_interrupt_ime(&self) -> bool {
1056 self.requested_interrupt_ime
1057 }
1058}
1059
1060#[derive(Clone, Copy, Debug)]
1062struct OpenPopup {
1063 id: Id,
1065
1066 pos: Option<Pos2>,
1068
1069 open_this_frame: bool,
1071}
1072
1073impl OpenPopup {
1074 fn new(id: Id, pos: Option<Pos2>) -> Self {
1076 Self {
1077 id,
1078 pos,
1079 open_this_frame: true,
1080 }
1081 }
1082}
1083
1084impl Memory {
1088 pub(crate) fn is_popup_open(&self, popup_id: Id) -> bool {
1089 self.popups
1090 .get(&self.viewport_id)
1091 .is_some_and(|state| state.id == popup_id)
1092 || self.everything_is_visible()
1093 }
1094
1095 pub(crate) fn any_popup_open(&self) -> bool {
1096 self.popups.contains_key(&self.viewport_id) || self.everything_is_visible()
1097 }
1098
1099 pub(crate) fn open_popup(&mut self, popup_id: Id) {
1100 self.popups
1101 .insert(self.viewport_id, OpenPopup::new(popup_id, None));
1102 }
1103
1104 pub(crate) fn keep_popup_open(&mut self, popup_id: Id) {
1105 if let Some(state) = self.popups.get_mut(&self.viewport_id)
1106 && state.id == popup_id
1107 {
1108 state.open_this_frame = true;
1109 }
1110 }
1111
1112 pub(crate) fn open_popup_at(&mut self, popup_id: Id, pos: impl Into<Option<Pos2>>) {
1113 self.popups
1114 .insert(self.viewport_id, OpenPopup::new(popup_id, pos.into()));
1115 }
1116
1117 pub(crate) fn popup_position(&self, id: Id) -> Option<Pos2> {
1118 let state = self.popups.get(&self.viewport_id)?;
1119 if state.id == id { state.pos } else { None }
1120 }
1121
1122 pub(crate) fn close_all_popups(&mut self) {
1123 self.popups.clear();
1124 }
1125
1126 pub(crate) fn close_popup(&mut self, popup_id: Id) {
1127 if self.is_popup_open(popup_id) {
1128 self.popups.remove(&self.viewport_id);
1129 }
1130 }
1131
1132 pub(crate) fn toggle_popup(&mut self, popup_id: Id) {
1133 if self.is_popup_open(popup_id) {
1134 self.close_popup(popup_id);
1135 } else {
1136 self.open_popup(popup_id);
1137 }
1138 }
1139}
1140
1141impl Memory {
1142 #[inline(always)]
1148 pub fn everything_is_visible(&self) -> bool {
1149 self.everything_is_visible
1150 }
1151
1152 pub fn set_everything_is_visible(&mut self, value: bool) {
1158 self.everything_is_visible = value;
1159 }
1160}
1161
1162type OrderMap = HashMap<LayerId, usize>;
1166
1167#[derive(Clone, Debug, Default)]
1170#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
1171#[cfg_attr(feature = "serde", serde(default))]
1172pub struct Areas {
1173 areas: IdMap<area::AreaState>,
1174
1175 visible_areas_last_frame: ahash::HashSet<LayerId>,
1176 visible_areas_current_frame: ahash::HashSet<LayerId>,
1177
1178 order: Vec<LayerId>,
1183
1184 order_map: OrderMap,
1186
1187 wants_to_be_on_top: ahash::HashSet<LayerId>,
1193
1194 sublayers: ahash::HashMap<LayerId, HashSet<LayerId>>,
1198}
1199
1200impl Areas {
1201 pub(crate) fn count(&self) -> usize {
1202 self.areas.len()
1203 }
1204
1205 pub(crate) fn get(&self, id: Id) -> Option<&area::AreaState> {
1206 self.areas.get(&id)
1207 }
1208
1209 pub(crate) fn get_mut(&mut self, id: Id) -> Option<&mut area::AreaState> {
1210 self.areas.get_mut(&id)
1211 }
1212
1213 pub(crate) fn is_interactable(&self, layer_id: LayerId) -> bool {
1215 self.get(layer_id.id).is_none_or(|area| area.interactable)
1216 }
1217
1218 pub(crate) fn order(&self) -> &[LayerId] {
1220 &self.order
1221 }
1222
1223 pub(crate) fn compare_order(&self, a: LayerId, b: LayerId) -> core::cmp::Ordering {
1227 match a.order.cmp(&b.order) {
1231 core::cmp::Ordering::Equal => self.order_map.get(&a).cmp(&self.order_map.get(&b)),
1232 cmp => cmp,
1233 }
1234 }
1235
1236 pub(crate) fn set_state(&mut self, layer_id: LayerId, state: area::AreaState) {
1237 self.visible_areas_current_frame.insert(layer_id);
1238 self.areas.insert(layer_id.id, state);
1239 if !self.order.contains(&layer_id) {
1240 self.order.push(layer_id);
1241 }
1242 }
1243
1244 pub fn layer_id_at(
1246 &self,
1247 pos: Pos2,
1248 layer_to_global: &HashMap<LayerId, TSTransform>,
1249 ) -> Option<LayerId> {
1250 for layer in self.order.iter().rev() {
1251 if self.is_visible(layer)
1252 && let Some(state) = self.areas.get(&layer.id)
1253 {
1254 let mut rect = state.rect();
1255 if state.interactable {
1256 if let Some(to_global) = layer_to_global.get(layer) {
1257 rect = *to_global * rect;
1258 }
1259
1260 if rect.contains(pos) {
1261 return Some(*layer);
1262 }
1263 }
1264 }
1265 }
1266 None
1267 }
1268
1269 pub fn visible_last_frame(&self, layer_id: &LayerId) -> bool {
1270 self.visible_areas_last_frame.contains(layer_id)
1271 }
1272
1273 pub fn is_visible(&self, layer_id: &LayerId) -> bool {
1274 self.visible_areas_last_frame.contains(layer_id)
1275 || self.visible_areas_current_frame.contains(layer_id)
1276 }
1277
1278 pub fn visible_layer_ids(&self) -> ahash::HashSet<LayerId> {
1279 core::iter::chain(
1280 &self.visible_areas_last_frame,
1281 &self.visible_areas_current_frame,
1282 )
1283 .copied()
1284 .collect()
1285 }
1286
1287 pub(crate) fn visible_windows(&self) -> impl Iterator<Item = (LayerId, &area::AreaState)> {
1288 self.visible_layer_ids()
1289 .into_iter()
1290 .filter(|layer| layer.order == crate::Order::Middle)
1291 .filter(|&layer| !self.is_sublayer(&layer))
1292 .filter_map(|layer| Some((layer, self.get(layer.id)?)))
1293 }
1294
1295 pub fn move_to_top(&mut self, layer_id: LayerId) {
1296 self.visible_areas_current_frame.insert(layer_id);
1297 self.wants_to_be_on_top.insert(layer_id);
1298
1299 if !self.order.contains(&layer_id) {
1300 self.order.push(layer_id);
1301 }
1302 }
1303
1304 pub fn set_sublayer(&mut self, parent: LayerId, child: LayerId) {
1314 debug_assert_eq!(
1315 parent.order, child.order,
1316 "DEBUG ASSERT: Trying to set sublayers across layers of different order ({:?}, {:?}), which is currently undefined behavior in egui",
1317 parent.order, child.order
1318 );
1319
1320 self.sublayers.entry(parent).or_default().insert(child);
1321
1322 if !self.order.contains(&parent) {
1324 self.order.push(parent);
1325 }
1326 if !self.order.contains(&child) {
1327 self.order.push(child);
1328 }
1329 }
1330
1331 pub fn top_layer_id(&self, order: Order) -> Option<LayerId> {
1332 self.order
1333 .iter()
1334 .rfind(|layer| layer.order == order && !self.is_sublayer(layer))
1335 .copied()
1336 }
1337
1338 pub fn parent_layer(&self, layer_id: LayerId) -> Option<LayerId> {
1340 self.sublayers.iter().find_map(|(parent, children)| {
1341 if children.contains(&layer_id) {
1342 Some(*parent)
1343 } else {
1344 None
1345 }
1346 })
1347 }
1348
1349 pub fn child_layers(&self, layer_id: LayerId) -> impl Iterator<Item = LayerId> + '_ {
1351 self.sublayers.get(&layer_id).into_iter().flatten().copied()
1352 }
1353
1354 pub(crate) fn is_sublayer(&self, layer: &LayerId) -> bool {
1355 self.parent_layer(*layer).is_some()
1356 }
1357
1358 pub(crate) fn end_pass(&mut self) {
1359 let Self {
1360 visible_areas_last_frame,
1361 visible_areas_current_frame,
1362 order,
1363 wants_to_be_on_top,
1364 sublayers,
1365 ..
1366 } = self;
1367
1368 core::mem::swap(visible_areas_last_frame, visible_areas_current_frame);
1369 visible_areas_current_frame.clear();
1370
1371 order.sort_by_key(|layer| (layer.order, wants_to_be_on_top.contains(layer)));
1372 wants_to_be_on_top.clear();
1373
1374 #[expect(clippy::iter_over_hash_type)]
1377 for (parent, children) in core::mem::take(sublayers) {
1378 let mut moved_layers = vec![parent]; order.retain(|l| {
1381 if children.contains(l) {
1382 moved_layers.push(*l); false
1384 } else {
1385 true
1386 }
1387 });
1388 let Some(parent_pos) = order.iter().position(|l| l == &parent) else {
1389 continue;
1390 };
1391 order.splice(parent_pos..=parent_pos, moved_layers); }
1393
1394 self.order_map = self
1395 .order
1396 .iter()
1397 .enumerate()
1398 .map(|(i, id)| (*id, i))
1399 .collect();
1400 }
1401}
1402
1403#[test]
1406fn memory_impl_send_sync() {
1407 fn assert_send_sync<T: Send + Sync>() {}
1408 assert_send_sync::<Memory>();
1409}
1410
1411#[test]
1413fn lost_focus_fires_after_mid_frame_focus_transfer() {
1414 use crate::data::input::RawInput;
1415 let a = Id::new("A");
1416 let b = Id::new("B");
1417 let mut focus = Focus::default();
1418 let raw = RawInput::default();
1419
1420 fn lost_focus_check(focus: &Focus, id: Id) -> bool {
1421 let was_focused =
1422 focus.id_previous_frame == Some(id) || focus.id_two_frames_ago == Some(id);
1423 was_focused && focus.focused() != Some(id)
1424 }
1425
1426 {
1428 focus.begin_pass(&raw);
1429 focus.focused_widget = Some(FocusWidget::new(a));
1430 }
1431
1432 {
1434 focus.begin_pass(&raw);
1435 assert_eq!(focus.id_previous_frame, Some(a));
1436 assert!(!lost_focus_check(&focus, a));
1437 focus.focused_widget = Some(FocusWidget::new(b));
1438 }
1439
1440 {
1442 focus.begin_pass(&raw);
1443 assert_eq!(focus.id_two_frames_ago, Some(a));
1444 assert_eq!(focus.id_previous_frame, Some(b));
1445 assert!(lost_focus_check(&focus, a), "`A` lost_focus must fire");
1446 assert!(!lost_focus_check(&focus, b));
1447 }
1448
1449 {
1451 focus.begin_pass(&raw);
1452 assert!(
1453 !lost_focus_check(&focus, a),
1454 "A's lost_focus must stop firing once the two-frame window passes",
1455 );
1456 }
1457}
1458
1459#[test]
1460fn order_map_total_ordering() {
1461 let mut layers = [
1462 LayerId::new(Order::Tooltip, Id::new("a")),
1463 LayerId::new(Order::Background, Id::new("b")),
1464 LayerId::new(Order::Background, Id::new("c")),
1465 LayerId::new(Order::Tooltip, Id::new("d")),
1466 LayerId::new(Order::Background, Id::new("e")),
1467 LayerId::new(Order::Background, Id::new("f")),
1468 LayerId::new(Order::Tooltip, Id::new("g")),
1469 ];
1470 let mut areas = Areas::default();
1471
1472 for &layer in &layers[3..] {
1474 areas.set_state(layer, crate::AreaState::default());
1475 }
1476 areas.end_pass(); layers.sort_by(|&a, &b| areas.compare_order(a, b));
1480
1481 let mut equivalence_classes = vec![0];
1483 let mut i = 0;
1484 for &[a, b] in layers.array_windows() {
1485 assert!(a.order <= b.order, "does not follow LayerId.order");
1486 if areas.compare_order(a, b) != core::cmp::Ordering::Equal {
1487 i += 1;
1488 }
1489 equivalence_classes.push(i);
1490 }
1491 assert_eq!(layers.len(), equivalence_classes.len());
1492 for (&l1, c1) in core::iter::zip(&layers, &equivalence_classes) {
1493 for (&l2, c2) in core::iter::zip(&layers, &equivalence_classes) {
1494 assert_eq!(
1495 c1.cmp(c2),
1496 areas.compare_order(l1, l2),
1497 "not a total ordering",
1498 );
1499 }
1500 }
1501}