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