mirror of
https://github.com/emilk/egui.git
synced 2026-06-26 22:53:14 -04:00
Add AtomKind::Layout (allow nesting)
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
use crate::{AtomKind, FontSelection, Id, IntoSizedArgs, IntoSizedResult, SizedAtom, Ui};
|
||||
use crate::{
|
||||
AtomKind, AtomLayout, FontSelection, Id, IntoSizedArgs, IntoSizedResult, SizedAtom, Ui,
|
||||
};
|
||||
use emath::{Align2, NumExt as _, Vec2};
|
||||
use epaint::text::TextWrapMode;
|
||||
|
||||
@@ -101,6 +103,17 @@ impl<'a> Atom<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Nest an [`AtomLayout`] (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 {
|
||||
Atom {
|
||||
kind: AtomKind::Layout(Box::new(layout)),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn this into a [`SizedAtom`].
|
||||
pub fn into_sized(
|
||||
self,
|
||||
@@ -161,3 +174,12 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this is a concrete `From` (not a blanket `From<impl Into<AtomKind>>`) on purpose.
|
||||
// `AtomLayout` must NOT implement `Into<AtomKind>`, or this would conflict with the blanket impl
|
||||
// above. Keep nesting going through `AtomKind::Layout` / `Atom::layout` only.
|
||||
impl<'a> From<AtomLayout<'a>> for Atom<'a> {
|
||||
fn from(layout: AtomLayout<'a>) -> Self {
|
||||
Atom::layout(layout)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{FontSelection, Image, ImageSource, SizedAtomKind, Ui, WidgetText};
|
||||
use crate::{AtomLayout, FontSelection, Image, ImageSource, SizedAtomKind, Ui, WidgetText};
|
||||
use emath::Vec2;
|
||||
use epaint::text::TextWrapMode;
|
||||
use std::fmt::Debug;
|
||||
@@ -65,6 +65,13 @@ pub enum AtomKind<'a> {
|
||||
/// Note: This api is experimental, expect breaking changes here.
|
||||
/// 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`].
|
||||
///
|
||||
/// The nested layout 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>>),
|
||||
}
|
||||
|
||||
impl Clone for AtomKind<'_> {
|
||||
@@ -77,6 +84,7 @@ impl Clone for AtomKind<'_> {
|
||||
log::warn!("Cannot clone atom closures");
|
||||
AtomKind::Empty
|
||||
}
|
||||
AtomKind::Layout(layout) => AtomKind::Layout(layout.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,6 +96,7 @@ 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>)"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,6 +158,15 @@ impl<'a> AtomKind<'a> {
|
||||
fallback_font,
|
||||
},
|
||||
),
|
||||
AtomKind::Layout(layout) => {
|
||||
// The nested layout is self-contained: it resolves its own wrap mode, fallback
|
||||
// font and frame, so we only forward the available size.
|
||||
let sized = layout.measure(ui, available_size);
|
||||
IntoSizedResult {
|
||||
intrinsic_size: sized.intrinsic_size,
|
||||
sized: SizedAtomKind::Layout(Box::new(sized)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::atomics::ATOMS_SMALL_VEC_SIZE;
|
||||
use crate::{
|
||||
AtomKind, Atoms, FontSelection, Frame, Id, Image, IntoAtoms, Response, Sense, SizedAtom,
|
||||
SizedAtomKind, Ui, Widget,
|
||||
@@ -29,6 +28,7 @@ use std::sync::Arc;
|
||||
///
|
||||
/// 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 atoms: Atoms<'a>,
|
||||
@@ -248,7 +248,7 @@ impl<'a> AtomLayout<'a> {
|
||||
|
||||
let mut height: f32 = 0.0;
|
||||
|
||||
let mut sized_items = SmallVec::new();
|
||||
let mut sized_items = Vec::new();
|
||||
|
||||
let mut grow_count = 0;
|
||||
|
||||
@@ -361,7 +361,7 @@ impl<'a> AtomLayout<'a> {
|
||||
/// [`Self::paint_at`]. This is what lets one [`AtomLayout`] be nested inside another.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SizedAtomLayout<'a> {
|
||||
pub sized_atoms: SmallVec<[SizedAtom<'a>; ATOMS_SMALL_VEC_SIZE]>,
|
||||
pub sized_atoms: Vec<SizedAtom<'a>>,
|
||||
pub frame: Frame,
|
||||
pub fallback_text_color: Color32,
|
||||
|
||||
@@ -535,6 +535,16 @@ impl<'atom> SizedAtomLayout<'atom> {
|
||||
image.paint_at(ui, rect);
|
||||
}
|
||||
SizedAtomKind::Empty { .. } => {}
|
||||
SizedAtomKind::Layout(layout) => {
|
||||
// Hand the nested layout the full (possibly grown) cell width so its own
|
||||
// `grow` atoms can expand, while keeping its measured height and honoring
|
||||
// this atom's vertical alignment within the row.
|
||||
let layout_rect = sized
|
||||
.align
|
||||
.align_size_within_rect(Vec2::new(frame.width(), size.y), frame);
|
||||
let layout_response = ui.interact(layout_rect, layout.id, layout.sense);
|
||||
layout.paint_at(ui, layout_rect, layout_response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
use crate::{Atom, AtomKind, Image, WidgetText};
|
||||
use smallvec::SmallVec;
|
||||
use std::borrow::Cow;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
// Rarely there should be more than 2 atoms in one Widget.
|
||||
// I guess it could happen in a menu button with Image and right text...
|
||||
pub(crate) const ATOMS_SMALL_VEC_SIZE: usize = 2;
|
||||
|
||||
/// A list of [`Atom`]s.
|
||||
///
|
||||
/// Many widgets take an `impl` [`IntoAtoms`] parameter,
|
||||
@@ -18,7 +13,7 @@ pub(crate) const ATOMS_SMALL_VEC_SIZE: usize = 2;
|
||||
/// ui.button((image, "Click me!"));
|
||||
/// # });
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Atoms<'a>(SmallVec<[Atom<'a>; ATOMS_SMALL_VEC_SIZE]>);
|
||||
pub struct Atoms<'a>(Vec<Atom<'a>>);
|
||||
|
||||
impl<'a> Atoms<'a> {
|
||||
pub fn new(atoms: impl IntoAtoms<'a>) -> Self {
|
||||
@@ -174,7 +169,7 @@ impl<'a> Atoms<'a> {
|
||||
|
||||
impl<'a> IntoIterator for Atoms<'a> {
|
||||
type Item = Atom<'a>;
|
||||
type IntoIter = smallvec::IntoIter<[Atom<'a>; ATOMS_SMALL_VEC_SIZE]>;
|
||||
type IntoIter = std::vec::IntoIter<Atom<'a>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::Image;
|
||||
use crate::{Image, SizedAtomLayout};
|
||||
use emath::Vec2;
|
||||
use epaint::Galley;
|
||||
use std::sync::Arc;
|
||||
@@ -6,9 +6,17 @@ use std::sync::Arc;
|
||||
/// A sized [`crate::AtomKind`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SizedAtomKind<'a> {
|
||||
Empty { size: Option<Vec2> },
|
||||
Empty {
|
||||
size: Option<Vec2>,
|
||||
},
|
||||
Text(Arc<Galley>),
|
||||
Image { image: Image<'a>, size: Vec2 },
|
||||
Image {
|
||||
image: Image<'a>,
|
||||
size: Vec2,
|
||||
},
|
||||
|
||||
/// A measured, nested [`crate::AtomLayout`]. See [`crate::AtomKind::Layout`].
|
||||
Layout(Box<SizedAtomLayout<'a>>),
|
||||
}
|
||||
|
||||
impl Default for SizedAtomKind<'_> {
|
||||
@@ -24,6 +32,7 @@ impl SizedAtomKind<'_> {
|
||||
SizedAtomKind::Text(galley) => galley.size(),
|
||||
SizedAtomKind::Image { image: _, size } => *size,
|
||||
SizedAtomKind::Empty { size } => size.unwrap_or_default(),
|
||||
SizedAtomKind::Layout(layout) => layout.frame_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user