mirror of
https://github.com/emilk/egui.git
synced 2026-09-02 06:40:06 -04:00
Add atom_ignore_spacing
This commit is contained in:
@@ -46,6 +46,9 @@ pub struct Atom<'a> {
|
||||
/// See [`crate::AtomExt::atom_align`]
|
||||
pub align: Align2,
|
||||
|
||||
/// See [`crate::AtomExt::atom_ignore_spacing`]
|
||||
pub ignore_spacing: bool,
|
||||
|
||||
/// The atom type / content
|
||||
pub kind: AtomKind<'a>,
|
||||
}
|
||||
@@ -58,6 +61,7 @@ impl Default for Atom<'_> {
|
||||
max_size: Vec2::INFINITY,
|
||||
grow: false,
|
||||
shrink: false,
|
||||
ignore_spacing: false,
|
||||
align: Align2::CENTER_CENTER,
|
||||
kind: AtomKind::Empty,
|
||||
}
|
||||
@@ -72,6 +76,7 @@ impl<'a> Atom<'a> {
|
||||
pub fn grow() -> Self {
|
||||
Atom {
|
||||
grow: true,
|
||||
ignore_spacing: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -144,6 +149,7 @@ impl<'a> Atom<'a> {
|
||||
size,
|
||||
intrinsic_size: intrinsic_size.at_least(self.size.unwrap_or_default()),
|
||||
grow: self.grow,
|
||||
ignore_spacing: self.ignore_spacing,
|
||||
align: self.align,
|
||||
kind: sized,
|
||||
}
|
||||
|
||||
@@ -70,6 +70,12 @@ pub trait AtomExt<'a> {
|
||||
self.atom_max_height(height)
|
||||
}
|
||||
|
||||
/// If `true`, this atom will not contribute to inter-atom gap spacing.
|
||||
///
|
||||
/// This is useful for invisible spacers like [`Atom::grow()`] where you want
|
||||
/// the grow space to replace the gaps rather than adding to them.
|
||||
fn atom_ignore_spacing(self, ignore_spacing: bool) -> Atom<'a>;
|
||||
|
||||
/// Sets the [`emath::Align2`] of a single atom within its available space.
|
||||
///
|
||||
/// Defaults to center-center.
|
||||
@@ -122,6 +128,12 @@ where
|
||||
atom
|
||||
}
|
||||
|
||||
fn atom_ignore_spacing(self, ignore_spacing: bool) -> Atom<'a> {
|
||||
let mut atom = self.into();
|
||||
atom.ignore_spacing = ignore_spacing;
|
||||
atom
|
||||
}
|
||||
|
||||
fn atom_align(self, align: emath::Align2) -> Atom<'a> {
|
||||
let mut atom = self.into();
|
||||
atom.align = align;
|
||||
|
||||
@@ -10,6 +10,33 @@ use smallvec::SmallVec;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Compute the effective gap between atom `i` and atom `i+1`.
|
||||
///
|
||||
/// Each atom with `ignore_spacing` has a "budget" of 1 full gap to remove,
|
||||
/// distributed across its adjacent gaps:
|
||||
/// - Edge atom (first or last): 1 adjacent gap → fully removed
|
||||
/// - Middle atom: 2 adjacent gaps → each reduced by 0.5×
|
||||
fn gap_between(
|
||||
ignore_spacing: impl Fn(usize) -> bool,
|
||||
count: usize,
|
||||
gap: f32,
|
||||
i: usize,
|
||||
) -> f32 {
|
||||
debug_assert!(i + 1 < count);
|
||||
let mut reduction = 0.0_f32;
|
||||
if ignore_spacing(i) {
|
||||
reduction += if i == 0 || i == count - 1 { 1.0 } else { 0.5 };
|
||||
}
|
||||
if ignore_spacing(i + 1) {
|
||||
reduction += if i + 1 == 0 || i + 1 == count - 1 {
|
||||
1.0
|
||||
} else {
|
||||
0.5
|
||||
};
|
||||
}
|
||||
gap * (1.0 - reduction).max(0.0)
|
||||
}
|
||||
|
||||
/// Intra-widget layout utility.
|
||||
///
|
||||
/// Used to lay out and paint [`crate::Atom`]s.
|
||||
@@ -250,8 +277,22 @@ impl<'a> AtomLayout<'a> {
|
||||
Align2([ui.layout().horizontal_align(), ui.layout().vertical_align()])
|
||||
});
|
||||
|
||||
// Collect ignore_spacing flags before atoms are consumed by into_sized.
|
||||
let ignore_spacing_flags: SmallVec<[bool; ATOMS_SMALL_VEC_SIZE]> =
|
||||
atoms.iter().map(|a| a.ignore_spacing).collect();
|
||||
|
||||
if atoms.len() > 1 {
|
||||
let gap_space = gap * (atoms.len() as f32 - 1.0);
|
||||
let atom_count = atoms.len();
|
||||
let gap_space: f32 = (0..atom_count - 1)
|
||||
.map(|i| {
|
||||
gap_between(
|
||||
|idx| ignore_spacing_flags[idx],
|
||||
atom_count,
|
||||
gap,
|
||||
i,
|
||||
)
|
||||
})
|
||||
.sum();
|
||||
desired_width += gap_space;
|
||||
intrinsic_width += gap_space;
|
||||
}
|
||||
@@ -454,7 +495,10 @@ impl<'atom> AllocatedAtomLayout<'atom> {
|
||||
|
||||
let mut response = AtomLayoutResponse::empty(response);
|
||||
|
||||
for sized in sized_atoms {
|
||||
let atom_count = sized_atoms.len();
|
||||
let ignore_flags: SmallVec<[bool; ATOMS_SMALL_VEC_SIZE]> =
|
||||
sized_atoms.iter().map(|a| a.ignore_spacing()).collect();
|
||||
for (i, sized) in sized_atoms.into_iter().enumerate() {
|
||||
let size = sized.size;
|
||||
// TODO(lucasmerlin): This is not ideal, since this might lead to accumulated rounding errors
|
||||
// https://github.com/emilk/egui/pull/5830#discussion_r2079627864
|
||||
@@ -463,7 +507,12 @@ impl<'atom> AllocatedAtomLayout<'atom> {
|
||||
let frame = aligned_rect
|
||||
.with_min_x(cursor)
|
||||
.with_max_x(cursor + size.x + growth);
|
||||
cursor = frame.right() + gap;
|
||||
let effective_gap = if i + 1 < atom_count {
|
||||
gap_between(|idx| ignore_flags[idx], atom_count, gap, i)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
cursor = frame.right() + effective_gap;
|
||||
let rect = sized.align.align_size_within_rect(size, frame);
|
||||
|
||||
if let Some(id) = sized.id {
|
||||
|
||||
@@ -8,6 +8,8 @@ pub struct SizedAtom<'a> {
|
||||
|
||||
pub(crate) grow: bool,
|
||||
|
||||
pub(crate) ignore_spacing: bool,
|
||||
|
||||
/// The size of the atom.
|
||||
///
|
||||
/// Used for placing this atom in [`crate::AtomLayout`], the cursor will advance by
|
||||
@@ -28,4 +30,9 @@ impl SizedAtom<'_> {
|
||||
pub fn is_grow(&self) -> bool {
|
||||
self.grow
|
||||
}
|
||||
|
||||
/// Was this [`crate::Atom`] marked as `ignore_spacing`?
|
||||
pub fn ignore_spacing(&self) -> bool {
|
||||
self.ignore_spacing
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user