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 ViewportBuilder, ViewportCommand, ViewportId, ViewportIdMap, ViewportIdPair, ViewportIdSet,
23 ViewportOutput, 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,
38 plugin::TypedPluginHandle,
39 resize, response, scroll_area,
40 util::IdTypeMap,
41 viewport::ViewportClass,
42};
43
44#[cfg(feature = "accesskit")]
45use crate::IdMap;
46
47#[derive(Clone, Copy, Debug)]
51pub struct RequestRepaintInfo {
52 pub viewport_id: ViewportId,
54
55 pub delay: Duration,
57
58 pub current_cumulative_pass_nr: u64,
63}
64
65thread_local! {
68 static IMMEDIATE_VIEWPORT_RENDERER: RefCell<Option<Box<ImmediateViewportRendererCallback>>> = Default::default();
69}
70
71struct WrappedTextureManager(Arc<RwLock<epaint::TextureManager>>);
74
75impl Default for WrappedTextureManager {
76 fn default() -> Self {
77 let mut tex_mngr = epaint::textures::TextureManager::default();
78
79 let font_id = tex_mngr.alloc(
81 "egui_font_texture".into(),
82 epaint::ColorImage::filled([0, 0], Color32::TRANSPARENT).into(),
83 Default::default(),
84 );
85 assert_eq!(
86 font_id,
87 TextureId::default(),
88 "font id should be equal to TextureId::default(), but was {font_id:?}",
89 );
90
91 Self(Arc::new(RwLock::new(tex_mngr)))
92 }
93}
94
95impl ContextImpl {
99 fn begin_pass_repaint_logic(&mut self, viewport_id: ViewportId) {
101 let viewport = self.viewports.entry(viewport_id).or_default();
102
103 std::mem::swap(
104 &mut viewport.repaint.prev_causes,
105 &mut viewport.repaint.causes,
106 );
107 viewport.repaint.causes.clear();
108
109 viewport.repaint.prev_pass_paint_delay = viewport.repaint.repaint_delay;
110
111 if viewport.repaint.outstanding == 0 {
112 viewport.repaint.repaint_delay = Duration::MAX;
114 } else {
115 viewport.repaint.repaint_delay = Duration::ZERO;
116 viewport.repaint.outstanding -= 1;
117 if let Some(callback) = &self.request_repaint_callback {
118 (callback)(RequestRepaintInfo {
119 viewport_id,
120 delay: Duration::ZERO,
121 current_cumulative_pass_nr: viewport.repaint.cumulative_pass_nr,
122 });
123 }
124 }
125 }
126
127 fn request_repaint(&mut self, viewport_id: ViewportId, cause: RepaintCause) {
128 self.request_repaint_after(Duration::ZERO, viewport_id, cause);
129 }
130
131 fn request_repaint_after(
132 &mut self,
133 mut delay: Duration,
134 viewport_id: ViewportId,
135 cause: RepaintCause,
136 ) {
137 let viewport = self.viewports.entry(viewport_id).or_default();
138
139 if delay == Duration::ZERO {
140 viewport.repaint.outstanding = 1;
143 } else {
144 }
149
150 if let Ok(predicted_frame_time) = Duration::try_from_secs_f32(viewport.input.predicted_dt) {
151 delay = delay.saturating_sub(predicted_frame_time);
153 }
154
155 viewport.repaint.causes.push(cause);
156
157 if delay < viewport.repaint.repaint_delay {
161 viewport.repaint.repaint_delay = delay;
162
163 if let Some(callback) = &self.request_repaint_callback {
164 (callback)(RequestRepaintInfo {
165 viewport_id,
166 delay,
167 current_cumulative_pass_nr: viewport.repaint.cumulative_pass_nr,
168 });
169 }
170 }
171 }
172
173 #[must_use]
174 fn requested_immediate_repaint_prev_pass(&self, viewport_id: &ViewportId) -> bool {
175 self.viewports
176 .get(viewport_id)
177 .is_some_and(|v| v.repaint.requested_immediate_repaint_prev_pass())
178 }
179
180 #[must_use]
181 fn has_requested_repaint(&self, viewport_id: &ViewportId) -> bool {
182 self.viewports
183 .get(viewport_id)
184 .is_some_and(|v| 0 < v.repaint.outstanding || v.repaint.repaint_delay < Duration::MAX)
185 }
186}
187
188#[derive(Default)]
195pub struct ViewportState {
196 pub class: ViewportClass,
201
202 pub builder: ViewportBuilder,
204
205 pub viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,
209
210 pub input: InputState,
211
212 pub this_pass: PassState,
214
215 pub prev_pass: PassState,
219
220 pub used: bool,
222
223 repaint: ViewportRepaintInfo,
225
226 pub hits: WidgetHits,
231
232 pub interact_widgets: InteractionSnapshot,
236
237 pub graphics: GraphicLayers,
241 pub output: PlatformOutput,
243 pub commands: Vec<ViewportCommand>,
244
245 pub num_multipass_in_row: usize,
248}
249
250#[derive(Clone, PartialEq, Eq, Hash)]
252pub struct RepaintCause {
253 pub file: &'static str,
255
256 pub line: u32,
258
259 pub reason: Cow<'static, str>,
261}
262
263impl std::fmt::Debug for RepaintCause {
264 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265 write!(f, "{}:{} {}", self.file, self.line, self.reason)
266 }
267}
268
269impl std::fmt::Display for RepaintCause {
270 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271 write!(f, "{}:{} {}", self.file, self.line, self.reason)
272 }
273}
274
275impl RepaintCause {
276 #[expect(clippy::new_without_default)]
278 #[track_caller]
279 pub fn new() -> Self {
280 let caller = Location::caller();
281 Self {
282 file: caller.file(),
283 line: caller.line(),
284 reason: "".into(),
285 }
286 }
287
288 #[track_caller]
291 pub fn new_reason(reason: impl Into<Cow<'static, str>>) -> Self {
292 let caller = Location::caller();
293 Self {
294 file: caller.file(),
295 line: caller.line(),
296 reason: reason.into(),
297 }
298 }
299}
300
301struct ViewportRepaintInfo {
303 cumulative_frame_nr: u64,
309
310 cumulative_pass_nr: u64,
314
315 repaint_delay: Duration,
322
323 outstanding: u8,
325
326 causes: Vec<RepaintCause>,
328
329 prev_causes: Vec<RepaintCause>,
332
333 prev_pass_paint_delay: Duration,
338}
339
340impl Default for ViewportRepaintInfo {
341 fn default() -> Self {
342 Self {
343 cumulative_frame_nr: 0,
344 cumulative_pass_nr: 0,
345
346 repaint_delay: Duration::MAX,
348
349 outstanding: 1,
351
352 causes: Default::default(),
353 prev_causes: Default::default(),
354
355 prev_pass_paint_delay: Duration::MAX,
356 }
357 }
358}
359
360impl ViewportRepaintInfo {
361 pub fn requested_immediate_repaint_prev_pass(&self) -> bool {
362 self.prev_pass_paint_delay == Duration::ZERO
363 }
364}
365
366#[derive(Default)]
369struct ContextImpl {
370 fonts: Option<Fonts>,
371 font_definitions: FontDefinitions,
372
373 memory: Memory,
374 animation_manager: AnimationManager,
375
376 plugins: plugin::Plugins,
377 safe_area: SafeAreaInsets,
378
379 tex_manager: WrappedTextureManager,
386
387 new_zoom_factor: Option<f32>,
389
390 os: OperatingSystem,
391
392 viewport_stack: Vec<ViewportIdPair>,
394
395 last_viewport: ViewportId,
397
398 paint_stats: PaintStats,
399
400 request_repaint_callback: Option<Box<dyn Fn(RequestRepaintInfo) + Send + Sync>>,
401
402 viewport_parents: ViewportIdMap<ViewportId>,
403 viewports: ViewportIdMap<ViewportState>,
404
405 embed_viewports: bool,
406
407 #[cfg(feature = "accesskit")]
408 is_accesskit_enabled: bool,
409
410 loaders: Arc<Loaders>,
411}
412
413impl ContextImpl {
414 fn begin_pass(&mut self, mut new_raw_input: RawInput) {
415 let viewport_id = new_raw_input.viewport_id;
416 let parent_id = new_raw_input
417 .viewports
418 .get(&viewport_id)
419 .and_then(|v| v.parent)
420 .unwrap_or_default();
421 let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent_id);
422
423 if let Some(safe_area) = new_raw_input.safe_area_insets {
424 self.safe_area = safe_area;
425 }
426
427 let is_outermost_viewport = self.viewport_stack.is_empty(); self.viewport_stack.push(ids);
429
430 self.begin_pass_repaint_logic(viewport_id);
431
432 let viewport = self.viewports.entry(viewport_id).or_default();
433
434 if is_outermost_viewport && let Some(new_zoom_factor) = self.new_zoom_factor.take() {
435 let ratio = self.memory.options.zoom_factor / new_zoom_factor;
436 self.memory.options.zoom_factor = new_zoom_factor;
437
438 let input = &viewport.input;
439 let mut rect = input.content_rect();
441 rect.min = (ratio * rect.min.to_vec2()).to_pos2();
442 rect.max = (ratio * rect.max.to_vec2()).to_pos2();
443 new_raw_input.screen_rect = Some(rect);
444 }
447 let native_pixels_per_point = new_raw_input
448 .viewport()
449 .native_pixels_per_point
450 .unwrap_or(1.0);
451 let pixels_per_point = self.memory.options.zoom_factor * native_pixels_per_point;
452
453 let all_viewport_ids: ViewportIdSet = self.all_viewport_ids();
454
455 let viewport = self.viewports.entry(self.viewport_id()).or_default();
456
457 self.memory.begin_pass(&new_raw_input, &all_viewport_ids);
458
459 viewport.input = std::mem::take(&mut viewport.input).begin_pass(
460 new_raw_input,
461 viewport.repaint.requested_immediate_repaint_prev_pass(),
462 pixels_per_point,
463 self.memory.options.input_options,
464 );
465 let repaint_after = viewport.input.wants_repaint_after();
466
467 let content_rect = viewport.input.content_rect();
468
469 viewport.this_pass.begin_pass(content_rect);
470
471 {
472 let mut layers: Vec<LayerId> = viewport.prev_pass.widgets.layer_ids().collect();
473 layers.sort_by(|&a, &b| self.memory.areas().compare_order(a, b));
474
475 viewport.hits = if let Some(pos) = viewport.input.pointer.interact_pos() {
476 let interact_radius = self.memory.options.style().interaction.interact_radius;
477
478 crate::hit_test::hit_test(
479 &viewport.prev_pass.widgets,
480 &layers,
481 &self.memory.to_global,
482 pos,
483 interact_radius,
484 )
485 } else {
486 WidgetHits::default()
487 };
488
489 viewport.interact_widgets = crate::interaction::interact(
490 &viewport.interact_widgets,
491 &viewport.prev_pass.widgets,
492 &viewport.hits,
493 &viewport.input,
494 self.memory.interaction_mut(),
495 );
496 }
497
498 self.memory.areas_mut().set_state(
500 LayerId::background(),
501 AreaState {
502 pivot_pos: Some(content_rect.left_top()),
503 pivot: Align2::LEFT_TOP,
504 size: Some(content_rect.size()),
505 interactable: true,
506 last_became_visible_at: None,
507 },
508 );
509
510 #[cfg(feature = "accesskit")]
511 if self.is_accesskit_enabled {
512 profiling::scope!("accesskit");
513 use crate::pass_state::AccessKitPassState;
514 let id = crate::accesskit_root_id();
515 let mut root_node = accesskit::Node::new(accesskit::Role::Window);
516 let pixels_per_point = viewport.input.pixels_per_point();
517 root_node.set_transform(accesskit::Affine::scale(pixels_per_point.into()));
518 let mut nodes = IdMap::default();
519 nodes.insert(id, root_node);
520 viewport.this_pass.accesskit_state = Some(AccessKitPassState {
521 nodes,
522 parent_map: IdMap::default(),
523 });
524 }
525
526 self.update_fonts_mut();
527
528 if let Some(delay) = repaint_after {
529 self.request_repaint_after(delay, viewport_id, RepaintCause::new());
530 }
531 }
532
533 fn update_fonts_mut(&mut self) {
535 profiling::function_scope!();
536 let input = &self.viewport().input;
537 let max_texture_side = input.max_texture_side;
538
539 if let Some(font_definitions) = self.memory.new_font_definitions.take() {
540 self.fonts = None;
542 self.font_definitions = font_definitions;
543
544 log::trace!("Loading new font definitions");
545 }
546
547 if !self.memory.add_fonts.is_empty() {
548 let fonts = self.memory.add_fonts.drain(..);
549 for font in fonts {
550 self.fonts = None; for family in font.families {
552 let fam = self
553 .font_definitions
554 .families
555 .entry(family.family)
556 .or_default();
557 match family.priority {
558 FontPriority::Highest => fam.insert(0, font.name.clone()),
559 FontPriority::Lowest => fam.push(font.name.clone()),
560 }
561 }
562 self.font_definitions
563 .font_data
564 .insert(font.name, Arc::new(font.data));
565 }
566
567 log::trace!("Adding new fonts");
568 }
569
570 let text_alpha_from_coverage = self.memory.options.style().visuals.text_alpha_from_coverage;
571
572 let mut is_new = false;
573
574 let fonts = self.fonts.get_or_insert_with(|| {
575 log::trace!("Creating new Fonts");
576
577 is_new = true;
578 profiling::scope!("Fonts::new");
579 Fonts::new(
580 max_texture_side,
581 text_alpha_from_coverage,
582 self.font_definitions.clone(),
583 )
584 });
585
586 {
587 profiling::scope!("Fonts::begin_pass");
588 fonts.begin_pass(max_texture_side, text_alpha_from_coverage);
589 }
590 }
591
592 #[cfg(feature = "accesskit")]
593 fn accesskit_node_builder(&mut self, id: Id) -> &mut accesskit::Node {
594 let state = self.viewport().this_pass.accesskit_state.as_mut().unwrap();
595 let builders = &mut state.nodes;
596 if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) {
597 entry.insert(Default::default());
598
599 fn find_accesskit_parent(
601 parent_map: &IdMap<Id>,
602 node_map: &IdMap<accesskit::Node>,
603 id: Id,
604 ) -> Option<Id> {
605 if let Some(parent_id) = parent_map.get(&id) {
606 if node_map.contains_key(parent_id) {
607 Some(*parent_id)
608 } else {
609 find_accesskit_parent(parent_map, node_map, *parent_id)
610 }
611 } else {
612 None
613 }
614 }
615
616 let parent_id = find_accesskit_parent(&state.parent_map, builders, id)
617 .unwrap_or(crate::accesskit_root_id());
618
619 let parent_builder = builders.get_mut(&parent_id).unwrap();
620 parent_builder.push_child(id.accesskit_id());
621 }
622 builders.get_mut(&id).unwrap()
623 }
624
625 fn pixels_per_point(&mut self) -> f32 {
626 self.viewport().input.pixels_per_point
627 }
628
629 pub(crate) fn viewport_id(&self) -> ViewportId {
633 self.viewport_stack.last().copied().unwrap_or_default().this
634 }
635
636 pub(crate) fn parent_viewport_id(&self) -> ViewportId {
640 let viewport_id = self.viewport_id();
641 *self
642 .viewport_parents
643 .get(&viewport_id)
644 .unwrap_or(&ViewportId::ROOT)
645 }
646
647 fn all_viewport_ids(&self) -> ViewportIdSet {
648 self.viewports
649 .keys()
650 .copied()
651 .chain([ViewportId::ROOT])
652 .collect()
653 }
654
655 pub(crate) fn viewport(&mut self) -> &mut ViewportState {
657 self.viewports.entry(self.viewport_id()).or_default()
658 }
659
660 fn viewport_for(&mut self, viewport_id: ViewportId) -> &mut ViewportState {
661 self.viewports.entry(viewport_id).or_default()
662 }
663}
664
665#[derive(Clone)]
718pub struct Context(Arc<RwLock<ContextImpl>>);
719
720impl std::fmt::Debug for Context {
721 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
722 f.debug_struct("Context").finish_non_exhaustive()
723 }
724}
725
726impl std::cmp::PartialEq for Context {
727 fn eq(&self, other: &Self) -> bool {
728 Arc::ptr_eq(&self.0, &other.0)
729 }
730}
731
732impl Default for Context {
733 fn default() -> Self {
734 let ctx_impl = ContextImpl {
735 embed_viewports: true,
736 viewports: std::iter::once((ViewportId::ROOT, ViewportState::default())).collect(),
737 ..Default::default()
738 };
739 let ctx = Self(Arc::new(RwLock::new(ctx_impl)));
740
741 ctx.add_plugin(plugin::CallbackPlugin::default());
742
743 ctx.add_plugin(crate::debug_text::DebugTextPlugin::default());
745 ctx.add_plugin(crate::text_selection::LabelSelectionState::default());
746 ctx.add_plugin(crate::DragAndDrop::default());
747
748 ctx
749 }
750}
751
752impl Context {
753 fn read<R>(&self, reader: impl FnOnce(&ContextImpl) -> R) -> R {
755 reader(&self.0.read())
756 }
757
758 fn write<R>(&self, writer: impl FnOnce(&mut ContextImpl) -> R) -> R {
760 writer(&mut self.0.write())
761 }
762
763 #[must_use]
787 pub fn run(&self, mut new_input: RawInput, mut run_ui: impl FnMut(&Self)) -> FullOutput {
788 profiling::function_scope!();
789 let viewport_id = new_input.viewport_id;
790 let max_passes = self.write(|ctx| ctx.memory.options.max_passes.get());
791
792 let mut output = FullOutput::default();
793 debug_assert_eq!(
794 output.platform_output.num_completed_passes, 0,
795 "output must be fresh, but had {} passes",
796 output.platform_output.num_completed_passes
797 );
798
799 loop {
800 profiling::scope!(
801 "pass",
802 output
803 .platform_output
804 .num_completed_passes
805 .to_string()
806 .as_str()
807 );
808
809 self.write(|ctx| {
812 let viewport = ctx.viewport_for(viewport_id);
813 viewport.output.num_completed_passes =
814 std::mem::take(&mut output.platform_output.num_completed_passes);
815 output.platform_output.request_discard_reasons.clear();
816 });
817
818 self.begin_pass(new_input.take());
819 run_ui(self);
820 output.append(self.end_pass());
821 debug_assert!(
822 0 < output.platform_output.num_completed_passes,
823 "Completed passes was lower than 0, was {}",
824 output.platform_output.num_completed_passes
825 );
826
827 if !output.platform_output.requested_discard() {
828 break; }
830
831 if max_passes <= output.platform_output.num_completed_passes {
832 log::debug!(
833 "Ignoring call request_discard, because max_passes={max_passes}. Requested from {:?}",
834 output.platform_output.request_discard_reasons
835 );
836
837 break;
838 }
839 }
840
841 self.write(|ctx| {
842 let did_multipass = 1 < output.platform_output.num_completed_passes;
843 let viewport = ctx.viewport_for(viewport_id);
844 if did_multipass {
845 viewport.num_multipass_in_row += 1;
846 } else {
847 viewport.num_multipass_in_row = 0;
848 }
849 viewport.repaint.cumulative_frame_nr += 1;
850 });
851
852 output
853 }
854
855 pub fn begin_pass(&self, mut new_input: RawInput) {
876 profiling::function_scope!();
877
878 let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
879 plugins.on_input(&mut new_input);
880
881 self.write(|ctx| ctx.begin_pass(new_input));
882
883 plugins.on_begin_pass(self);
885 }
886
887 #[deprecated = "Renamed begin_pass"]
889 pub fn begin_frame(&self, new_input: RawInput) {
890 self.begin_pass(new_input);
891 }
892}
893
894impl Context {
898 #[inline]
913 pub fn input<R>(&self, reader: impl FnOnce(&InputState) -> R) -> R {
914 self.write(move |ctx| reader(&ctx.viewport().input))
915 }
916
917 #[inline]
919 pub fn input_for<R>(&self, id: ViewportId, reader: impl FnOnce(&InputState) -> R) -> R {
920 self.write(move |ctx| reader(&ctx.viewport_for(id).input))
921 }
922
923 #[inline]
925 pub fn input_mut<R>(&self, writer: impl FnOnce(&mut InputState) -> R) -> R {
926 self.input_mut_for(self.viewport_id(), writer)
927 }
928
929 #[inline]
931 pub fn input_mut_for<R>(&self, id: ViewportId, writer: impl FnOnce(&mut InputState) -> R) -> R {
932 self.write(move |ctx| writer(&mut ctx.viewport_for(id).input))
933 }
934
935 #[inline]
937 pub fn memory<R>(&self, reader: impl FnOnce(&Memory) -> R) -> R {
938 self.read(move |ctx| reader(&ctx.memory))
939 }
940
941 #[inline]
943 pub fn memory_mut<R>(&self, writer: impl FnOnce(&mut Memory) -> R) -> R {
944 self.write(move |ctx| writer(&mut ctx.memory))
945 }
946
947 #[inline]
949 pub fn data<R>(&self, reader: impl FnOnce(&IdTypeMap) -> R) -> R {
950 self.read(move |ctx| reader(&ctx.memory.data))
951 }
952
953 #[inline]
955 pub fn data_mut<R>(&self, writer: impl FnOnce(&mut IdTypeMap) -> R) -> R {
956 self.write(move |ctx| writer(&mut ctx.memory.data))
957 }
958
959 #[inline]
961 pub fn graphics_mut<R>(&self, writer: impl FnOnce(&mut GraphicLayers) -> R) -> R {
962 self.write(move |ctx| writer(&mut ctx.viewport().graphics))
963 }
964
965 #[inline]
967 pub fn graphics<R>(&self, reader: impl FnOnce(&GraphicLayers) -> R) -> R {
968 self.write(move |ctx| reader(&ctx.viewport().graphics))
969 }
970
971 #[inline]
980 pub fn output<R>(&self, reader: impl FnOnce(&PlatformOutput) -> R) -> R {
981 self.write(move |ctx| reader(&ctx.viewport().output))
982 }
983
984 #[inline]
986 pub fn output_mut<R>(&self, writer: impl FnOnce(&mut PlatformOutput) -> R) -> R {
987 self.write(move |ctx| writer(&mut ctx.viewport().output))
988 }
989
990 #[inline]
994 pub(crate) fn pass_state<R>(&self, reader: impl FnOnce(&PassState) -> R) -> R {
995 self.write(move |ctx| reader(&ctx.viewport().this_pass))
996 }
997
998 #[inline]
1002 pub(crate) fn pass_state_mut<R>(&self, writer: impl FnOnce(&mut PassState) -> R) -> R {
1003 self.write(move |ctx| writer(&mut ctx.viewport().this_pass))
1004 }
1005
1006 #[inline]
1010 pub(crate) fn prev_pass_state<R>(&self, reader: impl FnOnce(&PassState) -> R) -> R {
1011 self.write(move |ctx| reader(&ctx.viewport().prev_pass))
1012 }
1013
1014 #[inline]
1019 pub fn fonts<R>(&self, reader: impl FnOnce(&FontsView<'_>) -> R) -> R {
1020 self.write(move |ctx| {
1021 let pixels_per_point = ctx.pixels_per_point();
1022 reader(
1023 &ctx.fonts
1024 .as_mut()
1025 .expect("No fonts available until first call to Context::run()")
1026 .with_pixels_per_point(pixels_per_point),
1027 )
1028 })
1029 }
1030
1031 #[inline]
1036 pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut FontsView<'_>) -> R) -> R {
1037 self.write(move |ctx| {
1038 let pixels_per_point = ctx.pixels_per_point();
1039 reader(
1040 &mut ctx
1041 .fonts
1042 .as_mut()
1043 .expect("No fonts available until first call to Context::run()")
1044 .with_pixels_per_point(pixels_per_point),
1045 )
1046 })
1047 }
1048
1049 #[inline]
1051 pub fn options<R>(&self, reader: impl FnOnce(&Options) -> R) -> R {
1052 self.read(move |ctx| reader(&ctx.memory.options))
1053 }
1054
1055 #[inline]
1057 pub fn options_mut<R>(&self, writer: impl FnOnce(&mut Options) -> R) -> R {
1058 self.write(move |ctx| writer(&mut ctx.memory.options))
1059 }
1060
1061 #[inline]
1063 pub fn tessellation_options<R>(&self, reader: impl FnOnce(&TessellationOptions) -> R) -> R {
1064 self.read(move |ctx| reader(&ctx.memory.options.tessellation_options))
1065 }
1066
1067 #[inline]
1069 pub fn tessellation_options_mut<R>(
1070 &self,
1071 writer: impl FnOnce(&mut TessellationOptions) -> R,
1072 ) -> R {
1073 self.write(move |ctx| writer(&mut ctx.memory.options.tessellation_options))
1074 }
1075
1076 pub fn check_for_id_clash(&self, id: Id, new_rect: Rect, what: &str) {
1086 let prev_rect = self.pass_state_mut(move |state| state.used_ids.insert(id, new_rect));
1087
1088 if !self.options(|opt| opt.warn_on_id_clash) {
1089 return;
1090 }
1091
1092 let Some(prev_rect) = prev_rect else { return };
1093
1094 let is_same_rect = prev_rect.expand(0.1).contains_rect(new_rect)
1097 || new_rect.expand(0.1).contains_rect(prev_rect);
1098 if is_same_rect {
1099 return;
1100 }
1101
1102 let show_error = |widget_rect: Rect, text: String| {
1103 let content_rect = self.content_rect();
1104
1105 let text = format!("🔥 {text}");
1106 let color = self.style().visuals.error_fg_color;
1107 let painter = self.debug_painter();
1108 painter.rect_stroke(widget_rect, 0.0, (1.0, color), StrokeKind::Outside);
1109
1110 let below = widget_rect.bottom() + 32.0 < content_rect.bottom();
1111
1112 let text_rect = if below {
1113 painter.debug_text(
1114 widget_rect.left_bottom() + vec2(0.0, 2.0),
1115 Align2::LEFT_TOP,
1116 color,
1117 text,
1118 )
1119 } else {
1120 painter.debug_text(
1121 widget_rect.left_top() - vec2(0.0, 2.0),
1122 Align2::LEFT_BOTTOM,
1123 color,
1124 text,
1125 )
1126 };
1127
1128 if let Some(pointer_pos) = self.pointer_hover_pos()
1129 && text_rect.contains(pointer_pos)
1130 {
1131 let tooltip_pos = if below {
1132 text_rect.left_bottom() + vec2(2.0, 4.0)
1133 } else {
1134 text_rect.left_top() + vec2(2.0, -4.0)
1135 };
1136
1137 painter.error(
1138 tooltip_pos,
1139 format!("Widget is {} this text.\n\n\
1140 ID clashes happens when things like Windows or CollapsingHeaders share names,\n\
1141 or when things like Plot and Grid:s aren't given unique id_salt:s.\n\n\
1142 Sometimes the solution is to use ui.push_id.",
1143 if below { "above" } else { "below" }),
1144 );
1145 }
1146 };
1147
1148 let id_str = id.short_debug_format();
1149
1150 if prev_rect.min.distance(new_rect.min) < 4.0 {
1151 show_error(new_rect, format!("Double use of {what} ID {id_str}"));
1152 } else {
1153 show_error(prev_rect, format!("First use of {what} ID {id_str}"));
1154 show_error(new_rect, format!("Second use of {what} ID {id_str}"));
1155 }
1156 }
1157
1158 pub(crate) fn create_widget(&self, w: WidgetRect, allow_focus: bool) -> Response {
1171 let interested_in_focus = w.enabled
1172 && w.sense.is_focusable()
1173 && self.memory(|mem| mem.allows_interaction(w.layer_id));
1174
1175 self.write(|ctx| {
1177 let viewport = ctx.viewport();
1178
1179 viewport.this_pass.widgets.insert(w.layer_id, w);
1183
1184 if allow_focus && interested_in_focus {
1185 ctx.memory.interested_in_focus(w.id, w.layer_id);
1186 }
1187 });
1188
1189 if allow_focus && !interested_in_focus {
1190 self.memory_mut(|mem| mem.surrender_focus(w.id));
1192 }
1193
1194 if w.sense.interactive() || w.sense.is_focusable() {
1195 self.check_for_id_clash(w.id, w.rect, "widget");
1196 }
1197
1198 #[allow(clippy::let_and_return, clippy::allow_attributes)]
1199 let res = self.get_response(w);
1200
1201 #[cfg(debug_assertions)]
1202 if res.contains_pointer() {
1203 let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
1204 plugins.on_widget_under_pointer(self, &w);
1205 }
1206
1207 #[cfg(feature = "accesskit")]
1208 if allow_focus && w.sense.is_focusable() {
1209 self.accesskit_node_builder(w.id, |builder| res.fill_accesskit_node_common(builder));
1213 }
1214
1215 #[cfg(feature = "accesskit")]
1216 self.write(|ctx| {
1217 use crate::{Align, pass_state::ScrollTarget, style::ScrollAnimation};
1218 let viewport = ctx.viewport_for(ctx.viewport_id());
1219
1220 viewport
1221 .input
1222 .consume_accesskit_action_requests(res.id, |request| {
1223 const DISTANCE: f32 = 100.0;
1226
1227 match &request.action {
1228 accesskit::Action::ScrollIntoView => {
1229 viewport.this_pass.scroll_target = [
1230 Some(ScrollTarget::new(
1231 res.rect.x_range(),
1232 Some(Align::Center),
1233 ScrollAnimation::none(),
1234 )),
1235 Some(ScrollTarget::new(
1236 res.rect.y_range(),
1237 Some(Align::Center),
1238 ScrollAnimation::none(),
1239 )),
1240 ];
1241 }
1242 accesskit::Action::ScrollDown => {
1243 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::UP;
1244 }
1245 accesskit::Action::ScrollUp => {
1246 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::DOWN;
1247 }
1248 accesskit::Action::ScrollLeft => {
1249 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::LEFT;
1250 }
1251 accesskit::Action::ScrollRight => {
1252 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::RIGHT;
1253 }
1254 _ => return false,
1255 }
1256 true
1257 });
1258 });
1259
1260 res
1261 }
1262
1263 pub fn read_response(&self, id: Id) -> Option<Response> {
1271 self.write(|ctx| {
1272 let viewport = ctx.viewport();
1273 let widget_rect = viewport
1274 .this_pass
1275 .widgets
1276 .get(id)
1277 .or_else(|| viewport.prev_pass.widgets.get(id))
1278 .copied();
1279 widget_rect.map(|mut rect| {
1280 if !(rect.rect.is_positive() && rect.rect.is_finite())
1283 && let Some(prev_rect) = viewport.prev_pass.widgets.get(id)
1284 {
1285 rect.rect = prev_rect.rect;
1286 }
1287 rect
1288 })
1289 })
1290 .map(|widget_rect| self.get_response(widget_rect))
1291 }
1292
1293 pub(crate) fn get_response(&self, widget_rect: WidgetRect) -> Response {
1295 use response::Flags;
1296
1297 let WidgetRect {
1298 id,
1299 layer_id,
1300 rect,
1301 interact_rect,
1302 sense,
1303 enabled,
1304 } = widget_rect;
1305
1306 let highlighted = self.prev_pass_state(|fs| fs.highlight_next_pass.contains(&id));
1308
1309 let mut res = Response {
1310 ctx: self.clone(),
1311 layer_id,
1312 id,
1313 rect,
1314 interact_rect,
1315 sense,
1316 flags: Flags::empty(),
1317 interact_pointer_pos: None,
1318 intrinsic_size: None,
1319 };
1320
1321 res.flags.set(Flags::ENABLED, enabled);
1322 res.flags.set(Flags::HIGHLIGHTED, highlighted);
1323
1324 self.write(|ctx| {
1325 let viewport = ctx.viewports.entry(ctx.viewport_id()).or_default();
1326
1327 res.flags.set(
1328 Flags::CONTAINS_POINTER,
1329 viewport.interact_widgets.contains_pointer.contains(&id),
1330 );
1331
1332 let input = &viewport.input;
1333 let memory = &mut ctx.memory;
1334
1335 if enabled
1336 && sense.senses_click()
1337 && memory.has_focus(id)
1338 && (input.key_pressed(Key::Space) || input.key_pressed(Key::Enter))
1339 {
1340 res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);
1342 }
1343
1344 #[cfg(feature = "accesskit")]
1345 if enabled
1346 && sense.senses_click()
1347 && input.has_accesskit_action_request(id, accesskit::Action::Click)
1348 {
1349 res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);
1350 }
1351
1352 if enabled && sense.senses_click() && Some(id) == viewport.interact_widgets.long_touched
1353 {
1354 res.flags.set(Flags::LONG_TOUCHED, true);
1355 }
1356
1357 let interaction = memory.interaction();
1358
1359 res.flags.set(
1360 Flags::IS_POINTER_BUTTON_DOWN_ON,
1361 interaction.potential_click_id == Some(id)
1362 || interaction.potential_drag_id == Some(id),
1363 );
1364
1365 if res.enabled() {
1366 res.flags.set(
1367 Flags::HOVERED,
1368 viewport.interact_widgets.hovered.contains(&id),
1369 );
1370 res.flags.set(
1371 Flags::DRAGGED,
1372 Some(id) == viewport.interact_widgets.dragged,
1373 );
1374 res.flags.set(
1375 Flags::DRAG_STARTED,
1376 Some(id) == viewport.interact_widgets.drag_started,
1377 );
1378 res.flags.set(
1379 Flags::DRAG_STOPPED,
1380 Some(id) == viewport.interact_widgets.drag_stopped,
1381 );
1382 }
1383
1384 let clicked = Some(id) == viewport.interact_widgets.clicked;
1385 let mut any_press = false;
1386
1387 for pointer_event in &input.pointer.pointer_events {
1388 match pointer_event {
1389 PointerEvent::Moved(_) => {}
1390 PointerEvent::Pressed { .. } => {
1391 any_press = true;
1392 }
1393 PointerEvent::Released { click, .. } => {
1394 if enabled && sense.senses_click() && clicked && click.is_some() {
1395 res.flags.set(Flags::CLICKED, true);
1396 }
1397
1398 res.flags.set(Flags::IS_POINTER_BUTTON_DOWN_ON, false);
1399 res.flags.set(Flags::DRAGGED, false);
1400 }
1401 }
1402 }
1403
1404 let is_interacted_with = res.is_pointer_button_down_on()
1407 || res.long_touched()
1408 || clicked
1409 || res.drag_stopped();
1410 if is_interacted_with {
1411 res.interact_pointer_pos = input.pointer.interact_pos();
1412 if let (Some(to_global), Some(pos)) = (
1413 memory.to_global.get(&res.layer_id),
1414 &mut res.interact_pointer_pos,
1415 ) {
1416 *pos = to_global.inverse() * *pos;
1417 }
1418 }
1419
1420 if input.pointer.any_down() && !is_interacted_with {
1421 res.flags.set(Flags::HOVERED, false);
1423 }
1424
1425 let should_surrender_focus = match memory.options.input_options.surrender_focus_on {
1426 SurrenderFocusOn::Presses => any_press,
1427 SurrenderFocusOn::Clicks => input.pointer.any_click(),
1428 SurrenderFocusOn::Never => false,
1429 };
1430
1431 let pointer_clicked_elsewhere = should_surrender_focus && !res.hovered();
1432 if pointer_clicked_elsewhere && memory.has_focus(id) {
1433 memory.surrender_focus(id);
1434 }
1435 });
1436
1437 res
1438 }
1439
1440 #[inline]
1444 pub fn register_widget_info(&self, id: Id, make_info: impl Fn() -> crate::WidgetInfo) {
1445 #[cfg(debug_assertions)]
1446 self.write(|ctx| {
1447 if ctx.memory.options.style().debug.show_interactive_widgets {
1448 ctx.viewport().this_pass.widgets.set_info(id, make_info());
1449 }
1450 });
1451
1452 #[cfg(not(debug_assertions))]
1453 {
1454 _ = (self, id, make_info);
1455 }
1456 }
1457
1458 pub fn layer_painter(&self, layer_id: LayerId) -> Painter {
1460 let content_rect = self.content_rect();
1461 Painter::new(self.clone(), layer_id, content_rect)
1462 }
1463
1464 pub fn debug_painter(&self) -> Painter {
1466 Self::layer_painter(self, LayerId::debug())
1467 }
1468
1469 #[track_caller]
1483 pub fn debug_text(&self, text: impl Into<WidgetText>) {
1484 crate::debug_text::print(self, text);
1485 }
1486
1487 pub fn os(&self) -> OperatingSystem {
1495 self.read(|ctx| ctx.os)
1496 }
1497
1498 pub fn set_os(&self, os: OperatingSystem) {
1503 self.write(|ctx| ctx.os = os);
1504 }
1505
1506 pub fn set_cursor_icon(&self, cursor_icon: CursorIcon) {
1514 self.output_mut(|o| o.cursor_icon = cursor_icon);
1515 }
1516
1517 pub fn send_cmd(&self, cmd: crate::OutputCommand) {
1520 self.output_mut(|o| o.commands.push(cmd));
1521 }
1522
1523 pub fn open_url(&self, open_url: crate::OpenUrl) {
1532 self.send_cmd(crate::OutputCommand::OpenUrl(open_url));
1533 }
1534
1535 pub fn copy_text(&self, text: String) {
1541 self.send_cmd(crate::OutputCommand::CopyText(text));
1542 }
1543
1544 pub fn copy_image(&self, image: crate::ColorImage) {
1550 self.send_cmd(crate::OutputCommand::CopyImage(image));
1551 }
1552
1553 fn can_show_modifier_symbols(&self) -> bool {
1554 let ModifierNames {
1555 alt,
1556 ctrl,
1557 shift,
1558 mac_cmd,
1559 ..
1560 } = ModifierNames::SYMBOLS;
1561
1562 let font_id = TextStyle::Body.resolve(&self.style());
1563 self.fonts_mut(|f| {
1564 let mut font = f.fonts.font(&font_id.family);
1565 font.has_glyphs(alt)
1566 && font.has_glyphs(ctrl)
1567 && font.has_glyphs(shift)
1568 && font.has_glyphs(mac_cmd)
1569 })
1570 }
1571
1572 pub fn format_modifiers(&self, modifiers: Modifiers) -> String {
1574 let os = self.os();
1575
1576 let is_mac = os.is_mac();
1577
1578 if is_mac && self.can_show_modifier_symbols() {
1579 ModifierNames::SYMBOLS.format(&modifiers, is_mac)
1580 } else {
1581 ModifierNames::NAMES.format(&modifiers, is_mac)
1582 }
1583 }
1584
1585 pub fn format_shortcut(&self, shortcut: &KeyboardShortcut) -> String {
1589 let os = self.os();
1590
1591 let is_mac = os.is_mac();
1592
1593 if is_mac && self.can_show_modifier_symbols() {
1594 shortcut.format(&ModifierNames::SYMBOLS, is_mac)
1595 } else {
1596 shortcut.format(&ModifierNames::NAMES, is_mac)
1597 }
1598 }
1599
1600 pub fn cumulative_frame_nr(&self) -> u64 {
1606 self.cumulative_frame_nr_for(self.viewport_id())
1607 }
1608
1609 pub fn cumulative_frame_nr_for(&self, id: ViewportId) -> u64 {
1615 self.read(|ctx| {
1616 ctx.viewports
1617 .get(&id)
1618 .map(|v| v.repaint.cumulative_frame_nr)
1619 .unwrap_or_else(|| {
1620 if cfg!(debug_assertions) {
1621 panic!("cumulative_frame_nr_for failed to find the viewport {id:?}");
1622 } else {
1623 0
1624 }
1625 })
1626 })
1627 }
1628
1629 pub fn cumulative_pass_nr(&self) -> u64 {
1636 self.cumulative_pass_nr_for(self.viewport_id())
1637 }
1638
1639 pub fn cumulative_pass_nr_for(&self, id: ViewportId) -> u64 {
1643 self.read(|ctx| {
1644 ctx.viewports
1645 .get(&id)
1646 .map_or(0, |v| v.repaint.cumulative_pass_nr)
1647 })
1648 }
1649
1650 pub fn current_pass_index(&self) -> usize {
1659 self.output(|o| o.num_completed_passes)
1660 }
1661
1662 #[track_caller]
1675 pub fn request_repaint(&self) {
1676 self.request_repaint_of(self.viewport_id());
1677 }
1678
1679 #[track_caller]
1692 pub fn request_repaint_of(&self, id: ViewportId) {
1693 let cause = RepaintCause::new();
1694 self.write(|ctx| ctx.request_repaint(id, cause));
1695 }
1696
1697 #[track_caller]
1726 pub fn request_repaint_after(&self, duration: Duration) {
1727 self.request_repaint_after_for(duration, self.viewport_id());
1728 }
1729
1730 #[track_caller]
1734 pub fn request_repaint_after_secs(&self, seconds: f32) {
1735 if let Ok(duration) = std::time::Duration::try_from_secs_f32(seconds) {
1736 self.request_repaint_after(duration);
1737 }
1738 }
1739
1740 #[track_caller]
1769 pub fn request_repaint_after_for(&self, duration: Duration, id: ViewportId) {
1770 let cause = RepaintCause::new();
1771 self.write(|ctx| ctx.request_repaint_after(duration, id, cause));
1772 }
1773
1774 #[must_use]
1776 pub fn requested_repaint_last_pass(&self) -> bool {
1777 self.requested_repaint_last_pass_for(&self.viewport_id())
1778 }
1779
1780 #[must_use]
1782 pub fn requested_repaint_last_pass_for(&self, viewport_id: &ViewportId) -> bool {
1783 self.read(|ctx| ctx.requested_immediate_repaint_prev_pass(viewport_id))
1784 }
1785
1786 #[must_use]
1788 pub fn has_requested_repaint(&self) -> bool {
1789 self.has_requested_repaint_for(&self.viewport_id())
1790 }
1791
1792 #[must_use]
1794 pub fn has_requested_repaint_for(&self, viewport_id: &ViewportId) -> bool {
1795 self.read(|ctx| ctx.has_requested_repaint(viewport_id))
1796 }
1797
1798 pub fn repaint_causes(&self) -> Vec<RepaintCause> {
1802 self.read(|ctx| {
1803 ctx.viewports
1804 .get(&ctx.viewport_id())
1805 .map(|v| v.repaint.prev_causes.clone())
1806 })
1807 .unwrap_or_default()
1808 }
1809
1810 pub fn set_request_repaint_callback(
1816 &self,
1817 callback: impl Fn(RequestRepaintInfo) + Send + Sync + 'static,
1818 ) {
1819 let callback = Box::new(callback);
1820 self.write(|ctx| ctx.request_repaint_callback = Some(callback));
1821 }
1822
1823 #[track_caller]
1846 pub fn request_discard(&self, reason: impl Into<Cow<'static, str>>) {
1847 let cause = RepaintCause::new_reason(reason);
1848 self.output_mut(|o| o.request_discard_reasons.push(cause));
1849
1850 log::trace!(
1851 "request_discard: {}",
1852 if self.will_discard() {
1853 "allowed"
1854 } else {
1855 "denied"
1856 }
1857 );
1858 }
1859
1860 pub fn will_discard(&self) -> bool {
1866 self.write(|ctx| {
1867 let vp = ctx.viewport();
1868 vp.output.requested_discard()
1870 && vp.output.num_completed_passes + 1 < ctx.memory.options.max_passes.get()
1871 })
1872 }
1873}
1874
1875impl Context {
1877 pub fn on_begin_pass(&self, debug_name: &'static str, cb: plugin::ContextCallback) {
1881 self.with_plugin(|p: &mut crate::plugin::CallbackPlugin| {
1882 p.on_begin_plugins.push((debug_name, cb));
1883 });
1884 }
1885
1886 pub fn on_end_pass(&self, debug_name: &'static str, cb: plugin::ContextCallback) {
1890 self.with_plugin(|p: &mut crate::plugin::CallbackPlugin| {
1891 p.on_end_plugins.push((debug_name, cb));
1892 });
1893 }
1894
1895 pub fn add_plugin(&self, plugin: impl plugin::Plugin + 'static) {
1902 let handle = plugin::PluginHandle::new(plugin);
1903
1904 let added = self.write(|ctx| ctx.plugins.add(handle.clone()));
1905
1906 if added {
1907 handle.lock().dyn_plugin_mut().setup(self);
1908 }
1909 }
1910
1911 pub fn with_plugin<T: plugin::Plugin + 'static, R>(
1915 &self,
1916 f: impl FnOnce(&mut T) -> R,
1917 ) -> Option<R> {
1918 let plugin = self.read(|ctx| ctx.plugins.get(std::any::TypeId::of::<T>()));
1919 plugin.map(|plugin| f(plugin.lock().typed_plugin_mut()))
1920 }
1921
1922 pub fn plugin<T: plugin::Plugin>(&self) -> TypedPluginHandle<T> {
1927 if let Some(plugin) = self.plugin_opt() {
1928 plugin
1929 } else {
1930 panic!("Plugin of type {:?} not found", std::any::type_name::<T>());
1931 }
1932 }
1933
1934 pub fn plugin_opt<T: plugin::Plugin>(&self) -> Option<TypedPluginHandle<T>> {
1936 let plugin = self.read(|ctx| ctx.plugins.get(std::any::TypeId::of::<T>()));
1937 plugin.map(TypedPluginHandle::new)
1938 }
1939
1940 pub fn plugin_or_default<T: plugin::Plugin + Default>(&self) -> TypedPluginHandle<T> {
1942 if let Some(plugin) = self.plugin_opt() {
1943 plugin
1944 } else {
1945 let default_plugin = T::default();
1946 self.add_plugin(default_plugin);
1947 self.plugin()
1948 }
1949 }
1950}
1951
1952impl Context {
1953 pub fn set_fonts(&self, font_definitions: FontDefinitions) {
1961 profiling::function_scope!();
1962
1963 let mut update_fonts = true;
1964
1965 self.read(|ctx| {
1966 if let Some(current_fonts) = ctx.fonts.as_ref() {
1967 if current_fonts.definitions() == &font_definitions {
1969 update_fonts = false; }
1971 }
1972 });
1973
1974 if update_fonts {
1975 self.memory_mut(|mem| mem.new_font_definitions = Some(font_definitions));
1976 }
1977 }
1978
1979 pub fn add_font(&self, new_font: FontInsert) {
1987 profiling::function_scope!();
1988
1989 let mut update_fonts = true;
1990
1991 self.read(|ctx| {
1992 if let Some(current_fonts) = ctx.fonts.as_ref()
1993 && current_fonts
1994 .definitions()
1995 .font_data
1996 .contains_key(&new_font.name)
1997 {
1998 update_fonts = false; }
2000 });
2001
2002 if update_fonts {
2003 self.memory_mut(|mem| mem.add_fonts.push(new_font));
2004 }
2005 }
2006
2007 pub fn system_theme(&self) -> Option<Theme> {
2010 self.memory(|mem| mem.options.system_theme)
2011 }
2012
2013 pub fn theme(&self) -> Theme {
2016 self.options(|opt| opt.theme())
2017 }
2018
2019 pub fn set_theme(&self, theme_preference: impl Into<crate::ThemePreference>) {
2028 self.options_mut(|opt| opt.theme_preference = theme_preference.into());
2029 }
2030
2031 pub fn style(&self) -> Arc<Style> {
2033 self.options(|opt| opt.style().clone())
2034 }
2035
2036 pub fn style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {
2047 self.options_mut(|opt| mutate_style(Arc::make_mut(opt.style_mut())));
2048 }
2049
2050 pub fn set_style(&self, style: impl Into<Arc<Style>>) {
2058 self.options_mut(|opt| *opt.style_mut() = style.into());
2059 }
2060
2061 pub fn all_styles_mut(&self, mut mutate_style: impl FnMut(&mut Style)) {
2071 self.options_mut(|opt| {
2072 mutate_style(Arc::make_mut(&mut opt.dark_style));
2073 mutate_style(Arc::make_mut(&mut opt.light_style));
2074 });
2075 }
2076
2077 pub fn style_of(&self, theme: Theme) -> Arc<Style> {
2079 self.options(|opt| match theme {
2080 Theme::Dark => opt.dark_style.clone(),
2081 Theme::Light => opt.light_style.clone(),
2082 })
2083 }
2084
2085 pub fn style_mut_of(&self, theme: Theme, mutate_style: impl FnOnce(&mut Style)) {
2095 self.options_mut(|opt| match theme {
2096 Theme::Dark => mutate_style(Arc::make_mut(&mut opt.dark_style)),
2097 Theme::Light => mutate_style(Arc::make_mut(&mut opt.light_style)),
2098 });
2099 }
2100
2101 pub fn set_style_of(&self, theme: Theme, style: impl Into<Arc<Style>>) {
2108 let style = style.into();
2109 self.options_mut(|opt| match theme {
2110 Theme::Dark => opt.dark_style = style,
2111 Theme::Light => opt.light_style = style,
2112 });
2113 }
2114
2115 pub fn set_visuals_of(&self, theme: Theme, visuals: crate::Visuals) {
2125 self.style_mut_of(theme, |style| style.visuals = visuals);
2126 }
2127
2128 pub fn set_visuals(&self, visuals: crate::Visuals) {
2138 self.style_mut_of(self.theme(), |style| style.visuals = visuals);
2139 }
2140
2141 #[inline(always)]
2145 pub fn pixels_per_point(&self) -> f32 {
2146 self.input(|i| i.pixels_per_point)
2147 }
2148
2149 pub fn set_pixels_per_point(&self, pixels_per_point: f32) {
2154 if pixels_per_point != self.pixels_per_point() {
2155 self.set_zoom_factor(pixels_per_point / self.native_pixels_per_point().unwrap_or(1.0));
2156 }
2157 }
2158
2159 #[inline(always)]
2164 pub fn native_pixels_per_point(&self) -> Option<f32> {
2165 self.input(|i| i.viewport().native_pixels_per_point)
2166 }
2167
2168 #[inline(always)]
2176 pub fn zoom_factor(&self) -> f32 {
2177 self.options(|o| o.zoom_factor)
2178 }
2179
2180 #[inline(always)]
2194 pub fn set_zoom_factor(&self, zoom_factor: f32) {
2195 let cause = RepaintCause::new();
2196 self.write(|ctx| {
2197 if ctx.memory.options.zoom_factor != zoom_factor {
2198 ctx.new_zoom_factor = Some(zoom_factor);
2199 #[expect(clippy::iter_over_hash_type)]
2200 for viewport_id in ctx.all_viewport_ids() {
2201 ctx.request_repaint(viewport_id, cause.clone());
2202 }
2203 }
2204 });
2205 }
2206
2207 pub fn load_texture(
2248 &self,
2249 name: impl Into<String>,
2250 image: impl Into<ImageData>,
2251 options: TextureOptions,
2252 ) -> TextureHandle {
2253 let name = name.into();
2254 let image = image.into();
2255 let max_texture_side = self.input(|i| i.max_texture_side);
2256 debug_assert!(
2257 image.width() <= max_texture_side && image.height() <= max_texture_side,
2258 "Texture {:?} has size {}x{}, but the maximum texture side is {}",
2259 name,
2260 image.width(),
2261 image.height(),
2262 max_texture_side
2263 );
2264 let tex_mngr = self.tex_manager();
2265 let tex_id = tex_mngr.write().alloc(name, image, options);
2266 TextureHandle::new(tex_mngr, tex_id)
2267 }
2268
2269 pub fn tex_manager(&self) -> Arc<RwLock<epaint::textures::TextureManager>> {
2275 self.read(|ctx| ctx.tex_manager.0.clone())
2276 }
2277
2278 pub(crate) fn constrain_window_rect_to_area(window: Rect, area: Rect) -> Rect {
2282 let mut pos = window.min;
2283
2284 let margin_x = (window.width() - area.width()).at_least(0.0);
2286 let margin_y = (window.height() - area.height()).at_least(0.0);
2287
2288 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()
2294 }
2295}
2296
2297impl Context {
2298 #[must_use]
2300 pub fn end_pass(&self) -> FullOutput {
2301 profiling::function_scope!();
2302
2303 if self.options(|o| o.zoom_with_keyboard) {
2304 crate::gui_zoom::zoom_with_keyboard(self);
2305 }
2306
2307 let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
2309 plugins.on_end_pass(self);
2310
2311 #[cfg(debug_assertions)]
2312 self.debug_painting();
2313
2314 let mut output = self.write(|ctx| ctx.end_pass());
2315 plugins.on_output(&mut output);
2316 output
2317 }
2318
2319 #[must_use]
2321 #[deprecated = "Renamed end_pass"]
2322 pub fn end_frame(&self) -> FullOutput {
2323 self.end_pass()
2324 }
2325
2326 #[cfg(debug_assertions)]
2328 fn debug_painting(&self) {
2329 #![expect(clippy::iter_over_hash_type)] let paint_widget = |widget: &WidgetRect, text: &str, color: Color32| {
2332 let rect = widget.interact_rect;
2333 if rect.is_positive() {
2334 let painter = Painter::new(self.clone(), widget.layer_id, Rect::EVERYTHING);
2335 painter.debug_rect(rect, color, text);
2336 }
2337 };
2338
2339 let paint_widget_id = |id: Id, text: &str, color: Color32| {
2340 if let Some(widget) =
2341 self.write(|ctx| ctx.viewport().this_pass.widgets.get(id).copied())
2342 {
2343 paint_widget(&widget, text, color);
2344 }
2345 };
2346
2347 if self.style().debug.show_interactive_widgets {
2348 let rects = self.write(|ctx| ctx.viewport().this_pass.widgets.clone());
2350 for (layer_id, rects) in rects.layers() {
2351 let painter = Painter::new(self.clone(), *layer_id, Rect::EVERYTHING);
2352 for rect in rects {
2353 if rect.sense.interactive() {
2354 let (color, text) = if rect.sense.senses_click() && rect.sense.senses_drag()
2355 {
2356 (Color32::from_rgb(0x88, 0, 0x88), "click+drag")
2357 } else if rect.sense.senses_click() {
2358 (Color32::from_rgb(0x88, 0, 0), "click")
2359 } else if rect.sense.senses_drag() {
2360 (Color32::from_rgb(0, 0, 0x88), "drag")
2361 } else {
2362 (Color32::from_rgb(0, 0, 0x88), "hover")
2364 };
2365 painter.debug_rect(rect.interact_rect, color, text);
2366 }
2367 }
2368 }
2369
2370 {
2372 let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());
2373 let InteractionSnapshot {
2374 clicked,
2375 long_touched: _,
2376 drag_started: _,
2377 dragged,
2378 drag_stopped: _,
2379 contains_pointer,
2380 hovered,
2381 } = interact_widgets;
2382
2383 if true {
2384 for &id in &contains_pointer {
2385 paint_widget_id(id, "contains_pointer", Color32::BLUE);
2386 }
2387
2388 let widget_rects = self.write(|w| w.viewport().this_pass.widgets.clone());
2389
2390 let mut contains_pointer: Vec<Id> = contains_pointer.iter().copied().collect();
2391 contains_pointer.sort_by_key(|&id| {
2392 widget_rects
2393 .order(id)
2394 .map(|(layer_id, order_in_layer)| (layer_id.order, order_in_layer))
2395 });
2396
2397 let mut debug_text = "Widgets in order:\n".to_owned();
2398 for id in contains_pointer {
2399 let mut widget_text = format!("{id:?}");
2400 if let Some(rect) = widget_rects.get(id) {
2401 widget_text +=
2402 &format!(" {:?} {:?} {:?}", rect.layer_id, rect.rect, rect.sense);
2403 }
2404 if let Some(info) = widget_rects.info(id) {
2405 widget_text += &format!(" {info:?}");
2406 }
2407 debug_text += &format!("{widget_text}\n");
2408 }
2409 self.debug_text(debug_text);
2410 }
2411 if true {
2412 for widget in hovered {
2413 paint_widget_id(widget, "hovered", Color32::WHITE);
2414 }
2415 }
2416 if let Some(widget) = clicked {
2417 paint_widget_id(widget, "clicked", Color32::RED);
2418 }
2419 if let Some(widget) = dragged {
2420 paint_widget_id(widget, "dragged", Color32::GREEN);
2421 }
2422 }
2423 }
2424
2425 if self.style().debug.show_widget_hits {
2426 let hits = self.write(|ctx| ctx.viewport().hits.clone());
2427 let WidgetHits {
2428 close,
2429 contains_pointer,
2430 click,
2431 drag,
2432 } = hits;
2433
2434 if false {
2435 for widget in &close {
2436 paint_widget(widget, "close", Color32::from_gray(70));
2437 }
2438 }
2439 if true {
2440 for widget in &contains_pointer {
2441 paint_widget(widget, "contains_pointer", Color32::BLUE);
2442 }
2443 }
2444 if let Some(widget) = &click {
2445 paint_widget(widget, "click", Color32::RED);
2446 }
2447 if let Some(widget) = &drag {
2448 paint_widget(widget, "drag", Color32::GREEN);
2449 }
2450 }
2451
2452 if let Some(debug_rect) = self.pass_state_mut(|fs| fs.debug_rect.take()) {
2453 debug_rect.paint(&self.debug_painter());
2454 }
2455
2456 let num_multipass_in_row = self.viewport(|vp| vp.num_multipass_in_row);
2457 if 3 <= num_multipass_in_row {
2458 let mut warning = format!(
2462 "egui PERF WARNING: request_discard has been called {num_multipass_in_row} frames in a row"
2463 );
2464 self.viewport(|vp| {
2465 for reason in &vp.output.request_discard_reasons {
2466 warning += &format!("\n {reason}");
2467 }
2468 });
2469
2470 self.debug_painter()
2471 .debug_text(Pos2::ZERO, Align2::LEFT_TOP, Color32::RED, warning);
2472 }
2473 }
2474}
2475
2476impl ContextImpl {
2477 fn end_pass(&mut self) -> FullOutput {
2478 let ended_viewport_id = self.viewport_id();
2479 let viewport = self.viewports.entry(ended_viewport_id).or_default();
2480 let pixels_per_point = viewport.input.pixels_per_point;
2481
2482 self.loaders.end_pass(viewport.repaint.cumulative_pass_nr);
2483
2484 viewport.repaint.cumulative_pass_nr += 1;
2485
2486 self.memory.end_pass(&viewport.this_pass.used_ids);
2487
2488 if let Some(fonts) = self.fonts.as_mut() {
2489 let tex_mngr = &mut self.tex_manager.0.write();
2490 if let Some(font_image_delta) = fonts.font_image_delta() {
2491 tex_mngr.set(TextureId::default(), font_image_delta);
2493 }
2494 }
2495
2496 let textures_delta = self.tex_manager.0.write().take_delta();
2498
2499 let mut platform_output: PlatformOutput = std::mem::take(&mut viewport.output);
2500
2501 #[cfg(feature = "accesskit")]
2502 {
2503 profiling::scope!("accesskit");
2504 let state = viewport.this_pass.accesskit_state.take();
2505 if let Some(state) = state {
2506 let root_id = crate::accesskit_root_id().accesskit_id();
2507 let nodes = {
2508 state
2509 .nodes
2510 .into_iter()
2511 .map(|(id, node)| (id.accesskit_id(), node))
2512 .collect()
2513 };
2514 let focus_id = self
2515 .memory
2516 .focused()
2517 .map_or(root_id, |id| id.accesskit_id());
2518 platform_output.accesskit_update = Some(accesskit::TreeUpdate {
2519 nodes,
2520 tree: Some(accesskit::Tree::new(root_id)),
2521 focus: focus_id,
2522 });
2523 }
2524 }
2525
2526 let shapes = viewport
2527 .graphics
2528 .drain(self.memory.areas().order(), &self.memory.to_global);
2529
2530 let mut repaint_needed = false;
2531
2532 if self.memory.options.repaint_on_widget_change {
2533 profiling::scope!("compare-widget-rects");
2534 #[allow(clippy::allow_attributes, clippy::collapsible_if)] if viewport.prev_pass.widgets != viewport.this_pass.widgets {
2536 repaint_needed = true; }
2538 }
2539
2540 std::mem::swap(&mut viewport.prev_pass, &mut viewport.this_pass);
2541
2542 if repaint_needed {
2543 self.request_repaint(ended_viewport_id, RepaintCause::new());
2544 }
2545 let all_viewport_ids = self.all_viewport_ids();
2548
2549 self.last_viewport = ended_viewport_id;
2550
2551 self.viewports.retain(|&id, viewport| {
2552 if id == ViewportId::ROOT {
2553 return true; }
2555
2556 let parent = *self.viewport_parents.entry(id).or_default();
2557
2558 if !all_viewport_ids.contains(&parent) {
2559 log::debug!(
2560 "Removing viewport {:?} ({:?}): the parent is gone",
2561 id,
2562 viewport.builder.title
2563 );
2564
2565 return false;
2566 }
2567
2568 let is_our_child = parent == ended_viewport_id && id != ViewportId::ROOT;
2569 if is_our_child {
2570 if !viewport.used {
2571 log::debug!(
2572 "Removing viewport {:?} ({:?}): it was never used this pass",
2573 id,
2574 viewport.builder.title
2575 );
2576
2577 return false; }
2579
2580 viewport.used = false; }
2582
2583 true
2584 });
2585
2586 self.viewport_stack.pop();
2588
2589 let is_last = self.viewport_stack.is_empty();
2592
2593 let viewport_output = self
2594 .viewports
2595 .iter_mut()
2596 .map(|(&id, viewport)| {
2597 let parent = *self.viewport_parents.entry(id).or_default();
2598 let commands = if is_last {
2599 std::mem::take(&mut viewport.commands)
2603 } else {
2604 vec![]
2605 };
2606
2607 (
2608 id,
2609 ViewportOutput {
2610 parent,
2611 class: viewport.class,
2612 builder: viewport.builder.clone(),
2613 viewport_ui_cb: viewport.viewport_ui_cb.clone(),
2614 commands,
2615 repaint_delay: viewport.repaint.repaint_delay,
2616 },
2617 )
2618 })
2619 .collect();
2620
2621 if is_last {
2622 self.viewports.retain(|id, _| all_viewport_ids.contains(id));
2624 debug_assert!(
2625 self.viewports.contains_key(&ViewportId::ROOT),
2626 "Bug in egui: we removed the root viewport"
2627 );
2628 self.viewport_parents
2629 .retain(|id, _| all_viewport_ids.contains(id));
2630 } else {
2631 let viewport_id = self.viewport_id();
2632 self.memory.set_viewport_id(viewport_id);
2633 }
2634
2635 platform_output.num_completed_passes += 1;
2636
2637 FullOutput {
2638 platform_output,
2639 textures_delta,
2640 shapes,
2641 pixels_per_point,
2642 viewport_output,
2643 }
2644 }
2645}
2646
2647impl Context {
2648 pub fn tessellate(
2654 &self,
2655 shapes: Vec<ClippedShape>,
2656 pixels_per_point: f32,
2657 ) -> Vec<ClippedPrimitive> {
2658 profiling::function_scope!();
2659
2660 self.write(|ctx| {
2665 let tessellation_options = ctx.memory.options.tessellation_options;
2666 let texture_atlas = if let Some(fonts) = ctx.fonts.as_ref() {
2667 fonts.texture_atlas()
2668 } else {
2669 log::warn!("No font size matching {pixels_per_point} pixels per point found.");
2670 ctx.fonts
2671 .iter()
2672 .next()
2673 .expect("No fonts loaded")
2674 .texture_atlas()
2675 };
2676
2677 let paint_stats = PaintStats::from_shapes(&shapes);
2678 let clipped_primitives = {
2679 profiling::scope!("tessellator::tessellate_shapes");
2680 tessellator::Tessellator::new(
2681 pixels_per_point,
2682 tessellation_options,
2683 texture_atlas.size(),
2684 texture_atlas.prepared_discs(),
2685 )
2686 .tessellate_shapes(shapes)
2687 };
2688 ctx.paint_stats = paint_stats.with_clipped_primitives(&clipped_primitives);
2689 clipped_primitives
2690 })
2691 }
2692
2693 pub fn content_rect(&self) -> Rect {
2702 self.input(|i| i.content_rect()).round_ui()
2703 }
2704
2705 pub fn viewport_rect(&self) -> Rect {
2716 self.input(|i| i.viewport_rect()).round_ui()
2717 }
2718
2719 #[deprecated(
2721 note = "screen_rect has been split into viewport_rect() and content_rect(). You likely should use content_rect()"
2722 )]
2723 pub fn screen_rect(&self) -> Rect {
2724 self.input(|i| i.content_rect()).round_ui()
2725 }
2726
2727 pub fn available_rect(&self) -> Rect {
2729 self.pass_state(|s| s.available_rect()).round_ui()
2730 }
2731
2732 pub fn used_rect(&self) -> Rect {
2734 self.write(|ctx| {
2735 let mut used = ctx.viewport().this_pass.used_by_panels;
2736 for (_id, window) in ctx.memory.areas().visible_windows() {
2737 used |= window.rect();
2738 }
2739 used.round_ui()
2740 })
2741 }
2742
2743 pub fn used_size(&self) -> Vec2 {
2747 (self.used_rect().max - Pos2::ZERO).round_ui()
2748 }
2749
2750 pub fn is_pointer_over_area(&self) -> bool {
2754 let pointer_pos = self.input(|i| i.pointer.interact_pos());
2755 if let Some(pointer_pos) = pointer_pos {
2756 if let Some(layer) = self.layer_id_at(pointer_pos) {
2757 if layer.order == Order::Background {
2758 !self.pass_state(|state| state.unused_rect.contains(pointer_pos))
2759 } else {
2760 true
2761 }
2762 } else {
2763 false
2764 }
2765 } else {
2766 false
2767 }
2768 }
2769
2770 pub fn wants_pointer_input(&self) -> bool {
2777 self.is_using_pointer()
2778 || (self.is_pointer_over_area() && !self.input(|i| i.pointer.any_down()))
2779 }
2780
2781 pub fn is_using_pointer(&self) -> bool {
2785 self.memory(|m| m.interaction().is_using_pointer())
2786 }
2787
2788 pub fn wants_keyboard_input(&self) -> bool {
2790 self.memory(|m| m.focused().is_some())
2791 }
2792
2793 pub fn highlight_widget(&self, id: Id) {
2800 self.pass_state_mut(|fs| fs.highlight_next_pass.insert(id));
2801 }
2802
2803 #[expect(deprecated)]
2807 #[deprecated = "Use `is_popup_open` instead"]
2808 pub fn is_context_menu_open(&self) -> bool {
2809 self.data(|d| {
2810 d.get_temp::<crate::menu::BarState>(crate::menu::CONTEXT_MENU_ID_STR.into())
2811 .is_some_and(|state| state.has_root())
2812 })
2813 }
2814
2815 pub fn is_popup_open(&self) -> bool {
2819 self.pass_state_mut(|fs| {
2820 fs.layers
2821 .values()
2822 .any(|layer| !layer.open_popups.is_empty())
2823 })
2824 }
2825}
2826
2827impl Context {
2829 #[inline(always)]
2833 pub fn pointer_latest_pos(&self) -> Option<Pos2> {
2834 self.input(|i| i.pointer.latest_pos())
2835 }
2836
2837 #[inline(always)]
2839 pub fn pointer_hover_pos(&self) -> Option<Pos2> {
2840 self.input(|i| i.pointer.hover_pos())
2841 }
2842
2843 #[inline(always)]
2849 pub fn pointer_interact_pos(&self) -> Option<Pos2> {
2850 self.input(|i| i.pointer.interact_pos())
2851 }
2852
2853 pub fn multi_touch(&self) -> Option<MultiTouchInfo> {
2855 self.input(|i| i.multi_touch())
2856 }
2857}
2858
2859impl Context {
2860 pub fn set_transform_layer(&self, layer_id: LayerId, transform: TSTransform) {
2872 self.memory_mut(|m| {
2873 if transform == TSTransform::IDENTITY {
2874 m.to_global.remove(&layer_id)
2875 } else {
2876 m.to_global.insert(layer_id, transform)
2877 }
2878 });
2879 }
2880
2881 pub fn layer_transform_to_global(&self, layer_id: LayerId) -> Option<TSTransform> {
2885 self.memory(|m| m.to_global.get(&layer_id).copied())
2886 }
2887
2888 pub fn layer_transform_from_global(&self, layer_id: LayerId) -> Option<TSTransform> {
2892 self.layer_transform_to_global(layer_id)
2893 .map(|t| t.inverse())
2894 }
2895
2896 pub fn transform_layer_shapes(&self, layer_id: LayerId, transform: TSTransform) {
2904 if transform != TSTransform::IDENTITY {
2905 self.graphics_mut(|g| g.entry(layer_id).transform(transform));
2906 }
2907 }
2908
2909 pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
2911 self.memory(|mem| mem.layer_id_at(pos))
2912 }
2913
2914 pub fn move_to_top(&self, layer_id: LayerId) {
2918 self.memory_mut(|mem| mem.areas_mut().move_to_top(layer_id));
2919 }
2920
2921 pub fn set_sublayer(&self, parent: LayerId, child: LayerId) {
2929 self.memory_mut(|mem| mem.areas_mut().set_sublayer(parent, child));
2930 }
2931
2932 pub fn top_layer_id(&self) -> Option<LayerId> {
2934 self.memory(|mem| mem.areas().top_layer_id(Order::Middle))
2935 }
2936
2937 pub fn rect_contains_pointer(&self, layer_id: LayerId, rect: Rect) -> bool {
2945 let rect = if let Some(to_global) = self.layer_transform_to_global(layer_id) {
2946 to_global * rect
2947 } else {
2948 rect
2949 };
2950 if !rect.is_positive() {
2951 return false;
2952 }
2953
2954 let pointer_pos = self.input(|i| i.pointer.interact_pos());
2955 let Some(pointer_pos) = pointer_pos else {
2956 return false;
2957 };
2958
2959 if !rect.contains(pointer_pos) {
2960 return false;
2961 }
2962
2963 if self.layer_id_at(pointer_pos) != Some(layer_id) {
2964 return false;
2965 }
2966
2967 true
2968 }
2969
2970 #[cfg(debug_assertions)]
2974 pub fn debug_on_hover(&self) -> bool {
2975 self.options(|opt| opt.style().debug.debug_on_hover)
2976 }
2977
2978 #[cfg(debug_assertions)]
2980 pub fn set_debug_on_hover(&self, debug_on_hover: bool) {
2981 self.all_styles_mut(|style| style.debug.debug_on_hover = debug_on_hover);
2982 }
2983}
2984
2985impl Context {
2987 #[track_caller] pub fn animate_bool(&self, id: Id, value: bool) -> f32 {
2998 let animation_time = self.style().animation_time;
2999 self.animate_bool_with_time_and_easing(id, value, animation_time, emath::easing::linear)
3000 }
3001
3002 #[track_caller] pub fn animate_bool_responsive(&self, id: Id, value: bool) -> f32 {
3008 self.animate_bool_with_easing(id, value, emath::easing::cubic_out)
3009 }
3010
3011 #[track_caller] pub fn animate_bool_with_easing(&self, id: Id, value: bool, easing: fn(f32) -> f32) -> f32 {
3014 let animation_time = self.style().animation_time;
3015 self.animate_bool_with_time_and_easing(id, value, animation_time, easing)
3016 }
3017
3018 #[track_caller] pub fn animate_bool_with_time(&self, id: Id, target_value: bool, animation_time: f32) -> f32 {
3021 self.animate_bool_with_time_and_easing(
3022 id,
3023 target_value,
3024 animation_time,
3025 emath::easing::linear,
3026 )
3027 }
3028
3029 #[track_caller] pub fn animate_bool_with_time_and_easing(
3038 &self,
3039 id: Id,
3040 target_value: bool,
3041 animation_time: f32,
3042 easing: fn(f32) -> f32,
3043 ) -> f32 {
3044 let animated_value = self.write(|ctx| {
3045 ctx.animation_manager.animate_bool(
3046 &ctx.viewports.entry(ctx.viewport_id()).or_default().input,
3047 animation_time,
3048 id,
3049 target_value,
3050 )
3051 });
3052
3053 let animation_in_progress = 0.0 < animated_value && animated_value < 1.0;
3054 if animation_in_progress {
3055 self.request_repaint();
3056 }
3057
3058 if target_value {
3059 easing(animated_value)
3060 } else {
3061 1.0 - easing(1.0 - animated_value)
3062 }
3063 }
3064
3065 #[track_caller] pub fn animate_value_with_time(&self, id: Id, target_value: f32, animation_time: f32) -> f32 {
3071 let animated_value = self.write(|ctx| {
3072 ctx.animation_manager.animate_value(
3073 &ctx.viewports.entry(ctx.viewport_id()).or_default().input,
3074 animation_time,
3075 id,
3076 target_value,
3077 )
3078 });
3079 let animation_in_progress = animated_value != target_value;
3080 if animation_in_progress {
3081 self.request_repaint();
3082 }
3083
3084 animated_value
3085 }
3086
3087 pub fn clear_animations(&self) {
3089 self.write(|ctx| ctx.animation_manager = Default::default());
3090 }
3091}
3092
3093impl Context {
3094 pub fn settings_ui(&self, ui: &mut Ui) {
3096 let prev_options = self.options(|o| o.clone());
3097 let mut options = prev_options.clone();
3098
3099 ui.collapsing("🔠 Font tweak", |ui| {
3100 self.fonts_tweak_ui(ui);
3101 });
3102
3103 options.ui(ui);
3104
3105 if options != prev_options {
3106 self.options_mut(move |o| *o = options);
3107 }
3108 }
3109
3110 fn fonts_tweak_ui(&self, ui: &mut Ui) {
3111 let mut font_definitions = self.write(|ctx| ctx.font_definitions.clone());
3112 let mut changed = false;
3113
3114 for (name, data) in &mut font_definitions.font_data {
3115 ui.collapsing(name, |ui| {
3116 let mut tweak = data.tweak;
3117 if tweak.ui(ui).changed() {
3118 Arc::make_mut(data).tweak = tweak;
3119 changed = true;
3120 }
3121 });
3122 }
3123
3124 if changed {
3125 self.set_fonts(font_definitions);
3126 }
3127 }
3128
3129 pub fn inspection_ui(&self, ui: &mut Ui) {
3131 use crate::containers::CollapsingHeader;
3132
3133 crate::Grid::new("egui-inspection-grid")
3134 .num_columns(2)
3135 .striped(true)
3136 .show(ui, |ui| {
3137 ui.label("Total ui frames:");
3138 ui.monospace(ui.ctx().cumulative_frame_nr().to_string());
3139 ui.end_row();
3140
3141 ui.label("Total ui passes:");
3142 ui.monospace(ui.ctx().cumulative_pass_nr().to_string());
3143 ui.end_row();
3144
3145 ui.label("Is using pointer")
3146 .on_hover_text("Is egui currently using the pointer actively (e.g. dragging a slider)?");
3147 ui.monospace(self.is_using_pointer().to_string());
3148 ui.end_row();
3149
3150 ui.label("Wants pointer input")
3151 .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).");
3152 ui.monospace(self.wants_pointer_input().to_string());
3153 ui.end_row();
3154
3155 ui.label("Wants keyboard input").on_hover_text("Is egui currently listening for text input?");
3156 ui.monospace(self.wants_keyboard_input().to_string());
3157 ui.end_row();
3158
3159 ui.label("Keyboard focus widget").on_hover_text("Is egui currently listening for text input?");
3160 ui.monospace(self.memory(|m| m.focused())
3161 .as_ref()
3162 .map(Id::short_debug_format)
3163 .unwrap_or_default());
3164 ui.end_row();
3165
3166 let pointer_pos = self
3167 .pointer_hover_pos()
3168 .map_or_else(String::new, |pos| format!("{pos:?}"));
3169 ui.label("Pointer pos");
3170 ui.monospace(pointer_pos);
3171 ui.end_row();
3172
3173 let top_layer = self
3174 .pointer_hover_pos()
3175 .and_then(|pos| self.layer_id_at(pos))
3176 .map_or_else(String::new, |layer| layer.short_debug_format());
3177 ui.label("Top layer under mouse");
3178 ui.monospace(top_layer);
3179 ui.end_row();
3180 });
3181
3182 ui.add_space(16.0);
3183
3184 ui.label(format!(
3185 "There are {} text galleys in the layout cache",
3186 self.fonts(|f| f.num_galleys_in_cache())
3187 ))
3188 .on_hover_text("This is approximately the number of text strings on screen");
3189 ui.add_space(16.0);
3190
3191 CollapsingHeader::new("🔃 Repaint Causes")
3192 .default_open(false)
3193 .show(ui, |ui| {
3194 ui.set_min_height(120.0);
3195 ui.label("What caused egui to repaint:");
3196 ui.add_space(8.0);
3197 let causes = ui.ctx().repaint_causes();
3198 for cause in causes {
3199 ui.label(cause.to_string());
3200 }
3201 });
3202
3203 CollapsingHeader::new("📥 Input")
3204 .default_open(false)
3205 .show(ui, |ui| {
3206 let input = ui.input(|i| i.clone());
3207 input.ui(ui);
3208 });
3209
3210 CollapsingHeader::new("📊 Paint stats")
3211 .default_open(false)
3212 .show(ui, |ui| {
3213 let paint_stats = self.read(|ctx| ctx.paint_stats);
3214 paint_stats.ui(ui);
3215 });
3216
3217 CollapsingHeader::new("🖼 Textures")
3218 .default_open(false)
3219 .show(ui, |ui| {
3220 self.texture_ui(ui);
3221 });
3222
3223 CollapsingHeader::new("🖼 Image loaders")
3224 .default_open(false)
3225 .show(ui, |ui| {
3226 self.loaders_ui(ui);
3227 });
3228
3229 CollapsingHeader::new("🔠 Font texture")
3230 .default_open(false)
3231 .show(ui, |ui| {
3232 let font_image_size = self.fonts(|f| f.font_image_size());
3233 crate::introspection::font_texture_ui(ui, font_image_size);
3234 });
3235
3236 CollapsingHeader::new("Label text selection state")
3237 .default_open(false)
3238 .show(ui, |ui| {
3239 ui.label(format!(
3240 "{:#?}",
3241 *ui.ctx()
3242 .plugin::<crate::text_selection::LabelSelectionState>()
3243 .lock()
3244 ));
3245 });
3246
3247 CollapsingHeader::new("Interaction")
3248 .default_open(false)
3249 .show(ui, |ui| {
3250 let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());
3251 interact_widgets.ui(ui);
3252 });
3253 }
3254
3255 pub fn texture_ui(&self, ui: &mut crate::Ui) {
3257 let tex_mngr = self.tex_manager();
3258 let tex_mngr = tex_mngr.read();
3259
3260 let mut textures: Vec<_> = tex_mngr.allocated().collect();
3261 textures.sort_by_key(|(id, _)| *id);
3262
3263 let mut bytes = 0;
3264 for (_, tex) in &textures {
3265 bytes += tex.bytes_used();
3266 }
3267
3268 ui.label(format!(
3269 "{} allocated texture(s), using {:.1} MB",
3270 textures.len(),
3271 bytes as f64 * 1e-6
3272 ));
3273 let max_preview_size = vec2(48.0, 32.0);
3274
3275 let pixels_per_point = self.pixels_per_point();
3276
3277 ui.group(|ui| {
3278 ScrollArea::vertical()
3279 .max_height(300.0)
3280 .auto_shrink([false, true])
3281 .show(ui, |ui| {
3282 ui.style_mut().override_text_style = Some(TextStyle::Monospace);
3283 Grid::new("textures")
3284 .striped(true)
3285 .num_columns(4)
3286 .spacing(vec2(16.0, 2.0))
3287 .min_row_height(max_preview_size.y)
3288 .show(ui, |ui| {
3289 for (&texture_id, meta) in textures {
3290 let [w, h] = meta.size;
3291 let point_size = vec2(w as f32, h as f32) / pixels_per_point;
3292
3293 let mut size = point_size;
3294 size *= (max_preview_size.x / size.x).min(1.0);
3295 size *= (max_preview_size.y / size.y).min(1.0);
3296 ui.image(SizedTexture::new(texture_id, size))
3297 .on_hover_ui(|ui| {
3298 let max_size = 0.5 * ui.ctx().content_rect().size();
3300 let mut size = point_size;
3301 size *= max_size.x / size.x.max(max_size.x);
3302 size *= max_size.y / size.y.max(max_size.y);
3303 ui.image(SizedTexture::new(texture_id, size));
3304 });
3305
3306 ui.label(format!("{w} x {h}"));
3307 ui.label(format!("{:.3} MB", meta.bytes_used() as f64 * 1e-6));
3308 ui.label(format!("{:?}", meta.name));
3309 ui.end_row();
3310 }
3311 });
3312 });
3313 });
3314 }
3315
3316 pub fn loaders_ui(&self, ui: &mut crate::Ui) {
3318 struct LoaderInfo {
3319 id: String,
3320 byte_size: usize,
3321 }
3322
3323 let mut byte_loaders = vec![];
3324 let mut image_loaders = vec![];
3325 let mut texture_loaders = vec![];
3326
3327 {
3328 let loaders = self.loaders();
3329 let Loaders {
3330 include: _,
3331 bytes,
3332 image,
3333 texture,
3334 } = loaders.as_ref();
3335
3336 for loader in bytes.lock().iter() {
3337 byte_loaders.push(LoaderInfo {
3338 id: loader.id().to_owned(),
3339 byte_size: loader.byte_size(),
3340 });
3341 }
3342 for loader in image.lock().iter() {
3343 image_loaders.push(LoaderInfo {
3344 id: loader.id().to_owned(),
3345 byte_size: loader.byte_size(),
3346 });
3347 }
3348 for loader in texture.lock().iter() {
3349 texture_loaders.push(LoaderInfo {
3350 id: loader.id().to_owned(),
3351 byte_size: loader.byte_size(),
3352 });
3353 }
3354 }
3355
3356 fn loaders_ui(ui: &mut crate::Ui, title: &str, loaders: &[LoaderInfo]) {
3357 let heading = format!("{} {title} loaders", loaders.len());
3358 crate::CollapsingHeader::new(heading)
3359 .default_open(true)
3360 .show(ui, |ui| {
3361 Grid::new("loaders")
3362 .striped(true)
3363 .num_columns(2)
3364 .show(ui, |ui| {
3365 ui.label("ID");
3366 ui.label("Size");
3367 ui.end_row();
3368
3369 for loader in loaders {
3370 ui.label(&loader.id);
3371 ui.label(format!("{:.3} MB", loader.byte_size as f64 * 1e-6));
3372 ui.end_row();
3373 }
3374 });
3375 });
3376 }
3377
3378 loaders_ui(ui, "byte", &byte_loaders);
3379 loaders_ui(ui, "image", &image_loaders);
3380 loaders_ui(ui, "texture", &texture_loaders);
3381 }
3382
3383 pub fn memory_ui(&self, ui: &mut crate::Ui) {
3385 if ui
3386 .button("Reset all")
3387 .on_hover_text("Reset all egui state")
3388 .clicked()
3389 {
3390 self.memory_mut(|mem| *mem = Default::default());
3391 }
3392
3393 let (num_state, num_serialized) = self.data(|d| (d.len(), d.count_serialized()));
3394 ui.label(format!(
3395 "{num_state} widget states stored (of which {num_serialized} are serialized)."
3396 ));
3397
3398 ui.horizontal(|ui| {
3399 ui.label(format!(
3400 "{} areas (panels, windows, popups, …)",
3401 self.memory(|mem| mem.areas().count())
3402 ));
3403 if ui.button("Reset").clicked() {
3404 self.memory_mut(|mem| *mem.areas_mut() = Default::default());
3405 }
3406 });
3407 ui.indent("layers", |ui| {
3408 ui.label("Layers, ordered back to front.");
3409 let layers_ids: Vec<LayerId> = self.memory(|mem| mem.areas().order().to_vec());
3410 for layer_id in layers_ids {
3411 if let Some(area) = AreaState::load(self, layer_id.id) {
3412 let is_visible = self.memory(|mem| mem.areas().is_visible(&layer_id));
3413 if !is_visible {
3414 continue;
3415 }
3416 let text = format!("{} - {:?}", layer_id.short_debug_format(), area.rect(),);
3417 let response =
3419 ui.add(Label::new(RichText::new(text).monospace()).sense(Sense::click()));
3420 if response.hovered() && is_visible {
3421 ui.ctx()
3422 .debug_painter()
3423 .debug_rect(area.rect(), Color32::RED, "");
3424 }
3425 } else {
3426 ui.monospace(layer_id.short_debug_format());
3427 }
3428 }
3429 });
3430
3431 ui.horizontal(|ui| {
3432 ui.label(format!(
3433 "{} collapsing headers",
3434 self.data(|d| d.count::<containers::collapsing_header::InnerState>())
3435 ));
3436 if ui.button("Reset").clicked() {
3437 self.data_mut(|d| d.remove_by_type::<containers::collapsing_header::InnerState>());
3438 }
3439 });
3440
3441 #[expect(deprecated)]
3442 ui.horizontal(|ui| {
3443 ui.label(format!(
3444 "{} menu bars",
3445 self.data(|d| d.count::<crate::menu::BarState>())
3446 ));
3447 if ui.button("Reset").clicked() {
3448 self.data_mut(|d| d.remove_by_type::<crate::menu::BarState>());
3449 }
3450 });
3451
3452 ui.horizontal(|ui| {
3453 ui.label(format!(
3454 "{} scroll areas",
3455 self.data(|d| d.count::<scroll_area::State>())
3456 ));
3457 if ui.button("Reset").clicked() {
3458 self.data_mut(|d| d.remove_by_type::<scroll_area::State>());
3459 }
3460 });
3461
3462 ui.horizontal(|ui| {
3463 ui.label(format!(
3464 "{} resize areas",
3465 self.data(|d| d.count::<resize::State>())
3466 ));
3467 if ui.button("Reset").clicked() {
3468 self.data_mut(|d| d.remove_by_type::<resize::State>());
3469 }
3470 });
3471
3472 ui.shrink_width_to_current(); ui.label("NOTE: the position of this window cannot be reset from within itself.");
3474
3475 ui.collapsing("Interaction", |ui| {
3476 let interaction = self.memory(|mem| mem.interaction().clone());
3477 interaction.ui(ui);
3478 });
3479 }
3480}
3481
3482impl Context {
3483 pub fn style_ui(&self, ui: &mut Ui, theme: Theme) {
3485 let mut style: Style = (*self.style_of(theme)).clone();
3486 style.ui(ui);
3487 self.set_style_of(theme, style);
3488 }
3489}
3490
3491impl Context {
3493 #[cfg(feature = "accesskit")]
3503 pub fn accesskit_node_builder<R>(
3504 &self,
3505 id: Id,
3506 writer: impl FnOnce(&mut accesskit::Node) -> R,
3507 ) -> Option<R> {
3508 self.write(|ctx| {
3509 ctx.viewport()
3510 .this_pass
3511 .accesskit_state
3512 .is_some()
3513 .then(|| ctx.accesskit_node_builder(id))
3514 .map(writer)
3515 })
3516 }
3517
3518 #[cfg(feature = "accesskit")]
3519 pub(crate) fn register_accesskit_parent(&self, id: Id, parent_id: Id) {
3520 self.write(|ctx| {
3521 if let Some(state) = ctx.viewport().this_pass.accesskit_state.as_mut() {
3522 state.parent_map.insert(id, parent_id);
3523 }
3524 });
3525 }
3526
3527 #[cfg(feature = "accesskit")]
3529 pub fn enable_accesskit(&self) {
3530 self.write(|ctx| ctx.is_accesskit_enabled = true);
3531 }
3532
3533 #[cfg(feature = "accesskit")]
3535 pub fn disable_accesskit(&self) {
3536 self.write(|ctx| ctx.is_accesskit_enabled = false);
3537 }
3538}
3539
3540impl Context {
3542 pub fn include_bytes(&self, uri: impl Into<Cow<'static, str>>, bytes: impl Into<Bytes>) {
3549 self.loaders().include.insert(uri, bytes);
3550 }
3551
3552 pub fn is_loader_installed(&self, id: &str) -> bool {
3555 let loaders = self.loaders();
3556
3557 loaders.bytes.lock().iter().any(|l| l.id() == id)
3558 || loaders.image.lock().iter().any(|l| l.id() == id)
3559 || loaders.texture.lock().iter().any(|l| l.id() == id)
3560 }
3561
3562 pub fn add_bytes_loader(&self, loader: Arc<dyn load::BytesLoader + Send + Sync + 'static>) {
3568 self.loaders().bytes.lock().push(loader);
3569 }
3570
3571 pub fn add_image_loader(&self, loader: Arc<dyn load::ImageLoader + Send + Sync + 'static>) {
3577 self.loaders().image.lock().push(loader);
3578 }
3579
3580 pub fn add_texture_loader(&self, loader: Arc<dyn load::TextureLoader + Send + Sync + 'static>) {
3586 self.loaders().texture.lock().push(loader);
3587 }
3588
3589 pub fn forget_image(&self, uri: &str) {
3594 use load::BytesLoader as _;
3595
3596 profiling::function_scope!();
3597
3598 let loaders = self.loaders();
3599
3600 loaders.include.forget(uri);
3601 for loader in loaders.bytes.lock().iter() {
3602 loader.forget(uri);
3603 }
3604 for loader in loaders.image.lock().iter() {
3605 loader.forget(uri);
3606 }
3607 for loader in loaders.texture.lock().iter() {
3608 loader.forget(uri);
3609 }
3610 }
3611
3612 pub fn forget_all_images(&self) {
3616 use load::BytesLoader as _;
3617
3618 profiling::function_scope!();
3619
3620 let loaders = self.loaders();
3621
3622 loaders.include.forget_all();
3623 for loader in loaders.bytes.lock().iter() {
3624 loader.forget_all();
3625 }
3626 for loader in loaders.image.lock().iter() {
3627 loader.forget_all();
3628 }
3629 for loader in loaders.texture.lock().iter() {
3630 loader.forget_all();
3631 }
3632 }
3633
3634 pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult {
3653 profiling::function_scope!(uri);
3654
3655 let loaders = self.loaders();
3656 let bytes_loaders = loaders.bytes.lock();
3657
3658 for loader in bytes_loaders.iter().rev() {
3660 let result = loader.load(self, uri);
3661 match result {
3662 Err(load::LoadError::NotSupported) => {}
3663 _ => return result,
3664 }
3665 }
3666
3667 Err(load::LoadError::NoMatchingBytesLoader)
3668 }
3669
3670 pub fn try_load_image(&self, uri: &str, size_hint: load::SizeHint) -> load::ImageLoadResult {
3691 profiling::function_scope!(uri);
3692
3693 let loaders = self.loaders();
3694 let image_loaders = loaders.image.lock();
3695 if image_loaders.is_empty() {
3696 return Err(load::LoadError::NoImageLoaders);
3697 }
3698
3699 let mut format = None;
3700
3701 for loader in image_loaders.iter().rev() {
3703 match loader.load(self, uri, size_hint) {
3704 Err(load::LoadError::NotSupported) => {}
3705 Err(load::LoadError::FormatNotSupported { detected_format }) => {
3706 format = format.or(detected_format);
3707 }
3708 result => return result,
3709 }
3710 }
3711
3712 Err(load::LoadError::NoMatchingImageLoader {
3713 detected_format: format,
3714 })
3715 }
3716
3717 pub fn try_load_texture(
3736 &self,
3737 uri: &str,
3738 texture_options: TextureOptions,
3739 size_hint: load::SizeHint,
3740 ) -> load::TextureLoadResult {
3741 profiling::function_scope!(uri);
3742
3743 let loaders = self.loaders();
3744 let texture_loaders = loaders.texture.lock();
3745
3746 for loader in texture_loaders.iter().rev() {
3748 match loader.load(self, uri, texture_options, size_hint) {
3749 Err(load::LoadError::NotSupported) => {}
3750 result => return result,
3751 }
3752 }
3753
3754 Err(load::LoadError::NoMatchingTextureLoader)
3755 }
3756
3757 pub fn loaders(&self) -> Arc<Loaders> {
3759 self.read(|this| this.loaders.clone())
3760 }
3761
3762 pub fn has_pending_images(&self) -> bool {
3764 self.read(|this| {
3765 this.loaders.image.lock().iter().any(|i| i.has_pending())
3766 || this.loaders.bytes.lock().iter().any(|i| i.has_pending())
3767 })
3768 }
3769}
3770
3771impl Context {
3773 pub fn viewport_id(&self) -> ViewportId {
3779 self.read(|ctx| ctx.viewport_id())
3780 }
3781
3782 pub fn parent_viewport_id(&self) -> ViewportId {
3788 self.read(|ctx| ctx.parent_viewport_id())
3789 }
3790
3791 pub fn viewport<R>(&self, reader: impl FnOnce(&ViewportState) -> R) -> R {
3793 self.write(|ctx| reader(ctx.viewport()))
3794 }
3795
3796 pub fn viewport_for<R>(
3798 &self,
3799 viewport_id: ViewportId,
3800 reader: impl FnOnce(&ViewportState) -> R,
3801 ) -> R {
3802 self.write(|ctx| reader(ctx.viewport_for(viewport_id)))
3803 }
3804
3805 pub fn set_immediate_viewport_renderer(
3818 callback: impl for<'a> Fn(&Self, ImmediateViewport<'a>) + 'static,
3819 ) {
3820 let callback = Box::new(callback);
3821 IMMEDIATE_VIEWPORT_RENDERER.with(|render_sync| {
3822 render_sync.replace(Some(callback));
3823 });
3824 }
3825
3826 pub fn embed_viewports(&self) -> bool {
3831 self.read(|ctx| ctx.embed_viewports)
3832 }
3833
3834 pub fn set_embed_viewports(&self, value: bool) {
3839 self.write(|ctx| ctx.embed_viewports = value);
3840 }
3841
3842 pub fn send_viewport_cmd(&self, command: ViewportCommand) {
3846 self.send_viewport_cmd_to(self.viewport_id(), command);
3847 }
3848
3849 pub fn send_viewport_cmd_to(&self, id: ViewportId, command: ViewportCommand) {
3853 self.request_repaint_of(id);
3854
3855 if command.requires_parent_repaint() {
3856 self.request_repaint_of(self.parent_viewport_id());
3857 }
3858
3859 self.write(|ctx| ctx.viewport_for(id).commands.push(command));
3860 }
3861
3862 pub fn show_viewport_deferred(
3892 &self,
3893 new_viewport_id: ViewportId,
3894 viewport_builder: ViewportBuilder,
3895 viewport_ui_cb: impl Fn(&Self, ViewportClass) + Send + Sync + 'static,
3896 ) {
3897 profiling::function_scope!();
3898
3899 if self.embed_viewports() {
3900 viewport_ui_cb(self, ViewportClass::Embedded);
3901 } else {
3902 self.write(|ctx| {
3903 ctx.viewport_parents
3904 .insert(new_viewport_id, ctx.viewport_id());
3905
3906 let viewport = ctx.viewports.entry(new_viewport_id).or_default();
3907 viewport.class = ViewportClass::Deferred;
3908 viewport.builder = viewport_builder;
3909 viewport.used = true;
3910 viewport.viewport_ui_cb = Some(Arc::new(move |ctx| {
3911 (viewport_ui_cb)(ctx, ViewportClass::Deferred);
3912 }));
3913 });
3914 }
3915 }
3916
3917 pub fn show_viewport_immediate<T>(
3944 &self,
3945 new_viewport_id: ViewportId,
3946 builder: ViewportBuilder,
3947 mut viewport_ui_cb: impl FnMut(&Self, ViewportClass) -> T,
3948 ) -> T {
3949 profiling::function_scope!();
3950
3951 if self.embed_viewports() {
3952 return viewport_ui_cb(self, ViewportClass::Embedded);
3953 }
3954
3955 IMMEDIATE_VIEWPORT_RENDERER.with(|immediate_viewport_renderer| {
3956 let immediate_viewport_renderer = immediate_viewport_renderer.borrow();
3957 let Some(immediate_viewport_renderer) = immediate_viewport_renderer.as_ref() else {
3958 return viewport_ui_cb(self, ViewportClass::Embedded);
3960 };
3961
3962 let ids = self.write(|ctx| {
3963 let parent_viewport_id = ctx.viewport_id();
3964
3965 ctx.viewport_parents
3966 .insert(new_viewport_id, parent_viewport_id);
3967
3968 let viewport = ctx.viewports.entry(new_viewport_id).or_default();
3969 viewport.builder = builder.clone();
3970 viewport.used = true;
3971 viewport.viewport_ui_cb = None; ViewportIdPair::from_self_and_parent(new_viewport_id, parent_viewport_id)
3974 });
3975
3976 let mut out = None;
3977 {
3978 let out = &mut out;
3979
3980 let viewport = ImmediateViewport {
3981 ids,
3982 builder,
3983 viewport_ui_cb: Box::new(move |context| {
3984 *out = Some(viewport_ui_cb(context, ViewportClass::Immediate));
3985 }),
3986 };
3987
3988 immediate_viewport_renderer(self, viewport);
3989 }
3990
3991 out.expect(
3992 "egui backend is implemented incorrectly - the user callback was never called",
3993 )
3994 })
3995 }
3996}
3997
3998impl Context {
4000 pub fn interaction_snapshot<R>(&self, reader: impl FnOnce(&InteractionSnapshot) -> R) -> R {
4002 self.write(|w| reader(&w.viewport().interact_widgets))
4003 }
4004
4005 pub fn dragged_id(&self) -> Option<Id> {
4013 self.interaction_snapshot(|i| i.dragged)
4014 }
4015
4016 pub fn is_being_dragged(&self, id: Id) -> bool {
4023 self.dragged_id() == Some(id)
4024 }
4025
4026 pub fn drag_started_id(&self) -> Option<Id> {
4030 self.interaction_snapshot(|i| i.drag_started)
4031 }
4032
4033 pub fn drag_stopped_id(&self) -> Option<Id> {
4035 self.interaction_snapshot(|i| i.drag_stopped)
4036 }
4037
4038 pub fn set_dragged_id(&self, id: Id) {
4040 self.write(|ctx| {
4041 let vp = ctx.viewport();
4042 let i = &mut vp.interact_widgets;
4043 if i.dragged != Some(id) {
4044 i.drag_stopped = i.dragged.or(i.drag_stopped);
4045 i.dragged = Some(id);
4046 i.drag_started = Some(id);
4047 }
4048
4049 ctx.memory.interaction_mut().potential_drag_id = Some(id);
4050 });
4051 }
4052
4053 pub fn stop_dragging(&self) {
4055 self.write(|ctx| {
4056 let vp = ctx.viewport();
4057 let i = &mut vp.interact_widgets;
4058 if i.dragged.is_some() {
4059 i.drag_stopped = i.dragged;
4060 i.dragged = None;
4061 }
4062
4063 ctx.memory.interaction_mut().potential_drag_id = None;
4064 });
4065 }
4066
4067 #[inline(always)]
4071 pub fn dragging_something_else(&self, not_this: Id) -> bool {
4072 let dragged = self.dragged_id();
4073 dragged.is_some() && dragged != Some(not_this)
4074 }
4075}
4076
4077#[test]
4078fn context_impl_send_sync() {
4079 fn assert_send_sync<T: Send + Sync>() {}
4080 assert_send_sync::<Context>();
4081}
4082
4083#[cfg(test)]
4084mod test {
4085 use super::Context;
4086
4087 #[test]
4088 fn test_single_pass() {
4089 let ctx = Context::default();
4090 ctx.options_mut(|o| o.max_passes = 1.try_into().unwrap());
4091
4092 {
4094 let mut num_calls = 0;
4095 let output = ctx.run(Default::default(), |ctx| {
4096 num_calls += 1;
4097 assert_eq!(ctx.output(|o| o.num_completed_passes), 0);
4098 assert!(!ctx.output(|o| o.requested_discard()));
4099 assert!(!ctx.will_discard());
4100 });
4101 assert_eq!(num_calls, 1);
4102 assert_eq!(output.platform_output.num_completed_passes, 1);
4103 assert!(!output.platform_output.requested_discard());
4104 }
4105
4106 {
4108 let mut num_calls = 0;
4109 let output = ctx.run(Default::default(), |ctx| {
4110 num_calls += 1;
4111 ctx.request_discard("test");
4112 assert!(!ctx.will_discard(), "The request should have been denied");
4113 });
4114 assert_eq!(num_calls, 1);
4115 assert_eq!(output.platform_output.num_completed_passes, 1);
4116 assert!(
4117 output.platform_output.requested_discard(),
4118 "The request should be reported"
4119 );
4120 assert_eq!(
4121 output
4122 .platform_output
4123 .request_discard_reasons
4124 .first()
4125 .unwrap()
4126 .reason,
4127 "test"
4128 );
4129 }
4130 }
4131
4132 #[test]
4133 fn test_dual_pass() {
4134 let ctx = Context::default();
4135 ctx.options_mut(|o| o.max_passes = 2.try_into().unwrap());
4136
4137 {
4139 let mut num_calls = 0;
4140 let output = ctx.run(Default::default(), |ctx| {
4141 assert_eq!(ctx.output(|o| o.num_completed_passes), 0);
4142 assert!(!ctx.output(|o| o.requested_discard()));
4143 assert!(!ctx.will_discard());
4144 num_calls += 1;
4145 });
4146 assert_eq!(num_calls, 1);
4147 assert_eq!(output.platform_output.num_completed_passes, 1);
4148 assert!(!output.platform_output.requested_discard());
4149 }
4150
4151 {
4153 let mut num_calls = 0;
4154 let output = ctx.run(Default::default(), |ctx| {
4155 assert_eq!(ctx.output(|o| o.num_completed_passes), num_calls);
4156
4157 assert!(!ctx.will_discard());
4158 if num_calls == 0 {
4159 ctx.request_discard("test");
4160 assert!(ctx.will_discard());
4161 }
4162
4163 num_calls += 1;
4164 });
4165 assert_eq!(num_calls, 2);
4166 assert_eq!(output.platform_output.num_completed_passes, 2);
4167 assert!(
4168 !output.platform_output.requested_discard(),
4169 "The request should have been cleared when fulfilled"
4170 );
4171 }
4172
4173 {
4175 let mut num_calls = 0;
4176 let output = ctx.run(Default::default(), |ctx| {
4177 assert_eq!(ctx.output(|o| o.num_completed_passes), num_calls);
4178
4179 assert!(!ctx.will_discard());
4180 ctx.request_discard("test");
4181 if num_calls == 0 {
4182 assert!(ctx.will_discard(), "First request granted");
4183 } else {
4184 assert!(!ctx.will_discard(), "Second request should be denied");
4185 }
4186
4187 num_calls += 1;
4188 });
4189 assert_eq!(num_calls, 2);
4190 assert_eq!(output.platform_output.num_completed_passes, 2);
4191 assert!(
4192 output.platform_output.requested_discard(),
4193 "The unfulfilled request should be reported"
4194 );
4195 }
4196 }
4197
4198 #[test]
4199 fn test_multi_pass() {
4200 let ctx = Context::default();
4201 ctx.options_mut(|o| o.max_passes = 10.try_into().unwrap());
4202
4203 {
4205 let mut num_calls = 0;
4206 let output = ctx.run(Default::default(), |ctx| {
4207 assert_eq!(ctx.output(|o| o.num_completed_passes), num_calls);
4208
4209 assert!(!ctx.will_discard());
4210 if num_calls <= 2 {
4211 ctx.request_discard("test");
4212 assert!(ctx.will_discard());
4213 }
4214
4215 num_calls += 1;
4216 });
4217 assert_eq!(num_calls, 4);
4218 assert_eq!(output.platform_output.num_completed_passes, 4);
4219 assert!(
4220 !output.platform_output.requested_discard(),
4221 "The request should have been cleared when fulfilled"
4222 );
4223 }
4224 }
4225}