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