Skip to main content

egui/
context.rs

1#![warn(missing_docs)] // Let's keep `Context` well-documented.
2
3use 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/// Information given to the backend about when it is time to repaint the ui.
46///
47/// This is given in the callback set by [`Context::set_request_repaint_callback`].
48#[derive(Clone, Copy, Debug)]
49pub struct RequestRepaintInfo {
50    /// This is used to specify what viewport that should repaint.
51    pub viewport_id: ViewportId,
52
53    /// Repaint after this duration. If zero, repaint as soon as possible.
54    pub delay: Duration,
55
56    /// The number of fully completed passes, of the entire lifetime of the [`Context`].
57    ///
58    /// This can be compared to [`Context::cumulative_pass_nr`] to see if we we still
59    /// need another repaint (ui pass / frame), or if one has already happened.
60    pub current_cumulative_pass_nr: u64,
61}
62
63// ----------------------------------------------------------------------------
64
65thread_local! {
66    static IMMEDIATE_VIEWPORT_RENDERER: RefCell<Option<Box<ImmediateViewportRendererCallback>>> = Default::default();
67}
68
69// ----------------------------------------------------------------------------
70
71struct 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        // Will be filled in later
78        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
93// ----------------------------------------------------------------------------
94
95/// Repaint-logic
96impl ContextImpl {
97    /// This is where we update the repaint logic.
98    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            // We are repainting now, so we can wait a while for the next repaint.
111            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            // Each request results in two repaints, just to give some things time to settle.
139            // This solves some corner-cases of missing repaints on frame-delayed responses.
140            viewport.repaint.outstanding = 1;
141        } else {
142            // For non-zero delays, we only repaint once, because
143            // otherwise we would just schedule an immediate repaint _now_,
144            // which would then clear the delay and repaint again.
145            // Hovering a tooltip is a good example of a case where we want to repaint after a delay.
146        }
147
148        if let Ok(predicted_frame_time) = Duration::try_from_secs_f32(viewport.input.predicted_dt) {
149            // Make it less likely we over-shoot the target:
150            delay = delay.saturating_sub(predicted_frame_time);
151        }
152
153        viewport.repaint.causes.push(cause);
154
155        // We save some CPU time by only calling the callback if we need to.
156        // If the new delay is greater or equal to the previous lowest,
157        // it means we have already called the callback, and don't need to do it again.
158        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// ----------------------------------------------------------------------------
187
188/// State stored per viewport.
189///
190/// Mostly for internal use.
191/// Things here may move and change without warning.
192#[derive(Default)]
193pub struct ViewportState {
194    /// The type of viewport.
195    ///
196    /// This will never be [`ViewportClass::EmbeddedWindow`],
197    /// since those don't result in real viewports.
198    pub class: ViewportClass,
199
200    /// The latest delta
201    pub builder: ViewportBuilder,
202
203    /// The user-code that shows the GUI, used for deferred viewports.
204    ///
205    /// `None` for immediate viewports.
206    pub viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,
207
208    pub input: InputState,
209
210    /// State that is collected during a pass and then cleared.
211    pub this_pass: PassState,
212
213    /// The final [`PassState`] from last pass.
214    ///
215    /// Only read from.
216    pub prev_pass: PassState,
217
218    /// Has this viewport been updated this pass?
219    pub used: bool,
220
221    /// State related to repaint scheduling.
222    repaint: ViewportRepaintInfo,
223
224    // ----------------------
225    // Updated at the start of the pass:
226    //
227    /// Which widgets are under the pointer?
228    pub hits: WidgetHits,
229
230    /// What widgets are being interacted with this pass?
231    ///
232    /// Based on the widgets from last pass, and input in this pass.
233    pub interact_widgets: InteractionSnapshot,
234
235    // ----------------------
236    // The output of a pass:
237    //
238    pub graphics: GraphicLayers,
239    // Most of the things in `PlatformOutput` are not actually viewport dependent.
240    pub output: PlatformOutput,
241    pub commands: Vec<ViewportCommand>,
242
243    // ----------------------
244    // Cross-frame statistics:
245    pub num_multipass_in_row: usize,
246}
247
248/// What called [`Context::request_repaint`] or [`Context::request_discard`]?
249#[derive(Clone, PartialEq, Eq, Hash)]
250pub struct RepaintCause {
251    /// What file had the call that requested the repaint?
252    pub file: &'static str,
253
254    /// What line number of the call that requested the repaint?
255    pub line: u32,
256
257    /// Explicit reason; human readable.
258    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    /// Capture the file and line number of the call site.
275    #[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    /// Capture the file and line number of the call site,
287    /// as well as add a reason.
288    #[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
299/// Per-viewport state related to repaint scheduling.
300struct ViewportRepaintInfo {
301    /// Monotonically increasing counter.
302    ///
303    /// Incremented at the end of [`Context::run`].
304    /// This can be smaller than [`Self::cumulative_pass_nr`],
305    /// but never larger.
306    cumulative_frame_nr: u64,
307
308    /// Monotonically increasing counter, counting the number of passes.
309    /// This can be larger than [`Self::cumulative_frame_nr`],
310    /// but never smaller.
311    cumulative_pass_nr: u64,
312
313    /// The duration which the backend will poll for new events
314    /// before forcing another egui update, even if there's no new events.
315    ///
316    /// Also used to suppress multiple calls to the repaint callback during the same pass.
317    ///
318    /// This is also returned in [`crate::ViewportOutput`].
319    repaint_delay: Duration,
320
321    /// While positive, keep requesting repaints. Decrement at the start of each pass.
322    outstanding: u8,
323
324    /// What caused repaints during this pass?
325    causes: Vec<RepaintCause>,
326
327    /// What triggered a repaint the previous pass?
328    /// (i.e: why are we updating now?)
329    prev_causes: Vec<RepaintCause>,
330
331    /// What was the output of `repaint_delay` on the previous pass?
332    ///
333    /// If this was zero, we are repainting as quickly as possible
334    /// (as far as we know).
335    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            // We haven't scheduled a repaint yet.
345            repaint_delay: Duration::MAX,
346
347            // Let's run a couple of frames at the start, because why not.
348            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// ----------------------------------------------------------------------------
365
366#[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    /// All viewports share the same texture manager and texture namespace.
378    ///
379    /// In all viewports, [`TextureId::default`] is special, and points to the font atlas.
380    /// The font-atlas texture _may_ be different across viewports, as they may have different
381    /// `pixels_per_point`, so we do special book-keeping for that.
382    /// See <https://github.com/emilk/egui/issues/3664>.
383    tex_manager: WrappedTextureManager,
384
385    /// Set during the pass, becomes active at the start of the next pass.
386    new_zoom_factor: Option<f32>,
387
388    os: OperatingSystem,
389
390    /// How deeply nested are we?
391    viewport_stack: Vec<ViewportIdPair>,
392
393    /// What is the last viewport rendered?
394    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(); // not necessarily root, just outermost immediate viewport
425        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            // This is a bit hacky, but is required to avoid jitter:
437            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            // We should really scale everything else in the input too,
442            // but the `screen_rect` is the most important part.
443        }
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        // Ensure we register the background area so panels and background ui can catch clicks:
496        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    /// Load fonts unless already loaded.
530    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            // New font definition loaded, so we need to reload all fonts.
537            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; // recreate all the fonts
547                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            /// Find the first ancestor that already has an accesskit node.
595            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    /// Return the `ViewportId` of the current viewport.
626    ///
627    /// For the root viewport this will return [`ViewportId::ROOT`].
628    pub(crate) fn viewport_id(&self) -> ViewportId {
629        self.viewport_stack.last().copied().unwrap_or_default().this
630    }
631
632    /// Return the `ViewportId` of his parent.
633    ///
634    /// For the root viewport this will return [`ViewportId::ROOT`].
635    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    /// The current active viewport
652    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// ----------------------------------------------------------------------------
662
663/// Your handle to egui.
664///
665/// This is the first thing you need when working with egui.
666/// Contains the [`InputState`], [`Memory`], [`PlatformOutput`], and more.
667///
668/// [`Context`] is cheap to clone, and any clones refers to the same mutable data
669/// ([`Context`] uses refcounting internally).
670///
671/// ## Locking
672/// All methods are marked `&self`; [`Context`] has interior mutability protected by an [`RwLock`].
673///
674/// To access parts of a `Context` you need to use some of the helper functions that take closures:
675///
676/// ```
677/// # let ctx = egui::Context::default();
678/// if ctx.input(|i| i.key_pressed(egui::Key::A)) {
679///     ctx.copy_text("Hello!".to_owned());
680/// }
681/// ```
682///
683/// Within such a closure you may NOT recursively lock the same [`Context`], as that can lead to a deadlock.
684/// Therefore it is important that any lock of [`Context`] is short-lived.
685///
686/// These are effectively transactional accesses.
687///
688/// [`Ui`] has many of the same accessor functions, and the same applies there.
689///
690/// ## Example:
691///
692/// ``` no_run
693/// # fn handle_platform_output(_: egui::PlatformOutput) {}
694/// # fn paint(textures_delta: egui::TexturesDelta, _: Vec<egui::ClippedPrimitive>) {}
695/// let mut ctx = egui::Context::default();
696///
697/// // Game loop:
698/// loop {
699///     let raw_input = egui::RawInput::default();
700///     let full_output = ctx.run(raw_input, |ctx| {
701///         egui::CentralPanel::default().show(&ctx, |ui| {
702///             ui.label("Hello world!");
703///             if ui.button("Click me").clicked() {
704///                 // take some action here
705///             }
706///         });
707///     });
708///     handle_platform_output(full_output.platform_output);
709///     let clipped_primitives = ctx.tessellate(full_output.shapes, full_output.pixels_per_point);
710///     paint(full_output.textures_delta, clipped_primitives);
711/// }
712/// ```
713#[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        // Register built-in plugins:
740        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    /// Do read-only (shared access) transaction on Context
750    fn read<R>(&self, reader: impl FnOnce(&ContextImpl) -> R) -> R {
751        reader(&self.0.read())
752    }
753
754    /// Do read-write (exclusive access) transaction on Context
755    fn write<R>(&self, writer: impl FnOnce(&mut ContextImpl) -> R) -> R {
756        writer(&mut self.0.write())
757    }
758
759    /// Run the ui code for one frame.
760    ///
761    /// At most [`Options::max_passes`] calls will be issued to `run_ui`,
762    /// and only on the rare occasion that [`Context::request_discard`] is called.
763    /// Usually, it `run_ui` will only be called once.
764    ///
765    /// The [`Ui`] given to the callback will cover the entire [`Self::content_rect`],
766    /// with no margin or background color. Use [`crate::Frame`] to add that.
767    ///
768    /// You can organize your GUI using [`crate::Panel`].
769    ///
770    /// Instead of calling `run_ui`, you can alternatively use [`Self::begin_pass`] and [`Context::end_pass`].
771    ///
772    /// ```
773    /// // One egui context that you keep reusing:
774    /// let mut ctx = egui::Context::default();
775    ///
776    /// // Each frame:
777    /// let input = egui::RawInput::default();
778    /// let full_output = ctx.run_ui(input, |ui| {
779    ///     ui.label("Hello egui!");
780    /// });
781    /// // handle full_output
782    /// ```
783    ///
784    /// ## See also
785    /// * [`Self::run`]
786    #[must_use]
787    pub fn run_ui(&self, new_input: RawInput, mut run_ui: impl FnMut(&mut Ui)) -> FullOutput {
788        self.run_ui_dyn(new_input, &mut run_ui)
789    }
790
791    #[must_use]
792    fn run_ui_dyn(&self, new_input: RawInput, run_ui: &mut dyn FnMut(&mut Ui)) -> FullOutput {
793        let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
794        #[expect(deprecated)]
795        self.run(new_input, |ctx| {
796            let mut root_ui = Ui::new(
797                ctx.clone(),
798                Id::new((ctx.viewport_id(), "__top_ui")),
799                UiBuilder::new()
800                    .layer_id(LayerId::background())
801                    .max_rect(ctx.available_rect()),
802            );
803
804            {
805                plugins.on_begin_pass(&mut root_ui);
806                run_ui(&mut root_ui);
807                plugins.on_end_pass(&mut root_ui);
808            }
809
810            ctx.pass_state_mut(|state| {
811                state.root_ui_available_rect = Some(root_ui.available_rect_before_wrap());
812                state.root_ui_min_rect = Some(root_ui.min_rect());
813            });
814        })
815    }
816
817    /// Run the ui code for one frame.
818    ///
819    /// At most [`Options::max_passes`] calls will be issued to `run_ui`,
820    /// and only on the rare occasion that [`Context::request_discard`] is called.
821    /// Usually, it `run_ui` will only be called once.
822    ///
823    /// Put your widgets into a [`crate::Panel`], [`crate::CentralPanel`], [`crate::Window`] or [`crate::Area`].
824    ///
825    /// Instead of calling `run`, you can alternatively use [`Self::begin_pass`] and [`Context::end_pass`].
826    ///
827    /// ```
828    /// // One egui context that you keep reusing:
829    /// let mut ctx = egui::Context::default();
830    ///
831    /// // Each frame:
832    /// let input = egui::RawInput::default();
833    /// let full_output = ctx.run(input, |ctx| {
834    ///     egui::CentralPanel::default().show(&ctx, |ui| {
835    ///         ui.label("Hello egui!");
836    ///     });
837    /// });
838    /// // handle full_output
839    /// ```
840    ///
841    /// ## See also
842    /// * [`Self::run_ui`]
843    #[must_use]
844    #[deprecated = "Call run_ui instead"]
845    pub fn run(&self, new_input: RawInput, mut run_ui: impl FnMut(&Self)) -> FullOutput {
846        self.run_dyn(new_input, &mut run_ui)
847    }
848
849    #[must_use]
850    fn run_dyn(&self, mut new_input: RawInput, run_ui: &mut dyn FnMut(&Self)) -> FullOutput {
851        profiling::function_scope!();
852        let viewport_id = new_input.viewport_id;
853        let max_passes = self.write(|ctx| ctx.memory.options.max_passes.get());
854
855        let mut output = FullOutput::default();
856        debug_assert_eq!(
857            output.platform_output.num_completed_passes, 0,
858            "output must be fresh, but had {} passes",
859            output.platform_output.num_completed_passes
860        );
861
862        loop {
863            profiling::scope!(
864                "pass",
865                output
866                    .platform_output
867                    .num_completed_passes
868                    .to_string()
869                    .as_str()
870            );
871
872            // We must move the `num_passes` (back) to the viewport output so that [`Self::will_discard`]
873            // has access to the latest pass count.
874            self.write(|ctx| {
875                let viewport = ctx.viewport_for(viewport_id);
876                viewport.output.num_completed_passes =
877                    std::mem::take(&mut output.platform_output.num_completed_passes);
878                output.platform_output.request_discard_reasons.clear();
879            });
880
881            self.begin_pass(new_input.take());
882            run_ui(self);
883            output.append(self.end_pass());
884            debug_assert!(
885                0 < output.platform_output.num_completed_passes,
886                "Completed passes was lower than 0, was {}",
887                output.platform_output.num_completed_passes
888            );
889
890            if !output.platform_output.requested_discard() {
891                break; // no need for another pass
892            }
893
894            if max_passes <= output.platform_output.num_completed_passes {
895                log::debug!(
896                    "Ignoring call request_discard, because max_passes={max_passes}. Requested from {:?}",
897                    output.platform_output.request_discard_reasons
898                );
899
900                break;
901            }
902        }
903
904        self.write(|ctx| {
905            let did_multipass = 1 < output.platform_output.num_completed_passes;
906            let viewport = ctx.viewport_for(viewport_id);
907            if did_multipass {
908                viewport.num_multipass_in_row += 1;
909            } else {
910                viewport.num_multipass_in_row = 0;
911            }
912            viewport.repaint.cumulative_frame_nr += 1;
913        });
914
915        output
916    }
917
918    /// An alternative to calling [`Self::run`].
919    ///
920    /// It is usually better to use [`Self::run`], because
921    /// `run` supports multi-pass layout using [`Self::request_discard`].
922    ///
923    /// ```
924    /// // One egui context that you keep reusing:
925    /// let mut ctx = egui::Context::default();
926    ///
927    /// // Each frame:
928    /// let input = egui::RawInput::default();
929    /// ctx.begin_pass(input);
930    ///
931    /// egui::CentralPanel::default().show(&ctx, |ui| {
932    ///     ui.label("Hello egui!");
933    /// });
934    ///
935    /// let full_output = ctx.end_pass();
936    /// // handle full_output
937    /// ```
938    pub fn begin_pass(&self, mut new_input: RawInput) {
939        profiling::function_scope!();
940
941        let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
942        plugins.on_input(&mut new_input);
943
944        self.write(|ctx| ctx.begin_pass(new_input));
945    }
946
947    /// See [`Self::begin_pass`].
948    #[deprecated = "Renamed begin_pass"]
949    pub fn begin_frame(&self, new_input: RawInput) {
950        self.begin_pass(new_input);
951    }
952}
953
954/// ## Borrows parts of [`Context`]
955/// These functions all lock the [`Context`].
956/// Please see the documentation of [`Context`] for how locking works!
957impl Context {
958    /// Read-only access to [`InputState`].
959    ///
960    /// Note that this locks the [`Context`].
961    ///
962    /// ```
963    /// # let mut ctx = egui::Context::default();
964    /// ctx.input(|i| {
965    ///     // ⚠️ Using `ctx` (even from other `Arc` reference) again here will lead to a deadlock!
966    /// });
967    ///
968    /// if let Some(pos) = ctx.input(|i| i.pointer.hover_pos()) {
969    ///     // This is fine!
970    /// }
971    /// ```
972    #[inline]
973    pub fn input<R>(&self, reader: impl FnOnce(&InputState) -> R) -> R {
974        self.write(move |ctx| reader(&ctx.viewport().input))
975    }
976
977    /// This will create a `InputState::default()` if there is no input state for that viewport
978    #[inline]
979    pub fn input_for<R>(&self, id: ViewportId, reader: impl FnOnce(&InputState) -> R) -> R {
980        self.write(move |ctx| reader(&ctx.viewport_for(id).input))
981    }
982
983    /// Read-write access to [`InputState`].
984    #[inline]
985    pub fn input_mut<R>(&self, writer: impl FnOnce(&mut InputState) -> R) -> R {
986        self.input_mut_for(self.viewport_id(), writer)
987    }
988
989    /// This will create a `InputState::default()` if there is no input state for that viewport
990    #[inline]
991    pub fn input_mut_for<R>(&self, id: ViewportId, writer: impl FnOnce(&mut InputState) -> R) -> R {
992        self.write(move |ctx| writer(&mut ctx.viewport_for(id).input))
993    }
994
995    /// Read-only access to [`Memory`].
996    #[inline]
997    pub fn memory<R>(&self, reader: impl FnOnce(&Memory) -> R) -> R {
998        self.read(move |ctx| reader(&ctx.memory))
999    }
1000
1001    /// Read-write access to [`Memory`].
1002    #[inline]
1003    pub fn memory_mut<R>(&self, writer: impl FnOnce(&mut Memory) -> R) -> R {
1004        self.write(move |ctx| writer(&mut ctx.memory))
1005    }
1006
1007    /// Read-only access to [`IdTypeMap`], which stores superficial widget state.
1008    #[inline]
1009    pub fn data<R>(&self, reader: impl FnOnce(&IdTypeMap) -> R) -> R {
1010        self.read(move |ctx| reader(&ctx.memory.data))
1011    }
1012
1013    /// Read-write access to [`IdTypeMap`], which stores superficial widget state.
1014    #[inline]
1015    pub fn data_mut<R>(&self, writer: impl FnOnce(&mut IdTypeMap) -> R) -> R {
1016        self.write(move |ctx| writer(&mut ctx.memory.data))
1017    }
1018
1019    /// Read-write access to [`GraphicLayers`], where painted [`crate::Shape`]s are written to.
1020    #[inline]
1021    pub fn graphics_mut<R>(&self, writer: impl FnOnce(&mut GraphicLayers) -> R) -> R {
1022        self.write(move |ctx| writer(&mut ctx.viewport().graphics))
1023    }
1024
1025    /// Read-only access to [`GraphicLayers`], where painted [`crate::Shape`]s are written to.
1026    #[inline]
1027    pub fn graphics<R>(&self, reader: impl FnOnce(&GraphicLayers) -> R) -> R {
1028        self.write(move |ctx| reader(&ctx.viewport().graphics))
1029    }
1030
1031    /// Read-only access to [`PlatformOutput`].
1032    ///
1033    /// This is what egui outputs each pass and frame.
1034    ///
1035    /// ```
1036    /// # let mut ctx = egui::Context::default();
1037    /// ctx.output_mut(|o| o.cursor_icon = egui::CursorIcon::Progress);
1038    /// ```
1039    #[inline]
1040    pub fn output<R>(&self, reader: impl FnOnce(&PlatformOutput) -> R) -> R {
1041        self.write(move |ctx| reader(&ctx.viewport().output))
1042    }
1043
1044    /// Read-write access to [`PlatformOutput`].
1045    #[inline]
1046    pub fn output_mut<R>(&self, writer: impl FnOnce(&mut PlatformOutput) -> R) -> R {
1047        self.write(move |ctx| writer(&mut ctx.viewport().output))
1048    }
1049
1050    /// Read-only access to [`PassState`].
1051    ///
1052    /// This is only valid during the call to [`Self::run`] (between [`Self::begin_pass`] and [`Self::end_pass`]).
1053    #[inline]
1054    pub(crate) fn pass_state<R>(&self, reader: impl FnOnce(&PassState) -> R) -> R {
1055        self.write(move |ctx| reader(&ctx.viewport().this_pass))
1056    }
1057
1058    /// Read-write access to [`PassState`].
1059    ///
1060    /// This is only valid during the call to [`Self::run`] (between [`Self::begin_pass`] and [`Self::end_pass`]).
1061    #[inline]
1062    pub(crate) fn pass_state_mut<R>(&self, writer: impl FnOnce(&mut PassState) -> R) -> R {
1063        self.write(move |ctx| writer(&mut ctx.viewport().this_pass))
1064    }
1065
1066    /// Read-only access to the [`PassState`] from the previous pass.
1067    ///
1068    /// This is swapped at the end of each pass.
1069    #[inline]
1070    pub(crate) fn prev_pass_state<R>(&self, reader: impl FnOnce(&PassState) -> R) -> R {
1071        self.write(move |ctx| reader(&ctx.viewport().prev_pass))
1072    }
1073
1074    /// Read-only access to [`Fonts`].
1075    ///
1076    /// Not valid until first call to [`Context::run()`].
1077    /// That's because since we don't know the proper `pixels_per_point` until then.
1078    #[inline]
1079    pub fn fonts<R>(&self, reader: impl FnOnce(&FontsView<'_>) -> R) -> R {
1080        self.write(move |ctx| {
1081            let pixels_per_point = ctx.pixels_per_point();
1082            reader(
1083                &ctx.fonts
1084                    .as_mut()
1085                    .expect("No fonts available until first call to Context::run()")
1086                    .with_pixels_per_point(pixels_per_point),
1087            )
1088        })
1089    }
1090
1091    /// Read-write access to [`Fonts`].
1092    ///
1093    /// Not valid until first call to [`Context::run()`].
1094    /// That's because since we don't know the proper `pixels_per_point` until then.
1095    #[inline]
1096    pub fn fonts_mut<R>(&self, reader: impl FnOnce(&mut FontsView<'_>) -> R) -> R {
1097        self.write(move |ctx| {
1098            let pixels_per_point = ctx.pixels_per_point();
1099            reader(
1100                &mut ctx
1101                    .fonts
1102                    .as_mut()
1103                    .expect("No fonts available until first call to Context::run()")
1104                    .with_pixels_per_point(pixels_per_point),
1105            )
1106        })
1107    }
1108
1109    /// Read-only access to [`Options`].
1110    #[inline]
1111    pub fn options<R>(&self, reader: impl FnOnce(&Options) -> R) -> R {
1112        self.read(move |ctx| reader(&ctx.memory.options))
1113    }
1114
1115    /// Read-write access to [`Options`].
1116    #[inline]
1117    pub fn options_mut<R>(&self, writer: impl FnOnce(&mut Options) -> R) -> R {
1118        self.write(move |ctx| writer(&mut ctx.memory.options))
1119    }
1120
1121    /// Read-only access to [`TessellationOptions`].
1122    #[inline]
1123    pub fn tessellation_options<R>(&self, reader: impl FnOnce(&TessellationOptions) -> R) -> R {
1124        self.read(move |ctx| reader(&ctx.memory.options.tessellation_options))
1125    }
1126
1127    /// Read-write access to [`TessellationOptions`].
1128    #[inline]
1129    pub fn tessellation_options_mut<R>(
1130        &self,
1131        writer: impl FnOnce(&mut TessellationOptions) -> R,
1132    ) -> R {
1133        self.write(move |ctx| writer(&mut ctx.memory.options.tessellation_options))
1134    }
1135
1136    /// If the given [`Id`] has been used previously the same pass at different position,
1137    /// then an error will be printed on screen.
1138    ///
1139    /// This function is already called for all widgets that do any interaction,
1140    /// but you can call this from widgets that store state but that does not interact.
1141    ///
1142    /// The given [`Rect`] should be approximately where the widget will be.
1143    /// The most important thing is that [`Rect::min`] is approximately correct,
1144    /// because that's where the warning will be painted. If you don't know what size to pick, just pick [`Vec2::ZERO`].
1145    pub fn check_for_id_clash(&self, id: Id, new_rect: Rect, what: &str) {
1146        let prev_rect = self.pass_state_mut(move |state| state.used_ids.insert(id, new_rect));
1147
1148        if !self.options(|opt| opt.warn_on_id_clash) {
1149            return;
1150        }
1151
1152        let Some(prev_rect) = prev_rect else { return };
1153
1154        // It is ok to reuse the same ID for e.g. a frame around a widget,
1155        // or to check for interaction with the same widget twice:
1156        let is_same_rect = prev_rect.expand(0.1).contains_rect(new_rect)
1157            || new_rect.expand(0.1).contains_rect(prev_rect);
1158        if is_same_rect {
1159            return;
1160        }
1161
1162        let show_error = |widget_rect: Rect, text: String| {
1163            let content_rect = self.content_rect();
1164
1165            let text = format!("🔥 {text}");
1166            let color = self.global_style().visuals.error_fg_color;
1167            let painter = self.debug_painter();
1168            painter.rect_stroke(widget_rect, 0.0, (1.0, color), StrokeKind::Outside);
1169
1170            let below = widget_rect.bottom() + 32.0 < content_rect.bottom();
1171
1172            let text_rect = if below {
1173                painter.debug_text(
1174                    widget_rect.left_bottom() + vec2(0.0, 2.0),
1175                    Align2::LEFT_TOP,
1176                    color,
1177                    text,
1178                )
1179            } else {
1180                painter.debug_text(
1181                    widget_rect.left_top() - vec2(0.0, 2.0),
1182                    Align2::LEFT_BOTTOM,
1183                    color,
1184                    text,
1185                )
1186            };
1187
1188            if let Some(pointer_pos) = self.pointer_hover_pos()
1189                && text_rect.contains(pointer_pos)
1190            {
1191                let tooltip_pos = if below {
1192                    text_rect.left_bottom() + vec2(2.0, 4.0)
1193                } else {
1194                    text_rect.left_top() + vec2(2.0, -4.0)
1195                };
1196
1197                painter.error(
1198                        tooltip_pos,
1199                        format!("Widget is {} this text.\n\n\
1200                             ID clashes happens when things like Windows or CollapsingHeaders share names,\n\
1201                             or when things like Plot and Grid:s aren't given unique id_salt:s.\n\n\
1202                             Sometimes the solution is to use ui.push_id.",
1203                                if below { "above" } else { "below" }),
1204                    );
1205            }
1206        };
1207
1208        let id_str = id.short_debug_format();
1209
1210        if prev_rect.min.distance(new_rect.min) < 4.0 {
1211            show_error(new_rect, format!("Double use of {what} ID {id_str}"));
1212        } else {
1213            show_error(prev_rect, format!("First use of {what} ID {id_str}"));
1214            show_error(new_rect, format!("Second use of {what} ID {id_str}"));
1215        }
1216    }
1217
1218    // ---------------------------------------------------------------------
1219
1220    /// Create a widget and check for interaction.
1221    ///
1222    /// If this is not called, the widget doesn't exist.
1223    ///
1224    /// You should use [`Ui::interact`] instead.
1225    ///
1226    /// If the widget already exists, its state (sense, Rect, etc) will be updated.
1227    ///
1228    /// `allow_focus` should usually be true, unless you call this function multiple times with the
1229    /// same widget, then `allow_focus` should only be true once (like in [`Ui::new`] (true) and [`Ui::remember_min_rect`] (false)).
1230    pub(crate) fn create_widget(
1231        &self,
1232        w: WidgetRect,
1233        allow_focus: bool,
1234        options: crate::InteractOptions,
1235    ) -> Response {
1236        let interested_in_focus = w.enabled
1237            && w.sense.is_focusable()
1238            && self.memory(|mem| mem.allows_interaction(w.layer_id));
1239
1240        // Remember this widget
1241        self.write(|ctx| {
1242            let viewport = ctx.viewport();
1243
1244            // We add all widgets here, even non-interactive ones,
1245            // because we need this list not only for checking for blocking widgets,
1246            // but also to know when we have reached the widget we are checking for cover.
1247            viewport.this_pass.widgets.insert(w.layer_id, w, options);
1248
1249            if allow_focus && interested_in_focus {
1250                ctx.memory.interested_in_focus(w.id, w.layer_id);
1251            }
1252        });
1253
1254        if allow_focus && !interested_in_focus {
1255            // Not interested or allowed input:
1256            self.memory_mut(|mem| mem.surrender_focus(w.id));
1257        }
1258
1259        if w.sense.interactive() || w.sense.is_focusable() {
1260            self.check_for_id_clash(w.id, w.rect, "widget");
1261        }
1262
1263        #[allow(clippy::allow_attributes, clippy::let_and_return)]
1264        let res = self.get_response(w);
1265
1266        #[cfg(debug_assertions)]
1267        if res.contains_pointer() {
1268            let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
1269            plugins.on_widget_under_pointer(self, &w);
1270        }
1271
1272        if allow_focus && w.sense.is_focusable() {
1273            // Make sure anything that can receive focus has an AccessKit node.
1274            // TODO(mwcampbell): For nodes that are filled from widget info,
1275            // some information is written to the node twice.
1276            self.accesskit_node_builder(w.id, |builder| res.fill_accesskit_node_common(builder));
1277        }
1278
1279        self.write(|ctx| {
1280            use crate::{Align, pass_state::ScrollTarget, style::ScrollAnimation};
1281            let viewport = ctx.viewport_for(ctx.viewport_id());
1282
1283            viewport
1284                .input
1285                .consume_accesskit_action_requests(res.id, |request| {
1286                    use accesskit::Action;
1287
1288                    // TODO(lucasmerlin): Correctly handle the scroll unit:
1289                    // https://github.com/AccessKit/accesskit/blob/e639c0e0d8ccbfd9dff302d972fa06f9766d608e/common/src/lib.rs#L2621
1290                    const DISTANCE: f32 = 100.0;
1291
1292                    match &request.action {
1293                        Action::ScrollIntoView => {
1294                            viewport.this_pass.scroll_target = [
1295                                Some(ScrollTarget::new(
1296                                    res.rect.x_range(),
1297                                    Some(Align::Center),
1298                                    ScrollAnimation::none(),
1299                                )),
1300                                Some(ScrollTarget::new(
1301                                    res.rect.y_range(),
1302                                    Some(Align::Center),
1303                                    ScrollAnimation::none(),
1304                                )),
1305                            ];
1306                        }
1307                        Action::ScrollDown => {
1308                            viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::UP;
1309                        }
1310                        Action::ScrollUp => {
1311                            viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::DOWN;
1312                        }
1313                        Action::ScrollLeft => {
1314                            viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::LEFT;
1315                        }
1316                        Action::ScrollRight => {
1317                            viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::RIGHT;
1318                        }
1319                        _ => return false,
1320                    }
1321                    true
1322                });
1323        });
1324
1325        res
1326    }
1327
1328    /// Read the response of some widget, which may be called _before_ creating the widget (!).
1329    ///
1330    /// This is because widget interaction happens at the start of the pass, using the widget rects from the previous pass.
1331    ///
1332    /// If the widget was not visible the previous pass (or this pass), this will return `None`.
1333    ///
1334    /// If you try to read a [`Ui`]'s response, while still inside, this will return the [`Rect`] from the previous frame.
1335    pub fn read_response(&self, id: Id) -> Option<Response> {
1336        self.write(|ctx| {
1337            let viewport = ctx.viewport();
1338            let widget_rect = viewport
1339                .this_pass
1340                .widgets
1341                .get(id)
1342                .or_else(|| viewport.prev_pass.widgets.get(id))
1343                .copied();
1344            widget_rect.map(|mut rect| {
1345                // If the Rect is invalid the Ui hasn't registered its final Rect yet.
1346                // We return the Rect from last frame instead.
1347                if !(rect.rect.is_positive() && rect.rect.is_finite())
1348                    && let Some(prev_rect) = viewport.prev_pass.widgets.get(id)
1349                {
1350                    rect.rect = prev_rect.rect;
1351                }
1352                rect
1353            })
1354        })
1355        .map(|widget_rect| self.get_response(widget_rect))
1356    }
1357
1358    /// Do all interaction for an existing widget, without (re-)registering it.
1359    pub(crate) fn get_response(&self, widget_rect: WidgetRect) -> Response {
1360        use response::Flags;
1361
1362        let WidgetRect {
1363            id,
1364            parent_id: _,
1365            layer_id,
1366            rect,
1367            interact_rect,
1368            sense,
1369            enabled,
1370        } = widget_rect;
1371
1372        // previous pass + "highlight next pass" == "highlight this pass"
1373        let highlighted = self.prev_pass_state(|fs| fs.highlight_next_pass.contains(&id));
1374
1375        let mut res = Response {
1376            ctx: self.clone(),
1377            layer_id,
1378            id,
1379            rect,
1380            interact_rect,
1381            sense,
1382            flags: Flags::empty(),
1383            interact_pointer_pos_or_nan: Pos2::NAN,
1384            intrinsic_size_or_nan: Vec2::NAN,
1385        };
1386
1387        res.flags.set(Flags::ENABLED, enabled);
1388        res.flags.set(Flags::HIGHLIGHTED, highlighted);
1389
1390        self.write(|ctx| {
1391            let viewport = ctx.viewports.entry(ctx.viewport_id()).or_default();
1392
1393            res.flags.set(
1394                Flags::CONTAINS_POINTER,
1395                viewport.interact_widgets.contains_pointer.contains(&id),
1396            );
1397
1398            let input = &viewport.input;
1399            let memory = &mut ctx.memory;
1400
1401            if enabled
1402                && sense.senses_click()
1403                && memory.has_focus(id)
1404                && (input.key_pressed(Key::Space) || input.key_pressed(Key::Enter))
1405            {
1406                // Space/enter works like a primary click for e.g. selected buttons
1407                res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);
1408            }
1409
1410            if enabled
1411                && sense.senses_click()
1412                && input.has_accesskit_action_request(id, accesskit::Action::Click)
1413            {
1414                res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);
1415            }
1416
1417            if enabled && sense.senses_click() && Some(id) == viewport.interact_widgets.long_touched
1418            {
1419                res.flags.set(Flags::LONG_TOUCHED, true);
1420            }
1421
1422            let interaction = memory.interaction();
1423
1424            res.flags.set(
1425                Flags::IS_POINTER_BUTTON_DOWN_ON,
1426                interaction.potential_click_id == Some(id)
1427                    || interaction.potential_drag_id == Some(id),
1428            );
1429
1430            if res.enabled() {
1431                res.flags.set(
1432                    Flags::HOVERED,
1433                    viewport.interact_widgets.hovered.contains(&id),
1434                );
1435                res.flags.set(
1436                    Flags::DRAGGED,
1437                    Some(id) == viewport.interact_widgets.dragged,
1438                );
1439                res.flags.set(
1440                    Flags::DRAG_STARTED,
1441                    Some(id) == viewport.interact_widgets.drag_started,
1442                );
1443                res.flags.set(
1444                    Flags::DRAG_STOPPED,
1445                    Some(id) == viewport.interact_widgets.drag_stopped,
1446                );
1447            }
1448
1449            let clicked = Some(id) == viewport.interact_widgets.clicked;
1450            let mut any_press = false;
1451
1452            for pointer_event in &input.pointer.pointer_events {
1453                match pointer_event {
1454                    PointerEvent::Moved(_) => {}
1455                    PointerEvent::Pressed { .. } => {
1456                        any_press = true;
1457                    }
1458                    PointerEvent::Released { click, .. } => {
1459                        if enabled && sense.senses_click() && clicked && click.is_some() {
1460                            res.flags.set(Flags::CLICKED, true);
1461                        }
1462
1463                        res.flags.set(Flags::IS_POINTER_BUTTON_DOWN_ON, false);
1464                        res.flags.set(Flags::DRAGGED, false);
1465                    }
1466                }
1467            }
1468
1469            // is_pointer_button_down_on is false when released, but we want interact_pointer_pos
1470            // to still work.
1471            let is_interacted_with = res.is_pointer_button_down_on()
1472                || res.long_touched()
1473                || clicked
1474                || res.drag_stopped();
1475            if is_interacted_with && let Some(mut pos) = input.pointer.interact_pos() {
1476                if let Some(to_global) = memory.to_global.get(&res.layer_id) {
1477                    pos = to_global.inverse() * pos;
1478                }
1479                res.interact_pointer_pos_or_nan = pos;
1480            }
1481
1482            if input.pointer.any_down() && !is_interacted_with {
1483                // We don't hover widgets while interacting with *other* widgets:
1484                res.flags.set(Flags::HOVERED, false);
1485            }
1486
1487            let should_surrender_focus = match memory.options.input_options.surrender_focus_on {
1488                SurrenderFocusOn::Presses => any_press,
1489                SurrenderFocusOn::Clicks => input.pointer.any_click(),
1490                SurrenderFocusOn::Never => false,
1491            };
1492
1493            let pointer_clicked_elsewhere = should_surrender_focus && !res.hovered();
1494            if pointer_clicked_elsewhere && memory.has_focus(id) {
1495                memory.surrender_focus(id);
1496            }
1497        });
1498
1499        res
1500    }
1501
1502    /// This is called by [`Response::widget_info`], but can also be called directly.
1503    ///
1504    /// With some debug flags it will store the widget info in [`crate::WidgetRects`] for later display.
1505    #[inline]
1506    pub fn register_widget_info(&self, id: Id, make_info: impl Fn() -> crate::WidgetInfo) {
1507        #[cfg(debug_assertions)]
1508        self.write(|ctx| {
1509            if ctx.memory.options.style().debug.show_interactive_widgets {
1510                ctx.viewport().this_pass.widgets.set_info(id, make_info());
1511            }
1512        });
1513
1514        #[cfg(not(debug_assertions))]
1515        {
1516            _ = (self, id, make_info);
1517        }
1518    }
1519
1520    /// Get a full-screen painter for a new or existing layer
1521    pub fn layer_painter(&self, layer_id: LayerId) -> Painter {
1522        let content_rect = self.content_rect();
1523        Painter::new(self.clone(), layer_id, content_rect)
1524    }
1525
1526    /// Paint on top of _everything_ else (even on top of tooltips and popups).
1527    pub fn debug_painter(&self) -> Painter {
1528        Self::layer_painter(self, LayerId::debug())
1529    }
1530
1531    /// Print this text next to the cursor at the end of the pass.
1532    ///
1533    /// If you call this multiple times, the text will be appended.
1534    ///
1535    /// This only works if compiled with `debug_assertions`.
1536    ///
1537    /// ```
1538    /// # let ctx = egui::Context::default();
1539    /// # let state = true;
1540    /// ctx.debug_text(format!("State: {state:?}"));
1541    /// ```
1542    ///
1543    /// This is just a convenience for calling [`crate::debug_text::print`].
1544    #[track_caller]
1545    pub fn debug_text(&self, text: impl Into<WidgetText>) {
1546        crate::debug_text::print(self, text);
1547    }
1548
1549    /// Current time in seconds, relative to some unknown epoch.
1550    pub fn time(&self) -> f64 {
1551        self.input(|i| i.time)
1552    }
1553
1554    /// What operating system are we running on?
1555    ///
1556    /// When compiling natively, this is
1557    /// figured out from the `target_os`.
1558    ///
1559    /// For web, this can be figured out from the user-agent,
1560    /// and is done so by [`eframe`](https://github.com/emilk/egui/tree/main/crates/eframe).
1561    pub fn os(&self) -> OperatingSystem {
1562        self.read(|ctx| ctx.os)
1563    }
1564
1565    /// Set the operating system we are running on.
1566    ///
1567    /// If you are writing wasm-based integration for egui you
1568    /// may want to set this based on e.g. the user-agent.
1569    pub fn set_os(&self, os: OperatingSystem) {
1570        self.write(|ctx| ctx.os = os);
1571    }
1572
1573    /// Set the cursor icon.
1574    ///
1575    /// Equivalent to:
1576    /// ```
1577    /// # let ctx = egui::Context::default();
1578    /// ctx.output_mut(|o| o.cursor_icon = egui::CursorIcon::PointingHand);
1579    /// ```
1580    pub fn set_cursor_icon(&self, cursor_icon: CursorIcon) {
1581        self.output_mut(|o| o.cursor_icon = cursor_icon);
1582    }
1583
1584    /// Add a command to [`PlatformOutput::commands`],
1585    /// for the integration to execute at the end of the frame.
1586    pub fn send_cmd(&self, cmd: crate::OutputCommand) {
1587        self.output_mut(|o| o.commands.push(cmd));
1588    }
1589
1590    /// Open an URL in a browser.
1591    ///
1592    /// Equivalent to:
1593    /// ```
1594    /// # let ctx = egui::Context::default();
1595    /// # let open_url = egui::OpenUrl::same_tab("http://www.example.com");
1596    /// ctx.send_cmd(egui::OutputCommand::OpenUrl(open_url));
1597    /// ```
1598    pub fn open_url(&self, open_url: crate::OpenUrl) {
1599        self.send_cmd(crate::OutputCommand::OpenUrl(open_url));
1600    }
1601
1602    /// Copy the given text to the system clipboard.
1603    ///
1604    /// Note that in web applications, the clipboard is only accessible in secure contexts (e.g.,
1605    /// HTTPS or localhost). If this method is used outside of a secure context, it will log an
1606    /// error and do nothing. See <https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts>.
1607    pub fn copy_text(&self, text: String) {
1608        self.send_cmd(crate::OutputCommand::CopyText(text));
1609    }
1610
1611    /// Copy the given image to the system clipboard.
1612    ///
1613    /// Note that in web applications, the clipboard is only accessible in secure contexts (e.g.,
1614    /// HTTPS or localhost). If this method is used outside of a secure context, it will log an
1615    /// error and do nothing. See <https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts>.
1616    pub fn copy_image(&self, image: crate::ColorImage) {
1617        self.send_cmd(crate::OutputCommand::CopyImage(image));
1618    }
1619
1620    fn can_show_modifier_symbols(&self) -> bool {
1621        let ModifierNames {
1622            alt,
1623            ctrl,
1624            shift,
1625            mac_cmd,
1626            ..
1627        } = ModifierNames::SYMBOLS;
1628
1629        let font_id = TextStyle::Body.resolve(&self.global_style());
1630        self.fonts_mut(|f| {
1631            let mut font = f.fonts.font(&font_id.family);
1632            font.has_glyphs(alt)
1633                && font.has_glyphs(ctrl)
1634                && font.has_glyphs(shift)
1635                && font.has_glyphs(mac_cmd)
1636        })
1637    }
1638
1639    /// Format the given modifiers in a human-readable way (e.g. `Ctrl+Shift+X`).
1640    pub fn format_modifiers(&self, modifiers: Modifiers) -> String {
1641        let os = self.os();
1642
1643        let is_mac = os.is_mac();
1644
1645        if is_mac && self.can_show_modifier_symbols() {
1646            ModifierNames::SYMBOLS.format(&modifiers, is_mac)
1647        } else {
1648            ModifierNames::NAMES.format(&modifiers, is_mac)
1649        }
1650    }
1651
1652    /// Format the given shortcut in a human-readable way (e.g. `Ctrl+Shift+X`).
1653    ///
1654    /// Can be used to get the text for [`crate::Button::shortcut_text`].
1655    pub fn format_shortcut(&self, shortcut: &KeyboardShortcut) -> String {
1656        let os = self.os();
1657
1658        let is_mac = os.is_mac();
1659
1660        if is_mac && self.can_show_modifier_symbols() {
1661            shortcut.format(&ModifierNames::SYMBOLS, is_mac)
1662        } else {
1663            shortcut.format(&ModifierNames::NAMES, is_mac)
1664        }
1665    }
1666
1667    /// The total number of completed frames.
1668    ///
1669    /// Starts at zero, and is incremented once at the end of each call to [`Self::run`].
1670    ///
1671    /// This is always smaller or equal to [`Self::cumulative_pass_nr`].
1672    pub fn cumulative_frame_nr(&self) -> u64 {
1673        self.cumulative_frame_nr_for(self.viewport_id())
1674    }
1675
1676    /// The total number of completed frames.
1677    ///
1678    /// Starts at zero, and is incremented once at the end of each call to [`Self::run`].
1679    ///
1680    /// This is always smaller or equal to [`Self::cumulative_pass_nr_for`].
1681    pub fn cumulative_frame_nr_for(&self, id: ViewportId) -> u64 {
1682        self.read(|ctx| {
1683            ctx.viewports
1684                .get(&id)
1685                .map(|v| v.repaint.cumulative_frame_nr)
1686                .unwrap_or_else(|| {
1687                    if cfg!(debug_assertions) {
1688                        panic!("cumulative_frame_nr_for failed to find the viewport {id:?}");
1689                    } else {
1690                        0
1691                    }
1692                })
1693        })
1694    }
1695
1696    /// The total number of completed passes (usually there is one pass per rendered frame).
1697    ///
1698    /// Starts at zero, and is incremented for each completed pass inside of [`Self::run`] (usually once).
1699    ///
1700    /// If you instead want to know which pass index this is within the current frame,
1701    /// use [`Self::current_pass_index`].
1702    pub fn cumulative_pass_nr(&self) -> u64 {
1703        self.cumulative_pass_nr_for(self.viewport_id())
1704    }
1705
1706    /// The total number of completed passes (usually there is one pass per rendered frame).
1707    ///
1708    /// Starts at zero, and is incremented for each completed pass inside of [`Self::run`] (usually once).
1709    pub fn cumulative_pass_nr_for(&self, id: ViewportId) -> u64 {
1710        self.read(|ctx| {
1711            ctx.viewports
1712                .get(&id)
1713                .map_or(0, |v| v.repaint.cumulative_pass_nr)
1714        })
1715    }
1716
1717    /// The index of the current pass in the current frame, starting at zero.
1718    ///
1719    /// Usually this is zero, but if something called [`Self::request_discard`] to do multi-pass layout,
1720    /// then this will be incremented for each pass.
1721    ///
1722    /// This just reads the value of [`PlatformOutput::num_completed_passes`].
1723    ///
1724    /// To know the total number of passes ever completed, use [`Self::cumulative_pass_nr`].
1725    pub fn current_pass_index(&self) -> usize {
1726        self.output(|o| o.num_completed_passes)
1727    }
1728
1729    /// Call this if there is need to repaint the UI, i.e. if you are showing an animation.
1730    ///
1731    /// If this is called at least once in a frame, then there will be another frame right after this.
1732    /// Call as many times as you wish, only one repaint will be issued.
1733    ///
1734    /// To request repaint with a delay, use [`Self::request_repaint_after`].
1735    ///
1736    /// If called from outside the UI thread, the UI thread will wake up and run,
1737    /// provided the egui integration has set that up via [`Self::set_request_repaint_callback`]
1738    /// (this will work on `eframe`).
1739    ///
1740    /// This will repaint the current viewport.
1741    #[track_caller]
1742    pub fn request_repaint(&self) {
1743        self.request_repaint_of(self.viewport_id());
1744    }
1745
1746    /// Call this if there is need to repaint the UI, i.e. if you are showing an animation.
1747    ///
1748    /// If this is called at least once in a frame, then there will be another frame right after this.
1749    /// Call as many times as you wish, only one repaint will be issued.
1750    ///
1751    /// To request repaint with a delay, use [`Self::request_repaint_after_for`].
1752    ///
1753    /// If called from outside the UI thread, the UI thread will wake up and run,
1754    /// provided the egui integration has set that up via [`Self::set_request_repaint_callback`]
1755    /// (this will work on `eframe`).
1756    ///
1757    /// This will repaint the specified viewport.
1758    #[track_caller]
1759    pub fn request_repaint_of(&self, id: ViewportId) {
1760        let cause = RepaintCause::new();
1761        self.write(|ctx| ctx.request_repaint(id, cause));
1762    }
1763
1764    /// Request repaint after at most the specified duration elapses.
1765    ///
1766    /// The backend can chose to repaint sooner, for instance if some other code called
1767    /// this method with a lower duration, or if new events arrived.
1768    ///
1769    /// The function can be multiple times, but only the *smallest* duration will be considered.
1770    /// So, if the function is called two times with `1 second` and `2 seconds`, egui will repaint
1771    /// after `1 second`
1772    ///
1773    /// This is primarily useful for applications who would like to save battery by avoiding wasted
1774    /// redraws when the app is not in focus. But sometimes the GUI of the app might become stale
1775    /// and outdated if it is not updated for too long.
1776    ///
1777    /// Let's say, something like a stopwatch widget that displays the time in seconds. You would waste
1778    /// resources repainting multiple times within the same second (when you have no input),
1779    /// just calculate the difference of duration between current time and next second change,
1780    /// and call this function, to make sure that you are displaying the latest updated time, but
1781    /// not wasting resources on needless repaints within the same second.
1782    ///
1783    /// ### Quirk:
1784    /// Duration begins at the next frame. Let's say for example that it's a very inefficient app
1785    /// and takes 500 milliseconds per frame at 2 fps. The widget / user might want a repaint in
1786    /// next 500 milliseconds. Now, app takes 1000 ms per frame (1 fps) because the backend event
1787    /// timeout takes 500 milliseconds AFTER the vsync swap buffer.
1788    /// So, it's not that we are requesting repaint within X duration. We are rather timing out
1789    /// during app idle time where we are not receiving any new input events.
1790    ///
1791    /// This repaints the current viewport.
1792    #[track_caller]
1793    pub fn request_repaint_after(&self, duration: Duration) {
1794        self.request_repaint_after_for(duration, self.viewport_id());
1795    }
1796
1797    /// Repaint after this many seconds.
1798    ///
1799    /// See [`Self::request_repaint_after`] for details.
1800    #[track_caller]
1801    pub fn request_repaint_after_secs(&self, seconds: f32) {
1802        if let Ok(duration) = std::time::Duration::try_from_secs_f32(seconds) {
1803            self.request_repaint_after(duration);
1804        }
1805    }
1806
1807    /// Request repaint after at most the specified duration elapses.
1808    ///
1809    /// The backend can chose to repaint sooner, for instance if some other code called
1810    /// this method with a lower duration, or if new events arrived.
1811    ///
1812    /// The function can be multiple times, but only the *smallest* duration will be considered.
1813    /// So, if the function is called two times with `1 second` and `2 seconds`, egui will repaint
1814    /// after `1 second`
1815    ///
1816    /// This is primarily useful for applications who would like to save battery by avoiding wasted
1817    /// redraws when the app is not in focus. But sometimes the GUI of the app might become stale
1818    /// and outdated if it is not updated for too long.
1819    ///
1820    /// Let's say, something like a stopwatch widget that displays the time in seconds. You would waste
1821    /// resources repainting multiple times within the same second (when you have no input),
1822    /// just calculate the difference of duration between current time and next second change,
1823    /// and call this function, to make sure that you are displaying the latest updated time, but
1824    /// not wasting resources on needless repaints within the same second.
1825    ///
1826    /// ### Quirk:
1827    /// Duration begins at the next frame. Let's say for example that it's a very inefficient app
1828    /// and takes 500 milliseconds per frame at 2 fps. The widget / user might want a repaint in
1829    /// next 500 milliseconds. Now, app takes 1000 ms per frame (1 fps) because the backend event
1830    /// timeout takes 500 milliseconds AFTER the vsync swap buffer.
1831    /// So, it's not that we are requesting repaint within X duration. We are rather timing out
1832    /// during app idle time where we are not receiving any new input events.
1833    ///
1834    /// This repaints the specified viewport.
1835    #[track_caller]
1836    pub fn request_repaint_after_for(&self, duration: Duration, id: ViewportId) {
1837        let cause = RepaintCause::new();
1838        self.write(|ctx| ctx.request_repaint_after(duration, id, cause));
1839    }
1840
1841    /// Was a repaint requested last pass for the current viewport?
1842    #[must_use]
1843    pub fn requested_repaint_last_pass(&self) -> bool {
1844        self.requested_repaint_last_pass_for(&self.viewport_id())
1845    }
1846
1847    /// Was a repaint requested last pass for the given viewport?
1848    #[must_use]
1849    pub fn requested_repaint_last_pass_for(&self, viewport_id: &ViewportId) -> bool {
1850        self.read(|ctx| ctx.requested_immediate_repaint_prev_pass(viewport_id))
1851    }
1852
1853    /// Has a repaint been requested for the current viewport?
1854    #[must_use]
1855    pub fn has_requested_repaint(&self) -> bool {
1856        self.has_requested_repaint_for(&self.viewport_id())
1857    }
1858
1859    /// Has a repaint been requested for the given viewport?
1860    #[must_use]
1861    pub fn has_requested_repaint_for(&self, viewport_id: &ViewportId) -> bool {
1862        self.read(|ctx| ctx.has_requested_repaint(viewport_id))
1863    }
1864
1865    /// Why are we repainting?
1866    ///
1867    /// This can be helpful in debugging why egui is constantly repainting.
1868    pub fn repaint_causes(&self) -> Vec<RepaintCause> {
1869        self.read(|ctx| {
1870            ctx.viewports
1871                .get(&ctx.viewport_id())
1872                .map(|v| v.repaint.prev_causes.clone())
1873        })
1874        .unwrap_or_default()
1875    }
1876
1877    /// For integrations: this callback will be called when an egui user calls [`Self::request_repaint`] or [`Self::request_repaint_after`].
1878    ///
1879    /// This lets you wake up a sleeping UI thread.
1880    ///
1881    /// Note that only one callback can be set. Any new call overrides the previous callback.
1882    pub fn set_request_repaint_callback(
1883        &self,
1884        callback: impl Fn(RequestRepaintInfo) + Send + Sync + 'static,
1885    ) {
1886        let callback = Box::new(callback);
1887        self.write(|ctx| ctx.request_repaint_callback = Some(callback));
1888    }
1889
1890    /// Request to discard the visual output of this pass,
1891    /// and to immediately do another one.
1892    ///
1893    /// This can be called to cover up visual glitches during a "sizing pass".
1894    /// For instance, when a [`crate::Grid`] is first shown we don't yet know the
1895    /// width and heights of its columns and rows. egui will do a best guess,
1896    /// but it will likely be wrong. Next pass it can read the sizes from the previous
1897    /// pass, and from there on the widths will be stable.
1898    /// This means the first pass will look glitchy, and ideally should not be shown to the user.
1899    /// So [`crate::Grid`] calls [`Self::request_discard`] to cover up this glitches.
1900    ///
1901    /// There is a limit to how many passes egui will perform, set by [`Options::max_passes`] (default=2).
1902    /// Therefore, the request might be declined.
1903    ///
1904    /// You can check if the current pass will be discarded with [`Self::will_discard`].
1905    ///
1906    /// You should be very conservative with when you call [`Self::request_discard`],
1907    /// as it will cause an extra ui pass, potentially leading to extra CPU use and frame judder.
1908    ///
1909    /// The given reason should be a human-readable string that explains why `request_discard`
1910    /// was called. This will be shown in certain debug situations, to help you figure out
1911    /// why a pass was discarded.
1912    #[track_caller]
1913    pub fn request_discard(&self, reason: impl Into<Cow<'static, str>>) {
1914        let cause = RepaintCause::new_reason(reason);
1915        self.output_mut(|o| o.request_discard_reasons.push(cause));
1916
1917        log::trace!(
1918            "request_discard: {}",
1919            if self.will_discard() {
1920                "allowed"
1921            } else {
1922                "denied"
1923            }
1924        );
1925    }
1926
1927    /// Will the visual output of this pass be discarded?
1928    ///
1929    /// If true, you can early-out from expensive graphics operations.
1930    ///
1931    /// See [`Self::request_discard`] for more.
1932    pub fn will_discard(&self) -> bool {
1933        self.write(|ctx| {
1934            let vp = ctx.viewport();
1935            // NOTE: `num_passes` is incremented
1936            vp.output.requested_discard()
1937                && vp.output.num_completed_passes + 1 < ctx.memory.options.max_passes.get()
1938        })
1939    }
1940}
1941
1942/// Plugins
1943impl Context {
1944    /// Call the given callback at the start of each pass of each viewport.
1945    ///
1946    /// This is a convenience wrapper around [`Self::add_plugin`].
1947    pub fn on_begin_pass(&self, debug_name: &'static str, cb: plugin::ContextCallback) {
1948        self.with_plugin(|p: &mut crate::plugin::CallbackPlugin| {
1949            p.on_begin_plugins.push((debug_name, cb));
1950        });
1951    }
1952
1953    /// Call the given callback at the end of each pass of each viewport.
1954    ///
1955    /// This is a convenience wrapper around [`Self::add_plugin`].
1956    pub fn on_end_pass(&self, debug_name: &'static str, cb: plugin::ContextCallback) {
1957        self.with_plugin(|p: &mut crate::plugin::CallbackPlugin| {
1958            p.on_end_plugins.push((debug_name, cb));
1959        });
1960    }
1961
1962    /// Register a [`Plugin`](plugin::Plugin)
1963    ///
1964    /// Plugins are called in the order they are added.
1965    ///
1966    /// A plugin of the same type can only be added once (further calls with the same type will be ignored).
1967    /// This way it's convenient to add plugins in `eframe::run_simple_native`.
1968    pub fn add_plugin(&self, plugin: impl plugin::Plugin + 'static) {
1969        let handle = plugin::PluginHandle::new(plugin);
1970
1971        let added = self.write(|ctx| ctx.plugins.add(Arc::clone(&handle)));
1972
1973        if added {
1974            handle.lock().dyn_plugin_mut().setup(self);
1975        }
1976    }
1977
1978    /// Call the provided closure with the plugin of type `T`, if it was registered.
1979    ///
1980    /// Returns `None` if the plugin was not registered.
1981    pub fn with_plugin<T: plugin::Plugin + 'static, R>(
1982        &self,
1983        f: impl FnOnce(&mut T) -> R,
1984    ) -> Option<R> {
1985        let plugin = self.read(|ctx| ctx.plugins.get(std::any::TypeId::of::<T>()));
1986        plugin.map(|plugin| f(plugin.lock().typed_plugin_mut()))
1987    }
1988
1989    /// Get a handle to the plugin of type `T`.
1990    ///
1991    /// ## Panics
1992    /// If the plugin of type `T` was not registered, this will panic.
1993    pub fn plugin<T: plugin::Plugin>(&self) -> TypedPluginHandle<T> {
1994        if let Some(plugin) = self.plugin_opt() {
1995            plugin
1996        } else {
1997            panic!("Plugin of type {:?} not found", std::any::type_name::<T>());
1998        }
1999    }
2000
2001    /// Get a handle to the plugin of type `T`, if it was registered.
2002    pub fn plugin_opt<T: plugin::Plugin>(&self) -> Option<TypedPluginHandle<T>> {
2003        let plugin = self.read(|ctx| ctx.plugins.get(std::any::TypeId::of::<T>()));
2004        plugin.map(TypedPluginHandle::new)
2005    }
2006
2007    /// Get a handle to the plugin of type `T`, or insert its default.
2008    pub fn plugin_or_default<T: plugin::Plugin + Default>(&self) -> TypedPluginHandle<T> {
2009        if let Some(plugin) = self.plugin_opt() {
2010            plugin
2011        } else {
2012            let default_plugin = T::default();
2013            self.add_plugin(default_plugin);
2014            self.plugin()
2015        }
2016    }
2017}
2018
2019impl Context {
2020    /// Tell `egui` which fonts to use.
2021    ///
2022    /// The default `egui` fonts only support latin and cyrillic alphabets,
2023    /// but you can call this to install additional fonts that support e.g. korean characters.
2024    ///
2025    /// The new fonts will become active at the start of the next pass.
2026    /// This will overwrite the existing fonts.
2027    pub fn set_fonts(&self, font_definitions: FontDefinitions) {
2028        profiling::function_scope!();
2029
2030        let update_fonts = self.read(|ctx| {
2031            // NOTE: this comparison is expensive since it checks TTF data for equality
2032            // TODO(valadaptive): add_font only checks the *names* for equality. Change this?
2033            ctx.fonts
2034                .as_ref()
2035                .is_none_or(|fonts| fonts.definitions() != &font_definitions)
2036        });
2037
2038        if update_fonts {
2039            self.memory_mut(|mem| mem.new_font_definitions = Some(font_definitions));
2040        }
2041    }
2042
2043    /// Tell `egui` which fonts to use.
2044    ///
2045    /// The default `egui` fonts only support latin and cyrillic alphabets,
2046    /// but you can call this to install additional fonts that support e.g. korean characters.
2047    ///
2048    /// The new font will become active at the start of the next pass.
2049    /// This will keep the existing fonts.
2050    pub fn add_font(&self, new_font: FontInsert) {
2051        profiling::function_scope!();
2052
2053        let mut update_fonts = true;
2054
2055        self.read(|ctx| {
2056            if let Some(current_fonts) = ctx.fonts.as_ref()
2057                && current_fonts
2058                    .definitions()
2059                    .font_data
2060                    .contains_key(&new_font.name)
2061            {
2062                update_fonts = false; // no need to update
2063            }
2064        });
2065
2066        if update_fonts {
2067            self.memory_mut(|mem| mem.add_fonts.push(new_font));
2068        }
2069    }
2070
2071    /// Does the OS use dark or light mode?
2072    /// This is used when the theme preference is set to [`crate::ThemePreference::System`].
2073    pub fn system_theme(&self) -> Option<Theme> {
2074        self.memory(|mem| mem.options.system_theme)
2075    }
2076
2077    /// The [`Theme`] used to select the appropriate [`Style`] (dark or light)
2078    /// used by all subsequent popups, menus, etc.
2079    pub fn theme(&self) -> Theme {
2080        self.options(|opt| opt.theme())
2081    }
2082
2083    /// The [`Theme`] used to select between dark and light [`Self::style`]
2084    /// as the active style used by all subsequent popups, menus, etc.
2085    ///
2086    /// Example:
2087    /// ```
2088    /// # let mut ctx = egui::Context::default();
2089    /// ctx.set_theme(egui::Theme::Light); // Switch to light mode
2090    /// ```
2091    pub fn set_theme(&self, theme_preference: impl Into<crate::ThemePreference>) {
2092        self.options_mut(|opt| opt.theme_preference = theme_preference.into());
2093    }
2094
2095    /// The currently active [`Style`] used by all subsequent popups, menus, etc.
2096    pub fn global_style(&self) -> Arc<Style> {
2097        self.options(|opt| Arc::clone(opt.style()))
2098    }
2099
2100    /// The currently active [`Style`] used by all subsequent popups, menus, etc.
2101    #[deprecated = "Renamed to `global_style` to avoid confusion with `ui.style()`"]
2102    pub fn style(&self) -> Arc<Style> {
2103        self.options(|opt| Arc::clone(opt.style()))
2104    }
2105
2106    /// Mutate the currently active [`Style`] used by all subsequent popups, menus, etc.
2107    /// Use [`Self::all_styles_mut`] to mutate both dark and light mode styles.
2108    ///
2109    /// Example:
2110    /// ```
2111    /// # let mut ctx = egui::Context::default();
2112    /// ctx.global_style_mut(|style| {
2113    ///     style.spacing.item_spacing = egui::vec2(10.0, 20.0);
2114    /// });
2115    /// ```
2116    pub fn global_style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {
2117        self.options_mut(|opt| mutate_style(Arc::make_mut(opt.style_mut())));
2118    }
2119
2120    /// Mutate the currently active [`Style`] used by all subsequent popups, menus, etc.
2121    /// Use [`Self::all_styles_mut`] to mutate both dark and light mode styles.
2122    ///
2123    /// Example:
2124    /// ```
2125    /// # let mut ctx = egui::Context::default();
2126    /// ctx.global_style_mut(|style| {
2127    ///     style.spacing.item_spacing = egui::vec2(10.0, 20.0);
2128    /// });
2129    /// ```
2130    #[deprecated = "Renamed to `global_style_mut` to avoid confusion with `ui.style_mut()`"]
2131    pub fn style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {
2132        self.options_mut(|opt| mutate_style(Arc::make_mut(opt.style_mut())));
2133    }
2134
2135    /// The currently active [`Style`] used by all new popups, menus, etc.
2136    ///
2137    /// Use [`Self::all_styles_mut`] to mutate both dark and light mode styles.
2138    ///
2139    /// You can also change this using [`Self::global_style_mut`].
2140    ///
2141    /// You can use [`Ui::style_mut`] to change the style of a single [`Ui`].
2142    pub fn set_global_style(&self, style: impl Into<Arc<Style>>) {
2143        self.options_mut(|opt| *opt.style_mut() = style.into());
2144    }
2145
2146    /// The currently active [`Style`] used by all new popups, menus, etc.
2147    ///
2148    /// Use [`Self::all_styles_mut`] to mutate both dark and light mode styles.
2149    ///
2150    /// You can also change this using [`Self::style_mut`].
2151    ///
2152    /// You can use [`Ui::style_mut`] to change the style of a single [`Ui`].
2153    #[deprecated = "Renamed to `set_global_style` to avoid confusion with `ui.set_style()`"]
2154    pub fn set_style(&self, style: impl Into<Arc<Style>>) {
2155        self.options_mut(|opt| *opt.style_mut() = style.into());
2156    }
2157
2158    /// Mutate the [`Style`]s used by all subsequent popups, menus, etc. in both dark and light mode.
2159    ///
2160    /// Example:
2161    /// ```
2162    /// # let mut ctx = egui::Context::default();
2163    /// ctx.all_styles_mut(|style| {
2164    ///     style.spacing.item_spacing = egui::vec2(10.0, 20.0);
2165    /// });
2166    /// ```
2167    pub fn all_styles_mut(&self, mut mutate_style: impl FnMut(&mut Style)) {
2168        self.options_mut(|opt| {
2169            mutate_style(Arc::make_mut(&mut opt.dark_style));
2170            mutate_style(Arc::make_mut(&mut opt.light_style));
2171        });
2172    }
2173
2174    /// The [`Style`] used by all subsequent popups, menus, etc.
2175    pub fn style_of(&self, theme: Theme) -> Arc<Style> {
2176        self.options(|opt| match theme {
2177            Theme::Dark => Arc::clone(&opt.dark_style),
2178            Theme::Light => Arc::clone(&opt.light_style),
2179        })
2180    }
2181
2182    /// Mutate the [`Style`] used by all subsequent popups, menus, etc.
2183    ///
2184    /// Example:
2185    /// ```
2186    /// # let mut ctx = egui::Context::default();
2187    /// ctx.style_mut_of(egui::Theme::Dark, |style| {
2188    ///     style.spacing.item_spacing = egui::vec2(10.0, 20.0);
2189    /// });
2190    /// ```
2191    pub fn style_mut_of(&self, theme: Theme, mutate_style: impl FnOnce(&mut Style)) {
2192        self.options_mut(|opt| match theme {
2193            Theme::Dark => mutate_style(Arc::make_mut(&mut opt.dark_style)),
2194            Theme::Light => mutate_style(Arc::make_mut(&mut opt.light_style)),
2195        });
2196    }
2197
2198    /// The [`Style`] used by all new popups, menus, etc.
2199    /// Use [`Self::set_theme`] to choose between dark and light mode.
2200    ///
2201    /// You can also change this using [`Self::style_mut_of`].
2202    ///
2203    /// You can use [`Ui::style_mut`] to change the style of a single [`Ui`].
2204    pub fn set_style_of(&self, theme: Theme, style: impl Into<Arc<Style>>) {
2205        let style = style.into();
2206        self.options_mut(|opt| match theme {
2207            Theme::Dark => opt.dark_style = style,
2208            Theme::Light => opt.light_style = style,
2209        });
2210    }
2211
2212    /// The [`crate::Visuals`] used by all subsequent popups, menus, etc.
2213    ///
2214    /// You can also use [`Ui::visuals_mut`] to change the visuals of a single [`Ui`].
2215    ///
2216    /// Example:
2217    /// ```
2218    /// # let mut ctx = egui::Context::default();
2219    /// ctx.set_visuals_of(egui::Theme::Dark, egui::Visuals { panel_fill: egui::Color32::RED, ..Default::default() });
2220    /// ```
2221    pub fn set_visuals_of(&self, theme: Theme, visuals: crate::Visuals) {
2222        self.style_mut_of(theme, |style| style.visuals = visuals);
2223    }
2224
2225    /// The [`crate::Visuals`] used by all subsequent popups, menus, etc.
2226    ///
2227    /// You can also use [`Ui::visuals_mut`] to change the visuals of a single [`Ui`].
2228    ///
2229    /// Example:
2230    /// ```
2231    /// # let mut ctx = egui::Context::default();
2232    /// ctx.set_visuals(egui::Visuals { panel_fill: egui::Color32::RED, ..Default::default() });
2233    /// ```
2234    pub fn set_visuals(&self, visuals: crate::Visuals) {
2235        self.style_mut_of(self.theme(), |style| style.visuals = visuals);
2236    }
2237
2238    /// The number of physical pixels for each logical point.
2239    ///
2240    /// This is calculated as [`Self::zoom_factor`] * [`Self::native_pixels_per_point`]
2241    #[inline(always)]
2242    pub fn pixels_per_point(&self) -> f32 {
2243        self.input(|i| i.pixels_per_point)
2244    }
2245
2246    /// Set the number of physical pixels for each logical point.
2247    /// Will become active at the start of the next pass.
2248    ///
2249    /// This will actually translate to a call to [`Self::set_zoom_factor`].
2250    pub fn set_pixels_per_point(&self, pixels_per_point: f32) {
2251        if pixels_per_point != self.pixels_per_point() {
2252            self.set_zoom_factor(pixels_per_point / self.native_pixels_per_point().unwrap_or(1.0));
2253        }
2254    }
2255
2256    /// The number of physical pixels for each logical point on this monitor.
2257    ///
2258    /// This is given as input to egui via [`crate::ViewportInfo::native_pixels_per_point`]
2259    /// and cannot be changed.
2260    #[inline(always)]
2261    pub fn native_pixels_per_point(&self) -> Option<f32> {
2262        self.input(|i| i.viewport().native_pixels_per_point)
2263    }
2264
2265    /// Global zoom factor of the UI.
2266    ///
2267    /// This is used to calculate the `pixels_per_point`
2268    /// for the UI as `pixels_per_point = zoom_factor * native_pixels_per_point`.
2269    ///
2270    /// The default is 1.0.
2271    /// Make larger to make everything larger.
2272    #[inline(always)]
2273    pub fn zoom_factor(&self) -> f32 {
2274        self.options(|o| o.zoom_factor)
2275    }
2276
2277    /// Sets zoom factor of the UI.
2278    /// Will become active at the start of the next pass.
2279    ///
2280    /// Note that calling this will not update [`Self::zoom_factor`] until the end of the pass.
2281    ///
2282    /// This is used to calculate the `pixels_per_point`
2283    /// for the UI as `pixels_per_point = zoom_fator * native_pixels_per_point`.
2284    ///
2285    /// The default is 1.0.
2286    /// Make larger to make everything larger.
2287    ///
2288    /// It is better to call this than modifying
2289    /// [`Options::zoom_factor`].
2290    #[inline(always)]
2291    pub fn set_zoom_factor(&self, zoom_factor: f32) {
2292        let cause = RepaintCause::new();
2293        self.write(|ctx| {
2294            if ctx.memory.options.zoom_factor != zoom_factor {
2295                ctx.new_zoom_factor = Some(zoom_factor);
2296                #[expect(clippy::iter_over_hash_type)]
2297                for viewport_id in ctx.all_viewport_ids() {
2298                    ctx.request_repaint(viewport_id, cause.clone());
2299                }
2300            }
2301        });
2302    }
2303
2304    /// Allocate a texture.
2305    ///
2306    /// This is for advanced users.
2307    /// Most users should use [`crate::Ui::image`] or [`Self::try_load_texture`]
2308    /// instead.
2309    ///
2310    /// In order to display an image you must convert it to a texture using this function.
2311    /// The function will hand over the image data to the egui backend, which will
2312    /// upload it to the GPU.
2313    ///
2314    /// ⚠️ Make sure to only call this ONCE for each image, i.e. NOT in your main GUI code.
2315    /// The call is NOT immediate safe.
2316    ///
2317    /// The given name can be useful for later debugging, and will be visible if you call [`Self::texture_ui`].
2318    ///
2319    /// For how to load an image, see [`crate::ImageData`] and [`crate::ColorImage::from_rgba_unmultiplied`].
2320    ///
2321    /// ```
2322    /// struct MyImage {
2323    ///     texture: Option<egui::TextureHandle>,
2324    /// }
2325    ///
2326    /// impl MyImage {
2327    ///     fn ui(&mut self, ui: &mut egui::Ui) {
2328    ///         let texture: &egui::TextureHandle = self.texture.get_or_insert_with(|| {
2329    ///             // Load the texture only once.
2330    ///             ui.ctx().load_texture(
2331    ///                 "my-image",
2332    ///                 egui::ColorImage::example(),
2333    ///                 Default::default()
2334    ///             )
2335    ///         });
2336    ///
2337    ///         // Show the image:
2338    ///         ui.image((texture.id(), texture.size_vec2()));
2339    ///     }
2340    /// }
2341    /// ```
2342    ///
2343    /// See also [`crate::ImageData`], [`crate::Ui::image`] and [`crate::Image`].
2344    pub fn load_texture(
2345        &self,
2346        name: impl Into<String>,
2347        image: impl Into<ImageData>,
2348        options: TextureOptions,
2349    ) -> TextureHandle {
2350        let name = name.into();
2351        let image = image.into();
2352        let max_texture_side = self.input(|i| i.max_texture_side);
2353        debug_assert!(
2354            image.width() <= max_texture_side && image.height() <= max_texture_side,
2355            "Texture {:?} has size {}x{}, but the maximum texture side is {}",
2356            name,
2357            image.width(),
2358            image.height(),
2359            max_texture_side
2360        );
2361        let tex_mngr = self.tex_manager();
2362        let tex_id = tex_mngr.write().alloc(name, image, options);
2363        TextureHandle::new(tex_mngr, tex_id)
2364    }
2365
2366    /// Low-level texture manager.
2367    ///
2368    /// In general it is easier to use [`Self::load_texture`] and [`TextureHandle`].
2369    ///
2370    /// You can show stats about the allocated textures using [`Self::texture_ui`].
2371    pub fn tex_manager(&self) -> Arc<RwLock<epaint::textures::TextureManager>> {
2372        self.read(|ctx| Arc::clone(&ctx.tex_manager.0))
2373    }
2374
2375    // ---------------------------------------------------------------------
2376
2377    /// Constrain the position of a window/area so it fits within the provided boundary.
2378    pub(crate) fn constrain_window_rect_to_area(window: Rect, area: Rect) -> Rect {
2379        let mut pos = window.min;
2380
2381        // Constrain to screen, unless window is too large to fit:
2382        let margin_x = (window.width() - area.width()).at_least(0.0);
2383        let margin_y = (window.height() - area.height()).at_least(0.0);
2384
2385        pos.x = pos.x.at_most(area.right() + margin_x - window.width()); // move left if needed
2386        pos.x = pos.x.at_least(area.left() - margin_x); // move right if needed
2387        pos.y = pos.y.at_most(area.bottom() + margin_y - window.height()); // move right if needed
2388        pos.y = pos.y.at_least(area.top() - margin_y); // move down if needed
2389
2390        Rect::from_min_size(pos, window.size()).round_ui()
2391    }
2392}
2393
2394impl Context {
2395    /// Call at the end of each frame if you called [`Context::begin_pass`].
2396    #[must_use]
2397    pub fn end_pass(&self) -> FullOutput {
2398        profiling::function_scope!();
2399
2400        if self.options(|o| o.zoom_with_keyboard) {
2401            crate::gui_zoom::zoom_with_keyboard(self);
2402        }
2403
2404        for shortcut in self.options(|o| o.quit_shortcuts.clone()) {
2405            if self.input_mut(|i| i.consume_shortcut(&shortcut)) {
2406                self.send_viewport_cmd(ViewportCommand::Close);
2407            }
2408        }
2409
2410        #[cfg(debug_assertions)]
2411        self.debug_painting();
2412
2413        let mut output = self.write(|ctx| ctx.end_pass());
2414
2415        let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
2416        plugins.on_output(&mut output);
2417
2418        output
2419    }
2420
2421    /// Call at the end of each frame if you called [`Context::begin_pass`].
2422    #[must_use]
2423    #[deprecated = "Renamed end_pass"]
2424    pub fn end_frame(&self) -> FullOutput {
2425        self.end_pass()
2426    }
2427
2428    /// Called at the end of the pass.
2429    #[cfg(debug_assertions)]
2430    fn debug_painting(&self) {
2431        #![expect(clippy::iter_over_hash_type)] // ok to be sloppy in debug painting
2432
2433        let paint_widget = |widget: &WidgetRect, text: &str, color: Color32| {
2434            let rect = widget.interact_rect;
2435            if rect.is_positive() {
2436                let painter = Painter::new(self.clone(), widget.layer_id, Rect::EVERYTHING);
2437                painter.debug_rect(rect, color, text);
2438            }
2439        };
2440
2441        let paint_widget_id = |id: Id, text: &str, color: Color32| {
2442            if let Some(widget) =
2443                self.write(|ctx| ctx.viewport().this_pass.widgets.get(id).copied())
2444            {
2445                let text = format!("{text} - {id:?}");
2446                paint_widget(&widget, &text, color);
2447            }
2448        };
2449
2450        if self.global_style().debug.show_interactive_widgets {
2451            // Show all interactive widgets:
2452            let rects = self.write(|ctx| ctx.viewport().this_pass.widgets.clone());
2453            for (layer_id, rects) in rects.layers() {
2454                let painter = Painter::new(self.clone(), *layer_id, Rect::EVERYTHING);
2455                for rect in rects {
2456                    if rect.sense.interactive() {
2457                        let (color, text) = if rect.sense.senses_click() && rect.sense.senses_drag()
2458                        {
2459                            (Color32::from_rgb(0x88, 0, 0x88), "click+drag")
2460                        } else if rect.sense.senses_click() {
2461                            (Color32::from_rgb(0x88, 0, 0), "click")
2462                        } else if rect.sense.senses_drag() {
2463                            (Color32::from_rgb(0, 0, 0x88), "drag")
2464                        } else {
2465                            // unreachable since we only show interactive
2466                            (Color32::from_rgb(0, 0, 0x88), "hover")
2467                        };
2468                        painter.debug_rect(rect.interact_rect, color, text);
2469                    }
2470                }
2471            }
2472
2473            // Show the ones actually interacted with:
2474            {
2475                let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());
2476                let InteractionSnapshot {
2477                    clicked,
2478                    long_touched: _,
2479                    drag_started: _,
2480                    dragged,
2481                    drag_stopped: _,
2482                    contains_pointer,
2483                    hovered,
2484                } = interact_widgets;
2485
2486                if true {
2487                    for &id in &contains_pointer {
2488                        paint_widget_id(id, "contains_pointer", Color32::BLUE);
2489                    }
2490
2491                    let widget_rects = self.write(|w| w.viewport().this_pass.widgets.clone());
2492
2493                    let mut contains_pointer: Vec<Id> = contains_pointer.iter().copied().collect();
2494                    contains_pointer.sort_by_key(|&id| {
2495                        widget_rects
2496                            .order(id)
2497                            .map(|(layer_id, order_in_layer)| (layer_id.order, order_in_layer))
2498                    });
2499
2500                    let mut debug_text = "Widgets in order:\n".to_owned();
2501                    for id in contains_pointer {
2502                        let mut widget_text = format!("{id:?}");
2503                        if let Some(rect) = widget_rects.get(id) {
2504                            widget_text +=
2505                                &format!(" {:?} {:?} {:?}", rect.layer_id, rect.rect, rect.sense);
2506                        }
2507                        if let Some(info) = widget_rects.info(id) {
2508                            widget_text += &format!(" {info:?}");
2509                        }
2510                        debug_text += &format!("{widget_text}\n");
2511                    }
2512                    self.debug_text(debug_text);
2513                }
2514                if true {
2515                    for widget in hovered {
2516                        paint_widget_id(widget, "hovered", Color32::WHITE);
2517                    }
2518                }
2519                if let Some(widget) = clicked {
2520                    paint_widget_id(widget, "clicked", Color32::RED);
2521                }
2522                if let Some(widget) = dragged {
2523                    paint_widget_id(widget, "dragged", Color32::GREEN);
2524                }
2525            }
2526        }
2527
2528        if self.global_style().debug.show_widget_hits {
2529            let hits = self.write(|ctx| ctx.viewport().hits.clone());
2530            let WidgetHits {
2531                close,
2532                contains_pointer,
2533                click,
2534                drag,
2535            } = hits;
2536
2537            if false {
2538                for widget in &close {
2539                    paint_widget(widget, "close", Color32::from_gray(70));
2540                }
2541            }
2542            if true {
2543                for widget in &contains_pointer {
2544                    paint_widget(widget, "contains_pointer", Color32::BLUE);
2545                }
2546            }
2547            if let Some(widget) = &click {
2548                paint_widget(widget, "click", Color32::RED);
2549            }
2550            if let Some(widget) = &drag {
2551                paint_widget(widget, "drag", Color32::GREEN);
2552            }
2553        }
2554
2555        if self.global_style().debug.show_focused_widget
2556            && let Some(focused_id) = self.memory(|mem| mem.focused())
2557        {
2558            paint_widget_id(focused_id, "focused", Color32::PURPLE);
2559        }
2560
2561        if let Some(debug_rect) = self.pass_state_mut(|fs| fs.debug_rect.take()) {
2562            debug_rect.paint(&self.debug_painter());
2563        }
2564
2565        let num_multipass_in_row = self.viewport(|vp| vp.num_multipass_in_row);
2566        if 3 <= num_multipass_in_row {
2567            // If you see this message, it means we've been paying the cost of multi-pass for multiple frames in a row.
2568            // This is likely a bug. `request_discard` should only be called in rare situations, when some layout changes.
2569
2570            let mut warning = format!(
2571                "egui PERF WARNING: request_discard has been called {num_multipass_in_row} frames in a row"
2572            );
2573            self.viewport(|vp| {
2574                for reason in &vp.output.request_discard_reasons {
2575                    warning += &format!("\n  {reason}");
2576                }
2577            });
2578
2579            self.debug_painter()
2580                .debug_text(Pos2::ZERO, Align2::LEFT_TOP, Color32::RED, warning);
2581        }
2582    }
2583}
2584
2585impl ContextImpl {
2586    fn end_pass(&mut self) -> FullOutput {
2587        let ended_viewport_id = self.viewport_id();
2588        let viewport = self.viewports.entry(ended_viewport_id).or_default();
2589        let pixels_per_point = viewport.input.pixels_per_point;
2590
2591        self.loaders.end_pass(viewport.repaint.cumulative_pass_nr);
2592
2593        viewport.repaint.cumulative_pass_nr += 1;
2594
2595        self.memory.end_pass(&viewport.this_pass.used_ids);
2596
2597        if let Some(fonts) = self.fonts.as_mut() {
2598            let tex_mngr = &mut self.tex_manager.0.write();
2599            if let Some(font_image_delta) = fonts.font_image_delta() {
2600                // A partial font atlas update, e.g. a new glyph has been entered.
2601                tex_mngr.set(TextureId::default(), font_image_delta);
2602            }
2603        }
2604
2605        // Inform the backend of all textures that have been updated (including font atlas).
2606        let textures_delta = self.tex_manager.0.write().take_delta();
2607
2608        let mut platform_output: PlatformOutput = std::mem::take(&mut viewport.output);
2609
2610        {
2611            profiling::scope!("accesskit");
2612            let state = viewport.this_pass.accesskit_state.take();
2613            if let Some(state) = state {
2614                let root_id = crate::accesskit_root_id().accesskit_id();
2615                let nodes = {
2616                    state
2617                        .nodes
2618                        .into_iter()
2619                        .map(|(id, node)| (id.accesskit_id(), node))
2620                        .collect()
2621                };
2622                let focus_id = self
2623                    .memory
2624                    .focused()
2625                    .map_or(root_id, |id| id.accesskit_id());
2626                platform_output.accesskit_update = Some(accesskit::TreeUpdate {
2627                    nodes,
2628                    tree: Some(accesskit::Tree::new(root_id)),
2629                    tree_id: accesskit::TreeId::ROOT,
2630                    focus: focus_id,
2631                });
2632            }
2633        }
2634
2635        let shapes = viewport
2636            .graphics
2637            .drain(self.memory.areas().order(), &self.memory.to_global);
2638
2639        let mut repaint_needed = false;
2640
2641        if self.memory.options.repaint_on_widget_change {
2642            profiling::scope!("compare-widget-rects");
2643            #[allow(clippy::allow_attributes, clippy::collapsible_if)] // false positive on wasm
2644            if viewport.prev_pass.widgets != viewport.this_pass.widgets {
2645                repaint_needed = true; // Some widget has moved
2646            }
2647        }
2648
2649        #[cfg(debug_assertions)]
2650        let shapes = if self.memory.options.style().debug.warn_if_rect_changes_id {
2651            let mut shapes = shapes;
2652            warn_if_rect_changes_id(
2653                &mut shapes,
2654                &viewport.prev_pass.widgets,
2655                &viewport.this_pass.widgets,
2656            );
2657            shapes
2658        } else {
2659            shapes
2660        };
2661
2662        std::mem::swap(&mut viewport.prev_pass, &mut viewport.this_pass);
2663
2664        if repaint_needed {
2665            self.request_repaint(ended_viewport_id, RepaintCause::new());
2666        }
2667        //  -------------------
2668
2669        let all_viewport_ids = self.all_viewport_ids();
2670
2671        self.last_viewport = ended_viewport_id;
2672
2673        self.viewports.retain(|&id, viewport| {
2674            if id == ViewportId::ROOT {
2675                return true; // never remove the root
2676            }
2677
2678            let parent = *self.viewport_parents.entry(id).or_default();
2679
2680            if !all_viewport_ids.contains(&parent) {
2681                log::debug!(
2682                    "Removing viewport {:?} ({:?}): the parent is gone",
2683                    id,
2684                    viewport.builder.title
2685                );
2686
2687                return false;
2688            }
2689
2690            let is_our_child = parent == ended_viewport_id && id != ViewportId::ROOT;
2691            if is_our_child {
2692                if !viewport.used {
2693                    log::debug!(
2694                        "Removing viewport {:?} ({:?}): it was never used this pass",
2695                        id,
2696                        viewport.builder.title
2697                    );
2698
2699                    return false; // Only keep children that have been updated this pass
2700                }
2701
2702                viewport.used = false; // reset so we can check again next pass
2703            }
2704
2705            true
2706        });
2707
2708        // If we are an immediate viewport, this will resume the previous viewport.
2709        self.viewport_stack.pop();
2710
2711        // The last viewport is not necessarily the root viewport,
2712        // just the top _immediate_ viewport.
2713        let is_last = self.viewport_stack.is_empty();
2714
2715        let viewport_output = self
2716            .viewports
2717            .iter_mut()
2718            .map(|(&id, viewport)| {
2719                let parent = *self.viewport_parents.entry(id).or_default();
2720                let commands = if is_last {
2721                    // Let the primary immediate viewport handle the commands of its children too.
2722                    // This can make things easier for the backend, as otherwise we may get commands
2723                    // that affect a viewport while its egui logic is running.
2724                    std::mem::take(&mut viewport.commands)
2725                } else {
2726                    vec![]
2727                };
2728
2729                (
2730                    id,
2731                    ViewportOutput {
2732                        parent,
2733                        class: viewport.class,
2734                        builder: viewport.builder.clone(),
2735                        viewport_ui_cb: viewport.viewport_ui_cb.clone(),
2736                        commands,
2737                        repaint_delay: viewport.repaint.repaint_delay,
2738                    },
2739                )
2740            })
2741            .collect();
2742
2743        if is_last {
2744            // Remove dead viewports:
2745            self.viewports.retain(|id, _| all_viewport_ids.contains(id));
2746            debug_assert!(
2747                self.viewports.contains_key(&ViewportId::ROOT),
2748                "Bug in egui: we removed the root viewport"
2749            );
2750            self.viewport_parents
2751                .retain(|id, _| all_viewport_ids.contains(id));
2752        } else {
2753            let viewport_id = self.viewport_id();
2754            self.memory.set_viewport_id(viewport_id);
2755        }
2756
2757        platform_output.num_completed_passes += 1;
2758
2759        FullOutput {
2760            platform_output,
2761            textures_delta,
2762            shapes,
2763            pixels_per_point,
2764            viewport_output,
2765        }
2766    }
2767}
2768
2769impl Context {
2770    /// Tessellate the given shapes into triangle meshes.
2771    ///
2772    /// `pixels_per_point` is used for feathering (anti-aliasing).
2773    /// For this you can use [`FullOutput::pixels_per_point`], [`Self::pixels_per_point`],
2774    /// or whatever is appropriate for your viewport.
2775    pub fn tessellate(
2776        &self,
2777        shapes: Vec<ClippedShape>,
2778        pixels_per_point: f32,
2779    ) -> Vec<ClippedPrimitive> {
2780        profiling::function_scope!();
2781
2782        // A tempting optimization is to reuse the tessellation from last frame if the
2783        // shapes are the same, but just comparing the shapes takes about 50% of the time
2784        // it takes to tessellate them, so it is not a worth optimization.
2785
2786        self.write(|ctx| {
2787            let tessellation_options = ctx.memory.options.tessellation_options;
2788            let texture_atlas = if let Some(fonts) = ctx.fonts.as_ref() {
2789                fonts.texture_atlas()
2790            } else {
2791                log::warn!("No font size matching {pixels_per_point} pixels per point found.");
2792                ctx.fonts
2793                    .iter()
2794                    .next()
2795                    .expect("No fonts loaded")
2796                    .texture_atlas()
2797            };
2798
2799            let paint_stats = PaintStats::from_shapes(&shapes);
2800            let clipped_primitives = {
2801                profiling::scope!("tessellator::tessellate_shapes");
2802                tessellator::Tessellator::new(
2803                    pixels_per_point,
2804                    tessellation_options,
2805                    texture_atlas.size(),
2806                    texture_atlas.prepared_discs(),
2807                )
2808                .tessellate_shapes(shapes)
2809            };
2810            ctx.paint_stats = paint_stats.with_clipped_primitives(&clipped_primitives);
2811            clipped_primitives
2812        })
2813    }
2814
2815    // ---------------------------------------------------------------------
2816
2817    /// Returns the position and size of the egui area that is safe for content rendering.
2818    ///
2819    /// Returns [`Self::viewport_rect`] minus areas that might be partially covered by, for example,
2820    /// the OS status bar or display notches.
2821    ///
2822    /// If you want to render behind e.g. the dynamic island on iOS, use [`Self::viewport_rect`].
2823    pub fn content_rect(&self) -> Rect {
2824        self.input(|i| i.content_rect()).round_ui()
2825    }
2826
2827    /// Returns the position and size of the full area available to egui
2828    ///
2829    /// This includes reas that might be partially covered by, for example, the OS status bar or
2830    /// display notches. See [`Self::content_rect`] to get a rect that is safe for content.
2831    ///
2832    /// This rectangle includes e.g. the dynamic island on iOS.
2833    /// If you want to only render _below_ the that (not behind), then you should use
2834    /// [`Self::content_rect`] instead.
2835    ///
2836    /// See also [`RawInput::safe_area_insets`].
2837    pub fn viewport_rect(&self) -> Rect {
2838        self.input(|i| i.viewport_rect()).round_ui()
2839    }
2840
2841    /// Position and size of the egui area.
2842    #[deprecated(
2843        note = "screen_rect has been split into viewport_rect() and content_rect(). You likely should use content_rect()"
2844    )]
2845    pub fn screen_rect(&self) -> Rect {
2846        self.input(|i| i.content_rect()).round_ui()
2847    }
2848
2849    /// How much space is still available after panels have been added.
2850    #[deprecated = "Use content_rect (or viewport_rect) instead"]
2851    pub fn available_rect(&self) -> Rect {
2852        #[expect(deprecated)] // legacy
2853        self.pass_state(|s| s.available_rect()).round_ui()
2854    }
2855
2856    /// How much space is used by windows and the top-level [`Ui`].
2857    pub fn globally_used_rect(&self) -> Rect {
2858        self.write(|ctx| {
2859            let viewport = ctx.viewport();
2860            let root_ui_min_rect =
2861                (viewport.this_pass.root_ui_min_rect).or(viewport.prev_pass.root_ui_min_rect);
2862
2863            let mut used = root_ui_min_rect.unwrap_or_else(|| {
2864                #[expect(deprecated)] // legacy
2865                ctx.viewport().this_pass.used_by_panels
2866            });
2867            for (_id, window) in ctx.memory.areas().visible_windows() {
2868                used |= window.rect();
2869            }
2870            used.round_ui()
2871        })
2872    }
2873
2874    /// How much space is used by windows and the top-level [`Ui`].
2875    #[deprecated = "Renamed to globally_used_rect"]
2876    pub fn used_rect(&self) -> Rect {
2877        self.globally_used_rect()
2878    }
2879
2880    /// How much space is used by windows and the top-level [`Ui`].
2881    ///
2882    /// You can shrink your egui area to this size and still fit all egui components.
2883    #[deprecated = "Use globally_used_rect instead"]
2884    pub fn used_size(&self) -> Vec2 {
2885        (self.globally_used_rect().max - Pos2::ZERO).round_ui()
2886    }
2887
2888    // ---------------------------------------------------------------------
2889
2890    /// Is the pointer (mouse/touch) over any egui area?
2891    pub fn is_pointer_over_egui(&self) -> bool {
2892        let pointer_pos = self.input(|i| i.pointer.interact_pos());
2893        let Some(pointer_pos) = pointer_pos else {
2894            return false;
2895        };
2896        let Some(layer) = self.layer_id_at(pointer_pos) else {
2897            return false;
2898        };
2899        if layer.order == Order::Background {
2900            let root_ui_available_rect = self
2901                .pass_state(|state| state.root_ui_available_rect)
2902                .or_else(|| self.prev_pass_state(|state| state.root_ui_available_rect));
2903
2904            if let Some(root_ui_available_rect) = root_ui_available_rect {
2905                // Modern `run_ui` code
2906                !root_ui_available_rect.contains(pointer_pos)
2907            } else {
2908                // Legacy code
2909                #[expect(deprecated)]
2910                !self.pass_state(|state| state.unused_rect.contains(pointer_pos))
2911            }
2912        } else {
2913            true
2914        }
2915    }
2916
2917    /// Is the pointer (mouse/touch) over any egui area?
2918    #[deprecated = "Renamed to is_pointer_over_egui"]
2919    pub fn is_pointer_over_area(&self) -> bool {
2920        self.is_pointer_over_egui()
2921    }
2922
2923    /// True if egui is currently interested in the pointer (mouse or touch).
2924    ///
2925    /// Could be the pointer is hovering over a [`crate::Window`] or the user is dragging a widget.
2926    /// If `false`, the pointer is outside of any egui area and so
2927    /// you may be interested in what it is doing (e.g. controlling your game).
2928    /// Returns `false` if a drag started outside of egui and then moved over an egui area.
2929    pub fn egui_wants_pointer_input(&self) -> bool {
2930        self.egui_is_using_pointer()
2931            || (self.is_pointer_over_egui() && !self.input(|i| i.pointer.any_down()))
2932    }
2933
2934    /// True if egui is currently interested in the pointer (mouse or touch).
2935    ///
2936    /// Could be the pointer is hovering over a [`crate::Window`] or the user is dragging a widget.
2937    /// If `false`, the pointer is outside of any egui area and so
2938    /// you may be interested in what it is doing (e.g. controlling your game).
2939    /// Returns `false` if a drag started outside of egui and then moved over an egui area.
2940    #[deprecated = "Renamed to egui_wants_pointer_input"]
2941    pub fn wants_pointer_input(&self) -> bool {
2942        self.egui_wants_pointer_input()
2943    }
2944
2945    /// Is egui currently using the pointer position (e.g. dragging a slider)?
2946    ///
2947    /// NOTE: this will return `false` if the pointer is just hovering over an egui area.
2948    pub fn egui_is_using_pointer(&self) -> bool {
2949        self.memory(|m| m.interaction().is_using_pointer())
2950    }
2951
2952    /// Is egui currently using the pointer position (e.g. dragging a slider)?
2953    ///
2954    /// NOTE: this will return `false` if the pointer is just hovering over an egui area.
2955    #[deprecated = "Renamed to egui_is_using_pointer"]
2956    pub fn is_using_pointer(&self) -> bool {
2957        self.egui_is_using_pointer()
2958    }
2959
2960    /// If `true`, egui is currently listening on text input (e.g. typing text in a [`crate::TextEdit`]).
2961    pub fn egui_wants_keyboard_input(&self) -> bool {
2962        self.memory(|m| m.focused().is_some())
2963    }
2964
2965    /// If `true`, egui is currently listening on text input (e.g. typing text in a [`crate::TextEdit`]).
2966    #[deprecated = "Renamed to egui_wants_keyboard_input"]
2967    pub fn wants_keyboard_input(&self) -> bool {
2968        self.egui_wants_keyboard_input()
2969    }
2970
2971    /// Is the currently focused widget a text edit?
2972    pub fn text_edit_focused(&self) -> bool {
2973        if let Some(id) = self.memory(|mem| mem.focused()) {
2974            crate::text_edit::TextEditState::load(self, id).is_some()
2975        } else {
2976            false
2977        }
2978    }
2979
2980    /// Highlight this widget, to make it look like it is hovered, even if it isn't.
2981    ///
2982    /// If you call this after the widget has been fully rendered,
2983    /// then it won't be highlighted until the next ui pass.
2984    ///
2985    /// See also [`Response::highlight`].
2986    pub fn highlight_widget(&self, id: Id) {
2987        self.pass_state_mut(|fs| fs.highlight_next_pass.insert(id));
2988    }
2989
2990    /// Is an egui context menu open?
2991    ///
2992    /// This only works with the old, deprecated [`crate::menu`] API.
2993    #[expect(deprecated)]
2994    #[deprecated = "Use `any_popup_open` instead"]
2995    pub fn is_context_menu_open(&self) -> bool {
2996        self.data(|d| {
2997            d.get_temp::<crate::menu::BarState>(crate::menu::CONTEXT_MENU_ID_STR.into())
2998                .is_some_and(|state| state.has_root())
2999        })
3000    }
3001
3002    /// Is a popup or (context) menu open?
3003    ///
3004    /// Will return false for [`crate::Tooltip`]s (which are technically popups as well).
3005    pub fn any_popup_open(&self) -> bool {
3006        self.pass_state_mut(|fs| {
3007            fs.layers
3008                .values()
3009                .any(|layer| !layer.open_popups.is_empty())
3010        })
3011    }
3012
3013    /// Is a popup or (context) menu open?
3014    ///
3015    /// Will return false for [`crate::Tooltip`]s (which are technically popups as well).
3016    #[deprecated = "Renamed to any_popup_open"]
3017    pub fn is_popup_open(&self) -> bool {
3018        self.pass_state_mut(|fs| {
3019            fs.layers
3020                .values()
3021                .any(|layer| !layer.open_popups.is_empty())
3022        })
3023    }
3024}
3025
3026// Ergonomic methods to forward some calls often used in 'if let' without holding the borrow
3027impl Context {
3028    /// Latest reported pointer position.
3029    ///
3030    /// When tapping a touch screen, this will be `None`.
3031    #[inline(always)]
3032    pub fn pointer_latest_pos(&self) -> Option<Pos2> {
3033        self.input(|i| i.pointer.latest_pos())
3034    }
3035
3036    /// If it is a good idea to show a tooltip, where is pointer?
3037    #[inline(always)]
3038    pub fn pointer_hover_pos(&self) -> Option<Pos2> {
3039        self.input(|i| i.pointer.hover_pos())
3040    }
3041
3042    /// If you detect a click or drag and want to know where it happened, use this.
3043    ///
3044    /// Latest position of the mouse, but ignoring any [`crate::Event::PointerGone`]
3045    /// if there were interactions this pass.
3046    /// When tapping a touch screen, this will be the location of the touch.
3047    #[inline(always)]
3048    pub fn pointer_interact_pos(&self) -> Option<Pos2> {
3049        self.input(|i| i.pointer.interact_pos())
3050    }
3051
3052    /// Calls [`InputState::multi_touch`].
3053    pub fn multi_touch(&self) -> Option<MultiTouchInfo> {
3054        self.input(|i| i.multi_touch())
3055    }
3056}
3057
3058impl Context {
3059    /// Transform the graphics of the given layer.
3060    ///
3061    /// This will also affect input.
3062    /// The direction of the given transform is "into the global coordinate system".
3063    ///
3064    /// This is a sticky setting, remembered from one frame to the next.
3065    ///
3066    /// Can be used to implement pan and zoom (see relevant demo).
3067    ///
3068    /// For a temporary transform, use [`Self::transform_layer_shapes`] or
3069    /// [`Ui::with_visual_transform`].
3070    pub fn set_transform_layer(&self, layer_id: LayerId, transform: TSTransform) {
3071        self.memory_mut(|m| {
3072            if transform == TSTransform::IDENTITY {
3073                m.to_global.remove(&layer_id)
3074            } else {
3075                m.to_global.insert(layer_id, transform)
3076            }
3077        });
3078    }
3079
3080    /// Return how to transform the graphics of the given layer into the global coordinate system.
3081    ///
3082    /// Set this with [`Self::layer_transform_to_global`].
3083    pub fn layer_transform_to_global(&self, layer_id: LayerId) -> Option<TSTransform> {
3084        self.memory(|m| m.to_global.get(&layer_id).copied())
3085    }
3086
3087    /// Return how to transform the graphics of the global coordinate system into the local coordinate system of the given layer.
3088    ///
3089    /// This returns the inverse of [`Self::layer_transform_to_global`].
3090    pub fn layer_transform_from_global(&self, layer_id: LayerId) -> Option<TSTransform> {
3091        self.layer_transform_to_global(layer_id)
3092            .map(|t| t.inverse())
3093    }
3094
3095    /// Transform all the graphics at the given layer.
3096    ///
3097    /// Is used to implement drag-and-drop preview.
3098    ///
3099    /// This only applied to the existing graphics at the layer, not to new graphics added later.
3100    ///
3101    /// For a persistent transform, use [`Self::set_transform_layer`] instead.
3102    pub fn transform_layer_shapes(&self, layer_id: LayerId, transform: TSTransform) {
3103        if transform != TSTransform::IDENTITY {
3104            self.graphics_mut(|g| g.entry(layer_id).transform(transform));
3105        }
3106    }
3107
3108    /// Top-most layer at the given position.
3109    pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
3110        self.memory(|mem| mem.layer_id_at(pos))
3111    }
3112
3113    /// Moves the given area to the top in its [`Order`].
3114    ///
3115    /// [`crate::Area`]s and [`crate::Window`]s also do this automatically when being clicked on or interacted with.
3116    pub fn move_to_top(&self, layer_id: LayerId) {
3117        self.memory_mut(|mem| mem.areas_mut().move_to_top(layer_id));
3118    }
3119
3120    /// Mark the `child` layer as a sublayer of `parent`.
3121    ///
3122    /// Sublayers are moved directly above the parent layer at the end of the frame. This is mainly
3123    /// intended for adding a new [`crate::Area`] inside a [`crate::Window`].
3124    ///
3125    /// This currently only supports one level of nesting. If `parent` is a sublayer of another
3126    /// layer, the behavior is unspecified.
3127    pub fn set_sublayer(&self, parent: LayerId, child: LayerId) {
3128        self.memory_mut(|mem| mem.areas_mut().set_sublayer(parent, child));
3129    }
3130
3131    /// Retrieve the [`LayerId`] of the top level windows.
3132    pub fn top_layer_id(&self) -> Option<LayerId> {
3133        self.memory(|mem| mem.areas().top_layer_id(Order::Middle))
3134    }
3135
3136    /// Does the given rectangle contain the mouse pointer?
3137    ///
3138    /// Will return false if some other area is covering the given layer.
3139    ///
3140    /// The given rectangle is assumed to have been clipped by its parent clip rect.
3141    ///
3142    /// See also [`Response::contains_pointer`].
3143    pub fn rect_contains_pointer(&self, layer_id: LayerId, rect: Rect) -> bool {
3144        let rect = if let Some(to_global) = self.layer_transform_to_global(layer_id) {
3145            to_global * rect
3146        } else {
3147            rect
3148        };
3149        if !rect.is_positive() {
3150            return false;
3151        }
3152
3153        let pointer_pos = self.input(|i| i.pointer.interact_pos());
3154        let Some(pointer_pos) = pointer_pos else {
3155            return false;
3156        };
3157
3158        if !rect.contains(pointer_pos) {
3159            return false;
3160        }
3161
3162        if self.layer_id_at(pointer_pos) != Some(layer_id) {
3163            return false;
3164        }
3165
3166        true
3167    }
3168
3169    // ---------------------------------------------------------------------
3170
3171    /// Whether or not to debug widget layout on hover.
3172    #[cfg(debug_assertions)]
3173    pub fn debug_on_hover(&self) -> bool {
3174        self.options(|opt| opt.style().debug.debug_on_hover)
3175    }
3176
3177    /// Turn on/off whether or not to debug widget layout on hover.
3178    #[cfg(debug_assertions)]
3179    pub fn set_debug_on_hover(&self, debug_on_hover: bool) {
3180        self.all_styles_mut(|style| style.debug.debug_on_hover = debug_on_hover);
3181    }
3182}
3183
3184/// ## Animation
3185impl Context {
3186    /// Returns a value in the range [0, 1], to indicate "how on" this thing is.
3187    ///
3188    /// The first time called it will return `if value { 1.0 } else { 0.0 }`
3189    /// Calling this with `value = true` will always yield a number larger than zero, quickly going towards one.
3190    /// Calling this with `value = false` will always yield a number less than one, quickly going towards zero.
3191    ///
3192    /// The function will call [`Self::request_repaint()`] when appropriate.
3193    ///
3194    /// The animation time is taken from [`Style::animation_time`].
3195    #[track_caller] // To track repaint cause
3196    pub fn animate_bool(&self, id: Id, value: bool) -> f32 {
3197        let animation_time = self.global_style().animation_time;
3198        self.animate_bool_with_time_and_easing(id, value, animation_time, emath::easing::linear)
3199    }
3200
3201    /// Like [`Self::animate_bool`], but uses an easing function that makes the value move
3202    /// quickly in the beginning and slow down towards the end.
3203    ///
3204    /// The exact easing function may come to change in future versions of egui.
3205    #[track_caller] // To track repaint cause
3206    pub fn animate_bool_responsive(&self, id: Id, value: bool) -> f32 {
3207        self.animate_bool_with_easing(id, value, emath::easing::cubic_out)
3208    }
3209
3210    /// Like [`Self::animate_bool`] but allows you to control the easing function.
3211    #[track_caller] // To track repaint cause
3212    pub fn animate_bool_with_easing(&self, id: Id, value: bool, easing: fn(f32) -> f32) -> f32 {
3213        let animation_time = self.global_style().animation_time;
3214        self.animate_bool_with_time_and_easing(id, value, animation_time, easing)
3215    }
3216
3217    /// Like [`Self::animate_bool`] but allows you to control the animation time.
3218    #[track_caller] // To track repaint cause
3219    pub fn animate_bool_with_time(&self, id: Id, target_value: bool, animation_time: f32) -> f32 {
3220        self.animate_bool_with_time_and_easing(
3221            id,
3222            target_value,
3223            animation_time,
3224            emath::easing::linear,
3225        )
3226    }
3227
3228    /// Like [`Self::animate_bool`] but allows you to control the animation time and easing function.
3229    ///
3230    /// Use e.g. [`emath::easing::quadratic_out`]
3231    /// for a responsive start and a slow end.
3232    ///
3233    /// The easing function flips when `target_value` is `false`,
3234    /// so that when going back towards 0.0, we get the reverse behavior.
3235    #[track_caller] // To track repaint cause
3236    pub fn animate_bool_with_time_and_easing(
3237        &self,
3238        id: Id,
3239        target_value: bool,
3240        animation_time: f32,
3241        easing: fn(f32) -> f32,
3242    ) -> f32 {
3243        let animated_value = self.write(|ctx| {
3244            ctx.animation_manager.animate_bool(
3245                &ctx.viewports.entry(ctx.viewport_id()).or_default().input,
3246                animation_time,
3247                id,
3248                target_value,
3249            )
3250        });
3251
3252        let animation_in_progress = 0.0 < animated_value && animated_value < 1.0;
3253        if animation_in_progress {
3254            self.request_repaint();
3255        }
3256
3257        if target_value {
3258            easing(animated_value)
3259        } else {
3260            1.0 - easing(1.0 - animated_value)
3261        }
3262    }
3263
3264    /// Smoothly animate an `f32` value.
3265    ///
3266    /// At the first call the value is written to memory.
3267    /// When it is called with a new value, it linearly interpolates to it in the given time.
3268    #[track_caller] // To track repaint cause
3269    pub fn animate_value_with_time(&self, id: Id, target_value: f32, animation_time: f32) -> f32 {
3270        let animated_value = self.write(|ctx| {
3271            ctx.animation_manager.animate_value(
3272                &ctx.viewports.entry(ctx.viewport_id()).or_default().input,
3273                animation_time,
3274                id,
3275                target_value,
3276            )
3277        });
3278        let animation_in_progress = animated_value != target_value;
3279        if animation_in_progress {
3280            self.request_repaint();
3281        }
3282
3283        animated_value
3284    }
3285
3286    /// Clear memory of any animations.
3287    pub fn clear_animations(&self) {
3288        self.write(|ctx| ctx.animation_manager = Default::default());
3289    }
3290}
3291
3292impl Context {
3293    /// Show a ui for settings (style and tessellation options).
3294    pub fn settings_ui(&self, ui: &mut Ui) {
3295        let prev_options = self.options(|o| o.clone());
3296        let mut options = prev_options.clone();
3297
3298        ui.collapsing("🔠 Font tweak", |ui| {
3299            self.fonts_tweak_ui(ui);
3300        });
3301
3302        options.ui(ui);
3303
3304        if options != prev_options {
3305            self.options_mut(move |o| *o = options);
3306        }
3307    }
3308
3309    fn fonts_tweak_ui(&self, ui: &mut Ui) {
3310        let mut font_definitions = self.write(|ctx| ctx.font_definitions.clone());
3311        let mut changed = false;
3312
3313        for (name, data) in &mut font_definitions.font_data {
3314            ui.collapsing(name, |ui| {
3315                let mut tweak = data.tweak.clone();
3316                if tweak.ui(ui).changed() {
3317                    Arc::make_mut(data).tweak = tweak;
3318                    changed = true;
3319                }
3320            });
3321        }
3322
3323        if changed {
3324            self.set_fonts(font_definitions);
3325        }
3326    }
3327
3328    /// Show the state of egui, including its input and output.
3329    pub fn inspection_ui(&self, ui: &mut Ui) {
3330        use crate::containers::CollapsingHeader;
3331
3332        crate::Grid::new("egui-inspection-grid")
3333            .num_columns(2)
3334            .striped(true)
3335            .show(ui, |ui| {
3336                ui.label("Total ui frames:");
3337                ui.monospace(ui.ctx().cumulative_frame_nr().to_string());
3338                ui.end_row();
3339
3340                ui.label("Total ui passes:");
3341                ui.monospace(ui.ctx().cumulative_pass_nr().to_string());
3342                ui.end_row();
3343
3344                ui.label("Is using pointer")
3345                    .on_hover_text("Is egui currently using the pointer actively (e.g. dragging a slider)?");
3346                ui.monospace(self.egui_is_using_pointer().to_string());
3347                ui.end_row();
3348
3349                ui.label("Wants pointer input")
3350                    .on_hover_text("Is egui currently interested in the location of the pointer (either because it is in use, or because it is hovering over a window).");
3351                ui.monospace(self.egui_wants_pointer_input().to_string());
3352                ui.end_row();
3353
3354                ui.label("Wants keyboard input").on_hover_text("Is egui currently listening for text input?");
3355                ui.monospace(self.egui_wants_keyboard_input().to_string());
3356                ui.end_row();
3357
3358                ui.label("Keyboard focus widget").on_hover_text("Is egui currently listening for text input?");
3359                ui.monospace(self.memory(|m| m.focused())
3360                    .as_ref()
3361                    .map(Id::short_debug_format)
3362                    .unwrap_or_default());
3363                ui.end_row();
3364
3365                let pointer_pos = self
3366                    .pointer_hover_pos()
3367                    .map_or_else(String::new, |pos| format!("{pos:?}"));
3368                ui.label("Pointer pos");
3369                ui.monospace(pointer_pos);
3370                ui.end_row();
3371
3372                let top_layer = self
3373                    .pointer_hover_pos()
3374                    .and_then(|pos| self.layer_id_at(pos))
3375                    .map_or_else(String::new, |layer| layer.short_debug_format());
3376                ui.label("Top layer under mouse");
3377                ui.monospace(top_layer);
3378                ui.end_row();
3379            });
3380
3381        ui.add_space(16.0);
3382
3383        ui.label(format!(
3384            "There are {} text galleys in the layout cache",
3385            self.fonts(|f| f.num_galleys_in_cache())
3386        ))
3387        .on_hover_text("This is approximately the number of text strings on screen");
3388        ui.add_space(16.0);
3389
3390        CollapsingHeader::new("🔃 Repaint Causes")
3391            .default_open(false)
3392            .show(ui, |ui| {
3393                ui.set_min_height(120.0);
3394                ui.label("What caused egui to repaint:");
3395                ui.add_space(8.0);
3396                let causes = ui.ctx().repaint_causes();
3397                for cause in causes {
3398                    ui.label(cause.to_string());
3399                }
3400            });
3401
3402        CollapsingHeader::new("📥 Input")
3403            .default_open(false)
3404            .show(ui, |ui| {
3405                let input = ui.input(|i| i.clone());
3406                input.ui(ui);
3407            });
3408
3409        CollapsingHeader::new("📊 Paint stats")
3410            .default_open(false)
3411            .show(ui, |ui| {
3412                let paint_stats = self.read(|ctx| ctx.paint_stats);
3413                paint_stats.ui(ui);
3414            });
3415
3416        CollapsingHeader::new("🖼 Textures")
3417            .default_open(false)
3418            .show(ui, |ui| {
3419                self.texture_ui(ui);
3420            });
3421
3422        CollapsingHeader::new("🖼 Image loaders")
3423            .default_open(false)
3424            .show(ui, |ui| {
3425                self.loaders_ui(ui);
3426            });
3427
3428        CollapsingHeader::new("🔠 Font texture")
3429            .default_open(false)
3430            .show(ui, |ui| {
3431                let font_image_size = self.fonts(|f| f.font_image_size());
3432                crate::introspection::font_texture_ui(ui, font_image_size);
3433            });
3434
3435        CollapsingHeader::new("Label text selection state")
3436            .default_open(false)
3437            .show(ui, |ui| {
3438                ui.label(format!(
3439                    "{:#?}",
3440                    *ui.ctx()
3441                        .plugin::<crate::text_selection::LabelSelectionState>()
3442                        .lock()
3443                ));
3444            });
3445
3446        CollapsingHeader::new("Interaction")
3447            .default_open(false)
3448            .show(ui, |ui| {
3449                let interact_widgets = self.write(|ctx| ctx.viewport().interact_widgets.clone());
3450                interact_widgets.ui(ui);
3451            });
3452    }
3453
3454    /// Show stats about the allocated textures.
3455    pub fn texture_ui(&self, ui: &mut crate::Ui) {
3456        let tex_mngr = self.tex_manager();
3457        let tex_mngr = tex_mngr.read();
3458
3459        let mut textures: Vec<_> = tex_mngr.allocated().collect();
3460        textures.sort_by_key(|(id, _)| *id);
3461
3462        let mut bytes = 0;
3463        for (_, tex) in &textures {
3464            bytes += tex.bytes_used();
3465        }
3466
3467        ui.label(format!(
3468            "{} allocated texture(s), using {:.1} MB",
3469            textures.len(),
3470            bytes as f64 * 1e-6
3471        ));
3472        let max_preview_size = vec2(48.0, 32.0);
3473
3474        let pixels_per_point = self.pixels_per_point();
3475
3476        ui.group(|ui| {
3477            ScrollArea::vertical()
3478                .max_height(300.0)
3479                .auto_shrink([false, true])
3480                .show(ui, |ui| {
3481                    ui.style_mut().override_text_style = Some(TextStyle::Monospace);
3482                    Grid::new("textures")
3483                        .striped(true)
3484                        .num_columns(4)
3485                        .spacing(vec2(16.0, 2.0))
3486                        .min_row_height(max_preview_size.y)
3487                        .show(ui, |ui| {
3488                            for (&texture_id, meta) in textures {
3489                                let [w, h] = meta.size;
3490                                let point_size = vec2(w as f32, h as f32) / pixels_per_point;
3491
3492                                let mut size = point_size;
3493                                size *= (max_preview_size.x / size.x).min(1.0);
3494                                size *= (max_preview_size.y / size.y).min(1.0);
3495                                ui.image(SizedTexture::new(texture_id, size))
3496                                    .on_hover_ui(|ui| {
3497                                        // show larger on hover
3498                                        let max_size = 0.5 * ui.ctx().content_rect().size();
3499                                        let mut size = point_size;
3500                                        size *= max_size.x / size.x.max(max_size.x);
3501                                        size *= max_size.y / size.y.max(max_size.y);
3502                                        ui.image(SizedTexture::new(texture_id, size));
3503                                    });
3504
3505                                ui.label(format!("{w} x {h}"));
3506                                ui.label(format!("{:.3} MB", meta.bytes_used() as f64 * 1e-6));
3507                                ui.label(format!("{:?}", meta.name));
3508                                ui.end_row();
3509                            }
3510                        });
3511                });
3512        });
3513    }
3514
3515    /// Show stats about different image loaders.
3516    pub fn loaders_ui(&self, ui: &mut crate::Ui) {
3517        struct LoaderInfo {
3518            id: String,
3519            byte_size: usize,
3520        }
3521
3522        let mut byte_loaders = vec![];
3523        let mut image_loaders = vec![];
3524        let mut texture_loaders = vec![];
3525
3526        {
3527            let loaders = self.loaders();
3528            let Loaders {
3529                include: _,
3530                bytes,
3531                image,
3532                texture,
3533            } = loaders.as_ref();
3534
3535            for loader in bytes.lock().iter() {
3536                byte_loaders.push(LoaderInfo {
3537                    id: loader.id().to_owned(),
3538                    byte_size: loader.byte_size(),
3539                });
3540            }
3541            for loader in image.lock().iter() {
3542                image_loaders.push(LoaderInfo {
3543                    id: loader.id().to_owned(),
3544                    byte_size: loader.byte_size(),
3545                });
3546            }
3547            for loader in texture.lock().iter() {
3548                texture_loaders.push(LoaderInfo {
3549                    id: loader.id().to_owned(),
3550                    byte_size: loader.byte_size(),
3551                });
3552            }
3553        }
3554
3555        fn loaders_ui(ui: &mut crate::Ui, title: &str, loaders: &[LoaderInfo]) {
3556            let heading = format!("{} {title} loaders", loaders.len());
3557            crate::CollapsingHeader::new(heading)
3558                .default_open(true)
3559                .show(ui, |ui| {
3560                    Grid::new("loaders")
3561                        .striped(true)
3562                        .num_columns(2)
3563                        .show(ui, |ui| {
3564                            ui.label("ID");
3565                            ui.label("Size");
3566                            ui.end_row();
3567
3568                            for loader in loaders {
3569                                ui.label(&loader.id);
3570                                ui.label(format!("{:.3} MB", loader.byte_size as f64 * 1e-6));
3571                                ui.end_row();
3572                            }
3573                        });
3574                });
3575        }
3576
3577        loaders_ui(ui, "byte", &byte_loaders);
3578        loaders_ui(ui, "image", &image_loaders);
3579        loaders_ui(ui, "texture", &texture_loaders);
3580    }
3581
3582    /// Shows the contents of [`Self::memory`].
3583    pub fn memory_ui(&self, ui: &mut crate::Ui) {
3584        if ui
3585            .button("Reset all")
3586            .on_hover_text("Reset all egui state")
3587            .clicked()
3588        {
3589            self.memory_mut(|mem| *mem = Default::default());
3590        }
3591
3592        let (num_state, num_serialized) = self.data(|d| (d.len(), d.count_serialized()));
3593        ui.label(format!(
3594            "{num_state} widget states stored (of which {num_serialized} are serialized)."
3595        ));
3596
3597        ui.horizontal(|ui| {
3598            ui.label(format!(
3599                "{} areas (panels, windows, popups, …)",
3600                self.memory(|mem| mem.areas().count())
3601            ));
3602            if ui.button("Reset").clicked() {
3603                self.memory_mut(|mem| *mem.areas_mut() = Default::default());
3604            }
3605        });
3606        ui.indent("layers", |ui| {
3607            ui.label("Layers, ordered back to front.");
3608            let layers_ids: Vec<LayerId> = self.memory(|mem| mem.areas().order().to_vec());
3609            for layer_id in layers_ids {
3610                if let Some(area) = AreaState::load(self, layer_id.id) {
3611                    let is_visible = self.memory(|mem| mem.areas().is_visible(&layer_id));
3612                    if !is_visible {
3613                        continue;
3614                    }
3615                    let text = format!("{} - {:?}", layer_id.short_debug_format(), area.rect(),);
3616                    // TODO(emilk): `Sense::hover_highlight()`
3617                    let response =
3618                        ui.add(Label::new(RichText::new(text).monospace()).sense(Sense::click()));
3619                    if response.hovered() && is_visible {
3620                        ui.debug_painter().debug_rect(area.rect(), Color32::RED, "");
3621                    }
3622                } else {
3623                    ui.monospace(layer_id.short_debug_format());
3624                }
3625            }
3626        });
3627
3628        ui.horizontal(|ui| {
3629            ui.label(format!(
3630                "{} collapsing headers",
3631                self.data(|d| d.count::<containers::collapsing_header::InnerState>())
3632            ));
3633            if ui.button("Reset").clicked() {
3634                self.data_mut(|d| d.remove_by_type::<containers::collapsing_header::InnerState>());
3635            }
3636        });
3637
3638        #[expect(deprecated)]
3639        ui.horizontal(|ui| {
3640            ui.label(format!(
3641                "{} menu bars",
3642                self.data(|d| d.count::<crate::menu::BarState>())
3643            ));
3644            if ui.button("Reset").clicked() {
3645                self.data_mut(|d| d.remove_by_type::<crate::menu::BarState>());
3646            }
3647        });
3648
3649        ui.horizontal(|ui| {
3650            ui.label(format!(
3651                "{} scroll areas",
3652                self.data(|d| d.count::<scroll_area::State>())
3653            ));
3654            if ui.button("Reset").clicked() {
3655                self.data_mut(|d| d.remove_by_type::<scroll_area::State>());
3656            }
3657        });
3658
3659        ui.horizontal(|ui| {
3660            ui.label(format!(
3661                "{} resize areas",
3662                self.data(|d| d.count::<resize::State>())
3663            ));
3664            if ui.button("Reset").clicked() {
3665                self.data_mut(|d| d.remove_by_type::<resize::State>());
3666            }
3667        });
3668
3669        ui.shrink_width_to_current(); // don't let the text below grow this window wider
3670        ui.label("NOTE: the position of this window cannot be reset from within itself.");
3671
3672        ui.collapsing("Interaction", |ui| {
3673            let interaction = self.memory(|mem| mem.interaction().clone());
3674            interaction.ui(ui);
3675        });
3676    }
3677}
3678
3679impl Context {
3680    /// Edit the [`Style`].
3681    pub fn style_ui(&self, ui: &mut Ui, theme: Theme) {
3682        let mut style: Style = (*self.style_of(theme)).clone();
3683        style.ui(ui);
3684        self.set_style_of(theme, style);
3685    }
3686}
3687
3688/// ## Accessibility
3689impl Context {
3690    /// If AccessKit support is active for the current frame, get or create
3691    /// a node builder with the specified ID and return a mutable reference to it.
3692    /// For newly created nodes, the parent is the parent [`Ui`]s ID.
3693    /// And an [`Ui`]s parent can be set with [`UiBuilder::accessibility_parent`].
3694    ///
3695    /// The `Context` lock is held while the given closure is called!
3696    ///
3697    /// Returns `None` if accesskit is off.
3698    // TODO(emilk): consider making both read-only and read-write versions
3699    pub fn accesskit_node_builder<R>(
3700        &self,
3701        id: Id,
3702        writer: impl FnOnce(&mut accesskit::Node) -> R,
3703    ) -> Option<R> {
3704        self.write(|ctx| ctx.accesskit_node_builder(id).map(writer))
3705    }
3706
3707    pub(crate) fn register_accesskit_parent(&self, id: Id, parent_id: Id) {
3708        self.write(|ctx| {
3709            if let Some(state) = ctx.viewport().this_pass.accesskit_state.as_mut() {
3710                state.parent_map.insert(id, parent_id);
3711            }
3712        });
3713    }
3714
3715    /// Enable generation of AccessKit tree updates in all future frames.
3716    pub fn enable_accesskit(&self) {
3717        self.write(|ctx| ctx.is_accesskit_enabled = true);
3718    }
3719
3720    /// Disable generation of AccessKit tree updates in all future frames.
3721    pub fn disable_accesskit(&self) {
3722        self.write(|ctx| ctx.is_accesskit_enabled = false);
3723    }
3724}
3725
3726/// ## Image loading
3727impl Context {
3728    /// Associate some static bytes with a `uri`.
3729    ///
3730    /// The same `uri` may be passed to [`Ui::image`] later to load the bytes as an image.
3731    ///
3732    /// By convention, the `uri` should start with `bytes://`.
3733    /// Following that convention will lead to better error messages.
3734    pub fn include_bytes(&self, uri: impl Into<Cow<'static, str>>, bytes: impl Into<Bytes>) {
3735        self.loaders().include.insert(uri, bytes);
3736    }
3737
3738    /// Returns `true` if the chain of bytes, image, or texture loaders
3739    /// contains a loader with the given `id`.
3740    pub fn is_loader_installed(&self, id: &str) -> bool {
3741        let loaders = self.loaders();
3742
3743        loaders.bytes.lock().iter().any(|l| l.id() == id)
3744            || loaders.image.lock().iter().any(|l| l.id() == id)
3745            || loaders.texture.lock().iter().any(|l| l.id() == id)
3746    }
3747
3748    /// Add a new bytes loader.
3749    ///
3750    /// It will be tried first, before any already installed loaders.
3751    ///
3752    /// See [`load`] for more information.
3753    pub fn add_bytes_loader(&self, loader: Arc<dyn load::BytesLoader + Send + Sync + 'static>) {
3754        self.loaders().bytes.lock().push(loader);
3755    }
3756
3757    /// Add a new image loader.
3758    ///
3759    /// It will be tried first, before any already installed loaders.
3760    ///
3761    /// See [`load`] for more information.
3762    pub fn add_image_loader(&self, loader: Arc<dyn load::ImageLoader + Send + Sync + 'static>) {
3763        self.loaders().image.lock().push(loader);
3764    }
3765
3766    /// Add a new texture loader.
3767    ///
3768    /// It will be tried first, before any already installed loaders.
3769    ///
3770    /// See [`load`] for more information.
3771    pub fn add_texture_loader(&self, loader: Arc<dyn load::TextureLoader + Send + Sync + 'static>) {
3772        self.loaders().texture.lock().push(loader);
3773    }
3774
3775    /// Release all memory and textures related to the given image URI.
3776    ///
3777    /// If you attempt to load the image again, it will be reloaded from scratch.
3778    /// Also this cancels any ongoing loading of the image.
3779    pub fn forget_image(&self, uri: &str) {
3780        use load::BytesLoader as _;
3781
3782        profiling::function_scope!();
3783
3784        let loaders = self.loaders();
3785
3786        loaders.include.forget(uri);
3787        for loader in loaders.bytes.lock().iter() {
3788            loader.forget(uri);
3789        }
3790        for loader in loaders.image.lock().iter() {
3791            loader.forget(uri);
3792        }
3793        for loader in loaders.texture.lock().iter() {
3794            loader.forget(uri);
3795        }
3796    }
3797
3798    /// Release all memory and textures related to images used in [`Ui::image`] or [`crate::Image`].
3799    ///
3800    /// If you attempt to load any images again, they will be reloaded from scratch.
3801    pub fn forget_all_images(&self) {
3802        use load::BytesLoader as _;
3803
3804        profiling::function_scope!();
3805
3806        let loaders = self.loaders();
3807
3808        loaders.include.forget_all();
3809        for loader in loaders.bytes.lock().iter() {
3810            loader.forget_all();
3811        }
3812        for loader in loaders.image.lock().iter() {
3813            loader.forget_all();
3814        }
3815        for loader in loaders.texture.lock().iter() {
3816            loader.forget_all();
3817        }
3818    }
3819
3820    /// Try loading the bytes from the given uri using any available bytes loaders.
3821    ///
3822    /// Loaders are expected to cache results, so that this call is immediate-mode safe.
3823    ///
3824    /// This calls the loaders one by one in the order in which they were registered.
3825    /// If a loader returns [`LoadError::NotSupported`][not_supported],
3826    /// then the next loader is called. This process repeats until all loaders have
3827    /// been exhausted, at which point this returns [`LoadError::NotSupported`][not_supported].
3828    ///
3829    /// # Errors
3830    /// This may fail with:
3831    /// - [`LoadError::NotSupported`][not_supported] if none of the registered loaders support loading the given `uri`.
3832    /// - [`LoadError::Loading`][custom] if one of the loaders _does_ support loading the `uri`, but the loading process failed.
3833    ///
3834    /// ⚠ May deadlock if called from within a `BytesLoader`!
3835    ///
3836    /// [not_supported]: crate::load::LoadError::NotSupported
3837    /// [custom]: crate::load::LoadError::Loading
3838    pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult {
3839        profiling::function_scope!(uri);
3840
3841        let loaders = self.loaders();
3842        let bytes_loaders = loaders.bytes.lock();
3843
3844        // Try most recently added loaders first (hence `.rev()`)
3845        for loader in bytes_loaders.iter().rev() {
3846            let result = loader.load(self, uri);
3847            match result {
3848                Err(load::LoadError::NotSupported) => {}
3849                _ => return result,
3850            }
3851        }
3852
3853        Err(load::LoadError::NoMatchingBytesLoader)
3854    }
3855
3856    /// Try loading the image from the given uri using any available image loaders.
3857    ///
3858    /// Loaders are expected to cache results, so that this call is immediate-mode safe.
3859    ///
3860    /// This calls the loaders one by one in the order in which they were registered.
3861    /// If a loader returns [`LoadError::NotSupported`][not_supported],
3862    /// then the next loader is called. This process repeats until all loaders have
3863    /// been exhausted, at which point this returns [`LoadError::NotSupported`][not_supported].
3864    ///
3865    /// # Errors
3866    /// This may fail with:
3867    /// - [`LoadError::NoImageLoaders`][no_image_loaders] if tbere are no registered image loaders.
3868    /// - [`LoadError::NotSupported`][not_supported] if none of the registered loaders support loading the given `uri`.
3869    /// - [`LoadError::Loading`][custom] if one of the loaders _does_ support loading the `uri`, but the loading process failed.
3870    ///
3871    /// ⚠ May deadlock if called from within an `ImageLoader`!
3872    ///
3873    /// [no_image_loaders]: crate::load::LoadError::NoImageLoaders
3874    /// [not_supported]: crate::load::LoadError::NotSupported
3875    /// [custom]: crate::load::LoadError::Loading
3876    pub fn try_load_image(&self, uri: &str, size_hint: load::SizeHint) -> load::ImageLoadResult {
3877        profiling::function_scope!(uri);
3878
3879        let loaders = self.loaders();
3880        let image_loaders = loaders.image.lock();
3881        if image_loaders.is_empty() {
3882            return Err(load::LoadError::NoImageLoaders);
3883        }
3884
3885        let mut format = None;
3886
3887        // Try most recently added loaders first (hence `.rev()`)
3888        for loader in image_loaders.iter().rev() {
3889            match loader.load(self, uri, size_hint) {
3890                Err(load::LoadError::NotSupported) => {}
3891                Err(load::LoadError::FormatNotSupported { detected_format }) => {
3892                    format = format.or(detected_format);
3893                }
3894                result => return result,
3895            }
3896        }
3897
3898        Err(load::LoadError::NoMatchingImageLoader {
3899            detected_format: format,
3900        })
3901    }
3902
3903    /// Try loading the texture from the given uri using any available texture loaders.
3904    ///
3905    /// Loaders are expected to cache results, so that this call is immediate-mode safe.
3906    ///
3907    /// This calls the loaders one by one in the order in which they were registered.
3908    /// If a loader returns [`LoadError::NotSupported`][not_supported],
3909    /// then the next loader is called. This process repeats until all loaders have
3910    /// been exhausted, at which point this returns [`LoadError::NotSupported`][not_supported].
3911    ///
3912    /// # Errors
3913    /// This may fail with:
3914    /// - [`LoadError::NotSupported`][not_supported] if none of the registered loaders support loading the given `uri`.
3915    /// - [`LoadError::Loading`][custom] if one of the loaders _does_ support loading the `uri`, but the loading process failed.
3916    ///
3917    /// ⚠ May deadlock if called from within a `TextureLoader`!
3918    ///
3919    /// [not_supported]: crate::load::LoadError::NotSupported
3920    /// [custom]: crate::load::LoadError::Loading
3921    pub fn try_load_texture(
3922        &self,
3923        uri: &str,
3924        texture_options: TextureOptions,
3925        size_hint: load::SizeHint,
3926    ) -> load::TextureLoadResult {
3927        profiling::function_scope!(uri);
3928
3929        let loaders = self.loaders();
3930        let texture_loaders = loaders.texture.lock();
3931
3932        // Try most recently added loaders first (hence `.rev()`)
3933        for loader in texture_loaders.iter().rev() {
3934            match loader.load(self, uri, texture_options, size_hint) {
3935                Err(load::LoadError::NotSupported) => {}
3936                result => return result,
3937            }
3938        }
3939
3940        Err(load::LoadError::NoMatchingTextureLoader)
3941    }
3942
3943    /// The loaders of bytes, images, and textures.
3944    pub fn loaders(&self) -> Arc<Loaders> {
3945        self.read(|this| Arc::clone(&this.loaders))
3946    }
3947
3948    /// Returns `true` if any image is currently being loaded.
3949    pub fn has_pending_images(&self) -> bool {
3950        self.read(|this| {
3951            this.loaders.image.lock().iter().any(|i| i.has_pending())
3952                || this.loaders.bytes.lock().iter().any(|i| i.has_pending())
3953        })
3954    }
3955}
3956
3957/// ## Viewports
3958impl Context {
3959    /// Return the `ViewportId` of the current viewport.
3960    ///
3961    /// If this is the root viewport, this will return [`ViewportId::ROOT`].
3962    ///
3963    /// Don't use this outside of `Self::run`, or after `Self::end_pass`.
3964    pub fn viewport_id(&self) -> ViewportId {
3965        self.read(|ctx| ctx.viewport_id())
3966    }
3967
3968    /// Return the `ViewportId` of his parent.
3969    ///
3970    /// If this is the root viewport, this will return [`ViewportId::ROOT`].
3971    ///
3972    /// Don't use this outside of `Self::run`, or after `Self::end_pass`.
3973    pub fn parent_viewport_id(&self) -> ViewportId {
3974        self.read(|ctx| ctx.parent_viewport_id())
3975    }
3976
3977    /// Read the state of the current viewport.
3978    pub fn viewport<R>(&self, reader: impl FnOnce(&ViewportState) -> R) -> R {
3979        self.write(|ctx| reader(ctx.viewport()))
3980    }
3981
3982    /// Read the state of a specific current viewport.
3983    pub fn viewport_for<R>(
3984        &self,
3985        viewport_id: ViewportId,
3986        reader: impl FnOnce(&ViewportState) -> R,
3987    ) -> R {
3988        self.write(|ctx| reader(ctx.viewport_for(viewport_id)))
3989    }
3990
3991    /// For integrations: Set this to render a sync viewport.
3992    ///
3993    /// This will only set the callback for the current thread,
3994    /// which most likely should be the main thread.
3995    ///
3996    /// When an immediate viewport is created with [`Self::show_viewport_immediate`] it will be rendered by this function.
3997    ///
3998    /// When called, the integration needs to:
3999    /// * Check if there already is a window for this viewport id, and if not open one
4000    /// * Set the window attributes (position, size, …) based on [`ImmediateViewport::builder`].
4001    /// * Call [`Context::run`] with [`ImmediateViewport::viewport_ui_cb`].
4002    /// * Handle the output from [`Context::run`], including rendering
4003    pub fn set_immediate_viewport_renderer(
4004        callback: impl for<'a> Fn(&Self, ImmediateViewport<'a>) + 'static,
4005    ) {
4006        let callback = Box::new(callback);
4007        IMMEDIATE_VIEWPORT_RENDERER.with(|render_sync| {
4008            render_sync.replace(Some(callback));
4009        });
4010    }
4011
4012    /// If `true`, [`Self::show_viewport_deferred`] and [`Self::show_viewport_immediate`] will
4013    /// embed the new viewports inside the existing one, instead of spawning a new native window.
4014    ///
4015    /// `eframe` sets this to `false` on supported platforms, but the default value is `true`.
4016    pub fn embed_viewports(&self) -> bool {
4017        self.read(|ctx| ctx.embed_viewports)
4018    }
4019
4020    /// If `true`, [`Self::show_viewport_deferred`] and [`Self::show_viewport_immediate`] will
4021    /// embed the new viewports inside the existing one, instead of spawning a new native window.
4022    ///
4023    /// `eframe` sets this to `false` on supported platforms, but the default value is `true`.
4024    pub fn set_embed_viewports(&self, value: bool) {
4025        self.write(|ctx| ctx.embed_viewports = value);
4026    }
4027
4028    /// Send a command to the current viewport.
4029    ///
4030    /// This lets you affect the current viewport, e.g. resizing the window.
4031    pub fn send_viewport_cmd(&self, command: ViewportCommand) {
4032        self.send_viewport_cmd_to(self.viewport_id(), command);
4033    }
4034
4035    /// Send a command to a specific viewport.
4036    ///
4037    /// This lets you affect another viewport, e.g. resizing its window.
4038    pub fn send_viewport_cmd_to(&self, id: ViewportId, command: ViewportCommand) {
4039        self.request_repaint_of(id);
4040
4041        if command.requires_parent_repaint() {
4042            self.request_repaint_of(self.parent_viewport_id());
4043        }
4044
4045        self.write(|ctx| ctx.viewport_for(id).commands.push(command));
4046    }
4047
4048    /// Show a deferred viewport, creating a new native window, if possible.
4049    ///
4050    /// The given id must be unique for each viewport.
4051    ///
4052    /// You need to call this each pass when the child viewport should exist.
4053    ///
4054    /// You can check if the user wants to close the viewport by checking the
4055    /// [`crate::ViewportInfo::close_requested`] flags found in [`crate::InputState::viewport`].
4056    ///
4057    /// The given callback will be called whenever the child viewport needs repainting,
4058    /// e.g. on an event or when [`Self::request_repaint`] is called.
4059    /// This means it may be called multiple times, for instance while the
4060    /// parent viewport (the caller) is sleeping but the child viewport is animating.
4061    ///
4062    /// You will need to wrap your viewport state in an `Arc<RwLock<T>>` or `Arc<Mutex<T>>`.
4063    /// When this is called again with the same id in `ViewportBuilder` the render function for that viewport will be updated.
4064    ///
4065    /// You can also use [`Self::show_viewport_immediate`], which uses a simpler `FnOnce`
4066    /// with no need for `Send` or `Sync`. The downside is that it will require
4067    /// the parent viewport (the caller) to repaint anytime the child is repainted,
4068    /// and vice versa.
4069    ///
4070    /// If [`Context::embed_viewports`] is `true` (e.g. if the current egui
4071    /// backend does not support multiple viewports), the given callback
4072    /// will be called immediately, embedding the new viewport in the current one,
4073    /// inside of a [`crate::Window`].
4074    /// You can know by checking for [`ViewportClass::EmbeddedWindow`].
4075    ///
4076    /// See [`crate::viewport`] for more information about viewports.
4077    pub fn show_viewport_deferred(
4078        &self,
4079        new_viewport_id: ViewportId,
4080        viewport_builder: ViewportBuilder,
4081        viewport_ui_cb: impl Fn(&mut Ui, ViewportClass) + Send + Sync + 'static,
4082    ) {
4083        profiling::function_scope!();
4084
4085        if self.embed_viewports() {
4086            crate::Window::from_viewport(new_viewport_id, viewport_builder).show(self, |ui| {
4087                viewport_ui_cb(ui, ViewportClass::EmbeddedWindow);
4088            });
4089        } else {
4090            self.write(|ctx| {
4091                ctx.viewport_parents
4092                    .insert(new_viewport_id, ctx.viewport_id());
4093
4094                let viewport = ctx.viewports.entry(new_viewport_id).or_default();
4095                viewport.class = ViewportClass::Deferred;
4096                viewport.builder = viewport_builder;
4097                viewport.used = true;
4098                viewport.viewport_ui_cb = Some(Arc::new(move |ui| {
4099                    (viewport_ui_cb)(ui, ViewportClass::Deferred);
4100                }));
4101            });
4102        }
4103    }
4104
4105    /// Show an immediate viewport, creating a new native window, if possible.
4106    ///
4107    /// This is the easier type of viewport to use, but it is less performant
4108    /// as it requires both parent and child to repaint if any one of them needs repainting,
4109    /// which effectively produce double work for two viewports, and triple work for three viewports, etc.
4110    /// To avoid this, use [`Self::show_viewport_deferred`] instead.
4111    ///
4112    /// The given id must be unique for each viewport.
4113    ///
4114    /// You need to call this each pass when the child viewport should exist.
4115    ///
4116    /// You can check if the user wants to close the viewport by checking the
4117    /// [`crate::ViewportInfo::close_requested`] flags found in [`crate::InputState::viewport`].
4118    ///
4119    /// The given ui function will be called immediately.
4120    /// This may only be called on the main thread.
4121    /// This call will pause the current viewport and render the child viewport in its own window.
4122    /// This means that the child viewport will not be repainted when the parent viewport is repainted, and vice versa.
4123    ///
4124    /// If [`Context::embed_viewports`] is `true` (e.g. if the current egui
4125    /// backend does not support multiple viewports), the given callback
4126    /// will be called immediately, embedding the new viewport in the current one,
4127    /// inside of a [`crate::Window`].
4128    /// You can know by checking for [`ViewportClass::EmbeddedWindow`].
4129    ///
4130    /// See [`crate::viewport`] for more information about viewports.
4131    pub fn show_viewport_immediate<T>(
4132        &self,
4133        new_viewport_id: ViewportId,
4134        builder: ViewportBuilder,
4135        mut viewport_ui_cb: impl FnMut(&mut Ui, ViewportClass) -> T,
4136    ) -> T {
4137        profiling::function_scope!();
4138
4139        if self.embed_viewports() {
4140            return self.show_embedded_viewport(new_viewport_id, builder, |ui| {
4141                viewport_ui_cb(ui, ViewportClass::EmbeddedWindow)
4142            });
4143        }
4144
4145        IMMEDIATE_VIEWPORT_RENDERER.with(|immediate_viewport_renderer| {
4146            let immediate_viewport_renderer = immediate_viewport_renderer.borrow();
4147            let Some(immediate_viewport_renderer) = immediate_viewport_renderer.as_ref() else {
4148                // This egui backend does not support multiple viewports.
4149                return self.show_embedded_viewport(new_viewport_id, builder, |ui| {
4150                    viewport_ui_cb(ui, ViewportClass::EmbeddedWindow)
4151                });
4152            };
4153
4154            let ids = self.write(|ctx| {
4155                let parent_viewport_id = ctx.viewport_id();
4156
4157                ctx.viewport_parents
4158                    .insert(new_viewport_id, parent_viewport_id);
4159
4160                let viewport = ctx.viewports.entry(new_viewport_id).or_default();
4161                viewport.builder = builder.clone();
4162                viewport.used = true;
4163                viewport.viewport_ui_cb = None; // it is immediate
4164
4165                ViewportIdPair::from_self_and_parent(new_viewport_id, parent_viewport_id)
4166            });
4167
4168            let mut out = None;
4169            {
4170                let out = &mut out;
4171
4172                let viewport = ImmediateViewport {
4173                    ids,
4174                    builder,
4175                    viewport_ui_cb: Box::new(move |ui| {
4176                        *out = Some((viewport_ui_cb)(ui, ViewportClass::Immediate));
4177                    }),
4178                };
4179
4180                immediate_viewport_renderer(self, viewport);
4181            }
4182
4183            out.expect(
4184                "egui backend is implemented incorrectly - the user callback was never called",
4185            )
4186        })
4187    }
4188
4189    fn show_embedded_viewport<T>(
4190        &self,
4191        new_viewport_id: ViewportId,
4192        builder: ViewportBuilder,
4193        viewport_ui_cb: impl FnOnce(&mut Ui) -> T,
4194    ) -> T {
4195        crate::Window::from_viewport(new_viewport_id, builder)
4196            .collapsible(false)
4197            .show(self, |ui| viewport_ui_cb(ui))
4198            .unwrap_or_else(|| panic!("Window did not show"))
4199            .inner
4200            .unwrap_or_else(|| panic!("Window was collapsed"))
4201    }
4202}
4203
4204/// ## Interaction
4205impl Context {
4206    /// Read you what widgets are currently being interacted with.
4207    pub fn interaction_snapshot<R>(&self, reader: impl FnOnce(&InteractionSnapshot) -> R) -> R {
4208        self.write(|w| reader(&w.viewport().interact_widgets))
4209    }
4210
4211    /// The widget currently being dragged, if any.
4212    ///
4213    /// For widgets that sense both clicks and drags, this will
4214    /// not be set until the mouse cursor has moved a certain distance.
4215    ///
4216    /// NOTE: if the widget was released this pass, this will be `None`.
4217    /// Use [`Self::drag_stopped_id`] instead.
4218    pub fn dragged_id(&self) -> Option<Id> {
4219        self.interaction_snapshot(|i| i.dragged)
4220    }
4221
4222    /// Is this specific widget being dragged?
4223    ///
4224    /// A widget that sense both clicks and drags is only marked as "dragged"
4225    /// when the mouse has moved a bit.
4226    ///
4227    /// See also: [`crate::Response::dragged`].
4228    pub fn is_being_dragged(&self, id: Id) -> bool {
4229        self.dragged_id() == Some(id)
4230    }
4231
4232    /// This widget just started being dragged this pass.
4233    ///
4234    /// The same widget should also be found in [`Self::dragged_id`].
4235    pub fn drag_started_id(&self) -> Option<Id> {
4236        self.interaction_snapshot(|i| i.drag_started)
4237    }
4238
4239    /// This widget was being dragged, but was released this pass.
4240    pub fn drag_stopped_id(&self) -> Option<Id> {
4241        self.interaction_snapshot(|i| i.drag_stopped)
4242    }
4243
4244    /// Set which widget is being dragged.
4245    pub fn set_dragged_id(&self, id: Id) {
4246        self.write(|ctx| {
4247            let vp = ctx.viewport();
4248            let i = &mut vp.interact_widgets;
4249            if i.dragged != Some(id) {
4250                i.drag_stopped = i.dragged.or(i.drag_stopped);
4251                i.dragged = Some(id);
4252                i.drag_started = Some(id);
4253            }
4254
4255            ctx.memory.interaction_mut().potential_drag_id = Some(id);
4256        });
4257    }
4258
4259    /// Stop dragging any widget.
4260    pub fn stop_dragging(&self) {
4261        self.write(|ctx| {
4262            let vp = ctx.viewport();
4263            let i = &mut vp.interact_widgets;
4264            if i.dragged.is_some() {
4265                i.drag_stopped = i.dragged;
4266                i.dragged = None;
4267            }
4268
4269            ctx.memory.interaction_mut().potential_drag_id = None;
4270        });
4271    }
4272
4273    /// Is something else being dragged?
4274    ///
4275    /// Returns true if we are dragging something, but not the given widget.
4276    #[inline(always)]
4277    pub fn dragging_something_else(&self, not_this: Id) -> bool {
4278        let dragged = self.dragged_id();
4279        dragged.is_some() && dragged != Some(not_this)
4280    }
4281}
4282
4283#[test]
4284fn context_impl_send_sync() {
4285    fn assert_send_sync<T: Send + Sync>() {}
4286    assert_send_sync::<Context>();
4287}
4288
4289/// Check if any [`Rect`] appears with different [`Id`]s between two passes.
4290///
4291/// This helps detect cases where the same screen area is claimed by different widget ids
4292/// across passes, which is often a sign of id instability.
4293#[cfg(debug_assertions)]
4294fn warn_if_rect_changes_id(
4295    out_shapes: &mut Vec<ClippedShape>,
4296    prev_widgets: &crate::WidgetRects,
4297    new_widgets: &crate::WidgetRects,
4298) {
4299    profiling::function_scope!();
4300
4301    use std::collections::BTreeMap;
4302
4303    /// A wrapper around [`Rect`] that implements [`Ord`] using the bit representation of its floats.
4304    #[derive(Clone, Copy, PartialEq, Eq)]
4305    struct OrderedRect(Rect);
4306
4307    impl PartialOrd for OrderedRect {
4308        fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
4309            Some(self.cmp(other))
4310        }
4311    }
4312
4313    impl Ord for OrderedRect {
4314        fn cmp(&self, other: &Self) -> std::cmp::Ordering {
4315            let lhs = self.0;
4316            let rhs = other.0;
4317            lhs.min
4318                .x
4319                .to_bits()
4320                .cmp(&rhs.min.x.to_bits())
4321                .then(lhs.min.y.to_bits().cmp(&rhs.min.y.to_bits()))
4322                .then(lhs.max.x.to_bits().cmp(&rhs.max.x.to_bits()))
4323                .then(lhs.max.y.to_bits().cmp(&rhs.max.y.to_bits()))
4324        }
4325    }
4326
4327    fn create_lookup<'a>(
4328        widgets: impl Iterator<Item = &'a WidgetRect>,
4329    ) -> BTreeMap<OrderedRect, Vec<&'a WidgetRect>> {
4330        let mut lookup: BTreeMap<OrderedRect, Vec<&'a WidgetRect>> = BTreeMap::default();
4331        for w in widgets {
4332            lookup.entry(OrderedRect(w.rect)).or_default().push(w);
4333        }
4334        lookup
4335    }
4336
4337    for (layer_id, new_layer_widgets) in new_widgets.layers() {
4338        let prev = create_lookup(prev_widgets.get_layer(*layer_id));
4339        let new = create_lookup(new_layer_widgets.iter());
4340
4341        for (hashable_rect, new_at_rect) in new {
4342            let Some(prev_at_rect) = prev.get(&hashable_rect) else {
4343                continue; // this rect did not exist in the previous pass
4344            };
4345
4346            if prev_at_rect
4347                .iter()
4348                .any(|w| new_at_rect.iter().any(|nw| nw.id == w.id))
4349            {
4350                continue; // at least one id stayed the same, so this is not an id change
4351            }
4352
4353            // Only warn if at least one of the previous ids is gone from this layer entirely.
4354            // If they all still exist (just at a different rect), then the rect match
4355            // is just a coincidence caused by widgets shifting (e.g. a window being dragged).
4356            if prev_at_rect.iter().all(|w| new_widgets.contains(w.id)) {
4357                continue;
4358            }
4359
4360            // Only warn if at least one widget has the same parent_id in both frames.
4361            // If all parent_ids changed too, this is a cascading id shift, not a widget bug.
4362            if !prev_at_rect
4363                .iter()
4364                .any(|pw| new_at_rect.iter().any(|nw| nw.parent_id == pw.parent_id))
4365            {
4366                continue;
4367            }
4368
4369            let rect = new_at_rect[0].rect;
4370
4371            log::warn!(
4372                "Widget rect {rect:?} changed id between passes: prev ids: {:?}, new ids: {:?}",
4373                prev_at_rect
4374                    .iter()
4375                    .map(|w| w.id.short_debug_format())
4376                    .collect::<Vec<_>>(),
4377                new_at_rect
4378                    .iter()
4379                    .map(|w| w.id.short_debug_format())
4380                    .collect::<Vec<_>>(),
4381            );
4382            out_shapes.push(ClippedShape {
4383                clip_rect: Rect::EVERYTHING,
4384                shape: epaint::Shape::rect_stroke(
4385                    rect,
4386                    0,
4387                    (2.0, Color32::RED),
4388                    StrokeKind::Outside,
4389                ),
4390            });
4391        }
4392    }
4393}
4394
4395#[cfg(test)]
4396mod test {
4397    use super::Context;
4398
4399    #[test]
4400    fn test_single_pass() {
4401        let ctx = Context::default();
4402        ctx.options_mut(|o| o.max_passes = 1.try_into().unwrap());
4403
4404        // A single call, no request to discard:
4405        {
4406            let mut num_calls = 0;
4407            let output = ctx.run_ui(Default::default(), |ui| {
4408                num_calls += 1;
4409                assert_eq!(ui.output(|o| o.num_completed_passes), 0);
4410                assert!(!ui.output(|o| o.requested_discard()));
4411                assert!(!ui.will_discard());
4412            });
4413            assert_eq!(num_calls, 1);
4414            assert_eq!(output.platform_output.num_completed_passes, 1);
4415            assert!(!output.platform_output.requested_discard());
4416        }
4417
4418        // A single call, with a denied request to discard:
4419        {
4420            let mut num_calls = 0;
4421            let output = ctx.run_ui(Default::default(), |ui| {
4422                num_calls += 1;
4423                ui.request_discard("test");
4424                assert!(!ui.will_discard(), "The request should have been denied");
4425            });
4426            assert_eq!(num_calls, 1);
4427            assert_eq!(output.platform_output.num_completed_passes, 1);
4428            assert!(
4429                output.platform_output.requested_discard(),
4430                "The request should be reported"
4431            );
4432            assert_eq!(
4433                output
4434                    .platform_output
4435                    .request_discard_reasons
4436                    .first()
4437                    .unwrap()
4438                    .reason,
4439                "test"
4440            );
4441        }
4442    }
4443
4444    #[test]
4445    fn test_dual_pass() {
4446        let ctx = Context::default();
4447        ctx.options_mut(|o| o.max_passes = 2.try_into().unwrap());
4448
4449        // Normal single pass:
4450        {
4451            let mut num_calls = 0;
4452            let output = ctx.run_ui(Default::default(), |ui| {
4453                assert_eq!(ui.output(|o| o.num_completed_passes), 0);
4454                assert!(!ui.output(|o| o.requested_discard()));
4455                assert!(!ui.will_discard());
4456                num_calls += 1;
4457            });
4458            assert_eq!(num_calls, 1);
4459            assert_eq!(output.platform_output.num_completed_passes, 1);
4460            assert!(!output.platform_output.requested_discard());
4461        }
4462
4463        // Request discard once:
4464        {
4465            let mut num_calls = 0;
4466            let output = ctx.run_ui(Default::default(), |ui| {
4467                assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);
4468
4469                assert!(!ui.will_discard());
4470                if num_calls == 0 {
4471                    ui.request_discard("test");
4472                    assert!(ui.will_discard());
4473                }
4474
4475                num_calls += 1;
4476            });
4477            assert_eq!(num_calls, 2);
4478            assert_eq!(output.platform_output.num_completed_passes, 2);
4479            assert!(
4480                !output.platform_output.requested_discard(),
4481                "The request should have been cleared when fulfilled"
4482            );
4483        }
4484
4485        // Request discard twice:
4486        {
4487            let mut num_calls = 0;
4488            let output = ctx.run_ui(Default::default(), |ui| {
4489                assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);
4490
4491                assert!(!ui.will_discard());
4492                ui.request_discard("test");
4493                if num_calls == 0 {
4494                    assert!(ui.will_discard(), "First request granted");
4495                } else {
4496                    assert!(!ui.will_discard(), "Second request should be denied");
4497                }
4498
4499                num_calls += 1;
4500            });
4501            assert_eq!(num_calls, 2);
4502            assert_eq!(output.platform_output.num_completed_passes, 2);
4503            assert!(
4504                output.platform_output.requested_discard(),
4505                "The unfulfilled request should be reported"
4506            );
4507        }
4508    }
4509
4510    #[test]
4511    fn test_multi_pass() {
4512        let ctx = Context::default();
4513        ctx.options_mut(|o| o.max_passes = 10.try_into().unwrap());
4514
4515        // Request discard three times:
4516        {
4517            let mut num_calls = 0;
4518            let output = ctx.run_ui(Default::default(), |ui| {
4519                assert_eq!(ui.output(|o| o.num_completed_passes), num_calls);
4520
4521                assert!(!ui.will_discard());
4522                if num_calls <= 2 {
4523                    ui.request_discard("test");
4524                    assert!(ui.will_discard());
4525                }
4526
4527                num_calls += 1;
4528            });
4529            assert_eq!(num_calls, 4);
4530            assert_eq!(output.platform_output.num_completed_passes, 4);
4531            assert!(
4532                !output.platform_output.requested_discard(),
4533                "The request should have been cleared when fulfilled"
4534            );
4535        }
4536    }
4537}