1#![warn(missing_docs)] use std::{borrow::Cow, cell::RefCell, panic::Location, sync::Arc, time::Duration};
4
5use emath::GuiRounding as _;
6use epaint::{
7 ClippedPrimitive, ClippedShape, Color32, ImageData, Pos2, Rect, StrokeKind,
8 TessellationOptions, TextureId, Vec2,
9 emath::{self, TSTransform},
10 mutex::RwLock,
11 stats::PaintStats,
12 tessellator,
13 text::{FontInsert, FontPriority, Fonts, FontsView},
14 vec2,
15};
16
17use crate::{
18 Align2, CursorIcon, DeferredViewportUiCallback, FontDefinitions, Grid, Id, ImmediateViewport,
19 ImmediateViewportRendererCallback, Key, KeyboardShortcut, Label, LayerId, Memory,
20 ModifierNames, Modifiers, NumExt as _, Order, Painter, RawInput, Response, RichText,
21 SafeAreaInsets, ScrollArea, Sense, Style, TextStyle, TextureHandle, TextureOptions, Ui,
22 UiBuilder, ViewportBuilder, ViewportCommand, ViewportId, ViewportIdMap, ViewportIdPair,
23 ViewportIdSet, ViewportOutput, Visuals, Widget as _, WidgetRect, WidgetText,
24 animation_manager::AnimationManager,
25 containers::{self, area::AreaState},
26 data::output::PlatformOutput,
27 epaint,
28 hit_test::WidgetHits,
29 input_state::{InputState, MultiTouchInfo, PointerEvent, SurrenderFocusOn},
30 interaction::InteractionSnapshot,
31 layers::GraphicLayers,
32 load::{self, Bytes, Loaders, SizedTexture},
33 memory::{Options, Theme},
34 os::OperatingSystem,
35 output::FullOutput,
36 pass_state::PassState,
37 plugin::{self, TypedPluginHandle},
38 resize, response, scroll_area,
39 util::IdTypeMap,
40 viewport::ViewportClass,
41};
42
43use crate::IdMap;
44
45#[derive(Clone, Copy, Debug)]
49pub struct RequestRepaintInfo {
50 pub viewport_id: ViewportId,
52
53 pub delay: Duration,
55
56 pub current_cumulative_pass_nr: u64,
61}
62
63thread_local! {
66 static IMMEDIATE_VIEWPORT_RENDERER: RefCell<Option<Box<ImmediateViewportRendererCallback>>> = Default::default();
67}
68
69struct WrappedTextureManager(Arc<RwLock<epaint::TextureManager>>);
72
73impl Default for WrappedTextureManager {
74 fn default() -> Self {
75 let mut tex_mngr = epaint::textures::TextureManager::default();
76
77 let font_id = tex_mngr.alloc(
79 "egui_font_texture".into(),
80 epaint::ColorImage::filled([0, 0], Color32::TRANSPARENT).into(),
81 Default::default(),
82 );
83 assert_eq!(
84 font_id,
85 TextureId::default(),
86 "font id should be equal to TextureId::default(), but was {font_id:?}",
87 );
88
89 Self(Arc::new(RwLock::new(tex_mngr)))
90 }
91}
92
93impl ContextImpl {
97 fn begin_pass_repaint_logic(&mut self, viewport_id: ViewportId) {
99 let viewport = self.viewports.entry(viewport_id).or_default();
100
101 std::mem::swap(
102 &mut viewport.repaint.prev_causes,
103 &mut viewport.repaint.causes,
104 );
105 viewport.repaint.causes.clear();
106
107 viewport.repaint.prev_pass_paint_delay = viewport.repaint.repaint_delay;
108
109 if viewport.repaint.outstanding == 0 {
110 viewport.repaint.repaint_delay = Duration::MAX;
112 } else {
113 viewport.repaint.repaint_delay = Duration::ZERO;
114 viewport.repaint.outstanding -= 1;
115 if let Some(callback) = &self.request_repaint_callback {
116 (callback)(RequestRepaintInfo {
117 viewport_id,
118 delay: Duration::ZERO,
119 current_cumulative_pass_nr: viewport.repaint.cumulative_pass_nr,
120 });
121 }
122 }
123 }
124
125 fn request_repaint(&mut self, viewport_id: ViewportId, cause: RepaintCause) {
126 self.request_repaint_after(Duration::ZERO, viewport_id, cause);
127 }
128
129 fn request_repaint_after(
130 &mut self,
131 mut delay: Duration,
132 viewport_id: ViewportId,
133 cause: RepaintCause,
134 ) {
135 let viewport = self.viewports.entry(viewport_id).or_default();
136
137 if delay == Duration::ZERO {
138 viewport.repaint.outstanding = 1;
141 } else {
142 }
147
148 if let Ok(predicted_frame_time) = Duration::try_from_secs_f32(viewport.input.predicted_dt) {
149 delay = delay.saturating_sub(predicted_frame_time);
151 }
152
153 viewport.repaint.causes.push(cause);
154
155 if delay < viewport.repaint.repaint_delay {
159 viewport.repaint.repaint_delay = delay;
160
161 if let Some(callback) = &self.request_repaint_callback {
162 (callback)(RequestRepaintInfo {
163 viewport_id,
164 delay,
165 current_cumulative_pass_nr: viewport.repaint.cumulative_pass_nr,
166 });
167 }
168 }
169 }
170
171 #[must_use]
172 fn requested_immediate_repaint_prev_pass(&self, viewport_id: &ViewportId) -> bool {
173 self.viewports
174 .get(viewport_id)
175 .is_some_and(|v| v.repaint.requested_immediate_repaint_prev_pass())
176 }
177
178 #[must_use]
179 fn has_requested_repaint(&self, viewport_id: &ViewportId) -> bool {
180 self.viewports
181 .get(viewport_id)
182 .is_some_and(|v| 0 < v.repaint.outstanding || v.repaint.repaint_delay < Duration::MAX)
183 }
184}
185
186#[derive(Default)]
193pub struct ViewportState {
194 pub class: ViewportClass,
199
200 pub builder: ViewportBuilder,
202
203 pub viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,
207
208 pub input: InputState,
209
210 pub this_pass: PassState,
212
213 pub prev_pass: PassState,
217
218 pub used: bool,
220
221 repaint: ViewportRepaintInfo,
223
224 pub hits: WidgetHits,
229
230 pub interact_widgets: InteractionSnapshot,
234
235 pub graphics: GraphicLayers,
239 pub output: PlatformOutput,
241 pub commands: Vec<ViewportCommand>,
242
243 pub num_multipass_in_row: usize,
246}
247
248#[derive(Clone, PartialEq, Eq, Hash)]
250pub struct RepaintCause {
251 pub file: &'static str,
253
254 pub line: u32,
256
257 pub reason: Cow<'static, str>,
259}
260
261impl std::fmt::Debug for RepaintCause {
262 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263 write!(f, "{}:{} {}", self.file, self.line, self.reason)
264 }
265}
266
267impl std::fmt::Display for RepaintCause {
268 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269 write!(f, "{}:{} {}", self.file, self.line, self.reason)
270 }
271}
272
273impl RepaintCause {
274 #[expect(clippy::new_without_default)]
276 #[track_caller]
277 pub fn new() -> Self {
278 let caller = Location::caller();
279 Self {
280 file: caller.file(),
281 line: caller.line(),
282 reason: "".into(),
283 }
284 }
285
286 #[track_caller]
289 pub fn new_reason(reason: impl Into<Cow<'static, str>>) -> Self {
290 let caller = Location::caller();
291 Self {
292 file: caller.file(),
293 line: caller.line(),
294 reason: reason.into(),
295 }
296 }
297}
298
299struct ViewportRepaintInfo {
301 cumulative_frame_nr: u64,
307
308 cumulative_pass_nr: u64,
312
313 repaint_delay: Duration,
320
321 outstanding: u8,
323
324 causes: Vec<RepaintCause>,
326
327 prev_causes: Vec<RepaintCause>,
330
331 prev_pass_paint_delay: Duration,
336}
337
338impl Default for ViewportRepaintInfo {
339 fn default() -> Self {
340 Self {
341 cumulative_frame_nr: 0,
342 cumulative_pass_nr: 0,
343
344 repaint_delay: Duration::MAX,
346
347 outstanding: 1,
349
350 causes: Default::default(),
351 prev_causes: Default::default(),
352
353 prev_pass_paint_delay: Duration::MAX,
354 }
355 }
356}
357
358impl ViewportRepaintInfo {
359 pub fn requested_immediate_repaint_prev_pass(&self) -> bool {
360 self.prev_pass_paint_delay == Duration::ZERO
361 }
362}
363
364#[derive(Default)]
367struct ContextImpl {
368 fonts: Option<Fonts>,
369 font_definitions: FontDefinitions,
370
371 memory: Memory,
372 animation_manager: AnimationManager,
373
374 plugins: plugin::Plugins,
375 safe_area: SafeAreaInsets,
376
377 tex_manager: WrappedTextureManager,
384
385 new_zoom_factor: Option<f32>,
387
388 os: OperatingSystem,
389
390 viewport_stack: Vec<ViewportIdPair>,
392
393 last_viewport: ViewportId,
395
396 paint_stats: PaintStats,
397
398 request_repaint_callback: Option<Box<dyn Fn(RequestRepaintInfo) + Send + Sync>>,
399
400 viewport_parents: ViewportIdMap<ViewportId>,
401 viewports: ViewportIdMap<ViewportState>,
402
403 embed_viewports: bool,
404
405 is_accesskit_enabled: bool,
406
407 loaders: Arc<Loaders>,
408}
409
410impl ContextImpl {
411 fn begin_pass(&mut self, mut new_raw_input: RawInput) {
412 let viewport_id = new_raw_input.viewport_id;
413 let parent_id = new_raw_input
414 .viewports
415 .get(&viewport_id)
416 .and_then(|v| v.parent)
417 .unwrap_or_default();
418 let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent_id);
419
420 if let Some(safe_area) = new_raw_input.safe_area_insets {
421 self.safe_area = safe_area;
422 }
423
424 let is_outermost_viewport = self.viewport_stack.is_empty(); self.viewport_stack.push(ids);
426
427 self.begin_pass_repaint_logic(viewport_id);
428
429 let viewport = self.viewports.entry(viewport_id).or_default();
430
431 if is_outermost_viewport && let Some(new_zoom_factor) = self.new_zoom_factor.take() {
432 let ratio = self.memory.options.zoom_factor / new_zoom_factor;
433 self.memory.options.zoom_factor = new_zoom_factor;
434
435 let input = &viewport.input;
436 let mut rect = input.content_rect();
438 rect.min = (ratio * rect.min.to_vec2()).to_pos2();
439 rect.max = (ratio * rect.max.to_vec2()).to_pos2();
440 new_raw_input.screen_rect = Some(rect);
441 }
444 let native_pixels_per_point = new_raw_input
445 .viewport()
446 .native_pixels_per_point
447 .unwrap_or(1.0);
448 let pixels_per_point = self.memory.options.zoom_factor * native_pixels_per_point;
449
450 let all_viewport_ids: ViewportIdSet = self.all_viewport_ids();
451
452 let viewport = self.viewports.entry(self.viewport_id()).or_default();
453
454 self.memory.begin_pass(&new_raw_input, &all_viewport_ids);
455
456 viewport.input = std::mem::take(&mut viewport.input).begin_pass(
457 new_raw_input,
458 viewport.repaint.requested_immediate_repaint_prev_pass(),
459 pixels_per_point,
460 self.memory.options.input_options,
461 );
462 let repaint_after = viewport.input.wants_repaint_after();
463
464 let content_rect = viewport.input.content_rect();
465
466 viewport.this_pass.begin_pass(content_rect);
467
468 {
469 let mut layers: Vec<LayerId> = viewport.prev_pass.widgets.layer_ids().collect();
470 layers.sort_by(|&a, &b| self.memory.areas().compare_order(a, b));
471
472 viewport.hits = if let Some(pos) = viewport.input.pointer.interact_pos() {
473 let interact_radius = self.memory.options.style().interaction.interact_radius;
474
475 crate::hit_test::hit_test(
476 &viewport.prev_pass.widgets,
477 &layers,
478 &self.memory.to_global,
479 pos,
480 interact_radius,
481 )
482 } else {
483 WidgetHits::default()
484 };
485
486 viewport.interact_widgets = crate::interaction::interact(
487 &viewport.interact_widgets,
488 &viewport.prev_pass.widgets,
489 &viewport.hits,
490 &viewport.input,
491 self.memory.interaction_mut(),
492 );
493 }
494
495 self.memory.areas_mut().set_state(
497 LayerId::background(),
498 AreaState {
499 pivot_pos: Some(content_rect.left_top()),
500 pivot: Align2::LEFT_TOP,
501 size: Some(content_rect.size()),
502 interactable: true,
503 last_became_visible_at: None,
504 },
505 );
506
507 if self.is_accesskit_enabled {
508 profiling::scope!("accesskit");
509 use crate::pass_state::AccessKitPassState;
510 let id = crate::accesskit_root_id();
511 let mut root_node = accesskit::Node::new(accesskit::Role::Window);
512 let pixels_per_point = viewport.input.pixels_per_point();
513 root_node.set_transform(accesskit::Affine::scale(pixels_per_point.into()));
514 let mut nodes = IdMap::default();
515 nodes.insert(id, root_node);
516 viewport.this_pass.accesskit_state = Some(AccessKitPassState {
517 nodes,
518 parent_map: IdMap::default(),
519 });
520 }
521
522 self.update_fonts_mut();
523
524 if let Some(delay) = repaint_after {
525 self.request_repaint_after(delay, viewport_id, RepaintCause::new());
526 }
527 }
528
529 fn update_fonts_mut(&mut self) {
531 profiling::function_scope!();
532 let input = &self.viewport().input;
533 let max_texture_side = input.max_texture_side;
534
535 if let Some(font_definitions) = self.memory.new_font_definitions.take() {
536 self.fonts = None;
538 self.font_definitions = font_definitions;
539
540 log::trace!("Loading new font definitions");
541 }
542
543 if !self.memory.add_fonts.is_empty() {
544 let fonts = self.memory.add_fonts.drain(..);
545 for font in fonts {
546 self.fonts = None; for family in font.families {
548 let fam = self
549 .font_definitions
550 .families
551 .entry(family.family)
552 .or_default();
553 match family.priority {
554 FontPriority::Highest => fam.insert(0, font.name.clone()),
555 FontPriority::Lowest => fam.push(font.name.clone()),
556 }
557 }
558 self.font_definitions
559 .font_data
560 .insert(font.name, Arc::new(font.data));
561 }
562
563 log::trace!("Adding new fonts");
564 }
565
566 let Visuals {
567 mut text_options, ..
568 } = self.memory.options.style().visuals;
569 text_options.max_texture_side = max_texture_side;
570
571 let mut is_new = false;
572
573 let fonts = self.fonts.get_or_insert_with(|| {
574 log::trace!("Creating new Fonts");
575
576 is_new = true;
577 profiling::scope!("Fonts::new");
578 Fonts::new(text_options, self.font_definitions.clone())
579 });
580
581 {
582 profiling::scope!("Fonts::begin_pass");
583 fonts.begin_pass(text_options);
584 }
585 }
586
587 fn accesskit_node_builder(&mut self, id: Id) -> Option<&mut accesskit::Node> {
588 let state = self.viewport().this_pass.accesskit_state.as_mut()?;
589 let builders = &mut state.nodes;
590
591 if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) {
592 entry.insert(Default::default());
593
594 fn find_accesskit_parent(
596 parent_map: &IdMap<Id>,
597 node_map: &IdMap<accesskit::Node>,
598 id: Id,
599 ) -> Option<Id> {
600 if let Some(parent_id) = parent_map.get(&id) {
601 if node_map.contains_key(parent_id) {
602 Some(*parent_id)
603 } else {
604 find_accesskit_parent(parent_map, node_map, *parent_id)
605 }
606 } else {
607 None
608 }
609 }
610
611 let parent_id = find_accesskit_parent(&state.parent_map, builders, id)
612 .unwrap_or_else(crate::accesskit_root_id);
613
614 let parent_builder = builders.get_mut(&parent_id)?;
615 parent_builder.push_child(id.accesskit_id());
616 }
617
618 builders.get_mut(&id)
619 }
620
621 fn pixels_per_point(&mut self) -> f32 {
622 self.viewport().input.pixels_per_point
623 }
624
625 pub(crate) fn viewport_id(&self) -> ViewportId {
629 self.viewport_stack.last().copied().unwrap_or_default().this
630 }
631
632 pub(crate) fn parent_viewport_id(&self) -> ViewportId {
636 let viewport_id = self.viewport_id();
637 *self
638 .viewport_parents
639 .get(&viewport_id)
640 .unwrap_or(&ViewportId::ROOT)
641 }
642
643 fn all_viewport_ids(&self) -> ViewportIdSet {
644 self.viewports
645 .keys()
646 .copied()
647 .chain([ViewportId::ROOT])
648 .collect()
649 }
650
651 pub(crate) fn viewport(&mut self) -> &mut ViewportState {
653 self.viewports.entry(self.viewport_id()).or_default()
654 }
655
656 fn viewport_for(&mut self, viewport_id: ViewportId) -> &mut ViewportState {
657 self.viewports.entry(viewport_id).or_default()
658 }
659}
660
661#[derive(Clone)]
714pub struct Context(Arc<RwLock<ContextImpl>>);
715
716impl std::fmt::Debug for Context {
717 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
718 f.debug_struct("Context").finish_non_exhaustive()
719 }
720}
721
722impl std::cmp::PartialEq for Context {
723 fn eq(&self, other: &Self) -> bool {
724 Arc::ptr_eq(&self.0, &other.0)
725 }
726}
727
728impl Default for Context {
729 fn default() -> Self {
730 let ctx_impl = ContextImpl {
731 embed_viewports: true,
732 viewports: std::iter::once((ViewportId::ROOT, ViewportState::default())).collect(),
733 ..Default::default()
734 };
735 let ctx = Self(Arc::new(RwLock::new(ctx_impl)));
736
737 ctx.add_plugin(plugin::CallbackPlugin::default());
738
739 ctx.add_plugin(crate::debug_text::DebugTextPlugin::default());
741 ctx.add_plugin(crate::text_selection::LabelSelectionState::default());
742 ctx.add_plugin(crate::DragAndDrop::default());
743
744 ctx
745 }
746}
747
748impl Context {
749 fn read<R>(&self, reader: impl FnOnce(&ContextImpl) -> R) -> R {
751 reader(&self.0.read())
752 }
753
754 fn write<R>(&self, writer: impl FnOnce(&mut ContextImpl) -> R) -> R {
756 writer(&mut self.0.write())
757 }
758
759 #[must_use]
787 pub fn run_ui(&self, new_input: RawInput, mut run_ui: impl FnMut(&mut Ui)) -> FullOutput {
788 self.run_ui_dyn(new_input, &mut run_ui)
789 }
790
791 #[must_use]
792 fn run_ui_dyn(&self, new_input: RawInput, run_ui: &mut dyn FnMut(&mut Ui)) -> FullOutput {
793 let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
794 #[expect(deprecated)]
795 self.run(new_input, |ctx| {
796 let mut root_ui = Ui::new(
797 ctx.clone(),
798 Id::new((ctx.viewport_id(), "__top_ui")),
799 UiBuilder::new()
800 .layer_id(LayerId::background())
801 .max_rect(ctx.available_rect()),
802 );
803
804 {
805 plugins.on_begin_pass(&mut root_ui);
806 run_ui(&mut root_ui);
807 plugins.on_end_pass(&mut root_ui);
808 }
809
810 ctx.pass_state_mut(|state| {
811 state.root_ui_available_rect = Some(root_ui.available_rect_before_wrap());
812 state.root_ui_min_rect = Some(root_ui.min_rect());
813 });
814 })
815 }
816
817 #[must_use]
844 #[deprecated = "Call run_ui instead"]
845 pub fn run(&self, new_input: RawInput, mut run_ui: impl FnMut(&Self)) -> FullOutput {
846 self.run_dyn(new_input, &mut run_ui)
847 }
848
849 #[must_use]
850 fn run_dyn(&self, mut new_input: RawInput, run_ui: &mut dyn FnMut(&Self)) -> FullOutput {
851 profiling::function_scope!();
852 let viewport_id = new_input.viewport_id;
853 let max_passes = self.write(|ctx| ctx.memory.options.max_passes.get());
854
855 let mut output = FullOutput::default();
856 debug_assert_eq!(
857 output.platform_output.num_completed_passes, 0,
858 "output must be fresh, but had {} passes",
859 output.platform_output.num_completed_passes
860 );
861
862 loop {
863 profiling::scope!(
864 "pass",
865 output
866 .platform_output
867 .num_completed_passes
868 .to_string()
869 .as_str()
870 );
871
872 self.write(|ctx| {
875 let viewport = ctx.viewport_for(viewport_id);
876 viewport.output.num_completed_passes =
877 std::mem::take(&mut output.platform_output.num_completed_passes);
878 output.platform_output.request_discard_reasons.clear();
879 });
880
881 self.begin_pass(new_input.take());
882 run_ui(self);
883 output.append(self.end_pass());
884 debug_assert!(
885 0 < output.platform_output.num_completed_passes,
886 "Completed passes was lower than 0, was {}",
887 output.platform_output.num_completed_passes
888 );
889
890 if !output.platform_output.requested_discard() {
891 break; }
893
894 if max_passes <= output.platform_output.num_completed_passes {
895 log::debug!(
896 "Ignoring call request_discard, because max_passes={max_passes}. Requested from {:?}",
897 output.platform_output.request_discard_reasons
898 );
899
900 break;
901 }
902 }
903
904 self.write(|ctx| {
905 let did_multipass = 1 < output.platform_output.num_completed_passes;
906 let viewport = ctx.viewport_for(viewport_id);
907 if did_multipass {
908 viewport.num_multipass_in_row += 1;
909 } else {
910 viewport.num_multipass_in_row = 0;
911 }
912 viewport.repaint.cumulative_frame_nr += 1;
913 });
914
915 output
916 }
917
918 pub fn begin_pass(&self, mut new_input: RawInput) {
939 profiling::function_scope!();
940
941 let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
942 plugins.on_input(&mut new_input);
943
944 self.write(|ctx| ctx.begin_pass(new_input));
945 }
946
947 #[deprecated = "Renamed begin_pass"]
949 pub fn begin_frame(&self, new_input: RawInput) {
950 self.begin_pass(new_input);
951 }
952}
953
954impl Context {
958 #[inline]
973 pub fn input<R>(&self, reader: impl FnOnce(&InputState) -> R) -> R {
974 self.write(move |ctx| reader(&ctx.viewport().input))
975 }
976
977 #[inline]
979 pub fn input_for<R>(&self, id: ViewportId, reader: impl FnOnce(&InputState) -> R) -> R {
980 self.write(move |ctx| reader(&ctx.viewport_for(id).input))
981 }
982
983 #[inline]
985 pub fn input_mut<R>(&self, writer: impl FnOnce(&mut InputState) -> R) -> R {
986 self.input_mut_for(self.viewport_id(), writer)
987 }
988
989 #[inline]
991 pub fn input_mut_for<R>(&self, id: ViewportId, writer: impl FnOnce(&mut InputState) -> R) -> R {
992 self.write(move |ctx| writer(&mut ctx.viewport_for(id).input))
993 }
994
995 #[inline]
997 pub fn memory<R>(&self, reader: impl FnOnce(&Memory) -> R) -> R {
998 self.read(move |ctx| reader(&ctx.memory))
999 }
1000
1001 #[inline]
1003 pub fn memory_mut<R>(&self, writer: impl FnOnce(&mut Memory) -> R) -> R {
1004 self.write(move |ctx| writer(&mut ctx.memory))
1005 }
1006
1007 #[inline]
1009 pub fn data<R>(&self, reader: impl FnOnce(&IdTypeMap) -> R) -> R {
1010 self.read(move |ctx| reader(&ctx.memory.data))
1011 }
1012
1013 #[inline]
1015 pub fn data_mut<R>(&self, writer: impl FnOnce(&mut IdTypeMap) -> R) -> R {
1016 self.write(move |ctx| writer(&mut ctx.memory.data))
1017 }
1018
1019 #[inline]
1021 pub fn graphics_mut<R>(&self, writer: impl FnOnce(&mut GraphicLayers) -> R) -> R {
1022 self.write(move |ctx| writer(&mut ctx.viewport().graphics))
1023 }
1024
1025 #[inline]
1027 pub fn graphics<R>(&self, reader: impl FnOnce(&GraphicLayers) -> R) -> R {
1028 self.write(move |ctx| reader(&ctx.viewport().graphics))
1029 }
1030
1031 #[inline]
1040 pub fn output<R>(&self, reader: impl FnOnce(&PlatformOutput) -> R) -> R {
1041 self.write(move |ctx| reader(&ctx.viewport().output))
1042 }
1043
1044 #[inline]
1046 pub fn output_mut<R>(&self, writer: impl FnOnce(&mut PlatformOutput) -> R) -> R {
1047 self.write(move |ctx| writer(&mut ctx.viewport().output))
1048 }
1049
1050 #[inline]
1054 pub(crate) fn pass_state<R>(&self, reader: impl FnOnce(&PassState) -> R) -> R {
1055 self.write(move |ctx| reader(&ctx.viewport().this_pass))
1056 }
1057
1058 #[inline]
1062 pub(crate) fn pass_state_mut<R>(&self, writer: impl FnOnce(&mut PassState) -> R) -> R {
1063 self.write(move |ctx| writer(&mut ctx.viewport().this_pass))
1064 }
1065
1066 #[inline]
1070 pub(crate) fn prev_pass_state<R>(&self, reader: impl FnOnce(&PassState) -> R) -> R {
1071 self.write(move |ctx| reader(&ctx.viewport().prev_pass))
1072 }
1073
1074 #[inline]
1079 pub fn fonts<R>(&self, reader: impl FnOnce(&FontsView<'_>) -> R) -> R {
1080 self.write(move |ctx| {
1081 let pixels_per_point = ctx.pixels_per_point();
1082 reader(
1083 &ctx.fonts
1084 .as_mut()
1085 .expect("No fonts available until first call to Context::run()")
1086 .with_pixels_per_point(pixels_per_point),
1087 )
1088 })
1089 }
1090
1091 #[inline]
1096 pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut FontsView<'_>) -> R) -> R {
1097 self.write(move |ctx| {
1098 let pixels_per_point = ctx.pixels_per_point();
1099 reader(
1100 &mut ctx
1101 .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]
1111 pub fn options<R>(&self, reader: impl FnOnce(&Options) -> R) -> R {
1112 self.read(move |ctx| reader(&ctx.memory.options))
1113 }
1114
1115 #[inline]
1117 pub fn options_mut<R>(&self, writer: impl FnOnce(&mut Options) -> R) -> R {
1118 self.write(move |ctx| writer(&mut ctx.memory.options))
1119 }
1120
1121 #[inline]
1123 pub fn tessellation_options<R>(&self, reader: impl FnOnce(&TessellationOptions) -> R) -> R {
1124 self.read(move |ctx| reader(&ctx.memory.options.tessellation_options))
1125 }
1126
1127 #[inline]
1129 pub fn tessellation_options_mut<R>(
1130 &self,
1131 writer: impl FnOnce(&mut TessellationOptions) -> R,
1132 ) -> R {
1133 self.write(move |ctx| writer(&mut ctx.memory.options.tessellation_options))
1134 }
1135
1136 pub fn check_for_id_clash(&self, id: Id, new_rect: Rect, what: &str) {
1146 let prev_rect = self.pass_state_mut(move |state| state.used_ids.insert(id, new_rect));
1147
1148 if !self.options(|opt| opt.warn_on_id_clash) {
1149 return;
1150 }
1151
1152 let Some(prev_rect) = prev_rect else { return };
1153
1154 let is_same_rect = prev_rect.expand(0.1).contains_rect(new_rect)
1157 || new_rect.expand(0.1).contains_rect(prev_rect);
1158 if is_same_rect {
1159 return;
1160 }
1161
1162 let show_error = |widget_rect: Rect, text: String| {
1163 let content_rect = self.content_rect();
1164
1165 let text = format!("🔥 {text}");
1166 let color = self.global_style().visuals.error_fg_color;
1167 let painter = self.debug_painter();
1168 painter.rect_stroke(widget_rect, 0.0, (1.0, color), StrokeKind::Outside);
1169
1170 let below = widget_rect.bottom() + 32.0 < content_rect.bottom();
1171
1172 let text_rect = if below {
1173 painter.debug_text(
1174 widget_rect.left_bottom() + vec2(0.0, 2.0),
1175 Align2::LEFT_TOP,
1176 color,
1177 text,
1178 )
1179 } else {
1180 painter.debug_text(
1181 widget_rect.left_top() - vec2(0.0, 2.0),
1182 Align2::LEFT_BOTTOM,
1183 color,
1184 text,
1185 )
1186 };
1187
1188 if let Some(pointer_pos) = self.pointer_hover_pos()
1189 && text_rect.contains(pointer_pos)
1190 {
1191 let tooltip_pos = if below {
1192 text_rect.left_bottom() + vec2(2.0, 4.0)
1193 } else {
1194 text_rect.left_top() + vec2(2.0, -4.0)
1195 };
1196
1197 painter.error(
1198 tooltip_pos,
1199 format!("Widget is {} this text.\n\n\
1200 ID clashes happens when things like Windows or CollapsingHeaders share names,\n\
1201 or when things like Plot and Grid:s aren't given unique id_salt:s.\n\n\
1202 Sometimes the solution is to use ui.push_id.",
1203 if below { "above" } else { "below" }),
1204 );
1205 }
1206 };
1207
1208 let id_str = id.short_debug_format();
1209
1210 if prev_rect.min.distance(new_rect.min) < 4.0 {
1211 show_error(new_rect, format!("Double use of {what} ID {id_str}"));
1212 } else {
1213 show_error(prev_rect, format!("First use of {what} ID {id_str}"));
1214 show_error(new_rect, format!("Second use of {what} ID {id_str}"));
1215 }
1216 }
1217
1218 pub(crate) fn create_widget(
1231 &self,
1232 w: WidgetRect,
1233 allow_focus: bool,
1234 options: crate::InteractOptions,
1235 ) -> Response {
1236 let interested_in_focus = w.enabled
1237 && w.sense.is_focusable()
1238 && self.memory(|mem| mem.allows_interaction(w.layer_id));
1239
1240 self.write(|ctx| {
1242 let viewport = ctx.viewport();
1243
1244 viewport.this_pass.widgets.insert(w.layer_id, w, options);
1248
1249 if allow_focus && interested_in_focus {
1250 ctx.memory.interested_in_focus(w.id, w.layer_id);
1251 }
1252 });
1253
1254 if allow_focus && !interested_in_focus {
1255 self.memory_mut(|mem| mem.surrender_focus(w.id));
1257 }
1258
1259 if w.sense.interactive() || w.sense.is_focusable() {
1260 self.check_for_id_clash(w.id, w.rect, "widget");
1261 }
1262
1263 #[allow(clippy::allow_attributes, clippy::let_and_return)]
1264 let res = self.get_response(w);
1265
1266 #[cfg(debug_assertions)]
1267 if res.contains_pointer() {
1268 let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
1269 plugins.on_widget_under_pointer(self, &w);
1270 }
1271
1272 if allow_focus && w.sense.is_focusable() {
1273 self.accesskit_node_builder(w.id, |builder| res.fill_accesskit_node_common(builder));
1277 }
1278
1279 self.write(|ctx| {
1280 use crate::{Align, pass_state::ScrollTarget, style::ScrollAnimation};
1281 let viewport = ctx.viewport_for(ctx.viewport_id());
1282
1283 viewport
1284 .input
1285 .consume_accesskit_action_requests(res.id, |request| {
1286 use accesskit::Action;
1287
1288 const DISTANCE: f32 = 100.0;
1291
1292 match &request.action {
1293 Action::ScrollIntoView => {
1294 viewport.this_pass.scroll_target = [
1295 Some(ScrollTarget::new(
1296 res.rect.x_range(),
1297 Some(Align::Center),
1298 ScrollAnimation::none(),
1299 )),
1300 Some(ScrollTarget::new(
1301 res.rect.y_range(),
1302 Some(Align::Center),
1303 ScrollAnimation::none(),
1304 )),
1305 ];
1306 }
1307 Action::ScrollDown => {
1308 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::UP;
1309 }
1310 Action::ScrollUp => {
1311 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::DOWN;
1312 }
1313 Action::ScrollLeft => {
1314 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::LEFT;
1315 }
1316 Action::ScrollRight => {
1317 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::RIGHT;
1318 }
1319 _ => return false,
1320 }
1321 true
1322 });
1323 });
1324
1325 res
1326 }
1327
1328 pub fn read_response(&self, id: Id) -> Option<Response> {
1336 self.write(|ctx| {
1337 let viewport = ctx.viewport();
1338 let widget_rect = viewport
1339 .this_pass
1340 .widgets
1341 .get(id)
1342 .or_else(|| viewport.prev_pass.widgets.get(id))
1343 .copied();
1344 widget_rect.map(|mut rect| {
1345 if !(rect.rect.is_positive() && rect.rect.is_finite())
1348 && let Some(prev_rect) = viewport.prev_pass.widgets.get(id)
1349 {
1350 rect.rect = prev_rect.rect;
1351 }
1352 rect
1353 })
1354 })
1355 .map(|widget_rect| self.get_response(widget_rect))
1356 }
1357
1358 pub(crate) fn get_response(&self, widget_rect: WidgetRect) -> Response {
1360 use response::Flags;
1361
1362 let WidgetRect {
1363 id,
1364 parent_id: _,
1365 layer_id,
1366 rect,
1367 interact_rect,
1368 sense,
1369 enabled,
1370 } = widget_rect;
1371
1372 let highlighted = self.prev_pass_state(|fs| fs.highlight_next_pass.contains(&id));
1374
1375 let mut res = Response {
1376 ctx: self.clone(),
1377 layer_id,
1378 id,
1379 rect,
1380 interact_rect,
1381 sense,
1382 flags: Flags::empty(),
1383 interact_pointer_pos_or_nan: Pos2::NAN,
1384 intrinsic_size_or_nan: Vec2::NAN,
1385 };
1386
1387 res.flags.set(Flags::ENABLED, enabled);
1388 res.flags.set(Flags::HIGHLIGHTED, highlighted);
1389
1390 self.write(|ctx| {
1391 let viewport = ctx.viewports.entry(ctx.viewport_id()).or_default();
1392
1393 res.flags.set(
1394 Flags::CONTAINS_POINTER,
1395 viewport.interact_widgets.contains_pointer.contains(&id),
1396 );
1397
1398 let input = &viewport.input;
1399 let memory = &mut ctx.memory;
1400
1401 if enabled
1402 && sense.senses_click()
1403 && memory.has_focus(id)
1404 && (input.key_pressed(Key::Space) || input.key_pressed(Key::Enter))
1405 {
1406 res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);
1408 }
1409
1410 if enabled
1411 && sense.senses_click()
1412 && input.has_accesskit_action_request(id, accesskit::Action::Click)
1413 {
1414 res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);
1415 }
1416
1417 if enabled && sense.senses_click() && Some(id) == viewport.interact_widgets.long_touched
1418 {
1419 res.flags.set(Flags::LONG_TOUCHED, true);
1420 }
1421
1422 let interaction = memory.interaction();
1423
1424 res.flags.set(
1425 Flags::IS_POINTER_BUTTON_DOWN_ON,
1426 interaction.potential_click_id == Some(id)
1427 || interaction.potential_drag_id == Some(id),
1428 );
1429
1430 if res.enabled() {
1431 res.flags.set(
1432 Flags::HOVERED,
1433 viewport.interact_widgets.hovered.contains(&id),
1434 );
1435 res.flags.set(
1436 Flags::DRAGGED,
1437 Some(id) == viewport.interact_widgets.dragged,
1438 );
1439 res.flags.set(
1440 Flags::DRAG_STARTED,
1441 Some(id) == viewport.interact_widgets.drag_started,
1442 );
1443 res.flags.set(
1444 Flags::DRAG_STOPPED,
1445 Some(id) == viewport.interact_widgets.drag_stopped,
1446 );
1447 }
1448
1449 let clicked = Some(id) == viewport.interact_widgets.clicked;
1450 let mut any_press = false;
1451
1452 for pointer_event in &input.pointer.pointer_events {
1453 match pointer_event {
1454 PointerEvent::Moved(_) => {}
1455 PointerEvent::Pressed { .. } => {
1456 any_press = true;
1457 }
1458 PointerEvent::Released { click, .. } => {
1459 if enabled && sense.senses_click() && clicked && click.is_some() {
1460 res.flags.set(Flags::CLICKED, true);
1461 }
1462
1463 res.flags.set(Flags::IS_POINTER_BUTTON_DOWN_ON, false);
1464 res.flags.set(Flags::DRAGGED, false);
1465 }
1466 }
1467 }
1468
1469 let is_interacted_with = res.is_pointer_button_down_on()
1472 || res.long_touched()
1473 || clicked
1474 || res.drag_stopped();
1475 if is_interacted_with && let Some(mut pos) = input.pointer.interact_pos() {
1476 if let Some(to_global) = memory.to_global.get(&res.layer_id) {
1477 pos = to_global.inverse() * pos;
1478 }
1479 res.interact_pointer_pos_or_nan = pos;
1480 }
1481
1482 if input.pointer.any_down() && !is_interacted_with {
1483 res.flags.set(Flags::HOVERED, false);
1485 }
1486
1487 let should_surrender_focus = match memory.options.input_options.surrender_focus_on {
1488 SurrenderFocusOn::Presses => any_press,
1489 SurrenderFocusOn::Clicks => input.pointer.any_click(),
1490 SurrenderFocusOn::Never => false,
1491 };
1492
1493 let pointer_clicked_elsewhere = should_surrender_focus && !res.hovered();
1494 if pointer_clicked_elsewhere && memory.has_focus(id) {
1495 memory.surrender_focus(id);
1496 }
1497 });
1498
1499 res
1500 }
1501
1502 #[inline]
1506 pub fn register_widget_info(&self, id: Id, make_info: impl Fn() -> crate::WidgetInfo) {
1507 #[cfg(debug_assertions)]
1508 self.write(|ctx| {
1509 if ctx.memory.options.style().debug.show_interactive_widgets {
1510 ctx.viewport().this_pass.widgets.set_info(id, make_info());
1511 }
1512 });
1513
1514 #[cfg(not(debug_assertions))]
1515 {
1516 _ = (self, id, make_info);
1517 }
1518 }
1519
1520 pub fn layer_painter(&self, layer_id: LayerId) -> Painter {
1522 let content_rect = self.content_rect();
1523 Painter::new(self.clone(), layer_id, content_rect)
1524 }
1525
1526 pub fn debug_painter(&self) -> Painter {
1528 Self::layer_painter(self, LayerId::debug())
1529 }
1530
1531 #[track_caller]
1545 pub fn debug_text(&self, text: impl Into<WidgetText>) {
1546 crate::debug_text::print(self, text);
1547 }
1548
1549 pub fn time(&self) -> f64 {
1551 self.input(|i| i.time)
1552 }
1553
1554 pub fn os(&self) -> OperatingSystem {
1562 self.read(|ctx| ctx.os)
1563 }
1564
1565 pub fn set_os(&self, os: OperatingSystem) {
1570 self.write(|ctx| ctx.os = os);
1571 }
1572
1573 pub fn set_cursor_icon(&self, cursor_icon: CursorIcon) {
1581 self.output_mut(|o| o.cursor_icon = cursor_icon);
1582 }
1583
1584 pub fn send_cmd(&self, cmd: crate::OutputCommand) {
1587 self.output_mut(|o| o.commands.push(cmd));
1588 }
1589
1590 pub fn open_url(&self, open_url: crate::OpenUrl) {
1599 self.send_cmd(crate::OutputCommand::OpenUrl(open_url));
1600 }
1601
1602 pub fn copy_text(&self, text: String) {
1608 self.send_cmd(crate::OutputCommand::CopyText(text));
1609 }
1610
1611 pub fn copy_image(&self, image: crate::ColorImage) {
1617 self.send_cmd(crate::OutputCommand::CopyImage(image));
1618 }
1619
1620 fn can_show_modifier_symbols(&self) -> bool {
1621 let ModifierNames {
1622 alt,
1623 ctrl,
1624 shift,
1625 mac_cmd,
1626 ..
1627 } = ModifierNames::SYMBOLS;
1628
1629 let font_id = TextStyle::Body.resolve(&self.global_style());
1630 self.fonts_mut(|f| {
1631 let mut font = f.fonts.font(&font_id.family);
1632 font.has_glyphs(alt)
1633 && font.has_glyphs(ctrl)
1634 && font.has_glyphs(shift)
1635 && font.has_glyphs(mac_cmd)
1636 })
1637 }
1638
1639 pub fn format_modifiers(&self, modifiers: Modifiers) -> String {
1641 let os = self.os();
1642
1643 let is_mac = os.is_mac();
1644
1645 if is_mac && self.can_show_modifier_symbols() {
1646 ModifierNames::SYMBOLS.format(&modifiers, is_mac)
1647 } else {
1648 ModifierNames::NAMES.format(&modifiers, is_mac)
1649 }
1650 }
1651
1652 pub fn format_shortcut(&self, shortcut: &KeyboardShortcut) -> String {
1656 let os = self.os();
1657
1658 let is_mac = os.is_mac();
1659
1660 if is_mac && self.can_show_modifier_symbols() {
1661 shortcut.format(&ModifierNames::SYMBOLS, is_mac)
1662 } else {
1663 shortcut.format(&ModifierNames::NAMES, is_mac)
1664 }
1665 }
1666
1667 pub fn cumulative_frame_nr(&self) -> u64 {
1673 self.cumulative_frame_nr_for(self.viewport_id())
1674 }
1675
1676 pub fn cumulative_frame_nr_for(&self, id: ViewportId) -> u64 {
1682 self.read(|ctx| {
1683 ctx.viewports
1684 .get(&id)
1685 .map(|v| v.repaint.cumulative_frame_nr)
1686 .unwrap_or_else(|| {
1687 if cfg!(debug_assertions) {
1688 panic!("cumulative_frame_nr_for failed to find the viewport {id:?}");
1689 } else {
1690 0
1691 }
1692 })
1693 })
1694 }
1695
1696 pub fn cumulative_pass_nr(&self) -> u64 {
1703 self.cumulative_pass_nr_for(self.viewport_id())
1704 }
1705
1706 pub fn cumulative_pass_nr_for(&self, id: ViewportId) -> u64 {
1710 self.read(|ctx| {
1711 ctx.viewports
1712 .get(&id)
1713 .map_or(0, |v| v.repaint.cumulative_pass_nr)
1714 })
1715 }
1716
1717 pub fn current_pass_index(&self) -> usize {
1726 self.output(|o| o.num_completed_passes)
1727 }
1728
1729 #[track_caller]
1742 pub fn request_repaint(&self) {
1743 self.request_repaint_of(self.viewport_id());
1744 }
1745
1746 #[track_caller]
1759 pub fn request_repaint_of(&self, id: ViewportId) {
1760 let cause = RepaintCause::new();
1761 self.write(|ctx| ctx.request_repaint(id, cause));
1762 }
1763
1764 #[track_caller]
1793 pub fn request_repaint_after(&self, duration: Duration) {
1794 self.request_repaint_after_for(duration, self.viewport_id());
1795 }
1796
1797 #[track_caller]
1801 pub fn request_repaint_after_secs(&self, seconds: f32) {
1802 if let Ok(duration) = std::time::Duration::try_from_secs_f32(seconds) {
1803 self.request_repaint_after(duration);
1804 }
1805 }
1806
1807 #[track_caller]
1836 pub fn request_repaint_after_for(&self, duration: Duration, id: ViewportId) {
1837 let cause = RepaintCause::new();
1838 self.write(|ctx| ctx.request_repaint_after(duration, id, cause));
1839 }
1840
1841 #[must_use]
1843 pub fn requested_repaint_last_pass(&self) -> bool {
1844 self.requested_repaint_last_pass_for(&self.viewport_id())
1845 }
1846
1847 #[must_use]
1849 pub fn requested_repaint_last_pass_for(&self, viewport_id: &ViewportId) -> bool {
1850 self.read(|ctx| ctx.requested_immediate_repaint_prev_pass(viewport_id))
1851 }
1852
1853 #[must_use]
1855 pub fn has_requested_repaint(&self) -> bool {
1856 self.has_requested_repaint_for(&self.viewport_id())
1857 }
1858
1859 #[must_use]
1861 pub fn has_requested_repaint_for(&self, viewport_id: &ViewportId) -> bool {
1862 self.read(|ctx| ctx.has_requested_repaint(viewport_id))
1863 }
1864
1865 pub fn repaint_causes(&self) -> Vec<RepaintCause> {
1869 self.read(|ctx| {
1870 ctx.viewports
1871 .get(&ctx.viewport_id())
1872 .map(|v| v.repaint.prev_causes.clone())
1873 })
1874 .unwrap_or_default()
1875 }
1876
1877 pub fn set_request_repaint_callback(
1883 &self,
1884 callback: impl Fn(RequestRepaintInfo) + Send + Sync + 'static,
1885 ) {
1886 let callback = Box::new(callback);
1887 self.write(|ctx| ctx.request_repaint_callback = Some(callback));
1888 }
1889
1890 #[track_caller]
1913 pub fn request_discard(&self, reason: impl Into<Cow<'static, str>>) {
1914 let cause = RepaintCause::new_reason(reason);
1915 self.output_mut(|o| o.request_discard_reasons.push(cause));
1916
1917 log::trace!(
1918 "request_discard: {}",
1919 if self.will_discard() {
1920 "allowed"
1921 } else {
1922 "denied"
1923 }
1924 );
1925 }
1926
1927 pub fn will_discard(&self) -> bool {
1933 self.write(|ctx| {
1934 let vp = ctx.viewport();
1935 vp.output.requested_discard()
1937 && vp.output.num_completed_passes + 1 < ctx.memory.options.max_passes.get()
1938 })
1939 }
1940}
1941
1942impl Context {
1944 pub fn on_begin_pass(&self, debug_name: &'static str, cb: plugin::ContextCallback) {
1948 self.with_plugin(|p: &mut crate::plugin::CallbackPlugin| {
1949 p.on_begin_plugins.push((debug_name, cb));
1950 });
1951 }
1952
1953 pub fn on_end_pass(&self, debug_name: &'static str, cb: plugin::ContextCallback) {
1957 self.with_plugin(|p: &mut crate::plugin::CallbackPlugin| {
1958 p.on_end_plugins.push((debug_name, cb));
1959 });
1960 }
1961
1962 pub fn add_plugin(&self, plugin: impl plugin::Plugin + 'static) {
1969 let handle = plugin::PluginHandle::new(plugin);
1970
1971 let added = self.write(|ctx| ctx.plugins.add(Arc::clone(&handle)));
1972
1973 if added {
1974 handle.lock().dyn_plugin_mut().setup(self);
1975 }
1976 }
1977
1978 pub fn with_plugin<T: plugin::Plugin + 'static, R>(
1982 &self,
1983 f: impl FnOnce(&mut T) -> R,
1984 ) -> Option<R> {
1985 let plugin = self.read(|ctx| ctx.plugins.get(std::any::TypeId::of::<T>()));
1986 plugin.map(|plugin| f(plugin.lock().typed_plugin_mut()))
1987 }
1988
1989 pub fn plugin<T: plugin::Plugin>(&self) -> TypedPluginHandle<T> {
1994 if let Some(plugin) = self.plugin_opt() {
1995 plugin
1996 } else {
1997 panic!("Plugin of type {:?} not found", std::any::type_name::<T>());
1998 }
1999 }
2000
2001 pub fn plugin_opt<T: plugin::Plugin>(&self) -> Option<TypedPluginHandle<T>> {
2003 let plugin = self.read(|ctx| ctx.plugins.get(std::any::TypeId::of::<T>()));
2004 plugin.map(TypedPluginHandle::new)
2005 }
2006
2007 pub fn plugin_or_default<T: plugin::Plugin + Default>(&self) -> TypedPluginHandle<T> {
2009 if let Some(plugin) = self.plugin_opt() {
2010 plugin
2011 } else {
2012 let default_plugin = T::default();
2013 self.add_plugin(default_plugin);
2014 self.plugin()
2015 }
2016 }
2017}
2018
2019impl Context {
2020 pub fn set_fonts(&self, font_definitions: FontDefinitions) {
2028 profiling::function_scope!();
2029
2030 let update_fonts = self.read(|ctx| {
2031 ctx.fonts
2034 .as_ref()
2035 .is_none_or(|fonts| fonts.definitions() != &font_definitions)
2036 });
2037
2038 if update_fonts {
2039 self.memory_mut(|mem| mem.new_font_definitions = Some(font_definitions));
2040 }
2041 }
2042
2043 pub fn add_font(&self, new_font: FontInsert) {
2051 profiling::function_scope!();
2052
2053 let mut update_fonts = true;
2054
2055 self.read(|ctx| {
2056 if let Some(current_fonts) = ctx.fonts.as_ref()
2057 && current_fonts
2058 .definitions()
2059 .font_data
2060 .contains_key(&new_font.name)
2061 {
2062 update_fonts = false; }
2064 });
2065
2066 if update_fonts {
2067 self.memory_mut(|mem| mem.add_fonts.push(new_font));
2068 }
2069 }
2070
2071 pub fn system_theme(&self) -> Option<Theme> {
2074 self.memory(|mem| mem.options.system_theme)
2075 }
2076
2077 pub fn theme(&self) -> Theme {
2080 self.options(|opt| opt.theme())
2081 }
2082
2083 pub fn set_theme(&self, theme_preference: impl Into<crate::ThemePreference>) {
2092 self.options_mut(|opt| opt.theme_preference = theme_preference.into());
2093 }
2094
2095 pub fn global_style(&self) -> Arc<Style> {
2097 self.options(|opt| Arc::clone(opt.style()))
2098 }
2099
2100 #[deprecated = "Renamed to `global_style` to avoid confusion with `ui.style()`"]
2102 pub fn style(&self) -> Arc<Style> {
2103 self.options(|opt| Arc::clone(opt.style()))
2104 }
2105
2106 pub fn global_style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {
2117 self.options_mut(|opt| mutate_style(Arc::make_mut(opt.style_mut())));
2118 }
2119
2120 #[deprecated = "Renamed to `global_style_mut` to avoid confusion with `ui.style_mut()`"]
2131 pub fn style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {
2132 self.options_mut(|opt| mutate_style(Arc::make_mut(opt.style_mut())));
2133 }
2134
2135 pub fn set_global_style(&self, style: impl Into<Arc<Style>>) {
2143 self.options_mut(|opt| *opt.style_mut() = style.into());
2144 }
2145
2146 #[deprecated = "Renamed to `set_global_style` to avoid confusion with `ui.set_style()`"]
2154 pub fn set_style(&self, style: impl Into<Arc<Style>>) {
2155 self.options_mut(|opt| *opt.style_mut() = style.into());
2156 }
2157
2158 pub fn all_styles_mut(&self, mut mutate_style: impl FnMut(&mut Style)) {
2168 self.options_mut(|opt| {
2169 mutate_style(Arc::make_mut(&mut opt.dark_style));
2170 mutate_style(Arc::make_mut(&mut opt.light_style));
2171 });
2172 }
2173
2174 pub fn style_of(&self, theme: Theme) -> Arc<Style> {
2176 self.options(|opt| match theme {
2177 Theme::Dark => Arc::clone(&opt.dark_style),
2178 Theme::Light => Arc::clone(&opt.light_style),
2179 })
2180 }
2181
2182 pub fn style_mut_of(&self, theme: Theme, mutate_style: impl FnOnce(&mut Style)) {
2192 self.options_mut(|opt| match theme {
2193 Theme::Dark => mutate_style(Arc::make_mut(&mut opt.dark_style)),
2194 Theme::Light => mutate_style(Arc::make_mut(&mut opt.light_style)),
2195 });
2196 }
2197
2198 pub fn set_style_of(&self, theme: Theme, style: impl Into<Arc<Style>>) {
2205 let style = style.into();
2206 self.options_mut(|opt| match theme {
2207 Theme::Dark => opt.dark_style = style,
2208 Theme::Light => opt.light_style = style,
2209 });
2210 }
2211
2212 pub fn set_visuals_of(&self, theme: Theme, visuals: crate::Visuals) {
2222 self.style_mut_of(theme, |style| style.visuals = visuals);
2223 }
2224
2225 pub fn set_visuals(&self, visuals: crate::Visuals) {
2235 self.style_mut_of(self.theme(), |style| style.visuals = visuals);
2236 }
2237
2238 #[inline(always)]
2242 pub fn pixels_per_point(&self) -> f32 {
2243 self.input(|i| i.pixels_per_point)
2244 }
2245
2246 pub fn set_pixels_per_point(&self, pixels_per_point: f32) {
2251 if pixels_per_point != self.pixels_per_point() {
2252 self.set_zoom_factor(pixels_per_point / self.native_pixels_per_point().unwrap_or(1.0));
2253 }
2254 }
2255
2256 #[inline(always)]
2261 pub fn native_pixels_per_point(&self) -> Option<f32> {
2262 self.input(|i| i.viewport().native_pixels_per_point)
2263 }
2264
2265 #[inline(always)]
2273 pub fn zoom_factor(&self) -> f32 {
2274 self.options(|o| o.zoom_factor)
2275 }
2276
2277 #[inline(always)]
2291 pub fn set_zoom_factor(&self, zoom_factor: f32) {
2292 let cause = RepaintCause::new();
2293 self.write(|ctx| {
2294 if ctx.memory.options.zoom_factor != zoom_factor {
2295 ctx.new_zoom_factor = Some(zoom_factor);
2296 #[expect(clippy::iter_over_hash_type)]
2297 for viewport_id in ctx.all_viewport_ids() {
2298 ctx.request_repaint(viewport_id, cause.clone());
2299 }
2300 }
2301 });
2302 }
2303
2304 pub fn load_texture(
2345 &self,
2346 name: impl Into<String>,
2347 image: impl Into<ImageData>,
2348 options: TextureOptions,
2349 ) -> TextureHandle {
2350 let name = name.into();
2351 let image = image.into();
2352 let max_texture_side = self.input(|i| i.max_texture_side);
2353 debug_assert!(
2354 image.width() <= max_texture_side && image.height() <= max_texture_side,
2355 "Texture {:?} has size {}x{}, but the maximum texture side is {}",
2356 name,
2357 image.width(),
2358 image.height(),
2359 max_texture_side
2360 );
2361 let tex_mngr = self.tex_manager();
2362 let tex_id = tex_mngr.write().alloc(name, image, options);
2363 TextureHandle::new(tex_mngr, tex_id)
2364 }
2365
2366 pub fn tex_manager(&self) -> Arc<RwLock<epaint::textures::TextureManager>> {
2372 self.read(|ctx| Arc::clone(&ctx.tex_manager.0))
2373 }
2374
2375 pub(crate) fn constrain_window_rect_to_area(window: Rect, area: Rect) -> Rect {
2379 let mut pos = window.min;
2380
2381 let margin_x = (window.width() - area.width()).at_least(0.0);
2383 let margin_y = (window.height() - area.height()).at_least(0.0);
2384
2385 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()
2391 }
2392}
2393
2394impl Context {
2395 #[must_use]
2397 pub fn end_pass(&self) -> FullOutput {
2398 profiling::function_scope!();
2399
2400 if self.options(|o| o.zoom_with_keyboard) {
2401 crate::gui_zoom::zoom_with_keyboard(self);
2402 }
2403
2404 for shortcut in self.options(|o| o.quit_shortcuts.clone()) {
2405 if self.input_mut(|i| i.consume_shortcut(&shortcut)) {
2406 self.send_viewport_cmd(ViewportCommand::Close);
2407 }
2408 }
2409
2410 #[cfg(debug_assertions)]
2411 self.debug_painting();
2412
2413 let mut output = self.write(|ctx| ctx.end_pass());
2414
2415 let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
2416 plugins.on_output(&mut output);
2417
2418 output
2419 }
2420
2421 #[must_use]
2423 #[deprecated = "Renamed end_pass"]
2424 pub fn end_frame(&self) -> FullOutput {
2425 self.end_pass()
2426 }
2427
2428 #[cfg(debug_assertions)]
2430 fn debug_painting(&self) {
2431 #![expect(clippy::iter_over_hash_type)] let paint_widget = |widget: &WidgetRect, text: &str, color: Color32| {
2434 let rect = widget.interact_rect;
2435 if rect.is_positive() {
2436 let painter = Painter::new(self.clone(), widget.layer_id, Rect::EVERYTHING);
2437 painter.debug_rect(rect, color, text);
2438 }
2439 };
2440
2441 let paint_widget_id = |id: Id, text: &str, color: Color32| {
2442 if let Some(widget) =
2443 self.write(|ctx| ctx.viewport().this_pass.widgets.get(id).copied())
2444 {
2445 let text = format!("{text} - {id:?}");
2446 paint_widget(&widget, &text, color);
2447 }
2448 };
2449
2450 if self.global_style().debug.show_interactive_widgets {
2451 let rects = self.write(|ctx| ctx.viewport().this_pass.widgets.clone());
2453 for (layer_id, rects) in rects.layers() {
2454 let painter = Painter::new(self.clone(), *layer_id, Rect::EVERYTHING);
2455 for rect in rects {
2456 if rect.sense.interactive() {
2457 let (color, text) = if rect.sense.senses_click() && rect.sense.senses_drag()
2458 {
2459 (Color32::from_rgb(0x88, 0, 0x88), "click+drag")
2460 } else if rect.sense.senses_click() {
2461 (Color32::from_rgb(0x88, 0, 0), "click")
2462 } else if rect.sense.senses_drag() {
2463 (Color32::from_rgb(0, 0, 0x88), "drag")
2464 } else {
2465 (Color32::from_rgb(0, 0, 0x88), "hover")
2467 };
2468 painter.debug_rect(rect.interact_rect, color, text);
2469 }
2470 }
2471 }
2472
2473 {
2475 let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());
2476 let InteractionSnapshot {
2477 clicked,
2478 long_touched: _,
2479 drag_started: _,
2480 dragged,
2481 drag_stopped: _,
2482 contains_pointer,
2483 hovered,
2484 } = interact_widgets;
2485
2486 if true {
2487 for &id in &contains_pointer {
2488 paint_widget_id(id, "contains_pointer", Color32::BLUE);
2489 }
2490
2491 let widget_rects = self.write(|w| w.viewport().this_pass.widgets.clone());
2492
2493 let mut contains_pointer: Vec<Id> = contains_pointer.iter().copied().collect();
2494 contains_pointer.sort_by_key(|&id| {
2495 widget_rects
2496 .order(id)
2497 .map(|(layer_id, order_in_layer)| (layer_id.order, order_in_layer))
2498 });
2499
2500 let mut debug_text = "Widgets in order:\n".to_owned();
2501 for id in contains_pointer {
2502 let mut widget_text = format!("{id:?}");
2503 if let Some(rect) = widget_rects.get(id) {
2504 widget_text +=
2505 &format!(" {:?} {:?} {:?}", rect.layer_id, rect.rect, rect.sense);
2506 }
2507 if let Some(info) = widget_rects.info(id) {
2508 widget_text += &format!(" {info:?}");
2509 }
2510 debug_text += &format!("{widget_text}\n");
2511 }
2512 self.debug_text(debug_text);
2513 }
2514 if true {
2515 for widget in hovered {
2516 paint_widget_id(widget, "hovered", Color32::WHITE);
2517 }
2518 }
2519 if let Some(widget) = clicked {
2520 paint_widget_id(widget, "clicked", Color32::RED);
2521 }
2522 if let Some(widget) = dragged {
2523 paint_widget_id(widget, "dragged", Color32::GREEN);
2524 }
2525 }
2526 }
2527
2528 if self.global_style().debug.show_widget_hits {
2529 let hits = self.write(|ctx| ctx.viewport().hits.clone());
2530 let WidgetHits {
2531 close,
2532 contains_pointer,
2533 click,
2534 drag,
2535 } = hits;
2536
2537 if false {
2538 for widget in &close {
2539 paint_widget(widget, "close", Color32::from_gray(70));
2540 }
2541 }
2542 if true {
2543 for widget in &contains_pointer {
2544 paint_widget(widget, "contains_pointer", Color32::BLUE);
2545 }
2546 }
2547 if let Some(widget) = &click {
2548 paint_widget(widget, "click", Color32::RED);
2549 }
2550 if let Some(widget) = &drag {
2551 paint_widget(widget, "drag", Color32::GREEN);
2552 }
2553 }
2554
2555 if self.global_style().debug.show_focused_widget
2556 && let Some(focused_id) = self.memory(|mem| mem.focused())
2557 {
2558 paint_widget_id(focused_id, "focused", Color32::PURPLE);
2559 }
2560
2561 if let Some(debug_rect) = self.pass_state_mut(|fs| fs.debug_rect.take()) {
2562 debug_rect.paint(&self.debug_painter());
2563 }
2564
2565 let num_multipass_in_row = self.viewport(|vp| vp.num_multipass_in_row);
2566 if 3 <= num_multipass_in_row {
2567 let mut warning = format!(
2571 "egui PERF WARNING: request_discard has been called {num_multipass_in_row} frames in a row"
2572 );
2573 self.viewport(|vp| {
2574 for reason in &vp.output.request_discard_reasons {
2575 warning += &format!("\n {reason}");
2576 }
2577 });
2578
2579 self.debug_painter()
2580 .debug_text(Pos2::ZERO, Align2::LEFT_TOP, Color32::RED, warning);
2581 }
2582 }
2583}
2584
2585impl ContextImpl {
2586 fn end_pass(&mut self) -> FullOutput {
2587 let ended_viewport_id = self.viewport_id();
2588 let viewport = self.viewports.entry(ended_viewport_id).or_default();
2589 let pixels_per_point = viewport.input.pixels_per_point;
2590
2591 self.loaders.end_pass(viewport.repaint.cumulative_pass_nr);
2592
2593 viewport.repaint.cumulative_pass_nr += 1;
2594
2595 self.memory.end_pass(&viewport.this_pass.used_ids);
2596
2597 if let Some(fonts) = self.fonts.as_mut() {
2598 let tex_mngr = &mut self.tex_manager.0.write();
2599 if let Some(font_image_delta) = fonts.font_image_delta() {
2600 tex_mngr.set(TextureId::default(), font_image_delta);
2602 }
2603 }
2604
2605 let textures_delta = self.tex_manager.0.write().take_delta();
2607
2608 let mut platform_output: PlatformOutput = std::mem::take(&mut viewport.output);
2609
2610 {
2611 profiling::scope!("accesskit");
2612 let state = viewport.this_pass.accesskit_state.take();
2613 if let Some(state) = state {
2614 let root_id = crate::accesskit_root_id().accesskit_id();
2615 let nodes = {
2616 state
2617 .nodes
2618 .into_iter()
2619 .map(|(id, node)| (id.accesskit_id(), node))
2620 .collect()
2621 };
2622 let focus_id = self
2623 .memory
2624 .focused()
2625 .map_or(root_id, |id| id.accesskit_id());
2626 platform_output.accesskit_update = Some(accesskit::TreeUpdate {
2627 nodes,
2628 tree: Some(accesskit::Tree::new(root_id)),
2629 tree_id: accesskit::TreeId::ROOT,
2630 focus: focus_id,
2631 });
2632 }
2633 }
2634
2635 let shapes = viewport
2636 .graphics
2637 .drain(self.memory.areas().order(), &self.memory.to_global);
2638
2639 let mut repaint_needed = false;
2640
2641 if self.memory.options.repaint_on_widget_change {
2642 profiling::scope!("compare-widget-rects");
2643 #[allow(clippy::allow_attributes, clippy::collapsible_if)] if viewport.prev_pass.widgets != viewport.this_pass.widgets {
2645 repaint_needed = true; }
2647 }
2648
2649 #[cfg(debug_assertions)]
2650 let shapes = if self.memory.options.style().debug.warn_if_rect_changes_id {
2651 let mut shapes = shapes;
2652 warn_if_rect_changes_id(
2653 &mut shapes,
2654 &viewport.prev_pass.widgets,
2655 &viewport.this_pass.widgets,
2656 );
2657 shapes
2658 } else {
2659 shapes
2660 };
2661
2662 std::mem::swap(&mut viewport.prev_pass, &mut viewport.this_pass);
2663
2664 if repaint_needed {
2665 self.request_repaint(ended_viewport_id, RepaintCause::new());
2666 }
2667 let all_viewport_ids = self.all_viewport_ids();
2670
2671 self.last_viewport = ended_viewport_id;
2672
2673 self.viewports.retain(|&id, viewport| {
2674 if id == ViewportId::ROOT {
2675 return true; }
2677
2678 let parent = *self.viewport_parents.entry(id).or_default();
2679
2680 if !all_viewport_ids.contains(&parent) {
2681 log::debug!(
2682 "Removing viewport {:?} ({:?}): the parent is gone",
2683 id,
2684 viewport.builder.title
2685 );
2686
2687 return false;
2688 }
2689
2690 let is_our_child = parent == ended_viewport_id && id != ViewportId::ROOT;
2691 if is_our_child {
2692 if !viewport.used {
2693 log::debug!(
2694 "Removing viewport {:?} ({:?}): it was never used this pass",
2695 id,
2696 viewport.builder.title
2697 );
2698
2699 return false; }
2701
2702 viewport.used = false; }
2704
2705 true
2706 });
2707
2708 self.viewport_stack.pop();
2710
2711 let is_last = self.viewport_stack.is_empty();
2714
2715 let viewport_output = self
2716 .viewports
2717 .iter_mut()
2718 .map(|(&id, viewport)| {
2719 let parent = *self.viewport_parents.entry(id).or_default();
2720 let commands = if is_last {
2721 std::mem::take(&mut viewport.commands)
2725 } else {
2726 vec![]
2727 };
2728
2729 (
2730 id,
2731 ViewportOutput {
2732 parent,
2733 class: viewport.class,
2734 builder: viewport.builder.clone(),
2735 viewport_ui_cb: viewport.viewport_ui_cb.clone(),
2736 commands,
2737 repaint_delay: viewport.repaint.repaint_delay,
2738 },
2739 )
2740 })
2741 .collect();
2742
2743 if is_last {
2744 self.viewports.retain(|id, _| all_viewport_ids.contains(id));
2746 debug_assert!(
2747 self.viewports.contains_key(&ViewportId::ROOT),
2748 "Bug in egui: we removed the root viewport"
2749 );
2750 self.viewport_parents
2751 .retain(|id, _| all_viewport_ids.contains(id));
2752 } else {
2753 let viewport_id = self.viewport_id();
2754 self.memory.set_viewport_id(viewport_id);
2755 }
2756
2757 platform_output.num_completed_passes += 1;
2758
2759 FullOutput {
2760 platform_output,
2761 textures_delta,
2762 shapes,
2763 pixels_per_point,
2764 viewport_output,
2765 }
2766 }
2767}
2768
2769impl Context {
2770 pub fn tessellate(
2776 &self,
2777 shapes: Vec<ClippedShape>,
2778 pixels_per_point: f32,
2779 ) -> Vec<ClippedPrimitive> {
2780 profiling::function_scope!();
2781
2782 self.write(|ctx| {
2787 let tessellation_options = ctx.memory.options.tessellation_options;
2788 let texture_atlas = if let Some(fonts) = ctx.fonts.as_ref() {
2789 fonts.texture_atlas()
2790 } else {
2791 log::warn!("No font size matching {pixels_per_point} pixels per point found.");
2792 ctx.fonts
2793 .iter()
2794 .next()
2795 .expect("No fonts loaded")
2796 .texture_atlas()
2797 };
2798
2799 let paint_stats = PaintStats::from_shapes(&shapes);
2800 let clipped_primitives = {
2801 profiling::scope!("tessellator::tessellate_shapes");
2802 tessellator::Tessellator::new(
2803 pixels_per_point,
2804 tessellation_options,
2805 texture_atlas.size(),
2806 texture_atlas.prepared_discs(),
2807 )
2808 .tessellate_shapes(shapes)
2809 };
2810 ctx.paint_stats = paint_stats.with_clipped_primitives(&clipped_primitives);
2811 clipped_primitives
2812 })
2813 }
2814
2815 pub fn content_rect(&self) -> Rect {
2824 self.input(|i| i.content_rect()).round_ui()
2825 }
2826
2827 pub fn viewport_rect(&self) -> Rect {
2838 self.input(|i| i.viewport_rect()).round_ui()
2839 }
2840
2841 #[deprecated(
2843 note = "screen_rect has been split into viewport_rect() and content_rect(). You likely should use content_rect()"
2844 )]
2845 pub fn screen_rect(&self) -> Rect {
2846 self.input(|i| i.content_rect()).round_ui()
2847 }
2848
2849 #[deprecated = "Use content_rect (or viewport_rect) instead"]
2851 pub fn available_rect(&self) -> Rect {
2852 #[expect(deprecated)] self.pass_state(|s| s.available_rect()).round_ui()
2854 }
2855
2856 pub fn globally_used_rect(&self) -> Rect {
2858 self.write(|ctx| {
2859 let viewport = ctx.viewport();
2860 let root_ui_min_rect =
2861 (viewport.this_pass.root_ui_min_rect).or(viewport.prev_pass.root_ui_min_rect);
2862
2863 let mut used = root_ui_min_rect.unwrap_or_else(|| {
2864 #[expect(deprecated)] ctx.viewport().this_pass.used_by_panels
2866 });
2867 for (_id, window) in ctx.memory.areas().visible_windows() {
2868 used |= window.rect();
2869 }
2870 used.round_ui()
2871 })
2872 }
2873
2874 #[deprecated = "Renamed to globally_used_rect"]
2876 pub fn used_rect(&self) -> Rect {
2877 self.globally_used_rect()
2878 }
2879
2880 #[deprecated = "Use globally_used_rect instead"]
2884 pub fn used_size(&self) -> Vec2 {
2885 (self.globally_used_rect().max - Pos2::ZERO).round_ui()
2886 }
2887
2888 pub fn is_pointer_over_egui(&self) -> bool {
2892 let pointer_pos = self.input(|i| i.pointer.interact_pos());
2893 let Some(pointer_pos) = pointer_pos else {
2894 return false;
2895 };
2896 let Some(layer) = self.layer_id_at(pointer_pos) else {
2897 return false;
2898 };
2899 if layer.order == Order::Background {
2900 let root_ui_available_rect = self
2901 .pass_state(|state| state.root_ui_available_rect)
2902 .or_else(|| self.prev_pass_state(|state| state.root_ui_available_rect));
2903
2904 if let Some(root_ui_available_rect) = root_ui_available_rect {
2905 !root_ui_available_rect.contains(pointer_pos)
2907 } else {
2908 #[expect(deprecated)]
2910 !self.pass_state(|state| state.unused_rect.contains(pointer_pos))
2911 }
2912 } else {
2913 true
2914 }
2915 }
2916
2917 #[deprecated = "Renamed to is_pointer_over_egui"]
2919 pub fn is_pointer_over_area(&self) -> bool {
2920 self.is_pointer_over_egui()
2921 }
2922
2923 pub fn egui_wants_pointer_input(&self) -> bool {
2930 self.egui_is_using_pointer()
2931 || (self.is_pointer_over_egui() && !self.input(|i| i.pointer.any_down()))
2932 }
2933
2934 #[deprecated = "Renamed to egui_wants_pointer_input"]
2941 pub fn wants_pointer_input(&self) -> bool {
2942 self.egui_wants_pointer_input()
2943 }
2944
2945 pub fn egui_is_using_pointer(&self) -> bool {
2949 self.memory(|m| m.interaction().is_using_pointer())
2950 }
2951
2952 #[deprecated = "Renamed to egui_is_using_pointer"]
2956 pub fn is_using_pointer(&self) -> bool {
2957 self.egui_is_using_pointer()
2958 }
2959
2960 pub fn egui_wants_keyboard_input(&self) -> bool {
2962 self.memory(|m| m.focused().is_some())
2963 }
2964
2965 #[deprecated = "Renamed to egui_wants_keyboard_input"]
2967 pub fn wants_keyboard_input(&self) -> bool {
2968 self.egui_wants_keyboard_input()
2969 }
2970
2971 pub fn text_edit_focused(&self) -> bool {
2973 if let Some(id) = self.memory(|mem| mem.focused()) {
2974 crate::text_edit::TextEditState::load(self, id).is_some()
2975 } else {
2976 false
2977 }
2978 }
2979
2980 pub fn highlight_widget(&self, id: Id) {
2987 self.pass_state_mut(|fs| fs.highlight_next_pass.insert(id));
2988 }
2989
2990 #[expect(deprecated)]
2994 #[deprecated = "Use `any_popup_open` instead"]
2995 pub fn is_context_menu_open(&self) -> bool {
2996 self.data(|d| {
2997 d.get_temp::<crate::menu::BarState>(crate::menu::CONTEXT_MENU_ID_STR.into())
2998 .is_some_and(|state| state.has_root())
2999 })
3000 }
3001
3002 pub fn any_popup_open(&self) -> bool {
3006 self.pass_state_mut(|fs| {
3007 fs.layers
3008 .values()
3009 .any(|layer| !layer.open_popups.is_empty())
3010 })
3011 }
3012
3013 #[deprecated = "Renamed to any_popup_open"]
3017 pub fn is_popup_open(&self) -> bool {
3018 self.pass_state_mut(|fs| {
3019 fs.layers
3020 .values()
3021 .any(|layer| !layer.open_popups.is_empty())
3022 })
3023 }
3024}
3025
3026impl Context {
3028 #[inline(always)]
3032 pub fn pointer_latest_pos(&self) -> Option<Pos2> {
3033 self.input(|i| i.pointer.latest_pos())
3034 }
3035
3036 #[inline(always)]
3038 pub fn pointer_hover_pos(&self) -> Option<Pos2> {
3039 self.input(|i| i.pointer.hover_pos())
3040 }
3041
3042 #[inline(always)]
3048 pub fn pointer_interact_pos(&self) -> Option<Pos2> {
3049 self.input(|i| i.pointer.interact_pos())
3050 }
3051
3052 pub fn multi_touch(&self) -> Option<MultiTouchInfo> {
3054 self.input(|i| i.multi_touch())
3055 }
3056}
3057
3058impl Context {
3059 pub fn set_transform_layer(&self, layer_id: LayerId, transform: TSTransform) {
3071 self.memory_mut(|m| {
3072 if transform == TSTransform::IDENTITY {
3073 m.to_global.remove(&layer_id)
3074 } else {
3075 m.to_global.insert(layer_id, transform)
3076 }
3077 });
3078 }
3079
3080 pub fn layer_transform_to_global(&self, layer_id: LayerId) -> Option<TSTransform> {
3084 self.memory(|m| m.to_global.get(&layer_id).copied())
3085 }
3086
3087 pub fn layer_transform_from_global(&self, layer_id: LayerId) -> Option<TSTransform> {
3091 self.layer_transform_to_global(layer_id)
3092 .map(|t| t.inverse())
3093 }
3094
3095 pub fn transform_layer_shapes(&self, layer_id: LayerId, transform: TSTransform) {
3103 if transform != TSTransform::IDENTITY {
3104 self.graphics_mut(|g| g.entry(layer_id).transform(transform));
3105 }
3106 }
3107
3108 pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
3110 self.memory(|mem| mem.layer_id_at(pos))
3111 }
3112
3113 pub fn move_to_top(&self, layer_id: LayerId) {
3117 self.memory_mut(|mem| mem.areas_mut().move_to_top(layer_id));
3118 }
3119
3120 pub fn set_sublayer(&self, parent: LayerId, child: LayerId) {
3128 self.memory_mut(|mem| mem.areas_mut().set_sublayer(parent, child));
3129 }
3130
3131 pub fn top_layer_id(&self) -> Option<LayerId> {
3133 self.memory(|mem| mem.areas().top_layer_id(Order::Middle))
3134 }
3135
3136 pub fn rect_contains_pointer(&self, layer_id: LayerId, rect: Rect) -> bool {
3144 let rect = if let Some(to_global) = self.layer_transform_to_global(layer_id) {
3145 to_global * rect
3146 } else {
3147 rect
3148 };
3149 if !rect.is_positive() {
3150 return false;
3151 }
3152
3153 let pointer_pos = self.input(|i| i.pointer.interact_pos());
3154 let Some(pointer_pos) = pointer_pos else {
3155 return false;
3156 };
3157
3158 if !rect.contains(pointer_pos) {
3159 return false;
3160 }
3161
3162 if self.layer_id_at(pointer_pos) != Some(layer_id) {
3163 return false;
3164 }
3165
3166 true
3167 }
3168
3169 #[cfg(debug_assertions)]
3173 pub fn debug_on_hover(&self) -> bool {
3174 self.options(|opt| opt.style().debug.debug_on_hover)
3175 }
3176
3177 #[cfg(debug_assertions)]
3179 pub fn set_debug_on_hover(&self, debug_on_hover: bool) {
3180 self.all_styles_mut(|style| style.debug.debug_on_hover = debug_on_hover);
3181 }
3182}
3183
3184impl Context {
3186 #[track_caller] pub fn animate_bool(&self, id: Id, value: bool) -> f32 {
3197 let animation_time = self.global_style().animation_time;
3198 self.animate_bool_with_time_and_easing(id, value, animation_time, emath::easing::linear)
3199 }
3200
3201 #[track_caller] pub fn animate_bool_responsive(&self, id: Id, value: bool) -> f32 {
3207 self.animate_bool_with_easing(id, value, emath::easing::cubic_out)
3208 }
3209
3210 #[track_caller] pub fn animate_bool_with_easing(&self, id: Id, value: bool, easing: fn(f32) -> f32) -> f32 {
3213 let animation_time = self.global_style().animation_time;
3214 self.animate_bool_with_time_and_easing(id, value, animation_time, easing)
3215 }
3216
3217 #[track_caller] pub fn animate_bool_with_time(&self, id: Id, target_value: bool, animation_time: f32) -> f32 {
3220 self.animate_bool_with_time_and_easing(
3221 id,
3222 target_value,
3223 animation_time,
3224 emath::easing::linear,
3225 )
3226 }
3227
3228 #[track_caller] pub fn animate_bool_with_time_and_easing(
3237 &self,
3238 id: Id,
3239 target_value: bool,
3240 animation_time: f32,
3241 easing: fn(f32) -> f32,
3242 ) -> f32 {
3243 let animated_value = self.write(|ctx| {
3244 ctx.animation_manager.animate_bool(
3245 &ctx.viewports.entry(ctx.viewport_id()).or_default().input,
3246 animation_time,
3247 id,
3248 target_value,
3249 )
3250 });
3251
3252 let animation_in_progress = 0.0 < animated_value && animated_value < 1.0;
3253 if animation_in_progress {
3254 self.request_repaint();
3255 }
3256
3257 if target_value {
3258 easing(animated_value)
3259 } else {
3260 1.0 - easing(1.0 - animated_value)
3261 }
3262 }
3263
3264 #[track_caller] pub fn animate_value_with_time(&self, id: Id, target_value: f32, animation_time: f32) -> f32 {
3270 let animated_value = self.write(|ctx| {
3271 ctx.animation_manager.animate_value(
3272 &ctx.viewports.entry(ctx.viewport_id()).or_default().input,
3273 animation_time,
3274 id,
3275 target_value,
3276 )
3277 });
3278 let animation_in_progress = animated_value != target_value;
3279 if animation_in_progress {
3280 self.request_repaint();
3281 }
3282
3283 animated_value
3284 }
3285
3286 pub fn clear_animations(&self) {
3288 self.write(|ctx| ctx.animation_manager = Default::default());
3289 }
3290}
3291
3292impl Context {
3293 pub fn settings_ui(&self, ui: &mut Ui) {
3295 let prev_options = self.options(|o| o.clone());
3296 let mut options = prev_options.clone();
3297
3298 ui.collapsing("🔠 Font tweak", |ui| {
3299 self.fonts_tweak_ui(ui);
3300 });
3301
3302 options.ui(ui);
3303
3304 if options != prev_options {
3305 self.options_mut(move |o| *o = options);
3306 }
3307 }
3308
3309 fn fonts_tweak_ui(&self, ui: &mut Ui) {
3310 let mut font_definitions = self.write(|ctx| ctx.font_definitions.clone());
3311 let mut changed = false;
3312
3313 for (name, data) in &mut font_definitions.font_data {
3314 ui.collapsing(name, |ui| {
3315 let mut tweak = data.tweak.clone();
3316 if tweak.ui(ui).changed() {
3317 Arc::make_mut(data).tweak = tweak;
3318 changed = true;
3319 }
3320 });
3321 }
3322
3323 if changed {
3324 self.set_fonts(font_definitions);
3325 }
3326 }
3327
3328 pub fn inspection_ui(&self, ui: &mut Ui) {
3330 use crate::containers::CollapsingHeader;
3331
3332 crate::Grid::new("egui-inspection-grid")
3333 .num_columns(2)
3334 .striped(true)
3335 .show(ui, |ui| {
3336 ui.label("Total ui frames:");
3337 ui.monospace(ui.ctx().cumulative_frame_nr().to_string());
3338 ui.end_row();
3339
3340 ui.label("Total ui passes:");
3341 ui.monospace(ui.ctx().cumulative_pass_nr().to_string());
3342 ui.end_row();
3343
3344 ui.label("Is using pointer")
3345 .on_hover_text("Is egui currently using the pointer actively (e.g. dragging a slider)?");
3346 ui.monospace(self.egui_is_using_pointer().to_string());
3347 ui.end_row();
3348
3349 ui.label("Wants pointer input")
3350 .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).");
3351 ui.monospace(self.egui_wants_pointer_input().to_string());
3352 ui.end_row();
3353
3354 ui.label("Wants keyboard input").on_hover_text("Is egui currently listening for text input?");
3355 ui.monospace(self.egui_wants_keyboard_input().to_string());
3356 ui.end_row();
3357
3358 ui.label("Keyboard focus widget").on_hover_text("Is egui currently listening for text input?");
3359 ui.monospace(self.memory(|m| m.focused())
3360 .as_ref()
3361 .map(Id::short_debug_format)
3362 .unwrap_or_default());
3363 ui.end_row();
3364
3365 let pointer_pos = self
3366 .pointer_hover_pos()
3367 .map_or_else(String::new, |pos| format!("{pos:?}"));
3368 ui.label("Pointer pos");
3369 ui.monospace(pointer_pos);
3370 ui.end_row();
3371
3372 let top_layer = self
3373 .pointer_hover_pos()
3374 .and_then(|pos| self.layer_id_at(pos))
3375 .map_or_else(String::new, |layer| layer.short_debug_format());
3376 ui.label("Top layer under mouse");
3377 ui.monospace(top_layer);
3378 ui.end_row();
3379 });
3380
3381 ui.add_space(16.0);
3382
3383 ui.label(format!(
3384 "There are {} text galleys in the layout cache",
3385 self.fonts(|f| f.num_galleys_in_cache())
3386 ))
3387 .on_hover_text("This is approximately the number of text strings on screen");
3388 ui.add_space(16.0);
3389
3390 CollapsingHeader::new("🔃 Repaint Causes")
3391 .default_open(false)
3392 .show(ui, |ui| {
3393 ui.set_min_height(120.0);
3394 ui.label("What caused egui to repaint:");
3395 ui.add_space(8.0);
3396 let causes = ui.ctx().repaint_causes();
3397 for cause in causes {
3398 ui.label(cause.to_string());
3399 }
3400 });
3401
3402 CollapsingHeader::new("📥 Input")
3403 .default_open(false)
3404 .show(ui, |ui| {
3405 let input = ui.input(|i| i.clone());
3406 input.ui(ui);
3407 });
3408
3409 CollapsingHeader::new("📊 Paint stats")
3410 .default_open(false)
3411 .show(ui, |ui| {
3412 let paint_stats = self.read(|ctx| ctx.paint_stats);
3413 paint_stats.ui(ui);
3414 });
3415
3416 CollapsingHeader::new("🖼 Textures")
3417 .default_open(false)
3418 .show(ui, |ui| {
3419 self.texture_ui(ui);
3420 });
3421
3422 CollapsingHeader::new("🖼 Image loaders")
3423 .default_open(false)
3424 .show(ui, |ui| {
3425 self.loaders_ui(ui);
3426 });
3427
3428 CollapsingHeader::new("🔠 Font texture")
3429 .default_open(false)
3430 .show(ui, |ui| {
3431 let font_image_size = self.fonts(|f| f.font_image_size());
3432 crate::introspection::font_texture_ui(ui, font_image_size);
3433 });
3434
3435 CollapsingHeader::new("Label text selection state")
3436 .default_open(false)
3437 .show(ui, |ui| {
3438 ui.label(format!(
3439 "{:#?}",
3440 *ui.ctx()
3441 .plugin::<crate::text_selection::LabelSelectionState>()
3442 .lock()
3443 ));
3444 });
3445
3446 CollapsingHeader::new("Interaction")
3447 .default_open(false)
3448 .show(ui, |ui| {
3449 let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());
3450 interact_widgets.ui(ui);
3451 });
3452 }
3453
3454 pub fn texture_ui(&self, ui: &mut crate::Ui) {
3456 let tex_mngr = self.tex_manager();
3457 let tex_mngr = tex_mngr.read();
3458
3459 let mut textures: Vec<_> = tex_mngr.allocated().collect();
3460 textures.sort_by_key(|(id, _)| *id);
3461
3462 let mut bytes = 0;
3463 for (_, tex) in &textures {
3464 bytes += tex.bytes_used();
3465 }
3466
3467 ui.label(format!(
3468 "{} allocated texture(s), using {:.1} MB",
3469 textures.len(),
3470 bytes as f64 * 1e-6
3471 ));
3472 let max_preview_size = vec2(48.0, 32.0);
3473
3474 let pixels_per_point = self.pixels_per_point();
3475
3476 ui.group(|ui| {
3477 ScrollArea::vertical()
3478 .max_height(300.0)
3479 .auto_shrink([false, true])
3480 .show(ui, |ui| {
3481 ui.style_mut().override_text_style = Some(TextStyle::Monospace);
3482 Grid::new("textures")
3483 .striped(true)
3484 .num_columns(4)
3485 .spacing(vec2(16.0, 2.0))
3486 .min_row_height(max_preview_size.y)
3487 .show(ui, |ui| {
3488 for (&texture_id, meta) in textures {
3489 let [w, h] = meta.size;
3490 let point_size = vec2(w as f32, h as f32) / pixels_per_point;
3491
3492 let mut size = point_size;
3493 size *= (max_preview_size.x / size.x).min(1.0);
3494 size *= (max_preview_size.y / size.y).min(1.0);
3495 ui.image(SizedTexture::new(texture_id, size))
3496 .on_hover_ui(|ui| {
3497 let max_size = 0.5 * ui.ctx().content_rect().size();
3499 let mut size = point_size;
3500 size *= max_size.x / size.x.max(max_size.x);
3501 size *= max_size.y / size.y.max(max_size.y);
3502 ui.image(SizedTexture::new(texture_id, size));
3503 });
3504
3505 ui.label(format!("{w} x {h}"));
3506 ui.label(format!("{:.3} MB", meta.bytes_used() as f64 * 1e-6));
3507 ui.label(format!("{:?}", meta.name));
3508 ui.end_row();
3509 }
3510 });
3511 });
3512 });
3513 }
3514
3515 pub fn loaders_ui(&self, ui: &mut crate::Ui) {
3517 struct LoaderInfo {
3518 id: String,
3519 byte_size: usize,
3520 }
3521
3522 let mut byte_loaders = vec![];
3523 let mut image_loaders = vec![];
3524 let mut texture_loaders = vec![];
3525
3526 {
3527 let loaders = self.loaders();
3528 let Loaders {
3529 include: _,
3530 bytes,
3531 image,
3532 texture,
3533 } = loaders.as_ref();
3534
3535 for loader in bytes.lock().iter() {
3536 byte_loaders.push(LoaderInfo {
3537 id: loader.id().to_owned(),
3538 byte_size: loader.byte_size(),
3539 });
3540 }
3541 for loader in image.lock().iter() {
3542 image_loaders.push(LoaderInfo {
3543 id: loader.id().to_owned(),
3544 byte_size: loader.byte_size(),
3545 });
3546 }
3547 for loader in texture.lock().iter() {
3548 texture_loaders.push(LoaderInfo {
3549 id: loader.id().to_owned(),
3550 byte_size: loader.byte_size(),
3551 });
3552 }
3553 }
3554
3555 fn loaders_ui(ui: &mut crate::Ui, title: &str, loaders: &[LoaderInfo]) {
3556 let heading = format!("{} {title} loaders", loaders.len());
3557 crate::CollapsingHeader::new(heading)
3558 .default_open(true)
3559 .show(ui, |ui| {
3560 Grid::new("loaders")
3561 .striped(true)
3562 .num_columns(2)
3563 .show(ui, |ui| {
3564 ui.label("ID");
3565 ui.label("Size");
3566 ui.end_row();
3567
3568 for loader in loaders {
3569 ui.label(&loader.id);
3570 ui.label(format!("{:.3} MB", loader.byte_size as f64 * 1e-6));
3571 ui.end_row();
3572 }
3573 });
3574 });
3575 }
3576
3577 loaders_ui(ui, "byte", &byte_loaders);
3578 loaders_ui(ui, "image", &image_loaders);
3579 loaders_ui(ui, "texture", &texture_loaders);
3580 }
3581
3582 pub fn memory_ui(&self, ui: &mut crate::Ui) {
3584 if ui
3585 .button("Reset all")
3586 .on_hover_text("Reset all egui state")
3587 .clicked()
3588 {
3589 self.memory_mut(|mem| *mem = Default::default());
3590 }
3591
3592 let (num_state, num_serialized) = self.data(|d| (d.len(), d.count_serialized()));
3593 ui.label(format!(
3594 "{num_state} widget states stored (of which {num_serialized} are serialized)."
3595 ));
3596
3597 ui.horizontal(|ui| {
3598 ui.label(format!(
3599 "{} areas (panels, windows, popups, …)",
3600 self.memory(|mem| mem.areas().count())
3601 ));
3602 if ui.button("Reset").clicked() {
3603 self.memory_mut(|mem| *mem.areas_mut() = Default::default());
3604 }
3605 });
3606 ui.indent("layers", |ui| {
3607 ui.label("Layers, ordered back to front.");
3608 let layers_ids: Vec<LayerId> = self.memory(|mem| mem.areas().order().to_vec());
3609 for layer_id in layers_ids {
3610 if let Some(area) = AreaState::load(self, layer_id.id) {
3611 let is_visible = self.memory(|mem| mem.areas().is_visible(&layer_id));
3612 if !is_visible {
3613 continue;
3614 }
3615 let text = format!("{} - {:?}", layer_id.short_debug_format(), area.rect(),);
3616 let response =
3618 ui.add(Label::new(RichText::new(text).monospace()).sense(Sense::click()));
3619 if response.hovered() && is_visible {
3620 ui.debug_painter().debug_rect(area.rect(), Color32::RED, "");
3621 }
3622 } else {
3623 ui.monospace(layer_id.short_debug_format());
3624 }
3625 }
3626 });
3627
3628 ui.horizontal(|ui| {
3629 ui.label(format!(
3630 "{} collapsing headers",
3631 self.data(|d| d.count::<containers::collapsing_header::InnerState>())
3632 ));
3633 if ui.button("Reset").clicked() {
3634 self.data_mut(|d| d.remove_by_type::<containers::collapsing_header::InnerState>());
3635 }
3636 });
3637
3638 #[expect(deprecated)]
3639 ui.horizontal(|ui| {
3640 ui.label(format!(
3641 "{} menu bars",
3642 self.data(|d| d.count::<crate::menu::BarState>())
3643 ));
3644 if ui.button("Reset").clicked() {
3645 self.data_mut(|d| d.remove_by_type::<crate::menu::BarState>());
3646 }
3647 });
3648
3649 ui.horizontal(|ui| {
3650 ui.label(format!(
3651 "{} scroll areas",
3652 self.data(|d| d.count::<scroll_area::State>())
3653 ));
3654 if ui.button("Reset").clicked() {
3655 self.data_mut(|d| d.remove_by_type::<scroll_area::State>());
3656 }
3657 });
3658
3659 ui.horizontal(|ui| {
3660 ui.label(format!(
3661 "{} resize areas",
3662 self.data(|d| d.count::<resize::State>())
3663 ));
3664 if ui.button("Reset").clicked() {
3665 self.data_mut(|d| d.remove_by_type::<resize::State>());
3666 }
3667 });
3668
3669 ui.shrink_width_to_current(); ui.label("NOTE: the position of this window cannot be reset from within itself.");
3671
3672 ui.collapsing("Interaction", |ui| {
3673 let interaction = self.memory(|mem| mem.interaction().clone());
3674 interaction.ui(ui);
3675 });
3676 }
3677}
3678
3679impl Context {
3680 pub fn style_ui(&self, ui: &mut Ui, theme: Theme) {
3682 let mut style: Style = (*self.style_of(theme)).clone();
3683 style.ui(ui);
3684 self.set_style_of(theme, style);
3685 }
3686}
3687
3688impl Context {
3690 pub fn accesskit_node_builder<R>(
3700 &self,
3701 id: Id,
3702 writer: impl FnOnce(&mut accesskit::Node) -> R,
3703 ) -> Option<R> {
3704 self.write(|ctx| ctx.accesskit_node_builder(id).map(writer))
3705 }
3706
3707 pub(crate) fn register_accesskit_parent(&self, id: Id, parent_id: Id) {
3708 self.write(|ctx| {
3709 if let Some(state) = ctx.viewport().this_pass.accesskit_state.as_mut() {
3710 state.parent_map.insert(id, parent_id);
3711 }
3712 });
3713 }
3714
3715 pub fn enable_accesskit(&self) {
3717 self.write(|ctx| ctx.is_accesskit_enabled = true);
3718 }
3719
3720 pub fn disable_accesskit(&self) {
3722 self.write(|ctx| ctx.is_accesskit_enabled = false);
3723 }
3724}
3725
3726impl Context {
3728 pub fn include_bytes(&self, uri: impl Into<Cow<'static, str>>, bytes: impl Into<Bytes>) {
3735 self.loaders().include.insert(uri, bytes);
3736 }
3737
3738 pub fn is_loader_installed(&self, id: &str) -> bool {
3741 let loaders = self.loaders();
3742
3743 loaders.bytes.lock().iter().any(|l| l.id() == id)
3744 || loaders.image.lock().iter().any(|l| l.id() == id)
3745 || loaders.texture.lock().iter().any(|l| l.id() == id)
3746 }
3747
3748 pub fn add_bytes_loader(&self, loader: Arc<dyn load::BytesLoader + Send + Sync + 'static>) {
3754 self.loaders().bytes.lock().push(loader);
3755 }
3756
3757 pub fn add_image_loader(&self, loader: Arc<dyn load::ImageLoader + Send + Sync + 'static>) {
3763 self.loaders().image.lock().push(loader);
3764 }
3765
3766 pub fn add_texture_loader(&self, loader: Arc<dyn load::TextureLoader + Send + Sync + 'static>) {
3772 self.loaders().texture.lock().push(loader);
3773 }
3774
3775 pub fn forget_image(&self, uri: &str) {
3780 use load::BytesLoader as _;
3781
3782 profiling::function_scope!();
3783
3784 let loaders = self.loaders();
3785
3786 loaders.include.forget(uri);
3787 for loader in loaders.bytes.lock().iter() {
3788 loader.forget(uri);
3789 }
3790 for loader in loaders.image.lock().iter() {
3791 loader.forget(uri);
3792 }
3793 for loader in loaders.texture.lock().iter() {
3794 loader.forget(uri);
3795 }
3796 }
3797
3798 pub fn forget_all_images(&self) {
3802 use load::BytesLoader as _;
3803
3804 profiling::function_scope!();
3805
3806 let loaders = self.loaders();
3807
3808 loaders.include.forget_all();
3809 for loader in loaders.bytes.lock().iter() {
3810 loader.forget_all();
3811 }
3812 for loader in loaders.image.lock().iter() {
3813 loader.forget_all();
3814 }
3815 for loader in loaders.texture.lock().iter() {
3816 loader.forget_all();
3817 }
3818 }
3819
3820 pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult {
3839 profiling::function_scope!(uri);
3840
3841 let loaders = self.loaders();
3842 let bytes_loaders = loaders.bytes.lock();
3843
3844 for loader in bytes_loaders.iter().rev() {
3846 let result = loader.load(self, uri);
3847 match result {
3848 Err(load::LoadError::NotSupported) => {}
3849 _ => return result,
3850 }
3851 }
3852
3853 Err(load::LoadError::NoMatchingBytesLoader)
3854 }
3855
3856 pub fn try_load_image(&self, uri: &str, size_hint: load::SizeHint) -> load::ImageLoadResult {
3877 profiling::function_scope!(uri);
3878
3879 let loaders = self.loaders();
3880 let image_loaders = loaders.image.lock();
3881 if image_loaders.is_empty() {
3882 return Err(load::LoadError::NoImageLoaders);
3883 }
3884
3885 let mut format = None;
3886
3887 for loader in image_loaders.iter().rev() {
3889 match loader.load(self, uri, size_hint) {
3890 Err(load::LoadError::NotSupported) => {}
3891 Err(load::LoadError::FormatNotSupported { detected_format }) => {
3892 format = format.or(detected_format);
3893 }
3894 result => return result,
3895 }
3896 }
3897
3898 Err(load::LoadError::NoMatchingImageLoader {
3899 detected_format: format,
3900 })
3901 }
3902
3903 pub fn try_load_texture(
3922 &self,
3923 uri: &str,
3924 texture_options: TextureOptions,
3925 size_hint: load::SizeHint,
3926 ) -> load::TextureLoadResult {
3927 profiling::function_scope!(uri);
3928
3929 let loaders = self.loaders();
3930 let texture_loaders = loaders.texture.lock();
3931
3932 for loader in texture_loaders.iter().rev() {
3934 match loader.load(self, uri, texture_options, size_hint) {
3935 Err(load::LoadError::NotSupported) => {}
3936 result => return result,
3937 }
3938 }
3939
3940 Err(load::LoadError::NoMatchingTextureLoader)
3941 }
3942
3943 pub fn loaders(&self) -> Arc<Loaders> {
3945 self.read(|this| Arc::clone(&this.loaders))
3946 }
3947
3948 pub fn has_pending_images(&self) -> bool {
3950 self.read(|this| {
3951 this.loaders.image.lock().iter().any(|i| i.has_pending())
3952 || this.loaders.bytes.lock().iter().any(|i| i.has_pending())
3953 })
3954 }
3955}
3956
3957impl Context {
3959 pub fn viewport_id(&self) -> ViewportId {
3965 self.read(|ctx| ctx.viewport_id())
3966 }
3967
3968 pub fn parent_viewport_id(&self) -> ViewportId {
3974 self.read(|ctx| ctx.parent_viewport_id())
3975 }
3976
3977 pub fn viewport<R>(&self, reader: impl FnOnce(&ViewportState) -> R) -> R {
3979 self.write(|ctx| reader(ctx.viewport()))
3980 }
3981
3982 pub fn viewport_for<R>(
3984 &self,
3985 viewport_id: ViewportId,
3986 reader: impl FnOnce(&ViewportState) -> R,
3987 ) -> R {
3988 self.write(|ctx| reader(ctx.viewport_for(viewport_id)))
3989 }
3990
3991 pub fn set_immediate_viewport_renderer(
4004 callback: impl for<'a> Fn(&Self, ImmediateViewport<'a>) + 'static,
4005 ) {
4006 let callback = Box::new(callback);
4007 IMMEDIATE_VIEWPORT_RENDERER.with(|render_sync| {
4008 render_sync.replace(Some(callback));
4009 });
4010 }
4011
4012 pub fn embed_viewports(&self) -> bool {
4017 self.read(|ctx| ctx.embed_viewports)
4018 }
4019
4020 pub fn set_embed_viewports(&self, value: bool) {
4025 self.write(|ctx| ctx.embed_viewports = value);
4026 }
4027
4028 pub fn send_viewport_cmd(&self, command: ViewportCommand) {
4032 self.send_viewport_cmd_to(self.viewport_id(), command);
4033 }
4034
4035 pub fn send_viewport_cmd_to(&self, id: ViewportId, command: ViewportCommand) {
4039 self.request_repaint_of(id);
4040
4041 if command.requires_parent_repaint() {
4042 self.request_repaint_of(self.parent_viewport_id());
4043 }
4044
4045 self.write(|ctx| ctx.viewport_for(id).commands.push(command));
4046 }
4047
4048 pub fn show_viewport_deferred(
4078 &self,
4079 new_viewport_id: ViewportId,
4080 viewport_builder: ViewportBuilder,
4081 viewport_ui_cb: impl Fn(&mut Ui, ViewportClass) + Send + Sync + 'static,
4082 ) {
4083 profiling::function_scope!();
4084
4085 if self.embed_viewports() {
4086 crate::Window::from_viewport(new_viewport_id, viewport_builder).show(self, |ui| {
4087 viewport_ui_cb(ui, ViewportClass::EmbeddedWindow);
4088 });
4089 } else {
4090 self.write(|ctx| {
4091 ctx.viewport_parents
4092 .insert(new_viewport_id, ctx.viewport_id());
4093
4094 let viewport = ctx.viewports.entry(new_viewport_id).or_default();
4095 viewport.class = ViewportClass::Deferred;
4096 viewport.builder = viewport_builder;
4097 viewport.used = true;
4098 viewport.viewport_ui_cb = Some(Arc::new(move |ui| {
4099 (viewport_ui_cb)(ui, ViewportClass::Deferred);
4100 }));
4101 });
4102 }
4103 }
4104
4105 pub fn show_viewport_immediate<T>(
4132 &self,
4133 new_viewport_id: ViewportId,
4134 builder: ViewportBuilder,
4135 mut viewport_ui_cb: impl FnMut(&mut Ui, ViewportClass) -> T,
4136 ) -> T {
4137 profiling::function_scope!();
4138
4139 if self.embed_viewports() {
4140 return self.show_embedded_viewport(new_viewport_id, builder, |ui| {
4141 viewport_ui_cb(ui, ViewportClass::EmbeddedWindow)
4142 });
4143 }
4144
4145 IMMEDIATE_VIEWPORT_RENDERER.with(|immediate_viewport_renderer| {
4146 let immediate_viewport_renderer = immediate_viewport_renderer.borrow();
4147 let Some(immediate_viewport_renderer) = immediate_viewport_renderer.as_ref() else {
4148 return self.show_embedded_viewport(new_viewport_id, builder, |ui| {
4150 viewport_ui_cb(ui, ViewportClass::EmbeddedWindow)
4151 });
4152 };
4153
4154 let ids = self.write(|ctx| {
4155 let parent_viewport_id = ctx.viewport_id();
4156
4157 ctx.viewport_parents
4158 .insert(new_viewport_id, parent_viewport_id);
4159
4160 let viewport = ctx.viewports.entry(new_viewport_id).or_default();
4161 viewport.builder = builder.clone();
4162 viewport.used = true;
4163 viewport.viewport_ui_cb = None; ViewportIdPair::from_self_and_parent(new_viewport_id, parent_viewport_id)
4166 });
4167
4168 let mut out = None;
4169 {
4170 let out = &mut out;
4171
4172 let viewport = ImmediateViewport {
4173 ids,
4174 builder,
4175 viewport_ui_cb: Box::new(move |ui| {
4176 *out = Some((viewport_ui_cb)(ui, ViewportClass::Immediate));
4177 }),
4178 };
4179
4180 immediate_viewport_renderer(self, viewport);
4181 }
4182
4183 out.expect(
4184 "egui backend is implemented incorrectly - the user callback was never called",
4185 )
4186 })
4187 }
4188
4189 fn show_embedded_viewport<T>(
4190 &self,
4191 new_viewport_id: ViewportId,
4192 builder: ViewportBuilder,
4193 viewport_ui_cb: impl FnOnce(&mut Ui) -> T,
4194 ) -> T {
4195 crate::Window::from_viewport(new_viewport_id, builder)
4196 .collapsible(false)
4197 .show(self, |ui| viewport_ui_cb(ui))
4198 .unwrap_or_else(|| panic!("Window did not show"))
4199 .inner
4200 .unwrap_or_else(|| panic!("Window was collapsed"))
4201 }
4202}
4203
4204impl Context {
4206 pub fn interaction_snapshot<R>(&self, reader: impl FnOnce(&InteractionSnapshot) -> R) -> R {
4208 self.write(|w| reader(&w.viewport().interact_widgets))
4209 }
4210
4211 pub fn dragged_id(&self) -> Option<Id> {
4219 self.interaction_snapshot(|i| i.dragged)
4220 }
4221
4222 pub fn is_being_dragged(&self, id: Id) -> bool {
4229 self.dragged_id() == Some(id)
4230 }
4231
4232 pub fn drag_started_id(&self) -> Option<Id> {
4236 self.interaction_snapshot(|i| i.drag_started)
4237 }
4238
4239 pub fn drag_stopped_id(&self) -> Option<Id> {
4241 self.interaction_snapshot(|i| i.drag_stopped)
4242 }
4243
4244 pub fn set_dragged_id(&self, id: Id) {
4246 self.write(|ctx| {
4247 let vp = ctx.viewport();
4248 let i = &mut vp.interact_widgets;
4249 if i.dragged != Some(id) {
4250 i.drag_stopped = i.dragged.or(i.drag_stopped);
4251 i.dragged = Some(id);
4252 i.drag_started = Some(id);
4253 }
4254
4255 ctx.memory.interaction_mut().potential_drag_id = Some(id);
4256 });
4257 }
4258
4259 pub fn stop_dragging(&self) {
4261 self.write(|ctx| {
4262 let vp = ctx.viewport();
4263 let i = &mut vp.interact_widgets;
4264 if i.dragged.is_some() {
4265 i.drag_stopped = i.dragged;
4266 i.dragged = None;
4267 }
4268
4269 ctx.memory.interaction_mut().potential_drag_id = None;
4270 });
4271 }
4272
4273 #[inline(always)]
4277 pub fn dragging_something_else(&self, not_this: Id) -> bool {
4278 let dragged = self.dragged_id();
4279 dragged.is_some() && dragged != Some(not_this)
4280 }
4281}
4282
4283#[test]
4284fn context_impl_send_sync() {
4285 fn assert_send_sync<T: Send + Sync>() {}
4286 assert_send_sync::<Context>();
4287}
4288
4289#[cfg(debug_assertions)]
4294fn warn_if_rect_changes_id(
4295 out_shapes: &mut Vec<ClippedShape>,
4296 prev_widgets: &crate::WidgetRects,
4297 new_widgets: &crate::WidgetRects,
4298) {
4299 profiling::function_scope!();
4300
4301 use std::collections::BTreeMap;
4302
4303 #[derive(Clone, Copy, PartialEq, Eq)]
4305 struct OrderedRect(Rect);
4306
4307 impl PartialOrd for OrderedRect {
4308 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
4309 Some(self.cmp(other))
4310 }
4311 }
4312
4313 impl Ord for OrderedRect {
4314 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
4315 let lhs = self.0;
4316 let rhs = other.0;
4317 lhs.min
4318 .x
4319 .to_bits()
4320 .cmp(&rhs.min.x.to_bits())
4321 .then(lhs.min.y.to_bits().cmp(&rhs.min.y.to_bits()))
4322 .then(lhs.max.x.to_bits().cmp(&rhs.max.x.to_bits()))
4323 .then(lhs.max.y.to_bits().cmp(&rhs.max.y.to_bits()))
4324 }
4325 }
4326
4327 fn create_lookup<'a>(
4328 widgets: impl Iterator<Item = &'a WidgetRect>,
4329 ) -> BTreeMap<OrderedRect, Vec<&'a WidgetRect>> {
4330 let mut lookup: BTreeMap<OrderedRect, Vec<&'a WidgetRect>> = BTreeMap::default();
4331 for w in widgets {
4332 lookup.entry(OrderedRect(w.rect)).or_default().push(w);
4333 }
4334 lookup
4335 }
4336
4337 for (layer_id, new_layer_widgets) in new_widgets.layers() {
4338 let prev = create_lookup(prev_widgets.get_layer(*layer_id));
4339 let new = create_lookup(new_layer_widgets.iter());
4340
4341 for (hashable_rect, new_at_rect) in new {
4342 let Some(prev_at_rect) = prev.get(&hashable_rect) else {
4343 continue; };
4345
4346 if prev_at_rect
4347 .iter()
4348 .any(|w| new_at_rect.iter().any(|nw| nw.id == w.id))
4349 {
4350 continue; }
4352
4353 if prev_at_rect.iter().all(|w| new_widgets.contains(w.id)) {
4357 continue;
4358 }
4359
4360 if !prev_at_rect
4363 .iter()
4364 .any(|pw| new_at_rect.iter().any(|nw| nw.parent_id == pw.parent_id))
4365 {
4366 continue;
4367 }
4368
4369 let rect = new_at_rect[0].rect;
4370
4371 log::warn!(
4372 "Widget rect {rect:?} changed id between passes: prev ids: {:?}, new ids: {:?}",
4373 prev_at_rect
4374 .iter()
4375 .map(|w| w.id.short_debug_format())
4376 .collect::<Vec<_>>(),
4377 new_at_rect
4378 .iter()
4379 .map(|w| w.id.short_debug_format())
4380 .collect::<Vec<_>>(),
4381 );
4382 out_shapes.push(ClippedShape {
4383 clip_rect: Rect::EVERYTHING,
4384 shape: epaint::Shape::rect_stroke(
4385 rect,
4386 0,
4387 (2.0, Color32::RED),
4388 StrokeKind::Outside,
4389 ),
4390 });
4391 }
4392 }
4393}
4394
4395#[cfg(test)]
4396mod test {
4397 use super::Context;
4398
4399 #[test]
4400 fn test_single_pass() {
4401 let ctx = Context::default();
4402 ctx.options_mut(|o| o.max_passes = 1.try_into().unwrap());
4403
4404 {
4406 let mut num_calls = 0;
4407 let output = ctx.run_ui(Default::default(), |ui| {
4408 num_calls += 1;
4409 assert_eq!(ui.output(|o| o.num_completed_passes), 0);
4410 assert!(!ui.output(|o| o.requested_discard()));
4411 assert!(!ui.will_discard());
4412 });
4413 assert_eq!(num_calls, 1);
4414 assert_eq!(output.platform_output.num_completed_passes, 1);
4415 assert!(!output.platform_output.requested_discard());
4416 }
4417
4418 {
4420 let mut num_calls = 0;
4421 let output = ctx.run_ui(Default::default(), |ui| {
4422 num_calls += 1;
4423 ui.request_discard("test");
4424 assert!(!ui.will_discard(), "The request should have been denied");
4425 });
4426 assert_eq!(num_calls, 1);
4427 assert_eq!(output.platform_output.num_completed_passes, 1);
4428 assert!(
4429 output.platform_output.requested_discard(),
4430 "The request should be reported"
4431 );
4432 assert_eq!(
4433 output
4434 .platform_output
4435 .request_discard_reasons
4436 .first()
4437 .unwrap()
4438 .reason,
4439 "test"
4440 );
4441 }
4442 }
4443
4444 #[test]
4445 fn test_dual_pass() {
4446 let ctx = Context::default();
4447 ctx.options_mut(|o| o.max_passes = 2.try_into().unwrap());
4448
4449 {
4451 let mut num_calls = 0;
4452 let output = ctx.run_ui(Default::default(), |ui| {
4453 assert_eq!(ui.output(|o| o.num_completed_passes), 0);
4454 assert!(!ui.output(|o| o.requested_discard()));
4455 assert!(!ui.will_discard());
4456 num_calls += 1;
4457 });
4458 assert_eq!(num_calls, 1);
4459 assert_eq!(output.platform_output.num_completed_passes, 1);
4460 assert!(!output.platform_output.requested_discard());
4461 }
4462
4463 {
4465 let mut num_calls = 0;
4466 let output = ctx.run_ui(Default::default(), |ui| {
4467 assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);
4468
4469 assert!(!ui.will_discard());
4470 if num_calls == 0 {
4471 ui.request_discard("test");
4472 assert!(ui.will_discard());
4473 }
4474
4475 num_calls += 1;
4476 });
4477 assert_eq!(num_calls, 2);
4478 assert_eq!(output.platform_output.num_completed_passes, 2);
4479 assert!(
4480 !output.platform_output.requested_discard(),
4481 "The request should have been cleared when fulfilled"
4482 );
4483 }
4484
4485 {
4487 let mut num_calls = 0;
4488 let output = ctx.run_ui(Default::default(), |ui| {
4489 assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);
4490
4491 assert!(!ui.will_discard());
4492 ui.request_discard("test");
4493 if num_calls == 0 {
4494 assert!(ui.will_discard(), "First request granted");
4495 } else {
4496 assert!(!ui.will_discard(), "Second request should be denied");
4497 }
4498
4499 num_calls += 1;
4500 });
4501 assert_eq!(num_calls, 2);
4502 assert_eq!(output.platform_output.num_completed_passes, 2);
4503 assert!(
4504 output.platform_output.requested_discard(),
4505 "The unfulfilled request should be reported"
4506 );
4507 }
4508 }
4509
4510 #[test]
4511 fn test_multi_pass() {
4512 let ctx = Context::default();
4513 ctx.options_mut(|o| o.max_passes = 10.try_into().unwrap());
4514
4515 {
4517 let mut num_calls = 0;
4518 let output = ctx.run_ui(Default::default(), |ui| {
4519 assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);
4520
4521 assert!(!ui.will_discard());
4522 if num_calls <= 2 {
4523 ui.request_discard("test");
4524 assert!(ui.will_discard());
4525 }
4526
4527 num_calls += 1;
4528 });
4529 assert_eq!(num_calls, 4);
4530 assert_eq!(output.platform_output.num_completed_passes, 4);
4531 assert!(
4532 !output.platform_output.requested_discard(),
4533 "The request should have been cleared when fulfilled"
4534 );
4535 }
4536 }
4537}