1#![warn(missing_docs)] use core::{cell::RefCell, panic::Location, time::Duration};
4use std::{borrow::Cow, sync::Arc};
5
6use emath::GuiRounding as _;
7use epaint::{
8 ClippedPrimitive, ClippedShape, Color32, ImageData, Pos2, Rect, StrokeKind,
9 TessellationOptions, TextureId, Vec2,
10 emath::{self, TSTransform},
11 mutex::RwLock,
12 stats::PaintStats,
13 tessellator,
14 text::{FontInsert, FontPriority, Fonts, FontsView},
15 vec2,
16};
17
18use crate::{
19 Align2, CursorIcon, DeferredViewportUiCallback, FontDefinitions, Grid, Id, ImmediateViewport,
20 ImmediateViewportRendererCallback, Key, KeyboardShortcut, Label, LayerId, Memory,
21 ModifierNames, Modifiers, NumExt as _, Order, Painter, RawInput, Response, RichText,
22 SafeAreaInsets, ScrollArea, Sense, Style, TextStyle, TextureHandle, TextureOptions, Ui,
23 UiBuilder, ViewportBuilder, ViewportCommand, ViewportId, ViewportIdMap, ViewportIdPair,
24 ViewportIdSet, ViewportOutput, Visuals, Widget as _, WidgetRect, WidgetText,
25 animation_manager::AnimationManager,
26 containers::{self, area::AreaState},
27 data::output::PlatformOutput,
28 epaint,
29 hit_test::WidgetHits,
30 input_state::{InputState, MultiTouchInfo, PointerEvent, SurrenderFocusOn},
31 interaction::InteractionSnapshot,
32 layers::GraphicLayers,
33 load::{self, Bytes, Loaders, SizedTexture},
34 memory::{Options, Theme},
35 os::OperatingSystem,
36 output::{FullOutput, LogicOutput},
37 pass_state::PassState,
38 plugin::{self, TypedPluginHandle},
39 resize, response, scroll_area,
40 util::IdTypeMap,
41 viewport::ViewportClass,
42};
43
44use crate::IdMap;
45
46#[derive(Clone, Copy, Debug)]
50pub struct RequestRepaintInfo {
51 pub viewport_id: ViewportId,
53
54 pub delay: Duration,
56
57 pub current_cumulative_pass_nr: u64,
62}
63
64thread_local! {
67 static IMMEDIATE_VIEWPORT_RENDERER: RefCell<Option<Box<ImmediateViewportRendererCallback>>> = Default::default();
68}
69
70struct WrappedTextureManager(Arc<RwLock<epaint::TextureManager>>);
73
74impl Default for WrappedTextureManager {
75 fn default() -> Self {
76 let mut tex_mngr = epaint::textures::TextureManager::default();
77
78 let font_id = tex_mngr.alloc(
80 "egui_font_texture".into(),
81 epaint::ColorImage::filled([0, 0], Color32::TRANSPARENT).into(),
82 Default::default(),
83 );
84 assert_eq!(
85 font_id,
86 TextureId::default(),
87 "font id should be equal to TextureId::default(), but was {font_id:?}",
88 );
89
90 Self(Arc::new(RwLock::new(tex_mngr)))
91 }
92}
93
94impl ContextImpl {
98 fn begin_pass_repaint_logic(&mut self, viewport_id: ViewportId) {
100 let viewport = self.viewports.entry(viewport_id).or_default();
101
102 core::mem::swap(
103 &mut viewport.repaint.prev_causes,
104 &mut viewport.repaint.causes,
105 );
106 viewport.repaint.causes.clear();
107
108 viewport.repaint.prev_pass_paint_delay = viewport.repaint.repaint_delay;
109
110 if viewport.repaint.outstanding == 0 {
111 viewport.repaint.repaint_delay = Duration::MAX;
113 } else {
114 viewport.repaint.repaint_delay = Duration::ZERO;
115 viewport.repaint.outstanding -= 1;
116 if let Some(callback) = &self.request_repaint_callback {
117 (callback)(RequestRepaintInfo {
118 viewport_id,
119 delay: Duration::ZERO,
120 current_cumulative_pass_nr: viewport.repaint.cumulative_pass_nr,
121 });
122 }
123 }
124 }
125
126 fn request_repaint(&mut self, viewport_id: ViewportId, cause: RepaintCause) {
127 self.request_repaint_after(Duration::ZERO, viewport_id, cause);
128 }
129
130 fn request_repaint_after(
131 &mut self,
132 mut delay: Duration,
133 viewport_id: ViewportId,
134 cause: RepaintCause,
135 ) {
136 let viewport = self.viewports.entry(viewport_id).or_default();
137
138 if delay == Duration::ZERO {
139 viewport.repaint.outstanding = 1;
142 } else {
143 }
148
149 if let Ok(predicted_frame_time) = Duration::try_from_secs_f32(viewport.input.predicted_dt) {
150 delay = delay.saturating_sub(predicted_frame_time);
152 }
153
154 viewport.repaint.causes.push(cause);
155
156 if delay < viewport.repaint.repaint_delay {
160 viewport.repaint.repaint_delay = delay;
161
162 if let Some(callback) = &self.request_repaint_callback {
163 (callback)(RequestRepaintInfo {
164 viewport_id,
165 delay,
166 current_cumulative_pass_nr: viewport.repaint.cumulative_pass_nr,
167 });
168 }
169 }
170 }
171
172 #[must_use]
173 fn requested_immediate_repaint_prev_pass(&self, viewport_id: &ViewportId) -> bool {
174 self.viewports
175 .get(viewport_id)
176 .is_some_and(|v| v.repaint.requested_immediate_repaint_prev_pass())
177 }
178
179 #[must_use]
180 fn has_requested_repaint(&self, viewport_id: &ViewportId) -> bool {
181 self.viewports
182 .get(viewport_id)
183 .is_some_and(|v| 0 < v.repaint.outstanding || v.repaint.repaint_delay < Duration::MAX)
184 }
185}
186
187#[derive(Default)]
194pub struct ViewportState {
195 pub class: ViewportClass,
200
201 pub builder: ViewportBuilder,
203
204 pub viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,
208
209 pub input: InputState,
210
211 pub this_pass: PassState,
213
214 pub prev_pass: PassState,
218
219 pub used: bool,
221
222 repaint: ViewportRepaintInfo,
224
225 pub hits: WidgetHits,
230
231 pub interact_widgets: InteractionSnapshot,
235
236 pub graphics: GraphicLayers,
240 pub output: PlatformOutput,
242 pub commands: Vec<ViewportCommand>,
243
244 pub num_multipass_in_row: usize,
247
248 pub(crate) last_sent_window_theme: Option<crate::SystemTheme>,
253}
254
255#[derive(Clone, PartialEq, Eq, Hash)]
257pub struct RepaintCause {
258 pub file: &'static str,
260
261 pub line: u32,
263
264 pub reason: Cow<'static, str>,
266}
267
268impl core::fmt::Debug for RepaintCause {
269 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
270 write!(f, "{}:{} {}", self.file, self.line, self.reason)
271 }
272}
273
274impl core::fmt::Display for RepaintCause {
275 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
276 write!(f, "{}:{} {}", self.file, self.line, self.reason)
277 }
278}
279
280impl RepaintCause {
281 #[expect(clippy::new_without_default)]
283 #[track_caller]
284 pub fn new() -> Self {
285 let caller = Location::caller();
286 Self {
287 file: caller.file(),
288 line: caller.line(),
289 reason: "".into(),
290 }
291 }
292
293 #[track_caller]
296 pub fn new_reason(reason: impl Into<Cow<'static, str>>) -> Self {
297 let caller = Location::caller();
298 Self {
299 file: caller.file(),
300 line: caller.line(),
301 reason: reason.into(),
302 }
303 }
304}
305
306struct ViewportRepaintInfo {
308 cumulative_frame_nr: u64,
314
315 cumulative_pass_nr: u64,
319
320 repaint_delay: Duration,
327
328 outstanding: u8,
330
331 causes: Vec<RepaintCause>,
333
334 prev_causes: Vec<RepaintCause>,
337
338 prev_pass_paint_delay: Duration,
343}
344
345impl Default for ViewportRepaintInfo {
346 fn default() -> Self {
347 Self {
348 cumulative_frame_nr: 0,
349 cumulative_pass_nr: 0,
350
351 repaint_delay: Duration::MAX,
353
354 outstanding: 1,
356
357 causes: Default::default(),
358 prev_causes: Default::default(),
359
360 prev_pass_paint_delay: Duration::MAX,
361 }
362 }
363}
364
365impl ViewportRepaintInfo {
366 pub fn requested_immediate_repaint_prev_pass(&self) -> bool {
367 self.prev_pass_paint_delay == Duration::ZERO
368 }
369}
370
371#[derive(Default)]
374struct ContextImpl {
375 fonts: Option<Fonts>,
376 font_definitions: FontDefinitions,
377
378 memory: Memory,
379 animation_manager: AnimationManager,
380
381 plugins: plugin::Plugins,
382 safe_area: SafeAreaInsets,
383
384 tex_manager: WrappedTextureManager,
391
392 new_zoom_factor: Option<f32>,
394
395 os: OperatingSystem,
396
397 viewport_stack: Vec<ViewportIdPair>,
399
400 last_viewport: ViewportId,
402
403 paint_stats: PaintStats,
404
405 request_repaint_callback: Option<Box<dyn Fn(RequestRepaintInfo) + Send + Sync>>,
406
407 viewport_parents: ViewportIdMap<ViewportId>,
408 viewports: ViewportIdMap<ViewportState>,
409
410 embed_viewports: bool,
411
412 is_accesskit_enabled: bool,
413
414 loaders: Arc<Loaders>,
415}
416
417impl ContextImpl {
418 fn begin_pass(&mut self, mut new_raw_input: RawInput) {
419 let viewport_id = new_raw_input.viewport_id;
420 let parent_id = new_raw_input
421 .viewports
422 .get(&viewport_id)
423 .and_then(|v| v.parent)
424 .unwrap_or_default();
425 let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent_id);
426
427 if let Some(safe_area) = new_raw_input.safe_area_insets {
428 self.safe_area = safe_area;
429 }
430
431 let is_outermost_viewport = self.viewport_stack.is_empty(); self.viewport_stack.push(ids);
433
434 self.begin_pass_repaint_logic(viewport_id);
435
436 let viewport = self.viewports.entry(viewport_id).or_default();
437
438 if is_outermost_viewport && let Some(new_zoom_factor) = self.new_zoom_factor.take() {
439 let ratio = self.memory.options.zoom_factor / new_zoom_factor;
440 self.memory.options.zoom_factor = new_zoom_factor;
441
442 let input = &viewport.input;
443 let mut rect = input.content_rect();
445 rect.min = (ratio * rect.min.to_vec2()).to_pos2();
446 rect.max = (ratio * rect.max.to_vec2()).to_pos2();
447 new_raw_input.screen_rect = Some(rect);
448 }
451 let native_pixels_per_point = new_raw_input
452 .viewport()
453 .native_pixels_per_point
454 .unwrap_or(1.0);
455 let pixels_per_point = self.memory.options.zoom_factor * native_pixels_per_point;
456
457 let all_viewport_ids: ViewportIdSet = self.all_viewport_ids();
458
459 let viewport = self.viewports.entry(self.viewport_id()).or_default();
460
461 self.memory.begin_pass(&new_raw_input, &all_viewport_ids);
462
463 viewport.input = core::mem::take(&mut viewport.input).begin_pass(
464 new_raw_input,
465 viewport.repaint.requested_immediate_repaint_prev_pass(),
466 pixels_per_point,
467 self.memory.options.input_options,
468 );
469 let repaint_after = viewport.input.wants_repaint_after();
470
471 let content_rect = viewport.input.content_rect();
472
473 viewport.this_pass.begin_pass();
474
475 {
476 let mut layers: Vec<LayerId> = viewport
478 .prev_pass
479 .widgets
480 .layer_ids()
481 .filter(|layer_id| self.memory.areas().is_interactable(*layer_id))
482 .collect();
483 layers.sort_by(|&a, &b| self.memory.areas().compare_order(a, b));
484
485 viewport.hits = if let Some(pos) = viewport.input.pointer.interact_pos() {
486 let interact_radius = self.memory.options.style().interaction.interact_radius;
487
488 crate::hit_test::hit_test(
489 &viewport.prev_pass.widgets,
490 &layers,
491 &self.memory.to_global,
492 pos,
493 interact_radius,
494 )
495 } else {
496 WidgetHits::default()
497 };
498
499 viewport.interact_widgets = crate::interaction::interact(
500 &viewport.interact_widgets,
501 &viewport.prev_pass.widgets,
502 &viewport.hits,
503 &viewport.input,
504 self.memory.interaction_mut(),
505 );
506 }
507
508 self.memory.areas_mut().set_state(
510 LayerId::background(),
511 AreaState {
512 pivot_pos: Some(content_rect.left_top()),
513 pivot: Align2::LEFT_TOP,
514 size: Some(content_rect.size()),
515 interactable: true,
516 last_became_visible_at: None,
517 },
518 );
519
520 if self.is_accesskit_enabled {
521 profiling::scope!("accesskit");
522 use crate::pass_state::AccessKitPassState;
523 let id = crate::accesskit_root_id();
524 let mut root_node = accesskit::Node::new(accesskit::Role::Window);
525 let pixels_per_point = viewport.input.pixels_per_point();
526 root_node.set_transform(accesskit::Affine::scale(pixels_per_point.into()));
527 let mut nodes = IdMap::default();
528 nodes.insert(id, root_node);
529 viewport.this_pass.accesskit_state = Some(AccessKitPassState {
530 nodes,
531 parent_map: IdMap::default(),
532 });
533 }
534
535 self.update_fonts_mut();
536
537 if let Some(delay) = repaint_after {
538 self.request_repaint_after(delay, viewport_id, RepaintCause::new());
539 }
540 }
541
542 fn update_fonts_mut(&mut self) {
544 profiling::function_scope!();
545 let input = &self.viewport().input;
546 let max_texture_side = input.max_texture_side;
547
548 if let Some(font_definitions) = self.memory.new_font_definitions.take() {
549 self.fonts = None;
551 self.font_definitions = font_definitions;
552
553 log::trace!("Loading new font definitions");
554 }
555
556 if !self.memory.add_fonts.is_empty() {
557 let fonts = self.memory.add_fonts.drain(..);
558 for font in fonts {
559 self.fonts = None; for family in font.families {
561 let fam = self
562 .font_definitions
563 .families
564 .entry(family.family)
565 .or_default();
566 match family.priority {
567 FontPriority::Highest => fam.insert(0, font.name.clone()),
568 FontPriority::Lowest => fam.push(font.name.clone()),
569 }
570 }
571 self.font_definitions
572 .font_data
573 .insert(font.name, Arc::new(font.data));
574 }
575
576 log::trace!("Adding new fonts");
577 }
578
579 let Visuals {
580 mut text_options, ..
581 } = self.memory.options.style().visuals;
582 text_options.max_texture_side = max_texture_side;
583
584 let mut is_new = false;
585
586 let fonts = self.fonts.get_or_insert_with(|| {
587 log::trace!("Creating new Fonts");
588
589 is_new = true;
590 profiling::scope!("Fonts::new");
591 Fonts::new(text_options, self.font_definitions.clone())
592 });
593
594 {
595 profiling::scope!("Fonts::begin_pass");
596 fonts.begin_pass(text_options);
597 }
598 }
599
600 fn accesskit_node_builder(&mut self, id: Id) -> Option<&mut accesskit::Node> {
601 let state = self.viewport().this_pass.accesskit_state.as_mut()?;
602 let builders = &mut state.nodes;
603
604 if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) {
605 entry.insert(Default::default());
606
607 fn find_accesskit_parent(
609 parent_map: &IdMap<Id>,
610 node_map: &IdMap<accesskit::Node>,
611 id: Id,
612 ) -> Option<Id> {
613 if let Some(parent_id) = parent_map.get(&id) {
614 if node_map.contains_key(parent_id) {
615 Some(*parent_id)
616 } else {
617 find_accesskit_parent(parent_map, node_map, *parent_id)
618 }
619 } else {
620 None
621 }
622 }
623
624 let parent_id = find_accesskit_parent(&state.parent_map, builders, id)
625 .unwrap_or_else(crate::accesskit_root_id);
626
627 let parent_builder = builders.get_mut(&parent_id)?;
628 parent_builder.push_child(id.accesskit_id());
629 }
630
631 builders.get_mut(&id)
632 }
633
634 fn pixels_per_point(&mut self) -> f32 {
635 self.viewport().input.pixels_per_point
636 }
637
638 pub(crate) fn viewport_id(&self) -> ViewportId {
642 self.viewport_stack.last().copied().unwrap_or_default().this
643 }
644
645 pub(crate) fn parent_viewport_id(&self) -> ViewportId {
649 let viewport_id = self.viewport_id();
650 *self
651 .viewport_parents
652 .get(&viewport_id)
653 .unwrap_or(&ViewportId::ROOT)
654 }
655
656 fn all_viewport_ids(&self) -> ViewportIdSet {
657 core::iter::chain(self.viewports.keys().copied(), [ViewportId::ROOT]).collect()
658 }
659
660 pub(crate) fn viewport(&mut self) -> &mut ViewportState {
662 self.viewports.entry(self.viewport_id()).or_default()
663 }
664
665 fn viewport_for(&mut self, viewport_id: ViewportId) -> &mut ViewportState {
666 self.viewports.entry(viewport_id).or_default()
667 }
668}
669
670#[derive(Clone)]
723pub struct Context(Arc<RwLock<ContextImpl>>);
724
725impl core::fmt::Debug for Context {
726 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
727 f.debug_struct("Context").finish_non_exhaustive()
728 }
729}
730
731impl core::cmp::PartialEq for Context {
732 fn eq(&self, other: &Self) -> bool {
733 Arc::ptr_eq(&self.0, &other.0)
734 }
735}
736
737impl Default for Context {
738 fn default() -> Self {
739 let ctx_impl = ContextImpl {
740 embed_viewports: true,
741 viewports: core::iter::once((ViewportId::ROOT, ViewportState::default())).collect(),
742 ..Default::default()
743 };
744 let ctx = Self(Arc::new(RwLock::new(ctx_impl)));
745
746 ctx.add_plugin(plugin::CallbackPlugin::default());
747
748 ctx.add_plugin(crate::debug_text::DebugTextPlugin::default());
750 ctx.add_plugin(crate::text_selection::LabelSelectionState::default());
751 ctx.add_plugin(crate::DragAndDrop::default());
752
753 ctx
754 }
755}
756
757impl Context {
758 fn read<R>(&self, reader: impl FnOnce(&ContextImpl) -> R) -> R {
760 reader(&self.0.read())
761 }
762
763 fn write<R>(&self, writer: impl FnOnce(&mut ContextImpl) -> R) -> R {
765 writer(&mut self.0.write())
766 }
767
768 #[must_use]
794 pub fn run_ui(&self, new_input: RawInput, mut run_ui: impl FnMut(&mut Ui)) -> FullOutput {
795 self.run_ui_dyn(new_input, &mut run_ui)
796 }
797
798 #[must_use]
799 fn run_ui_dyn(&self, new_input: RawInput, run_ui: &mut dyn FnMut(&mut Ui)) -> FullOutput {
800 let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
801 self.run_dyn(new_input, &mut |ctx| {
802 let mut root_ui = Ui::new(
803 ctx.clone(),
804 Id::new((ctx.viewport_id(), "__top_ui")),
805 UiBuilder::new()
806 .layer_id(LayerId::background())
807 .max_rect(ctx.viewport_rect()),
808 );
809
810 {
811 plugins.on_begin_pass(&mut root_ui);
812 run_ui(&mut root_ui);
813 plugins.on_end_pass(&mut root_ui);
814 }
815
816 ctx.pass_state_mut(|state| {
817 state.root_ui_available_rect = Some(root_ui.available_rect_before_wrap());
818 state.root_ui_min_rect = Some(root_ui.min_rect());
819 });
820 })
821 }
822
823 #[must_use]
824 fn run_dyn(&self, mut new_input: RawInput, run_ui: &mut dyn FnMut(&Self)) -> FullOutput {
825 profiling::function_scope!();
826 let viewport_id = new_input.viewport_id;
827 let max_passes = self.write(|ctx| ctx.memory.options.max_passes.get());
828
829 let mut output = FullOutput::default();
830 debug_assert_eq!(
831 output.platform_output.num_completed_passes, 0,
832 "output must be fresh, but had {} passes",
833 output.platform_output.num_completed_passes
834 );
835
836 loop {
837 profiling::scope!(
838 "pass",
839 output
840 .platform_output
841 .num_completed_passes
842 .to_string()
843 .as_str()
844 );
845
846 self.write(|ctx| {
849 let viewport = ctx.viewport_for(viewport_id);
850 viewport.output.num_completed_passes =
851 core::mem::take(&mut output.platform_output.num_completed_passes);
852 output.platform_output.request_discard_reasons.clear();
853 });
854
855 self.begin_pass(new_input.take());
856 run_ui(self);
857 output.append(self.end_pass());
858 debug_assert!(
859 0 < output.platform_output.num_completed_passes,
860 "Completed passes was lower than 0, was {}",
861 output.platform_output.num_completed_passes
862 );
863
864 if !output.platform_output.requested_discard() {
865 break; }
867
868 if max_passes <= output.platform_output.num_completed_passes {
869 log::debug!(
870 "Ignoring call request_discard, because max_passes={max_passes}. Requested from {:?}",
871 output.platform_output.request_discard_reasons
872 );
873
874 break;
875 }
876 }
877
878 self.write(|ctx| {
879 let did_multipass = 1 < output.platform_output.num_completed_passes;
880 let viewport = ctx.viewport_for(viewport_id);
881 if did_multipass {
882 viewport.num_multipass_in_row += 1;
883 } else {
884 viewport.num_multipass_in_row = 0;
885 }
886 viewport.repaint.cumulative_frame_nr += 1;
887 });
888
889 output
890 }
891
892 #[must_use]
913 pub fn run_logic(&self, new_input: &RawInput, logic: impl FnOnce(&Self)) -> LogicOutput {
914 profiling::function_scope!();
915
916 let viewport_id = new_input.viewport_id;
917
918 self.write(|ctx| {
919 ctx.begin_pass_repaint_logic(viewport_id);
922
923 let raw = &mut ctx.viewport_for(viewport_id).input.raw;
925 raw.viewport_id = viewport_id;
926 raw.viewports = new_input.viewports.clone();
927 raw.focused = new_input.focused;
928 });
929
930 logic(self);
931
932 self.write(|ctx| LogicOutput {
933 platform_output: core::mem::take(&mut ctx.viewport_for(viewport_id).output),
934 viewport_commands: ctx
935 .viewports
936 .iter_mut()
937 .filter(|(_, viewport)| !viewport.commands.is_empty())
938 .map(|(&id, viewport)| (id, core::mem::take(&mut viewport.commands)))
939 .collect(),
940 })
941 }
942
943 pub fn begin_pass(&self, mut new_input: RawInput) {
963 profiling::function_scope!();
964
965 let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
966 plugins.on_input(self, &mut new_input);
967
968 self.write(|ctx| ctx.begin_pass(new_input));
969 }
970}
971
972impl Context {
976 #[inline]
991 pub fn input<R>(&self, reader: impl FnOnce(&InputState) -> R) -> R {
992 self.write(move |ctx| reader(&ctx.viewport().input))
993 }
994
995 #[inline]
997 pub fn input_for<R>(&self, id: ViewportId, reader: impl FnOnce(&InputState) -> R) -> R {
998 self.write(move |ctx| reader(&ctx.viewport_for(id).input))
999 }
1000
1001 #[inline]
1003 pub fn input_mut<R>(&self, writer: impl FnOnce(&mut InputState) -> R) -> R {
1004 self.input_mut_for(self.viewport_id(), writer)
1005 }
1006
1007 #[inline]
1009 pub fn input_mut_for<R>(&self, id: ViewportId, writer: impl FnOnce(&mut InputState) -> R) -> R {
1010 self.write(move |ctx| writer(&mut ctx.viewport_for(id).input))
1011 }
1012
1013 #[inline]
1015 pub fn memory<R>(&self, reader: impl FnOnce(&Memory) -> R) -> R {
1016 self.read(move |ctx| reader(&ctx.memory))
1017 }
1018
1019 #[inline]
1021 pub fn memory_mut<R>(&self, writer: impl FnOnce(&mut Memory) -> R) -> R {
1022 self.write(move |ctx| writer(&mut ctx.memory))
1023 }
1024
1025 #[inline]
1027 pub fn data<R>(&self, reader: impl FnOnce(&IdTypeMap) -> R) -> R {
1028 self.read(move |ctx| reader(&ctx.memory.data))
1029 }
1030
1031 #[inline]
1033 pub fn data_mut<R>(&self, writer: impl FnOnce(&mut IdTypeMap) -> R) -> R {
1034 self.write(move |ctx| writer(&mut ctx.memory.data))
1035 }
1036
1037 #[inline]
1039 pub fn graphics_mut<R>(&self, writer: impl FnOnce(&mut GraphicLayers) -> R) -> R {
1040 self.write(move |ctx| writer(&mut ctx.viewport().graphics))
1041 }
1042
1043 #[inline]
1045 pub fn graphics<R>(&self, reader: impl FnOnce(&GraphicLayers) -> R) -> R {
1046 self.write(move |ctx| reader(&ctx.viewport().graphics))
1047 }
1048
1049 #[inline]
1058 pub fn output<R>(&self, reader: impl FnOnce(&PlatformOutput) -> R) -> R {
1059 self.write(move |ctx| reader(&ctx.viewport().output))
1060 }
1061
1062 #[inline]
1064 pub fn output_mut<R>(&self, writer: impl FnOnce(&mut PlatformOutput) -> R) -> R {
1065 self.write(move |ctx| writer(&mut ctx.viewport().output))
1066 }
1067
1068 #[inline]
1072 pub(crate) fn pass_state<R>(&self, reader: impl FnOnce(&PassState) -> R) -> R {
1073 self.write(move |ctx| reader(&ctx.viewport().this_pass))
1074 }
1075
1076 #[inline]
1080 pub(crate) fn pass_state_mut<R>(&self, writer: impl FnOnce(&mut PassState) -> R) -> R {
1081 self.write(move |ctx| writer(&mut ctx.viewport().this_pass))
1082 }
1083
1084 #[inline]
1088 pub(crate) fn prev_pass_state<R>(&self, reader: impl FnOnce(&PassState) -> R) -> R {
1089 self.write(move |ctx| reader(&ctx.viewport().prev_pass))
1090 }
1091
1092 #[inline]
1097 pub fn fonts<R>(&self, reader: impl FnOnce(&FontsView<'_>) -> R) -> R {
1098 self.write(move |ctx| {
1099 let pixels_per_point = ctx.pixels_per_point();
1100 reader(
1101 &ctx.fonts
1102 .as_mut()
1103 .expect("No fonts available until first call to Context::run()")
1104 .with_pixels_per_point(pixels_per_point),
1105 )
1106 })
1107 }
1108
1109 #[inline]
1114 pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut FontsView<'_>) -> R) -> R {
1115 self.write(move |ctx| {
1116 let pixels_per_point = ctx.pixels_per_point();
1117 reader(
1118 &mut ctx
1119 .fonts
1120 .as_mut()
1121 .expect("No fonts available until first call to Context::run()")
1122 .with_pixels_per_point(pixels_per_point),
1123 )
1124 })
1125 }
1126
1127 #[inline]
1129 pub fn options<R>(&self, reader: impl FnOnce(&Options) -> R) -> R {
1130 self.read(move |ctx| reader(&ctx.memory.options))
1131 }
1132
1133 #[inline]
1135 pub fn options_mut<R>(&self, writer: impl FnOnce(&mut Options) -> R) -> R {
1136 self.write(move |ctx| writer(&mut ctx.memory.options))
1137 }
1138
1139 #[inline]
1141 pub fn tessellation_options<R>(&self, reader: impl FnOnce(&TessellationOptions) -> R) -> R {
1142 self.read(move |ctx| reader(&ctx.memory.options.tessellation_options))
1143 }
1144
1145 #[inline]
1147 pub fn tessellation_options_mut<R>(
1148 &self,
1149 writer: impl FnOnce(&mut TessellationOptions) -> R,
1150 ) -> R {
1151 self.write(move |ctx| writer(&mut ctx.memory.options.tessellation_options))
1152 }
1153
1154 pub fn check_for_id_clash(&self, id: Id, new_rect: Rect, what: &str) {
1164 let prev_rect = self.pass_state_mut(move |state| state.used_ids.insert(id, new_rect));
1165
1166 if !self.options(|opt| opt.warn_on_id_clash) {
1167 return;
1168 }
1169
1170 let Some(prev_rect) = prev_rect else { return };
1171
1172 let is_same_rect = prev_rect.expand(0.1).contains_rect(new_rect)
1175 || new_rect.expand(0.1).contains_rect(prev_rect);
1176 if is_same_rect {
1177 return;
1178 }
1179
1180 let show_error = |widget_rect: Rect, text: String| {
1181 let content_rect = self.content_rect();
1182
1183 let text = format!("🔥 {text}");
1184 let color = self.global_style().visuals.error_fg_color;
1185 let painter = self.debug_painter();
1186 painter.rect_stroke(widget_rect, 0.0, (1.0, color), StrokeKind::Outside);
1187
1188 let below = widget_rect.bottom() + 32.0 < content_rect.bottom();
1189
1190 let text_rect = if below {
1191 painter.debug_text(
1192 widget_rect.left_bottom() + vec2(0.0, 2.0),
1193 Align2::LEFT_TOP,
1194 color,
1195 text,
1196 )
1197 } else {
1198 painter.debug_text(
1199 widget_rect.left_top() - vec2(0.0, 2.0),
1200 Align2::LEFT_BOTTOM,
1201 color,
1202 text,
1203 )
1204 };
1205
1206 if let Some(pointer_pos) = self.pointer_hover_pos()
1207 && text_rect.contains(pointer_pos)
1208 {
1209 let tooltip_pos = if below {
1210 text_rect.left_bottom() + vec2(2.0, 4.0)
1211 } else {
1212 text_rect.left_top() + vec2(2.0, -4.0)
1213 };
1214
1215 painter.error(
1216 tooltip_pos,
1217 format!("Widget is {} this text.\n\n\
1218 ID clashes happens when things like Windows or CollapsingHeaders share names,\n\
1219 or when things like Plot and Grid:s aren't given unique id_salt:s.\n\n\
1220 Sometimes the solution is to use ui.push_id.",
1221 if below { "above" } else { "below" }),
1222 );
1223 }
1224 };
1225
1226 let id_str = id.short_debug_format();
1227
1228 if prev_rect.min.distance(new_rect.min) < 4.0 {
1229 show_error(new_rect, format!("Double use of {what} ID {id_str}"));
1230 } else {
1231 show_error(prev_rect, format!("First use of {what} ID {id_str}"));
1232 show_error(new_rect, format!("Second use of {what} ID {id_str}"));
1233 }
1234 }
1235
1236 pub(crate) fn create_widget(
1249 &self,
1250 w: WidgetRect,
1251 allow_focus: bool,
1252 options: crate::InteractOptions,
1253 ) -> Response {
1254 debug_assert!(!w.rect.any_nan(), "widget rect is NaN: {:?}", w.rect);
1255
1256 let interested_in_focus = w.enabled
1257 && w.sense.is_focusable()
1258 && self.memory(|mem| mem.allows_interaction(w.layer_id));
1259
1260 self.write(|ctx| {
1262 let viewport = ctx.viewport();
1263
1264 viewport.this_pass.widgets.insert(w.layer_id, w, options);
1268
1269 if allow_focus && interested_in_focus {
1270 ctx.memory.interested_in_focus(w.id, w.layer_id);
1271 }
1272 });
1273
1274 if allow_focus && !interested_in_focus {
1275 self.memory_mut(|mem| mem.surrender_focus(w.id));
1277 }
1278
1279 if w.sense.interactive() || w.sense.is_focusable() {
1280 self.check_for_id_clash(w.id, w.rect, "widget");
1281 }
1282
1283 #[allow(clippy::allow_attributes, clippy::let_and_return)]
1284 let res = self.get_response(w);
1285
1286 #[cfg(debug_assertions)]
1287 if res.contains_pointer() {
1288 let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
1289 plugins.on_widget_under_pointer(self, &w);
1290 }
1291
1292 if allow_focus && w.sense.is_focusable() {
1293 self.accesskit_node_builder(w.id, |builder| res.fill_accesskit_node_common(builder));
1297 }
1298
1299 self.write(|ctx| {
1300 use crate::{Align, pass_state::ScrollTarget, style::ScrollAnimation};
1301 let viewport = ctx.viewport_for(ctx.viewport_id());
1302
1303 viewport
1304 .input
1305 .consume_accesskit_action_requests(res.id, |request| {
1306 use accesskit::Action;
1307
1308 const DISTANCE: f32 = 100.0;
1311
1312 match &request.action {
1313 Action::ScrollIntoView => {
1314 viewport.this_pass.scroll_target = [
1315 Some(ScrollTarget::new(
1316 res.rect.x_range(),
1317 Some(Align::Center),
1318 ScrollAnimation::none(),
1319 )),
1320 Some(ScrollTarget::new(
1321 res.rect.y_range(),
1322 Some(Align::Center),
1323 ScrollAnimation::none(),
1324 )),
1325 ];
1326 }
1327 Action::ScrollDown => {
1328 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::UP;
1329 }
1330 Action::ScrollUp => {
1331 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::DOWN;
1332 }
1333 Action::ScrollLeft => {
1334 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::LEFT;
1335 }
1336 Action::ScrollRight => {
1337 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::RIGHT;
1338 }
1339 _ => return false,
1340 }
1341 true
1342 });
1343 });
1344
1345 res
1346 }
1347
1348 pub fn read_response(&self, id: Id) -> Option<Response> {
1356 self.write(|ctx| {
1357 let viewport = ctx.viewport();
1358 let widget_rect = viewport
1359 .this_pass
1360 .widgets
1361 .get(id)
1362 .or_else(|| viewport.prev_pass.widgets.get(id))
1363 .copied();
1364 widget_rect.map(|mut rect| {
1365 if !(rect.rect.is_positive() && rect.rect.is_finite())
1368 && let Some(prev_rect) = viewport.prev_pass.widgets.get(id)
1369 {
1370 rect.rect = prev_rect.rect;
1371 }
1372 rect
1373 })
1374 })
1375 .map(|widget_rect| self.get_response(widget_rect))
1376 }
1377
1378 #[must_use]
1389 pub fn interactive_rects_last_pass(&self) -> Vec<Rect> {
1390 self.read(|ctx| {
1391 let Some(viewport) = ctx.viewports.get(&ctx.viewport_id()) else {
1392 return Vec::new();
1393 };
1394
1395 let mut layers: Vec<LayerId> = viewport.prev_pass.widgets.layer_ids().collect();
1396 layers.sort_by(|&a, &b| ctx.memory.areas().compare_order(a, b));
1397
1398 let mut rects = Vec::new();
1399 for layer_id in layers {
1400 if !ctx.memory.allows_interaction(layer_id) {
1401 continue;
1402 }
1403
1404 let to_global = ctx.memory.to_global.get(&layer_id).copied();
1405 for widget in viewport.prev_pass.widgets.get_layer(layer_id) {
1406 if !widget.enabled || !widget.sense.interactive() {
1407 continue;
1408 }
1409
1410 let rect = if let Some(to_global) = to_global {
1411 to_global * widget.interact_rect
1412 } else {
1413 widget.interact_rect
1414 };
1415 if rect.is_positive() && rect.is_finite() {
1416 rects.push(rect);
1417 }
1418 }
1419 }
1420 rects
1421 })
1422 }
1423
1424 pub(crate) fn get_response(&self, widget_rect: WidgetRect) -> Response {
1426 use response::Flags;
1427
1428 let WidgetRect {
1429 id,
1430 parent_id: _,
1431 layer_id,
1432 rect,
1433 interact_rect,
1434 sense,
1435 enabled,
1436 } = widget_rect;
1437
1438 let highlighted = self.prev_pass_state(|fs| fs.highlight_next_pass.contains(&id));
1440
1441 let mut res = Response {
1442 ctx: self.clone(),
1443 layer_id,
1444 id,
1445 rect,
1446 interact_rect,
1447 sense,
1448 flags: Flags::empty(),
1449 interact_pointer_pos_or_nan: Pos2::NAN,
1450 intrinsic_size_or_nan: Vec2::NAN,
1451 };
1452
1453 res.flags.set(Flags::ENABLED, enabled);
1454 res.flags.set(Flags::HIGHLIGHTED, highlighted);
1455
1456 self.write(|ctx| {
1457 let viewport = ctx.viewports.entry(ctx.viewport_id()).or_default();
1458
1459 res.flags.set(
1460 Flags::CONTAINS_POINTER,
1461 viewport.interact_widgets.contains_pointer.contains(&id),
1462 );
1463
1464 let input = &viewport.input;
1465 let memory = &mut ctx.memory;
1466
1467 if enabled
1468 && sense.senses_click()
1469 && memory.has_focus(id)
1470 && (input.key_pressed(Key::Space) || input.key_pressed(Key::Enter))
1471 {
1472 res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);
1474 }
1475
1476 if enabled
1477 && sense.senses_click()
1478 && input.has_accesskit_action_request(id, accesskit::Action::Click)
1479 {
1480 res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);
1481 }
1482
1483 if enabled && sense.senses_click() && Some(id) == viewport.interact_widgets.long_touched
1484 {
1485 res.flags.set(Flags::LONG_TOUCHED, true);
1486 }
1487
1488 let interaction = memory.interaction();
1489
1490 res.flags.set(
1491 Flags::IS_POINTER_BUTTON_DOWN_ON,
1492 interaction.potential_click_id == Some(id)
1493 || interaction.potential_drag_id == Some(id),
1494 );
1495
1496 if res.enabled() {
1497 res.flags.set(
1498 Flags::HOVERED,
1499 viewport.interact_widgets.hovered.contains(&id),
1500 );
1501 res.flags.set(
1502 Flags::DRAGGED,
1503 Some(id) == viewport.interact_widgets.dragged,
1504 );
1505 res.flags.set(
1506 Flags::DRAG_STARTED,
1507 Some(id) == viewport.interact_widgets.drag_started,
1508 );
1509 res.flags.set(
1510 Flags::DRAG_STOPPED,
1511 Some(id) == viewport.interact_widgets.drag_stopped,
1512 );
1513 }
1514
1515 let clicked = Some(id) == viewport.interact_widgets.clicked;
1516 let mut any_press = false;
1517
1518 for pointer_event in &input.pointer.pointer_events {
1519 match pointer_event {
1520 PointerEvent::Moved(_) => {}
1521 PointerEvent::Pressed { .. } => {
1522 any_press = true;
1523 }
1524 PointerEvent::Released { click, .. } => {
1525 if enabled && sense.senses_click() && clicked && click.is_some() {
1526 res.flags.set(Flags::CLICKED, true);
1527 }
1528
1529 res.flags.set(Flags::IS_POINTER_BUTTON_DOWN_ON, false);
1530 res.flags.set(Flags::DRAGGED, false);
1531 }
1532 }
1533 }
1534
1535 let is_interacted_with = res.is_pointer_button_down_on()
1538 || res.long_touched()
1539 || clicked
1540 || res.drag_stopped();
1541 if is_interacted_with && let Some(mut pos) = input.pointer.interact_pos() {
1542 if let Some(to_global) = memory.to_global.get(&res.layer_id) {
1543 pos = to_global.inverse() * pos;
1544 }
1545 res.interact_pointer_pos_or_nan = pos;
1546 }
1547
1548 if input.pointer.any_down() && !is_interacted_with {
1549 res.flags.set(Flags::HOVERED, false);
1551 }
1552
1553 let should_surrender_focus = match memory.options.input_options.surrender_focus_on {
1554 SurrenderFocusOn::Presses => any_press,
1555 SurrenderFocusOn::Clicks => input.pointer.any_click(),
1556 SurrenderFocusOn::Never => false,
1557 };
1558
1559 let pointer_clicked_elsewhere = should_surrender_focus && !res.hovered();
1560 if pointer_clicked_elsewhere && memory.has_focus(id) {
1561 memory.surrender_focus(id);
1562 }
1563 });
1564
1565 res
1566 }
1567
1568 #[inline]
1572 pub fn register_widget_info(&self, id: Id, make_info: impl Fn() -> crate::WidgetInfo) {
1573 #[cfg(debug_assertions)]
1574 self.write(|ctx| {
1575 if ctx.memory.options.style().debug.show_interactive_widgets {
1576 ctx.viewport().this_pass.widgets.set_info(id, make_info());
1577 }
1578 });
1579
1580 #[cfg(not(debug_assertions))]
1581 {
1582 _ = (self, id, make_info);
1583 }
1584 }
1585
1586 pub fn layer_painter(&self, layer_id: LayerId) -> Painter {
1588 let content_rect = self.content_rect();
1589 Painter::new(self.clone(), layer_id, content_rect)
1590 }
1591
1592 pub fn debug_painter(&self) -> Painter {
1594 Self::layer_painter(self, LayerId::debug())
1595 }
1596
1597 #[track_caller]
1611 pub fn debug_text(&self, text: impl Into<WidgetText>) {
1612 crate::debug_text::print(self, text);
1613 }
1614
1615 pub fn time(&self) -> f64 {
1617 self.input(|i| i.time)
1618 }
1619
1620 pub fn os(&self) -> OperatingSystem {
1628 self.read(|ctx| ctx.os)
1629 }
1630
1631 pub fn set_os(&self, os: OperatingSystem) {
1636 self.write(|ctx| ctx.os = os);
1637 }
1638
1639 pub fn set_cursor_icon(&self, cursor_icon: CursorIcon) {
1647 self.output_mut(|o| o.cursor_icon = cursor_icon);
1648 }
1649
1650 pub fn set_cursor_image(&self, image: Option<crate::CustomCursorImage>) {
1660 self.output_mut(|o| o.cursor_image = image);
1661 }
1662
1663 pub fn send_cmd(&self, cmd: crate::OutputCommand) {
1666 self.output_mut(|o| o.commands.push(cmd));
1667 }
1668
1669 pub fn open_url(&self, open_url: crate::OpenUrl) {
1678 self.send_cmd(crate::OutputCommand::OpenUrl(open_url));
1679 }
1680
1681 pub fn copy_text(&self, text: String) {
1687 self.send_cmd(crate::OutputCommand::CopyText(text));
1688 }
1689
1690 pub fn copy_image(&self, image: crate::ColorImage) {
1696 self.send_cmd(crate::OutputCommand::CopyImage(image));
1697 }
1698
1699 fn can_show_modifier_symbols(&self) -> bool {
1700 let ModifierNames {
1701 alt,
1702 ctrl,
1703 shift,
1704 mac_cmd,
1705 ..
1706 } = ModifierNames::SYMBOLS;
1707
1708 let font_id = TextStyle::Body.resolve(&self.global_style());
1709 self.fonts_mut(|f| {
1710 let mut font = f.fonts.font(&font_id.family);
1711 font.has_glyphs(alt)
1712 && font.has_glyphs(ctrl)
1713 && font.has_glyphs(shift)
1714 && font.has_glyphs(mac_cmd)
1715 })
1716 }
1717
1718 pub fn format_modifiers(&self, modifiers: Modifiers) -> String {
1720 let os = self.os();
1721
1722 let is_mac = os.is_mac();
1723
1724 if is_mac && self.can_show_modifier_symbols() {
1725 ModifierNames::SYMBOLS.format(&modifiers, is_mac)
1726 } else {
1727 ModifierNames::NAMES.format(&modifiers, is_mac)
1728 }
1729 }
1730
1731 pub fn format_shortcut(&self, shortcut: &KeyboardShortcut) -> String {
1735 let os = self.os();
1736
1737 let is_mac = os.is_mac();
1738
1739 if is_mac && self.can_show_modifier_symbols() {
1740 shortcut.format(&ModifierNames::SYMBOLS, is_mac)
1741 } else {
1742 shortcut.format(&ModifierNames::NAMES, is_mac)
1743 }
1744 }
1745
1746 pub fn cumulative_frame_nr(&self) -> u64 {
1752 self.cumulative_frame_nr_for(self.viewport_id())
1753 }
1754
1755 pub fn cumulative_frame_nr_for(&self, id: ViewportId) -> u64 {
1761 self.read(|ctx| {
1762 ctx.viewports
1763 .get(&id)
1764 .map(|v| v.repaint.cumulative_frame_nr)
1765 .unwrap_or_else(|| {
1766 if cfg!(debug_assertions) {
1767 panic!("cumulative_frame_nr_for failed to find the viewport {id:?}");
1768 } else {
1769 0
1770 }
1771 })
1772 })
1773 }
1774
1775 pub fn cumulative_pass_nr(&self) -> u64 {
1782 self.cumulative_pass_nr_for(self.viewport_id())
1783 }
1784
1785 pub fn cumulative_pass_nr_for(&self, id: ViewportId) -> u64 {
1789 self.read(|ctx| {
1790 ctx.viewports
1791 .get(&id)
1792 .map_or(0, |v| v.repaint.cumulative_pass_nr)
1793 })
1794 }
1795
1796 pub fn current_pass_index(&self) -> usize {
1805 self.output(|o| o.num_completed_passes)
1806 }
1807
1808 #[track_caller]
1821 pub fn request_repaint(&self) {
1822 self.request_repaint_of(self.viewport_id());
1823 }
1824
1825 #[track_caller]
1838 pub fn request_repaint_of(&self, id: ViewportId) {
1839 let cause = RepaintCause::new();
1840 self.write(|ctx| ctx.request_repaint(id, cause));
1841 }
1842
1843 #[track_caller]
1872 pub fn request_repaint_after(&self, duration: Duration) {
1873 self.request_repaint_after_for(duration, self.viewport_id());
1874 }
1875
1876 #[track_caller]
1880 pub fn request_repaint_after_secs(&self, seconds: f32) {
1881 if let Ok(duration) = core::time::Duration::try_from_secs_f32(seconds) {
1882 self.request_repaint_after(duration);
1883 }
1884 }
1885
1886 #[track_caller]
1915 pub fn request_repaint_after_for(&self, duration: Duration, id: ViewportId) {
1916 let cause = RepaintCause::new();
1917 self.write(|ctx| ctx.request_repaint_after(duration, id, cause));
1918 }
1919
1920 #[must_use]
1922 pub fn requested_repaint_last_pass(&self) -> bool {
1923 self.requested_repaint_last_pass_for(&self.viewport_id())
1924 }
1925
1926 #[must_use]
1928 pub fn requested_repaint_last_pass_for(&self, viewport_id: &ViewportId) -> bool {
1929 self.read(|ctx| ctx.requested_immediate_repaint_prev_pass(viewport_id))
1930 }
1931
1932 #[must_use]
1934 pub fn has_requested_repaint(&self) -> bool {
1935 self.has_requested_repaint_for(&self.viewport_id())
1936 }
1937
1938 #[must_use]
1940 pub fn has_requested_repaint_for(&self, viewport_id: &ViewportId) -> bool {
1941 self.read(|ctx| ctx.has_requested_repaint(viewport_id))
1942 }
1943
1944 pub fn repaint_causes(&self) -> Vec<RepaintCause> {
1948 self.read(|ctx| {
1949 ctx.viewports
1950 .get(&ctx.viewport_id())
1951 .map(|v| v.repaint.prev_causes.clone())
1952 })
1953 .unwrap_or_default()
1954 }
1955
1956 pub fn set_request_repaint_callback(
1962 &self,
1963 callback: impl Fn(RequestRepaintInfo) + Send + Sync + 'static,
1964 ) {
1965 let callback = Box::new(callback);
1966 self.write(|ctx| ctx.request_repaint_callback = Some(callback));
1967 }
1968
1969 #[track_caller]
1992 pub fn request_discard(&self, reason: impl Into<Cow<'static, str>>) {
1993 let cause = RepaintCause::new_reason(reason);
1994 self.output_mut(|o| o.request_discard_reasons.push(cause));
1995
1996 log::trace!(
1997 "request_discard: {}",
1998 if self.will_discard() {
1999 "allowed"
2000 } else {
2001 "denied"
2002 }
2003 );
2004 }
2005
2006 pub fn will_discard(&self) -> bool {
2012 self.write(|ctx| {
2013 let vp = ctx.viewport();
2014 vp.output.requested_discard()
2016 && vp.output.num_completed_passes + 1 < ctx.memory.options.max_passes.get()
2017 })
2018 }
2019}
2020
2021impl Context {
2023 pub fn on_begin_pass(&self, debug_name: &'static str, cb: plugin::ContextCallback) {
2027 self.with_plugin(|p: &mut crate::plugin::CallbackPlugin| {
2028 p.on_begin_plugins.push((debug_name, cb));
2029 });
2030 }
2031
2032 pub fn on_end_pass(&self, debug_name: &'static str, cb: plugin::ContextCallback) {
2036 self.with_plugin(|p: &mut crate::plugin::CallbackPlugin| {
2037 p.on_end_plugins.push((debug_name, cb));
2038 });
2039 }
2040
2041 pub fn add_plugin(&self, plugin: impl plugin::Plugin + 'static) {
2048 let handle = plugin::PluginHandle::new(plugin);
2049
2050 let added = self.write(|ctx| ctx.plugins.add(Arc::clone(&handle)));
2051
2052 if added {
2053 handle.lock().dyn_plugin_mut().setup(self);
2054 }
2055 }
2056
2057 pub fn with_plugin<T: plugin::Plugin + 'static, R>(
2061 &self,
2062 f: impl FnOnce(&mut T) -> R,
2063 ) -> Option<R> {
2064 let plugin = self.read(|ctx| ctx.plugins.get(core::any::TypeId::of::<T>()));
2065 plugin.map(|plugin| f(plugin.lock().typed_plugin_mut()))
2066 }
2067
2068 pub fn plugin<T: plugin::Plugin>(&self) -> TypedPluginHandle<T> {
2073 if let Some(plugin) = self.plugin_opt() {
2074 plugin
2075 } else {
2076 panic!("Plugin of type {:?} not found", core::any::type_name::<T>());
2077 }
2078 }
2079
2080 pub fn plugin_opt<T: plugin::Plugin>(&self) -> Option<TypedPluginHandle<T>> {
2082 let plugin = self.read(|ctx| ctx.plugins.get(core::any::TypeId::of::<T>()));
2083 plugin.map(TypedPluginHandle::new)
2084 }
2085
2086 pub fn plugin_or_default<T: plugin::Plugin + Default>(&self) -> TypedPluginHandle<T> {
2088 if let Some(plugin) = self.plugin_opt() {
2089 plugin
2090 } else {
2091 let default_plugin = T::default();
2092 self.add_plugin(default_plugin);
2093 self.plugin()
2094 }
2095 }
2096}
2097
2098impl Context {
2099 pub fn set_fonts(&self, font_definitions: FontDefinitions) {
2107 profiling::function_scope!();
2108
2109 let update_fonts = self.read(|ctx| {
2110 ctx.fonts
2113 .as_ref()
2114 .is_none_or(|fonts| fonts.definitions() != &font_definitions)
2115 });
2116
2117 if update_fonts {
2118 self.memory_mut(|mem| mem.new_font_definitions = Some(font_definitions));
2119 }
2120 }
2121
2122 pub fn add_font(&self, new_font: FontInsert) {
2130 profiling::function_scope!();
2131
2132 let mut update_fonts = true;
2133
2134 self.read(|ctx| {
2135 if let Some(current_fonts) = ctx.fonts.as_ref()
2136 && current_fonts
2137 .definitions()
2138 .font_data
2139 .contains_key(&new_font.name)
2140 {
2141 update_fonts = false; }
2143 });
2144
2145 if update_fonts {
2146 self.memory_mut(|mem| mem.add_fonts.push(new_font));
2147 }
2148 }
2149
2150 pub fn system_theme(&self) -> Option<Theme> {
2153 self.memory(|mem| mem.options.system_theme)
2154 }
2155
2156 pub fn theme(&self) -> Theme {
2159 self.options(|opt| opt.theme())
2160 }
2161
2162 pub fn set_theme(&self, theme_preference: impl Into<crate::ThemePreference>) {
2171 self.options_mut(|opt| opt.theme_preference = theme_preference.into());
2172 }
2173
2174 pub fn global_style(&self) -> Arc<Style> {
2176 self.options(|opt| Arc::clone(opt.style()))
2177 }
2178
2179 pub fn global_style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {
2190 self.options_mut(|opt| mutate_style(Arc::make_mut(opt.style_mut())));
2191 }
2192
2193 pub fn set_global_style(&self, style: impl Into<Arc<Style>>) {
2201 self.options_mut(|opt| *opt.style_mut() = style.into());
2202 }
2203
2204 pub fn all_styles_mut(&self, mut mutate_style: impl FnMut(&mut Style)) {
2214 self.options_mut(|opt| {
2215 mutate_style(Arc::make_mut(&mut opt.dark_style));
2216 mutate_style(Arc::make_mut(&mut opt.light_style));
2217 });
2218 }
2219
2220 pub fn style_of(&self, theme: Theme) -> Arc<Style> {
2222 self.options(|opt| match theme {
2223 Theme::Dark => Arc::clone(&opt.dark_style),
2224 Theme::Light => Arc::clone(&opt.light_style),
2225 })
2226 }
2227
2228 pub fn style_mut_of(&self, theme: Theme, mutate_style: impl FnOnce(&mut Style)) {
2238 self.options_mut(|opt| match theme {
2239 Theme::Dark => mutate_style(Arc::make_mut(&mut opt.dark_style)),
2240 Theme::Light => mutate_style(Arc::make_mut(&mut opt.light_style)),
2241 });
2242 }
2243
2244 pub fn set_style_of(&self, theme: Theme, style: impl Into<Arc<Style>>) {
2251 let style = style.into();
2252 self.options_mut(|opt| match theme {
2253 Theme::Dark => opt.dark_style = style,
2254 Theme::Light => opt.light_style = style,
2255 });
2256 }
2257
2258 pub fn set_visuals_of(&self, theme: Theme, visuals: crate::Visuals) {
2268 self.style_mut_of(theme, |style| style.visuals = visuals);
2269 }
2270
2271 pub fn set_visuals(&self, visuals: crate::Visuals) {
2281 self.style_mut_of(self.theme(), |style| style.visuals = visuals);
2282 }
2283
2284 #[inline(always)]
2288 pub fn pixels_per_point(&self) -> f32 {
2289 self.input(|i| i.pixels_per_point)
2290 }
2291
2292 pub fn set_pixels_per_point(&self, pixels_per_point: f32) {
2297 if pixels_per_point != self.pixels_per_point() {
2298 self.set_zoom_factor(pixels_per_point / self.native_pixels_per_point().unwrap_or(1.0));
2299 }
2300 }
2301
2302 #[inline(always)]
2307 pub fn native_pixels_per_point(&self) -> Option<f32> {
2308 self.input(|i| i.viewport().native_pixels_per_point)
2309 }
2310
2311 #[inline(always)]
2319 pub fn zoom_factor(&self) -> f32 {
2320 self.options(|o| o.zoom_factor)
2321 }
2322
2323 #[inline(always)]
2337 pub fn set_zoom_factor(&self, zoom_factor: f32) {
2338 let cause = RepaintCause::new();
2339 self.write(|ctx| {
2340 if ctx.memory.options.zoom_factor != zoom_factor {
2341 ctx.new_zoom_factor = Some(zoom_factor);
2342 #[expect(clippy::iter_over_hash_type)]
2343 for viewport_id in ctx.all_viewport_ids() {
2344 ctx.request_repaint(viewport_id, cause.clone());
2345 }
2346 }
2347 });
2348 }
2349
2350 pub fn load_texture(
2391 &self,
2392 name: impl Into<String>,
2393 image: impl Into<ImageData>,
2394 options: TextureOptions,
2395 ) -> TextureHandle {
2396 let name = name.into();
2397 let image = image.into();
2398 let max_texture_side = self.input(|i| i.max_texture_side);
2399 debug_assert!(
2400 image.width() <= max_texture_side && image.height() <= max_texture_side,
2401 "Texture {:?} has size {}x{}, but the maximum texture side is {}",
2402 name,
2403 image.width(),
2404 image.height(),
2405 max_texture_side
2406 );
2407 let tex_mngr = self.tex_manager();
2408 let tex_id = tex_mngr.write().alloc(name, image, options);
2409 TextureHandle::new(tex_mngr, tex_id)
2410 }
2411
2412 pub fn tex_manager(&self) -> Arc<RwLock<epaint::textures::TextureManager>> {
2418 self.read(|ctx| Arc::clone(&ctx.tex_manager.0))
2419 }
2420
2421 pub(crate) fn constrain_window_rect_to_area(window: Rect, area: Rect) -> Rect {
2425 let mut pos = window.min;
2426
2427 let margin_x = (window.width() - area.width()).at_least(0.0);
2429 let margin_y = (window.height() - area.height()).at_least(0.0);
2430
2431 pos.x = pos.x.at_most(area.right() + margin_x - window.width()); pos.x = pos.x.at_least(area.left() - margin_x); pos.y = pos.y.at_most(area.bottom() + margin_y - window.height()); pos.y = pos.y.at_least(area.top() - margin_y); Rect::from_min_size(pos, window.size()).round_ui()
2437 }
2438}
2439
2440impl Context {
2441 #[must_use]
2443 pub fn end_pass(&self) -> FullOutput {
2444 profiling::function_scope!();
2445
2446 if self.options(|o| o.zoom_with_keyboard) {
2447 crate::gui_zoom::zoom_with_keyboard(self);
2448 }
2449
2450 for shortcut in self.options(|o| o.quit_shortcuts.clone()) {
2451 if self.input_mut(|i| i.consume_shortcut(&shortcut)) {
2452 self.send_viewport_cmd(ViewportCommand::Close);
2453 }
2454 }
2455
2456 self.sync_window_theme();
2457
2458 #[cfg(debug_assertions)]
2459 self.debug_painting();
2460
2461 let mut output = self.write(|ctx| ctx.end_pass());
2462
2463 let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
2464 plugins.on_output(self, &mut output);
2465
2466 output
2467 }
2468
2469 fn sync_window_theme(&self) {
2475 if !self.options(|o| o.sync_window_theme) {
2476 return;
2477 }
2478
2479 use crate::{SystemTheme, ThemePreference};
2480 let window_theme = match self.options(|o| o.theme_preference) {
2481 ThemePreference::System => SystemTheme::SystemDefault,
2482 ThemePreference::Dark => SystemTheme::Dark,
2483 ThemePreference::Light => SystemTheme::Light,
2484 };
2485
2486 let changed = self.write(|ctx| {
2487 let viewport = ctx.viewport();
2488 if viewport.last_sent_window_theme == Some(window_theme) {
2489 false
2490 } else {
2491 viewport.last_sent_window_theme = Some(window_theme);
2492 true
2493 }
2494 });
2495
2496 if changed {
2497 self.send_viewport_cmd(ViewportCommand::SetTheme(window_theme));
2498 }
2499 }
2500
2501 #[cfg(debug_assertions)]
2503 fn debug_painting(&self) {
2504 #![expect(clippy::iter_over_hash_type)] use core::fmt::Write as _;
2506
2507 let paint_widget = |widget: &WidgetRect, text: &str, color: Color32| {
2508 let rect = widget.interact_rect;
2509 if rect.is_positive() {
2510 let painter = Painter::new(self.clone(), widget.layer_id, Rect::EVERYTHING);
2511 painter.debug_rect(rect, color, text);
2512 }
2513 };
2514
2515 let paint_widget_id = |id: Id, text: &str, color: Color32| {
2516 if let Some(widget) =
2517 self.write(|ctx| ctx.viewport().this_pass.widgets.get(id).copied())
2518 {
2519 let text = format!("{text} - {id:?}");
2520 paint_widget(&widget, &text, color);
2521 }
2522 };
2523
2524 if self.global_style().debug.show_interactive_widgets {
2525 let rects = self.write(|ctx| ctx.viewport().this_pass.widgets.clone());
2527 for (layer_id, rects) in rects.layers() {
2528 let painter = Painter::new(self.clone(), *layer_id, Rect::EVERYTHING);
2529 for rect in rects {
2530 if rect.sense.interactive() {
2531 let (color, text) = if rect.sense.senses_click() && rect.sense.senses_drag()
2532 {
2533 (Color32::from_rgb(0x88, 0, 0x88), "click+drag")
2534 } else if rect.sense.senses_click() {
2535 (Color32::from_rgb(0x88, 0, 0), "click")
2536 } else if rect.sense.senses_drag() {
2537 (Color32::from_rgb(0, 0, 0x88), "drag")
2538 } else {
2539 (Color32::from_rgb(0, 0, 0x88), "hover")
2541 };
2542 painter.debug_rect(rect.interact_rect, color, text);
2543 }
2544 }
2545 }
2546
2547 {
2549 let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());
2550 let InteractionSnapshot {
2551 clicked,
2552 long_touched: _,
2553 drag_started: _,
2554 dragged,
2555 drag_stopped: _,
2556 contains_pointer,
2557 hovered,
2558 } = interact_widgets;
2559
2560 if true {
2561 for &id in &contains_pointer {
2562 paint_widget_id(id, "contains_pointer", Color32::BLUE);
2563 }
2564
2565 let widget_rects = self.write(|w| w.viewport().this_pass.widgets.clone());
2566
2567 let mut contains_pointer: Vec<Id> = contains_pointer.iter().copied().collect();
2568 contains_pointer.sort_by_key(|&id| {
2569 widget_rects
2570 .order(id)
2571 .map(|(layer_id, order_in_layer)| (layer_id.order, order_in_layer))
2572 });
2573
2574 let mut debug_text = "Widgets in order:\n".to_owned();
2575 for id in contains_pointer {
2576 let mut widget_text = format!("{id:?}");
2577 if let Some(rect) = widget_rects.get(id) {
2578 write!(
2579 widget_text,
2580 " {:?} {:?} {:?}",
2581 rect.layer_id, rect.rect, rect.sense
2582 )
2583 .ok();
2584 }
2585 if let Some(info) = widget_rects.info(id) {
2586 write!(widget_text, " {info:?}").ok();
2587 }
2588 writeln!(debug_text, "{widget_text}").ok();
2589 }
2590 self.debug_text(debug_text);
2591 }
2592 if true {
2593 for widget in hovered {
2594 paint_widget_id(widget, "hovered", Color32::WHITE);
2595 }
2596 }
2597 if let Some(widget) = clicked {
2598 paint_widget_id(widget, "clicked", Color32::RED);
2599 }
2600 if let Some(widget) = dragged {
2601 paint_widget_id(widget, "dragged", Color32::GREEN);
2602 }
2603 }
2604 }
2605
2606 if self.global_style().debug.show_widget_hits {
2607 let hits = self.write(|ctx| ctx.viewport().hits.clone());
2608 let WidgetHits {
2609 close,
2610 contains_pointer,
2611 click,
2612 drag,
2613 } = hits;
2614
2615 if false {
2616 for widget in &close {
2617 paint_widget(widget, "close", Color32::from_gray(70));
2618 }
2619 }
2620 if true {
2621 for widget in &contains_pointer {
2622 paint_widget(widget, "contains_pointer", Color32::BLUE);
2623 }
2624 }
2625 if let Some(widget) = &click {
2626 paint_widget(widget, "click", Color32::RED);
2627 }
2628 if let Some(widget) = &drag {
2629 paint_widget(widget, "drag", Color32::GREEN);
2630 }
2631 }
2632
2633 if self.global_style().debug.show_focused_widget
2634 && let Some(focused_id) = self.memory(|mem| mem.focused())
2635 {
2636 paint_widget_id(focused_id, "focused", Color32::PURPLE);
2637 }
2638
2639 if let Some(debug_rect) = self.pass_state_mut(|fs| fs.debug_rect.take()) {
2640 debug_rect.paint(&self.debug_painter());
2641 }
2642
2643 let num_multipass_in_row = self.viewport(|vp| vp.num_multipass_in_row);
2644 if 3 <= num_multipass_in_row {
2645 let mut warning = format!(
2649 "egui PERF WARNING: request_discard has been called {num_multipass_in_row} frames in a row"
2650 );
2651 self.viewport(|vp| {
2652 for reason in &vp.output.request_discard_reasons {
2653 write!(warning, "\n {reason}").ok();
2654 }
2655 });
2656
2657 self.debug_painter()
2658 .debug_text(Pos2::ZERO, Align2::LEFT_TOP, Color32::RED, warning);
2659 }
2660 }
2661}
2662
2663impl ContextImpl {
2664 fn end_pass(&mut self) -> FullOutput {
2665 let ended_viewport_id = self.viewport_id();
2666 let viewport = self.viewports.entry(ended_viewport_id).or_default();
2667 let pixels_per_point = viewport.input.pixels_per_point;
2668
2669 self.loaders.end_pass(viewport.repaint.cumulative_pass_nr);
2670
2671 viewport.repaint.cumulative_pass_nr += 1;
2672
2673 self.memory.end_pass(&viewport.this_pass.used_ids);
2674
2675 if let Some(fonts) = self.fonts.as_mut() {
2676 let tex_mngr = &mut self.tex_manager.0.write();
2677 if let Some(font_image_delta) = fonts.font_image_delta() {
2678 tex_mngr.set(TextureId::default(), font_image_delta);
2680 }
2681 }
2682
2683 let textures_delta = self.tex_manager.0.write().take_delta();
2685
2686 let mut platform_output: PlatformOutput = core::mem::take(&mut viewport.output);
2687
2688 if self.memory.should_interrupt_ime()
2689 && let Some(ime) = &mut platform_output.ime
2690 {
2691 ime.should_interrupt_composition = true;
2692 }
2693
2694 {
2695 profiling::scope!("accesskit");
2696 let state = viewport.this_pass.accesskit_state.take();
2697 if let Some(state) = state {
2698 let root_id = crate::accesskit_root_id().accesskit_id();
2699 let nodes = {
2700 state
2701 .nodes
2702 .into_iter()
2703 .map(|(id, node)| (id.accesskit_id(), node))
2704 .collect()
2705 };
2706 let focus_id = self
2707 .memory
2708 .focused()
2709 .map_or(root_id, |id| id.accesskit_id());
2710 platform_output.accesskit_update = Some(accesskit::TreeUpdate {
2711 nodes,
2712 tree: Some(accesskit::Tree::new(root_id)),
2713 tree_id: accesskit::TreeId::ROOT,
2714 focus: focus_id,
2715 });
2716 }
2717 }
2718
2719 let shapes = viewport
2720 .graphics
2721 .drain(self.memory.areas().order(), &self.memory.to_global);
2722
2723 let mut repaint_needed = false;
2724
2725 if self.memory.options.repaint_on_widget_change {
2726 profiling::scope!("compare-widget-rects");
2727 #[allow(clippy::allow_attributes, clippy::collapsible_if)] if viewport.prev_pass.widgets != viewport.this_pass.widgets {
2729 repaint_needed = true; }
2731 }
2732
2733 #[cfg(debug_assertions)]
2734 let shapes = if self.memory.options.style().debug.warn_if_rect_changes_id {
2735 let mut shapes = shapes;
2736 warn_if_rect_changes_id(
2737 &mut shapes,
2738 &viewport.prev_pass.widgets,
2739 &viewport.this_pass.widgets,
2740 );
2741 shapes
2742 } else {
2743 shapes
2744 };
2745
2746 core::mem::swap(&mut viewport.prev_pass, &mut viewport.this_pass);
2747
2748 if repaint_needed {
2749 self.request_repaint(ended_viewport_id, RepaintCause::new());
2750 }
2751 let all_viewport_ids = self.all_viewport_ids();
2754
2755 self.last_viewport = ended_viewport_id;
2756
2757 self.viewports.retain(|&id, viewport| {
2758 if id == ViewportId::ROOT {
2759 return true; }
2761
2762 let parent = *self.viewport_parents.entry(id).or_default();
2763
2764 if !all_viewport_ids.contains(&parent) {
2765 log::debug!(
2766 "Removing viewport {:?} ({:?}): the parent is gone",
2767 id,
2768 viewport.builder.title
2769 );
2770
2771 return false;
2772 }
2773
2774 let is_our_child = parent == ended_viewport_id && id != ViewportId::ROOT;
2775 if is_our_child {
2776 if !viewport.used {
2777 log::debug!(
2778 "Removing viewport {:?} ({:?}): it was never used this pass",
2779 id,
2780 viewport.builder.title
2781 );
2782
2783 return false; }
2785
2786 viewport.used = false; }
2788
2789 true
2790 });
2791
2792 self.viewport_stack.pop();
2794
2795 let is_last = self.viewport_stack.is_empty();
2798
2799 let viewport_output = self
2800 .viewports
2801 .iter_mut()
2802 .map(|(&id, viewport)| {
2803 let parent = *self.viewport_parents.entry(id).or_default();
2804 let commands = if is_last {
2805 core::mem::take(&mut viewport.commands)
2809 } else {
2810 vec![]
2811 };
2812
2813 (
2814 id,
2815 ViewportOutput {
2816 parent,
2817 class: viewport.class,
2818 builder: viewport.builder.clone(),
2819 viewport_ui_cb: viewport.viewport_ui_cb.clone(),
2820 commands,
2821 repaint_delay: viewport.repaint.repaint_delay,
2822 },
2823 )
2824 })
2825 .collect();
2826
2827 if is_last {
2828 self.viewports.retain(|id, _| all_viewport_ids.contains(id));
2830 debug_assert!(
2831 self.viewports.contains_key(&ViewportId::ROOT),
2832 "Bug in egui: we removed the root viewport"
2833 );
2834 self.viewport_parents
2835 .retain(|id, _| all_viewport_ids.contains(id));
2836 } else {
2837 let viewport_id = self.viewport_id();
2838 self.memory.set_viewport_id(viewport_id);
2839 }
2840
2841 platform_output.num_completed_passes += 1;
2842
2843 FullOutput {
2844 platform_output,
2845 textures_delta,
2846 shapes,
2847 pixels_per_point,
2848 viewport_output,
2849 }
2850 }
2851}
2852
2853impl Context {
2854 pub fn tessellate(
2860 &self,
2861 shapes: Vec<ClippedShape>,
2862 pixels_per_point: f32,
2863 ) -> Vec<ClippedPrimitive> {
2864 profiling::function_scope!();
2865
2866 self.write(|ctx| {
2871 let tessellation_options = ctx.memory.options.tessellation_options;
2872 let texture_atlas = if let Some(fonts) = ctx.fonts.as_ref() {
2873 fonts.texture_atlas()
2874 } else {
2875 log::warn!("No font size matching {pixels_per_point} pixels per point found.");
2876 ctx.fonts
2877 .iter()
2878 .next()
2879 .expect("No fonts loaded")
2880 .texture_atlas()
2881 };
2882
2883 let paint_stats = PaintStats::from_shapes(&shapes);
2884 let clipped_primitives = {
2885 profiling::scope!("tessellator::tessellate_shapes");
2886 tessellator::Tessellator::new(
2887 pixels_per_point,
2888 tessellation_options,
2889 texture_atlas.size(),
2890 texture_atlas.prepared_discs(),
2891 )
2892 .tessellate_shapes(shapes)
2893 };
2894 ctx.paint_stats = paint_stats.with_clipped_primitives(&clipped_primitives);
2895 clipped_primitives
2896 })
2897 }
2898
2899 pub fn content_rect(&self) -> Rect {
2908 self.input(|i| i.content_rect()).round_ui()
2909 }
2910
2911 pub fn viewport_rect(&self) -> Rect {
2922 self.input(|i| i.viewport_rect()).round_ui()
2923 }
2924
2925 pub fn globally_used_rect(&self) -> Rect {
2927 self.write(|ctx| {
2928 let viewport = ctx.viewport();
2929 let root_ui_min_rect =
2930 (viewport.this_pass.root_ui_min_rect).or(viewport.prev_pass.root_ui_min_rect);
2931
2932 let mut used = root_ui_min_rect.unwrap_or(Rect::NOTHING);
2933 for (_id, window) in ctx.memory.areas().visible_windows() {
2934 used |= window.rect();
2935 }
2936 used.round_ui()
2937 })
2938 }
2939
2940 pub fn is_pointer_over_egui(&self) -> bool {
2944 let pointer_pos = self.input(|i| i.pointer.interact_pos());
2945 let Some(pointer_pos) = pointer_pos else {
2946 return false;
2947 };
2948 let Some(layer) = self.layer_id_at(pointer_pos) else {
2949 return false;
2950 };
2951 if layer.order == Order::Background {
2952 let root_ui_available_rect = self
2953 .pass_state(|state| state.root_ui_available_rect)
2954 .or_else(|| self.prev_pass_state(|state| state.root_ui_available_rect));
2955
2956 if let Some(root_ui_available_rect) = root_ui_available_rect {
2957 !root_ui_available_rect.contains(pointer_pos)
2959 } else {
2960 true }
2962 } else {
2963 true
2964 }
2965 }
2966
2967 pub fn egui_wants_pointer_input(&self) -> bool {
2974 self.egui_is_using_pointer()
2975 || (self.is_pointer_over_egui() && !self.input(|i| i.pointer.any_down()))
2976 }
2977
2978 pub fn egui_is_using_pointer(&self) -> bool {
2982 self.memory(|m| m.interaction().is_using_pointer())
2983 }
2984
2985 pub fn egui_wants_keyboard_input(&self) -> bool {
2987 self.memory(|m| m.focused().is_some())
2988 }
2989
2990 pub fn text_edit_focused(&self) -> bool {
2992 if let Some(id) = self.memory(|mem| mem.focused()) {
2993 crate::text_edit::TextEditState::load(self, id).is_some()
2994 } else {
2995 false
2996 }
2997 }
2998
2999 pub fn highlight_widget(&self, id: Id) {
3006 self.pass_state_mut(|fs| fs.highlight_next_pass.insert(id));
3007 }
3008
3009 pub fn any_popup_open(&self) -> bool {
3013 self.pass_state_mut(|fs| {
3014 fs.layers
3015 .values()
3016 .any(|layer| !layer.open_popups.is_empty())
3017 })
3018 }
3019}
3020
3021impl Context {
3023 #[inline(always)]
3027 pub fn pointer_latest_pos(&self) -> Option<Pos2> {
3028 self.input(|i| i.pointer.latest_pos())
3029 }
3030
3031 #[inline(always)]
3033 pub fn pointer_hover_pos(&self) -> Option<Pos2> {
3034 self.input(|i| i.pointer.hover_pos())
3035 }
3036
3037 #[inline(always)]
3043 pub fn pointer_interact_pos(&self) -> Option<Pos2> {
3044 self.input(|i| i.pointer.interact_pos())
3045 }
3046
3047 pub fn multi_touch(&self) -> Option<MultiTouchInfo> {
3049 self.input(|i| i.multi_touch())
3050 }
3051}
3052
3053impl Context {
3054 pub fn set_transform_layer(&self, layer_id: LayerId, transform: TSTransform) {
3066 self.memory_mut(|m| {
3067 if transform == TSTransform::IDENTITY {
3068 m.to_global.remove(&layer_id)
3069 } else {
3070 m.to_global.insert(layer_id, transform)
3071 }
3072 });
3073 }
3074
3075 pub fn layer_transform_to_global(&self, layer_id: LayerId) -> Option<TSTransform> {
3079 self.memory(|m| m.to_global.get(&layer_id).copied())
3080 }
3081
3082 pub fn layer_transform_from_global(&self, layer_id: LayerId) -> Option<TSTransform> {
3086 self.layer_transform_to_global(layer_id)
3087 .map(|t| t.inverse())
3088 }
3089
3090 pub fn transform_layer_shapes(&self, layer_id: LayerId, transform: TSTransform) {
3098 if transform != TSTransform::IDENTITY {
3099 self.graphics_mut(|g| g.entry(layer_id).transform(transform));
3100 }
3101 }
3102
3103 pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
3105 self.memory(|mem| mem.layer_id_at(pos))
3106 }
3107
3108 pub fn move_to_top(&self, layer_id: LayerId) {
3112 self.memory_mut(|mem| mem.areas_mut().move_to_top(layer_id));
3113 }
3114
3115 pub fn set_sublayer(&self, parent: LayerId, child: LayerId) {
3123 self.memory_mut(|mem| mem.areas_mut().set_sublayer(parent, child));
3124 }
3125
3126 pub fn top_layer_id(&self) -> Option<LayerId> {
3128 self.memory(|mem| mem.areas().top_layer_id(Order::Middle))
3129 }
3130
3131 pub fn rect_contains_pointer(&self, layer_id: LayerId, rect: Rect) -> bool {
3139 let rect = if let Some(to_global) = self.layer_transform_to_global(layer_id) {
3140 to_global * rect
3141 } else {
3142 rect
3143 };
3144 if !rect.is_positive() {
3145 return false;
3146 }
3147
3148 let pointer_pos = self.input(|i| i.pointer.interact_pos());
3149 let Some(pointer_pos) = pointer_pos else {
3150 return false;
3151 };
3152
3153 if !rect.contains(pointer_pos) {
3154 return false;
3155 }
3156
3157 if self.layer_id_at(pointer_pos) != Some(layer_id) {
3158 return false;
3159 }
3160
3161 true
3162 }
3163
3164 #[cfg(debug_assertions)]
3168 pub fn debug_on_hover(&self) -> bool {
3169 self.options(|opt| opt.style().debug.debug_on_hover)
3170 }
3171
3172 #[cfg(debug_assertions)]
3174 pub fn set_debug_on_hover(&self, debug_on_hover: bool) {
3175 self.all_styles_mut(|style| style.debug.debug_on_hover = debug_on_hover);
3176 }
3177}
3178
3179impl Context {
3181 #[track_caller] pub fn animate_bool(&self, id: Id, value: bool) -> f32 {
3192 let animation_time = self.global_style().animation_time;
3193 self.animate_bool_with_time_and_easing(id, value, animation_time, emath::easing::linear)
3194 }
3195
3196 #[track_caller] pub fn animate_bool_responsive(&self, id: Id, value: bool) -> f32 {
3202 self.animate_bool_with_easing(id, value, emath::easing::cubic_out)
3203 }
3204
3205 #[track_caller] pub fn animate_bool_with_easing(&self, id: Id, value: bool, easing: fn(f32) -> f32) -> f32 {
3208 let animation_time = self.global_style().animation_time;
3209 self.animate_bool_with_time_and_easing(id, value, animation_time, easing)
3210 }
3211
3212 #[track_caller] pub fn animate_bool_with_time(&self, id: Id, target_value: bool, animation_time: f32) -> f32 {
3215 self.animate_bool_with_time_and_easing(
3216 id,
3217 target_value,
3218 animation_time,
3219 emath::easing::linear,
3220 )
3221 }
3222
3223 #[track_caller] pub fn animate_bool_with_time_and_easing(
3232 &self,
3233 id: Id,
3234 target_value: bool,
3235 animation_time: f32,
3236 easing: fn(f32) -> f32,
3237 ) -> f32 {
3238 let animated_value = self.write(|ctx| {
3239 ctx.animation_manager.animate_bool(
3240 &ctx.viewports.entry(ctx.viewport_id()).or_default().input,
3241 animation_time,
3242 id,
3243 target_value,
3244 )
3245 });
3246
3247 let animation_in_progress = 0.0 < animated_value && animated_value < 1.0;
3248 if animation_in_progress {
3249 self.request_repaint();
3250 }
3251
3252 if target_value {
3253 easing(animated_value)
3254 } else {
3255 1.0 - easing(1.0 - animated_value)
3256 }
3257 }
3258
3259 #[track_caller] pub fn animate_value_with_time(&self, id: Id, target_value: f32, animation_time: f32) -> f32 {
3265 let animated_value = self.write(|ctx| {
3266 ctx.animation_manager.animate_value(
3267 &ctx.viewports.entry(ctx.viewport_id()).or_default().input,
3268 animation_time,
3269 id,
3270 target_value,
3271 )
3272 });
3273 let animation_in_progress = animated_value != target_value;
3274 if animation_in_progress {
3275 self.request_repaint();
3276 }
3277
3278 animated_value
3279 }
3280
3281 pub fn clear_animations(&self) {
3283 self.write(|ctx| ctx.animation_manager = Default::default());
3284 }
3285}
3286
3287impl Context {
3288 pub fn settings_ui(&self, ui: &mut Ui) {
3290 let prev_options = self.options(|o| o.clone());
3291 let mut options = prev_options.clone();
3292
3293 ui.collapsing("🔠 Font tweak", |ui| {
3294 self.fonts_tweak_ui(ui);
3295 });
3296
3297 options.ui(ui);
3298
3299 if options != prev_options {
3300 self.options_mut(move |o| *o = options);
3301 }
3302 }
3303
3304 fn fonts_tweak_ui(&self, ui: &mut Ui) {
3305 let mut font_definitions = self.write(|ctx| ctx.font_definitions.clone());
3306 let mut changed = false;
3307
3308 for (name, data) in &mut font_definitions.font_data {
3309 ui.collapsing(name, |ui| {
3310 let mut tweak = data.tweak.clone();
3311 let axes = data.variation_axes();
3312 if crate::style::font_tweak_ui(ui, &mut tweak, &axes).changed() {
3313 Arc::make_mut(data).tweak = tweak;
3314 changed = true;
3315 }
3316 });
3317 }
3318
3319 if changed {
3320 self.set_fonts(font_definitions);
3321 }
3322 }
3323
3324 pub fn inspection_ui(&self, ui: &mut Ui) {
3326 use crate::containers::CollapsingHeader;
3327
3328 crate::Grid::new("egui-inspection-grid")
3329 .num_columns(2)
3330 .striped(true)
3331 .show(ui, |ui| {
3332 ui.label("Total ui frames:");
3333 ui.monospace(ui.ctx().cumulative_frame_nr().to_string());
3334 ui.end_row();
3335
3336 ui.label("Total ui passes:");
3337 ui.monospace(ui.ctx().cumulative_pass_nr().to_string());
3338 ui.end_row();
3339
3340 ui.label("Is using pointer")
3341 .on_hover_text("Is egui currently using the pointer actively (e.g. dragging a slider)?");
3342 ui.monospace(self.egui_is_using_pointer().to_string());
3343 ui.end_row();
3344
3345 ui.label("Wants pointer input")
3346 .on_hover_text("Is egui currently interested in the location of the pointer (either because it is in use, or because it is hovering over a window).");
3347 ui.monospace(self.egui_wants_pointer_input().to_string());
3348 ui.end_row();
3349
3350 ui.label("Wants keyboard input").on_hover_text("Is egui currently listening for text input?");
3351 ui.monospace(self.egui_wants_keyboard_input().to_string());
3352 ui.end_row();
3353
3354 ui.label("Keyboard focus widget").on_hover_text("Is egui currently listening for text input?");
3355 ui.monospace(self.memory(|m| m.focused())
3356 .as_ref()
3357 .map(Id::short_debug_format)
3358 .unwrap_or_default());
3359 ui.end_row();
3360
3361 let pointer_pos = self
3362 .pointer_hover_pos()
3363 .map_or_else(String::new, |pos| format!("{pos:?}"));
3364 ui.label("Pointer pos");
3365 ui.monospace(pointer_pos);
3366 ui.end_row();
3367
3368 let top_layer = self
3369 .pointer_hover_pos()
3370 .and_then(|pos| self.layer_id_at(pos))
3371 .map_or_else(String::new, |layer| layer.short_debug_format());
3372 ui.label("Top layer under mouse");
3373 ui.monospace(top_layer);
3374 ui.end_row();
3375 });
3376
3377 ui.add_space(16.0);
3378
3379 ui.label(format!(
3380 "There are {} text galleys in the layout cache",
3381 self.fonts(|f| f.num_galleys_in_cache())
3382 ))
3383 .on_hover_text("This is approximately the number of text strings on screen");
3384 ui.add_space(16.0);
3385
3386 CollapsingHeader::new("🔃 Repaint Causes")
3387 .default_open(false)
3388 .show(ui, |ui| {
3389 ui.set_min_height(120.0);
3390 ui.label("What caused egui to repaint:");
3391 ui.add_space(8.0);
3392 let causes = ui.ctx().repaint_causes();
3393 for cause in causes {
3394 ui.label(cause.to_string());
3395 }
3396 });
3397
3398 CollapsingHeader::new("📥 Input")
3399 .default_open(false)
3400 .show(ui, |ui| {
3401 let input = ui.input(|i| i.clone());
3402 input.ui(ui);
3403 });
3404
3405 CollapsingHeader::new("📊 Paint stats")
3406 .default_open(false)
3407 .show(ui, |ui| {
3408 let paint_stats = self.read(|ctx| ctx.paint_stats);
3409 paint_stats.ui(ui);
3410 });
3411
3412 CollapsingHeader::new("🖼 Textures")
3413 .default_open(false)
3414 .show(ui, |ui| {
3415 self.texture_ui(ui);
3416 });
3417
3418 CollapsingHeader::new("🖼 Image loaders")
3419 .default_open(false)
3420 .show(ui, |ui| {
3421 self.loaders_ui(ui);
3422 });
3423
3424 CollapsingHeader::new("🔠 Font texture")
3425 .default_open(false)
3426 .show(ui, |ui| {
3427 let font_image_size = self.fonts(|f| f.font_image_size());
3428 crate::introspection::font_texture_ui(ui, font_image_size);
3429 });
3430
3431 CollapsingHeader::new("Label text selection state")
3432 .default_open(false)
3433 .show(ui, |ui| {
3434 ui.label(format!(
3435 "{:#?}",
3436 *ui.ctx()
3437 .plugin::<crate::text_selection::LabelSelectionState>()
3438 .lock()
3439 ));
3440 });
3441
3442 CollapsingHeader::new("Interaction")
3443 .default_open(false)
3444 .show(ui, |ui| {
3445 let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());
3446 interact_widgets.ui(ui);
3447 });
3448 }
3449
3450 pub fn texture_ui(&self, ui: &mut crate::Ui) {
3452 let tex_mngr = self.tex_manager();
3453 let tex_mngr = tex_mngr.read();
3454
3455 let mut textures: Vec<_> = tex_mngr.allocated().collect();
3456 textures.sort_by_key(|(id, _)| *id);
3457
3458 let mut bytes = 0;
3459 for (_, tex) in &textures {
3460 bytes += tex.bytes_used();
3461 }
3462
3463 ui.label(format!(
3464 "{} allocated texture(s), using {:.1} MB",
3465 textures.len(),
3466 bytes as f64 * 1e-6
3467 ));
3468 let max_preview_size = vec2(48.0, 32.0);
3469
3470 let pixels_per_point = self.pixels_per_point();
3471
3472 ui.group(|ui| {
3473 ScrollArea::vertical()
3474 .max_height(300.0)
3475 .auto_shrink([false, true])
3476 .show(ui, |ui| {
3477 ui.style_mut().override_text_style = Some(TextStyle::Monospace);
3478 Grid::new("textures")
3479 .striped(true)
3480 .num_columns(4)
3481 .spacing(vec2(16.0, 2.0))
3482 .min_row_height(max_preview_size.y)
3483 .show(ui, |ui| {
3484 for (&texture_id, meta) in textures {
3485 let [w, h] = meta.size;
3486 let point_size = vec2(w as f32, h as f32) / pixels_per_point;
3487
3488 let mut size = point_size;
3489 size *= (max_preview_size.x / size.x).min(1.0);
3490 size *= (max_preview_size.y / size.y).min(1.0);
3491 ui.image(SizedTexture::new(texture_id, size))
3492 .on_hover_ui(|ui| {
3493 let max_size = 0.5 * ui.ctx().content_rect().size();
3495 let mut size = point_size;
3496 size *= max_size.x / size.x.max(max_size.x);
3497 size *= max_size.y / size.y.max(max_size.y);
3498 ui.image(SizedTexture::new(texture_id, size));
3499 });
3500
3501 ui.label(format!("{w} x {h}"));
3502 ui.label(format!("{:.3} MB", meta.bytes_used() as f64 * 1e-6));
3503 ui.label(format!("{:?}", meta.name));
3504 ui.end_row();
3505 }
3506 });
3507 });
3508 });
3509 }
3510
3511 pub fn loaders_ui(&self, ui: &mut crate::Ui) {
3513 struct LoaderInfo {
3514 id: String,
3515 byte_size: usize,
3516 }
3517
3518 let mut byte_loaders = vec![];
3519 let mut image_loaders = vec![];
3520 let mut texture_loaders = vec![];
3521
3522 {
3523 let loaders = self.loaders();
3524 let Loaders {
3525 include: _,
3526 bytes,
3527 image,
3528 texture,
3529 } = loaders.as_ref();
3530
3531 for loader in bytes.lock().iter() {
3532 byte_loaders.push(LoaderInfo {
3533 id: loader.id().to_owned(),
3534 byte_size: loader.byte_size(),
3535 });
3536 }
3537 for loader in image.lock().iter() {
3538 image_loaders.push(LoaderInfo {
3539 id: loader.id().to_owned(),
3540 byte_size: loader.byte_size(),
3541 });
3542 }
3543 for loader in texture.lock().iter() {
3544 texture_loaders.push(LoaderInfo {
3545 id: loader.id().to_owned(),
3546 byte_size: loader.byte_size(),
3547 });
3548 }
3549 }
3550
3551 fn loaders_ui(ui: &mut crate::Ui, title: &str, loaders: &[LoaderInfo]) {
3552 let heading = format!("{} {title} loaders", loaders.len());
3553 crate::CollapsingHeader::new(heading)
3554 .default_open(true)
3555 .show(ui, |ui| {
3556 Grid::new("loaders")
3557 .striped(true)
3558 .num_columns(2)
3559 .show(ui, |ui| {
3560 ui.label("ID");
3561 ui.label("Size");
3562 ui.end_row();
3563
3564 for loader in loaders {
3565 ui.label(&loader.id);
3566 ui.label(format!("{:.3} MB", loader.byte_size as f64 * 1e-6));
3567 ui.end_row();
3568 }
3569 });
3570 });
3571 }
3572
3573 loaders_ui(ui, "byte", &byte_loaders);
3574 loaders_ui(ui, "image", &image_loaders);
3575 loaders_ui(ui, "texture", &texture_loaders);
3576 }
3577
3578 pub fn memory_ui(&self, ui: &mut crate::Ui) {
3580 if ui
3581 .button("Reset all")
3582 .on_hover_text("Reset all egui state")
3583 .clicked()
3584 {
3585 self.memory_mut(|mem| *mem = Default::default());
3586 }
3587
3588 let (num_state, num_serialized) = self.data(|d| (d.len(), d.count_serialized()));
3589 ui.label(format!(
3590 "{num_state} widget states stored (of which {num_serialized} are serialized)."
3591 ));
3592
3593 ui.horizontal(|ui| {
3594 ui.label(format!(
3595 "{} areas (panels, windows, popups, …)",
3596 self.memory(|mem| mem.areas().count())
3597 ));
3598 if ui.button("Reset").clicked() {
3599 self.memory_mut(|mem| *mem.areas_mut() = Default::default());
3600 }
3601 });
3602 ui.indent("layers", |ui| {
3603 ui.label("Layers, ordered back to front.");
3604 let layers_ids: Vec<LayerId> = self.memory(|mem| mem.areas().order().to_vec());
3605 for layer_id in layers_ids {
3606 if let Some(area) = AreaState::load(self, layer_id.id) {
3607 let is_visible = self.memory(|mem| mem.areas().is_visible(&layer_id));
3608 if !is_visible {
3609 continue;
3610 }
3611 let text = format!("{} - {:?}", layer_id.short_debug_format(), area.rect());
3612 let response =
3614 ui.add(Label::new(RichText::new(text).monospace()).sense(Sense::click()));
3615 if response.hovered() && is_visible {
3616 ui.debug_painter().debug_rect(area.rect(), Color32::RED, "");
3617 }
3618 } else {
3619 ui.monospace(layer_id.short_debug_format());
3620 }
3621 }
3622 });
3623
3624 ui.horizontal(|ui| {
3625 ui.label(format!(
3626 "{} collapsing headers",
3627 self.data(|d| d.count::<containers::collapsing_header::InnerState>())
3628 ));
3629 if ui.button("Reset").clicked() {
3630 self.data_mut(|d| d.remove_by_type::<containers::collapsing_header::InnerState>());
3631 }
3632 });
3633
3634 ui.horizontal(|ui| {
3635 ui.label(format!(
3636 "{} scroll areas",
3637 self.data(|d| d.count::<scroll_area::State>())
3638 ));
3639 if ui.button("Reset").clicked() {
3640 self.data_mut(|d| d.remove_by_type::<scroll_area::State>());
3641 }
3642 });
3643
3644 ui.horizontal(|ui| {
3645 ui.label(format!(
3646 "{} resize areas",
3647 self.data(|d| d.count::<resize::State>())
3648 ));
3649 if ui.button("Reset").clicked() {
3650 self.data_mut(|d| d.remove_by_type::<resize::State>());
3651 }
3652 });
3653
3654 ui.shrink_width_to_current(); ui.label("NOTE: the position of this window cannot be reset from within itself.");
3656
3657 ui.collapsing("Interaction", |ui| {
3658 let interaction = self.memory(|mem| mem.interaction().clone());
3659 interaction.ui(ui);
3660 });
3661 }
3662}
3663
3664impl Context {
3665 pub fn style_ui(&self, ui: &mut Ui, theme: Theme) {
3667 let mut style: Style = (*self.style_of(theme)).clone();
3668 style.ui(ui);
3669 self.set_style_of(theme, style);
3670 }
3671}
3672
3673impl Context {
3675 pub fn accesskit_node_builder<R>(
3685 &self,
3686 id: Id,
3687 writer: impl FnOnce(&mut accesskit::Node) -> R,
3688 ) -> Option<R> {
3689 self.write(|ctx| ctx.accesskit_node_builder(id).map(writer))
3690 }
3691
3692 pub(crate) fn register_accesskit_parent(&self, id: Id, parent_id: Id) {
3693 self.write(|ctx| {
3694 if let Some(state) = ctx.viewport().this_pass.accesskit_state.as_mut() {
3695 state.parent_map.insert(id, parent_id);
3696 }
3697 });
3698 }
3699
3700 pub fn enable_accesskit(&self) {
3702 self.write(|ctx| ctx.is_accesskit_enabled = true);
3703 }
3704
3705 pub fn disable_accesskit(&self) {
3707 self.write(|ctx| ctx.is_accesskit_enabled = false);
3708 }
3709}
3710
3711impl Context {
3713 pub fn include_bytes(&self, uri: impl Into<Cow<'static, str>>, bytes: impl Into<Bytes>) {
3720 self.loaders().include.insert(uri, bytes);
3721 }
3722
3723 pub fn is_loader_installed(&self, id: &str) -> bool {
3726 let loaders = self.loaders();
3727
3728 loaders.bytes.lock().iter().any(|l| l.id() == id)
3729 || loaders.image.lock().iter().any(|l| l.id() == id)
3730 || loaders.texture.lock().iter().any(|l| l.id() == id)
3731 }
3732
3733 pub fn add_bytes_loader(&self, loader: Arc<dyn load::BytesLoader + Send + Sync + 'static>) {
3739 self.loaders().bytes.lock().push(loader);
3740 }
3741
3742 pub fn add_image_loader(&self, loader: Arc<dyn load::ImageLoader + Send + Sync + 'static>) {
3748 self.loaders().image.lock().push(loader);
3749 }
3750
3751 pub fn add_texture_loader(&self, loader: Arc<dyn load::TextureLoader + Send + Sync + 'static>) {
3757 self.loaders().texture.lock().push(loader);
3758 }
3759
3760 pub fn forget_image(&self, uri: &str) {
3765 use load::BytesLoader as _;
3766
3767 profiling::function_scope!();
3768
3769 let loaders = self.loaders();
3770
3771 loaders.include.forget(uri);
3772 for loader in loaders.bytes.lock().iter() {
3773 loader.forget(uri);
3774 }
3775 for loader in loaders.image.lock().iter() {
3776 loader.forget(uri);
3777 }
3778 for loader in loaders.texture.lock().iter() {
3779 loader.forget(uri);
3780 }
3781 }
3782
3783 pub fn forget_all_images(&self) {
3787 use load::BytesLoader as _;
3788
3789 profiling::function_scope!();
3790
3791 let loaders = self.loaders();
3792
3793 loaders.include.forget_all();
3794 for loader in loaders.bytes.lock().iter() {
3795 loader.forget_all();
3796 }
3797 for loader in loaders.image.lock().iter() {
3798 loader.forget_all();
3799 }
3800 for loader in loaders.texture.lock().iter() {
3801 loader.forget_all();
3802 }
3803 }
3804
3805 pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult {
3824 profiling::function_scope!(uri);
3825
3826 let loaders = self.loaders();
3827 let bytes_loaders = loaders.bytes.lock();
3828
3829 for loader in bytes_loaders.iter().rev() {
3831 let result = loader.load(self, uri);
3832 match result {
3833 Err(load::LoadError::NotSupported) => {}
3834 _ => return result,
3835 }
3836 }
3837
3838 Err(load::LoadError::NoMatchingBytesLoader)
3839 }
3840
3841 pub fn try_load_image(&self, uri: &str, size_hint: load::SizeHint) -> load::ImageLoadResult {
3862 profiling::function_scope!(uri);
3863
3864 let loaders = self.loaders();
3865 let image_loaders = loaders.image.lock();
3866 if image_loaders.is_empty() {
3867 return Err(load::LoadError::NoImageLoaders);
3868 }
3869
3870 let mut format = None;
3871
3872 for loader in image_loaders.iter().rev() {
3874 match loader.load(self, uri, size_hint) {
3875 Err(load::LoadError::NotSupported) => {}
3876 Err(load::LoadError::FormatNotSupported { detected_format }) => {
3877 format = format.or(detected_format);
3878 }
3879 result => return result,
3880 }
3881 }
3882
3883 Err(load::LoadError::NoMatchingImageLoader {
3884 detected_format: format,
3885 })
3886 }
3887
3888 pub fn try_load_texture(
3907 &self,
3908 uri: &str,
3909 texture_options: TextureOptions,
3910 size_hint: load::SizeHint,
3911 ) -> load::TextureLoadResult {
3912 profiling::function_scope!(uri);
3913
3914 let loaders = self.loaders();
3915 let texture_loaders = loaders.texture.lock();
3916
3917 for loader in texture_loaders.iter().rev() {
3919 match loader.load(self, uri, texture_options, size_hint) {
3920 Err(load::LoadError::NotSupported) => {}
3921 result => return result,
3922 }
3923 }
3924
3925 Err(load::LoadError::NoMatchingTextureLoader)
3926 }
3927
3928 pub fn loaders(&self) -> Arc<Loaders> {
3930 self.read(|this| Arc::clone(&this.loaders))
3931 }
3932
3933 pub fn has_pending_images(&self) -> bool {
3935 self.read(|this| {
3936 this.loaders.image.lock().iter().any(|i| i.has_pending())
3937 || this.loaders.bytes.lock().iter().any(|i| i.has_pending())
3938 })
3939 }
3940}
3941
3942impl Context {
3944 pub fn viewport_id(&self) -> ViewportId {
3950 self.read(|ctx| ctx.viewport_id())
3951 }
3952
3953 pub fn parent_viewport_id(&self) -> ViewportId {
3959 self.read(|ctx| ctx.parent_viewport_id())
3960 }
3961
3962 pub fn viewport<R>(&self, reader: impl FnOnce(&ViewportState) -> R) -> R {
3964 self.write(|ctx| reader(ctx.viewport()))
3965 }
3966
3967 pub fn viewport_for<R>(
3969 &self,
3970 viewport_id: ViewportId,
3971 reader: impl FnOnce(&ViewportState) -> R,
3972 ) -> R {
3973 self.write(|ctx| reader(ctx.viewport_for(viewport_id)))
3974 }
3975
3976 pub fn set_immediate_viewport_renderer(
3989 callback: impl for<'a> Fn(&Self, ImmediateViewport<'a>) + 'static,
3990 ) {
3991 let callback = Box::new(callback);
3992 IMMEDIATE_VIEWPORT_RENDERER.with(|render_sync| {
3993 render_sync.replace(Some(callback));
3994 });
3995 }
3996
3997 pub fn embed_viewports(&self) -> bool {
4002 self.read(|ctx| ctx.embed_viewports)
4003 }
4004
4005 pub fn set_embed_viewports(&self, value: bool) {
4010 self.write(|ctx| ctx.embed_viewports = value);
4011 }
4012
4013 pub fn send_viewport_cmd(&self, command: ViewportCommand) {
4017 self.send_viewport_cmd_to(self.viewport_id(), command);
4018 }
4019
4020 pub fn send_viewport_cmd_to(&self, id: ViewportId, command: ViewportCommand) {
4024 self.request_repaint_of(id);
4025
4026 if command.requires_parent_repaint() {
4027 self.request_repaint_of(self.parent_viewport_id());
4028 }
4029
4030 self.write(|ctx| ctx.viewport_for(id).commands.push(command));
4031 }
4032
4033 pub fn show_viewport_deferred(
4063 &self,
4064 new_viewport_id: ViewportId,
4065 viewport_builder: ViewportBuilder,
4066 viewport_ui_cb: impl Fn(&mut Ui, ViewportClass) + Send + Sync + 'static,
4067 ) {
4068 profiling::function_scope!();
4069
4070 if self.embed_viewports() {
4071 crate::Window::from_viewport(new_viewport_id, viewport_builder).show(self, |ui| {
4072 viewport_ui_cb(ui, ViewportClass::EmbeddedWindow);
4073 });
4074 } else {
4075 self.write(|ctx| {
4076 ctx.viewport_parents
4077 .insert(new_viewport_id, ctx.viewport_id());
4078
4079 let viewport = ctx.viewports.entry(new_viewport_id).or_default();
4080 viewport.class = ViewportClass::Deferred;
4081 viewport.builder = viewport_builder;
4082 viewport.used = true;
4083 viewport.viewport_ui_cb = Some(Arc::new(move |ui| {
4084 (viewport_ui_cb)(ui, ViewportClass::Deferred);
4085 }));
4086 });
4087 }
4088 }
4089
4090 pub fn show_viewport_immediate<T>(
4117 &self,
4118 new_viewport_id: ViewportId,
4119 builder: ViewportBuilder,
4120 mut viewport_ui_cb: impl FnMut(&mut Ui, ViewportClass) -> T,
4121 ) -> T {
4122 profiling::function_scope!();
4123
4124 if self.embed_viewports() {
4125 return self.show_embedded_viewport(new_viewport_id, builder, |ui| {
4126 viewport_ui_cb(ui, ViewportClass::EmbeddedWindow)
4127 });
4128 }
4129
4130 IMMEDIATE_VIEWPORT_RENDERER.with(|immediate_viewport_renderer| {
4131 let immediate_viewport_renderer = immediate_viewport_renderer.borrow();
4132 let Some(immediate_viewport_renderer) = immediate_viewport_renderer.as_ref() else {
4133 return self.show_embedded_viewport(new_viewport_id, builder, |ui| {
4135 viewport_ui_cb(ui, ViewportClass::EmbeddedWindow)
4136 });
4137 };
4138
4139 let ids = self.write(|ctx| {
4140 let parent_viewport_id = ctx.viewport_id();
4141
4142 ctx.viewport_parents
4143 .insert(new_viewport_id, parent_viewport_id);
4144
4145 let viewport = ctx.viewports.entry(new_viewport_id).or_default();
4146 viewport.builder = builder.clone();
4147 viewport.used = true;
4148 viewport.viewport_ui_cb = None; ViewportIdPair::from_self_and_parent(new_viewport_id, parent_viewport_id)
4151 });
4152
4153 let mut out = None;
4154 {
4155 let out = &mut out;
4156
4157 let viewport = ImmediateViewport {
4158 ids,
4159 builder,
4160 viewport_ui_cb: Box::new(move |ui| {
4161 *out = Some((viewport_ui_cb)(ui, ViewportClass::Immediate));
4162 }),
4163 };
4164
4165 immediate_viewport_renderer(self, viewport);
4166 }
4167
4168 out.expect(
4169 "egui backend is implemented incorrectly - the user callback was never called",
4170 )
4171 })
4172 }
4173
4174 fn show_embedded_viewport<T>(
4175 &self,
4176 new_viewport_id: ViewportId,
4177 builder: ViewportBuilder,
4178 viewport_ui_cb: impl FnOnce(&mut Ui) -> T,
4179 ) -> T {
4180 crate::Window::from_viewport(new_viewport_id, builder)
4181 .collapsible(false)
4182 .show(self, |ui| viewport_ui_cb(ui))
4183 .unwrap_or_else(|| panic!("Window did not show"))
4184 .inner
4185 .unwrap_or_else(|| panic!("Window was collapsed"))
4186 }
4187}
4188
4189impl Context {
4191 pub fn interaction_snapshot<R>(&self, reader: impl FnOnce(&InteractionSnapshot) -> R) -> R {
4193 self.write(|w| reader(&w.viewport().interact_widgets))
4194 }
4195
4196 pub fn dragged_id(&self) -> Option<Id> {
4204 self.interaction_snapshot(|i| i.dragged)
4205 }
4206
4207 pub fn is_being_dragged(&self, id: Id) -> bool {
4214 self.dragged_id() == Some(id)
4215 }
4216
4217 pub fn drag_started_id(&self) -> Option<Id> {
4221 self.interaction_snapshot(|i| i.drag_started)
4222 }
4223
4224 pub fn drag_stopped_id(&self) -> Option<Id> {
4226 self.interaction_snapshot(|i| i.drag_stopped)
4227 }
4228
4229 pub fn set_dragged_id(&self, id: Id) {
4231 self.write(|ctx| {
4232 let vp = ctx.viewport();
4233 let i = &mut vp.interact_widgets;
4234 if i.dragged != Some(id) {
4235 i.drag_stopped = i.dragged.or(i.drag_stopped);
4236 i.dragged = Some(id);
4237 i.drag_started = Some(id);
4238 }
4239
4240 ctx.memory.interaction_mut().potential_drag_id = Some(id);
4241 });
4242 }
4243
4244 pub fn stop_dragging(&self) {
4246 self.write(|ctx| {
4247 let vp = ctx.viewport();
4248 let i = &mut vp.interact_widgets;
4249 if i.dragged.is_some() {
4250 i.drag_stopped = i.dragged;
4251 i.dragged = None;
4252 }
4253
4254 ctx.memory.interaction_mut().potential_drag_id = None;
4255 });
4256 }
4257
4258 #[inline(always)]
4262 pub fn dragging_something_else(&self, not_this: Id) -> bool {
4263 let dragged = self.dragged_id();
4264 dragged.is_some() && dragged != Some(not_this)
4265 }
4266}
4267
4268#[test]
4269fn context_impl_send_sync() {
4270 fn assert_send_sync<T: Send + Sync>() {}
4271 assert_send_sync::<Context>();
4272}
4273
4274#[cfg(debug_assertions)]
4279fn warn_if_rect_changes_id(
4280 out_shapes: &mut Vec<ClippedShape>,
4281 prev_widgets: &crate::WidgetRects,
4282 new_widgets: &crate::WidgetRects,
4283) {
4284 profiling::function_scope!();
4285
4286 use std::collections::BTreeMap;
4287
4288 #[derive(Clone, Copy, PartialEq, Eq)]
4290 struct OrderedRect(Rect);
4291
4292 impl PartialOrd for OrderedRect {
4293 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
4294 Some(self.cmp(other))
4295 }
4296 }
4297
4298 impl Ord for OrderedRect {
4299 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
4300 let lhs = self.0;
4301 let rhs = other.0;
4302 lhs.min
4303 .x
4304 .to_bits()
4305 .cmp(&rhs.min.x.to_bits())
4306 .then(lhs.min.y.to_bits().cmp(&rhs.min.y.to_bits()))
4307 .then(lhs.max.x.to_bits().cmp(&rhs.max.x.to_bits()))
4308 .then(lhs.max.y.to_bits().cmp(&rhs.max.y.to_bits()))
4309 }
4310 }
4311
4312 fn create_lookup<'a>(
4313 widgets: impl Iterator<Item = &'a WidgetRect>,
4314 ) -> BTreeMap<OrderedRect, Vec<&'a WidgetRect>> {
4315 let mut lookup: BTreeMap<OrderedRect, Vec<&'a WidgetRect>> = BTreeMap::default();
4316 for w in widgets {
4317 lookup.entry(OrderedRect(w.rect)).or_default().push(w);
4318 }
4319 lookup
4320 }
4321
4322 for (layer_id, new_layer_widgets) in new_widgets.layers() {
4323 let prev = create_lookup(prev_widgets.get_layer(*layer_id));
4324 let new = create_lookup(new_layer_widgets.iter());
4325
4326 for (hashable_rect, new_at_rect) in new {
4327 let Some(prev_at_rect) = prev.get(&hashable_rect) else {
4328 continue; };
4330
4331 if prev_at_rect
4332 .iter()
4333 .any(|w| new_at_rect.iter().any(|nw| nw.id == w.id))
4334 {
4335 continue; }
4337
4338 if prev_at_rect.iter().all(|w| new_widgets.contains(w.id)) {
4342 continue;
4343 }
4344
4345 if !prev_at_rect
4348 .iter()
4349 .any(|pw| new_at_rect.iter().any(|nw| nw.parent_id == pw.parent_id))
4350 {
4351 continue;
4352 }
4353
4354 let rect = new_at_rect[0].rect;
4355
4356 log::warn!(
4357 "Widget rect {rect:?} changed id between passes: prev ids: {:?}, new ids: {:?}",
4358 prev_at_rect
4359 .iter()
4360 .map(|w| w.id.short_debug_format())
4361 .collect::<Vec<_>>(),
4362 new_at_rect
4363 .iter()
4364 .map(|w| w.id.short_debug_format())
4365 .collect::<Vec<_>>(),
4366 );
4367 out_shapes.push(ClippedShape {
4368 clip_rect: Rect::EVERYTHING,
4369 shape: epaint::Shape::rect_stroke(
4370 rect,
4371 0,
4372 (2.0, Color32::RED),
4373 StrokeKind::Outside,
4374 ),
4375 });
4376 }
4377 }
4378}
4379
4380#[cfg(test)]
4381mod test {
4382 use super::Context;
4383
4384 #[test]
4385 fn test_single_pass() {
4386 let ctx = Context::default();
4387 ctx.options_mut(|o| o.max_passes = 1.try_into().unwrap());
4388
4389 {
4391 let mut num_calls = 0;
4392 let output = ctx.run_ui(Default::default(), |ui| {
4393 num_calls += 1;
4394 assert_eq!(ui.output(|o| o.num_completed_passes), 0);
4395 assert!(!ui.output(|o| o.requested_discard()));
4396 assert!(!ui.will_discard());
4397 });
4398 assert_eq!(num_calls, 1);
4399 assert_eq!(output.platform_output.num_completed_passes, 1);
4400 assert!(!output.platform_output.requested_discard());
4401 output.drop_without_applying_deltas();
4402 }
4403
4404 {
4406 let mut num_calls = 0;
4407 let output = ctx.run_ui(Default::default(), |ui| {
4408 num_calls += 1;
4409 ui.request_discard("test");
4410 assert!(!ui.will_discard(), "The request should have been denied");
4411 });
4412 assert_eq!(num_calls, 1);
4413 assert_eq!(output.platform_output.num_completed_passes, 1);
4414 assert!(
4415 output.platform_output.requested_discard(),
4416 "The request should be reported"
4417 );
4418 assert_eq!(
4419 output
4420 .platform_output
4421 .request_discard_reasons
4422 .first()
4423 .unwrap()
4424 .reason,
4425 "test"
4426 );
4427 output.drop_without_applying_deltas();
4428 }
4429 }
4430
4431 #[test]
4432 fn test_dual_pass() {
4433 let ctx = Context::default();
4434 ctx.options_mut(|o| o.max_passes = 2.try_into().unwrap());
4435
4436 {
4438 let mut num_calls = 0;
4439 let output = ctx.run_ui(Default::default(), |ui| {
4440 assert_eq!(ui.output(|o| o.num_completed_passes), 0);
4441 assert!(!ui.output(|o| o.requested_discard()));
4442 assert!(!ui.will_discard());
4443 num_calls += 1;
4444 });
4445 assert_eq!(num_calls, 1);
4446 assert_eq!(output.platform_output.num_completed_passes, 1);
4447 assert!(!output.platform_output.requested_discard());
4448 output.drop_without_applying_deltas();
4449 }
4450
4451 {
4453 let mut num_calls = 0;
4454 let output = ctx.run_ui(Default::default(), |ui| {
4455 assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);
4456
4457 assert!(!ui.will_discard());
4458 if num_calls == 0 {
4459 ui.request_discard("test");
4460 assert!(ui.will_discard());
4461 }
4462
4463 num_calls += 1;
4464 });
4465 assert_eq!(num_calls, 2);
4466 assert_eq!(output.platform_output.num_completed_passes, 2);
4467 assert!(
4468 !output.platform_output.requested_discard(),
4469 "The request should have been cleared when fulfilled"
4470 );
4471 output.drop_without_applying_deltas();
4472 }
4473
4474 {
4476 let mut num_calls = 0;
4477 let output = ctx.run_ui(Default::default(), |ui| {
4478 assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);
4479
4480 assert!(!ui.will_discard());
4481 ui.request_discard("test");
4482 if num_calls == 0 {
4483 assert!(ui.will_discard(), "First request granted");
4484 } else {
4485 assert!(!ui.will_discard(), "Second request should be denied");
4486 }
4487
4488 num_calls += 1;
4489 });
4490 assert_eq!(num_calls, 2);
4491 assert_eq!(output.platform_output.num_completed_passes, 2);
4492 assert!(
4493 output.platform_output.requested_discard(),
4494 "The unfulfilled request should be reported"
4495 );
4496 output.drop_without_applying_deltas();
4497 }
4498 }
4499
4500 #[test]
4501 fn test_multi_pass() {
4502 let ctx = Context::default();
4503 ctx.options_mut(|o| o.max_passes = 10.try_into().unwrap());
4504
4505 {
4507 let mut num_calls = 0;
4508 let output = ctx.run_ui(Default::default(), |ui| {
4509 assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);
4510
4511 assert!(!ui.will_discard());
4512 if num_calls <= 2 {
4513 ui.request_discard("test");
4514 assert!(ui.will_discard());
4515 }
4516
4517 num_calls += 1;
4518 });
4519 assert_eq!(num_calls, 4);
4520 assert_eq!(output.platform_output.num_completed_passes, 4);
4521 assert!(
4522 !output.platform_output.requested_discard(),
4523 "The request should have been cleared when fulfilled"
4524 );
4525 output.drop_without_applying_deltas();
4526 }
4527 }
4528}