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 tree_id: accesskit::TreeId::ROOT,
2522 focus: focus_id,
2523 });
2524 }
2525 }
2526
2527 let shapes = viewport
2528 .graphics
2529 .drain(self.memory.areas().order(), &self.memory.to_global);
2530
2531 let mut repaint_needed = false;
2532
2533 if self.memory.options.repaint_on_widget_change {
2534 profiling::scope!("compare-widget-rects");
2535 #[allow(clippy::allow_attributes, clippy::collapsible_if)] if viewport.prev_pass.widgets != viewport.this_pass.widgets {
2537 repaint_needed = true; }
2539 }
2540
2541 std::mem::swap(&mut viewport.prev_pass, &mut viewport.this_pass);
2542
2543 if repaint_needed {
2544 self.request_repaint(ended_viewport_id, RepaintCause::new());
2545 }
2546 let all_viewport_ids = self.all_viewport_ids();
2549
2550 self.last_viewport = ended_viewport_id;
2551
2552 self.viewports.retain(|&id, viewport| {
2553 if id == ViewportId::ROOT {
2554 return true; }
2556
2557 let parent = *self.viewport_parents.entry(id).or_default();
2558
2559 if !all_viewport_ids.contains(&parent) {
2560 log::debug!(
2561 "Removing viewport {:?} ({:?}): the parent is gone",
2562 id,
2563 viewport.builder.title
2564 );
2565
2566 return false;
2567 }
2568
2569 let is_our_child = parent == ended_viewport_id && id != ViewportId::ROOT;
2570 if is_our_child {
2571 if !viewport.used {
2572 log::debug!(
2573 "Removing viewport {:?} ({:?}): it was never used this pass",
2574 id,
2575 viewport.builder.title
2576 );
2577
2578 return false; }
2580
2581 viewport.used = false; }
2583
2584 true
2585 });
2586
2587 self.viewport_stack.pop();
2589
2590 let is_last = self.viewport_stack.is_empty();
2593
2594 let viewport_output = self
2595 .viewports
2596 .iter_mut()
2597 .map(|(&id, viewport)| {
2598 let parent = *self.viewport_parents.entry(id).or_default();
2599 let commands = if is_last {
2600 std::mem::take(&mut viewport.commands)
2604 } else {
2605 vec![]
2606 };
2607
2608 (
2609 id,
2610 ViewportOutput {
2611 parent,
2612 class: viewport.class,
2613 builder: viewport.builder.clone(),
2614 viewport_ui_cb: viewport.viewport_ui_cb.clone(),
2615 commands,
2616 repaint_delay: viewport.repaint.repaint_delay,
2617 },
2618 )
2619 })
2620 .collect();
2621
2622 if is_last {
2623 self.viewports.retain(|id, _| all_viewport_ids.contains(id));
2625 debug_assert!(
2626 self.viewports.contains_key(&ViewportId::ROOT),
2627 "Bug in egui: we removed the root viewport"
2628 );
2629 self.viewport_parents
2630 .retain(|id, _| all_viewport_ids.contains(id));
2631 } else {
2632 let viewport_id = self.viewport_id();
2633 self.memory.set_viewport_id(viewport_id);
2634 }
2635
2636 platform_output.num_completed_passes += 1;
2637
2638 FullOutput {
2639 platform_output,
2640 textures_delta,
2641 shapes,
2642 pixels_per_point,
2643 viewport_output,
2644 }
2645 }
2646}
2647
2648impl Context {
2649 pub fn tessellate(
2655 &self,
2656 shapes: Vec<ClippedShape>,
2657 pixels_per_point: f32,
2658 ) -> Vec<ClippedPrimitive> {
2659 profiling::function_scope!();
2660
2661 self.write(|ctx| {
2666 let tessellation_options = ctx.memory.options.tessellation_options;
2667 let texture_atlas = if let Some(fonts) = ctx.fonts.as_ref() {
2668 fonts.texture_atlas()
2669 } else {
2670 log::warn!("No font size matching {pixels_per_point} pixels per point found.");
2671 ctx.fonts
2672 .iter()
2673 .next()
2674 .expect("No fonts loaded")
2675 .texture_atlas()
2676 };
2677
2678 let paint_stats = PaintStats::from_shapes(&shapes);
2679 let clipped_primitives = {
2680 profiling::scope!("tessellator::tessellate_shapes");
2681 tessellator::Tessellator::new(
2682 pixels_per_point,
2683 tessellation_options,
2684 texture_atlas.size(),
2685 texture_atlas.prepared_discs(),
2686 )
2687 .tessellate_shapes(shapes)
2688 };
2689 ctx.paint_stats = paint_stats.with_clipped_primitives(&clipped_primitives);
2690 clipped_primitives
2691 })
2692 }
2693
2694 pub fn content_rect(&self) -> Rect {
2703 self.input(|i| i.content_rect()).round_ui()
2704 }
2705
2706 pub fn viewport_rect(&self) -> Rect {
2717 self.input(|i| i.viewport_rect()).round_ui()
2718 }
2719
2720 #[deprecated(
2722 note = "screen_rect has been split into viewport_rect() and content_rect(). You likely should use content_rect()"
2723 )]
2724 pub fn screen_rect(&self) -> Rect {
2725 self.input(|i| i.content_rect()).round_ui()
2726 }
2727
2728 pub fn available_rect(&self) -> Rect {
2730 self.pass_state(|s| s.available_rect()).round_ui()
2731 }
2732
2733 pub fn used_rect(&self) -> Rect {
2735 self.write(|ctx| {
2736 let mut used = ctx.viewport().this_pass.used_by_panels;
2737 for (_id, window) in ctx.memory.areas().visible_windows() {
2738 used |= window.rect();
2739 }
2740 used.round_ui()
2741 })
2742 }
2743
2744 pub fn used_size(&self) -> Vec2 {
2748 (self.used_rect().max - Pos2::ZERO).round_ui()
2749 }
2750
2751 pub fn is_pointer_over_area(&self) -> bool {
2755 let pointer_pos = self.input(|i| i.pointer.interact_pos());
2756 if let Some(pointer_pos) = pointer_pos {
2757 if let Some(layer) = self.layer_id_at(pointer_pos) {
2758 if layer.order == Order::Background {
2759 !self.pass_state(|state| state.unused_rect.contains(pointer_pos))
2760 } else {
2761 true
2762 }
2763 } else {
2764 false
2765 }
2766 } else {
2767 false
2768 }
2769 }
2770
2771 pub fn wants_pointer_input(&self) -> bool {
2778 self.is_using_pointer()
2779 || (self.is_pointer_over_area() && !self.input(|i| i.pointer.any_down()))
2780 }
2781
2782 pub fn is_using_pointer(&self) -> bool {
2786 self.memory(|m| m.interaction().is_using_pointer())
2787 }
2788
2789 pub fn wants_keyboard_input(&self) -> bool {
2791 self.memory(|m| m.focused().is_some())
2792 }
2793
2794 pub fn highlight_widget(&self, id: Id) {
2801 self.pass_state_mut(|fs| fs.highlight_next_pass.insert(id));
2802 }
2803
2804 #[expect(deprecated)]
2808 #[deprecated = "Use `is_popup_open` instead"]
2809 pub fn is_context_menu_open(&self) -> bool {
2810 self.data(|d| {
2811 d.get_temp::<crate::menu::BarState>(crate::menu::CONTEXT_MENU_ID_STR.into())
2812 .is_some_and(|state| state.has_root())
2813 })
2814 }
2815
2816 pub fn is_popup_open(&self) -> bool {
2820 self.pass_state_mut(|fs| {
2821 fs.layers
2822 .values()
2823 .any(|layer| !layer.open_popups.is_empty())
2824 })
2825 }
2826}
2827
2828impl Context {
2830 #[inline(always)]
2834 pub fn pointer_latest_pos(&self) -> Option<Pos2> {
2835 self.input(|i| i.pointer.latest_pos())
2836 }
2837
2838 #[inline(always)]
2840 pub fn pointer_hover_pos(&self) -> Option<Pos2> {
2841 self.input(|i| i.pointer.hover_pos())
2842 }
2843
2844 #[inline(always)]
2850 pub fn pointer_interact_pos(&self) -> Option<Pos2> {
2851 self.input(|i| i.pointer.interact_pos())
2852 }
2853
2854 pub fn multi_touch(&self) -> Option<MultiTouchInfo> {
2856 self.input(|i| i.multi_touch())
2857 }
2858}
2859
2860impl Context {
2861 pub fn set_transform_layer(&self, layer_id: LayerId, transform: TSTransform) {
2873 self.memory_mut(|m| {
2874 if transform == TSTransform::IDENTITY {
2875 m.to_global.remove(&layer_id)
2876 } else {
2877 m.to_global.insert(layer_id, transform)
2878 }
2879 });
2880 }
2881
2882 pub fn layer_transform_to_global(&self, layer_id: LayerId) -> Option<TSTransform> {
2886 self.memory(|m| m.to_global.get(&layer_id).copied())
2887 }
2888
2889 pub fn layer_transform_from_global(&self, layer_id: LayerId) -> Option<TSTransform> {
2893 self.layer_transform_to_global(layer_id)
2894 .map(|t| t.inverse())
2895 }
2896
2897 pub fn transform_layer_shapes(&self, layer_id: LayerId, transform: TSTransform) {
2905 if transform != TSTransform::IDENTITY {
2906 self.graphics_mut(|g| g.entry(layer_id).transform(transform));
2907 }
2908 }
2909
2910 pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
2912 self.memory(|mem| mem.layer_id_at(pos))
2913 }
2914
2915 pub fn move_to_top(&self, layer_id: LayerId) {
2919 self.memory_mut(|mem| mem.areas_mut().move_to_top(layer_id));
2920 }
2921
2922 pub fn set_sublayer(&self, parent: LayerId, child: LayerId) {
2930 self.memory_mut(|mem| mem.areas_mut().set_sublayer(parent, child));
2931 }
2932
2933 pub fn top_layer_id(&self) -> Option<LayerId> {
2935 self.memory(|mem| mem.areas().top_layer_id(Order::Middle))
2936 }
2937
2938 pub fn rect_contains_pointer(&self, layer_id: LayerId, rect: Rect) -> bool {
2946 let rect = if let Some(to_global) = self.layer_transform_to_global(layer_id) {
2947 to_global * rect
2948 } else {
2949 rect
2950 };
2951 if !rect.is_positive() {
2952 return false;
2953 }
2954
2955 let pointer_pos = self.input(|i| i.pointer.interact_pos());
2956 let Some(pointer_pos) = pointer_pos else {
2957 return false;
2958 };
2959
2960 if !rect.contains(pointer_pos) {
2961 return false;
2962 }
2963
2964 if self.layer_id_at(pointer_pos) != Some(layer_id) {
2965 return false;
2966 }
2967
2968 true
2969 }
2970
2971 #[cfg(debug_assertions)]
2975 pub fn debug_on_hover(&self) -> bool {
2976 self.options(|opt| opt.style().debug.debug_on_hover)
2977 }
2978
2979 #[cfg(debug_assertions)]
2981 pub fn set_debug_on_hover(&self, debug_on_hover: bool) {
2982 self.all_styles_mut(|style| style.debug.debug_on_hover = debug_on_hover);
2983 }
2984}
2985
2986impl Context {
2988 #[track_caller] pub fn animate_bool(&self, id: Id, value: bool) -> f32 {
2999 let animation_time = self.style().animation_time;
3000 self.animate_bool_with_time_and_easing(id, value, animation_time, emath::easing::linear)
3001 }
3002
3003 #[track_caller] pub fn animate_bool_responsive(&self, id: Id, value: bool) -> f32 {
3009 self.animate_bool_with_easing(id, value, emath::easing::cubic_out)
3010 }
3011
3012 #[track_caller] pub fn animate_bool_with_easing(&self, id: Id, value: bool, easing: fn(f32) -> f32) -> f32 {
3015 let animation_time = self.style().animation_time;
3016 self.animate_bool_with_time_and_easing(id, value, animation_time, easing)
3017 }
3018
3019 #[track_caller] pub fn animate_bool_with_time(&self, id: Id, target_value: bool, animation_time: f32) -> f32 {
3022 self.animate_bool_with_time_and_easing(
3023 id,
3024 target_value,
3025 animation_time,
3026 emath::easing::linear,
3027 )
3028 }
3029
3030 #[track_caller] pub fn animate_bool_with_time_and_easing(
3039 &self,
3040 id: Id,
3041 target_value: bool,
3042 animation_time: f32,
3043 easing: fn(f32) -> f32,
3044 ) -> f32 {
3045 let animated_value = self.write(|ctx| {
3046 ctx.animation_manager.animate_bool(
3047 &ctx.viewports.entry(ctx.viewport_id()).or_default().input,
3048 animation_time,
3049 id,
3050 target_value,
3051 )
3052 });
3053
3054 let animation_in_progress = 0.0 < animated_value && animated_value < 1.0;
3055 if animation_in_progress {
3056 self.request_repaint();
3057 }
3058
3059 if target_value {
3060 easing(animated_value)
3061 } else {
3062 1.0 - easing(1.0 - animated_value)
3063 }
3064 }
3065
3066 #[track_caller] pub fn animate_value_with_time(&self, id: Id, target_value: f32, animation_time: f32) -> f32 {
3072 let animated_value = self.write(|ctx| {
3073 ctx.animation_manager.animate_value(
3074 &ctx.viewports.entry(ctx.viewport_id()).or_default().input,
3075 animation_time,
3076 id,
3077 target_value,
3078 )
3079 });
3080 let animation_in_progress = animated_value != target_value;
3081 if animation_in_progress {
3082 self.request_repaint();
3083 }
3084
3085 animated_value
3086 }
3087
3088 pub fn clear_animations(&self) {
3090 self.write(|ctx| ctx.animation_manager = Default::default());
3091 }
3092}
3093
3094impl Context {
3095 pub fn settings_ui(&self, ui: &mut Ui) {
3097 let prev_options = self.options(|o| o.clone());
3098 let mut options = prev_options.clone();
3099
3100 ui.collapsing("🔠 Font tweak", |ui| {
3101 self.fonts_tweak_ui(ui);
3102 });
3103
3104 options.ui(ui);
3105
3106 if options != prev_options {
3107 self.options_mut(move |o| *o = options);
3108 }
3109 }
3110
3111 fn fonts_tweak_ui(&self, ui: &mut Ui) {
3112 let mut font_definitions = self.write(|ctx| ctx.font_definitions.clone());
3113 let mut changed = false;
3114
3115 for (name, data) in &mut font_definitions.font_data {
3116 ui.collapsing(name, |ui| {
3117 let mut tweak = data.tweak;
3118 if tweak.ui(ui).changed() {
3119 Arc::make_mut(data).tweak = tweak;
3120 changed = true;
3121 }
3122 });
3123 }
3124
3125 if changed {
3126 self.set_fonts(font_definitions);
3127 }
3128 }
3129
3130 pub fn inspection_ui(&self, ui: &mut Ui) {
3132 use crate::containers::CollapsingHeader;
3133
3134 crate::Grid::new("egui-inspection-grid")
3135 .num_columns(2)
3136 .striped(true)
3137 .show(ui, |ui| {
3138 ui.label("Total ui frames:");
3139 ui.monospace(ui.ctx().cumulative_frame_nr().to_string());
3140 ui.end_row();
3141
3142 ui.label("Total ui passes:");
3143 ui.monospace(ui.ctx().cumulative_pass_nr().to_string());
3144 ui.end_row();
3145
3146 ui.label("Is using pointer")
3147 .on_hover_text("Is egui currently using the pointer actively (e.g. dragging a slider)?");
3148 ui.monospace(self.is_using_pointer().to_string());
3149 ui.end_row();
3150
3151 ui.label("Wants pointer input")
3152 .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).");
3153 ui.monospace(self.wants_pointer_input().to_string());
3154 ui.end_row();
3155
3156 ui.label("Wants keyboard input").on_hover_text("Is egui currently listening for text input?");
3157 ui.monospace(self.wants_keyboard_input().to_string());
3158 ui.end_row();
3159
3160 ui.label("Keyboard focus widget").on_hover_text("Is egui currently listening for text input?");
3161 ui.monospace(self.memory(|m| m.focused())
3162 .as_ref()
3163 .map(Id::short_debug_format)
3164 .unwrap_or_default());
3165 ui.end_row();
3166
3167 let pointer_pos = self
3168 .pointer_hover_pos()
3169 .map_or_else(String::new, |pos| format!("{pos:?}"));
3170 ui.label("Pointer pos");
3171 ui.monospace(pointer_pos);
3172 ui.end_row();
3173
3174 let top_layer = self
3175 .pointer_hover_pos()
3176 .and_then(|pos| self.layer_id_at(pos))
3177 .map_or_else(String::new, |layer| layer.short_debug_format());
3178 ui.label("Top layer under mouse");
3179 ui.monospace(top_layer);
3180 ui.end_row();
3181 });
3182
3183 ui.add_space(16.0);
3184
3185 ui.label(format!(
3186 "There are {} text galleys in the layout cache",
3187 self.fonts(|f| f.num_galleys_in_cache())
3188 ))
3189 .on_hover_text("This is approximately the number of text strings on screen");
3190 ui.add_space(16.0);
3191
3192 CollapsingHeader::new("🔃 Repaint Causes")
3193 .default_open(false)
3194 .show(ui, |ui| {
3195 ui.set_min_height(120.0);
3196 ui.label("What caused egui to repaint:");
3197 ui.add_space(8.0);
3198 let causes = ui.ctx().repaint_causes();
3199 for cause in causes {
3200 ui.label(cause.to_string());
3201 }
3202 });
3203
3204 CollapsingHeader::new("📥 Input")
3205 .default_open(false)
3206 .show(ui, |ui| {
3207 let input = ui.input(|i| i.clone());
3208 input.ui(ui);
3209 });
3210
3211 CollapsingHeader::new("📊 Paint stats")
3212 .default_open(false)
3213 .show(ui, |ui| {
3214 let paint_stats = self.read(|ctx| ctx.paint_stats);
3215 paint_stats.ui(ui);
3216 });
3217
3218 CollapsingHeader::new("🖼 Textures")
3219 .default_open(false)
3220 .show(ui, |ui| {
3221 self.texture_ui(ui);
3222 });
3223
3224 CollapsingHeader::new("🖼 Image loaders")
3225 .default_open(false)
3226 .show(ui, |ui| {
3227 self.loaders_ui(ui);
3228 });
3229
3230 CollapsingHeader::new("🔠 Font texture")
3231 .default_open(false)
3232 .show(ui, |ui| {
3233 let font_image_size = self.fonts(|f| f.font_image_size());
3234 crate::introspection::font_texture_ui(ui, font_image_size);
3235 });
3236
3237 CollapsingHeader::new("Label text selection state")
3238 .default_open(false)
3239 .show(ui, |ui| {
3240 ui.label(format!(
3241 "{:#?}",
3242 *ui.ctx()
3243 .plugin::<crate::text_selection::LabelSelectionState>()
3244 .lock()
3245 ));
3246 });
3247
3248 CollapsingHeader::new("Interaction")
3249 .default_open(false)
3250 .show(ui, |ui| {
3251 let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());
3252 interact_widgets.ui(ui);
3253 });
3254 }
3255
3256 pub fn texture_ui(&self, ui: &mut crate::Ui) {
3258 let tex_mngr = self.tex_manager();
3259 let tex_mngr = tex_mngr.read();
3260
3261 let mut textures: Vec<_> = tex_mngr.allocated().collect();
3262 textures.sort_by_key(|(id, _)| *id);
3263
3264 let mut bytes = 0;
3265 for (_, tex) in &textures {
3266 bytes += tex.bytes_used();
3267 }
3268
3269 ui.label(format!(
3270 "{} allocated texture(s), using {:.1} MB",
3271 textures.len(),
3272 bytes as f64 * 1e-6
3273 ));
3274 let max_preview_size = vec2(48.0, 32.0);
3275
3276 let pixels_per_point = self.pixels_per_point();
3277
3278 ui.group(|ui| {
3279 ScrollArea::vertical()
3280 .max_height(300.0)
3281 .auto_shrink([false, true])
3282 .show(ui, |ui| {
3283 ui.style_mut().override_text_style = Some(TextStyle::Monospace);
3284 Grid::new("textures")
3285 .striped(true)
3286 .num_columns(4)
3287 .spacing(vec2(16.0, 2.0))
3288 .min_row_height(max_preview_size.y)
3289 .show(ui, |ui| {
3290 for (&texture_id, meta) in textures {
3291 let [w, h] = meta.size;
3292 let point_size = vec2(w as f32, h as f32) / pixels_per_point;
3293
3294 let mut size = point_size;
3295 size *= (max_preview_size.x / size.x).min(1.0);
3296 size *= (max_preview_size.y / size.y).min(1.0);
3297 ui.image(SizedTexture::new(texture_id, size))
3298 .on_hover_ui(|ui| {
3299 let max_size = 0.5 * ui.ctx().content_rect().size();
3301 let mut size = point_size;
3302 size *= max_size.x / size.x.max(max_size.x);
3303 size *= max_size.y / size.y.max(max_size.y);
3304 ui.image(SizedTexture::new(texture_id, size));
3305 });
3306
3307 ui.label(format!("{w} x {h}"));
3308 ui.label(format!("{:.3} MB", meta.bytes_used() as f64 * 1e-6));
3309 ui.label(format!("{:?}", meta.name));
3310 ui.end_row();
3311 }
3312 });
3313 });
3314 });
3315 }
3316
3317 pub fn loaders_ui(&self, ui: &mut crate::Ui) {
3319 struct LoaderInfo {
3320 id: String,
3321 byte_size: usize,
3322 }
3323
3324 let mut byte_loaders = vec![];
3325 let mut image_loaders = vec![];
3326 let mut texture_loaders = vec![];
3327
3328 {
3329 let loaders = self.loaders();
3330 let Loaders {
3331 include: _,
3332 bytes,
3333 image,
3334 texture,
3335 } = loaders.as_ref();
3336
3337 for loader in bytes.lock().iter() {
3338 byte_loaders.push(LoaderInfo {
3339 id: loader.id().to_owned(),
3340 byte_size: loader.byte_size(),
3341 });
3342 }
3343 for loader in image.lock().iter() {
3344 image_loaders.push(LoaderInfo {
3345 id: loader.id().to_owned(),
3346 byte_size: loader.byte_size(),
3347 });
3348 }
3349 for loader in texture.lock().iter() {
3350 texture_loaders.push(LoaderInfo {
3351 id: loader.id().to_owned(),
3352 byte_size: loader.byte_size(),
3353 });
3354 }
3355 }
3356
3357 fn loaders_ui(ui: &mut crate::Ui, title: &str, loaders: &[LoaderInfo]) {
3358 let heading = format!("{} {title} loaders", loaders.len());
3359 crate::CollapsingHeader::new(heading)
3360 .default_open(true)
3361 .show(ui, |ui| {
3362 Grid::new("loaders")
3363 .striped(true)
3364 .num_columns(2)
3365 .show(ui, |ui| {
3366 ui.label("ID");
3367 ui.label("Size");
3368 ui.end_row();
3369
3370 for loader in loaders {
3371 ui.label(&loader.id);
3372 ui.label(format!("{:.3} MB", loader.byte_size as f64 * 1e-6));
3373 ui.end_row();
3374 }
3375 });
3376 });
3377 }
3378
3379 loaders_ui(ui, "byte", &byte_loaders);
3380 loaders_ui(ui, "image", &image_loaders);
3381 loaders_ui(ui, "texture", &texture_loaders);
3382 }
3383
3384 pub fn memory_ui(&self, ui: &mut crate::Ui) {
3386 if ui
3387 .button("Reset all")
3388 .on_hover_text("Reset all egui state")
3389 .clicked()
3390 {
3391 self.memory_mut(|mem| *mem = Default::default());
3392 }
3393
3394 let (num_state, num_serialized) = self.data(|d| (d.len(), d.count_serialized()));
3395 ui.label(format!(
3396 "{num_state} widget states stored (of which {num_serialized} are serialized)."
3397 ));
3398
3399 ui.horizontal(|ui| {
3400 ui.label(format!(
3401 "{} areas (panels, windows, popups, …)",
3402 self.memory(|mem| mem.areas().count())
3403 ));
3404 if ui.button("Reset").clicked() {
3405 self.memory_mut(|mem| *mem.areas_mut() = Default::default());
3406 }
3407 });
3408 ui.indent("layers", |ui| {
3409 ui.label("Layers, ordered back to front.");
3410 let layers_ids: Vec<LayerId> = self.memory(|mem| mem.areas().order().to_vec());
3411 for layer_id in layers_ids {
3412 if let Some(area) = AreaState::load(self, layer_id.id) {
3413 let is_visible = self.memory(|mem| mem.areas().is_visible(&layer_id));
3414 if !is_visible {
3415 continue;
3416 }
3417 let text = format!("{} - {:?}", layer_id.short_debug_format(), area.rect(),);
3418 let response =
3420 ui.add(Label::new(RichText::new(text).monospace()).sense(Sense::click()));
3421 if response.hovered() && is_visible {
3422 ui.ctx()
3423 .debug_painter()
3424 .debug_rect(area.rect(), Color32::RED, "");
3425 }
3426 } else {
3427 ui.monospace(layer_id.short_debug_format());
3428 }
3429 }
3430 });
3431
3432 ui.horizontal(|ui| {
3433 ui.label(format!(
3434 "{} collapsing headers",
3435 self.data(|d| d.count::<containers::collapsing_header::InnerState>())
3436 ));
3437 if ui.button("Reset").clicked() {
3438 self.data_mut(|d| d.remove_by_type::<containers::collapsing_header::InnerState>());
3439 }
3440 });
3441
3442 #[expect(deprecated)]
3443 ui.horizontal(|ui| {
3444 ui.label(format!(
3445 "{} menu bars",
3446 self.data(|d| d.count::<crate::menu::BarState>())
3447 ));
3448 if ui.button("Reset").clicked() {
3449 self.data_mut(|d| d.remove_by_type::<crate::menu::BarState>());
3450 }
3451 });
3452
3453 ui.horizontal(|ui| {
3454 ui.label(format!(
3455 "{} scroll areas",
3456 self.data(|d| d.count::<scroll_area::State>())
3457 ));
3458 if ui.button("Reset").clicked() {
3459 self.data_mut(|d| d.remove_by_type::<scroll_area::State>());
3460 }
3461 });
3462
3463 ui.horizontal(|ui| {
3464 ui.label(format!(
3465 "{} resize areas",
3466 self.data(|d| d.count::<resize::State>())
3467 ));
3468 if ui.button("Reset").clicked() {
3469 self.data_mut(|d| d.remove_by_type::<resize::State>());
3470 }
3471 });
3472
3473 ui.shrink_width_to_current(); ui.label("NOTE: the position of this window cannot be reset from within itself.");
3475
3476 ui.collapsing("Interaction", |ui| {
3477 let interaction = self.memory(|mem| mem.interaction().clone());
3478 interaction.ui(ui);
3479 });
3480 }
3481}
3482
3483impl Context {
3484 pub fn style_ui(&self, ui: &mut Ui, theme: Theme) {
3486 let mut style: Style = (*self.style_of(theme)).clone();
3487 style.ui(ui);
3488 self.set_style_of(theme, style);
3489 }
3490}
3491
3492impl Context {
3494 #[cfg(feature = "accesskit")]
3504 pub fn accesskit_node_builder<R>(
3505 &self,
3506 id: Id,
3507 writer: impl FnOnce(&mut accesskit::Node) -> R,
3508 ) -> Option<R> {
3509 self.write(|ctx| {
3510 ctx.viewport()
3511 .this_pass
3512 .accesskit_state
3513 .is_some()
3514 .then(|| ctx.accesskit_node_builder(id))
3515 .map(writer)
3516 })
3517 }
3518
3519 #[cfg(feature = "accesskit")]
3520 pub(crate) fn register_accesskit_parent(&self, id: Id, parent_id: Id) {
3521 self.write(|ctx| {
3522 if let Some(state) = ctx.viewport().this_pass.accesskit_state.as_mut() {
3523 state.parent_map.insert(id, parent_id);
3524 }
3525 });
3526 }
3527
3528 #[cfg(feature = "accesskit")]
3530 pub fn enable_accesskit(&self) {
3531 self.write(|ctx| ctx.is_accesskit_enabled = true);
3532 }
3533
3534 #[cfg(feature = "accesskit")]
3536 pub fn disable_accesskit(&self) {
3537 self.write(|ctx| ctx.is_accesskit_enabled = false);
3538 }
3539}
3540
3541impl Context {
3543 pub fn include_bytes(&self, uri: impl Into<Cow<'static, str>>, bytes: impl Into<Bytes>) {
3550 self.loaders().include.insert(uri, bytes);
3551 }
3552
3553 pub fn is_loader_installed(&self, id: &str) -> bool {
3556 let loaders = self.loaders();
3557
3558 loaders.bytes.lock().iter().any(|l| l.id() == id)
3559 || loaders.image.lock().iter().any(|l| l.id() == id)
3560 || loaders.texture.lock().iter().any(|l| l.id() == id)
3561 }
3562
3563 pub fn add_bytes_loader(&self, loader: Arc<dyn load::BytesLoader + Send + Sync + 'static>) {
3569 self.loaders().bytes.lock().push(loader);
3570 }
3571
3572 pub fn add_image_loader(&self, loader: Arc<dyn load::ImageLoader + Send + Sync + 'static>) {
3578 self.loaders().image.lock().push(loader);
3579 }
3580
3581 pub fn add_texture_loader(&self, loader: Arc<dyn load::TextureLoader + Send + Sync + 'static>) {
3587 self.loaders().texture.lock().push(loader);
3588 }
3589
3590 pub fn forget_image(&self, uri: &str) {
3595 use load::BytesLoader as _;
3596
3597 profiling::function_scope!();
3598
3599 let loaders = self.loaders();
3600
3601 loaders.include.forget(uri);
3602 for loader in loaders.bytes.lock().iter() {
3603 loader.forget(uri);
3604 }
3605 for loader in loaders.image.lock().iter() {
3606 loader.forget(uri);
3607 }
3608 for loader in loaders.texture.lock().iter() {
3609 loader.forget(uri);
3610 }
3611 }
3612
3613 pub fn forget_all_images(&self) {
3617 use load::BytesLoader as _;
3618
3619 profiling::function_scope!();
3620
3621 let loaders = self.loaders();
3622
3623 loaders.include.forget_all();
3624 for loader in loaders.bytes.lock().iter() {
3625 loader.forget_all();
3626 }
3627 for loader in loaders.image.lock().iter() {
3628 loader.forget_all();
3629 }
3630 for loader in loaders.texture.lock().iter() {
3631 loader.forget_all();
3632 }
3633 }
3634
3635 pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult {
3654 profiling::function_scope!(uri);
3655
3656 let loaders = self.loaders();
3657 let bytes_loaders = loaders.bytes.lock();
3658
3659 for loader in bytes_loaders.iter().rev() {
3661 let result = loader.load(self, uri);
3662 match result {
3663 Err(load::LoadError::NotSupported) => {}
3664 _ => return result,
3665 }
3666 }
3667
3668 Err(load::LoadError::NoMatchingBytesLoader)
3669 }
3670
3671 pub fn try_load_image(&self, uri: &str, size_hint: load::SizeHint) -> load::ImageLoadResult {
3692 profiling::function_scope!(uri);
3693
3694 let loaders = self.loaders();
3695 let image_loaders = loaders.image.lock();
3696 if image_loaders.is_empty() {
3697 return Err(load::LoadError::NoImageLoaders);
3698 }
3699
3700 let mut format = None;
3701
3702 for loader in image_loaders.iter().rev() {
3704 match loader.load(self, uri, size_hint) {
3705 Err(load::LoadError::NotSupported) => {}
3706 Err(load::LoadError::FormatNotSupported { detected_format }) => {
3707 format = format.or(detected_format);
3708 }
3709 result => return result,
3710 }
3711 }
3712
3713 Err(load::LoadError::NoMatchingImageLoader {
3714 detected_format: format,
3715 })
3716 }
3717
3718 pub fn try_load_texture(
3737 &self,
3738 uri: &str,
3739 texture_options: TextureOptions,
3740 size_hint: load::SizeHint,
3741 ) -> load::TextureLoadResult {
3742 profiling::function_scope!(uri);
3743
3744 let loaders = self.loaders();
3745 let texture_loaders = loaders.texture.lock();
3746
3747 for loader in texture_loaders.iter().rev() {
3749 match loader.load(self, uri, texture_options, size_hint) {
3750 Err(load::LoadError::NotSupported) => {}
3751 result => return result,
3752 }
3753 }
3754
3755 Err(load::LoadError::NoMatchingTextureLoader)
3756 }
3757
3758 pub fn loaders(&self) -> Arc<Loaders> {
3760 self.read(|this| this.loaders.clone())
3761 }
3762
3763 pub fn has_pending_images(&self) -> bool {
3765 self.read(|this| {
3766 this.loaders.image.lock().iter().any(|i| i.has_pending())
3767 || this.loaders.bytes.lock().iter().any(|i| i.has_pending())
3768 })
3769 }
3770}
3771
3772impl Context {
3774 pub fn viewport_id(&self) -> ViewportId {
3780 self.read(|ctx| ctx.viewport_id())
3781 }
3782
3783 pub fn parent_viewport_id(&self) -> ViewportId {
3789 self.read(|ctx| ctx.parent_viewport_id())
3790 }
3791
3792 pub fn viewport<R>(&self, reader: impl FnOnce(&ViewportState) -> R) -> R {
3794 self.write(|ctx| reader(ctx.viewport()))
3795 }
3796
3797 pub fn viewport_for<R>(
3799 &self,
3800 viewport_id: ViewportId,
3801 reader: impl FnOnce(&ViewportState) -> R,
3802 ) -> R {
3803 self.write(|ctx| reader(ctx.viewport_for(viewport_id)))
3804 }
3805
3806 pub fn set_immediate_viewport_renderer(
3819 callback: impl for<'a> Fn(&Self, ImmediateViewport<'a>) + 'static,
3820 ) {
3821 let callback = Box::new(callback);
3822 IMMEDIATE_VIEWPORT_RENDERER.with(|render_sync| {
3823 render_sync.replace(Some(callback));
3824 });
3825 }
3826
3827 pub fn embed_viewports(&self) -> bool {
3832 self.read(|ctx| ctx.embed_viewports)
3833 }
3834
3835 pub fn set_embed_viewports(&self, value: bool) {
3840 self.write(|ctx| ctx.embed_viewports = value);
3841 }
3842
3843 pub fn send_viewport_cmd(&self, command: ViewportCommand) {
3847 self.send_viewport_cmd_to(self.viewport_id(), command);
3848 }
3849
3850 pub fn send_viewport_cmd_to(&self, id: ViewportId, command: ViewportCommand) {
3854 self.request_repaint_of(id);
3855
3856 if command.requires_parent_repaint() {
3857 self.request_repaint_of(self.parent_viewport_id());
3858 }
3859
3860 self.write(|ctx| ctx.viewport_for(id).commands.push(command));
3861 }
3862
3863 pub fn show_viewport_deferred(
3893 &self,
3894 new_viewport_id: ViewportId,
3895 viewport_builder: ViewportBuilder,
3896 viewport_ui_cb: impl Fn(&Self, ViewportClass) + Send + Sync + 'static,
3897 ) {
3898 profiling::function_scope!();
3899
3900 if self.embed_viewports() {
3901 viewport_ui_cb(self, ViewportClass::Embedded);
3902 } else {
3903 self.write(|ctx| {
3904 ctx.viewport_parents
3905 .insert(new_viewport_id, ctx.viewport_id());
3906
3907 let viewport = ctx.viewports.entry(new_viewport_id).or_default();
3908 viewport.class = ViewportClass::Deferred;
3909 viewport.builder = viewport_builder;
3910 viewport.used = true;
3911 viewport.viewport_ui_cb = Some(Arc::new(move |ctx| {
3912 (viewport_ui_cb)(ctx, ViewportClass::Deferred);
3913 }));
3914 });
3915 }
3916 }
3917
3918 pub fn show_viewport_immediate<T>(
3945 &self,
3946 new_viewport_id: ViewportId,
3947 builder: ViewportBuilder,
3948 mut viewport_ui_cb: impl FnMut(&Self, ViewportClass) -> T,
3949 ) -> T {
3950 profiling::function_scope!();
3951
3952 if self.embed_viewports() {
3953 return viewport_ui_cb(self, ViewportClass::Embedded);
3954 }
3955
3956 IMMEDIATE_VIEWPORT_RENDERER.with(|immediate_viewport_renderer| {
3957 let immediate_viewport_renderer = immediate_viewport_renderer.borrow();
3958 let Some(immediate_viewport_renderer) = immediate_viewport_renderer.as_ref() else {
3959 return viewport_ui_cb(self, ViewportClass::Embedded);
3961 };
3962
3963 let ids = self.write(|ctx| {
3964 let parent_viewport_id = ctx.viewport_id();
3965
3966 ctx.viewport_parents
3967 .insert(new_viewport_id, parent_viewport_id);
3968
3969 let viewport = ctx.viewports.entry(new_viewport_id).or_default();
3970 viewport.builder = builder.clone();
3971 viewport.used = true;
3972 viewport.viewport_ui_cb = None; ViewportIdPair::from_self_and_parent(new_viewport_id, parent_viewport_id)
3975 });
3976
3977 let mut out = None;
3978 {
3979 let out = &mut out;
3980
3981 let viewport = ImmediateViewport {
3982 ids,
3983 builder,
3984 viewport_ui_cb: Box::new(move |context| {
3985 *out = Some(viewport_ui_cb(context, ViewportClass::Immediate));
3986 }),
3987 };
3988
3989 immediate_viewport_renderer(self, viewport);
3990 }
3991
3992 out.expect(
3993 "egui backend is implemented incorrectly - the user callback was never called",
3994 )
3995 })
3996 }
3997}
3998
3999impl Context {
4001 pub fn interaction_snapshot<R>(&self, reader: impl FnOnce(&InteractionSnapshot) -> R) -> R {
4003 self.write(|w| reader(&w.viewport().interact_widgets))
4004 }
4005
4006 pub fn dragged_id(&self) -> Option<Id> {
4014 self.interaction_snapshot(|i| i.dragged)
4015 }
4016
4017 pub fn is_being_dragged(&self, id: Id) -> bool {
4024 self.dragged_id() == Some(id)
4025 }
4026
4027 pub fn drag_started_id(&self) -> Option<Id> {
4031 self.interaction_snapshot(|i| i.drag_started)
4032 }
4033
4034 pub fn drag_stopped_id(&self) -> Option<Id> {
4036 self.interaction_snapshot(|i| i.drag_stopped)
4037 }
4038
4039 pub fn set_dragged_id(&self, id: Id) {
4041 self.write(|ctx| {
4042 let vp = ctx.viewport();
4043 let i = &mut vp.interact_widgets;
4044 if i.dragged != Some(id) {
4045 i.drag_stopped = i.dragged.or(i.drag_stopped);
4046 i.dragged = Some(id);
4047 i.drag_started = Some(id);
4048 }
4049
4050 ctx.memory.interaction_mut().potential_drag_id = Some(id);
4051 });
4052 }
4053
4054 pub fn stop_dragging(&self) {
4056 self.write(|ctx| {
4057 let vp = ctx.viewport();
4058 let i = &mut vp.interact_widgets;
4059 if i.dragged.is_some() {
4060 i.drag_stopped = i.dragged;
4061 i.dragged = None;
4062 }
4063
4064 ctx.memory.interaction_mut().potential_drag_id = None;
4065 });
4066 }
4067
4068 #[inline(always)]
4072 pub fn dragging_something_else(&self, not_this: Id) -> bool {
4073 let dragged = self.dragged_id();
4074 dragged.is_some() && dragged != Some(not_this)
4075 }
4076}
4077
4078#[test]
4079fn context_impl_send_sync() {
4080 fn assert_send_sync<T: Send + Sync>() {}
4081 assert_send_sync::<Context>();
4082}
4083
4084#[cfg(test)]
4085mod test {
4086 use super::Context;
4087
4088 #[test]
4089 fn test_single_pass() {
4090 let ctx = Context::default();
4091 ctx.options_mut(|o| o.max_passes = 1.try_into().unwrap());
4092
4093 {
4095 let mut num_calls = 0;
4096 let output = ctx.run(Default::default(), |ctx| {
4097 num_calls += 1;
4098 assert_eq!(ctx.output(|o| o.num_completed_passes), 0);
4099 assert!(!ctx.output(|o| o.requested_discard()));
4100 assert!(!ctx.will_discard());
4101 });
4102 assert_eq!(num_calls, 1);
4103 assert_eq!(output.platform_output.num_completed_passes, 1);
4104 assert!(!output.platform_output.requested_discard());
4105 }
4106
4107 {
4109 let mut num_calls = 0;
4110 let output = ctx.run(Default::default(), |ctx| {
4111 num_calls += 1;
4112 ctx.request_discard("test");
4113 assert!(!ctx.will_discard(), "The request should have been denied");
4114 });
4115 assert_eq!(num_calls, 1);
4116 assert_eq!(output.platform_output.num_completed_passes, 1);
4117 assert!(
4118 output.platform_output.requested_discard(),
4119 "The request should be reported"
4120 );
4121 assert_eq!(
4122 output
4123 .platform_output
4124 .request_discard_reasons
4125 .first()
4126 .unwrap()
4127 .reason,
4128 "test"
4129 );
4130 }
4131 }
4132
4133 #[test]
4134 fn test_dual_pass() {
4135 let ctx = Context::default();
4136 ctx.options_mut(|o| o.max_passes = 2.try_into().unwrap());
4137
4138 {
4140 let mut num_calls = 0;
4141 let output = ctx.run(Default::default(), |ctx| {
4142 assert_eq!(ctx.output(|o| o.num_completed_passes), 0);
4143 assert!(!ctx.output(|o| o.requested_discard()));
4144 assert!(!ctx.will_discard());
4145 num_calls += 1;
4146 });
4147 assert_eq!(num_calls, 1);
4148 assert_eq!(output.platform_output.num_completed_passes, 1);
4149 assert!(!output.platform_output.requested_discard());
4150 }
4151
4152 {
4154 let mut num_calls = 0;
4155 let output = ctx.run(Default::default(), |ctx| {
4156 assert_eq!(ctx.output(|o| o.num_completed_passes), num_calls);
4157
4158 assert!(!ctx.will_discard());
4159 if num_calls == 0 {
4160 ctx.request_discard("test");
4161 assert!(ctx.will_discard());
4162 }
4163
4164 num_calls += 1;
4165 });
4166 assert_eq!(num_calls, 2);
4167 assert_eq!(output.platform_output.num_completed_passes, 2);
4168 assert!(
4169 !output.platform_output.requested_discard(),
4170 "The request should have been cleared when fulfilled"
4171 );
4172 }
4173
4174 {
4176 let mut num_calls = 0;
4177 let output = ctx.run(Default::default(), |ctx| {
4178 assert_eq!(ctx.output(|o| o.num_completed_passes), num_calls);
4179
4180 assert!(!ctx.will_discard());
4181 ctx.request_discard("test");
4182 if num_calls == 0 {
4183 assert!(ctx.will_discard(), "First request granted");
4184 } else {
4185 assert!(!ctx.will_discard(), "Second request should be denied");
4186 }
4187
4188 num_calls += 1;
4189 });
4190 assert_eq!(num_calls, 2);
4191 assert_eq!(output.platform_output.num_completed_passes, 2);
4192 assert!(
4193 output.platform_output.requested_discard(),
4194 "The unfulfilled request should be reported"
4195 );
4196 }
4197 }
4198
4199 #[test]
4200 fn test_multi_pass() {
4201 let ctx = Context::default();
4202 ctx.options_mut(|o| o.max_passes = 10.try_into().unwrap());
4203
4204 {
4206 let mut num_calls = 0;
4207 let output = ctx.run(Default::default(), |ctx| {
4208 assert_eq!(ctx.output(|o| o.num_completed_passes), num_calls);
4209
4210 assert!(!ctx.will_discard());
4211 if num_calls <= 2 {
4212 ctx.request_discard("test");
4213 assert!(ctx.will_discard());
4214 }
4215
4216 num_calls += 1;
4217 });
4218 assert_eq!(num_calls, 4);
4219 assert_eq!(output.platform_output.num_completed_passes, 4);
4220 assert!(
4221 !output.platform_output.requested_discard(),
4222 "The request should have been cleared when fulfilled"
4223 );
4224 }
4225 }
4226}