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