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(feature = "accesskit")]
1202 if allow_focus && w.sense.is_focusable() {
1203 self.accesskit_node_builder(w.id, |builder| res.fill_accesskit_node_common(builder));
1207 }
1208
1209 #[cfg(feature = "accesskit")]
1210 self.write(|ctx| {
1211 use crate::{Align, pass_state::ScrollTarget, style::ScrollAnimation};
1212 let viewport = ctx.viewport_for(ctx.viewport_id());
1213
1214 viewport
1215 .input
1216 .consume_accesskit_action_requests(res.id, |request| {
1217 const DISTANCE: f32 = 100.0;
1220
1221 match &request.action {
1222 accesskit::Action::ScrollIntoView => {
1223 viewport.this_pass.scroll_target = [
1224 Some(ScrollTarget::new(
1225 res.rect.x_range(),
1226 Some(Align::Center),
1227 ScrollAnimation::none(),
1228 )),
1229 Some(ScrollTarget::new(
1230 res.rect.y_range(),
1231 Some(Align::Center),
1232 ScrollAnimation::none(),
1233 )),
1234 ];
1235 }
1236 accesskit::Action::ScrollDown => {
1237 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::UP;
1238 }
1239 accesskit::Action::ScrollUp => {
1240 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::DOWN;
1241 }
1242 accesskit::Action::ScrollLeft => {
1243 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::LEFT;
1244 }
1245 accesskit::Action::ScrollRight => {
1246 viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::RIGHT;
1247 }
1248 _ => return false,
1249 }
1250 true
1251 });
1252 });
1253
1254 res
1255 }
1256
1257 pub fn read_response(&self, id: Id) -> Option<Response> {
1265 self.write(|ctx| {
1266 let viewport = ctx.viewport();
1267 let widget_rect = viewport
1268 .this_pass
1269 .widgets
1270 .get(id)
1271 .or_else(|| viewport.prev_pass.widgets.get(id))
1272 .copied();
1273 widget_rect.map(|mut rect| {
1274 if !(rect.rect.is_positive() && rect.rect.is_finite())
1277 && let Some(prev_rect) = viewport.prev_pass.widgets.get(id)
1278 {
1279 rect.rect = prev_rect.rect;
1280 }
1281 rect
1282 })
1283 })
1284 .map(|widget_rect| self.get_response(widget_rect))
1285 }
1286
1287 pub(crate) fn get_response(&self, widget_rect: WidgetRect) -> Response {
1289 use response::Flags;
1290
1291 let WidgetRect {
1292 id,
1293 layer_id,
1294 rect,
1295 interact_rect,
1296 sense,
1297 enabled,
1298 } = widget_rect;
1299
1300 let highlighted = self.prev_pass_state(|fs| fs.highlight_next_pass.contains(&id));
1302
1303 let mut res = Response {
1304 ctx: self.clone(),
1305 layer_id,
1306 id,
1307 rect,
1308 interact_rect,
1309 sense,
1310 flags: Flags::empty(),
1311 interact_pointer_pos: None,
1312 intrinsic_size: None,
1313 };
1314
1315 res.flags.set(Flags::ENABLED, enabled);
1316 res.flags.set(Flags::HIGHLIGHTED, highlighted);
1317
1318 self.write(|ctx| {
1319 let viewport = ctx.viewports.entry(ctx.viewport_id()).or_default();
1320
1321 res.flags.set(
1322 Flags::CONTAINS_POINTER,
1323 viewport.interact_widgets.contains_pointer.contains(&id),
1324 );
1325
1326 let input = &viewport.input;
1327 let memory = &mut ctx.memory;
1328
1329 if enabled
1330 && sense.senses_click()
1331 && memory.has_focus(id)
1332 && (input.key_pressed(Key::Space) || input.key_pressed(Key::Enter))
1333 {
1334 res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);
1336 }
1337
1338 #[cfg(feature = "accesskit")]
1339 if enabled
1340 && sense.senses_click()
1341 && input.has_accesskit_action_request(id, accesskit::Action::Click)
1342 {
1343 res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);
1344 }
1345
1346 if enabled && sense.senses_click() && Some(id) == viewport.interact_widgets.long_touched
1347 {
1348 res.flags.set(Flags::LONG_TOUCHED, true);
1349 }
1350
1351 let interaction = memory.interaction();
1352
1353 res.flags.set(
1354 Flags::IS_POINTER_BUTTON_DOWN_ON,
1355 interaction.potential_click_id == Some(id)
1356 || interaction.potential_drag_id == Some(id),
1357 );
1358
1359 if res.enabled() {
1360 res.flags.set(
1361 Flags::HOVERED,
1362 viewport.interact_widgets.hovered.contains(&id),
1363 );
1364 res.flags.set(
1365 Flags::DRAGGED,
1366 Some(id) == viewport.interact_widgets.dragged,
1367 );
1368 res.flags.set(
1369 Flags::DRAG_STARTED,
1370 Some(id) == viewport.interact_widgets.drag_started,
1371 );
1372 res.flags.set(
1373 Flags::DRAG_STOPPED,
1374 Some(id) == viewport.interact_widgets.drag_stopped,
1375 );
1376 }
1377
1378 let clicked = Some(id) == viewport.interact_widgets.clicked;
1379 let mut any_press = false;
1380
1381 for pointer_event in &input.pointer.pointer_events {
1382 match pointer_event {
1383 PointerEvent::Moved(_) => {}
1384 PointerEvent::Pressed { .. } => {
1385 any_press = true;
1386 }
1387 PointerEvent::Released { click, .. } => {
1388 if enabled && sense.senses_click() && clicked && click.is_some() {
1389 res.flags.set(Flags::CLICKED, true);
1390 }
1391
1392 res.flags.set(Flags::IS_POINTER_BUTTON_DOWN_ON, false);
1393 res.flags.set(Flags::DRAGGED, false);
1394 }
1395 }
1396 }
1397
1398 let is_interacted_with = res.is_pointer_button_down_on()
1401 || res.long_touched()
1402 || clicked
1403 || res.drag_stopped();
1404 if is_interacted_with {
1405 res.interact_pointer_pos = input.pointer.interact_pos();
1406 if let (Some(to_global), Some(pos)) = (
1407 memory.to_global.get(&res.layer_id),
1408 &mut res.interact_pointer_pos,
1409 ) {
1410 *pos = to_global.inverse() * *pos;
1411 }
1412 }
1413
1414 if input.pointer.any_down() && !is_interacted_with {
1415 res.flags.set(Flags::HOVERED, false);
1417 }
1418
1419 let should_surrender_focus = match memory.options.input_options.surrender_focus_on {
1420 SurrenderFocusOn::Presses => any_press,
1421 SurrenderFocusOn::Clicks => input.pointer.any_click(),
1422 SurrenderFocusOn::Never => false,
1423 };
1424
1425 let pointer_clicked_elsewhere = should_surrender_focus && !res.hovered();
1426 if pointer_clicked_elsewhere && memory.has_focus(id) {
1427 memory.surrender_focus(id);
1428 }
1429 });
1430
1431 res
1432 }
1433
1434 #[inline]
1438 pub fn register_widget_info(&self, id: Id, make_info: impl Fn() -> crate::WidgetInfo) {
1439 #[cfg(debug_assertions)]
1440 self.write(|ctx| {
1441 if ctx.memory.options.style().debug.show_interactive_widgets {
1442 ctx.viewport().this_pass.widgets.set_info(id, make_info());
1443 }
1444 });
1445
1446 #[cfg(not(debug_assertions))]
1447 {
1448 _ = (self, id, make_info);
1449 }
1450 }
1451
1452 pub fn layer_painter(&self, layer_id: LayerId) -> Painter {
1454 let content_rect = self.content_rect();
1455 Painter::new(self.clone(), layer_id, content_rect)
1456 }
1457
1458 pub fn debug_painter(&self) -> Painter {
1460 Self::layer_painter(self, LayerId::debug())
1461 }
1462
1463 #[track_caller]
1477 pub fn debug_text(&self, text: impl Into<WidgetText>) {
1478 crate::debug_text::print(self, text);
1479 }
1480
1481 pub fn os(&self) -> OperatingSystem {
1489 self.read(|ctx| ctx.os)
1490 }
1491
1492 pub fn set_os(&self, os: OperatingSystem) {
1497 self.write(|ctx| ctx.os = os);
1498 }
1499
1500 pub fn set_cursor_icon(&self, cursor_icon: CursorIcon) {
1508 self.output_mut(|o| o.cursor_icon = cursor_icon);
1509 }
1510
1511 pub fn send_cmd(&self, cmd: crate::OutputCommand) {
1514 self.output_mut(|o| o.commands.push(cmd));
1515 }
1516
1517 pub fn open_url(&self, open_url: crate::OpenUrl) {
1526 self.send_cmd(crate::OutputCommand::OpenUrl(open_url));
1527 }
1528
1529 pub fn copy_text(&self, text: String) {
1535 self.send_cmd(crate::OutputCommand::CopyText(text));
1536 }
1537
1538 pub fn copy_image(&self, image: crate::ColorImage) {
1544 self.send_cmd(crate::OutputCommand::CopyImage(image));
1545 }
1546
1547 fn can_show_modifier_symbols(&self) -> bool {
1548 let ModifierNames {
1549 alt,
1550 ctrl,
1551 shift,
1552 mac_cmd,
1553 ..
1554 } = ModifierNames::SYMBOLS;
1555
1556 let font_id = TextStyle::Body.resolve(&self.style());
1557 self.fonts_mut(|f| {
1558 let mut font = f.fonts.font(&font_id.family);
1559 font.has_glyphs(alt)
1560 && font.has_glyphs(ctrl)
1561 && font.has_glyphs(shift)
1562 && font.has_glyphs(mac_cmd)
1563 })
1564 }
1565
1566 pub fn format_modifiers(&self, modifiers: Modifiers) -> String {
1568 let os = self.os();
1569
1570 let is_mac = os.is_mac();
1571
1572 if is_mac && self.can_show_modifier_symbols() {
1573 ModifierNames::SYMBOLS.format(&modifiers, is_mac)
1574 } else {
1575 ModifierNames::NAMES.format(&modifiers, is_mac)
1576 }
1577 }
1578
1579 pub fn format_shortcut(&self, shortcut: &KeyboardShortcut) -> String {
1583 let os = self.os();
1584
1585 let is_mac = os.is_mac();
1586
1587 if is_mac && self.can_show_modifier_symbols() {
1588 shortcut.format(&ModifierNames::SYMBOLS, is_mac)
1589 } else {
1590 shortcut.format(&ModifierNames::NAMES, is_mac)
1591 }
1592 }
1593
1594 pub fn cumulative_frame_nr(&self) -> u64 {
1600 self.cumulative_frame_nr_for(self.viewport_id())
1601 }
1602
1603 pub fn cumulative_frame_nr_for(&self, id: ViewportId) -> u64 {
1609 self.read(|ctx| {
1610 ctx.viewports
1611 .get(&id)
1612 .map(|v| v.repaint.cumulative_frame_nr)
1613 .unwrap_or_else(|| {
1614 if cfg!(debug_assertions) {
1615 panic!("cumulative_frame_nr_for failed to find the viewport {id:?}");
1616 } else {
1617 0
1618 }
1619 })
1620 })
1621 }
1622
1623 pub fn cumulative_pass_nr(&self) -> u64 {
1630 self.cumulative_pass_nr_for(self.viewport_id())
1631 }
1632
1633 pub fn cumulative_pass_nr_for(&self, id: ViewportId) -> u64 {
1637 self.read(|ctx| {
1638 ctx.viewports
1639 .get(&id)
1640 .map_or(0, |v| v.repaint.cumulative_pass_nr)
1641 })
1642 }
1643
1644 pub fn current_pass_index(&self) -> usize {
1653 self.output(|o| o.num_completed_passes)
1654 }
1655
1656 #[track_caller]
1669 pub fn request_repaint(&self) {
1670 self.request_repaint_of(self.viewport_id());
1671 }
1672
1673 #[track_caller]
1686 pub fn request_repaint_of(&self, id: ViewportId) {
1687 let cause = RepaintCause::new();
1688 self.write(|ctx| ctx.request_repaint(id, cause));
1689 }
1690
1691 #[track_caller]
1720 pub fn request_repaint_after(&self, duration: Duration) {
1721 self.request_repaint_after_for(duration, self.viewport_id());
1722 }
1723
1724 #[track_caller]
1728 pub fn request_repaint_after_secs(&self, seconds: f32) {
1729 if let Ok(duration) = std::time::Duration::try_from_secs_f32(seconds) {
1730 self.request_repaint_after(duration);
1731 }
1732 }
1733
1734 #[track_caller]
1763 pub fn request_repaint_after_for(&self, duration: Duration, id: ViewportId) {
1764 let cause = RepaintCause::new();
1765 self.write(|ctx| ctx.request_repaint_after(duration, id, cause));
1766 }
1767
1768 #[must_use]
1770 pub fn requested_repaint_last_pass(&self) -> bool {
1771 self.requested_repaint_last_pass_for(&self.viewport_id())
1772 }
1773
1774 #[must_use]
1776 pub fn requested_repaint_last_pass_for(&self, viewport_id: &ViewportId) -> bool {
1777 self.read(|ctx| ctx.requested_immediate_repaint_prev_pass(viewport_id))
1778 }
1779
1780 #[must_use]
1782 pub fn has_requested_repaint(&self) -> bool {
1783 self.has_requested_repaint_for(&self.viewport_id())
1784 }
1785
1786 #[must_use]
1788 pub fn has_requested_repaint_for(&self, viewport_id: &ViewportId) -> bool {
1789 self.read(|ctx| ctx.has_requested_repaint(viewport_id))
1790 }
1791
1792 pub fn repaint_causes(&self) -> Vec<RepaintCause> {
1796 self.read(|ctx| {
1797 ctx.viewports
1798 .get(&ctx.viewport_id())
1799 .map(|v| v.repaint.prev_causes.clone())
1800 })
1801 .unwrap_or_default()
1802 }
1803
1804 pub fn set_request_repaint_callback(
1810 &self,
1811 callback: impl Fn(RequestRepaintInfo) + Send + Sync + 'static,
1812 ) {
1813 let callback = Box::new(callback);
1814 self.write(|ctx| ctx.request_repaint_callback = Some(callback));
1815 }
1816
1817 #[track_caller]
1840 pub fn request_discard(&self, reason: impl Into<Cow<'static, str>>) {
1841 let cause = RepaintCause::new_reason(reason);
1842 self.output_mut(|o| o.request_discard_reasons.push(cause));
1843
1844 log::trace!(
1845 "request_discard: {}",
1846 if self.will_discard() {
1847 "allowed"
1848 } else {
1849 "denied"
1850 }
1851 );
1852 }
1853
1854 pub fn will_discard(&self) -> bool {
1860 self.write(|ctx| {
1861 let vp = ctx.viewport();
1862 vp.output.requested_discard()
1864 && vp.output.num_completed_passes + 1 < ctx.memory.options.max_passes.get()
1865 })
1866 }
1867}
1868
1869impl Context {
1871 pub fn on_begin_pass(&self, debug_name: &'static str, cb: plugin::ContextCallback) {
1875 self.with_plugin(|p: &mut crate::plugin::CallbackPlugin| {
1876 p.on_begin_plugins.push((debug_name, cb));
1877 });
1878 }
1879
1880 pub fn on_end_pass(&self, debug_name: &'static str, cb: plugin::ContextCallback) {
1884 self.with_plugin(|p: &mut crate::plugin::CallbackPlugin| {
1885 p.on_end_plugins.push((debug_name, cb));
1886 });
1887 }
1888
1889 pub fn add_plugin(&self, plugin: impl plugin::Plugin + 'static) {
1896 let handle = plugin::PluginHandle::new(plugin);
1897
1898 let added = self.write(|ctx| ctx.plugins.add(handle.clone()));
1899
1900 if added {
1901 handle.lock().dyn_plugin_mut().setup(self);
1902 }
1903 }
1904
1905 pub fn with_plugin<T: plugin::Plugin + 'static, R>(
1909 &self,
1910 f: impl FnOnce(&mut T) -> R,
1911 ) -> Option<R> {
1912 let plugin = self.read(|ctx| ctx.plugins.get(std::any::TypeId::of::<T>()));
1913 plugin.map(|plugin| f(plugin.lock().typed_plugin_mut()))
1914 }
1915
1916 pub fn plugin<T: plugin::Plugin>(&self) -> TypedPluginHandle<T> {
1921 if let Some(plugin) = self.plugin_opt() {
1922 plugin
1923 } else {
1924 panic!("Plugin of type {:?} not found", std::any::type_name::<T>());
1925 }
1926 }
1927
1928 pub fn plugin_opt<T: plugin::Plugin>(&self) -> Option<TypedPluginHandle<T>> {
1930 let plugin = self.read(|ctx| ctx.plugins.get(std::any::TypeId::of::<T>()));
1931 plugin.map(TypedPluginHandle::new)
1932 }
1933
1934 pub fn plugin_or_default<T: plugin::Plugin + Default>(&self) -> TypedPluginHandle<T> {
1936 if let Some(plugin) = self.plugin_opt() {
1937 plugin
1938 } else {
1939 let default_plugin = T::default();
1940 self.add_plugin(default_plugin);
1941 self.plugin()
1942 }
1943 }
1944}
1945
1946impl Context {
1947 pub fn set_fonts(&self, font_definitions: FontDefinitions) {
1955 profiling::function_scope!();
1956
1957 let mut update_fonts = true;
1958
1959 self.read(|ctx| {
1960 if let Some(current_fonts) = ctx.fonts.as_ref() {
1961 if current_fonts.definitions() == &font_definitions {
1963 update_fonts = false; }
1965 }
1966 });
1967
1968 if update_fonts {
1969 self.memory_mut(|mem| mem.new_font_definitions = Some(font_definitions));
1970 }
1971 }
1972
1973 pub fn add_font(&self, new_font: FontInsert) {
1981 profiling::function_scope!();
1982
1983 let mut update_fonts = true;
1984
1985 self.read(|ctx| {
1986 if let Some(current_fonts) = ctx.fonts.as_ref()
1987 && current_fonts
1988 .definitions()
1989 .font_data
1990 .contains_key(&new_font.name)
1991 {
1992 update_fonts = false; }
1994 });
1995
1996 if update_fonts {
1997 self.memory_mut(|mem| mem.add_fonts.push(new_font));
1998 }
1999 }
2000
2001 pub fn system_theme(&self) -> Option<Theme> {
2004 self.memory(|mem| mem.options.system_theme)
2005 }
2006
2007 pub fn theme(&self) -> Theme {
2010 self.options(|opt| opt.theme())
2011 }
2012
2013 pub fn set_theme(&self, theme_preference: impl Into<crate::ThemePreference>) {
2022 self.options_mut(|opt| opt.theme_preference = theme_preference.into());
2023 }
2024
2025 pub fn style(&self) -> Arc<Style> {
2027 self.options(|opt| opt.style().clone())
2028 }
2029
2030 pub fn style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {
2041 self.options_mut(|opt| mutate_style(Arc::make_mut(opt.style_mut())));
2042 }
2043
2044 pub fn set_style(&self, style: impl Into<Arc<Style>>) {
2052 self.options_mut(|opt| *opt.style_mut() = style.into());
2053 }
2054
2055 pub fn all_styles_mut(&self, mut mutate_style: impl FnMut(&mut Style)) {
2065 self.options_mut(|opt| {
2066 mutate_style(Arc::make_mut(&mut opt.dark_style));
2067 mutate_style(Arc::make_mut(&mut opt.light_style));
2068 });
2069 }
2070
2071 pub fn style_of(&self, theme: Theme) -> Arc<Style> {
2073 self.options(|opt| match theme {
2074 Theme::Dark => opt.dark_style.clone(),
2075 Theme::Light => opt.light_style.clone(),
2076 })
2077 }
2078
2079 pub fn style_mut_of(&self, theme: Theme, mutate_style: impl FnOnce(&mut Style)) {
2089 self.options_mut(|opt| match theme {
2090 Theme::Dark => mutate_style(Arc::make_mut(&mut opt.dark_style)),
2091 Theme::Light => mutate_style(Arc::make_mut(&mut opt.light_style)),
2092 });
2093 }
2094
2095 pub fn set_style_of(&self, theme: Theme, style: impl Into<Arc<Style>>) {
2102 let style = style.into();
2103 self.options_mut(|opt| match theme {
2104 Theme::Dark => opt.dark_style = style,
2105 Theme::Light => opt.light_style = style,
2106 });
2107 }
2108
2109 pub fn set_visuals_of(&self, theme: Theme, visuals: crate::Visuals) {
2119 self.style_mut_of(theme, |style| style.visuals = visuals);
2120 }
2121
2122 pub fn set_visuals(&self, visuals: crate::Visuals) {
2132 self.style_mut_of(self.theme(), |style| style.visuals = visuals);
2133 }
2134
2135 #[inline(always)]
2139 pub fn pixels_per_point(&self) -> f32 {
2140 self.input(|i| i.pixels_per_point)
2141 }
2142
2143 pub fn set_pixels_per_point(&self, pixels_per_point: f32) {
2148 if pixels_per_point != self.pixels_per_point() {
2149 self.set_zoom_factor(pixels_per_point / self.native_pixels_per_point().unwrap_or(1.0));
2150 }
2151 }
2152
2153 #[inline(always)]
2158 pub fn native_pixels_per_point(&self) -> Option<f32> {
2159 self.input(|i| i.viewport().native_pixels_per_point)
2160 }
2161
2162 #[inline(always)]
2170 pub fn zoom_factor(&self) -> f32 {
2171 self.options(|o| o.zoom_factor)
2172 }
2173
2174 #[inline(always)]
2188 pub fn set_zoom_factor(&self, zoom_factor: f32) {
2189 let cause = RepaintCause::new();
2190 self.write(|ctx| {
2191 if ctx.memory.options.zoom_factor != zoom_factor {
2192 ctx.new_zoom_factor = Some(zoom_factor);
2193 #[expect(clippy::iter_over_hash_type)]
2194 for viewport_id in ctx.all_viewport_ids() {
2195 ctx.request_repaint(viewport_id, cause.clone());
2196 }
2197 }
2198 });
2199 }
2200
2201 pub fn load_texture(
2242 &self,
2243 name: impl Into<String>,
2244 image: impl Into<ImageData>,
2245 options: TextureOptions,
2246 ) -> TextureHandle {
2247 let name = name.into();
2248 let image = image.into();
2249 let max_texture_side = self.input(|i| i.max_texture_side);
2250 debug_assert!(
2251 image.width() <= max_texture_side && image.height() <= max_texture_side,
2252 "Texture {:?} has size {}x{}, but the maximum texture side is {}",
2253 name,
2254 image.width(),
2255 image.height(),
2256 max_texture_side
2257 );
2258 let tex_mngr = self.tex_manager();
2259 let tex_id = tex_mngr.write().alloc(name, image, options);
2260 TextureHandle::new(tex_mngr, tex_id)
2261 }
2262
2263 pub fn tex_manager(&self) -> Arc<RwLock<epaint::textures::TextureManager>> {
2269 self.read(|ctx| ctx.tex_manager.0.clone())
2270 }
2271
2272 pub(crate) fn constrain_window_rect_to_area(window: Rect, area: Rect) -> Rect {
2276 let mut pos = window.min;
2277
2278 let margin_x = (window.width() - area.width()).at_least(0.0);
2280 let margin_y = (window.height() - area.height()).at_least(0.0);
2281
2282 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()
2288 }
2289}
2290
2291impl Context {
2292 #[must_use]
2294 pub fn end_pass(&self) -> FullOutput {
2295 profiling::function_scope!();
2296
2297 if self.options(|o| o.zoom_with_keyboard) {
2298 crate::gui_zoom::zoom_with_keyboard(self);
2299 }
2300
2301 let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
2303 plugins.on_end_pass(self);
2304
2305 #[cfg(debug_assertions)]
2306 self.debug_painting();
2307
2308 let mut output = self.write(|ctx| ctx.end_pass());
2309 plugins.on_output(&mut output);
2310 output
2311 }
2312
2313 #[must_use]
2315 #[deprecated = "Renamed end_pass"]
2316 pub fn end_frame(&self) -> FullOutput {
2317 self.end_pass()
2318 }
2319
2320 #[cfg(debug_assertions)]
2322 fn debug_painting(&self) {
2323 #![expect(clippy::iter_over_hash_type)] let paint_widget = |widget: &WidgetRect, text: &str, color: Color32| {
2326 let rect = widget.interact_rect;
2327 if rect.is_positive() {
2328 let painter = Painter::new(self.clone(), widget.layer_id, Rect::EVERYTHING);
2329 painter.debug_rect(rect, color, text);
2330 }
2331 };
2332
2333 let paint_widget_id = |id: Id, text: &str, color: Color32| {
2334 if let Some(widget) =
2335 self.write(|ctx| ctx.viewport().this_pass.widgets.get(id).copied())
2336 {
2337 paint_widget(&widget, text, color);
2338 }
2339 };
2340
2341 if self.style().debug.show_interactive_widgets {
2342 let rects = self.write(|ctx| ctx.viewport().this_pass.widgets.clone());
2344 for (layer_id, rects) in rects.layers() {
2345 let painter = Painter::new(self.clone(), *layer_id, Rect::EVERYTHING);
2346 for rect in rects {
2347 if rect.sense.interactive() {
2348 let (color, text) = if rect.sense.senses_click() && rect.sense.senses_drag()
2349 {
2350 (Color32::from_rgb(0x88, 0, 0x88), "click+drag")
2351 } else if rect.sense.senses_click() {
2352 (Color32::from_rgb(0x88, 0, 0), "click")
2353 } else if rect.sense.senses_drag() {
2354 (Color32::from_rgb(0, 0, 0x88), "drag")
2355 } else {
2356 (Color32::from_rgb(0, 0, 0x88), "hover")
2358 };
2359 painter.debug_rect(rect.interact_rect, color, text);
2360 }
2361 }
2362 }
2363
2364 {
2366 let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());
2367 let InteractionSnapshot {
2368 clicked,
2369 long_touched: _,
2370 drag_started: _,
2371 dragged,
2372 drag_stopped: _,
2373 contains_pointer,
2374 hovered,
2375 } = interact_widgets;
2376
2377 if true {
2378 for &id in &contains_pointer {
2379 paint_widget_id(id, "contains_pointer", Color32::BLUE);
2380 }
2381
2382 let widget_rects = self.write(|w| w.viewport().this_pass.widgets.clone());
2383
2384 let mut contains_pointer: Vec<Id> = contains_pointer.iter().copied().collect();
2385 contains_pointer.sort_by_key(|&id| {
2386 widget_rects
2387 .order(id)
2388 .map(|(layer_id, order_in_layer)| (layer_id.order, order_in_layer))
2389 });
2390
2391 let mut debug_text = "Widgets in order:\n".to_owned();
2392 for id in contains_pointer {
2393 let mut widget_text = format!("{id:?}");
2394 if let Some(rect) = widget_rects.get(id) {
2395 widget_text +=
2396 &format!(" {:?} {:?} {:?}", rect.layer_id, rect.rect, rect.sense);
2397 }
2398 if let Some(info) = widget_rects.info(id) {
2399 widget_text += &format!(" {info:?}");
2400 }
2401 debug_text += &format!("{widget_text}\n");
2402 }
2403 self.debug_text(debug_text);
2404 }
2405 if true {
2406 for widget in hovered {
2407 paint_widget_id(widget, "hovered", Color32::WHITE);
2408 }
2409 }
2410 if let Some(widget) = clicked {
2411 paint_widget_id(widget, "clicked", Color32::RED);
2412 }
2413 if let Some(widget) = dragged {
2414 paint_widget_id(widget, "dragged", Color32::GREEN);
2415 }
2416 }
2417 }
2418
2419 if self.style().debug.show_widget_hits {
2420 let hits = self.write(|ctx| ctx.viewport().hits.clone());
2421 let WidgetHits {
2422 close,
2423 contains_pointer,
2424 click,
2425 drag,
2426 } = hits;
2427
2428 if false {
2429 for widget in &close {
2430 paint_widget(widget, "close", Color32::from_gray(70));
2431 }
2432 }
2433 if true {
2434 for widget in &contains_pointer {
2435 paint_widget(widget, "contains_pointer", Color32::BLUE);
2436 }
2437 }
2438 if let Some(widget) = &click {
2439 paint_widget(widget, "click", Color32::RED);
2440 }
2441 if let Some(widget) = &drag {
2442 paint_widget(widget, "drag", Color32::GREEN);
2443 }
2444 }
2445
2446 if let Some(debug_rect) = self.pass_state_mut(|fs| fs.debug_rect.take()) {
2447 debug_rect.paint(&self.debug_painter());
2448 }
2449
2450 let num_multipass_in_row = self.viewport(|vp| vp.num_multipass_in_row);
2451 if 3 <= num_multipass_in_row {
2452 let mut warning = format!(
2456 "egui PERF WARNING: request_discard has been called {num_multipass_in_row} frames in a row"
2457 );
2458 self.viewport(|vp| {
2459 for reason in &vp.output.request_discard_reasons {
2460 warning += &format!("\n {reason}");
2461 }
2462 });
2463
2464 self.debug_painter()
2465 .debug_text(Pos2::ZERO, Align2::LEFT_TOP, Color32::RED, warning);
2466 }
2467 }
2468}
2469
2470impl ContextImpl {
2471 fn end_pass(&mut self) -> FullOutput {
2472 let ended_viewport_id = self.viewport_id();
2473 let viewport = self.viewports.entry(ended_viewport_id).or_default();
2474 let pixels_per_point = viewport.input.pixels_per_point;
2475
2476 self.loaders.end_pass(viewport.repaint.cumulative_pass_nr);
2477
2478 viewport.repaint.cumulative_pass_nr += 1;
2479
2480 self.memory.end_pass(&viewport.this_pass.used_ids);
2481
2482 if let Some(fonts) = self.fonts.as_mut() {
2483 let tex_mngr = &mut self.tex_manager.0.write();
2484 if let Some(font_image_delta) = fonts.font_image_delta() {
2485 tex_mngr.set(TextureId::default(), font_image_delta);
2487 }
2488 }
2489
2490 let textures_delta = self.tex_manager.0.write().take_delta();
2492
2493 let mut platform_output: PlatformOutput = std::mem::take(&mut viewport.output);
2494
2495 #[cfg(feature = "accesskit")]
2496 {
2497 profiling::scope!("accesskit");
2498 let state = viewport.this_pass.accesskit_state.take();
2499 if let Some(state) = state {
2500 let root_id = crate::accesskit_root_id().accesskit_id();
2501 let nodes = {
2502 state
2503 .nodes
2504 .into_iter()
2505 .map(|(id, node)| (id.accesskit_id(), node))
2506 .collect()
2507 };
2508 let focus_id = self
2509 .memory
2510 .focused()
2511 .map_or(root_id, |id| id.accesskit_id());
2512 platform_output.accesskit_update = Some(accesskit::TreeUpdate {
2513 nodes,
2514 tree: Some(accesskit::Tree::new(root_id)),
2515 focus: focus_id,
2516 });
2517 }
2518 }
2519
2520 let shapes = viewport
2521 .graphics
2522 .drain(self.memory.areas().order(), &self.memory.to_global);
2523
2524 let mut repaint_needed = false;
2525
2526 if self.memory.options.repaint_on_widget_change {
2527 profiling::scope!("compare-widget-rects");
2528 #[allow(clippy::allow_attributes, clippy::collapsible_if)] if viewport.prev_pass.widgets != viewport.this_pass.widgets {
2530 repaint_needed = true; }
2532 }
2533
2534 std::mem::swap(&mut viewport.prev_pass, &mut viewport.this_pass);
2535
2536 if repaint_needed {
2537 self.request_repaint(ended_viewport_id, RepaintCause::new());
2538 }
2539 let all_viewport_ids = self.all_viewport_ids();
2542
2543 self.last_viewport = ended_viewport_id;
2544
2545 self.viewports.retain(|&id, viewport| {
2546 if id == ViewportId::ROOT {
2547 return true; }
2549
2550 let parent = *self.viewport_parents.entry(id).or_default();
2551
2552 if !all_viewport_ids.contains(&parent) {
2553 log::debug!(
2554 "Removing viewport {:?} ({:?}): the parent is gone",
2555 id,
2556 viewport.builder.title
2557 );
2558
2559 return false;
2560 }
2561
2562 let is_our_child = parent == ended_viewport_id && id != ViewportId::ROOT;
2563 if is_our_child {
2564 if !viewport.used {
2565 log::debug!(
2566 "Removing viewport {:?} ({:?}): it was never used this pass",
2567 id,
2568 viewport.builder.title
2569 );
2570
2571 return false; }
2573
2574 viewport.used = false; }
2576
2577 true
2578 });
2579
2580 self.viewport_stack.pop();
2582
2583 let is_last = self.viewport_stack.is_empty();
2586
2587 let viewport_output = self
2588 .viewports
2589 .iter_mut()
2590 .map(|(&id, viewport)| {
2591 let parent = *self.viewport_parents.entry(id).or_default();
2592 let commands = if is_last {
2593 std::mem::take(&mut viewport.commands)
2597 } else {
2598 vec![]
2599 };
2600
2601 (
2602 id,
2603 ViewportOutput {
2604 parent,
2605 class: viewport.class,
2606 builder: viewport.builder.clone(),
2607 viewport_ui_cb: viewport.viewport_ui_cb.clone(),
2608 commands,
2609 repaint_delay: viewport.repaint.repaint_delay,
2610 },
2611 )
2612 })
2613 .collect();
2614
2615 if is_last {
2616 self.viewports.retain(|id, _| all_viewport_ids.contains(id));
2618 debug_assert!(
2619 self.viewports.contains_key(&ViewportId::ROOT),
2620 "Bug in egui: we removed the root viewport"
2621 );
2622 self.viewport_parents
2623 .retain(|id, _| all_viewport_ids.contains(id));
2624 } else {
2625 let viewport_id = self.viewport_id();
2626 self.memory.set_viewport_id(viewport_id);
2627 }
2628
2629 platform_output.num_completed_passes += 1;
2630
2631 FullOutput {
2632 platform_output,
2633 textures_delta,
2634 shapes,
2635 pixels_per_point,
2636 viewport_output,
2637 }
2638 }
2639}
2640
2641impl Context {
2642 pub fn tessellate(
2648 &self,
2649 shapes: Vec<ClippedShape>,
2650 pixels_per_point: f32,
2651 ) -> Vec<ClippedPrimitive> {
2652 profiling::function_scope!();
2653
2654 self.write(|ctx| {
2659 let tessellation_options = ctx.memory.options.tessellation_options;
2660 let texture_atlas = if let Some(fonts) = ctx.fonts.as_ref() {
2661 fonts.texture_atlas()
2662 } else {
2663 log::warn!("No font size matching {pixels_per_point} pixels per point found.");
2664 ctx.fonts
2665 .iter()
2666 .next()
2667 .expect("No fonts loaded")
2668 .texture_atlas()
2669 };
2670
2671 let paint_stats = PaintStats::from_shapes(&shapes);
2672 let clipped_primitives = {
2673 profiling::scope!("tessellator::tessellate_shapes");
2674 tessellator::Tessellator::new(
2675 pixels_per_point,
2676 tessellation_options,
2677 texture_atlas.size(),
2678 texture_atlas.prepared_discs(),
2679 )
2680 .tessellate_shapes(shapes)
2681 };
2682 ctx.paint_stats = paint_stats.with_clipped_primitives(&clipped_primitives);
2683 clipped_primitives
2684 })
2685 }
2686
2687 pub fn content_rect(&self) -> Rect {
2696 self.input(|i| i.content_rect()).round_ui()
2697 }
2698
2699 pub fn viewport_rect(&self) -> Rect {
2710 self.input(|i| i.viewport_rect()).round_ui()
2711 }
2712
2713 #[deprecated(
2715 note = "screen_rect has been split into viewport_rect() and content_rect(). You likely should use content_rect()"
2716 )]
2717 pub fn screen_rect(&self) -> Rect {
2718 self.input(|i| i.content_rect()).round_ui()
2719 }
2720
2721 pub fn available_rect(&self) -> Rect {
2723 self.pass_state(|s| s.available_rect()).round_ui()
2724 }
2725
2726 pub fn used_rect(&self) -> Rect {
2728 self.write(|ctx| {
2729 let mut used = ctx.viewport().this_pass.used_by_panels;
2730 for (_id, window) in ctx.memory.areas().visible_windows() {
2731 used |= window.rect();
2732 }
2733 used.round_ui()
2734 })
2735 }
2736
2737 pub fn used_size(&self) -> Vec2 {
2741 (self.used_rect().max - Pos2::ZERO).round_ui()
2742 }
2743
2744 pub fn is_pointer_over_area(&self) -> bool {
2748 let pointer_pos = self.input(|i| i.pointer.interact_pos());
2749 if let Some(pointer_pos) = pointer_pos {
2750 if let Some(layer) = self.layer_id_at(pointer_pos) {
2751 if layer.order == Order::Background {
2752 !self.pass_state(|state| state.unused_rect.contains(pointer_pos))
2753 } else {
2754 true
2755 }
2756 } else {
2757 false
2758 }
2759 } else {
2760 false
2761 }
2762 }
2763
2764 pub fn wants_pointer_input(&self) -> bool {
2771 self.is_using_pointer()
2772 || (self.is_pointer_over_area() && !self.input(|i| i.pointer.any_down()))
2773 }
2774
2775 pub fn is_using_pointer(&self) -> bool {
2779 self.memory(|m| m.interaction().is_using_pointer())
2780 }
2781
2782 pub fn wants_keyboard_input(&self) -> bool {
2784 self.memory(|m| m.focused().is_some())
2785 }
2786
2787 pub fn highlight_widget(&self, id: Id) {
2794 self.pass_state_mut(|fs| fs.highlight_next_pass.insert(id));
2795 }
2796
2797 #[expect(deprecated)]
2801 #[deprecated = "Use `is_popup_open` instead"]
2802 pub fn is_context_menu_open(&self) -> bool {
2803 self.data(|d| {
2804 d.get_temp::<crate::menu::BarState>(crate::menu::CONTEXT_MENU_ID_STR.into())
2805 .is_some_and(|state| state.has_root())
2806 })
2807 }
2808
2809 pub fn is_popup_open(&self) -> bool {
2813 self.pass_state_mut(|fs| {
2814 fs.layers
2815 .values()
2816 .any(|layer| !layer.open_popups.is_empty())
2817 })
2818 }
2819}
2820
2821impl Context {
2823 #[inline(always)]
2827 pub fn pointer_latest_pos(&self) -> Option<Pos2> {
2828 self.input(|i| i.pointer.latest_pos())
2829 }
2830
2831 #[inline(always)]
2833 pub fn pointer_hover_pos(&self) -> Option<Pos2> {
2834 self.input(|i| i.pointer.hover_pos())
2835 }
2836
2837 #[inline(always)]
2843 pub fn pointer_interact_pos(&self) -> Option<Pos2> {
2844 self.input(|i| i.pointer.interact_pos())
2845 }
2846
2847 pub fn multi_touch(&self) -> Option<MultiTouchInfo> {
2849 self.input(|i| i.multi_touch())
2850 }
2851}
2852
2853impl Context {
2854 pub fn set_transform_layer(&self, layer_id: LayerId, transform: TSTransform) {
2866 self.memory_mut(|m| {
2867 if transform == TSTransform::IDENTITY {
2868 m.to_global.remove(&layer_id)
2869 } else {
2870 m.to_global.insert(layer_id, transform)
2871 }
2872 });
2873 }
2874
2875 pub fn layer_transform_to_global(&self, layer_id: LayerId) -> Option<TSTransform> {
2879 self.memory(|m| m.to_global.get(&layer_id).copied())
2880 }
2881
2882 pub fn layer_transform_from_global(&self, layer_id: LayerId) -> Option<TSTransform> {
2886 self.layer_transform_to_global(layer_id)
2887 .map(|t| t.inverse())
2888 }
2889
2890 pub fn transform_layer_shapes(&self, layer_id: LayerId, transform: TSTransform) {
2898 if transform != TSTransform::IDENTITY {
2899 self.graphics_mut(|g| g.entry(layer_id).transform(transform));
2900 }
2901 }
2902
2903 pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
2905 self.memory(|mem| mem.layer_id_at(pos))
2906 }
2907
2908 pub fn move_to_top(&self, layer_id: LayerId) {
2912 self.memory_mut(|mem| mem.areas_mut().move_to_top(layer_id));
2913 }
2914
2915 pub fn set_sublayer(&self, parent: LayerId, child: LayerId) {
2923 self.memory_mut(|mem| mem.areas_mut().set_sublayer(parent, child));
2924 }
2925
2926 pub fn top_layer_id(&self) -> Option<LayerId> {
2928 self.memory(|mem| mem.areas().top_layer_id(Order::Middle))
2929 }
2930
2931 pub fn rect_contains_pointer(&self, layer_id: LayerId, rect: Rect) -> bool {
2939 let rect = if let Some(to_global) = self.layer_transform_to_global(layer_id) {
2940 to_global * rect
2941 } else {
2942 rect
2943 };
2944 if !rect.is_positive() {
2945 return false;
2946 }
2947
2948 let pointer_pos = self.input(|i| i.pointer.interact_pos());
2949 let Some(pointer_pos) = pointer_pos else {
2950 return false;
2951 };
2952
2953 if !rect.contains(pointer_pos) {
2954 return false;
2955 }
2956
2957 if self.layer_id_at(pointer_pos) != Some(layer_id) {
2958 return false;
2959 }
2960
2961 true
2962 }
2963
2964 #[cfg(debug_assertions)]
2968 pub fn debug_on_hover(&self) -> bool {
2969 self.options(|opt| opt.style().debug.debug_on_hover)
2970 }
2971
2972 #[cfg(debug_assertions)]
2974 pub fn set_debug_on_hover(&self, debug_on_hover: bool) {
2975 self.all_styles_mut(|style| style.debug.debug_on_hover = debug_on_hover);
2976 }
2977}
2978
2979impl Context {
2981 #[track_caller] pub fn animate_bool(&self, id: Id, value: bool) -> f32 {
2992 let animation_time = self.style().animation_time;
2993 self.animate_bool_with_time_and_easing(id, value, animation_time, emath::easing::linear)
2994 }
2995
2996 #[track_caller] pub fn animate_bool_responsive(&self, id: Id, value: bool) -> f32 {
3002 self.animate_bool_with_easing(id, value, emath::easing::cubic_out)
3003 }
3004
3005 #[track_caller] pub fn animate_bool_with_easing(&self, id: Id, value: bool, easing: fn(f32) -> f32) -> f32 {
3008 let animation_time = self.style().animation_time;
3009 self.animate_bool_with_time_and_easing(id, value, animation_time, easing)
3010 }
3011
3012 #[track_caller] pub fn animate_bool_with_time(&self, id: Id, target_value: bool, animation_time: f32) -> f32 {
3015 self.animate_bool_with_time_and_easing(
3016 id,
3017 target_value,
3018 animation_time,
3019 emath::easing::linear,
3020 )
3021 }
3022
3023 #[track_caller] pub fn animate_bool_with_time_and_easing(
3032 &self,
3033 id: Id,
3034 target_value: bool,
3035 animation_time: f32,
3036 easing: fn(f32) -> f32,
3037 ) -> f32 {
3038 let animated_value = self.write(|ctx| {
3039 ctx.animation_manager.animate_bool(
3040 &ctx.viewports.entry(ctx.viewport_id()).or_default().input,
3041 animation_time,
3042 id,
3043 target_value,
3044 )
3045 });
3046
3047 let animation_in_progress = 0.0 < animated_value && animated_value < 1.0;
3048 if animation_in_progress {
3049 self.request_repaint();
3050 }
3051
3052 if target_value {
3053 easing(animated_value)
3054 } else {
3055 1.0 - easing(1.0 - animated_value)
3056 }
3057 }
3058
3059 #[track_caller] pub fn animate_value_with_time(&self, id: Id, target_value: f32, animation_time: f32) -> f32 {
3065 let animated_value = self.write(|ctx| {
3066 ctx.animation_manager.animate_value(
3067 &ctx.viewports.entry(ctx.viewport_id()).or_default().input,
3068 animation_time,
3069 id,
3070 target_value,
3071 )
3072 });
3073 let animation_in_progress = animated_value != target_value;
3074 if animation_in_progress {
3075 self.request_repaint();
3076 }
3077
3078 animated_value
3079 }
3080
3081 pub fn clear_animations(&self) {
3083 self.write(|ctx| ctx.animation_manager = Default::default());
3084 }
3085}
3086
3087impl Context {
3088 pub fn settings_ui(&self, ui: &mut Ui) {
3090 let prev_options = self.options(|o| o.clone());
3091 let mut options = prev_options.clone();
3092
3093 ui.collapsing("🔠 Font tweak", |ui| {
3094 self.fonts_tweak_ui(ui);
3095 });
3096
3097 options.ui(ui);
3098
3099 if options != prev_options {
3100 self.options_mut(move |o| *o = options);
3101 }
3102 }
3103
3104 fn fonts_tweak_ui(&self, ui: &mut Ui) {
3105 let mut font_definitions = self.write(|ctx| ctx.font_definitions.clone());
3106 let mut changed = false;
3107
3108 for (name, data) in &mut font_definitions.font_data {
3109 ui.collapsing(name, |ui| {
3110 let mut tweak = data.tweak;
3111 if tweak.ui(ui).changed() {
3112 Arc::make_mut(data).tweak = tweak;
3113 changed = true;
3114 }
3115 });
3116 }
3117
3118 if changed {
3119 self.set_fonts(font_definitions);
3120 }
3121 }
3122
3123 pub fn inspection_ui(&self, ui: &mut Ui) {
3125 use crate::containers::CollapsingHeader;
3126
3127 crate::Grid::new("egui-inspection-grid")
3128 .num_columns(2)
3129 .striped(true)
3130 .show(ui, |ui| {
3131 ui.label("Total ui frames:");
3132 ui.monospace(ui.ctx().cumulative_frame_nr().to_string());
3133 ui.end_row();
3134
3135 ui.label("Total ui passes:");
3136 ui.monospace(ui.ctx().cumulative_pass_nr().to_string());
3137 ui.end_row();
3138
3139 ui.label("Is using pointer")
3140 .on_hover_text("Is egui currently using the pointer actively (e.g. dragging a slider)?");
3141 ui.monospace(self.is_using_pointer().to_string());
3142 ui.end_row();
3143
3144 ui.label("Wants pointer input")
3145 .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).");
3146 ui.monospace(self.wants_pointer_input().to_string());
3147 ui.end_row();
3148
3149 ui.label("Wants keyboard input").on_hover_text("Is egui currently listening for text input?");
3150 ui.monospace(self.wants_keyboard_input().to_string());
3151 ui.end_row();
3152
3153 ui.label("Keyboard focus widget").on_hover_text("Is egui currently listening for text input?");
3154 ui.monospace(self.memory(|m| m.focused())
3155 .as_ref()
3156 .map(Id::short_debug_format)
3157 .unwrap_or_default());
3158 ui.end_row();
3159
3160 let pointer_pos = self
3161 .pointer_hover_pos()
3162 .map_or_else(String::new, |pos| format!("{pos:?}"));
3163 ui.label("Pointer pos");
3164 ui.monospace(pointer_pos);
3165 ui.end_row();
3166
3167 let top_layer = self
3168 .pointer_hover_pos()
3169 .and_then(|pos| self.layer_id_at(pos))
3170 .map_or_else(String::new, |layer| layer.short_debug_format());
3171 ui.label("Top layer under mouse");
3172 ui.monospace(top_layer);
3173 ui.end_row();
3174 });
3175
3176 ui.add_space(16.0);
3177
3178 ui.label(format!(
3179 "There are {} text galleys in the layout cache",
3180 self.fonts(|f| f.num_galleys_in_cache())
3181 ))
3182 .on_hover_text("This is approximately the number of text strings on screen");
3183 ui.add_space(16.0);
3184
3185 CollapsingHeader::new("🔃 Repaint Causes")
3186 .default_open(false)
3187 .show(ui, |ui| {
3188 ui.set_min_height(120.0);
3189 ui.label("What caused egui to repaint:");
3190 ui.add_space(8.0);
3191 let causes = ui.ctx().repaint_causes();
3192 for cause in causes {
3193 ui.label(cause.to_string());
3194 }
3195 });
3196
3197 CollapsingHeader::new("📥 Input")
3198 .default_open(false)
3199 .show(ui, |ui| {
3200 let input = ui.input(|i| i.clone());
3201 input.ui(ui);
3202 });
3203
3204 CollapsingHeader::new("📊 Paint stats")
3205 .default_open(false)
3206 .show(ui, |ui| {
3207 let paint_stats = self.read(|ctx| ctx.paint_stats);
3208 paint_stats.ui(ui);
3209 });
3210
3211 CollapsingHeader::new("🖼 Textures")
3212 .default_open(false)
3213 .show(ui, |ui| {
3214 self.texture_ui(ui);
3215 });
3216
3217 CollapsingHeader::new("🖼 Image loaders")
3218 .default_open(false)
3219 .show(ui, |ui| {
3220 self.loaders_ui(ui);
3221 });
3222
3223 CollapsingHeader::new("🔠 Font texture")
3224 .default_open(false)
3225 .show(ui, |ui| {
3226 let font_image_size = self.fonts(|f| f.font_image_size());
3227 crate::introspection::font_texture_ui(ui, font_image_size);
3228 });
3229
3230 CollapsingHeader::new("Label text selection state")
3231 .default_open(false)
3232 .show(ui, |ui| {
3233 ui.label(format!(
3234 "{:#?}",
3235 *ui.ctx()
3236 .plugin::<crate::text_selection::LabelSelectionState>()
3237 .lock()
3238 ));
3239 });
3240
3241 CollapsingHeader::new("Interaction")
3242 .default_open(false)
3243 .show(ui, |ui| {
3244 let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());
3245 interact_widgets.ui(ui);
3246 });
3247 }
3248
3249 pub fn texture_ui(&self, ui: &mut crate::Ui) {
3251 let tex_mngr = self.tex_manager();
3252 let tex_mngr = tex_mngr.read();
3253
3254 let mut textures: Vec<_> = tex_mngr.allocated().collect();
3255 textures.sort_by_key(|(id, _)| *id);
3256
3257 let mut bytes = 0;
3258 for (_, tex) in &textures {
3259 bytes += tex.bytes_used();
3260 }
3261
3262 ui.label(format!(
3263 "{} allocated texture(s), using {:.1} MB",
3264 textures.len(),
3265 bytes as f64 * 1e-6
3266 ));
3267 let max_preview_size = vec2(48.0, 32.0);
3268
3269 let pixels_per_point = self.pixels_per_point();
3270
3271 ui.group(|ui| {
3272 ScrollArea::vertical()
3273 .max_height(300.0)
3274 .auto_shrink([false, true])
3275 .show(ui, |ui| {
3276 ui.style_mut().override_text_style = Some(TextStyle::Monospace);
3277 Grid::new("textures")
3278 .striped(true)
3279 .num_columns(4)
3280 .spacing(vec2(16.0, 2.0))
3281 .min_row_height(max_preview_size.y)
3282 .show(ui, |ui| {
3283 for (&texture_id, meta) in textures {
3284 let [w, h] = meta.size;
3285 let point_size = vec2(w as f32, h as f32) / pixels_per_point;
3286
3287 let mut size = point_size;
3288 size *= (max_preview_size.x / size.x).min(1.0);
3289 size *= (max_preview_size.y / size.y).min(1.0);
3290 ui.image(SizedTexture::new(texture_id, size))
3291 .on_hover_ui(|ui| {
3292 let max_size = 0.5 * ui.ctx().content_rect().size();
3294 let mut size = point_size;
3295 size *= max_size.x / size.x.max(max_size.x);
3296 size *= max_size.y / size.y.max(max_size.y);
3297 ui.image(SizedTexture::new(texture_id, size));
3298 });
3299
3300 ui.label(format!("{w} x {h}"));
3301 ui.label(format!("{:.3} MB", meta.bytes_used() as f64 * 1e-6));
3302 ui.label(format!("{:?}", meta.name));
3303 ui.end_row();
3304 }
3305 });
3306 });
3307 });
3308 }
3309
3310 pub fn loaders_ui(&self, ui: &mut crate::Ui) {
3312 struct LoaderInfo {
3313 id: String,
3314 byte_size: usize,
3315 }
3316
3317 let mut byte_loaders = vec![];
3318 let mut image_loaders = vec![];
3319 let mut texture_loaders = vec![];
3320
3321 {
3322 let loaders = self.loaders();
3323 let Loaders {
3324 include: _,
3325 bytes,
3326 image,
3327 texture,
3328 } = loaders.as_ref();
3329
3330 for loader in bytes.lock().iter() {
3331 byte_loaders.push(LoaderInfo {
3332 id: loader.id().to_owned(),
3333 byte_size: loader.byte_size(),
3334 });
3335 }
3336 for loader in image.lock().iter() {
3337 image_loaders.push(LoaderInfo {
3338 id: loader.id().to_owned(),
3339 byte_size: loader.byte_size(),
3340 });
3341 }
3342 for loader in texture.lock().iter() {
3343 texture_loaders.push(LoaderInfo {
3344 id: loader.id().to_owned(),
3345 byte_size: loader.byte_size(),
3346 });
3347 }
3348 }
3349
3350 fn loaders_ui(ui: &mut crate::Ui, title: &str, loaders: &[LoaderInfo]) {
3351 let heading = format!("{} {title} loaders", loaders.len());
3352 crate::CollapsingHeader::new(heading)
3353 .default_open(true)
3354 .show(ui, |ui| {
3355 Grid::new("loaders")
3356 .striped(true)
3357 .num_columns(2)
3358 .show(ui, |ui| {
3359 ui.label("ID");
3360 ui.label("Size");
3361 ui.end_row();
3362
3363 for loader in loaders {
3364 ui.label(&loader.id);
3365 ui.label(format!("{:.3} MB", loader.byte_size as f64 * 1e-6));
3366 ui.end_row();
3367 }
3368 });
3369 });
3370 }
3371
3372 loaders_ui(ui, "byte", &byte_loaders);
3373 loaders_ui(ui, "image", &image_loaders);
3374 loaders_ui(ui, "texture", &texture_loaders);
3375 }
3376
3377 pub fn memory_ui(&self, ui: &mut crate::Ui) {
3379 if ui
3380 .button("Reset all")
3381 .on_hover_text("Reset all egui state")
3382 .clicked()
3383 {
3384 self.memory_mut(|mem| *mem = Default::default());
3385 }
3386
3387 let (num_state, num_serialized) = self.data(|d| (d.len(), d.count_serialized()));
3388 ui.label(format!(
3389 "{num_state} widget states stored (of which {num_serialized} are serialized)."
3390 ));
3391
3392 ui.horizontal(|ui| {
3393 ui.label(format!(
3394 "{} areas (panels, windows, popups, …)",
3395 self.memory(|mem| mem.areas().count())
3396 ));
3397 if ui.button("Reset").clicked() {
3398 self.memory_mut(|mem| *mem.areas_mut() = Default::default());
3399 }
3400 });
3401 ui.indent("layers", |ui| {
3402 ui.label("Layers, ordered back to front.");
3403 let layers_ids: Vec<LayerId> = self.memory(|mem| mem.areas().order().to_vec());
3404 for layer_id in layers_ids {
3405 if let Some(area) = AreaState::load(self, layer_id.id) {
3406 let is_visible = self.memory(|mem| mem.areas().is_visible(&layer_id));
3407 if !is_visible {
3408 continue;
3409 }
3410 let text = format!("{} - {:?}", layer_id.short_debug_format(), area.rect(),);
3411 let response =
3413 ui.add(Label::new(RichText::new(text).monospace()).sense(Sense::click()));
3414 if response.hovered() && is_visible {
3415 ui.ctx()
3416 .debug_painter()
3417 .debug_rect(area.rect(), Color32::RED, "");
3418 }
3419 } else {
3420 ui.monospace(layer_id.short_debug_format());
3421 }
3422 }
3423 });
3424
3425 ui.horizontal(|ui| {
3426 ui.label(format!(
3427 "{} collapsing headers",
3428 self.data(|d| d.count::<containers::collapsing_header::InnerState>())
3429 ));
3430 if ui.button("Reset").clicked() {
3431 self.data_mut(|d| d.remove_by_type::<containers::collapsing_header::InnerState>());
3432 }
3433 });
3434
3435 #[expect(deprecated)]
3436 ui.horizontal(|ui| {
3437 ui.label(format!(
3438 "{} menu bars",
3439 self.data(|d| d.count::<crate::menu::BarState>())
3440 ));
3441 if ui.button("Reset").clicked() {
3442 self.data_mut(|d| d.remove_by_type::<crate::menu::BarState>());
3443 }
3444 });
3445
3446 ui.horizontal(|ui| {
3447 ui.label(format!(
3448 "{} scroll areas",
3449 self.data(|d| d.count::<scroll_area::State>())
3450 ));
3451 if ui.button("Reset").clicked() {
3452 self.data_mut(|d| d.remove_by_type::<scroll_area::State>());
3453 }
3454 });
3455
3456 ui.horizontal(|ui| {
3457 ui.label(format!(
3458 "{} resize areas",
3459 self.data(|d| d.count::<resize::State>())
3460 ));
3461 if ui.button("Reset").clicked() {
3462 self.data_mut(|d| d.remove_by_type::<resize::State>());
3463 }
3464 });
3465
3466 ui.shrink_width_to_current(); ui.label("NOTE: the position of this window cannot be reset from within itself.");
3468
3469 ui.collapsing("Interaction", |ui| {
3470 let interaction = self.memory(|mem| mem.interaction().clone());
3471 interaction.ui(ui);
3472 });
3473 }
3474}
3475
3476impl Context {
3477 pub fn style_ui(&self, ui: &mut Ui, theme: Theme) {
3479 let mut style: Style = (*self.style_of(theme)).clone();
3480 style.ui(ui);
3481 self.set_style_of(theme, style);
3482 }
3483}
3484
3485impl Context {
3487 #[cfg(feature = "accesskit")]
3497 pub fn accesskit_node_builder<R>(
3498 &self,
3499 id: Id,
3500 writer: impl FnOnce(&mut accesskit::Node) -> R,
3501 ) -> Option<R> {
3502 self.write(|ctx| {
3503 ctx.viewport()
3504 .this_pass
3505 .accesskit_state
3506 .is_some()
3507 .then(|| ctx.accesskit_node_builder(id))
3508 .map(writer)
3509 })
3510 }
3511
3512 #[cfg(feature = "accesskit")]
3513 pub(crate) fn register_accesskit_parent(&self, id: Id, parent_id: Id) {
3514 self.write(|ctx| {
3515 if let Some(state) = ctx.viewport().this_pass.accesskit_state.as_mut() {
3516 state.parent_map.insert(id, parent_id);
3517 }
3518 });
3519 }
3520
3521 #[cfg(feature = "accesskit")]
3523 pub fn enable_accesskit(&self) {
3524 self.write(|ctx| ctx.is_accesskit_enabled = true);
3525 }
3526
3527 #[cfg(feature = "accesskit")]
3529 pub fn disable_accesskit(&self) {
3530 self.write(|ctx| ctx.is_accesskit_enabled = false);
3531 }
3532}
3533
3534impl Context {
3536 pub fn include_bytes(&self, uri: impl Into<Cow<'static, str>>, bytes: impl Into<Bytes>) {
3543 self.loaders().include.insert(uri, bytes);
3544 }
3545
3546 pub fn is_loader_installed(&self, id: &str) -> bool {
3549 let loaders = self.loaders();
3550
3551 loaders.bytes.lock().iter().any(|l| l.id() == id)
3552 || loaders.image.lock().iter().any(|l| l.id() == id)
3553 || loaders.texture.lock().iter().any(|l| l.id() == id)
3554 }
3555
3556 pub fn add_bytes_loader(&self, loader: Arc<dyn load::BytesLoader + Send + Sync + 'static>) {
3562 self.loaders().bytes.lock().push(loader);
3563 }
3564
3565 pub fn add_image_loader(&self, loader: Arc<dyn load::ImageLoader + Send + Sync + 'static>) {
3571 self.loaders().image.lock().push(loader);
3572 }
3573
3574 pub fn add_texture_loader(&self, loader: Arc<dyn load::TextureLoader + Send + Sync + 'static>) {
3580 self.loaders().texture.lock().push(loader);
3581 }
3582
3583 pub fn forget_image(&self, uri: &str) {
3588 use load::BytesLoader as _;
3589
3590 profiling::function_scope!();
3591
3592 let loaders = self.loaders();
3593
3594 loaders.include.forget(uri);
3595 for loader in loaders.bytes.lock().iter() {
3596 loader.forget(uri);
3597 }
3598 for loader in loaders.image.lock().iter() {
3599 loader.forget(uri);
3600 }
3601 for loader in loaders.texture.lock().iter() {
3602 loader.forget(uri);
3603 }
3604 }
3605
3606 pub fn forget_all_images(&self) {
3610 use load::BytesLoader as _;
3611
3612 profiling::function_scope!();
3613
3614 let loaders = self.loaders();
3615
3616 loaders.include.forget_all();
3617 for loader in loaders.bytes.lock().iter() {
3618 loader.forget_all();
3619 }
3620 for loader in loaders.image.lock().iter() {
3621 loader.forget_all();
3622 }
3623 for loader in loaders.texture.lock().iter() {
3624 loader.forget_all();
3625 }
3626 }
3627
3628 pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult {
3647 profiling::function_scope!(uri);
3648
3649 let loaders = self.loaders();
3650 let bytes_loaders = loaders.bytes.lock();
3651
3652 for loader in bytes_loaders.iter().rev() {
3654 let result = loader.load(self, uri);
3655 match result {
3656 Err(load::LoadError::NotSupported) => {}
3657 _ => return result,
3658 }
3659 }
3660
3661 Err(load::LoadError::NoMatchingBytesLoader)
3662 }
3663
3664 pub fn try_load_image(&self, uri: &str, size_hint: load::SizeHint) -> load::ImageLoadResult {
3685 profiling::function_scope!(uri);
3686
3687 let loaders = self.loaders();
3688 let image_loaders = loaders.image.lock();
3689 if image_loaders.is_empty() {
3690 return Err(load::LoadError::NoImageLoaders);
3691 }
3692
3693 let mut format = None;
3694
3695 for loader in image_loaders.iter().rev() {
3697 match loader.load(self, uri, size_hint) {
3698 Err(load::LoadError::NotSupported) => {}
3699 Err(load::LoadError::FormatNotSupported { detected_format }) => {
3700 format = format.or(detected_format);
3701 }
3702 result => return result,
3703 }
3704 }
3705
3706 Err(load::LoadError::NoMatchingImageLoader {
3707 detected_format: format,
3708 })
3709 }
3710
3711 pub fn try_load_texture(
3730 &self,
3731 uri: &str,
3732 texture_options: TextureOptions,
3733 size_hint: load::SizeHint,
3734 ) -> load::TextureLoadResult {
3735 profiling::function_scope!(uri);
3736
3737 let loaders = self.loaders();
3738 let texture_loaders = loaders.texture.lock();
3739
3740 for loader in texture_loaders.iter().rev() {
3742 match loader.load(self, uri, texture_options, size_hint) {
3743 Err(load::LoadError::NotSupported) => {}
3744 result => return result,
3745 }
3746 }
3747
3748 Err(load::LoadError::NoMatchingTextureLoader)
3749 }
3750
3751 pub fn loaders(&self) -> Arc<Loaders> {
3753 self.read(|this| this.loaders.clone())
3754 }
3755
3756 pub fn has_pending_images(&self) -> bool {
3758 self.read(|this| {
3759 this.loaders.image.lock().iter().any(|i| i.has_pending())
3760 || this.loaders.bytes.lock().iter().any(|i| i.has_pending())
3761 })
3762 }
3763}
3764
3765impl Context {
3767 pub fn viewport_id(&self) -> ViewportId {
3773 self.read(|ctx| ctx.viewport_id())
3774 }
3775
3776 pub fn parent_viewport_id(&self) -> ViewportId {
3782 self.read(|ctx| ctx.parent_viewport_id())
3783 }
3784
3785 pub fn viewport<R>(&self, reader: impl FnOnce(&ViewportState) -> R) -> R {
3787 self.write(|ctx| reader(ctx.viewport()))
3788 }
3789
3790 pub fn viewport_for<R>(
3792 &self,
3793 viewport_id: ViewportId,
3794 reader: impl FnOnce(&ViewportState) -> R,
3795 ) -> R {
3796 self.write(|ctx| reader(ctx.viewport_for(viewport_id)))
3797 }
3798
3799 pub fn set_immediate_viewport_renderer(
3812 callback: impl for<'a> Fn(&Self, ImmediateViewport<'a>) + 'static,
3813 ) {
3814 let callback = Box::new(callback);
3815 IMMEDIATE_VIEWPORT_RENDERER.with(|render_sync| {
3816 render_sync.replace(Some(callback));
3817 });
3818 }
3819
3820 pub fn embed_viewports(&self) -> bool {
3825 self.read(|ctx| ctx.embed_viewports)
3826 }
3827
3828 pub fn set_embed_viewports(&self, value: bool) {
3833 self.write(|ctx| ctx.embed_viewports = value);
3834 }
3835
3836 pub fn send_viewport_cmd(&self, command: ViewportCommand) {
3840 self.send_viewport_cmd_to(self.viewport_id(), command);
3841 }
3842
3843 pub fn send_viewport_cmd_to(&self, id: ViewportId, command: ViewportCommand) {
3847 self.request_repaint_of(id);
3848
3849 if command.requires_parent_repaint() {
3850 self.request_repaint_of(self.parent_viewport_id());
3851 }
3852
3853 self.write(|ctx| ctx.viewport_for(id).commands.push(command));
3854 }
3855
3856 pub fn show_viewport_deferred(
3886 &self,
3887 new_viewport_id: ViewportId,
3888 viewport_builder: ViewportBuilder,
3889 viewport_ui_cb: impl Fn(&Self, ViewportClass) + Send + Sync + 'static,
3890 ) {
3891 profiling::function_scope!();
3892
3893 if self.embed_viewports() {
3894 viewport_ui_cb(self, ViewportClass::Embedded);
3895 } else {
3896 self.write(|ctx| {
3897 ctx.viewport_parents
3898 .insert(new_viewport_id, ctx.viewport_id());
3899
3900 let viewport = ctx.viewports.entry(new_viewport_id).or_default();
3901 viewport.class = ViewportClass::Deferred;
3902 viewport.builder = viewport_builder;
3903 viewport.used = true;
3904 viewport.viewport_ui_cb = Some(Arc::new(move |ctx| {
3905 (viewport_ui_cb)(ctx, ViewportClass::Deferred);
3906 }));
3907 });
3908 }
3909 }
3910
3911 pub fn show_viewport_immediate<T>(
3938 &self,
3939 new_viewport_id: ViewportId,
3940 builder: ViewportBuilder,
3941 mut viewport_ui_cb: impl FnMut(&Self, ViewportClass) -> T,
3942 ) -> T {
3943 profiling::function_scope!();
3944
3945 if self.embed_viewports() {
3946 return viewport_ui_cb(self, ViewportClass::Embedded);
3947 }
3948
3949 IMMEDIATE_VIEWPORT_RENDERER.with(|immediate_viewport_renderer| {
3950 let immediate_viewport_renderer = immediate_viewport_renderer.borrow();
3951 let Some(immediate_viewport_renderer) = immediate_viewport_renderer.as_ref() else {
3952 return viewport_ui_cb(self, ViewportClass::Embedded);
3954 };
3955
3956 let ids = self.write(|ctx| {
3957 let parent_viewport_id = ctx.viewport_id();
3958
3959 ctx.viewport_parents
3960 .insert(new_viewport_id, parent_viewport_id);
3961
3962 let viewport = ctx.viewports.entry(new_viewport_id).or_default();
3963 viewport.builder = builder.clone();
3964 viewport.used = true;
3965 viewport.viewport_ui_cb = None; ViewportIdPair::from_self_and_parent(new_viewport_id, parent_viewport_id)
3968 });
3969
3970 let mut out = None;
3971 {
3972 let out = &mut out;
3973
3974 let viewport = ImmediateViewport {
3975 ids,
3976 builder,
3977 viewport_ui_cb: Box::new(move |context| {
3978 *out = Some(viewport_ui_cb(context, ViewportClass::Immediate));
3979 }),
3980 };
3981
3982 immediate_viewport_renderer(self, viewport);
3983 }
3984
3985 out.expect(
3986 "egui backend is implemented incorrectly - the user callback was never called",
3987 )
3988 })
3989 }
3990}
3991
3992impl Context {
3994 pub fn interaction_snapshot<R>(&self, reader: impl FnOnce(&InteractionSnapshot) -> R) -> R {
3996 self.write(|w| reader(&w.viewport().interact_widgets))
3997 }
3998
3999 pub fn dragged_id(&self) -> Option<Id> {
4007 self.interaction_snapshot(|i| i.dragged)
4008 }
4009
4010 pub fn is_being_dragged(&self, id: Id) -> bool {
4017 self.dragged_id() == Some(id)
4018 }
4019
4020 pub fn drag_started_id(&self) -> Option<Id> {
4024 self.interaction_snapshot(|i| i.drag_started)
4025 }
4026
4027 pub fn drag_stopped_id(&self) -> Option<Id> {
4029 self.interaction_snapshot(|i| i.drag_stopped)
4030 }
4031
4032 pub fn set_dragged_id(&self, id: Id) {
4034 self.write(|ctx| {
4035 let vp = ctx.viewport();
4036 let i = &mut vp.interact_widgets;
4037 if i.dragged != Some(id) {
4038 i.drag_stopped = i.dragged.or(i.drag_stopped);
4039 i.dragged = Some(id);
4040 i.drag_started = Some(id);
4041 }
4042
4043 ctx.memory.interaction_mut().potential_drag_id = Some(id);
4044 });
4045 }
4046
4047 pub fn stop_dragging(&self) {
4049 self.write(|ctx| {
4050 let vp = ctx.viewport();
4051 let i = &mut vp.interact_widgets;
4052 if i.dragged.is_some() {
4053 i.drag_stopped = i.dragged;
4054 i.dragged = None;
4055 }
4056
4057 ctx.memory.interaction_mut().potential_drag_id = None;
4058 });
4059 }
4060
4061 #[inline(always)]
4065 pub fn dragging_something_else(&self, not_this: Id) -> bool {
4066 let dragged = self.dragged_id();
4067 dragged.is_some() && dragged != Some(not_this)
4068 }
4069}
4070
4071#[test]
4072fn context_impl_send_sync() {
4073 fn assert_send_sync<T: Send + Sync>() {}
4074 assert_send_sync::<Context>();
4075}
4076
4077#[cfg(test)]
4078mod test {
4079 use super::Context;
4080
4081 #[test]
4082 fn test_single_pass() {
4083 let ctx = Context::default();
4084 ctx.options_mut(|o| o.max_passes = 1.try_into().unwrap());
4085
4086 {
4088 let mut num_calls = 0;
4089 let output = ctx.run(Default::default(), |ctx| {
4090 num_calls += 1;
4091 assert_eq!(ctx.output(|o| o.num_completed_passes), 0);
4092 assert!(!ctx.output(|o| o.requested_discard()));
4093 assert!(!ctx.will_discard());
4094 });
4095 assert_eq!(num_calls, 1);
4096 assert_eq!(output.platform_output.num_completed_passes, 1);
4097 assert!(!output.platform_output.requested_discard());
4098 }
4099
4100 {
4102 let mut num_calls = 0;
4103 let output = ctx.run(Default::default(), |ctx| {
4104 num_calls += 1;
4105 ctx.request_discard("test");
4106 assert!(!ctx.will_discard(), "The request should have been denied");
4107 });
4108 assert_eq!(num_calls, 1);
4109 assert_eq!(output.platform_output.num_completed_passes, 1);
4110 assert!(
4111 output.platform_output.requested_discard(),
4112 "The request should be reported"
4113 );
4114 assert_eq!(
4115 output
4116 .platform_output
4117 .request_discard_reasons
4118 .first()
4119 .unwrap()
4120 .reason,
4121 "test"
4122 );
4123 }
4124 }
4125
4126 #[test]
4127 fn test_dual_pass() {
4128 let ctx = Context::default();
4129 ctx.options_mut(|o| o.max_passes = 2.try_into().unwrap());
4130
4131 {
4133 let mut num_calls = 0;
4134 let output = ctx.run(Default::default(), |ctx| {
4135 assert_eq!(ctx.output(|o| o.num_completed_passes), 0);
4136 assert!(!ctx.output(|o| o.requested_discard()));
4137 assert!(!ctx.will_discard());
4138 num_calls += 1;
4139 });
4140 assert_eq!(num_calls, 1);
4141 assert_eq!(output.platform_output.num_completed_passes, 1);
4142 assert!(!output.platform_output.requested_discard());
4143 }
4144
4145 {
4147 let mut num_calls = 0;
4148 let output = ctx.run(Default::default(), |ctx| {
4149 assert_eq!(ctx.output(|o| o.num_completed_passes), num_calls);
4150
4151 assert!(!ctx.will_discard());
4152 if num_calls == 0 {
4153 ctx.request_discard("test");
4154 assert!(ctx.will_discard());
4155 }
4156
4157 num_calls += 1;
4158 });
4159 assert_eq!(num_calls, 2);
4160 assert_eq!(output.platform_output.num_completed_passes, 2);
4161 assert!(
4162 !output.platform_output.requested_discard(),
4163 "The request should have been cleared when fulfilled"
4164 );
4165 }
4166
4167 {
4169 let mut num_calls = 0;
4170 let output = ctx.run(Default::default(), |ctx| {
4171 assert_eq!(ctx.output(|o| o.num_completed_passes), num_calls);
4172
4173 assert!(!ctx.will_discard());
4174 ctx.request_discard("test");
4175 if num_calls == 0 {
4176 assert!(ctx.will_discard(), "First request granted");
4177 } else {
4178 assert!(!ctx.will_discard(), "Second request should be denied");
4179 }
4180
4181 num_calls += 1;
4182 });
4183 assert_eq!(num_calls, 2);
4184 assert_eq!(output.platform_output.num_completed_passes, 2);
4185 assert!(
4186 output.platform_output.requested_discard(),
4187 "The unfulfilled request should be reported"
4188 );
4189 }
4190 }
4191
4192 #[test]
4193 fn test_multi_pass() {
4194 let ctx = Context::default();
4195 ctx.options_mut(|o| o.max_passes = 10.try_into().unwrap());
4196
4197 {
4199 let mut num_calls = 0;
4200 let output = ctx.run(Default::default(), |ctx| {
4201 assert_eq!(ctx.output(|o| o.num_completed_passes), num_calls);
4202
4203 assert!(!ctx.will_discard());
4204 if num_calls <= 2 {
4205 ctx.request_discard("test");
4206 assert!(ctx.will_discard());
4207 }
4208
4209 num_calls += 1;
4210 });
4211 assert_eq!(num_calls, 4);
4212 assert_eq!(output.platform_output.num_completed_passes, 4);
4213 assert!(
4214 !output.platform_output.requested_discard(),
4215 "The request should have been cleared when fulfilled"
4216 );
4217 }
4218 }
4219}