Skip to main content

egui/
context.rs

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