1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 14:20:04 -04:00

Split AtomLayout into WidgetAtom and ContainerAtom

This commit is contained in:
lucasmerlin
2026-06-03 12:55:01 +02:00
parent 5e8bd371bc
commit 674ac3b3ab
16 changed files with 530 additions and 237 deletions

View File

@@ -1,5 +1,6 @@
use crate::{ use crate::{
AtomKind, AtomLayout, FontSelection, Id, IntoSizedArgs, IntoSizedResult, SizedAtom, Ui, AtomKind, ContainerAtom, FontSelection, Id, IntoSizedArgs, IntoSizedResult, SizedAtom, Ui,
WidgetAtom,
}; };
use emath::{Align2, NumExt as _, Vec2}; use emath::{Align2, NumExt as _, Vec2};
use epaint::text::TextWrapMode; use epaint::text::TextWrapMode;
@@ -103,13 +104,25 @@ impl<'a> Atom<'a> {
} }
} }
/// Nest an [`AtomLayout`] (e.g. an atom-based widget) as a single atom. /// Nest a [`WidgetAtom`] (e.g. an atom-based widget) as a single atom.
/// ///
/// The nested layout is sized when the parent is sized and painted (and interacted with) /// The nested widget is sized when the parent is sized and painted (and interacted with)
/// at the cell the parent computes for it. See [`AtomKind::Layout`]. /// at the cell the parent computes for it. See [`AtomKind::Widget`].
pub fn layout(layout: AtomLayout<'a>) -> Self { pub fn widget(widget: WidgetAtom<'a>) -> Self {
Atom { Atom {
kind: AtomKind::Layout(Box::new(layout)), kind: AtomKind::Widget(Box::new(widget)),
..Default::default()
}
}
/// Nest a [`ContainerAtom`] (a non-interactive atom-based layout) as a single atom.
///
/// Like [`Self::widget`], the nested layout is sized when the parent is sized and painted at
/// the cell the parent computes for it. Unlike [`Self::widget`], a [`ContainerAtom`] has no
/// id or sense, so it is painted but not interacted with. See [`AtomKind::Container`].
pub fn container(container: ContainerAtom<'a>) -> Self {
Atom {
kind: AtomKind::Container(Box::new(container)),
..Default::default() ..Default::default()
} }
} }

View File

@@ -7,7 +7,7 @@ use emath::Vec2;
pub trait AtomExt<'a> { pub trait AtomExt<'a> {
/// Set the [`Id`] for custom rendering. /// Set the [`Id`] for custom rendering.
/// ///
/// You can get the [`crate::Rect`] with the [`Id`] from [`crate::AtomLayoutResponse`] and use a /// You can get the [`crate::Rect`] with the [`Id`] from [`crate::WidgetAtomResponse`] and use a
/// [`crate::Painter`] or [`Ui::place`] to add/draw some custom content. /// [`crate::Painter`] or [`Ui::place`] to add/draw some custom content.
fn atom_id(self, id: Id) -> Atom<'a>; fn atom_id(self, id: Id) -> Atom<'a>;
@@ -25,7 +25,7 @@ pub trait AtomExt<'a> {
/// Grow this atom to the available space. /// Grow this atom to the available space.
/// ///
/// This will affect the size of the [`Atom`] in the main direction. Since /// This will affect the size of the [`Atom`] in the main direction. Since
/// [`crate::AtomLayout`] today only supports horizontal layout, it will affect the width. /// [`crate::ContainerAtom`] today only supports horizontal layout, it will affect the width.
/// ///
/// You can also combine this with [`Self::atom_shrink`] to make it always take exactly the /// You can also combine this with [`Self::atom_shrink`] to make it always take exactly the
/// remaining space. /// remaining space.
@@ -34,7 +34,7 @@ pub trait AtomExt<'a> {
/// Shrink this atom if there isn't enough space. /// Shrink this atom if there isn't enough space.
/// ///
/// This will affect the size of the [`Atom`] in the main direction. Since /// This will affect the size of the [`Atom`] in the main direction. Since
/// [`crate::AtomLayout`] today only supports horizontal layout, it will affect the width. /// [`crate::ContainerAtom`] today only supports horizontal layout, it will affect the width.
/// ///
/// NOTE: Only a single [`Atom`] may shrink for each widget. /// NOTE: Only a single [`Atom`] may shrink for each widget.
/// ///

View File

@@ -1,4 +1,6 @@
use crate::{AtomLayout, FontSelection, Image, ImageSource, SizedAtomKind, Ui, WidgetText}; use crate::{
ContainerAtom, FontSelection, Image, ImageSource, SizedAtomKind, Ui, WidgetAtom, WidgetText,
};
use emath::Vec2; use emath::Vec2;
use epaint::text::TextWrapMode; use epaint::text::TextWrapMode;
use std::fmt::Debug; use std::fmt::Debug;
@@ -30,7 +32,7 @@ pub enum AtomKind<'a> {
/// Text atom. /// Text atom.
/// ///
/// Truncation within [`crate::AtomLayout`] works like this: /// Truncation within a [`crate::ContainerAtom`] works like this:
/// - /// -
/// - if `wrap_mode` is not Extend /// - if `wrap_mode` is not Extend
/// - if no atom is `shrink` /// - if no atom is `shrink`
@@ -40,7 +42,7 @@ pub enum AtomKind<'a> {
/// - if `wrap_mode` is extend, Text will extend as expected. /// - if `wrap_mode` is extend, Text will extend as expected.
/// ///
/// Unless [`crate::AtomExt::atom_max_width`] is set, `wrap_mode` should only be set via [`crate::Style`] or /// Unless [`crate::AtomExt::atom_max_width`] is set, `wrap_mode` should only be set via [`crate::Style`] or
/// [`crate::AtomLayout::wrap_mode`], as setting a wrap mode on a [`WidgetText`] atom /// [`crate::ContainerAtom::wrap_mode`], as setting a wrap mode on a [`WidgetText`] atom
/// that is not `shrink` will have unexpected results. /// that is not `shrink` will have unexpected results.
/// ///
/// The size is determined by converting the [`WidgetText`] into a galley and using the galleys /// The size is determined by converting the [`WidgetText`] into a galley and using the galleys
@@ -66,12 +68,22 @@ pub enum AtomKind<'a> {
/// When cloning, this will be cloned as [`AtomKind::Empty`]. /// When cloning, this will be cloned as [`AtomKind::Empty`].
Closure(AtomClosure<'a>), Closure(AtomClosure<'a>),
/// A nested [`AtomLayout`], letting you embed an atom-based widget as a single atom /// A nested [`WidgetAtom`], letting you embed an atom-based widget as a single atom
/// inside another [`AtomLayout`]. /// inside another [`WidgetAtom`].
/// ///
/// The nested layout is measured (sized) when the parent is sized, and painted (and /// The nested widget is measured (sized) when the parent is sized, and painted (and
/// interacted with) at the cell rect the parent computes for it. /// interacted with) at the cell rect the parent computes for it.
Layout(Box<AtomLayout<'a>>), ///
/// Use [`Self::Container`] instead if you don't need the nested layout to interact.
Widget(Box<WidgetAtom<'a>>),
/// A nested [`ContainerAtom`], letting you embed a non-interactive atom-based layout as a
/// single atom inside another [`WidgetAtom`].
///
/// Like [`Self::Widget`], the nested layout is measured when the parent is sized and painted
/// at the cell rect the parent computes for it. Unlike [`Self::Widget`], a [`ContainerAtom`]
/// has no [`Id`](crate::Id) or [`Sense`](crate::Sense), so it is never interacted with.
Container(Box<ContainerAtom<'a>>),
} }
impl Clone for AtomKind<'_> { impl Clone for AtomKind<'_> {
@@ -84,7 +96,8 @@ impl Clone for AtomKind<'_> {
log::warn!("Cannot clone atom closures"); log::warn!("Cannot clone atom closures");
AtomKind::Empty AtomKind::Empty
} }
AtomKind::Layout(layout) => AtomKind::Layout(layout.clone()), AtomKind::Widget(layout) => AtomKind::Widget(layout.clone()),
AtomKind::Container(container) => AtomKind::Container(container.clone()),
} }
} }
} }
@@ -96,7 +109,8 @@ impl Debug for AtomKind<'_> {
AtomKind::Text(text) => write!(f, "AtomKind::Text({text:?})"), AtomKind::Text(text) => write!(f, "AtomKind::Text({text:?})"),
AtomKind::Image(image) => write!(f, "AtomKind::Image({image:?})"), AtomKind::Image(image) => write!(f, "AtomKind::Image({image:?})"),
AtomKind::Closure(_) => write!(f, "AtomKind::Closure(<closure>)"), AtomKind::Closure(_) => write!(f, "AtomKind::Closure(<closure>)"),
AtomKind::Layout(_) => write!(f, "AtomKind::Layout(<layout>)"), AtomKind::Widget(_) => write!(f, "AtomKind::Widget(<widget>)"),
AtomKind::Container(_) => write!(f, "AtomKind::Container(<container>)"),
} }
} }
} }
@@ -158,11 +172,18 @@ impl<'a> AtomKind<'a> {
fallback_font, fallback_font,
}, },
), ),
AtomKind::Layout(layout) => { AtomKind::Widget(widget) => {
let sized = layout.measure(ui, available_size); let sized = widget.measure(ui, available_size);
IntoSizedResult { IntoSizedResult {
intrinsic_size: sized.intrinsic_size, intrinsic_size: sized.intrinsic_size,
sized: SizedAtomKind::Layout(Box::new(sized)), sized: SizedAtomKind::Widget(Box::new(sized)),
}
}
AtomKind::Container(container) => {
let sized = container.measure(ui, available_size);
IntoSizedResult {
intrinsic_size: sized.intrinsic_size,
sized: SizedAtomKind::Container(Box::new(sized)),
} }
} }
} }
@@ -190,8 +211,14 @@ where
} }
} }
impl<'a> From<AtomLayout<'a>> for AtomKind<'a> { impl<'a> From<WidgetAtom<'a>> for AtomKind<'a> {
fn from(layout: AtomLayout<'a>) -> Self { fn from(widget: WidgetAtom<'a>) -> Self {
AtomKind::Layout(Box::new(layout)) AtomKind::Widget(Box::new(widget))
}
}
impl<'a> From<ContainerAtom<'a>> for AtomKind<'a> {
fn from(container: ContainerAtom<'a>) -> Self {
AtomKind::Container(Box::new(container))
} }
} }

View File

@@ -1,6 +1,5 @@
use crate::{ use crate::{
AtomKind, Atoms, FontSelection, Frame, Id, Image, IntoAtoms, Response, Sense, SizedAtom, AtomKind, Atoms, FontSelection, Frame, Id, Image, IntoAtoms, SizedAtom, SizedAtomKind, Ui,
SizedAtomKind, Ui, Widget,
}; };
use emath::{Align2, GuiRounding as _, NumExt as _, Rect, Vec2}; use emath::{Align2, GuiRounding as _, NumExt as _, Rect, Vec2};
use epaint::text::TextWrapMode; use epaint::text::TextWrapMode;
@@ -9,32 +8,33 @@ use smallvec::SmallVec;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::sync::Arc; use std::sync::Arc;
/// Intra-widget layout utility. /// The custom [`crate::Atom`] rects collected while painting, keyed by [`crate::Atom::custom`] id.
/// ///
/// Used to lay out and paint [`crate::Atom`]s. /// There should rarely be more than one.
/// This is used internally by widgets like [`crate::Button`] and [`crate::Checkbox`]. pub type CustomRects = SmallVec<[(Id, Rect); 1]>;
/// You can use it to make your own widgets.
/// Describes how a set of [`crate::Atom`]s is laid out and painted.
/// ///
/// Painting the atoms can be split in two phases: /// This is the container part of an atom-based widget: it owns the [`Atoms`], the [`Frame`]
/// - [`AtomLayout::allocate`] /// painted around them, the sizing constraints (`min_size` / `max_size`), the gap between
/// atoms, alignment and text styling. It knows nothing about how the widget is shown inside a
/// [`Ui`] (that is the job of [`crate::WidgetAtom`], which wraps a `ContainerAtom` and adds an
/// [`Id`](crate::Id) and a [`Sense`](crate::Sense)).
///
/// Painting the atoms is split in two phases:
/// - [`ContainerAtom::measure`]
/// - calculates sizes /// - calculates sizes
/// - converts texts to [`Galley`]s /// - converts texts to [`Galley`]s
/// - allocates a [`Response`] /// - returns a [`SizedContainerAtom`]
/// - returns a [`AllocatedAtomLayout`] /// - [`SizedContainerAtom::paint_at`]
/// - [`AllocatedAtomLayout::paint`]
/// - paints the [`Frame`] /// - paints the [`Frame`]
/// - calculates individual [`crate::Atom`] positions /// - calculates individual [`crate::Atom`] positions
/// - paints each single atom /// - paints each single atom
///
/// You can use this to first allocate a response and then modify, e.g., the [`Frame`] on the
/// [`AllocatedAtomLayout`] for interaction styling.
#[derive(Clone)] #[derive(Clone)]
pub struct AtomLayout<'a> { pub struct ContainerAtom<'a> {
id: Option<Id>,
pub atoms: Atoms<'a>, pub atoms: Atoms<'a>,
gap: Option<f32>, gap: Option<f32>,
pub(crate) frame: Frame, pub(crate) frame: Frame,
pub(crate) sense: Sense,
fallback_text_color: Option<Color32>, fallback_text_color: Option<Color32>,
fallback_font: Option<FontSelection>, fallback_font: Option<FontSelection>,
min_size: Vec2, min_size: Vec2,
@@ -43,20 +43,18 @@ pub struct AtomLayout<'a> {
align2: Option<Align2>, align2: Option<Align2>,
} }
impl Default for AtomLayout<'_> { impl Default for ContainerAtom<'_> {
fn default() -> Self { fn default() -> Self {
Self::new(()) Self::new(())
} }
} }
impl<'a> AtomLayout<'a> { impl<'a> ContainerAtom<'a> {
pub fn new(atoms: impl IntoAtoms<'a>) -> Self { pub fn new(atoms: impl IntoAtoms<'a>) -> Self {
Self { Self {
id: None,
atoms: atoms.into_atoms(), atoms: atoms.into_atoms(),
gap: None, gap: None,
frame: Frame::default(), frame: Frame::default(),
sense: Sense::hover(),
fallback_text_color: None, fallback_text_color: None,
fallback_font: None, fallback_font: None,
min_size: Vec2::ZERO, min_size: Vec2::ZERO,
@@ -82,13 +80,6 @@ impl<'a> AtomLayout<'a> {
self self
} }
/// Set the [`Sense`] used when allocating the [`Response`].
#[inline]
pub fn sense(mut self, sense: Sense) -> Self {
self.sense = sense;
self
}
/// Set the fallback (default) text color. /// Set the fallback (default) text color.
/// ///
/// Default: [`crate::Visuals::text_color`] /// Default: [`crate::Visuals::text_color`]
@@ -142,13 +133,6 @@ impl<'a> AtomLayout<'a> {
self self
} }
/// Set the [`Id`] used to allocate a [`Response`].
#[inline]
pub fn id(mut self, id: Id) -> Self {
self.id = Some(id);
self
}
/// Set the [`TextWrapMode`] for the [`crate::Atom`] marked as `shrink`. /// 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 /// Only a single [`crate::Atom`] may shrink. If this (or `ui.wrap_mode()`) is not
@@ -173,29 +157,22 @@ impl<'a> AtomLayout<'a> {
self self
} }
/// [`AtomLayout::allocate`] and [`AllocatedAtomLayout::paint`] in one go.
pub fn show(self, ui: &mut Ui) -> AtomLayoutResponse {
self.allocate(ui).paint(ui)
}
/// Measure the atoms (sizing only), without allocating space or interacting. /// Measure the atoms (sizing only), without allocating space or interacting.
/// ///
/// This converts texts to [`Galley`]s and calculates sizes, but unlike [`Self::allocate`] /// This converts texts to [`Galley`]s and calculates sizes, but it does *not* call
/// it does *not* call [`Ui::allocate_space`] (so the parent cursor is left untouched) nor /// [`Ui::allocate_space`] (so the parent cursor is left untouched) nor [`Ui::interact`].
/// [`Ui::interact`]. Use the returned [`SizedAtomLayout`] to paint at an arbitrary [`Rect`] /// Use the returned [`SizedContainerAtom`] to paint at an arbitrary [`Rect`] via
/// via [`SizedAtomLayout::paint_at`]. This is what makes it possible to nest one /// [`SizedContainerAtom::paint_at`]. This is what makes it possible to nest one atom-based
/// [`AtomLayout`] inside another. /// widget inside another.
/// ///
/// `available_size` is the space available to the whole widget (frame included); it is /// `available_size` is the space available to the whole widget (frame included); it is
/// clamped by `max_size`/`min_size`, exactly like [`Self::allocate`] does with /// clamped by `max_size`/`min_size`, exactly like [`crate::WidgetAtom::allocate`] does with
/// [`Ui::available_size`]. /// [`Ui::available_size`].
pub fn measure(self, ui: &Ui, available_size: Vec2) -> SizedAtomLayout<'a> { pub fn measure(self, ui: &Ui, available_size: Vec2) -> SizedContainerAtom<'a> {
let Self { let Self {
id,
mut atoms, mut atoms,
gap, gap,
frame, frame,
sense,
fallback_text_color, fallback_text_color,
min_size, min_size,
mut max_size, mut max_size,
@@ -222,8 +199,6 @@ impl<'a> AtomLayout<'a> {
} }
} }
let id = id.unwrap_or_else(|| ui.next_auto_id());
let fallback_text_color = let fallback_text_color =
fallback_text_color.unwrap_or_else(|| ui.style().visuals.text_color()); fallback_text_color.unwrap_or_else(|| ui.style().visuals.text_color());
let gap = gap.unwrap_or_else(|| ui.spacing().icon_spacing); let gap = gap.unwrap_or_else(|| ui.spacing().icon_spacing);
@@ -323,12 +298,10 @@ impl<'a> AtomLayout<'a> {
let intrinsic_size = let intrinsic_size =
(Vec2::new(intrinsic_width, intrinsic_height) + margin.sum()).at_least(min_size); (Vec2::new(intrinsic_width, intrinsic_height) + margin.sum()).at_least(min_size);
SizedAtomLayout { SizedContainerAtom {
sized_atoms: sized_items, sized_atoms: sized_items,
frame, frame,
fallback_text_color, fallback_text_color,
id,
sense,
outer_size, outer_size,
intrinsic_size, intrinsic_size,
grow_count, grow_count,
@@ -337,34 +310,16 @@ impl<'a> AtomLayout<'a> {
gap, gap,
} }
} }
/// Calculate sizes, create [`Galley`]s and allocate a [`Response`].
///
/// Use the returned [`AllocatedAtomLayout`] for painting.
pub fn allocate(self, ui: &mut Ui) -> AllocatedAtomLayout<'a> {
let sized = self.measure(ui, ui.available_size());
let (_, rect) = ui.allocate_space(sized.outer_size);
let mut response = ui.interact(rect, sized.id, sized.sense);
response.set_intrinsic_size(sized.intrinsic_size);
AllocatedAtomLayout { sized, response }
}
} }
/// A measured [`AtomLayout`], ready to be painted at a [`Rect`]. /// A measured [`ContainerAtom`], ready to be painted at a [`Rect`].
/// ///
/// Produced by [`AtomLayout::measure`]. Unlike [`AllocatedAtomLayout`], it has not yet /// Produced by [`ContainerAtom::measure`]. It has not yet allocated space or interacted, so it
/// allocated space or interacted, so it can be painted at an arbitrary [`Rect`] via /// can be painted at an arbitrary [`Rect`] via [`Self::paint_at`]. This is what lets one
/// [`Self::paint_at`]. This is what lets one [`AtomLayout`] be nested inside another. /// atom-based widget be nested inside another. To allocate space and interact, wrap it in a
/// [`crate::SizedWidgetAtom`] (or measure a [`crate::WidgetAtom`] directly).
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SizedAtomLayout<'a> { pub struct SizedContainerAtom<'a> {
/// The [`Id`] used to [`Ui::interact`] when this layout is allocated / painted.
id: Id,
/// The [`Sense`] used to [`Ui::interact`] when this layout is allocated / painted.
sense: Sense,
/// The total widget size we'll request, including the frame margin. Used to allocate space. /// The total widget size we'll request, including the frame margin. Used to allocate space.
/// ///
/// Actual allocated size may be different. /// Actual allocated size may be different.
@@ -396,19 +351,7 @@ pub struct SizedAtomLayout<'a> {
gap: f32, gap: f32,
} }
/// Instructions for painting an [`AtomLayout`]. impl<'atom> SizedContainerAtom<'atom> {
///
/// This is a [`SizedAtomLayout`] that has additionally allocated space and interacted,
/// producing a [`Response`].
#[derive(Clone, Debug)]
pub struct AllocatedAtomLayout<'a> {
/// The measured layout.
pub sized: SizedAtomLayout<'a>,
pub response: Response,
}
impl<'atom> SizedAtomLayout<'atom> {
pub fn iter_kinds(&self) -> impl Iterator<Item = &SizedAtomKind<'atom>> { pub fn iter_kinds(&self) -> impl Iterator<Item = &SizedAtomKind<'atom>> {
self.sized_atoms.iter().map(|atom| &atom.kind) self.sized_atoms.iter().map(|atom| &atom.kind)
} }
@@ -485,9 +428,11 @@ impl<'atom> SizedAtomLayout<'atom> {
/// Paint the [`Frame`] and individual [`crate::Atom`]s within `rect`. /// Paint the [`Frame`] and individual [`crate::Atom`]s within `rect`.
/// ///
/// `rect` is the full widget rect (frame included). For a top-level layout this is /// `rect` is the full widget rect (frame included). For a top-level layout this is
/// `response.rect`; when nested, the parent passes the cell rect it computed. `response` /// `response.rect`; when nested, the parent passes the cell rect it computed.
/// becomes the base of the returned [`AtomLayoutResponse`]. ///
pub fn paint_at(self, ui: &Ui, rect: Rect, response: Response) -> AtomLayoutResponse { /// Returns the [`CustomRects`] collected from [`crate::Atom::custom`] atoms, so the caller
/// can build an [`crate::WidgetAtomResponse`].
pub fn paint_at(self, ui: &Ui, rect: Rect) -> CustomRects {
let Self { let Self {
sized_atoms, sized_atoms,
frame, frame,
@@ -515,7 +460,7 @@ impl<'atom> SizedAtomLayout<'atom> {
let mut cursor = aligned_rect.left(); let mut cursor = aligned_rect.left();
let mut response = AtomLayoutResponse::empty(response); let mut custom_rects = CustomRects::new();
for sized in sized_atoms { for sized in sized_atoms {
let size = sized.size; let size = sized.size;
@@ -531,10 +476,10 @@ impl<'atom> SizedAtomLayout<'atom> {
if let Some(id) = sized.id { if let Some(id) = sized.id {
debug_assert!( debug_assert!(
!response.custom_rects.iter().any(|(i, _)| *i == id), !custom_rects.iter().any(|(i, _)| *i == id),
"Duplicate custom id" "Duplicate custom id"
); );
response.custom_rects.push((id, rect)); custom_rects.push((id, rect));
} }
match sized.kind { match sized.kind {
@@ -545,79 +490,22 @@ impl<'atom> SizedAtomLayout<'atom> {
image.paint_at(ui, rect); image.paint_at(ui, rect);
} }
SizedAtomKind::Empty { .. } => {} SizedAtomKind::Empty { .. } => {}
SizedAtomKind::Layout(layout) => { SizedAtomKind::Widget(widget) => {
// TODO(lucasmerlin): Add some kind of justify flag to AtomLayout // TODO(lucasmerlin): Add some kind of justify flag to the layout
let layout_response = ui.interact(frame, layout.id, layout.sense); widget.paint_at(ui, frame);
layout.paint_at(ui, frame, layout_response); }
SizedAtomKind::Container(container) => {
// A nested container has no id/sense, so it is painted but not interacted with.
container.paint_at(ui, frame);
} }
} }
} }
response custom_rects
} }
} }
impl AllocatedAtomLayout<'_> { impl<'a> Deref for ContainerAtom<'a> {
/// Paint the [`Frame`] and individual [`crate::Atom`]s at the allocated [`Response`]'s rect.
pub fn paint(self, ui: &Ui) -> AtomLayoutResponse {
let rect = self.response.rect;
self.sized.paint_at(ui, rect, self.response)
}
}
/// Response from a [`AtomLayout::show`] or [`AllocatedAtomLayout::paint`].
///
/// Use [`AtomLayoutResponse::rect`] to get the response rects from [`crate::Atom::custom`].
#[derive(Clone, Debug)]
pub struct AtomLayoutResponse {
pub response: Response,
// There should rarely be more than one custom rect.
custom_rects: SmallVec<[(Id, Rect); 1]>,
}
impl AtomLayoutResponse {
pub fn empty(response: Response) -> Self {
Self {
response,
custom_rects: Default::default(),
}
}
pub fn custom_rects(&self) -> impl Iterator<Item = (Id, Rect)> + '_ {
self.custom_rects.iter().copied()
}
/// Use this together with [`crate::Atom::custom`] to add custom painting / child widgets.
///
/// NOTE: Don't `unwrap` rects, they might be empty when the widget is not visible.
pub fn rect(&self, id: Id) -> Option<Rect> {
self.custom_rects
.iter()
.find_map(|(i, r)| if *i == id { Some(*r) } else { None })
}
}
impl Deref for AtomLayoutResponse {
type Target = Response;
fn deref(&self) -> &Self::Target {
&self.response
}
}
impl DerefMut for AtomLayoutResponse {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.response
}
}
impl Widget for AtomLayout<'_> {
fn ui(self, ui: &mut Ui) -> Response {
self.show(ui).response
}
}
impl<'a> Deref for AtomLayout<'a> {
type Target = Atoms<'a>; type Target = Atoms<'a>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@@ -625,13 +513,13 @@ impl<'a> Deref for AtomLayout<'a> {
} }
} }
impl DerefMut for AtomLayout<'_> { impl DerefMut for ContainerAtom<'_> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.atoms &mut self.atoms
} }
} }
impl<'a> Deref for SizedAtomLayout<'a> { impl<'a> Deref for SizedContainerAtom<'a> {
type Target = [SizedAtom<'a>]; type Target = [SizedAtom<'a>];
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@@ -639,22 +527,8 @@ impl<'a> Deref for SizedAtomLayout<'a> {
} }
} }
impl DerefMut for SizedAtomLayout<'_> { impl DerefMut for SizedContainerAtom<'_> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.sized_atoms &mut self.sized_atoms
} }
} }
impl<'a> Deref for AllocatedAtomLayout<'a> {
type Target = SizedAtomLayout<'a>;
fn deref(&self) -> &Self::Target {
&self.sized
}
}
impl DerefMut for AllocatedAtomLayout<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.sized
}
}

View File

@@ -1,15 +1,17 @@
mod atom; mod atom;
mod atom_ext; mod atom_ext;
mod atom_kind; mod atom_kind;
mod atom_layout;
mod atoms; mod atoms;
mod container_atom;
mod sized_atom; mod sized_atom;
mod sized_atom_kind; mod sized_atom_kind;
mod widget_atom;
pub use atom::*; pub use atom::*;
pub use atom_ext::*; pub use atom_ext::*;
pub use atom_kind::*; pub use atom_kind::*;
pub use atom_layout::*;
pub use atoms::*; pub use atoms::*;
pub use container_atom::*;
pub use sized_atom::*; pub use sized_atom::*;
pub use sized_atom_kind::*; pub use sized_atom_kind::*;
pub use widget_atom::*;

View File

@@ -10,7 +10,7 @@ pub struct SizedAtom<'a> {
/// The size of the atom. /// The size of the atom.
/// ///
/// Used for placing this atom in [`crate::AtomLayout`], the cursor will advance by /// Used for placing this atom in a [`crate::ContainerAtom`], the cursor will advance by
/// size.x + gap. /// size.x + gap.
pub size: Vec2, pub size: Vec2,

View File

@@ -1,4 +1,4 @@
use crate::{Image, SizedAtomLayout}; use crate::{Image, SizedContainerAtom, SizedWidgetAtom};
use emath::Vec2; use emath::Vec2;
use epaint::Galley; use epaint::Galley;
use std::sync::Arc; use std::sync::Arc;
@@ -9,7 +9,8 @@ pub enum SizedAtomKind<'a> {
Empty { size: Option<Vec2> }, Empty { size: Option<Vec2> },
Text(Arc<Galley>), Text(Arc<Galley>),
Image { image: Image<'a>, size: Vec2 }, Image { image: Image<'a>, size: Vec2 },
Layout(Box<SizedAtomLayout<'a>>), Widget(Box<SizedWidgetAtom<'a>>),
Container(Box<SizedContainerAtom<'a>>),
} }
impl Default for SizedAtomKind<'_> { impl Default for SizedAtomKind<'_> {
@@ -25,7 +26,8 @@ impl SizedAtomKind<'_> {
SizedAtomKind::Text(galley) => galley.size(), SizedAtomKind::Text(galley) => galley.size(),
SizedAtomKind::Image { image: _, size } => *size, SizedAtomKind::Image { image: _, size } => *size,
SizedAtomKind::Empty { size } => size.unwrap_or_default(), SizedAtomKind::Empty { size } => size.unwrap_or_default(),
SizedAtomKind::Layout(layout) => layout.outer_size, SizedAtomKind::Widget(widget) => widget.outer_size,
SizedAtomKind::Container(container) => container.outer_size,
} }
} }
} }

View File

@@ -0,0 +1,375 @@
use crate::{
Align2, Color32, ContainerAtom, FontSelection, Frame, Id, IntoAtoms, Response, Sense,
SizedContainerAtom, Ui, Widget,
};
use emath::{Rect, Vec2};
use epaint::text::TextWrapMode;
use smallvec::SmallVec;
use std::ops::{Deref, DerefMut};
/// An atom-based widget: a [`ContainerAtom`] plus everything needed to show it inside a [`Ui`].
///
/// The [`ContainerAtom`] defines how the [`crate::Atom`]s are laid out and painted (frame, sizes,
/// gap, alignment). The `WidgetAtom` wraps it and adds the [`Id`] and [`Sense`] used to allocate
/// a [`Response`] and interact. This is used internally by widgets like [`crate::Button`] and
/// [`crate::Checkbox`], and you can use it to make your own widgets.
///
/// Painting can be split in two phases:
/// - [`WidgetAtom::allocate`]
/// - measures the [`ContainerAtom`] (see [`ContainerAtom::measure`])
/// - allocates a [`Response`]
/// - returns an [`AllocatedWidgetAtom`]
/// - [`AllocatedWidgetAtom::paint`]
/// - paints the [`Frame`] and each single atom
///
/// You can use this to first allocate a response and then modify, e.g., the [`Frame`] on the
/// [`AllocatedWidgetAtom`] for interaction styling.
#[derive(Clone)]
pub struct WidgetAtom<'a> {
id: Option<Id>,
pub(crate) sense: Sense,
pub container: ContainerAtom<'a>,
}
impl Default for WidgetAtom<'_> {
fn default() -> Self {
Self::new(())
}
}
impl<'a> WidgetAtom<'a> {
pub fn new(atoms: impl IntoAtoms<'a>) -> Self {
Self {
id: None,
sense: Sense::hover(),
container: ContainerAtom::new(atoms),
}
}
/// Set the [`Id`] used to allocate a [`Response`].
#[inline]
pub fn id(mut self, id: Id) -> Self {
self.id = Some(id);
self
}
/// Set the [`Sense`] used when allocating the [`Response`].
#[inline]
pub fn sense(mut self, sense: Sense) -> Self {
self.sense = sense;
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
}
/// [`WidgetAtom::allocate`] and [`AllocatedWidgetAtom::paint`] in one go.
pub fn show(self, ui: &mut Ui) -> WidgetAtomResponse {
self.allocate(ui).paint(ui)
}
/// Measure the atoms (sizing only), without allocating space or interacting.
///
/// This resolves the [`Id`] and measures the [`ContainerAtom`] (see
/// [`ContainerAtom::measure`]), but unlike [`Self::allocate`] it does *not* call
/// [`Ui::allocate_space`] nor [`Ui::interact`]. Use the returned [`SizedWidgetAtom`] to
/// allocate later via [`SizedWidgetAtom::allocate`], or to paint at an arbitrary [`Rect`] via
/// [`SizedWidgetAtom::paint_at`].
///
/// `available_size` is the space available to the whole widget (frame included); it is
/// clamped by `max_size`/`min_size`, exactly like [`Self::allocate`] does with
/// [`Ui::available_size`].
pub fn measure(self, ui: &Ui, available_size: Vec2) -> SizedWidgetAtom<'a> {
let Self {
id,
sense,
container,
} = self;
let id = id.unwrap_or_else(|| ui.next_auto_id());
let container = container.measure(ui, available_size);
SizedWidgetAtom {
id,
sense,
container,
}
}
/// Calculate sizes, create [`crate::Galley`]s and allocate a [`Response`].
///
/// Use the returned [`AllocatedWidgetAtom`] for painting.
pub fn allocate(self, ui: &mut Ui) -> AllocatedWidgetAtom<'a> {
self.measure(ui, ui.available_size()).allocate(ui)
}
}
/// A measured [`WidgetAtom`]: a [`SizedContainerAtom`] plus the [`Id`] and [`Sense`] needed to
/// allocate a [`Response`].
///
/// Produced by [`WidgetAtom::measure`]. Unlike [`AllocatedWidgetAtom`], it has not yet allocated
/// space or interacted. Call [`Self::allocate`] to do so, or [`Self::paint_at`] to interact and
/// paint at an arbitrary [`Rect`] (used when nesting one atom-based widget inside another).
#[derive(Clone, Debug)]
pub struct SizedWidgetAtom<'a> {
/// The [`Id`] used to [`Ui::interact`] when this widget is allocated / painted.
id: Id,
/// The [`Sense`] used to [`Ui::interact`] when this widget is allocated / painted.
sense: Sense,
/// The measured container.
pub container: SizedContainerAtom<'a>,
}
impl<'a> SizedWidgetAtom<'a> {
/// Allocate space and interact, producing an [`AllocatedWidgetAtom`] ready for painting.
pub fn allocate(self, ui: &mut Ui) -> AllocatedWidgetAtom<'a> {
let (_, rect) = ui.allocate_space(self.container.outer_size);
let mut response = ui.interact(rect, self.id, self.sense);
response.set_intrinsic_size(self.container.intrinsic_size);
AllocatedWidgetAtom {
container: self.container,
response,
}
}
/// Interact at `rect` and paint the [`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
/// atom-based widget inside another.
pub fn paint_at(self, ui: &Ui, rect: Rect) -> WidgetAtomResponse {
let response = ui.interact(rect, self.id, self.sense);
let custom_rects = self.container.paint_at(ui, rect);
WidgetAtomResponse {
response,
custom_rects,
}
}
}
/// Instructions for painting a [`WidgetAtom`].
///
/// This is a [`SizedContainerAtom`] that has additionally allocated space and interacted,
/// producing a [`Response`].
#[derive(Clone, Debug)]
pub struct AllocatedWidgetAtom<'a> {
/// The measured container.
pub container: SizedContainerAtom<'a>,
pub response: Response,
}
impl AllocatedWidgetAtom<'_> {
/// Paint the [`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);
WidgetAtomResponse {
response: self.response,
custom_rects,
}
}
}
/// Response from a [`WidgetAtom::show`] or [`AllocatedWidgetAtom::paint`].
///
/// Use [`WidgetAtomResponse::rect`] to get the response rects from [`crate::Atom::custom`].
#[derive(Clone, Debug)]
pub struct WidgetAtomResponse {
pub response: Response,
// There should rarely be more than one custom rect.
pub(crate) custom_rects: SmallVec<[(Id, Rect); 1]>,
}
impl WidgetAtomResponse {
pub fn empty(response: Response) -> Self {
Self {
response,
custom_rects: Default::default(),
}
}
pub fn custom_rects(&self) -> impl Iterator<Item = (Id, Rect)> + '_ {
self.custom_rects.iter().copied()
}
/// Use this together with [`crate::Atom::custom`] to add custom painting / child widgets.
///
/// NOTE: Don't `unwrap` rects, they might be empty when the widget is not visible.
pub fn rect(&self, id: Id) -> Option<Rect> {
self.custom_rects
.iter()
.find_map(|(i, r)| if *i == id { Some(*r) } else { None })
}
}
impl Deref for WidgetAtomResponse {
type Target = Response;
fn deref(&self) -> &Self::Target {
&self.response
}
}
impl DerefMut for WidgetAtomResponse {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.response
}
}
impl Widget for WidgetAtom<'_> {
fn ui(self, ui: &mut Ui) -> Response {
self.show(ui).response
}
}
impl<'a> Deref for WidgetAtom<'a> {
type Target = ContainerAtom<'a>;
fn deref(&self) -> &Self::Target {
&self.container
}
}
impl DerefMut for WidgetAtom<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.container
}
}
impl<'a> Deref for SizedWidgetAtom<'a> {
type Target = SizedContainerAtom<'a>;
fn deref(&self) -> &Self::Target {
&self.container
}
}
impl DerefMut for SizedWidgetAtom<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.container
}
}
impl<'a> Deref for AllocatedWidgetAtom<'a> {
type Target = SizedContainerAtom<'a>;
fn deref(&self) -> &Self::Target {
&self.container
}
}
impl DerefMut for AllocatedWidgetAtom<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.container
}
}
/// `AtomLayout` was split into [`WidgetAtom`] (id, sense, allocation) and [`ContainerAtom`]
/// (layout & painting). [`WidgetAtom`] is the direct replacement.
#[deprecated = "Renamed to `WidgetAtom`"]
pub type AtomLayout<'a> = WidgetAtom<'a>;
/// `SizedAtomLayout` was split into [`SizedWidgetAtom`] (id, sense) and [`SizedContainerAtom`]
/// (the measured contents). [`SizedWidgetAtom`] is the direct replacement.
#[deprecated = "Renamed to `SizedWidgetAtom`"]
pub type SizedAtomLayout<'a> = SizedWidgetAtom<'a>;
/// Renamed to [`AllocatedWidgetAtom`].
#[deprecated = "Renamed to `AllocatedWidgetAtom`"]
pub type AllocatedAtomLayout<'a> = AllocatedWidgetAtom<'a>;
/// Renamed to [`WidgetAtomResponse`].
#[deprecated = "Renamed to `WidgetAtomResponse`"]
pub type AtomLayoutResponse = WidgetAtomResponse;

View File

@@ -1333,7 +1333,7 @@ fn title_ui(
let mut child_ui = ui.new_child(UiBuilder::new()); let mut child_ui = ui.new_child(UiBuilder::new());
let mut layout = AtomLayout::new(atoms) let mut layout = WidgetAtom::new(atoms)
.gap(spacing) .gap(spacing)
.fallback_font(TextStyle::Heading) .fallback_font(TextStyle::Heading)
.wrap_mode(TextWrapMode::Truncate) .wrap_mode(TextWrapMode::Truncate)

View File

@@ -1,9 +1,9 @@
use epaint::Margin; use epaint::Margin;
use crate::{ use crate::{
Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Color32, CornerRadius, Frame, Atom, AtomExt as _, AtomKind, Color32, CornerRadius, Frame, Image, IntoAtoms, NumExt as _,
Image, IntoAtoms, NumExt as _, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2, Widget, WidgetAtom,
Widget, WidgetInfo, WidgetText, WidgetType, WidgetAtomResponse, WidgetInfo, WidgetText, WidgetType,
widget_style::{ButtonStyle, Classes, HasClasses, SELECTED_CLASS, WidgetState}, widget_style::{ButtonStyle, Classes, HasClasses, SELECTED_CLASS, WidgetState},
}; };
@@ -27,7 +27,7 @@ use crate::{
/// ``` /// ```
#[must_use = "You should put this widget in a ui with `ui.add(widget);`"] #[must_use = "You should put this widget in a ui with `ui.add(widget);`"]
pub struct Button<'a> { pub struct Button<'a> {
layout: AtomLayout<'a>, layout: WidgetAtom<'a>,
fill: Option<Color32>, fill: Option<Color32>,
stroke: Option<Stroke>, stroke: Option<Stroke>,
small: bool, small: bool,
@@ -44,7 +44,7 @@ pub struct Button<'a> {
impl<'a> Button<'a> { impl<'a> Button<'a> {
pub fn new(atoms: impl IntoAtoms<'a>) -> Self { pub fn new(atoms: impl IntoAtoms<'a>) -> Self {
Self { Self {
layout: AtomLayout::new(atoms.into_atoms()) layout: WidgetAtom::new(atoms.into_atoms())
.sense(Sense::click()) .sense(Sense::click())
.fallback_font(TextStyle::Button), .fallback_font(TextStyle::Button),
fill: None, fill: None,
@@ -274,8 +274,8 @@ impl<'a> Button<'a> {
self self
} }
/// Show the button and return a [`AtomLayoutResponse`] for painting custom contents. /// Show the button and return a [`WidgetAtomResponse`] for painting custom contents.
pub fn atom_ui(self, ui: &mut Ui) -> AtomLayoutResponse { pub fn atom_ui(self, ui: &mut Ui) -> WidgetAtomResponse {
let Button { let Button {
mut layout, mut layout,
fill, fill,
@@ -357,7 +357,7 @@ impl<'a> Button<'a> {
let mut prepared = layout.min_size(min_size).allocate(ui); let mut prepared = layout.min_size(min_size).allocate(ui);
// Get AtomLayoutResponse, empty if not visible // Get WidgetAtomResponse, empty if not visible
let response = if ui.is_rect_visible(prepared.response.rect) { let response = if ui.is_rect_visible(prepared.response.rect) {
if image_tint_follows_text_color { if image_tint_follows_text_color {
prepared.map_images(|image| image.tint(text_style.color)); prepared.map_images(|image| image.tint(text_style.color));
@@ -367,7 +367,7 @@ impl<'a> Button<'a> {
prepared.paint(ui) prepared.paint(ui)
} else { } else {
AtomLayoutResponse::empty(prepared.response) WidgetAtomResponse::empty(prepared.response)
}; };
if let Some(cursor) = ui.visuals().interact_cursor if let Some(cursor) = ui.visuals().interact_cursor

View File

@@ -1,7 +1,7 @@
use emath::Rect; use emath::Rect;
use crate::{ use crate::{
Atom, AtomLayout, Atoms, Id, IntoAtoms, NumExt as _, Response, Sense, Shape, Ui, Vec2, Widget, Atom, Atoms, Id, IntoAtoms, NumExt as _, Response, Sense, Shape, Ui, Vec2, Widget, WidgetAtom,
WidgetInfo, WidgetType, epaint, pos2, WidgetInfo, WidgetType, epaint, pos2,
widget_style::{CheckboxStyle, Classes, HasClasses}, widget_style::{CheckboxStyle, Classes, HasClasses},
}; };
@@ -86,7 +86,7 @@ impl Widget for Checkbox<'_> {
let text = atoms.text().map(String::from); let text = atoms.text().map(String::from);
let mut prepared = AtomLayout::new(atoms) let mut prepared = WidgetAtom::new(atoms)
.sense(Sense::click()) .sense(Sense::click())
.min_size(min_size) .min_size(min_size)
.frame(frame) .frame(frame)

View File

@@ -1,5 +1,5 @@
use crate::{ use crate::{
Atom, AtomLayout, Atoms, Id, IntoAtoms, NumExt as _, Response, Sense, Ui, Vec2, Widget, Atom, Atoms, Id, IntoAtoms, NumExt as _, Response, Sense, Ui, Vec2, Widget, WidgetAtom,
WidgetInfo, WidgetType, epaint, WidgetInfo, WidgetType, epaint,
}; };
@@ -55,7 +55,7 @@ impl Widget for RadioButton<'_> {
let text = atoms.text().map(String::from); let text = atoms.text().map(String::from);
let mut prepared = AtomLayout::new(atoms) let mut prepared = WidgetAtom::new(atoms)
.sense(Sense::click()) .sense(Sense::click())
.min_size(min_size) .min_size(min_size)
.allocate(ui); .allocate(ui);

View File

@@ -4,10 +4,10 @@ use emath::{Rect, TSTransform};
use epaint::text::{Galley, LayoutJob, TextWrapMode, cursor::CCursor}; use epaint::text::{Galley, LayoutJob, TextWrapMode, cursor::CCursor};
use crate::{ use crate::{
Align, Align2, AsIdSalt, AtomExt as _, AtomKind, AtomLayout, Atoms, Color32, Context, Align, Align2, AsIdSalt, AtomExt as _, AtomKind, Atoms, Color32, Context, CursorIcon, Event,
CursorIcon, Event, EventFilter, FontSelection, Frame, Id, IdSalt, ImeEvent, IntoAtoms, EventFilter, FontSelection, Frame, Id, IdSalt, ImeEvent, IntoAtoms, IntoSizedResult, Key,
IntoSizedResult, Key, KeyboardShortcut, Margin, Modifiers, NumExt as _, Response, Sense, KeyboardShortcut, Margin, Modifiers, NumExt as _, Response, Sense, SizedAtomKind, TextBuffer,
SizedAtomKind, TextBuffer, TextStyle, Ui, Vec2, Widget, WidgetInfo, WidgetWithState, epaint, TextStyle, Ui, Vec2, Widget, WidgetAtom, WidgetInfo, WidgetWithState, epaint,
os::OperatingSystem, os::OperatingSystem,
output::OutputEvent, output::OutputEvent,
response, response,
@@ -679,7 +679,7 @@ impl TextEdit<'_> {
TextWrapMode::Truncate TextWrapMode::Truncate
}; };
let mut allocated = AtomLayout::new(atoms) let mut allocated = WidgetAtom::new(atoms)
.id(id) .id(id)
.min_size(Vec2::new(allocate_width, min_height)) .min_size(Vec2::new(allocate_width, min_height))
.max_width(allocate_width) .max_width(allocate_width)

View File

@@ -5,7 +5,7 @@ use crate::text::CCursorRange;
/// The output from a [`TextEdit`](crate::TextEdit). /// The output from a [`TextEdit`](crate::TextEdit).
pub struct TextEditOutput { pub struct TextEditOutput {
/// The interaction response. /// The interaction response.
pub response: crate::AtomLayoutResponse, pub response: crate::WidgetAtomResponse,
/// How the text was displayed. /// How the text was displayed.
pub galley: Arc<crate::Galley>, pub galley: Arc<crate::Galley>,

View File

@@ -124,11 +124,11 @@ fn test_button_shortcut_text() {
/// All of these should look the same. /// All of these should look the same.
#[test] #[test]
fn test_atom_letter_spacing() { fn test_atom_letter_spacing() {
use egui::AtomLayout; use egui::WidgetAtom;
let mut harness = HarnessBuilder::default().build_ui(|ui| { let mut harness = HarnessBuilder::default().build_ui(|ui| {
ui.add(AtomLayout::new("1.00x").gap(0.0)); ui.add(WidgetAtom::new("1.00x").gap(0.0));
ui.add(AtomLayout::new(("1.00", "x")).gap(0.0)); ui.add(WidgetAtom::new(("1.00", "x")).gap(0.0));
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 0.0; ui.spacing_mut().item_spacing.x = 0.0;
ui.label("1.00"); ui.label("1.00");

View File

@@ -3,10 +3,10 @@
use egui::accesskit::Role; use egui::accesskit::Role;
use egui::load::SizedTexture; use egui::load::SizedTexture;
use egui::{ use egui::{
Align, AtomExt as _, AtomLayout, Button, Color32, ColorImage, Direction, DragValue, Event, Align, AtomExt as _, Button, Color32, ColorImage, Direction, DragValue, Event, Grid,
Grid, IntoAtoms as _, Layout, PointerButton, Response, RichText, Slider, Stroke, StrokeKind, IntoAtoms as _, Layout, PointerButton, Response, RichText, Slider, Stroke, StrokeKind,
TextEdit, TextWrapMode, TextureHandle, TextureOptions, Ui, UiBuilder, Vec2, Widget as _, TextEdit, TextWrapMode, TextureHandle, TextureOptions, Ui, UiBuilder, Vec2, Widget as _,
include_image, WidgetAtom, include_image,
}; };
use egui_kittest::kittest::{Queryable as _, by}; use egui_kittest::kittest::{Queryable as _, by};
use egui_kittest::{Harness, Node, SnapshotResult, SnapshotResults}; use egui_kittest::{Harness, Node, SnapshotResult, SnapshotResults};
@@ -159,7 +159,7 @@ fn widget_tests() {
for atoms in interesting_atoms { for atoms in interesting_atoms {
results.add(test_widget_layout(&format!("atoms_{}", atoms.0), |ui| { results.add(test_widget_layout(&format!("atoms_{}", atoms.0), |ui| {
AtomLayout::new(atoms.1.clone()).ui(ui) WidgetAtom::new(atoms.1.clone()).ui(ui)
})); }));
} }
} }