1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-26 14:49:06 -04:00

Deduplicate shared builder fns

This commit is contained in:
lucasmerlin
2026-06-03 13:12:25 +02:00
parent 674ac3b3ab
commit 24ee6fe756
2 changed files with 121 additions and 194 deletions

View File

@@ -1,5 +1,6 @@
use crate::{
AtomKind, Atoms, FontSelection, Frame, Id, Image, IntoAtoms, SizedAtom, SizedAtomKind, Ui,
WidgetAtom,
};
use emath::{Align2, GuiRounding as _, NumExt as _, Rect, Vec2};
use epaint::text::TextWrapMode;
@@ -64,99 +65,6 @@ impl<'a> ContainerAtom<'a> {
}
}
/// Set the gap between atoms.
///
/// Default: `Spacing::icon_spacing`
#[inline]
pub fn gap(mut self, gap: f32) -> Self {
self.gap = Some(gap);
self
}
/// Set the [`Frame`].
#[inline]
pub fn frame(mut self, frame: Frame) -> Self {
self.frame = frame;
self
}
/// Set the fallback (default) text color.
///
/// Default: [`crate::Visuals::text_color`]
#[inline]
pub fn fallback_text_color(mut self, color: Color32) -> Self {
self.fallback_text_color = Some(color);
self
}
/// Set the fallback (default) font.
#[inline]
pub fn fallback_font(mut self, font: impl Into<FontSelection>) -> Self {
self.fallback_font = Some(font.into());
self
}
/// Set the minimum size of the Widget.
///
/// This will find and expand atoms with `grow: true`.
/// If there are no growable atoms then everything will be left-aligned.
#[inline]
pub fn min_size(mut self, size: Vec2) -> Self {
self.min_size = size;
self
}
/// Set the maximum size of the Widget.
///
/// By default, the size is limited by the available size in the [`Ui`].
#[inline]
pub fn max_size(mut self, size: Vec2) -> Self {
self.max_size = size;
self
}
/// Set the maximum width of the Widget.
///
/// By default, the width is limited by the available width in the [`Ui`].
#[inline]
pub fn max_width(mut self, width: f32) -> Self {
self.max_size.x = width;
self
}
/// Set the maximum height of the Widget.
///
/// By default, the height is limited by the available height in the [`Ui`].
#[inline]
pub fn max_height(mut self, height: f32) -> Self {
self.max_size.y = height;
self
}
/// Set the [`TextWrapMode`] for the [`crate::Atom`] marked as `shrink`.
///
/// Only a single [`crate::Atom`] may shrink. If this (or `ui.wrap_mode()`) is not
/// [`TextWrapMode::Extend`] and no item is set to shrink, the first (left-most)
/// [`AtomKind::Text`] will be set to shrink.
#[inline]
pub fn wrap_mode(mut self, wrap_mode: TextWrapMode) -> Self {
self.wrap_mode = Some(wrap_mode);
self
}
/// Set the [`Align2`].
///
/// This will align the [`crate::Atom`]s within the [`Rect`] returned by [`Ui::allocate_space`].
///
/// The default is chosen based on the [`Ui`]s [`crate::Layout`]. See
/// [this snapshot](https://github.com/emilk/egui/blob/master/tests/egui_tests/tests/snapshots/layout/button.png)
/// for info on how the [`crate::Layout`] affects the alignment.
#[inline]
pub fn align2(mut self, align2: Align2) -> Self {
self.align2 = Some(align2);
self
}
/// Measure the atoms (sizing only), without allocating space or interacting.
///
/// This converts texts to [`Galley`]s and calculates sizes, but it does *not* call
@@ -312,6 +220,118 @@ impl<'a> ContainerAtom<'a> {
}
}
/// Generates the layout-builder methods shared by [`ContainerAtom`] and [`WidgetAtom`] from a
/// single definition, so the two can never drift apart.
///
/// Each entry is written as it appears on [`ContainerAtom`] (mutating its own fields). The
/// matching method on [`WidgetAtom`] is generated automatically, forwarding to its inner
/// `container`. [`WidgetAtom`]-only builders (`id`, `sense`) stay inherent on [`WidgetAtom`].
macro_rules! shared_container_builders {
(
$(
$(#[$meta:meta])*
fn $name:ident($self:ident, $($arg:ident: $arg_ty:ty),* $(,)?) $body:block
)*
) => {
impl<'a> ContainerAtom<'a> {
$(
$(#[$meta])*
#[inline]
pub fn $name(mut $self, $($arg: $arg_ty),*) -> Self {
$body
$self
}
)*
}
impl<'a> WidgetAtom<'a> {
$(
$(#[$meta])*
#[inline]
pub fn $name(mut self, $($arg: $arg_ty),*) -> Self {
self.container = self.container.$name($($arg),*);
self
}
)*
}
};
}
shared_container_builders! {
/// Set the gap between atoms.
///
/// Default: `Spacing::icon_spacing`
fn gap(self, gap: f32) {
self.gap = Some(gap);
}
/// Set the [`Frame`].
fn frame(self, frame: Frame) {
self.frame = frame;
}
/// Set the fallback (default) text color.
///
/// Default: [`crate::Visuals::text_color`]
fn fallback_text_color(self, color: Color32) {
self.fallback_text_color = Some(color);
}
/// Set the fallback (default) font.
fn fallback_font(self, font: impl Into<FontSelection>) {
self.fallback_font = Some(font.into());
}
/// Set the minimum size of the Widget.
///
/// This will find and expand atoms with `grow: true`.
/// If there are no growable atoms then everything will be left-aligned.
fn min_size(self, size: Vec2) {
self.min_size = size;
}
/// Set the maximum size of the Widget.
///
/// By default, the size is limited by the available size in the [`Ui`].
fn max_size(self, size: Vec2) {
self.max_size = size;
}
/// Set the maximum width of the Widget.
///
/// By default, the width is limited by the available width in the [`Ui`].
fn max_width(self, width: f32) {
self.max_size.x = width;
}
/// Set the maximum height of the Widget.
///
/// By default, the height is limited by the available height in the [`Ui`].
fn max_height(self, height: f32) {
self.max_size.y = height;
}
/// Set the [`TextWrapMode`] for the [`crate::Atom`] marked as `shrink`.
///
/// Only a single [`crate::Atom`] may shrink. If this (or `ui.wrap_mode()`) is not
/// [`TextWrapMode::Extend`] and no item is set to shrink, the first (left-most)
/// [`AtomKind::Text`] will be set to shrink.
fn wrap_mode(self, wrap_mode: TextWrapMode) {
self.wrap_mode = Some(wrap_mode);
}
/// Set the [`Align2`].
///
/// This will align the [`crate::Atom`]s within the [`Rect`] returned by [`Ui::allocate_space`].
///
/// The default is chosen based on the [`Ui`]s [`crate::Layout`]. See
/// [this snapshot](https://github.com/emilk/egui/blob/master/tests/egui_tests/tests/snapshots/layout/button.png)
/// for info on how the [`crate::Layout`] affects the alignment.
fn align2(self, align2: Align2) {
self.align2 = Some(align2);
}
}
/// A measured [`ContainerAtom`], ready to be painted at a [`Rect`].
///
/// Produced by [`ContainerAtom::measure`]. It has not yet allocated space or interacted, so it

View File

@@ -1,9 +1,5 @@
use crate::{
Align2, Color32, ContainerAtom, FontSelection, Frame, Id, IntoAtoms, Response, Sense,
SizedContainerAtom, Ui, Widget,
};
use crate::{ContainerAtom, Id, IntoAtoms, Response, Sense, SizedContainerAtom, Ui, Widget};
use emath::{Rect, Vec2};
use epaint::text::TextWrapMode;
use smallvec::SmallVec;
use std::ops::{Deref, DerefMut};
@@ -20,9 +16,9 @@ use std::ops::{Deref, DerefMut};
/// - allocates a [`Response`]
/// - returns an [`AllocatedWidgetAtom`]
/// - [`AllocatedWidgetAtom::paint`]
/// - paints the [`Frame`] and each single atom
/// - paints the [`crate::Frame`] and each single atom
///
/// You can use this to first allocate a response and then modify, e.g., the [`Frame`] on the
/// You can use this to first allocate a response and then modify, e.g., the [`crate::Frame`] on the
/// [`AllocatedWidgetAtom`] for interaction styling.
#[derive(Clone)]
pub struct WidgetAtom<'a> {
@@ -60,98 +56,9 @@ impl<'a> WidgetAtom<'a> {
self
}
/// Set the gap between atoms.
///
/// Default: `Spacing::icon_spacing`
#[inline]
pub fn gap(mut self, gap: f32) -> Self {
self.container = self.container.gap(gap);
self
}
/// Set the [`Frame`].
#[inline]
pub fn frame(mut self, frame: Frame) -> Self {
self.container = self.container.frame(frame);
self
}
/// Set the fallback (default) text color.
///
/// Default: [`crate::Visuals::text_color`]
#[inline]
pub fn fallback_text_color(mut self, color: Color32) -> Self {
self.container = self.container.fallback_text_color(color);
self
}
/// Set the fallback (default) font.
#[inline]
pub fn fallback_font(mut self, font: impl Into<FontSelection>) -> Self {
self.container = self.container.fallback_font(font);
self
}
/// Set the minimum size of the Widget.
///
/// This will find and expand atoms with `grow: true`.
/// If there are no growable atoms then everything will be left-aligned.
#[inline]
pub fn min_size(mut self, size: Vec2) -> Self {
self.container = self.container.min_size(size);
self
}
/// Set the maximum size of the Widget.
///
/// By default, the size is limited by the available size in the [`Ui`].
#[inline]
pub fn max_size(mut self, size: Vec2) -> Self {
self.container = self.container.max_size(size);
self
}
/// Set the maximum width of the Widget.
///
/// By default, the width is limited by the available width in the [`Ui`].
#[inline]
pub fn max_width(mut self, width: f32) -> Self {
self.container = self.container.max_width(width);
self
}
/// Set the maximum height of the Widget.
///
/// By default, the height is limited by the available height in the [`Ui`].
#[inline]
pub fn max_height(mut self, height: f32) -> Self {
self.container = self.container.max_height(height);
self
}
/// Set the [`TextWrapMode`] for the [`crate::Atom`] marked as `shrink`.
///
/// Only a single [`crate::Atom`] may shrink. If this (or `ui.wrap_mode()`) is not
/// [`TextWrapMode::Extend`] and no item is set to shrink, the first (left-most)
/// [`crate::AtomKind::Text`] will be set to shrink.
#[inline]
pub fn wrap_mode(mut self, wrap_mode: TextWrapMode) -> Self {
self.container = self.container.wrap_mode(wrap_mode);
self
}
/// Set the [`Align2`].
///
/// This will align the [`crate::Atom`]s within the [`Rect`] returned by [`Ui::allocate_space`].
///
/// The default is chosen based on the [`Ui`]s [`crate::Layout`]. See
/// [this snapshot](https://github.com/emilk/egui/blob/master/tests/egui_tests/tests/snapshots/layout/button.png)
/// for info on how the [`crate::Layout`] affects the alignment.
#[inline]
pub fn align2(mut self, align2: Align2) -> Self {
self.container = self.container.align2(align2);
self
}
// The builders shared with `ContainerAtom` (`gap`, `frame`, `fallback_font`, `min_size`,
// `wrap_mode`, …) are generated by the `shared_container_builders!` macro in
// `container_atom.rs`, so the two types can never drift apart.
/// [`WidgetAtom::allocate`] and [`AllocatedWidgetAtom::paint`] in one go.
pub fn show(self, ui: &mut Ui) -> WidgetAtomResponse {
@@ -223,7 +130,7 @@ impl<'a> SizedWidgetAtom<'a> {
}
}
/// Interact at `rect` and paint the [`Frame`] and atoms there.
/// Interact at `rect` and paint the [`crate::Frame`] and atoms there.
///
/// Unlike [`Self::allocate`] this does not call [`Ui::allocate_space`]; it interacts at the
/// given `rect` using this widget's [`Id`] and [`Sense`]. This is used when nesting one
@@ -251,7 +158,7 @@ pub struct AllocatedWidgetAtom<'a> {
}
impl AllocatedWidgetAtom<'_> {
/// Paint the [`Frame`] and individual [`crate::Atom`]s at the allocated [`Response`]'s rect.
/// Paint the [`crate::Frame`] and individual [`crate::Atom`]s at the allocated [`Response`]'s rect.
pub fn paint(self, ui: &Ui) -> WidgetAtomResponse {
let rect = self.response.rect;
let custom_rects = self.container.paint_at(ui, rect);