egui/ui.rs
1#![warn(missing_docs)] // Let's keep `Ui` well-documented.
2#![expect(clippy::use_self)]
3
4use std::{any::Any, ops::Deref, sync::Arc};
5
6use crate::containers::menu;
7use crate::widget_style::{HasClasses as _, ROOT_CLASS};
8use crate::{IdSource, containers::*, ecolor::*, layout::*, placer::Placer, widgets::*, *};
9use emath::GuiRounding as _;
10
11// ----------------------------------------------------------------------------
12
13/// This is what you use to place widgets.
14///
15/// Represents a region of the screen with a type of layout (horizontal or vertical).
16///
17/// ```
18/// # egui::__run_test_ui(|ui| {
19/// ui.add(egui::Label::new("Hello World!"));
20/// ui.label("A shorter and more convenient way to add a label.");
21/// ui.horizontal(|ui| {
22/// ui.label("Add widgets");
23/// if ui.button("on the same row!").clicked() {
24/// /* … */
25/// }
26/// });
27/// # });
28/// ```
29pub struct Ui {
30 /// Generated based on id of parent ui together with an optional id salt.
31 ///
32 /// This should be stable from one frame to next
33 /// so it can be used as a source for storing state
34 /// (e.g. window position, or if a collapsing header is open).
35 ///
36 /// However, it is not necessarily globally unique.
37 /// For instance, sibling `Ui`s share the same [`Self::id`]
38 /// unless they where explicitly given different id salts using
39 /// [`UiBuilder::id_salt`].
40 id: Id,
41
42 /// This is a globally unique ID of this `Ui`,
43 /// based on where in the hierarchy of widgets this Ui is in.
44 ///
45 /// This means it is not _stable_, as it can change if new widgets
46 /// are added or removed prior to this one.
47 /// It should therefore only be used for transient interactions (clicks etc),
48 /// not for storing state over time.
49 unique_id: Id,
50
51 /// This is used to create a unique interact ID for some widgets.
52 ///
53 /// This value is based on where in the hierarchy of widgets this Ui is in,
54 /// and the value is increment with each added child widget.
55 /// This works as an Id source only as long as new widgets aren't added or removed.
56 /// They are therefore only good for Id:s that have no state.
57 next_auto_id_salt: u64,
58
59 /// Specifies paint layer, clip rectangle and a reference to [`Context`].
60 painter: Painter,
61
62 /// The [`Style`] (visuals, spacing, etc) of this ui.
63 /// Commonly many [`Ui`]s share the same [`Style`].
64 /// The [`Ui`] implements copy-on-write for this.
65 style: Arc<Style>,
66
67 /// Handles the [`Ui`] size and the placement of new widgets.
68 placer: Placer,
69
70 /// If false we are unresponsive to input,
71 /// and all widgets will assume a gray style.
72 enabled: bool,
73
74 /// Set to true in special cases where we do one frame
75 /// where we size up the contents of the Ui, without actually showing it.
76 sizing_pass: bool,
77
78 /// The [`UiStack`] for this [`Ui`].
79 stack: Arc<UiStack>,
80
81 /// The sense for the ui background.
82 sense: Sense,
83
84 /// Whether [`Ui::remember_min_rect`] should be called when the [`Ui`] is dropped.
85 /// This is an optimization, so we don't call [`Ui::remember_min_rect`] multiple times at the
86 /// end of a [`Ui::scope`].
87 min_rect_already_remembered: bool,
88}
89
90/// Allow using [`Ui`] like a [`Context`].
91impl Deref for Ui {
92 type Target = Context;
93
94 #[inline]
95 fn deref(&self) -> &Self::Target {
96 self.ctx()
97 }
98}
99
100impl Ui {
101 // ------------------------------------------------------------------------
102 // Creation:
103
104 /// Create a new top-level [`Ui`].
105 ///
106 /// Normally you would not use this directly, but instead use
107 /// [`crate::Panel`], [`crate::CentralPanel`], [`crate::Window`] or [`crate::Area`].
108 pub fn new(ctx: Context, id: Id, ui_builder: UiBuilder) -> Self {
109 let UiBuilder {
110 id_source,
111 ui_stack_info,
112 layer_id,
113 max_rect,
114 layout,
115 disabled,
116 invisible,
117 sizing_pass,
118 style,
119 sense,
120 accessibility_parent,
121 classes,
122 } = ui_builder;
123
124 let layer_id = layer_id.unwrap_or_else(LayerId::background);
125
126 debug_assert!(
127 id_source.is_none(),
128 "Top-level Ui:s should not have an UiBuilder::id_source"
129 );
130
131 let max_rect = max_rect.unwrap_or_else(|| ctx.content_rect());
132 let clip_rect = max_rect;
133 let layout = layout.unwrap_or_default();
134 let disabled = disabled || invisible;
135 let style = style.unwrap_or_else(|| ctx.global_style());
136 let sense = sense.unwrap_or_else(Sense::hover);
137 let classes = classes.with_class(ROOT_CLASS);
138
139 let placer = Placer::new(max_rect, layout);
140 let ui_stack = UiStack {
141 id,
142 layout_direction: layout.main_dir,
143 info: ui_stack_info,
144 parent: None,
145 min_rect: placer.min_rect(),
146 max_rect: placer.max_rect(),
147 classes,
148 };
149
150 let mut ui = Ui {
151 id,
152 unique_id: id,
153 next_auto_id_salt: id.with("auto").value(),
154 painter: Painter::new(ctx, layer_id, clip_rect),
155 style,
156 placer,
157 enabled: true,
158 sizing_pass,
159 stack: Arc::new(ui_stack),
160 sense,
161 min_rect_already_remembered: false,
162 };
163
164 if let Some(accessibility_parent) = accessibility_parent {
165 ui.ctx()
166 .register_accesskit_parent(ui.unique_id, accessibility_parent);
167 }
168
169 // Register in the widget stack early, to ensure we are behind all widgets we contain:
170 let start_rect = Rect::NOTHING; // This will be overwritten when `remember_min_rect` is called
171 ui.ctx().create_widget(
172 WidgetRect {
173 id: ui.unique_id,
174 parent_id: ui.id,
175 layer_id: ui.layer_id(),
176 rect: start_rect,
177 interact_rect: start_rect,
178 sense,
179 enabled: ui.enabled,
180 },
181 true,
182 Default::default(),
183 );
184
185 if disabled {
186 ui.disable();
187 }
188 if invisible {
189 ui.set_invisible();
190 }
191
192 ui.ctx().accesskit_node_builder(ui.unique_id, |node| {
193 node.set_role(accesskit::Role::GenericContainer);
194 });
195
196 ui
197 }
198
199 /// Create a child `Ui` with the properties of the given builder.
200 ///
201 /// This is a very low-level function.
202 /// Usually you are better off using [`Self::scope_builder`].
203 ///
204 /// Note that calling this does not allocate any space in the parent `Ui`,
205 /// so after adding widgets to the child `Ui` you probably want to allocate
206 /// the [`Ui::min_rect`] of the child in the parent `Ui` using e.g.
207 /// [`Ui::advance_cursor_after_rect`].
208 pub fn new_child(&mut self, ui_builder: UiBuilder) -> Self {
209 let UiBuilder {
210 id_source,
211 ui_stack_info,
212 layer_id,
213 max_rect,
214 layout,
215 disabled,
216 invisible,
217 sizing_pass,
218 style,
219 sense,
220 accessibility_parent,
221 classes,
222 } = ui_builder;
223
224 let mut painter = self.painter.clone();
225
226 let max_rect = max_rect.unwrap_or_else(|| self.available_rect_before_wrap());
227 let mut layout = layout.unwrap_or_else(|| *self.layout());
228 let enabled = self.enabled && !disabled && !invisible;
229 if let Some(layer_id) = layer_id {
230 painter.set_layer_id(layer_id);
231 }
232 if invisible {
233 painter.set_invisible();
234 }
235 let sizing_pass = self.sizing_pass || sizing_pass;
236 let style = style.unwrap_or_else(|| Arc::clone(&self.style));
237 let sense = sense.unwrap_or_else(Sense::hover);
238
239 if sizing_pass {
240 // During the sizing pass we want widgets to use up as little space as possible,
241 // so that we measure the only the space we _need_.
242 layout.cross_justify = false;
243 if layout.cross_align == Align::Center {
244 layout.cross_align = Align::Min;
245 }
246 }
247
248 debug_assert!(!max_rect.any_nan(), "max_rect is NaN: {max_rect:?}");
249
250 let id_source = id_source.unwrap_or_else(|| IdSource::Child(IdSalt::new("child")));
251 let (stable_id, unique_id) = match id_source {
252 IdSource::Explicit(id) => (id, id),
253 IdSource::Child(id_salt) => {
254 let stable_id = self.id.with(id_salt);
255 let unique_id = stable_id.with(self.next_auto_id_salt);
256 (stable_id, unique_id)
257 }
258 };
259 let next_auto_id_salt = unique_id.value().wrapping_add(1);
260
261 self.next_auto_id_salt = self.next_auto_id_salt.wrapping_add(1);
262
263 let placer = Placer::new(max_rect, layout);
264 let ui_stack = UiStack {
265 id: unique_id,
266 layout_direction: layout.main_dir,
267 info: ui_stack_info,
268 parent: Some(Arc::clone(&self.stack)),
269 min_rect: placer.min_rect(),
270 max_rect: placer.max_rect(),
271 classes,
272 };
273
274 let mut child_ui = Ui {
275 id: stable_id,
276 unique_id,
277 next_auto_id_salt,
278 painter,
279 style,
280 placer,
281 enabled,
282 sizing_pass,
283 stack: Arc::new(ui_stack),
284 sense,
285 min_rect_already_remembered: false,
286 };
287
288 if disabled {
289 child_ui.disable();
290 }
291
292 child_ui.ctx().register_accesskit_parent(
293 child_ui.unique_id,
294 accessibility_parent.unwrap_or(self.unique_id),
295 );
296
297 // Register in the widget stack early, to ensure we are behind all widgets we contain:
298 let start_rect = Rect::NOTHING; // This will be overwritten when `remember_min_rect` is called
299 child_ui.ctx().create_widget(
300 WidgetRect {
301 id: child_ui.unique_id,
302 parent_id: self.id,
303 layer_id: child_ui.layer_id(),
304 rect: start_rect,
305 interact_rect: start_rect,
306 sense,
307 enabled: child_ui.enabled,
308 },
309 true,
310 Default::default(),
311 );
312
313 child_ui
314 .ctx()
315 .accesskit_node_builder(child_ui.unique_id, |node| {
316 node.set_role(accesskit::Role::GenericContainer);
317 });
318
319 child_ui
320 }
321
322 // -------------------------------------------------
323
324 /// Set to true in special cases where we do one frame
325 /// where we size up the contents of the Ui, without actually showing it.
326 #[inline]
327 pub fn is_sizing_pass(&self) -> bool {
328 self.sizing_pass
329 }
330
331 // -------------------------------------------------
332
333 /// Generated based on id of parent ui together with an optional id salt.
334 ///
335 /// This should be stable from one frame to next
336 /// so it can be used as a source for storing state
337 /// (e.g. window position, or if a collapsing header is open).
338 ///
339 /// However, it is not necessarily globally unique.
340 /// For instance, sibling `Ui`s share the same [`Self::id`]
341 /// unless they were explicitly given different id salts using
342 /// [`UiBuilder::id_salt`].
343 #[inline]
344 pub fn id(&self) -> Id {
345 self.id
346 }
347
348 /// This is a globally unique ID of this `Ui`,
349 /// based on where in the hierarchy of widgets this Ui is in.
350 ///
351 /// This means it is not _stable_, as it can change if new widgets
352 /// are added or removed prior to this one.
353 /// It should therefore only be used for transient interactions (clicks etc),
354 /// not for storing state over time.
355 #[inline]
356 pub fn unique_id(&self) -> Id {
357 self.unique_id
358 }
359
360 /// Style options for this [`Ui`] and its children.
361 ///
362 /// Note that this may be a different [`Style`] than that of [`Context::global_style`].
363 #[inline]
364 pub fn style(&self) -> &Arc<Style> {
365 &self.style
366 }
367
368 /// Mutably borrow internal [`Style`].
369 /// Changes apply to this [`Ui`] and its subsequent children.
370 ///
371 /// To set the style of all [`Ui`]s, use [`Context::set_style_of`].
372 ///
373 /// Example:
374 /// ```
375 /// # egui::__run_test_ui(|ui| {
376 /// ui.style_mut().override_text_style = Some(egui::TextStyle::Heading);
377 /// # });
378 /// ```
379 pub fn style_mut(&mut self) -> &mut Style {
380 Arc::make_mut(&mut self.style) // clone-on-write
381 }
382
383 /// Changes apply to this [`Ui`] and its subsequent children.
384 ///
385 /// To set the style of all [`Ui`]s, use [`Context::set_style_of`].
386 pub fn set_style(&mut self, style: impl Into<Arc<Style>>) {
387 self.style = style.into();
388 }
389
390 /// Reset to the default style set in [`Context`].
391 pub fn reset_style(&mut self) {
392 self.style = self.ctx().global_style();
393 }
394
395 /// The current spacing options for this [`Ui`].
396 /// Short for `ui.style().spacing`.
397 #[inline]
398 pub fn spacing(&self) -> &crate::style::Spacing {
399 &self.style.spacing
400 }
401
402 /// Mutably borrow internal [`Spacing`].
403 /// Changes apply to this [`Ui`] and its subsequent children.
404 ///
405 /// Example:
406 /// ```
407 /// # egui::__run_test_ui(|ui| {
408 /// ui.spacing_mut().item_spacing = egui::vec2(10.0, 2.0);
409 /// # });
410 /// ```
411 pub fn spacing_mut(&mut self) -> &mut crate::style::Spacing {
412 &mut self.style_mut().spacing
413 }
414
415 /// The current visuals settings of this [`Ui`].
416 /// Short for `ui.style().visuals`.
417 #[inline]
418 pub fn visuals(&self) -> &crate::Visuals {
419 &self.style.visuals
420 }
421
422 /// Mutably borrow internal `visuals`.
423 /// Changes apply to this [`Ui`] and its subsequent children.
424 ///
425 /// To set the visuals of all [`Ui`]s, use [`Context::set_visuals_of`].
426 ///
427 /// Example:
428 /// ```
429 /// # egui::__run_test_ui(|ui| {
430 /// ui.visuals_mut().override_text_color = Some(egui::Color32::RED);
431 /// # });
432 /// ```
433 pub fn visuals_mut(&mut self) -> &mut crate::Visuals {
434 &mut self.style_mut().visuals
435 }
436
437 /// Is this [`Ui`] in a tooltip?
438 #[inline]
439 pub fn is_tooltip(&self) -> bool {
440 self.layer_id().order == Order::Tooltip
441 }
442
443 /// Get a reference to this [`Ui`]'s [`UiStack`].
444 #[inline]
445 pub fn stack(&self) -> &Arc<UiStack> {
446 &self.stack
447 }
448
449 /// Get a reference to the parent [`Context`].
450 #[inline]
451 pub fn ctx(&self) -> &Context {
452 self.painter.ctx()
453 }
454
455 /// Use this to paint stuff within this [`Ui`].
456 #[inline]
457 pub fn painter(&self) -> &Painter {
458 &self.painter
459 }
460
461 /// Number of physical pixels for each logical UI point.
462 #[inline]
463 pub fn pixels_per_point(&self) -> f32 {
464 self.painter.pixels_per_point()
465 }
466
467 /// If `false`, the [`Ui`] does not allow any interaction and
468 /// the widgets in it will draw with a gray look.
469 #[inline]
470 pub fn is_enabled(&self) -> bool {
471 self.enabled
472 }
473
474 /// Calling `disable()` will cause the [`Ui`] to deny all future interaction
475 /// and all the widgets will draw with a gray look.
476 ///
477 /// Usually it is more convenient to use [`Self::add_enabled_ui`] or [`Self::add_enabled`].
478 ///
479 /// Note that once disabled, there is no way to re-enable the [`Ui`].
480 ///
481 /// ### Example
482 /// ```
483 /// # egui::__run_test_ui(|ui| {
484 /// # let mut enabled = true;
485 /// ui.group(|ui| {
486 /// ui.checkbox(&mut enabled, "Enable subsection");
487 /// if !enabled {
488 /// ui.disable();
489 /// }
490 /// if ui.button("Button that is not always clickable").clicked() {
491 /// /* … */
492 /// }
493 /// });
494 /// # });
495 /// ```
496 pub fn disable(&mut self) {
497 self.enabled = false;
498 if self.is_visible() {
499 self.painter
500 .multiply_opacity(self.visuals().disabled_alpha());
501 }
502 }
503
504 /// If `false`, any widgets added to the [`Ui`] will be invisible and non-interactive.
505 ///
506 /// This is `false` if any parent had [`UiBuilder::invisible`]
507 /// or if [`Context::will_discard`].
508 #[inline]
509 pub fn is_visible(&self) -> bool {
510 self.painter.is_visible()
511 }
512
513 /// Calling `set_invisible()` will cause all further widgets to be invisible,
514 /// yet still allocate space.
515 ///
516 /// The widgets will not be interactive (`set_invisible()` implies `disable()`).
517 ///
518 /// Once invisible, there is no way to make the [`Ui`] visible again.
519 ///
520 /// Usually it is more convenient to use [`Self::add_visible`].
521 ///
522 /// ### Example
523 /// ```
524 /// # egui::__run_test_ui(|ui| {
525 /// # let mut visible = true;
526 /// ui.group(|ui| {
527 /// ui.checkbox(&mut visible, "Show subsection");
528 /// if !visible {
529 /// ui.set_invisible();
530 /// }
531 /// if ui.button("Button that is not always shown").clicked() {
532 /// /* … */
533 /// }
534 /// });
535 /// # });
536 /// ```
537 pub fn set_invisible(&mut self) {
538 self.painter.set_invisible();
539 self.disable();
540 }
541
542 /// Make the widget in this [`Ui`] semi-transparent.
543 ///
544 /// `opacity` must be between 0.0 and 1.0, where 0.0 means fully transparent (i.e., invisible)
545 /// and 1.0 means fully opaque.
546 ///
547 /// ### Example
548 /// ```
549 /// # egui::__run_test_ui(|ui| {
550 /// ui.group(|ui| {
551 /// ui.set_opacity(0.5);
552 /// if ui.button("Half-transparent button").clicked() {
553 /// /* … */
554 /// }
555 /// });
556 /// # });
557 /// ```
558 ///
559 /// See also: [`Self::opacity`] and [`Self::multiply_opacity`].
560 pub fn set_opacity(&mut self, opacity: f32) {
561 self.painter.set_opacity(opacity);
562 }
563
564 /// Like [`Self::set_opacity`], but multiplies the given value with the current opacity.
565 ///
566 /// See also: [`Self::set_opacity`] and [`Self::opacity`].
567 pub fn multiply_opacity(&mut self, opacity: f32) {
568 self.painter.multiply_opacity(opacity);
569 }
570
571 /// Read the current opacity of the underlying painter.
572 ///
573 /// See also: [`Self::set_opacity`] and [`Self::multiply_opacity`].
574 #[inline]
575 pub fn opacity(&self) -> f32 {
576 self.painter.opacity()
577 }
578
579 /// Read the [`Layout`].
580 #[inline]
581 pub fn layout(&self) -> &Layout {
582 self.placer.layout()
583 }
584
585 /// Which wrap mode should the text use in this [`Ui`]?
586 ///
587 /// This is determined first by [`Style::wrap_mode`], and then by the layout of this [`Ui`].
588 pub fn wrap_mode(&self) -> TextWrapMode {
589 if let Some(wrap_mode) = self.style.wrap_mode {
590 wrap_mode
591 } else if let Some(grid) = self.placer.grid() {
592 if grid.wrap_text() {
593 TextWrapMode::Wrap
594 } else {
595 TextWrapMode::Extend
596 }
597 } else {
598 let layout = self.layout();
599 if layout.is_vertical() || layout.is_horizontal() && layout.main_wrap() {
600 TextWrapMode::Wrap
601 } else {
602 TextWrapMode::Extend
603 }
604 }
605 }
606
607 /// How to vertically align text
608 #[inline]
609 pub fn text_valign(&self) -> Align {
610 self.style()
611 .override_text_valign
612 .unwrap_or_else(|| self.layout().vertical_align())
613 }
614
615 /// Create a painter for a sub-region of this Ui.
616 ///
617 /// The clip-rect of the returned [`Painter`] will be the intersection
618 /// of the given rectangle and the `clip_rect()` of this [`Ui`].
619 pub fn painter_at(&self, rect: Rect) -> Painter {
620 self.painter().with_clip_rect(rect)
621 }
622
623 /// Use this to paint stuff within this [`Ui`].
624 #[inline]
625 pub fn layer_id(&self) -> LayerId {
626 self.painter().layer_id()
627 }
628
629 /// The height of text of this text style.
630 ///
631 /// Returns a value rounded to [`emath::GUI_ROUNDING`].
632 pub fn text_style_height(&self, style: &TextStyle) -> f32 {
633 self.fonts_mut(|f| f.row_height(&style.resolve(self.style())))
634 }
635
636 /// Screen-space rectangle for clipping what we paint in this ui.
637 /// This is used, for instance, to avoid painting outside a window that is smaller than its contents.
638 #[inline]
639 pub fn clip_rect(&self) -> Rect {
640 self.painter.clip_rect()
641 }
642
643 /// Constrain the rectangle in which we can paint.
644 ///
645 /// Short for `ui.set_clip_rect(ui.clip_rect().intersect(new_clip_rect))`.
646 ///
647 /// See also: [`Self::clip_rect`] and [`Self::set_clip_rect`].
648 #[inline]
649 pub fn shrink_clip_rect(&mut self, new_clip_rect: Rect) {
650 self.painter.shrink_clip_rect(new_clip_rect);
651 }
652
653 /// Screen-space rectangle for clipping what we paint in this ui.
654 /// This is used, for instance, to avoid painting outside a window that is smaller than its contents.
655 ///
656 /// Warning: growing the clip rect might cause unexpected results!
657 /// When in doubt, use [`Self::shrink_clip_rect`] instead.
658 pub fn set_clip_rect(&mut self, clip_rect: Rect) {
659 self.painter.set_clip_rect(clip_rect);
660 }
661
662 /// Can be used for culling: if `false`, then no part of `rect` will be visible on screen.
663 ///
664 /// This is false if the whole `Ui` is invisible (see [`UiBuilder::invisible`])
665 /// or if [`Context::will_discard`] is true.
666 pub fn is_rect_visible(&self, rect: Rect) -> bool {
667 self.is_visible() && rect.intersects(self.clip_rect())
668 }
669}
670
671// ------------------------------------------------------------------------
672
673/// # Sizes etc
674impl Ui {
675 /// Where and how large the [`Ui`] is already.
676 /// All widgets that have been added to this [`Ui`] fits within this rectangle.
677 ///
678 /// No matter what, the final Ui will be at least this large.
679 ///
680 /// This will grow as new widgets are added, but never shrink.
681 pub fn min_rect(&self) -> Rect {
682 self.placer.min_rect()
683 }
684
685 /// Size of content; same as `min_rect().size()`
686 pub fn min_size(&self) -> Vec2 {
687 self.min_rect().size()
688 }
689
690 /// New widgets will *try* to fit within this rectangle.
691 ///
692 /// Text labels will wrap to fit within `max_rect`.
693 /// Separator lines will span the `max_rect`.
694 ///
695 /// If a new widget doesn't fit within the `max_rect` then the
696 /// [`Ui`] will make room for it by expanding both `min_rect` and `max_rect`.
697 pub fn max_rect(&self) -> Rect {
698 self.placer.max_rect()
699 }
700
701 /// Used for animation, kind of hacky
702 pub(crate) fn force_set_min_rect(&mut self, min_rect: Rect) {
703 self.placer.force_set_min_rect(min_rect);
704 }
705
706 // ------------------------------------------------------------------------
707
708 /// Set the maximum size of the ui.
709 /// You won't be able to shrink it below the current minimum size.
710 pub fn set_max_size(&mut self, size: Vec2) {
711 self.set_max_width(size.x);
712 self.set_max_height(size.y);
713 }
714
715 /// Set the maximum width of the ui.
716 /// You won't be able to shrink it below the current minimum size.
717 pub fn set_max_width(&mut self, width: f32) {
718 self.placer.set_max_width(width);
719 }
720
721 /// Set the maximum height of the ui.
722 /// You won't be able to shrink it below the current minimum size.
723 pub fn set_max_height(&mut self, height: f32) {
724 self.placer.set_max_height(height);
725 }
726
727 // ------------------------------------------------------------------------
728
729 /// Set the minimum size of the ui.
730 /// This can't shrink the ui, only make it larger.
731 pub fn set_min_size(&mut self, size: Vec2) {
732 self.set_min_width(size.x);
733 self.set_min_height(size.y);
734 }
735
736 /// Set the minimum width of the ui.
737 /// This can't shrink the ui, only make it larger.
738 pub fn set_min_width(&mut self, width: f32) {
739 debug_assert!(
740 0.0 <= width,
741 "Negative width makes no sense, but got: {width}"
742 );
743 self.placer.set_min_width(width);
744 }
745
746 /// Set the minimum height of the ui.
747 /// This can't shrink the ui, only make it larger.
748 pub fn set_min_height(&mut self, height: f32) {
749 debug_assert!(
750 0.0 <= height,
751 "Negative height makes no sense, but got: {height}"
752 );
753 self.placer.set_min_height(height);
754 }
755
756 /// Makes the ui always fill up the available space.
757 ///
758 /// This can be useful to call inside a panel with `resizable == true`
759 /// to make sure the resized space is used.
760 pub fn take_available_space(&mut self) {
761 self.set_min_size(self.available_size());
762 }
763
764 /// Makes the ui always fill up the available space in the x axis.
765 ///
766 /// This can be useful to call inside a side panel with
767 /// `resizable == true` to make sure the resized space is used.
768 pub fn take_available_width(&mut self) {
769 self.set_min_width(self.available_width());
770 }
771
772 /// Makes the ui always fill up the available space in the y axis.
773 ///
774 /// This can be useful to call inside a top bottom panel with
775 /// `resizable == true` to make sure the resized space is used.
776 pub fn take_available_height(&mut self) {
777 self.set_min_height(self.available_height());
778 }
779
780 // ------------------------------------------------------------------------
781
782 /// Helper: shrinks the max width to the current width,
783 /// so further widgets will try not to be wider than previous widgets.
784 /// Useful for normal vertical layouts.
785 pub fn shrink_width_to_current(&mut self) {
786 self.set_max_width(self.min_rect().width());
787 }
788
789 /// Helper: shrinks the max height to the current height,
790 /// so further widgets will try not to be taller than previous widgets.
791 pub fn shrink_height_to_current(&mut self) {
792 self.set_max_height(self.min_rect().height());
793 }
794
795 /// Expand the `min_rect` and `max_rect` of this ui to include a child at the given rect.
796 pub fn expand_to_include_rect(&mut self, rect: Rect) {
797 self.placer.expand_to_include_rect(rect);
798 }
799
800 /// `ui.set_width_range(min..=max);` is equivalent to `ui.set_min_width(min); ui.set_max_width(max);`.
801 pub fn set_width_range(&mut self, width: impl Into<Rangef>) {
802 let width = width.into();
803 self.set_min_width(width.min);
804 self.set_max_width(width.max);
805 }
806
807 /// `ui.set_height_range(min..=max);` is equivalent to `ui.set_min_height(min); ui.set_max_height(max);`.
808 pub fn set_height_range(&mut self, height: impl Into<Rangef>) {
809 let height = height.into();
810 self.set_min_height(height.min);
811 self.set_max_height(height.max);
812 }
813
814 /// Set both the minimum and maximum width.
815 pub fn set_width(&mut self, width: f32) {
816 self.set_min_width(width);
817 self.set_max_width(width);
818 }
819
820 /// Set both the minimum and maximum height.
821 pub fn set_height(&mut self, height: f32) {
822 self.set_min_height(height);
823 self.set_max_height(height);
824 }
825
826 /// Ensure we are big enough to contain the given x-coordinate.
827 /// This is sometimes useful to expand a ui to stretch to a certain place.
828 pub fn expand_to_include_x(&mut self, x: f32) {
829 self.placer.expand_to_include_x(x);
830 }
831
832 /// Ensure we are big enough to contain the given y-coordinate.
833 /// This is sometimes useful to expand a ui to stretch to a certain place.
834 pub fn expand_to_include_y(&mut self, y: f32) {
835 self.placer.expand_to_include_y(y);
836 }
837
838 // ------------------------------------------------------------------------
839 // Layout related measures:
840
841 /// The available space at the moment, given the current cursor.
842 ///
843 /// This how much more space we can take up without overflowing our parent.
844 /// Shrinks as widgets allocate space and the cursor moves.
845 /// A small size should be interpreted as "as little as possible".
846 /// An infinite size should be interpreted as "as much as you want".
847 pub fn available_size(&self) -> Vec2 {
848 self.placer.available_size()
849 }
850
851 /// The available width at the moment, given the current cursor.
852 ///
853 /// See [`Self::available_size`] for more information.
854 pub fn available_width(&self) -> f32 {
855 self.available_size().x
856 }
857
858 /// The available height at the moment, given the current cursor.
859 ///
860 /// See [`Self::available_size`] for more information.
861 pub fn available_height(&self) -> f32 {
862 self.available_size().y
863 }
864
865 /// In case of a wrapping layout, how much space is left on this row/column?
866 ///
867 /// If the layout does not wrap, this will return the same value as [`Self::available_size`].
868 pub fn available_size_before_wrap(&self) -> Vec2 {
869 self.placer.available_rect_before_wrap().size()
870 }
871
872 /// In case of a wrapping layout, how much space is left on this row/column?
873 ///
874 /// If the layout does not wrap, this will return the same value as [`Self::available_size`].
875 pub fn available_rect_before_wrap(&self) -> Rect {
876 self.placer.available_rect_before_wrap()
877 }
878}
879
880/// # [`Id`] creation
881impl Ui {
882 /// Use this to generate widget ids for widgets that have persistent state in [`Memory`].
883 pub fn make_persistent_id(&self, id_salt: impl AsIdSalt) -> Id {
884 self.id.with(id_salt)
885 }
886
887 /// This is the `Id` that will be assigned to the next widget added to this `Ui`.
888 pub fn next_auto_id(&self) -> Id {
889 Id::new(self.next_auto_id_salt)
890 }
891
892 /// Same as `ui.next_auto_id().with(id_salt)`
893 pub fn auto_id_with(&self, id_salt: impl AsIdSalt) -> Id {
894 Id::new(self.next_auto_id_salt).with(id_salt)
895 }
896
897 /// Pretend like `count` widgets have been allocated.
898 pub fn skip_ahead_auto_ids(&mut self, count: usize) {
899 self.next_auto_id_salt = self.next_auto_id_salt.wrapping_add(count as u64);
900 }
901}
902
903/// # Interaction
904impl Ui {
905 /// Check for clicks, drags and/or hover on a specific region of this [`Ui`].
906 pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response {
907 self.interact_opt(rect, id, sense, Default::default())
908 }
909
910 /// Check for clicks, drags and/or hover on a specific region of this [`Ui`].
911 pub fn interact_opt(
912 &self,
913 rect: Rect,
914 id: Id,
915 sense: Sense,
916 options: crate::InteractOptions,
917 ) -> Response {
918 self.ctx().register_accesskit_parent(id, self.unique_id);
919
920 self.ctx().create_widget(
921 WidgetRect {
922 id,
923 parent_id: self.id,
924 layer_id: self.layer_id(),
925 rect,
926 interact_rect: self.clip_rect().intersect(rect),
927 sense,
928 enabled: self.enabled,
929 },
930 true,
931 options,
932 )
933 }
934
935 /// Read the [`Ui`]'s background [`Response`].
936 /// Its [`Sense`] will be based on the [`UiBuilder::sense`] used to create this [`Ui`].
937 ///
938 /// The rectangle of the [`Response`] (and interactive area) will be [`Self::min_rect`]
939 /// of the last pass.
940 ///
941 /// The very first time when the [`Ui`] is created, this will return a [`Response`] with a
942 /// [`Rect`] of [`Rect::NOTHING`].
943 pub fn response(&self) -> Response {
944 // This is the inverse of Context::read_response. We prefer a response
945 // based on last frame's widget rect since the one from this frame is Rect::NOTHING until
946 // Ui::remember_min_rect is called or the Ui is dropped.
947 let mut response = self
948 .ctx()
949 .viewport(|viewport| {
950 viewport
951 .prev_pass
952 .widgets
953 .get(self.unique_id)
954 .or_else(|| viewport.this_pass.widgets.get(self.unique_id))
955 .copied()
956 })
957 .map(|widget_rect| self.ctx().get_response(widget_rect))
958 .expect(
959 "Since we always call Context::create_widget in Ui::new, this should never be None",
960 );
961 if self.should_close() {
962 response.set_close();
963 }
964 response
965 }
966
967 /// Update the [`WidgetRect`] created in [`Ui::new`] or [`Ui::new_child`] with the current
968 /// [`Ui::min_rect`].
969 fn remember_min_rect(&mut self) -> Response {
970 self.min_rect_already_remembered = true;
971 // We remove the id from used_ids to prevent a duplicate id warning from showing
972 // when the ui was created with `UiBuilder::sense`.
973 // This is a bit hacky, is there a better way?
974 self.ctx().pass_state_mut(|fs| {
975 fs.used_ids.remove(&self.unique_id);
976 });
977 // This will update the WidgetRect that was first created in `Ui::new`.
978 let mut response = self.ctx().create_widget(
979 WidgetRect {
980 id: self.unique_id,
981 parent_id: self.id,
982 layer_id: self.layer_id(),
983 rect: self.min_rect(),
984 interact_rect: self.clip_rect().intersect(self.min_rect()),
985 sense: self.sense,
986 enabled: self.enabled,
987 },
988 false,
989 Default::default(),
990 );
991 if self.should_close() {
992 response.set_close();
993 }
994 response
995 }
996
997 /// Is the pointer (mouse/touch) above this rectangle in this [`Ui`]?
998 ///
999 /// The `clip_rect` and layer of this [`Ui`] will be respected, so, for instance,
1000 /// if this [`Ui`] is behind some other window, this will always return `false`.
1001 ///
1002 /// However, this will NOT check if any other _widget_ in the same layer is covering this widget. For that, use [`Response::contains_pointer`] instead.
1003 pub fn rect_contains_pointer(&self, rect: Rect) -> bool {
1004 self.ctx()
1005 .rect_contains_pointer(self.layer_id(), self.clip_rect().intersect(rect))
1006 }
1007
1008 /// Is the pointer (mouse/touch) above the current [`Ui`]?
1009 ///
1010 /// Equivalent to `ui.rect_contains_pointer(ui.min_rect())`
1011 ///
1012 /// Note that this tests against the _current_ [`Ui::min_rect`].
1013 /// If you want to test against the final `min_rect`,
1014 /// use [`Self::response`] instead.
1015 pub fn ui_contains_pointer(&self) -> bool {
1016 self.rect_contains_pointer(self.min_rect())
1017 }
1018
1019 /// Find and close the first closable parent.
1020 ///
1021 /// Use [`UiBuilder::closable`] to make a [`Ui`] closable.
1022 /// You can then use [`Ui::should_close`] to check if it should be closed.
1023 ///
1024 /// This is implemented for all egui containers, e.g. [`crate::Popup`], [`crate::Modal`],
1025 /// [`crate::Area`], [`crate::Window`], [`crate::CollapsingHeader`], etc.
1026 ///
1027 /// What exactly happens when you close a container depends on the container implementation.
1028 /// [`crate::Area`] e.g. will return true from its [`Response::should_close`] method.
1029 ///
1030 /// If you want to close a specific kind of container, use [`Ui::close_kind`] instead.
1031 ///
1032 /// Also note that this won't bubble up across [`crate::Area`]s. If needed, you can check
1033 /// `response.should_close()` and close the parent manually. ([`menu`] does this for example).
1034 ///
1035 /// See also:
1036 /// - [`Ui::close_kind`]
1037 /// - [`Ui::should_close`]
1038 /// - [`Ui::will_parent_close`]
1039 pub fn close(&self) {
1040 let tag = self.stack.iter().find_map(|stack| {
1041 stack
1042 .info
1043 .tags
1044 .get_downcast::<ClosableTag>(ClosableTag::NAME)
1045 });
1046 if let Some(tag) = tag {
1047 tag.set_close();
1048 } else {
1049 log::warn!("Called ui.close() on a Ui that has no closable parent.");
1050 }
1051 }
1052
1053 /// Find and close the first closable parent of a specific [`UiKind`].
1054 ///
1055 /// This is useful if you want to e.g. close a [`crate::Window`]. Since it contains a
1056 /// `Collapsible`, [`Ui::close`] would close the `Collapsible` instead.
1057 /// You can close the [`crate::Window`] by calling `ui.close_kind(UiKind::Window)`.
1058 ///
1059 /// See also:
1060 /// - [`Ui::close`]
1061 /// - [`Ui::should_close`]
1062 /// - [`Ui::will_parent_close`]
1063 pub fn close_kind(&self, ui_kind: UiKind) {
1064 let tag = self
1065 .stack
1066 .iter()
1067 .filter(|stack| stack.info.kind == Some(ui_kind))
1068 .find_map(|stack| {
1069 stack
1070 .info
1071 .tags
1072 .get_downcast::<ClosableTag>(ClosableTag::NAME)
1073 });
1074 if let Some(tag) = tag {
1075 tag.set_close();
1076 } else {
1077 log::warn!("Called ui.close_kind({ui_kind:?}) on ui with no such closable parent.");
1078 }
1079 }
1080
1081 /// Was [`Ui::close`] called on this [`Ui`] or any of its children?
1082 /// Only works if the [`Ui`] was created with [`UiBuilder::closable`].
1083 ///
1084 /// You can also check via this [`Ui`]'s [`Response::should_close`].
1085 ///
1086 /// See also:
1087 /// - [`Ui::will_parent_close`]
1088 /// - [`Ui::close`]
1089 /// - [`Ui::close_kind`]
1090 /// - [`Response::should_close`]
1091 pub fn should_close(&self) -> bool {
1092 self.stack
1093 .info
1094 .tags
1095 .get_downcast(ClosableTag::NAME)
1096 .is_some_and(|tag: &ClosableTag| tag.should_close())
1097 }
1098
1099 /// Will this [`Ui`] or any of its parents close this frame?
1100 ///
1101 /// See also
1102 /// - [`Ui::should_close`]
1103 /// - [`Ui::close`]
1104 /// - [`Ui::close_kind`]
1105 pub fn will_parent_close(&self) -> bool {
1106 self.stack.iter().any(|stack| {
1107 stack
1108 .info
1109 .tags
1110 .get_downcast::<ClosableTag>(ClosableTag::NAME)
1111 .is_some_and(|tag| tag.should_close())
1112 })
1113 }
1114}
1115
1116/// # Allocating space: where do I put my widgets?
1117impl Ui {
1118 /// Allocate space for a widget and check for interaction in the space.
1119 /// Returns a [`Response`] which contains a rectangle, id, and interaction info.
1120 ///
1121 /// ## How sizes are negotiated
1122 /// Each widget should have a *minimum desired size* and a *desired size*.
1123 /// When asking for space, ask AT LEAST for your minimum, and don't ask for more than you need.
1124 /// If you want to fill the space, ask about [`Ui::available_size`] and use that.
1125 ///
1126 /// You may get MORE space than you asked for, for instance
1127 /// for justified layouts, like in menus.
1128 ///
1129 /// You will never get a rectangle that is smaller than the amount of space you asked for.
1130 ///
1131 /// ```
1132 /// # egui::__run_test_ui(|ui| {
1133 /// let response = ui.allocate_response(egui::vec2(100.0, 200.0), egui::Sense::click());
1134 /// if response.clicked() { /* … */ }
1135 /// ui.painter().rect_stroke(response.rect, 0.0, (1.0, egui::Color32::WHITE), egui::StrokeKind::Inside);
1136 /// # });
1137 /// ```
1138 pub fn allocate_response(&mut self, desired_size: Vec2, sense: Sense) -> Response {
1139 let (id, rect) = self.allocate_space(desired_size);
1140 let mut response = self.interact(rect, id, sense);
1141 response.set_intrinsic_size(desired_size);
1142 response
1143 }
1144
1145 /// Returns a [`Rect`] with exactly what you asked for.
1146 ///
1147 /// The response rect will be larger if this is part of a justified layout or similar.
1148 /// This means that if this is a narrow widget in a wide justified layout, then
1149 /// the widget will react to interactions outside the returned [`Rect`].
1150 pub fn allocate_exact_size(&mut self, desired_size: Vec2, sense: Sense) -> (Rect, Response) {
1151 let response = self.allocate_response(desired_size, sense);
1152 let rect = self
1153 .placer
1154 .align_size_within_rect(desired_size, response.rect);
1155 (rect, response)
1156 }
1157
1158 /// Allocate at least as much space as needed, and interact with that rect.
1159 ///
1160 /// The returned [`Rect`] will be the same size as `Response::rect`.
1161 pub fn allocate_at_least(&mut self, desired_size: Vec2, sense: Sense) -> (Rect, Response) {
1162 let response = self.allocate_response(desired_size, sense);
1163 (response.rect, response)
1164 }
1165
1166 /// Reserve this much space and move the cursor.
1167 /// Returns where to put the widget.
1168 ///
1169 /// ## How sizes are negotiated
1170 /// Each widget should have a *minimum desired size* and a *desired size*.
1171 /// When asking for space, ask AT LEAST for your minimum, and don't ask for more than you need.
1172 /// If you want to fill the space, ask about [`Ui::available_size`] and use that.
1173 ///
1174 /// You may get MORE space than you asked for, for instance
1175 /// for justified layouts, like in menus.
1176 ///
1177 /// You will never get a rectangle that is smaller than the amount of space you asked for.
1178 ///
1179 /// Returns an automatic [`Id`] (which you can use for interaction) and the [`Rect`] of where to put your widget.
1180 ///
1181 /// ```
1182 /// # egui::__run_test_ui(|ui| {
1183 /// let (id, rect) = ui.allocate_space(egui::vec2(100.0, 200.0));
1184 /// let response = ui.interact(rect, id, egui::Sense::click());
1185 /// # });
1186 /// ```
1187 pub fn allocate_space(&mut self, desired_size: Vec2) -> (Id, Rect) {
1188 #[cfg(debug_assertions)]
1189 let original_available = self.available_size_before_wrap();
1190
1191 let rect = self.allocate_space_impl(desired_size);
1192
1193 #[cfg(debug_assertions)]
1194 {
1195 let too_wide = desired_size.x > original_available.x;
1196 let too_high = desired_size.y > original_available.y;
1197
1198 let debug_expand_width = self.style().debug.show_expand_width;
1199 let debug_expand_height = self.style().debug.show_expand_height;
1200
1201 if (debug_expand_width && too_wide) || (debug_expand_height && too_high) {
1202 self.painter.rect_stroke(
1203 rect,
1204 0.0,
1205 (1.0, Color32::LIGHT_BLUE),
1206 crate::StrokeKind::Inside,
1207 );
1208
1209 let stroke = crate::Stroke::new(2.5, Color32::from_rgb(200, 0, 0));
1210 let paint_line_seg = |a, b| self.painter().line_segment([a, b], stroke);
1211
1212 if debug_expand_width && too_wide {
1213 paint_line_seg(rect.left_top(), rect.left_bottom());
1214 paint_line_seg(rect.left_center(), rect.right_center());
1215 paint_line_seg(
1216 pos2(rect.left() + original_available.x, rect.top()),
1217 pos2(rect.left() + original_available.x, rect.bottom()),
1218 );
1219 paint_line_seg(rect.right_top(), rect.right_bottom());
1220 }
1221
1222 if debug_expand_height && too_high {
1223 paint_line_seg(rect.left_top(), rect.right_top());
1224 paint_line_seg(rect.center_top(), rect.center_bottom());
1225 paint_line_seg(rect.left_bottom(), rect.right_bottom());
1226 }
1227 }
1228 }
1229
1230 let id = Id::new(self.next_auto_id_salt);
1231 self.next_auto_id_salt = self.next_auto_id_salt.wrapping_add(1);
1232
1233 (id, rect)
1234 }
1235
1236 /// Reserve this much space and move the cursor.
1237 /// Returns where to put the widget.
1238 fn allocate_space_impl(&mut self, desired_size: Vec2) -> Rect {
1239 let item_spacing = self.spacing().item_spacing;
1240 let frame_rect = self.placer.next_space(desired_size, item_spacing);
1241 debug_assert!(!frame_rect.any_nan(), "frame_rect is nan in allocate_space");
1242 let widget_rect = self.placer.justify_and_align(frame_rect, desired_size);
1243
1244 self.placer
1245 .advance_after_rects(frame_rect, widget_rect, item_spacing);
1246
1247 register_rect(self, widget_rect);
1248
1249 widget_rect
1250 }
1251
1252 /// Allocate a specific part of the [`Ui`].
1253 ///
1254 /// Ignore the layout of the [`Ui`]: just put my widget here!
1255 /// The layout cursor will advance to past this `rect`.
1256 pub fn allocate_rect(&mut self, rect: Rect, sense: Sense) -> Response {
1257 let rect = rect.round_ui();
1258 let id = self.advance_cursor_after_rect(rect);
1259 self.interact(rect, id, sense)
1260 }
1261
1262 /// Allocate a rect without interacting with it.
1263 pub fn advance_cursor_after_rect(&mut self, rect: Rect) -> Id {
1264 debug_assert!(!rect.any_nan(), "rect is nan in advance_cursor_after_rect");
1265 let rect = rect.round_ui();
1266
1267 let item_spacing = self.spacing().item_spacing;
1268 self.placer.advance_after_rects(rect, rect, item_spacing);
1269 register_rect(self, rect);
1270
1271 let id = Id::new(self.next_auto_id_salt);
1272 self.next_auto_id_salt = self.next_auto_id_salt.wrapping_add(1);
1273 id
1274 }
1275
1276 pub(crate) fn placer(&self) -> &Placer {
1277 &self.placer
1278 }
1279
1280 /// Where the next widget will be put.
1281 ///
1282 /// One side of this will always be infinite: the direction in which new widgets will be added.
1283 /// The opposing side is what is incremented.
1284 /// The crossing sides are initialized to `max_rect`.
1285 ///
1286 /// So one can think of `cursor` as a constraint on the available region.
1287 ///
1288 /// If something has already been added, this will point to `style.spacing.item_spacing` beyond the latest child.
1289 /// The cursor can thus be `style.spacing.item_spacing` pixels outside of the `min_rect`.
1290 pub fn cursor(&self) -> Rect {
1291 self.placer.cursor()
1292 }
1293
1294 pub(crate) fn set_cursor(&mut self, cursor: Rect) {
1295 self.placer.set_cursor(cursor);
1296 }
1297
1298 /// Where do we expect a zero-sized widget to be placed?
1299 pub fn next_widget_position(&self) -> Pos2 {
1300 self.placer.next_widget_position()
1301 }
1302
1303 /// Allocated the given space and then adds content to that space.
1304 /// If the contents overflow, more space will be allocated.
1305 /// When finished, the amount of space actually used (`min_rect`) will be allocated.
1306 /// So you can request a lot of space and then use less.
1307 #[inline]
1308 pub fn allocate_ui<R>(
1309 &mut self,
1310 desired_size: Vec2,
1311 add_contents: impl FnOnce(&mut Self) -> R,
1312 ) -> InnerResponse<R> {
1313 self.allocate_ui_with_layout(desired_size, *self.layout(), add_contents)
1314 }
1315
1316 /// Allocated the given space and then adds content to that space.
1317 /// If the contents overflow, more space will be allocated.
1318 /// When finished, the amount of space actually used (`min_rect`) will be allocated.
1319 /// So you can request a lot of space and then use less.
1320 #[inline]
1321 pub fn allocate_ui_with_layout<R>(
1322 &mut self,
1323 desired_size: Vec2,
1324 layout: Layout,
1325 add_contents: impl FnOnce(&mut Self) -> R,
1326 ) -> InnerResponse<R> {
1327 self.allocate_ui_with_layout_dyn(desired_size, layout, Box::new(add_contents))
1328 }
1329
1330 fn allocate_ui_with_layout_dyn<'c, R>(
1331 &mut self,
1332 desired_size: Vec2,
1333 layout: Layout,
1334 add_contents: Box<dyn FnOnce(&mut Self) -> R + 'c>,
1335 ) -> InnerResponse<R> {
1336 debug_assert!(
1337 desired_size.x >= 0.0 && desired_size.y >= 0.0,
1338 "Negative desired size: {desired_size:?}"
1339 );
1340 let item_spacing = self.spacing().item_spacing;
1341 let frame_rect = self.placer.next_space(desired_size, item_spacing);
1342 let child_rect = self.placer.justify_and_align(frame_rect, desired_size);
1343 self.scope_dyn(
1344 UiBuilder::new().max_rect(child_rect).layout(layout),
1345 add_contents,
1346 )
1347 }
1348
1349 /// Convenience function to get a region to paint on.
1350 ///
1351 /// Note that egui uses screen coordinates for everything.
1352 ///
1353 /// ```
1354 /// # use egui::*;
1355 /// # use std::f32::consts::TAU;
1356 /// # egui::__run_test_ui(|ui| {
1357 /// let size = Vec2::splat(16.0);
1358 /// let (response, painter) = ui.allocate_painter(size, Sense::hover());
1359 /// let rect = response.rect;
1360 /// let c = rect.center();
1361 /// let r = rect.width() / 2.0 - 1.0;
1362 /// let color = Color32::from_gray(128);
1363 /// let stroke = Stroke::new(1.0, color);
1364 /// painter.circle_stroke(c, r, stroke);
1365 /// painter.line_segment([c - vec2(0.0, r), c + vec2(0.0, r)], stroke);
1366 /// painter.line_segment([c, c + r * Vec2::angled(TAU * 1.0 / 8.0)], stroke);
1367 /// painter.line_segment([c, c + r * Vec2::angled(TAU * 3.0 / 8.0)], stroke);
1368 /// # });
1369 /// ```
1370 pub fn allocate_painter(&mut self, desired_size: Vec2, sense: Sense) -> (Response, Painter) {
1371 let response = self.allocate_response(desired_size, sense);
1372 let clip_rect = self.clip_rect().intersect(response.rect); // Make sure we don't paint out of bounds
1373 let painter = self.painter().with_clip_rect(clip_rect);
1374 (response, painter)
1375 }
1376}
1377
1378/// # Scrolling
1379impl Ui {
1380 /// Adjust the scroll position of any parent [`crate::ScrollArea`] so that the given [`Rect`] becomes visible.
1381 ///
1382 /// If `align` is [`Align::TOP`] it means "put the top of the rect at the top of the scroll area", etc.
1383 /// If `align` is `None`, it'll scroll enough to bring the cursor into view.
1384 ///
1385 /// See also: [`Response::scroll_to_me`], [`Ui::scroll_to_cursor`]. [`Ui::scroll_with_delta`]..
1386 ///
1387 /// ```
1388 /// # use egui::Align;
1389 /// # egui::__run_test_ui(|ui| {
1390 /// egui::ScrollArea::vertical().show(ui, |ui| {
1391 /// // …
1392 /// let response = ui.button("Center on me.");
1393 /// if response.clicked() {
1394 /// ui.scroll_to_rect(response.rect, Some(Align::Center));
1395 /// }
1396 /// });
1397 /// # });
1398 /// ```
1399 pub fn scroll_to_rect(&self, rect: Rect, align: Option<Align>) {
1400 self.scroll_to_rect_animation(rect, align, self.style.scroll_animation);
1401 }
1402
1403 /// Same as [`Self::scroll_to_rect`], but allows you to specify the [`style::ScrollAnimation`].
1404 pub fn scroll_to_rect_animation(
1405 &self,
1406 rect: Rect,
1407 align: Option<Align>,
1408 animation: style::ScrollAnimation,
1409 ) {
1410 for d in 0..2 {
1411 let range = Rangef::new(rect.min[d], rect.max[d]);
1412 self.ctx().pass_state_mut(|state| {
1413 state.scroll_target[d] =
1414 Some(pass_state::ScrollTarget::new(range, align, animation));
1415 });
1416 }
1417 }
1418
1419 /// Adjust the scroll position of any parent [`crate::ScrollArea`] so that the cursor (where the next widget goes) becomes visible.
1420 ///
1421 /// If `align` is [`Align::TOP`] it means "put the top of the rect at the top of the scroll area", etc.
1422 /// If `align` is not provided, it'll scroll enough to bring the cursor into view.
1423 ///
1424 /// See also: [`Response::scroll_to_me`], [`Ui::scroll_to_rect`]. [`Ui::scroll_with_delta`].
1425 ///
1426 /// ```
1427 /// # use egui::Align;
1428 /// # egui::__run_test_ui(|ui| {
1429 /// egui::ScrollArea::vertical().show(ui, |ui| {
1430 /// let scroll_bottom = ui.button("Scroll to bottom.").clicked();
1431 /// for i in 0..1000 {
1432 /// ui.label(format!("Item {}", i));
1433 /// }
1434 ///
1435 /// if scroll_bottom {
1436 /// ui.scroll_to_cursor(Some(Align::BOTTOM));
1437 /// }
1438 /// });
1439 /// # });
1440 /// ```
1441 pub fn scroll_to_cursor(&self, align: Option<Align>) {
1442 self.scroll_to_cursor_animation(align, self.style.scroll_animation);
1443 }
1444
1445 /// Same as [`Self::scroll_to_cursor`], but allows you to specify the [`style::ScrollAnimation`].
1446 pub fn scroll_to_cursor_animation(
1447 &self,
1448 align: Option<Align>,
1449 animation: style::ScrollAnimation,
1450 ) {
1451 let target = self.next_widget_position();
1452 for d in 0..2 {
1453 let target = Rangef::point(target[d]);
1454 self.ctx().pass_state_mut(|state| {
1455 state.scroll_target[d] =
1456 Some(pass_state::ScrollTarget::new(target, align, animation));
1457 });
1458 }
1459 }
1460
1461 /// Scroll this many points in the given direction, in the parent [`crate::ScrollArea`].
1462 ///
1463 /// The delta dictates how the _content_ (i.e. this UI) should move.
1464 ///
1465 /// A positive X-value indicates the content is being moved right,
1466 /// as when swiping right on a touch-screen or track-pad with natural scrolling.
1467 ///
1468 /// A positive Y-value indicates the content is being moved down,
1469 /// as when swiping down on a touch-screen or track-pad with natural scrolling.
1470 ///
1471 /// If this is called multiple times per frame for the same [`crate::ScrollArea`], the deltas will be summed.
1472 ///
1473 /// See also: [`Response::scroll_to_me`], [`Ui::scroll_to_rect`], [`Ui::scroll_to_cursor`]
1474 ///
1475 /// ```
1476 /// # use egui::{Align, Vec2};
1477 /// # egui::__run_test_ui(|ui| {
1478 /// let mut scroll_delta = Vec2::ZERO;
1479 /// if ui.button("Scroll down").clicked() {
1480 /// scroll_delta.y -= 64.0; // move content up
1481 /// }
1482 /// egui::ScrollArea::vertical().show(ui, |ui| {
1483 /// ui.scroll_with_delta(scroll_delta);
1484 /// for i in 0..1000 {
1485 /// ui.label(format!("Item {}", i));
1486 /// }
1487 /// });
1488 /// # });
1489 /// ```
1490 pub fn scroll_with_delta(&self, delta: Vec2) {
1491 self.scroll_with_delta_animation(delta, self.style.scroll_animation);
1492 }
1493
1494 /// Same as [`Self::scroll_with_delta`], but allows you to specify the [`style::ScrollAnimation`].
1495 pub fn scroll_with_delta_animation(&self, delta: Vec2, animation: style::ScrollAnimation) {
1496 self.ctx().pass_state_mut(|state| {
1497 state.scroll_delta.0 += delta;
1498 state.scroll_delta.1 = animation;
1499 });
1500 }
1501}
1502
1503/// # Adding widgets
1504impl Ui {
1505 /// Add a [`Widget`] to this [`Ui`] at a location dependent on the current [`Layout`].
1506 ///
1507 /// The returned [`Response`] can be used to check for interactions,
1508 /// as well as adding tooltips using [`Response::on_hover_text`].
1509 ///
1510 /// See also [`Self::add_sized`], [`Self::place`] and [`Self::put`].
1511 ///
1512 /// ```
1513 /// # egui::__run_test_ui(|ui| {
1514 /// # let mut my_value = 42;
1515 /// let response = ui.add(egui::Slider::new(&mut my_value, 0..=100));
1516 /// response.on_hover_text("Drag me!");
1517 /// # });
1518 /// ```
1519 #[inline]
1520 pub fn add(&mut self, widget: impl Widget) -> Response {
1521 widget.ui(self)
1522 }
1523
1524 /// Add a [`Widget`] to this [`Ui`] with a given size.
1525 /// The widget will attempt to fit within the given size, but some widgets may overflow.
1526 ///
1527 /// To fill all remaining area, use `ui.add_sized(ui.available_size(), widget);`
1528 ///
1529 /// See also [`Self::add`], [`Self::place`] and [`Self::put`].
1530 ///
1531 /// ```
1532 /// # egui::__run_test_ui(|ui| {
1533 /// # let mut my_value = 42;
1534 /// ui.add_sized([40.0, 20.0], egui::DragValue::new(&mut my_value));
1535 /// # });
1536 /// ```
1537 pub fn add_sized(&mut self, max_size: impl Into<Vec2>, widget: impl Widget) -> Response {
1538 // TODO(emilk): configure to overflow to main_dir instead of centered overflow
1539 // to handle the bug mentioned at https://github.com/emilk/egui/discussions/318#discussioncomment-627578
1540 // and fixed in https://github.com/emilk/egui/commit/035166276322b3f2324bd8b97ffcedc63fa8419f
1541 //
1542 // Make sure we keep the same main direction since it changes e.g. how text is wrapped:
1543 let layout = Layout::centered_and_justified(self.layout().main_dir());
1544 self.allocate_ui_with_layout(max_size.into(), layout, |ui| ui.add(widget))
1545 .inner
1546 }
1547
1548 /// Add a [`Widget`] to this [`Ui`] at a specific location (manual layout) without
1549 /// affecting this [`Ui`]s cursor.
1550 ///
1551 /// See also [`Self::add`] and [`Self::add_sized`] and [`Self::put`].
1552 pub fn place(&mut self, max_rect: Rect, widget: impl Widget) -> Response {
1553 self.new_child(
1554 UiBuilder::new()
1555 .max_rect(max_rect)
1556 .layout(Layout::centered_and_justified(Direction::TopDown)),
1557 )
1558 .add(widget)
1559 }
1560
1561 /// Add a [`Widget`] to this [`Ui`] at a specific location (manual layout) and advance the
1562 /// cursor after the widget.
1563 ///
1564 /// See also [`Self::add`], [`Self::add_sized`], and [`Self::place`].
1565 pub fn put(&mut self, max_rect: Rect, widget: impl Widget) -> Response {
1566 self.scope_builder(
1567 UiBuilder::new()
1568 .max_rect(max_rect)
1569 .layout(Layout::centered_and_justified(Direction::TopDown)),
1570 |ui| ui.add(widget),
1571 )
1572 .inner
1573 }
1574
1575 /// Add a single [`Widget`] that is possibly disabled, i.e. greyed out and non-interactive.
1576 ///
1577 /// If you call `add_enabled` from within an already disabled [`Ui`],
1578 /// the widget will always be disabled, even if the `enabled` argument is true.
1579 ///
1580 /// See also [`Self::add_enabled_ui`] and [`Self::is_enabled`].
1581 ///
1582 /// ```
1583 /// # egui::__run_test_ui(|ui| {
1584 /// ui.add_enabled(false, egui::Button::new("Can't click this"));
1585 /// # });
1586 /// ```
1587 pub fn add_enabled(&mut self, enabled: bool, widget: impl Widget) -> Response {
1588 if self.is_enabled() && !enabled {
1589 let old_painter = self.painter.clone();
1590 self.disable();
1591 let response = self.add(widget);
1592 self.enabled = true;
1593 self.painter = old_painter;
1594 response
1595 } else {
1596 self.add(widget)
1597 }
1598 }
1599
1600 /// Add a section that is possibly disabled, i.e. greyed out and non-interactive.
1601 ///
1602 /// If you call `add_enabled_ui` from within an already disabled [`Ui`],
1603 /// the result will always be disabled, even if the `enabled` argument is true.
1604 ///
1605 /// See also [`Self::add_enabled`] and [`Self::is_enabled`].
1606 ///
1607 /// ### Example
1608 /// ```
1609 /// # egui::__run_test_ui(|ui| {
1610 /// # let mut enabled = true;
1611 /// ui.checkbox(&mut enabled, "Enable subsection");
1612 /// ui.add_enabled_ui(enabled, |ui| {
1613 /// if ui.button("Button that is not always clickable").clicked() {
1614 /// /* … */
1615 /// }
1616 /// });
1617 /// # });
1618 /// ```
1619 pub fn add_enabled_ui<R>(
1620 &mut self,
1621 enabled: bool,
1622 add_contents: impl FnOnce(&mut Ui) -> R,
1623 ) -> InnerResponse<R> {
1624 self.scope(|ui| {
1625 if !enabled {
1626 ui.disable();
1627 }
1628 add_contents(ui)
1629 })
1630 }
1631
1632 /// Add a single [`Widget`] that is possibly invisible.
1633 ///
1634 /// An invisible widget still takes up the same space as if it were visible.
1635 ///
1636 /// If you call `add_visible` from within an already invisible [`Ui`],
1637 /// the widget will always be invisible, even if the `visible` argument is true.
1638 ///
1639 /// See also [`Self::set_invisible`] and [`Self::is_visible`].
1640 ///
1641 /// ```
1642 /// # egui::__run_test_ui(|ui| {
1643 /// ui.add_visible(false, egui::Label::new("You won't see me!"));
1644 /// # });
1645 /// ```
1646 pub fn add_visible(&mut self, visible: bool, widget: impl Widget) -> Response {
1647 if self.is_visible() && !visible {
1648 // temporary make us invisible:
1649 let old_painter = self.painter.clone();
1650 let old_enabled = self.enabled;
1651
1652 self.set_invisible();
1653
1654 let response = self.add(widget);
1655
1656 self.painter = old_painter;
1657 self.enabled = old_enabled;
1658 response
1659 } else {
1660 self.add(widget)
1661 }
1662 }
1663
1664 /// Add extra space before the next widget.
1665 ///
1666 /// The direction is dependent on the layout.
1667 /// Note that `add_space` isn't supported when in a grid layout.
1668 ///
1669 /// This will be in addition to the [`crate::style::Spacing::item_spacing`]
1670 /// that is always added, but `item_spacing` won't be added _again_ by `add_space`.
1671 ///
1672 /// [`Self::min_rect`] will expand to contain the space.
1673 #[inline]
1674 pub fn add_space(&mut self, amount: f32) {
1675 debug_assert!(!self.is_grid(), "add_space makes no sense in a grid layout");
1676 self.placer.advance_cursor(amount.round_ui());
1677 }
1678
1679 /// Show some text.
1680 ///
1681 /// Shortcut for `add(Label::new(text))`
1682 ///
1683 /// See also [`Label`].
1684 ///
1685 /// ### Example
1686 /// ```
1687 /// # egui::__run_test_ui(|ui| {
1688 /// use egui::{RichText, FontId, Color32};
1689 /// ui.label("Normal text");
1690 /// ui.label(RichText::new("Large text").font(FontId::proportional(40.0)));
1691 /// ui.label(RichText::new("Red text").color(Color32::RED));
1692 /// # });
1693 /// ```
1694 #[inline]
1695 pub fn label(&mut self, text: impl Into<WidgetText>) -> Response {
1696 Label::new(text).ui(self)
1697 }
1698
1699 /// Show colored text.
1700 ///
1701 /// Shortcut for `ui.label(RichText::new(text).color(color))`
1702 pub fn colored_label(
1703 &mut self,
1704 color: impl Into<Color32>,
1705 text: impl Into<RichText>,
1706 ) -> Response {
1707 Label::new(text.into().color(color)).ui(self)
1708 }
1709
1710 /// Show large text.
1711 ///
1712 /// Shortcut for `ui.label(RichText::new(text).heading())`
1713 pub fn heading(&mut self, text: impl Into<RichText>) -> Response {
1714 Label::new(text.into().heading()).ui(self)
1715 }
1716
1717 /// Show monospace (fixed width) text.
1718 ///
1719 /// Shortcut for `ui.label(RichText::new(text).monospace())`
1720 pub fn monospace(&mut self, text: impl Into<RichText>) -> Response {
1721 Label::new(text.into().monospace()).ui(self)
1722 }
1723
1724 /// Show text as monospace with a gray background.
1725 ///
1726 /// Shortcut for `ui.label(RichText::new(text).code())`
1727 pub fn code(&mut self, text: impl Into<RichText>) -> Response {
1728 Label::new(text.into().code()).ui(self)
1729 }
1730
1731 /// Show small text.
1732 ///
1733 /// Shortcut for `ui.label(RichText::new(text).small())`
1734 pub fn small(&mut self, text: impl Into<RichText>) -> Response {
1735 Label::new(text.into().small()).ui(self)
1736 }
1737
1738 /// Show text that stand out a bit (e.g. slightly brighter).
1739 ///
1740 /// Shortcut for `ui.label(RichText::new(text).strong())`
1741 pub fn strong(&mut self, text: impl Into<RichText>) -> Response {
1742 Label::new(text.into().strong()).ui(self)
1743 }
1744
1745 /// Show text that is weaker (fainter color).
1746 ///
1747 /// Shortcut for `ui.label(RichText::new(text).weak())`
1748 pub fn weak(&mut self, text: impl Into<RichText>) -> Response {
1749 Label::new(text.into().weak()).ui(self)
1750 }
1751
1752 /// Looks like a hyperlink.
1753 ///
1754 /// Shortcut for `add(Link::new(text))`.
1755 ///
1756 /// ```
1757 /// # egui::__run_test_ui(|ui| {
1758 /// if ui.link("Documentation").clicked() {
1759 /// // …
1760 /// }
1761 /// # });
1762 /// ```
1763 ///
1764 /// See also [`Link`].
1765 #[must_use = "You should check if the user clicked this with `if ui.link(…).clicked() { … } "]
1766 pub fn link(&mut self, text: impl Into<WidgetText>) -> Response {
1767 Link::new(text).ui(self)
1768 }
1769
1770 /// Link to a web page.
1771 ///
1772 /// Shortcut for `add(Hyperlink::new(url))`.
1773 ///
1774 /// ```
1775 /// # egui::__run_test_ui(|ui| {
1776 /// ui.hyperlink("https://www.egui.rs/");
1777 /// # });
1778 /// ```
1779 ///
1780 /// See also [`Hyperlink`].
1781 pub fn hyperlink(&mut self, url: impl ToString) -> Response {
1782 Hyperlink::new(url).ui(self)
1783 }
1784
1785 /// Shortcut for `add(Hyperlink::from_label_and_url(label, url))`.
1786 ///
1787 /// ```
1788 /// # egui::__run_test_ui(|ui| {
1789 /// ui.hyperlink_to("egui on GitHub", "https://www.github.com/emilk/egui/");
1790 /// # });
1791 /// ```
1792 ///
1793 /// See also [`Hyperlink`].
1794 pub fn hyperlink_to(&mut self, label: impl Into<WidgetText>, url: impl ToString) -> Response {
1795 Hyperlink::from_label_and_url(label, url).ui(self)
1796 }
1797
1798 /// No newlines (`\n`) allowed. Pressing enter key will result in the [`TextEdit`] losing focus (`response.lost_focus`).
1799 ///
1800 /// See also [`TextEdit`].
1801 pub fn text_edit_singleline<S: widgets::text_edit::TextBuffer>(
1802 &mut self,
1803 text: &mut S,
1804 ) -> Response {
1805 TextEdit::singleline(text).ui(self)
1806 }
1807
1808 /// A [`TextEdit`] for multiple lines. Pressing enter key will create a new line.
1809 ///
1810 /// See also [`TextEdit`].
1811 pub fn text_edit_multiline<S: widgets::text_edit::TextBuffer>(
1812 &mut self,
1813 text: &mut S,
1814 ) -> Response {
1815 TextEdit::multiline(text).ui(self)
1816 }
1817
1818 /// A [`TextEdit`] for code editing.
1819 ///
1820 /// This will be multiline, monospace, and will insert tabs instead of moving focus.
1821 ///
1822 /// See also [`TextEdit::code_editor`].
1823 pub fn code_editor<S: widgets::text_edit::TextBuffer>(&mut self, text: &mut S) -> Response {
1824 self.add(TextEdit::multiline(text).code_editor())
1825 }
1826
1827 /// Usage: `if ui.button("Click me").clicked() { … }`
1828 ///
1829 /// Shortcut for `add(Button::new(text))`
1830 ///
1831 /// See also [`Button`].
1832 ///
1833 /// ```
1834 /// # egui::__run_test_ui(|ui| {
1835 /// if ui.button("Click me!").clicked() {
1836 /// // …
1837 /// }
1838 ///
1839 /// # use egui::{RichText, Color32};
1840 /// if ui.button(RichText::new("delete").color(Color32::RED)).clicked() {
1841 /// // …
1842 /// }
1843 /// # });
1844 /// ```
1845 #[must_use = "You should check if the user clicked this with `if ui.button(…).clicked() { … } "]
1846 #[inline]
1847 pub fn button<'a>(&mut self, atoms: impl IntoAtoms<'a>) -> Response {
1848 Button::new(atoms).ui(self)
1849 }
1850
1851 /// A button as small as normal body text.
1852 ///
1853 /// Usage: `if ui.small_button("Click me").clicked() { … }`
1854 ///
1855 /// Shortcut for `add(Button::new(atoms).small())`
1856 #[must_use = "You should check if the user clicked this with `if ui.small_button(…).clicked() { … } "]
1857 pub fn small_button<'a>(&mut self, atoms: impl IntoAtoms<'a>) -> Response {
1858 Button::new(atoms).small().ui(self)
1859 }
1860
1861 /// Show a checkbox.
1862 ///
1863 /// See also [`Self::toggle_value`].
1864 #[inline]
1865 pub fn checkbox<'a>(&mut self, checked: &'a mut bool, atoms: impl IntoAtoms<'a>) -> Response {
1866 Checkbox::new(checked, atoms).ui(self)
1867 }
1868
1869 /// Acts like a checkbox, but looks like a [`Button::selectable`].
1870 ///
1871 /// Click to toggle to bool.
1872 ///
1873 /// See also [`Self::checkbox`].
1874 pub fn toggle_value<'a>(&mut self, selected: &mut bool, atoms: impl IntoAtoms<'a>) -> Response {
1875 let mut response = self.selectable_label(*selected, atoms);
1876 if response.clicked() {
1877 *selected = !*selected;
1878 response.mark_changed();
1879 }
1880 response
1881 }
1882
1883 /// Show a [`RadioButton`].
1884 /// Often you want to use [`Self::radio_value`] instead.
1885 #[must_use = "You should check if the user clicked this with `if ui.radio(…).clicked() { … } "]
1886 #[inline]
1887 pub fn radio<'a>(&mut self, selected: bool, atoms: impl IntoAtoms<'a>) -> Response {
1888 RadioButton::new(selected, atoms).ui(self)
1889 }
1890
1891 /// Show a [`RadioButton`]. It is selected if `*current_value == selected_value`.
1892 /// If clicked, `selected_value` is assigned to `*current_value`.
1893 ///
1894 /// ```
1895 /// # egui::__run_test_ui(|ui| {
1896 ///
1897 /// #[derive(PartialEq)]
1898 /// enum Enum { First, Second, Third }
1899 /// let mut my_enum = Enum::First;
1900 ///
1901 /// ui.radio_value(&mut my_enum, Enum::First, "First");
1902 ///
1903 /// // is equivalent to:
1904 ///
1905 /// if ui.add(egui::RadioButton::new(my_enum == Enum::First, "First")).clicked() {
1906 /// my_enum = Enum::First
1907 /// }
1908 /// # });
1909 /// ```
1910 pub fn radio_value<'a, Value: PartialEq>(
1911 &mut self,
1912 current_value: &mut Value,
1913 alternative: Value,
1914 atoms: impl IntoAtoms<'a>,
1915 ) -> Response {
1916 let mut response = self.radio(*current_value == alternative, atoms);
1917 if response.clicked() && *current_value != alternative {
1918 *current_value = alternative;
1919 response.mark_changed();
1920 }
1921 response
1922 }
1923
1924 /// Show a label which can be selected or not.
1925 ///
1926 /// See also [`Button::selectable`] and [`Self::toggle_value`].
1927 #[must_use = "You should check if the user clicked this with `if ui.selectable_label(…).clicked() { … } "]
1928 pub fn selectable_label<'a>(&mut self, checked: bool, text: impl IntoAtoms<'a>) -> Response {
1929 Button::selectable(checked, text).ui(self)
1930 }
1931
1932 /// Show selectable text. It is selected if `*current_value == selected_value`.
1933 /// If clicked, `selected_value` is assigned to `*current_value`.
1934 ///
1935 /// Example: `ui.selectable_value(&mut my_enum, Enum::Alternative, "Alternative")`.
1936 ///
1937 /// See also [`Button::selectable`] and [`Self::toggle_value`].
1938 pub fn selectable_value<'a, Value: PartialEq>(
1939 &mut self,
1940 current_value: &mut Value,
1941 selected_value: Value,
1942 text: impl IntoAtoms<'a>,
1943 ) -> Response {
1944 let mut response = self.selectable_label(*current_value == selected_value, text);
1945 if response.clicked() && *current_value != selected_value {
1946 *current_value = selected_value;
1947 response.mark_changed();
1948 }
1949 response
1950 }
1951
1952 /// Shortcut for `add(Separator::default())`
1953 ///
1954 /// See also [`Separator`].
1955 #[inline]
1956 pub fn separator(&mut self) -> Response {
1957 Separator::default().ui(self)
1958 }
1959
1960 /// Shortcut for `add(Spinner::new())`
1961 ///
1962 /// See also [`Spinner`].
1963 #[inline]
1964 pub fn spinner(&mut self) -> Response {
1965 Spinner::new().ui(self)
1966 }
1967
1968 /// Modify an angle. The given angle should be in radians, but is shown to the user in degrees.
1969 /// The angle is NOT wrapped, so the user may select, for instance 720° = 2𝞃 = 4π
1970 pub fn drag_angle(&mut self, radians: &mut f32) -> Response {
1971 let mut degrees = radians.to_degrees();
1972 let mut response = self.add(DragValue::new(&mut degrees).speed(1.0).suffix("°"));
1973
1974 // only touch `*radians` if we actually changed the degree value
1975 if degrees != radians.to_degrees() {
1976 *radians = degrees.to_radians();
1977 response.mark_changed();
1978 }
1979
1980 response
1981 }
1982
1983 /// Modify an angle. The given angle should be in radians,
1984 /// but is shown to the user in fractions of one Tau (i.e. fractions of one turn).
1985 /// The angle is NOT wrapped, so the user may select, for instance 2𝞃 (720°)
1986 pub fn drag_angle_tau(&mut self, radians: &mut f32) -> Response {
1987 use std::f32::consts::TAU;
1988
1989 let mut taus = *radians / TAU;
1990 let mut response = self.add(DragValue::new(&mut taus).speed(0.01).suffix("τ"));
1991
1992 if self.style().explanation_tooltips {
1993 response =
1994 response.on_hover_text("1τ = one turn, 0.5τ = half a turn, etc. 0.25τ = 90°");
1995 }
1996
1997 // only touch `*radians` if we actually changed the value
1998 if taus != *radians / TAU {
1999 *radians = taus * TAU;
2000 response.mark_changed();
2001 }
2002
2003 response
2004 }
2005
2006 /// Show an image available at the given `uri`.
2007 ///
2008 /// ⚠ This will do nothing unless you install some image loaders first!
2009 /// The easiest way to do this is via [`egui_extras::install_image_loaders`](https://docs.rs/egui_extras/latest/egui_extras/loaders/fn.install_image_loaders.html).
2010 ///
2011 /// The loaders handle caching image data, sampled textures, etc. across frames, so calling this is immediate-mode safe.
2012 ///
2013 /// ```
2014 /// # egui::__run_test_ui(|ui| {
2015 /// ui.image("https://picsum.photos/480");
2016 /// ui.image("file://assets/ferris.png");
2017 /// ui.image(egui::include_image!("../assets/ferris.png"));
2018 /// ui.add(
2019 /// egui::Image::new(egui::include_image!("../assets/ferris.png"))
2020 /// .max_width(200.0)
2021 /// .corner_radius(10),
2022 /// );
2023 /// # });
2024 /// ```
2025 ///
2026 /// Using [`crate::include_image`] is often the most ergonomic, and the path
2027 /// will be resolved at compile-time and embedded in the binary.
2028 /// When using a "file://" url on the other hand, you need to make sure
2029 /// the files can be found in the right spot at runtime!
2030 ///
2031 /// See also [`crate::Image`], [`crate::ImageSource`].
2032 #[inline]
2033 pub fn image<'a>(&mut self, source: impl Into<ImageSource<'a>>) -> Response {
2034 Image::new(source).ui(self)
2035 }
2036}
2037
2038/// # Colors
2039impl Ui {
2040 /// Shows a button with the given color.
2041 ///
2042 /// If the user clicks the button, a full color picker is shown.
2043 pub fn color_edit_button_srgba(&mut self, srgba: &mut Color32) -> Response {
2044 color_picker::color_edit_button_srgba(self, srgba, color_picker::Alpha::BlendOrAdditive)
2045 }
2046
2047 /// Shows a button with the given color.
2048 ///
2049 /// If the user clicks the button, a full color picker is shown.
2050 pub fn color_edit_button_hsva(&mut self, hsva: &mut Hsva) -> Response {
2051 color_picker::color_edit_button_hsva(self, hsva, color_picker::Alpha::BlendOrAdditive)
2052 }
2053
2054 /// Shows a button with the given color.
2055 ///
2056 /// If the user clicks the button, a full color picker is shown.
2057 /// The given color is in `sRGB` space.
2058 pub fn color_edit_button_srgb(&mut self, srgb: &mut [u8; 3]) -> Response {
2059 color_picker::color_edit_button_srgb(self, srgb)
2060 }
2061
2062 /// Shows a button with the given color.
2063 ///
2064 /// If the user clicks the button, a full color picker is shown.
2065 /// The given color is in linear RGB space.
2066 pub fn color_edit_button_rgb(&mut self, rgb: &mut [f32; 3]) -> Response {
2067 color_picker::color_edit_button_rgb(self, rgb)
2068 }
2069
2070 /// Shows a button with the given color.
2071 ///
2072 /// If the user clicks the button, a full color picker is shown.
2073 /// The given color is in `sRGBA` space with premultiplied alpha
2074 pub fn color_edit_button_srgba_premultiplied(&mut self, srgba: &mut [u8; 4]) -> Response {
2075 let mut color = Color32::from_rgba_premultiplied(srgba[0], srgba[1], srgba[2], srgba[3]);
2076 let response = self.color_edit_button_srgba(&mut color);
2077 *srgba = color.to_array();
2078 response
2079 }
2080
2081 /// Shows a button with the given color.
2082 ///
2083 /// If the user clicks the button, a full color picker is shown.
2084 /// The given color is in `sRGBA` space without premultiplied alpha.
2085 /// If unsure what "premultiplied alpha" is, then this is probably the function you want to use.
2086 pub fn color_edit_button_srgba_unmultiplied(&mut self, srgba: &mut [u8; 4]) -> Response {
2087 let mut rgba = Rgba::from_srgba_unmultiplied(srgba[0], srgba[1], srgba[2], srgba[3]);
2088 let response =
2089 color_picker::color_edit_button_rgba(self, &mut rgba, color_picker::Alpha::OnlyBlend);
2090 *srgba = rgba.to_srgba_unmultiplied();
2091 response
2092 }
2093
2094 /// Shows a button with the given color.
2095 ///
2096 /// If the user clicks the button, a full color picker is shown.
2097 /// The given color is in linear RGBA space with premultiplied alpha
2098 pub fn color_edit_button_rgba_premultiplied(&mut self, rgba_premul: &mut [f32; 4]) -> Response {
2099 let mut rgba = Rgba::from_rgba_premultiplied(
2100 rgba_premul[0],
2101 rgba_premul[1],
2102 rgba_premul[2],
2103 rgba_premul[3],
2104 );
2105 let response = color_picker::color_edit_button_rgba(
2106 self,
2107 &mut rgba,
2108 color_picker::Alpha::BlendOrAdditive,
2109 );
2110 *rgba_premul = rgba.to_array();
2111 response
2112 }
2113
2114 /// Shows a button with the given color.
2115 ///
2116 /// If the user clicks the button, a full color picker is shown.
2117 /// The given color is in linear RGBA space without premultiplied alpha.
2118 /// If unsure, what "premultiplied alpha" is, then this is probably the function you want to use.
2119 pub fn color_edit_button_rgba_unmultiplied(&mut self, rgba_unmul: &mut [f32; 4]) -> Response {
2120 let mut rgba = Rgba::from_rgba_unmultiplied(
2121 rgba_unmul[0],
2122 rgba_unmul[1],
2123 rgba_unmul[2],
2124 rgba_unmul[3],
2125 );
2126 let response =
2127 color_picker::color_edit_button_rgba(self, &mut rgba, color_picker::Alpha::OnlyBlend);
2128 *rgba_unmul = rgba.to_rgba_unmultiplied();
2129 response
2130 }
2131}
2132
2133/// # Adding Containers / Sub-uis:
2134impl Ui {
2135 /// Put into a [`Frame::group`], visually grouping the contents together
2136 ///
2137 /// ```
2138 /// # egui::__run_test_ui(|ui| {
2139 /// ui.group(|ui| {
2140 /// ui.label("Within a frame");
2141 /// });
2142 /// # });
2143 /// ```
2144 ///
2145 /// See also [`Self::scope`].
2146 pub fn group<R>(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {
2147 crate::Frame::group(self.style()).show(self, add_contents)
2148 }
2149
2150 /// Create a child Ui with an explicit [`Id`].
2151 ///
2152 /// ```
2153 /// # egui::__run_test_ui(|ui| {
2154 /// for i in 0..10 {
2155 /// // ui.collapsing("Same header", |ui| { }); // this will cause an ID clash because of the same title!
2156 ///
2157 /// ui.push_id(i, |ui| {
2158 /// ui.collapsing("Same header", |ui| { }); // this is fine!
2159 /// });
2160 /// }
2161 /// # });
2162 /// ```
2163 pub fn push_id<R>(
2164 &mut self,
2165 id_salt: impl AsIdSalt,
2166 add_contents: impl FnOnce(&mut Ui) -> R,
2167 ) -> InnerResponse<R> {
2168 self.scope_dyn(UiBuilder::new().id_salt(id_salt), Box::new(add_contents))
2169 }
2170
2171 /// Create a scoped child ui.
2172 ///
2173 /// You can use this to temporarily change the [`Style`] of a sub-region, for instance:
2174 ///
2175 /// ```
2176 /// # egui::__run_test_ui(|ui| {
2177 /// ui.scope(|ui| {
2178 /// ui.spacing_mut().slider_width = 200.0; // Temporary change
2179 /// // …
2180 /// });
2181 /// # });
2182 /// ```
2183 ///
2184 /// See also [`Self::scope_builder`] for more options.
2185 pub fn scope<R>(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {
2186 self.scope_dyn(UiBuilder::new(), Box::new(add_contents))
2187 }
2188
2189 /// Create a scoped child ui, inheriting properties from the parent as specified by the [`UiBuilder`].
2190 /// In contrast to [`Self::new_child`], this allocates the space used by the child.
2191 ///
2192 /// See also [`Self::scope`] and [`Self::scope_dyn`].
2193 pub fn scope_builder<R>(
2194 &mut self,
2195 ui_builder: UiBuilder,
2196 add_contents: impl FnOnce(&mut Ui) -> R,
2197 ) -> InnerResponse<R> {
2198 self.scope_dyn(ui_builder, Box::new(add_contents))
2199 }
2200
2201 /// [`Self::scope_builder`] but with dynamic dispatch.
2202 pub fn scope_dyn<'c, R>(
2203 &mut self,
2204 ui_builder: UiBuilder,
2205 add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,
2206 ) -> InnerResponse<R> {
2207 let next_auto_id_salt = self.next_auto_id_salt;
2208 let mut child_ui = self.new_child(ui_builder);
2209 self.next_auto_id_salt = next_auto_id_salt; // HACK: we want `scope` to only increment this once, so that `ui.scope` is equivalent to `ui.allocate_space`.
2210 let ret = add_contents(&mut child_ui);
2211 let response = child_ui.remember_min_rect();
2212 self.advance_cursor_after_rect(child_ui.min_rect());
2213 InnerResponse::new(ret, response)
2214 }
2215
2216 /// A [`CollapsingHeader`] that starts out collapsed.
2217 ///
2218 /// The name must be unique within the current parent,
2219 /// or you need to use [`CollapsingHeader::id_salt`].
2220 pub fn collapsing<R>(
2221 &mut self,
2222 heading: impl Into<WidgetText>,
2223 add_contents: impl FnOnce(&mut Ui) -> R,
2224 ) -> CollapsingResponse<R> {
2225 CollapsingHeader::new(heading).show(self, add_contents)
2226 }
2227
2228 /// Create a child ui which is indented to the right.
2229 ///
2230 /// The `id_salt` here be anything at all.
2231 // TODO(emilk): remove `id_salt` argument?
2232 #[inline]
2233 pub fn indent<R>(
2234 &mut self,
2235 id_salt: impl AsIdSalt,
2236 add_contents: impl FnOnce(&mut Ui) -> R,
2237 ) -> InnerResponse<R> {
2238 self.indent_dyn(id_salt, Box::new(add_contents))
2239 }
2240
2241 fn indent_dyn<'c, R>(
2242 &mut self,
2243 id_salt: impl AsIdSalt,
2244 add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,
2245 ) -> InnerResponse<R> {
2246 assert!(
2247 self.layout().is_vertical(),
2248 "You can only indent vertical layouts, found {:?}",
2249 self.layout()
2250 );
2251
2252 let indent = self.spacing().indent;
2253 let mut child_rect = self.placer.available_rect_before_wrap();
2254 child_rect.min.x += indent;
2255
2256 let mut child_ui = self.new_child(UiBuilder::new().id_salt(id_salt).max_rect(child_rect));
2257 let ret = add_contents(&mut child_ui);
2258
2259 let left_vline = self.visuals().indent_has_left_vline;
2260 let end_with_horizontal_line = self.spacing().indent_ends_with_horizontal_line;
2261
2262 if left_vline || end_with_horizontal_line {
2263 if end_with_horizontal_line {
2264 child_ui.add_space(4.0);
2265 }
2266
2267 let stroke = self.visuals().widgets.noninteractive.bg_stroke;
2268 let left_top = child_rect.min - 0.5 * indent * Vec2::X;
2269 let left_bottom = pos2(left_top.x, child_ui.min_rect().bottom() - 2.0);
2270
2271 if left_vline {
2272 // draw a faint line on the left to mark the indented section
2273 self.painter.line_segment([left_top, left_bottom], stroke);
2274 }
2275
2276 if end_with_horizontal_line {
2277 let fudge = 2.0; // looks nicer with button rounding in collapsing headers
2278 let right_bottom = pos2(child_ui.min_rect().right() - fudge, left_bottom.y);
2279 self.painter
2280 .line_segment([left_bottom, right_bottom], stroke);
2281 }
2282 }
2283
2284 let response = self.allocate_rect(child_ui.min_rect(), Sense::hover());
2285 InnerResponse::new(ret, response)
2286 }
2287
2288 /// Start a ui with horizontal layout.
2289 /// After you have called this, the function registers the contents as any other widget.
2290 ///
2291 /// Elements will be centered on the Y axis, i.e.
2292 /// adjusted up and down to lie in the center of the horizontal layout.
2293 /// The initial height is `style.spacing.interact_size.y`.
2294 /// Centering is almost always what you want if you are
2295 /// planning to mix widgets or use different types of text.
2296 ///
2297 /// If you don't want the contents to be centered, use [`Self::horizontal_top`] instead.
2298 ///
2299 /// The returned [`Response`] will only have checked for mouse hover
2300 /// but can be used for tooltips (`on_hover_text`).
2301 /// It also contains the [`Rect`] used by the horizontal layout.
2302 ///
2303 /// ```
2304 /// # egui::__run_test_ui(|ui| {
2305 /// ui.horizontal(|ui| {
2306 /// ui.label("Same");
2307 /// ui.label("row");
2308 /// });
2309 /// # });
2310 /// ```
2311 ///
2312 /// See also [`Self::with_layout`] for more options.
2313 #[inline]
2314 pub fn horizontal<R>(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {
2315 self.horizontal_with_main_wrap_dyn(false, Box::new(add_contents))
2316 }
2317
2318 /// Like [`Self::horizontal`], but allocates the full vertical height and then centers elements vertically.
2319 pub fn horizontal_centered<R>(
2320 &mut self,
2321 add_contents: impl FnOnce(&mut Ui) -> R,
2322 ) -> InnerResponse<R> {
2323 let initial_size = self.available_size_before_wrap();
2324 let layout = if self.placer.prefer_right_to_left() {
2325 Layout::right_to_left(Align::Center)
2326 } else {
2327 Layout::left_to_right(Align::Center)
2328 }
2329 .with_cross_align(Align::Center);
2330 self.allocate_ui_with_layout_dyn(initial_size, layout, Box::new(add_contents))
2331 }
2332
2333 /// Like [`Self::horizontal`], but aligns content with top.
2334 pub fn horizontal_top<R>(
2335 &mut self,
2336 add_contents: impl FnOnce(&mut Ui) -> R,
2337 ) -> InnerResponse<R> {
2338 let initial_size = self.available_size_before_wrap();
2339 let layout = if self.placer.prefer_right_to_left() {
2340 Layout::right_to_left(Align::Center)
2341 } else {
2342 Layout::left_to_right(Align::Center)
2343 }
2344 .with_cross_align(Align::Min);
2345 self.allocate_ui_with_layout_dyn(initial_size, layout, Box::new(add_contents))
2346 }
2347
2348 /// Start a ui with horizontal layout that wraps to a new row
2349 /// when it reaches the right edge of the `max_size`.
2350 /// After you have called this, the function registers the contents as any other widget.
2351 ///
2352 /// Elements will be centered on the Y axis, i.e.
2353 /// adjusted up and down to lie in the center of the horizontal layout.
2354 /// The initial height is `style.spacing.interact_size.y`.
2355 /// Centering is almost always what you want if you are
2356 /// planning to mix widgets or use different types of text.
2357 ///
2358 /// The returned [`Response`] will only have checked for mouse hover
2359 /// but can be used for tooltips (`on_hover_text`).
2360 /// It also contains the [`Rect`] used by the horizontal layout.
2361 ///
2362 /// See also [`Self::with_layout`] for more options.
2363 pub fn horizontal_wrapped<R>(
2364 &mut self,
2365 add_contents: impl FnOnce(&mut Ui) -> R,
2366 ) -> InnerResponse<R> {
2367 self.horizontal_with_main_wrap_dyn(true, Box::new(add_contents))
2368 }
2369
2370 fn horizontal_with_main_wrap_dyn<'c, R>(
2371 &mut self,
2372 main_wrap: bool,
2373 add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,
2374 ) -> InnerResponse<R> {
2375 let initial_size = vec2(
2376 self.available_size_before_wrap().x,
2377 self.spacing().interact_size.y, // Assume there will be something interactive on the horizontal layout
2378 );
2379
2380 let layout = if self.placer.prefer_right_to_left() {
2381 Layout::right_to_left(Align::Center)
2382 } else {
2383 Layout::left_to_right(Align::Center)
2384 }
2385 .with_main_wrap(main_wrap);
2386
2387 self.allocate_ui_with_layout_dyn(initial_size, layout, add_contents)
2388 }
2389
2390 /// Start a ui with vertical layout.
2391 /// Widgets will be left-justified.
2392 ///
2393 /// ```
2394 /// # egui::__run_test_ui(|ui| {
2395 /// ui.vertical(|ui| {
2396 /// ui.label("over");
2397 /// ui.label("under");
2398 /// });
2399 /// # });
2400 /// ```
2401 ///
2402 /// See also [`Self::with_layout`] for more options.
2403 #[inline]
2404 pub fn vertical<R>(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {
2405 self.scope_builder(
2406 UiBuilder::new().layout(Layout::top_down(Align::Min)),
2407 add_contents,
2408 )
2409 }
2410
2411 /// Start a ui with vertical layout.
2412 /// Widgets will be horizontally centered.
2413 ///
2414 /// ```
2415 /// # egui::__run_test_ui(|ui| {
2416 /// ui.vertical_centered(|ui| {
2417 /// ui.label("over");
2418 /// ui.label("under");
2419 /// });
2420 /// # });
2421 /// ```
2422 #[inline]
2423 pub fn vertical_centered<R>(
2424 &mut self,
2425 add_contents: impl FnOnce(&mut Ui) -> R,
2426 ) -> InnerResponse<R> {
2427 self.scope_builder(
2428 UiBuilder::new().layout(Layout::top_down(Align::Center)),
2429 add_contents,
2430 )
2431 }
2432
2433 /// Start a ui with vertical layout.
2434 /// Widgets will be horizontally centered and justified (fill full width).
2435 ///
2436 /// ```
2437 /// # egui::__run_test_ui(|ui| {
2438 /// ui.vertical_centered_justified(|ui| {
2439 /// ui.label("over");
2440 /// ui.label("under");
2441 /// });
2442 /// # });
2443 /// ```
2444 pub fn vertical_centered_justified<R>(
2445 &mut self,
2446 add_contents: impl FnOnce(&mut Ui) -> R,
2447 ) -> InnerResponse<R> {
2448 self.scope_builder(
2449 UiBuilder::new().layout(Layout::top_down(Align::Center).with_cross_justify(true)),
2450 add_contents,
2451 )
2452 }
2453
2454 /// The new layout will take up all available space.
2455 ///
2456 /// ```
2457 /// # egui::__run_test_ui(|ui| {
2458 /// ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
2459 /// ui.label("world!");
2460 /// ui.label("Hello");
2461 /// });
2462 /// # });
2463 /// ```
2464 ///
2465 /// If you don't want to use up all available space, use [`Self::allocate_ui_with_layout`].
2466 ///
2467 /// See also the helpers [`Self::horizontal`], [`Self::vertical`], etc.
2468 #[inline]
2469 pub fn with_layout<R>(
2470 &mut self,
2471 layout: Layout,
2472 add_contents: impl FnOnce(&mut Self) -> R,
2473 ) -> InnerResponse<R> {
2474 self.scope_builder(UiBuilder::new().layout(layout), add_contents)
2475 }
2476
2477 /// This will make the next added widget centered and justified in the available space.
2478 ///
2479 /// Only one widget may be added to the inner `Ui`!
2480 pub fn centered_and_justified<R>(
2481 &mut self,
2482 add_contents: impl FnOnce(&mut Self) -> R,
2483 ) -> InnerResponse<R> {
2484 self.scope_builder(
2485 UiBuilder::new().layout(Layout::centered_and_justified(Direction::TopDown)),
2486 add_contents,
2487 )
2488 }
2489
2490 pub(crate) fn set_grid(&mut self, grid: grid::GridLayout) {
2491 self.placer.set_grid(grid);
2492 }
2493
2494 pub(crate) fn save_grid(&mut self) {
2495 self.placer.save_grid();
2496 }
2497
2498 pub(crate) fn is_grid(&self) -> bool {
2499 self.placer.is_grid()
2500 }
2501
2502 /// Move to the next row in a grid layout or wrapping layout.
2503 /// Otherwise does nothing.
2504 pub fn end_row(&mut self) {
2505 self.placer
2506 .end_row(self.spacing().item_spacing, &self.painter().clone());
2507 }
2508
2509 /// Set row height in horizontal wrapping layout.
2510 pub fn set_row_height(&mut self, height: f32) {
2511 self.placer.set_row_height(height);
2512 }
2513
2514 /// Temporarily split a [`Ui`] into several columns.
2515 ///
2516 /// ```
2517 /// # egui::__run_test_ui(|ui| {
2518 /// ui.columns(2, |columns| {
2519 /// columns[0].label("First column");
2520 /// columns[1].label("Second column");
2521 /// });
2522 /// # });
2523 /// ```
2524 #[inline]
2525 pub fn columns<R>(
2526 &mut self,
2527 num_columns: usize,
2528 add_contents: impl FnOnce(&mut [Self]) -> R,
2529 ) -> R {
2530 self.columns_dyn(num_columns, Box::new(add_contents))
2531 }
2532
2533 fn columns_dyn<'c, R>(
2534 &mut self,
2535 num_columns: usize,
2536 add_contents: Box<dyn FnOnce(&mut [Self]) -> R + 'c>,
2537 ) -> R {
2538 // TODO(emilk): ensure there is space
2539 let spacing = self.spacing().item_spacing.x;
2540 let total_spacing = spacing * (num_columns as f32 - 1.0);
2541 let column_width = (self.available_width() - total_spacing) / (num_columns as f32);
2542 let top_left = self.cursor().min;
2543
2544 let mut columns: Vec<Self> = (0..num_columns)
2545 .map(|col_idx| {
2546 let pos = top_left + vec2((col_idx as f32) * (column_width + spacing), 0.0);
2547 let child_rect = Rect::from_min_max(
2548 pos,
2549 pos2(pos.x + column_width, self.max_rect().right_bottom().y),
2550 );
2551 let mut column_ui = self.new_child(
2552 UiBuilder::new()
2553 .max_rect(child_rect)
2554 .layout(Layout::top_down_justified(Align::LEFT)),
2555 );
2556 column_ui.set_width(column_width);
2557 column_ui
2558 })
2559 .collect();
2560
2561 let result = add_contents(&mut columns[..]);
2562
2563 let mut max_column_width = column_width;
2564 let mut max_height = 0.0;
2565 for column in &columns {
2566 max_column_width = max_column_width.max(column.min_rect().width());
2567 max_height = column.min_size().y.max(max_height);
2568 }
2569
2570 // Make sure we fit everything next frame:
2571 let total_required_width = total_spacing + max_column_width * (num_columns as f32);
2572
2573 let size = vec2(self.available_width().max(total_required_width), max_height);
2574 self.advance_cursor_after_rect(Rect::from_min_size(top_left, size));
2575 result
2576 }
2577
2578 /// Temporarily split a [`Ui`] into several columns.
2579 ///
2580 /// The same as [`Self::columns()`], but uses a constant for the column count.
2581 /// This allows for compile-time bounds checking, and makes the compiler happy.
2582 ///
2583 /// ```
2584 /// # egui::__run_test_ui(|ui| {
2585 /// ui.columns_const(|[col_1, col_2]| {
2586 /// col_1.label("First column");
2587 /// col_2.label("Second column");
2588 /// });
2589 /// # });
2590 /// ```
2591 #[inline]
2592 pub fn columns_const<const NUM_COL: usize, R>(
2593 &mut self,
2594 add_contents: impl FnOnce(&mut [Self; NUM_COL]) -> R,
2595 ) -> R {
2596 // TODO(emilk): ensure there is space
2597 let spacing = self.spacing().item_spacing.x;
2598 let total_spacing = spacing * (NUM_COL as f32 - 1.0);
2599 let column_width = (self.available_width() - total_spacing) / (NUM_COL as f32);
2600 let top_left = self.cursor().min;
2601
2602 let mut columns = std::array::from_fn(|col_idx| {
2603 let pos = top_left + vec2((col_idx as f32) * (column_width + spacing), 0.0);
2604 let child_rect = Rect::from_min_max(
2605 pos,
2606 pos2(pos.x + column_width, self.max_rect().right_bottom().y),
2607 );
2608 let mut column_ui = self.new_child(
2609 UiBuilder::new()
2610 .max_rect(child_rect)
2611 .layout(Layout::top_down_justified(Align::LEFT)),
2612 );
2613 column_ui.set_width(column_width);
2614 column_ui
2615 });
2616 let result = add_contents(&mut columns);
2617
2618 let mut max_column_width = column_width;
2619 let mut max_height = 0.0;
2620 for column in &columns {
2621 max_column_width = max_column_width.max(column.min_rect().width());
2622 max_height = column.min_size().y.max(max_height);
2623 }
2624
2625 // Make sure we fit everything next frame:
2626 let total_required_width = total_spacing + max_column_width * (NUM_COL as f32);
2627
2628 let size = vec2(self.available_width().max(total_required_width), max_height);
2629 self.advance_cursor_after_rect(Rect::from_min_size(top_left, size));
2630 result
2631 }
2632
2633 /// Create something that can be drag-and-dropped.
2634 ///
2635 /// The `id` needs to be globally unique.
2636 /// The payload is what will be dropped if the user starts dragging.
2637 ///
2638 /// In contrast to [`Response::dnd_set_drag_payload`],
2639 /// this function will paint the widget at the mouse cursor while the user is dragging.
2640 #[doc(alias = "drag and drop")]
2641 pub fn dnd_drag_source<Payload, R>(
2642 &mut self,
2643 id: Id,
2644 payload: Payload,
2645 add_contents: impl FnOnce(&mut Self) -> R,
2646 ) -> InnerResponse<R>
2647 where
2648 Payload: Any + Send + Sync,
2649 {
2650 let is_being_dragged = self.ctx().is_being_dragged(id);
2651
2652 if is_being_dragged {
2653 crate::DragAndDrop::set_payload(self.ctx(), payload);
2654
2655 // Paint the body to a new layer:
2656 let layer_id = LayerId::new(Order::Tooltip, id);
2657 let InnerResponse { inner, response } =
2658 self.scope_builder(UiBuilder::new().layer_id(layer_id), add_contents);
2659
2660 // Now we move the visuals of the body to where the mouse is.
2661 // Normally you need to decide a location for a widget first,
2662 // because otherwise that widget cannot interact with the mouse.
2663 // However, a dragged component cannot be interacted with anyway
2664 // (anything with `Order::Tooltip` always gets an empty [`Response`])
2665 // So this is fine!
2666
2667 if let Some(pointer_pos) = self.ctx().pointer_interact_pos() {
2668 let delta = pointer_pos - response.rect.center();
2669 self.ctx()
2670 .transform_layer_shapes(layer_id, emath::TSTransform::from_translation(delta));
2671 }
2672
2673 InnerResponse::new(inner, response)
2674 } else {
2675 let InnerResponse { inner, response } = self.scope(add_contents);
2676
2677 // Check for drags:
2678 let dnd_response = self
2679 .interact(response.rect, id, Sense::drag())
2680 .on_hover_cursor(CursorIcon::Grab);
2681
2682 InnerResponse::new(inner, dnd_response | response)
2683 }
2684 }
2685
2686 /// Surround the given ui with a frame which
2687 /// changes colors when you can drop something onto it.
2688 ///
2689 /// Returns the dropped item, if it was released this frame.
2690 ///
2691 /// The given frame is used for its margins, but the color is ignored.
2692 #[doc(alias = "drag and drop")]
2693 pub fn dnd_drop_zone<Payload, R>(
2694 &mut self,
2695 frame: Frame,
2696 add_contents: impl FnOnce(&mut Ui) -> R,
2697 ) -> (InnerResponse<R>, Option<Arc<Payload>>)
2698 where
2699 Payload: Any + Send + Sync,
2700 {
2701 let is_anything_being_dragged = DragAndDrop::has_any_payload(self.ctx());
2702 let can_accept_what_is_being_dragged =
2703 DragAndDrop::has_payload_of_type::<Payload>(self.ctx());
2704
2705 let mut frame = frame.begin(self);
2706 let inner = add_contents(&mut frame.content_ui);
2707 let response = frame.allocate_space(self);
2708
2709 // NOTE: we use `response.contains_pointer` here instead of `hovered`, because
2710 // `hovered` is always false when another widget is being dragged.
2711 let style = if is_anything_being_dragged
2712 && can_accept_what_is_being_dragged
2713 && response.contains_pointer()
2714 {
2715 self.visuals().widgets.active
2716 } else {
2717 self.visuals().widgets.inactive
2718 };
2719
2720 let mut fill = style.bg_fill;
2721 let mut stroke = style.bg_stroke;
2722
2723 if is_anything_being_dragged && !can_accept_what_is_being_dragged {
2724 // When dragging something else, show that it can't be dropped here:
2725 fill = self.visuals().disable(fill);
2726 stroke.color = self.visuals().disable(stroke.color);
2727 }
2728
2729 frame.frame.fill = fill;
2730 frame.frame.stroke = stroke;
2731
2732 frame.paint(self);
2733
2734 let payload = response.dnd_release_payload::<Payload>();
2735
2736 (InnerResponse { inner, response }, payload)
2737 }
2738
2739 /// Create a new Scope and transform its contents via a [`emath::TSTransform`].
2740 /// This only affects visuals, inputs will not be transformed. So this is mostly useful
2741 /// to create visual effects on interactions, e.g. scaling a button on hover / click.
2742 ///
2743 /// Check out [`Context::set_transform_layer`] for a persistent transform that also affects
2744 /// inputs.
2745 pub fn with_visual_transform<R>(
2746 &mut self,
2747 transform: emath::TSTransform,
2748 add_contents: impl FnOnce(&mut Self) -> R,
2749 ) -> InnerResponse<R> {
2750 let start_idx = self.ctx().graphics(|gx| {
2751 gx.get(self.layer_id())
2752 .map_or(crate::layers::ShapeIdx(0), |l| l.next_idx())
2753 });
2754
2755 let r = self.scope_dyn(UiBuilder::new(), Box::new(add_contents));
2756
2757 self.ctx().graphics_mut(|g| {
2758 let list = g.entry(self.layer_id());
2759 let end_idx = list.next_idx();
2760 list.transform_range(start_idx, end_idx, transform);
2761 });
2762
2763 r
2764 }
2765}
2766
2767/// # Menus
2768impl Ui {
2769 #[inline]
2770 /// Create a menu button that when clicked will show the given menu.
2771 ///
2772 /// If called from within a menu this will instead create a button for a sub-menu.
2773 ///
2774 /// ```
2775 /// # egui::__run_test_ui(|ui| {
2776 /// ui.menu_button("My menu", |ui| {
2777 /// ui.menu_button("My sub-menu", |ui| {
2778 /// if ui.button("Close the menu").clicked() {
2779 /// ui.close();
2780 /// }
2781 /// });
2782 /// });
2783 /// # });
2784 /// ```
2785 ///
2786 /// See also: [`Self::close`] and [`Response::context_menu`].
2787 pub fn menu_button<'a, R>(
2788 &mut self,
2789 atoms: impl IntoAtoms<'a>,
2790 add_contents: impl FnOnce(&mut Ui) -> R,
2791 ) -> InnerResponse<Option<R>> {
2792 let (response, inner) = if menu::is_in_menu(self) {
2793 menu::SubMenuButton::new(atoms).ui(self, add_contents)
2794 } else {
2795 menu::MenuButton::new(atoms).ui(self, add_contents)
2796 };
2797 InnerResponse::new(inner.map(|i| i.inner), response)
2798 }
2799
2800 /// Create a menu button with an image that when clicked will show the given menu.
2801 ///
2802 /// If called from within a menu this will instead create a button for a sub-menu.
2803 ///
2804 /// ```ignore
2805 /// # egui::__run_test_ui(|ui| {
2806 /// let img = egui::include_image!("../assets/ferris.png");
2807 ///
2808 /// ui.menu_image_button(title, img, |ui| {
2809 /// ui.menu_button("My sub-menu", |ui| {
2810 /// if ui.button("Close the menu").clicked() {
2811 /// ui.close();
2812 /// }
2813 /// });
2814 /// });
2815 /// # });
2816 /// ```
2817 ///
2818 ///
2819 /// See also: [`Self::close`] and [`Response::context_menu`].
2820 #[inline]
2821 pub fn menu_image_button<'a, R>(
2822 &mut self,
2823 image: impl Into<Image<'a>>,
2824 add_contents: impl FnOnce(&mut Ui) -> R,
2825 ) -> InnerResponse<Option<R>> {
2826 let (response, inner) = if menu::is_in_menu(self) {
2827 menu::SubMenuButton::from_button(
2828 Button::image(image).right_text(menu::SubMenuButton::RIGHT_ARROW),
2829 )
2830 .ui(self, add_contents)
2831 } else {
2832 menu::MenuButton::from_button(Button::image(image)).ui(self, add_contents)
2833 };
2834 InnerResponse::new(inner.map(|i| i.inner), response)
2835 }
2836
2837 /// Create a menu button with an image and a text that when clicked will show the given menu.
2838 ///
2839 /// If called from within a menu this will instead create a button for a sub-menu.
2840 ///
2841 /// ```
2842 /// # egui::__run_test_ui(|ui| {
2843 /// let img = egui::include_image!("../assets/ferris.png");
2844 /// let title = "My Menu";
2845 ///
2846 /// ui.menu_image_text_button(img, title, |ui| {
2847 /// ui.menu_button("My sub-menu", |ui| {
2848 /// if ui.button("Close the menu").clicked() {
2849 /// ui.close();
2850 /// }
2851 /// });
2852 /// });
2853 /// # });
2854 /// ```
2855 ///
2856 /// See also: [`Self::close`] and [`Response::context_menu`].
2857 #[inline]
2858 pub fn menu_image_text_button<'a, R>(
2859 &mut self,
2860 image: impl Into<Image<'a>>,
2861 title: impl Into<WidgetText>,
2862 add_contents: impl FnOnce(&mut Ui) -> R,
2863 ) -> InnerResponse<Option<R>> {
2864 let (response, inner) = if menu::is_in_menu(self) {
2865 menu::SubMenuButton::from_button(
2866 Button::image_and_text(image, title).right_text(menu::SubMenuButton::RIGHT_ARROW),
2867 )
2868 .ui(self, add_contents)
2869 } else {
2870 menu::MenuButton::from_button(Button::image_and_text(image, title))
2871 .ui(self, add_contents)
2872 };
2873 InnerResponse::new(inner.map(|i| i.inner), response)
2874 }
2875}
2876
2877// ----------------------------------------------------------------------------
2878
2879/// # Debug stuff
2880impl Ui {
2881 /// Shows where the next widget is going to be placed
2882 #[cfg(debug_assertions)]
2883 pub fn debug_paint_cursor(&self) {
2884 self.placer.debug_paint_cursor(&self.painter, "next");
2885 }
2886}
2887
2888impl Drop for Ui {
2889 fn drop(&mut self) {
2890 if !self.min_rect_already_remembered {
2891 // Register our final `min_rect`
2892 self.remember_min_rect();
2893 }
2894 #[cfg(debug_assertions)]
2895 register_rect(self, self.min_rect());
2896 }
2897}
2898
2899/// Show this rectangle to the user if certain debug options are set.
2900#[cfg(debug_assertions)]
2901fn register_rect(ui: &Ui, rect: Rect) {
2902 use emath::{Align2, GuiRounding as _};
2903
2904 let debug = ui.style().debug;
2905
2906 if debug.show_unaligned {
2907 let unaligned_line = |p0: Pos2, p1: Pos2| {
2908 let color = Color32::ORANGE;
2909 let font_id = TextStyle::Monospace.resolve(ui.style());
2910 ui.painter().line_segment([p0, p1], (1.0, color));
2911 ui.painter()
2912 .text(p0, Align2::LEFT_TOP, "Unaligned", font_id, color);
2913 };
2914
2915 if rect.left() != rect.left().round_ui() {
2916 unaligned_line(rect.left_top(), rect.left_bottom());
2917 }
2918 if rect.right() != rect.right().round_ui() {
2919 unaligned_line(rect.right_top(), rect.right_bottom());
2920 }
2921 if rect.top() != rect.top().round_ui() {
2922 unaligned_line(rect.left_top(), rect.right_top());
2923 }
2924 if rect.bottom() != rect.bottom().round_ui() {
2925 unaligned_line(rect.left_bottom(), rect.right_bottom());
2926 }
2927 }
2928
2929 let show_callstacks = debug.debug_on_hover
2930 || debug.debug_on_hover_with_all_modifiers && ui.input(|i| i.modifiers.all());
2931
2932 if !show_callstacks {
2933 return;
2934 }
2935
2936 if !ui.rect_contains_pointer(rect) {
2937 return;
2938 }
2939
2940 let is_clicking = ui.input(|i| i.pointer.could_any_button_be_click());
2941
2942 #[cfg(feature = "callstack")]
2943 let callstack = crate::callstack::capture();
2944
2945 #[cfg(not(feature = "callstack"))]
2946 let callstack = String::default();
2947
2948 // We only show one debug rectangle, or things get confusing:
2949 let debug_rect = pass_state::DebugRect {
2950 rect,
2951 callstack,
2952 is_clicking,
2953 };
2954
2955 let mut kept = false;
2956 ui.ctx().pass_state_mut(|fs| {
2957 if let Some(final_debug_rect) = &mut fs.debug_rect {
2958 // or maybe pick the one with deepest callstack?
2959 if final_debug_rect.rect.contains_rect(rect) {
2960 *final_debug_rect = debug_rect;
2961 kept = true;
2962 }
2963 } else {
2964 fs.debug_rect = Some(debug_rect);
2965 kept = true;
2966 }
2967 });
2968 if !kept {
2969 return;
2970 }
2971
2972 // ----------------------------------------------
2973
2974 // Use the debug-painter to avoid clip rect,
2975 // otherwise the content of the widget may cover what we paint here!
2976 let painter = ui.debug_painter();
2977
2978 if debug.hover_shows_next {
2979 ui.placer.debug_paint_cursor(&painter, "next");
2980 }
2981}
2982
2983#[cfg(not(debug_assertions))]
2984fn register_rect(_ui: &Ui, _rect: Rect) {}
2985
2986#[test]
2987fn ui_impl_send_sync() {
2988 fn assert_send_sync<T: Send + Sync>() {}
2989 assert_send_sync::<Ui>();
2990}