1#![warn(missing_docs)] use 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#[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))]
93 popup: Option<Id>,
94
95 #[cfg_attr(feature = "persistence", serde(skip))]
96 everything_is_visible: bool,
97
98 pub to_global: HashMap<LayerId, TSTransform>,
105
106 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 Up,
143
144 Right,
146
147 Down,
149
150 Left,
152
153 Previous,
155
156 Next,
158
159 #[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#[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 #[cfg_attr(feature = "serde", serde(skip))]
185 pub dark_style: std::sync::Arc<Style>,
186
187 #[cfg_attr(feature = "serde", serde(skip))]
189 pub light_style: std::sync::Arc<Style>,
190
191 pub theme_preference: ThemePreference,
196
197 pub fallback_theme: Theme,
202
203 #[cfg_attr(feature = "serde", serde(skip))]
206 pub(crate) system_theme: Option<Theme>,
207
208 pub zoom_factor: f32,
218
219 #[cfg_attr(feature = "serde", serde(skip))]
230 pub zoom_with_keyboard: bool,
231
232 pub tessellation_options: epaint::TessellationOptions,
234
235 pub repaint_on_widget_change: bool,
241
242 pub max_passes: NonZeroUsize,
258
259 pub screen_reader: bool,
269
270 pub preload_font_glyphs: bool,
277
278 pub warn_on_id_clash: bool,
282
283 pub line_scroll_speed: f32,
287
288 pub scroll_zoom_speed: f32,
290
291 pub input_options: crate::input_state::InputOptions,
293
294 pub reduce_texture_memory: bool,
306}
307
308impl Default for Options {
309 fn default() -> Self {
310 let is_web = cfg!(target_arch = "wasm32");
312 let line_scroll_speed = if is_web {
313 8.0
314 } else {
315 40.0 };
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 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 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 pub fn ui(&mut self, ui: &mut crate::Ui) {
374 let theme = self.theme();
375
376 let Self {
377 dark_style, light_style,
379 theme_preference,
380 fallback_theme: _,
381 system_theme: _,
382 zoom_factor: _, zoom_with_keyboard,
384 tessellation_options,
385 repaint_on_widget_change,
386 max_passes,
387 screen_reader: _, 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#[derive(Clone, Debug, Default)]
483pub(crate) struct InteractionState {
484 pub potential_click_id: Option<Id>,
486
487 pub potential_drag_id: Option<Id>,
494}
495
496#[derive(Clone, Debug, Default)]
498pub(crate) struct Focus {
499 focused_widget: Option<FocusWidget>,
501
502 id_previous_frame: Option<Id>,
504
505 id_next_frame: Option<Id>,
507
508 #[cfg(feature = "accesskit")]
509 id_requested_by_accesskit: Option<accesskit::NodeId>,
510
511 give_to_next: bool,
514
515 last_interested: Option<Id>,
517
518 focus_direction: FocusDirection,
520
521 top_modal_layer: Option<LayerId>,
523
524 top_modal_layer_current_frame: Option<LayerId>,
526
527 focus_widgets_cache: IdMap<Rect>,
529}
530
531#[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 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 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 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 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 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; 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 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 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 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 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 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 } else {
738 false }
740 });
741
742 let current_rect = self.focus_widgets_cache.get(¤t_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 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 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 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 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.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 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 pub fn areas_mut(&mut self) -> &mut Areas {
821 self.areas.entry(self.viewport_id).or_default()
822 }
823
824 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 #[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 pub fn layer_ids(&self) -> impl ExactSizeIterator<Item = LayerId> + '_ {
845 self.areas().order().iter().copied()
846 }
847
848 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 pub(crate) fn lost_focus(&self, id: Id) -> bool {
857 self.had_focus_last_frame(id) && !self.has_focus(id)
858 }
859
860 pub(crate) fn gained_focus(&self, id: Id) -> bool {
863 !self.had_focus_last_frame(id) && self.has_focus(id)
864 }
865
866 #[inline(always)]
873 pub fn has_focus(&self, id: Id) -> bool {
874 self.focused() == Some(id)
875 }
876
877 pub fn focused(&self) -> Option<Id> {
879 self.focus().and_then(|f| f.focused())
880 }
881
882 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 #[inline(always)]
901 pub fn request_focus(&mut self, id: Id) {
902 self.focus_mut().focused_widget = Some(FocusWidget::new(id));
903 }
904
905 #[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 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 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 #[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 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 pub fn top_modal_layer(&self) -> Option<LayerId> {
973 self.focus()?.top_modal_layer()
974 }
975
976 #[inline(always)]
978 pub fn stop_text_input(&mut self) {
979 self.focus_mut().focused_widget = None;
980 }
981
982 #[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 #[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 #[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 #[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 #[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 #[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 pub fn reset_areas(&mut self) {
1038 for area in self.areas.values_mut() {
1039 *area = Default::default();
1040 }
1041 }
1042
1043 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
1067impl Memory {
1071 pub fn is_popup_open(&self, popup_id: Id) -> bool {
1073 self.popup == Some(popup_id) || self.everything_is_visible()
1074 }
1075
1076 pub fn any_popup_open(&self) -> bool {
1078 self.popup.is_some() || self.everything_is_visible()
1079 }
1080
1081 pub fn open_popup(&mut self, popup_id: Id) {
1083 self.popup = Some(popup_id);
1084 }
1085
1086 pub fn close_popup(&mut self) {
1088 self.popup = None;
1089 }
1090
1091 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 #[inline(always)]
1108 pub fn everything_is_visible(&self) -> bool {
1109 self.everything_is_visible
1110 }
1111
1112 pub fn set_everything_is_visible(&mut self, value: bool) {
1118 self.everything_is_visible = value;
1119 }
1120}
1121
1122type OrderMap = HashMap<LayerId, usize>;
1126
1127#[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 order: Vec<LayerId>,
1143
1144 order_map: OrderMap,
1146
1147 wants_to_be_on_top: ahash::HashSet<LayerId>,
1153
1154 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 pub(crate) fn order(&self) -> &[LayerId] {
1171 &self.order
1172 }
1173
1174 pub(crate) fn compare_order(&self, a: LayerId, b: LayerId) -> std::cmp::Ordering {
1178 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 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 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 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 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 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 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#[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 for &layer in &layers[3..] {
1372 areas.set_state(layer, crate::AreaState::default());
1373 }
1374 areas.end_pass(); layers.sort_by(|&a, &b| areas.compare_order(a, b));
1378
1379 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}