mirror of
https://github.com/emilk/egui.git
synced 2026-06-26 22:53:14 -04:00
Split AtomLayout into WidgetAtom and ContainerAtom
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
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 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)
|
||||
/// at the cell the parent computes for it. See [`AtomKind::Layout`].
|
||||
pub fn layout(layout: AtomLayout<'a>) -> Self {
|
||||
/// 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::Widget`].
|
||||
pub fn widget(widget: WidgetAtom<'a>) -> Self {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use emath::Vec2;
|
||||
pub trait AtomExt<'a> {
|
||||
/// 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.
|
||||
fn atom_id(self, id: Id) -> Atom<'a>;
|
||||
|
||||
@@ -25,7 +25,7 @@ pub trait AtomExt<'a> {
|
||||
/// Grow this atom to the available space.
|
||||
///
|
||||
/// 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
|
||||
/// remaining space.
|
||||
@@ -34,7 +34,7 @@ pub trait AtomExt<'a> {
|
||||
/// Shrink this atom if there isn't enough space.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
|
||||
@@ -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 epaint::text::TextWrapMode;
|
||||
use std::fmt::Debug;
|
||||
@@ -30,7 +32,7 @@ pub enum AtomKind<'a> {
|
||||
|
||||
/// Text atom.
|
||||
///
|
||||
/// Truncation within [`crate::AtomLayout`] works like this:
|
||||
/// Truncation within a [`crate::ContainerAtom`] works like this:
|
||||
/// -
|
||||
/// - if `wrap_mode` is not Extend
|
||||
/// - if no atom is `shrink`
|
||||
@@ -40,7 +42,7 @@ pub enum AtomKind<'a> {
|
||||
/// - 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
|
||||
/// [`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.
|
||||
///
|
||||
/// 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`].
|
||||
Closure(AtomClosure<'a>),
|
||||
|
||||
/// A nested [`AtomLayout`], letting you embed an atom-based widget as a single atom
|
||||
/// inside another [`AtomLayout`].
|
||||
/// A nested [`WidgetAtom`], letting you embed an atom-based widget as a single atom
|
||||
/// 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.
|
||||
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<'_> {
|
||||
@@ -84,7 +96,8 @@ impl Clone for AtomKind<'_> {
|
||||
log::warn!("Cannot clone atom closures");
|
||||
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::Image(image) => write!(f, "AtomKind::Image({image:?})"),
|
||||
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,
|
||||
},
|
||||
),
|
||||
AtomKind::Layout(layout) => {
|
||||
let sized = layout.measure(ui, available_size);
|
||||
AtomKind::Widget(widget) => {
|
||||
let sized = widget.measure(ui, available_size);
|
||||
IntoSizedResult {
|
||||
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> {
|
||||
fn from(layout: AtomLayout<'a>) -> Self {
|
||||
AtomKind::Layout(Box::new(layout))
|
||||
impl<'a> From<WidgetAtom<'a>> for AtomKind<'a> {
|
||||
fn from(widget: WidgetAtom<'a>) -> Self {
|
||||
AtomKind::Widget(Box::new(widget))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<ContainerAtom<'a>> for AtomKind<'a> {
|
||||
fn from(container: ContainerAtom<'a>) -> Self {
|
||||
AtomKind::Container(Box::new(container))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{
|
||||
AtomKind, Atoms, FontSelection, Frame, Id, Image, IntoAtoms, Response, Sense, SizedAtom,
|
||||
SizedAtomKind, Ui, Widget,
|
||||
AtomKind, Atoms, FontSelection, Frame, Id, Image, IntoAtoms, SizedAtom, SizedAtomKind, Ui,
|
||||
};
|
||||
use emath::{Align2, GuiRounding as _, NumExt as _, Rect, Vec2};
|
||||
use epaint::text::TextWrapMode;
|
||||
@@ -9,32 +8,33 @@ use smallvec::SmallVec;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
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.
|
||||
/// This is used internally by widgets like [`crate::Button`] and [`crate::Checkbox`].
|
||||
/// You can use it to make your own widgets.
|
||||
/// There should rarely be more than one.
|
||||
pub type CustomRects = SmallVec<[(Id, Rect); 1]>;
|
||||
|
||||
/// Describes how a set of [`crate::Atom`]s is laid out and painted.
|
||||
///
|
||||
/// Painting the atoms can be split in two phases:
|
||||
/// - [`AtomLayout::allocate`]
|
||||
/// This is the container part of an atom-based widget: it owns the [`Atoms`], the [`Frame`]
|
||||
/// 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
|
||||
/// - converts texts to [`Galley`]s
|
||||
/// - allocates a [`Response`]
|
||||
/// - returns a [`AllocatedAtomLayout`]
|
||||
/// - [`AllocatedAtomLayout::paint`]
|
||||
/// - returns a [`SizedContainerAtom`]
|
||||
/// - [`SizedContainerAtom::paint_at`]
|
||||
/// - paints the [`Frame`]
|
||||
/// - calculates individual [`crate::Atom`] positions
|
||||
/// - 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)]
|
||||
pub struct AtomLayout<'a> {
|
||||
id: Option<Id>,
|
||||
pub struct ContainerAtom<'a> {
|
||||
pub atoms: Atoms<'a>,
|
||||
gap: Option<f32>,
|
||||
pub(crate) frame: Frame,
|
||||
pub(crate) sense: Sense,
|
||||
fallback_text_color: Option<Color32>,
|
||||
fallback_font: Option<FontSelection>,
|
||||
min_size: Vec2,
|
||||
@@ -43,20 +43,18 @@ pub struct AtomLayout<'a> {
|
||||
align2: Option<Align2>,
|
||||
}
|
||||
|
||||
impl Default for AtomLayout<'_> {
|
||||
impl Default for ContainerAtom<'_> {
|
||||
fn default() -> Self {
|
||||
Self::new(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AtomLayout<'a> {
|
||||
impl<'a> ContainerAtom<'a> {
|
||||
pub fn new(atoms: impl IntoAtoms<'a>) -> Self {
|
||||
Self {
|
||||
id: None,
|
||||
atoms: atoms.into_atoms(),
|
||||
gap: None,
|
||||
frame: Frame::default(),
|
||||
sense: Sense::hover(),
|
||||
fallback_text_color: None,
|
||||
fallback_font: None,
|
||||
min_size: Vec2::ZERO,
|
||||
@@ -82,13 +80,6 @@ impl<'a> AtomLayout<'a> {
|
||||
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.
|
||||
///
|
||||
/// Default: [`crate::Visuals::text_color`]
|
||||
@@ -142,13 +133,6 @@ impl<'a> AtomLayout<'a> {
|
||||
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`.
|
||||
///
|
||||
/// Only a single [`crate::Atom`] may shrink. If this (or `ui.wrap_mode()`) is not
|
||||
@@ -173,29 +157,22 @@ impl<'a> AtomLayout<'a> {
|
||||
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.
|
||||
///
|
||||
/// This converts texts to [`Galley`]s and calculates sizes, but unlike [`Self::allocate`]
|
||||
/// it does *not* call [`Ui::allocate_space`] (so the parent cursor is left untouched) nor
|
||||
/// [`Ui::interact`]. Use the returned [`SizedAtomLayout`] to paint at an arbitrary [`Rect`]
|
||||
/// via [`SizedAtomLayout::paint_at`]. This is what makes it possible to nest one
|
||||
/// [`AtomLayout`] inside another.
|
||||
/// This converts texts to [`Galley`]s and calculates sizes, but it does *not* call
|
||||
/// [`Ui::allocate_space`] (so the parent cursor is left untouched) nor [`Ui::interact`].
|
||||
/// Use the returned [`SizedContainerAtom`] to paint at an arbitrary [`Rect`] via
|
||||
/// [`SizedContainerAtom::paint_at`]. This is what makes it possible to nest one atom-based
|
||||
/// widget inside another.
|
||||
///
|
||||
/// `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`].
|
||||
pub fn measure(self, ui: &Ui, available_size: Vec2) -> SizedAtomLayout<'a> {
|
||||
pub fn measure(self, ui: &Ui, available_size: Vec2) -> SizedContainerAtom<'a> {
|
||||
let Self {
|
||||
id,
|
||||
mut atoms,
|
||||
gap,
|
||||
frame,
|
||||
sense,
|
||||
fallback_text_color,
|
||||
min_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 =
|
||||
fallback_text_color.unwrap_or_else(|| ui.style().visuals.text_color());
|
||||
let gap = gap.unwrap_or_else(|| ui.spacing().icon_spacing);
|
||||
@@ -323,12 +298,10 @@ impl<'a> AtomLayout<'a> {
|
||||
let intrinsic_size =
|
||||
(Vec2::new(intrinsic_width, intrinsic_height) + margin.sum()).at_least(min_size);
|
||||
|
||||
SizedAtomLayout {
|
||||
SizedContainerAtom {
|
||||
sized_atoms: sized_items,
|
||||
frame,
|
||||
fallback_text_color,
|
||||
id,
|
||||
sense,
|
||||
outer_size,
|
||||
intrinsic_size,
|
||||
grow_count,
|
||||
@@ -337,34 +310,16 @@ impl<'a> AtomLayout<'a> {
|
||||
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
|
||||
/// allocated space or interacted, so it can be painted at an arbitrary [`Rect`] via
|
||||
/// [`Self::paint_at`]. This is what lets one [`AtomLayout`] be nested inside another.
|
||||
/// Produced by [`ContainerAtom::measure`]. It has not yet allocated space or interacted, so it
|
||||
/// can be painted at an arbitrary [`Rect`] via [`Self::paint_at`]. This is what lets one
|
||||
/// 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)]
|
||||
pub struct SizedAtomLayout<'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,
|
||||
|
||||
pub struct SizedContainerAtom<'a> {
|
||||
/// The total widget size we'll request, including the frame margin. Used to allocate space.
|
||||
///
|
||||
/// Actual allocated size may be different.
|
||||
@@ -396,19 +351,7 @@ pub struct SizedAtomLayout<'a> {
|
||||
gap: f32,
|
||||
}
|
||||
|
||||
/// Instructions for painting an [`AtomLayout`].
|
||||
///
|
||||
/// 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> {
|
||||
impl<'atom> SizedContainerAtom<'atom> {
|
||||
pub fn iter_kinds(&self) -> impl Iterator<Item = &SizedAtomKind<'atom>> {
|
||||
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`.
|
||||
///
|
||||
/// `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`
|
||||
/// becomes the base of the returned [`AtomLayoutResponse`].
|
||||
pub fn paint_at(self, ui: &Ui, rect: Rect, response: Response) -> AtomLayoutResponse {
|
||||
/// `response.rect`; when nested, the parent passes the cell rect it computed.
|
||||
///
|
||||
/// 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 {
|
||||
sized_atoms,
|
||||
frame,
|
||||
@@ -515,7 +460,7 @@ impl<'atom> SizedAtomLayout<'atom> {
|
||||
|
||||
let mut cursor = aligned_rect.left();
|
||||
|
||||
let mut response = AtomLayoutResponse::empty(response);
|
||||
let mut custom_rects = CustomRects::new();
|
||||
|
||||
for sized in sized_atoms {
|
||||
let size = sized.size;
|
||||
@@ -531,10 +476,10 @@ impl<'atom> SizedAtomLayout<'atom> {
|
||||
|
||||
if let Some(id) = sized.id {
|
||||
debug_assert!(
|
||||
!response.custom_rects.iter().any(|(i, _)| *i == id),
|
||||
!custom_rects.iter().any(|(i, _)| *i == id),
|
||||
"Duplicate custom id"
|
||||
);
|
||||
response.custom_rects.push((id, rect));
|
||||
custom_rects.push((id, rect));
|
||||
}
|
||||
|
||||
match sized.kind {
|
||||
@@ -545,79 +490,22 @@ impl<'atom> SizedAtomLayout<'atom> {
|
||||
image.paint_at(ui, rect);
|
||||
}
|
||||
SizedAtomKind::Empty { .. } => {}
|
||||
SizedAtomKind::Layout(layout) => {
|
||||
// TODO(lucasmerlin): Add some kind of justify flag to AtomLayout
|
||||
let layout_response = ui.interact(frame, layout.id, layout.sense);
|
||||
layout.paint_at(ui, frame, layout_response);
|
||||
SizedAtomKind::Widget(widget) => {
|
||||
// TODO(lucasmerlin): Add some kind of justify flag to the layout
|
||||
widget.paint_at(ui, frame);
|
||||
}
|
||||
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<'_> {
|
||||
/// 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> {
|
||||
impl<'a> Deref for ContainerAtom<'a> {
|
||||
type Target = Atoms<'a>;
|
||||
|
||||
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 {
|
||||
&mut self.atoms
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for SizedAtomLayout<'a> {
|
||||
impl<'a> Deref for SizedContainerAtom<'a> {
|
||||
type Target = [SizedAtom<'a>];
|
||||
|
||||
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 {
|
||||
&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
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
mod atom;
|
||||
mod atom_ext;
|
||||
mod atom_kind;
|
||||
mod atom_layout;
|
||||
mod atoms;
|
||||
mod container_atom;
|
||||
mod sized_atom;
|
||||
mod sized_atom_kind;
|
||||
mod widget_atom;
|
||||
|
||||
pub use atom::*;
|
||||
pub use atom_ext::*;
|
||||
pub use atom_kind::*;
|
||||
pub use atom_layout::*;
|
||||
pub use atoms::*;
|
||||
pub use container_atom::*;
|
||||
pub use sized_atom::*;
|
||||
pub use sized_atom_kind::*;
|
||||
pub use widget_atom::*;
|
||||
|
||||
@@ -10,7 +10,7 @@ pub struct SizedAtom<'a> {
|
||||
|
||||
/// 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.
|
||||
pub size: Vec2,
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{Image, SizedAtomLayout};
|
||||
use crate::{Image, SizedContainerAtom, SizedWidgetAtom};
|
||||
use emath::Vec2;
|
||||
use epaint::Galley;
|
||||
use std::sync::Arc;
|
||||
@@ -9,7 +9,8 @@ pub enum SizedAtomKind<'a> {
|
||||
Empty { size: Option<Vec2> },
|
||||
Text(Arc<Galley>),
|
||||
Image { image: Image<'a>, size: Vec2 },
|
||||
Layout(Box<SizedAtomLayout<'a>>),
|
||||
Widget(Box<SizedWidgetAtom<'a>>),
|
||||
Container(Box<SizedContainerAtom<'a>>),
|
||||
}
|
||||
|
||||
impl Default for SizedAtomKind<'_> {
|
||||
@@ -25,7 +26,8 @@ impl SizedAtomKind<'_> {
|
||||
SizedAtomKind::Text(galley) => galley.size(),
|
||||
SizedAtomKind::Image { image: _, size } => *size,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
375
crates/egui/src/atomics/widget_atom.rs
Normal file
375
crates/egui/src/atomics/widget_atom.rs
Normal 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;
|
||||
@@ -1333,7 +1333,7 @@ fn title_ui(
|
||||
|
||||
let mut child_ui = ui.new_child(UiBuilder::new());
|
||||
|
||||
let mut layout = AtomLayout::new(atoms)
|
||||
let mut layout = WidgetAtom::new(atoms)
|
||||
.gap(spacing)
|
||||
.fallback_font(TextStyle::Heading)
|
||||
.wrap_mode(TextWrapMode::Truncate)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use epaint::Margin;
|
||||
|
||||
use crate::{
|
||||
Atom, AtomExt as _, AtomKind, AtomLayout, AtomLayoutResponse, Color32, CornerRadius, Frame,
|
||||
Image, IntoAtoms, NumExt as _, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2,
|
||||
Widget, WidgetInfo, WidgetText, WidgetType,
|
||||
Atom, AtomExt as _, AtomKind, Color32, CornerRadius, Frame, Image, IntoAtoms, NumExt as _,
|
||||
Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2, Widget, WidgetAtom,
|
||||
WidgetAtomResponse, WidgetInfo, WidgetText, WidgetType,
|
||||
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);`"]
|
||||
pub struct Button<'a> {
|
||||
layout: AtomLayout<'a>,
|
||||
layout: WidgetAtom<'a>,
|
||||
fill: Option<Color32>,
|
||||
stroke: Option<Stroke>,
|
||||
small: bool,
|
||||
@@ -44,7 +44,7 @@ pub struct Button<'a> {
|
||||
impl<'a> Button<'a> {
|
||||
pub fn new(atoms: impl IntoAtoms<'a>) -> Self {
|
||||
Self {
|
||||
layout: AtomLayout::new(atoms.into_atoms())
|
||||
layout: WidgetAtom::new(atoms.into_atoms())
|
||||
.sense(Sense::click())
|
||||
.fallback_font(TextStyle::Button),
|
||||
fill: None,
|
||||
@@ -274,8 +274,8 @@ impl<'a> Button<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Show the button and return a [`AtomLayoutResponse`] for painting custom contents.
|
||||
pub fn atom_ui(self, ui: &mut Ui) -> AtomLayoutResponse {
|
||||
/// Show the button and return a [`WidgetAtomResponse`] for painting custom contents.
|
||||
pub fn atom_ui(self, ui: &mut Ui) -> WidgetAtomResponse {
|
||||
let Button {
|
||||
mut layout,
|
||||
fill,
|
||||
@@ -357,7 +357,7 @@ impl<'a> Button<'a> {
|
||||
|
||||
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) {
|
||||
if image_tint_follows_text_color {
|
||||
prepared.map_images(|image| image.tint(text_style.color));
|
||||
@@ -367,7 +367,7 @@ impl<'a> Button<'a> {
|
||||
|
||||
prepared.paint(ui)
|
||||
} else {
|
||||
AtomLayoutResponse::empty(prepared.response)
|
||||
WidgetAtomResponse::empty(prepared.response)
|
||||
};
|
||||
|
||||
if let Some(cursor) = ui.visuals().interact_cursor
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use emath::Rect;
|
||||
|
||||
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,
|
||||
widget_style::{CheckboxStyle, Classes, HasClasses},
|
||||
};
|
||||
@@ -86,7 +86,7 @@ impl Widget for Checkbox<'_> {
|
||||
|
||||
let text = atoms.text().map(String::from);
|
||||
|
||||
let mut prepared = AtomLayout::new(atoms)
|
||||
let mut prepared = WidgetAtom::new(atoms)
|
||||
.sense(Sense::click())
|
||||
.min_size(min_size)
|
||||
.frame(frame)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -55,7 +55,7 @@ impl Widget for RadioButton<'_> {
|
||||
|
||||
let text = atoms.text().map(String::from);
|
||||
|
||||
let mut prepared = AtomLayout::new(atoms)
|
||||
let mut prepared = WidgetAtom::new(atoms)
|
||||
.sense(Sense::click())
|
||||
.min_size(min_size)
|
||||
.allocate(ui);
|
||||
|
||||
@@ -4,10 +4,10 @@ use emath::{Rect, TSTransform};
|
||||
use epaint::text::{Galley, LayoutJob, TextWrapMode, cursor::CCursor};
|
||||
|
||||
use crate::{
|
||||
Align, Align2, AsIdSalt, AtomExt as _, AtomKind, AtomLayout, Atoms, Color32, Context,
|
||||
CursorIcon, Event, EventFilter, FontSelection, Frame, Id, IdSalt, ImeEvent, IntoAtoms,
|
||||
IntoSizedResult, Key, KeyboardShortcut, Margin, Modifiers, NumExt as _, Response, Sense,
|
||||
SizedAtomKind, TextBuffer, TextStyle, Ui, Vec2, Widget, WidgetInfo, WidgetWithState, epaint,
|
||||
Align, Align2, AsIdSalt, AtomExt as _, AtomKind, Atoms, Color32, Context, CursorIcon, Event,
|
||||
EventFilter, FontSelection, Frame, Id, IdSalt, ImeEvent, IntoAtoms, IntoSizedResult, Key,
|
||||
KeyboardShortcut, Margin, Modifiers, NumExt as _, Response, Sense, SizedAtomKind, TextBuffer,
|
||||
TextStyle, Ui, Vec2, Widget, WidgetAtom, WidgetInfo, WidgetWithState, epaint,
|
||||
os::OperatingSystem,
|
||||
output::OutputEvent,
|
||||
response,
|
||||
@@ -679,7 +679,7 @@ impl TextEdit<'_> {
|
||||
TextWrapMode::Truncate
|
||||
};
|
||||
|
||||
let mut allocated = AtomLayout::new(atoms)
|
||||
let mut allocated = WidgetAtom::new(atoms)
|
||||
.id(id)
|
||||
.min_size(Vec2::new(allocate_width, min_height))
|
||||
.max_width(allocate_width)
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::text::CCursorRange;
|
||||
/// The output from a [`TextEdit`](crate::TextEdit).
|
||||
pub struct TextEditOutput {
|
||||
/// The interaction response.
|
||||
pub response: crate::AtomLayoutResponse,
|
||||
pub response: crate::WidgetAtomResponse,
|
||||
|
||||
/// How the text was displayed.
|
||||
pub galley: Arc<crate::Galley>,
|
||||
|
||||
@@ -124,11 +124,11 @@ fn test_button_shortcut_text() {
|
||||
/// All of these should look the same.
|
||||
#[test]
|
||||
fn test_atom_letter_spacing() {
|
||||
use egui::AtomLayout;
|
||||
use egui::WidgetAtom;
|
||||
|
||||
let mut harness = HarnessBuilder::default().build_ui(|ui| {
|
||||
ui.add(AtomLayout::new("1.00x").gap(0.0));
|
||||
ui.add(AtomLayout::new(("1.00", "x")).gap(0.0));
|
||||
ui.add(WidgetAtom::new("1.00x").gap(0.0));
|
||||
ui.add(WidgetAtom::new(("1.00", "x")).gap(0.0));
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 0.0;
|
||||
ui.label("1.00");
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
use egui::accesskit::Role;
|
||||
use egui::load::SizedTexture;
|
||||
use egui::{
|
||||
Align, AtomExt as _, AtomLayout, Button, Color32, ColorImage, Direction, DragValue, Event,
|
||||
Grid, IntoAtoms as _, Layout, PointerButton, Response, RichText, Slider, Stroke, StrokeKind,
|
||||
Align, AtomExt as _, Button, Color32, ColorImage, Direction, DragValue, Event, Grid,
|
||||
IntoAtoms as _, Layout, PointerButton, Response, RichText, Slider, Stroke, StrokeKind,
|
||||
TextEdit, TextWrapMode, TextureHandle, TextureOptions, Ui, UiBuilder, Vec2, Widget as _,
|
||||
include_image,
|
||||
WidgetAtom, include_image,
|
||||
};
|
||||
use egui_kittest::kittest::{Queryable as _, by};
|
||||
use egui_kittest::{Harness, Node, SnapshotResult, SnapshotResults};
|
||||
@@ -159,7 +159,7 @@ fn widget_tests() {
|
||||
|
||||
for atoms in interesting_atoms {
|
||||
results.add(test_widget_layout(&format!("atoms_{}", atoms.0), |ui| {
|
||||
AtomLayout::new(atoms.1.clone()).ui(ui)
|
||||
WidgetAtom::new(atoms.1.clone()).ui(ui)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user