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