1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-29 04:40:03 -04:00

Add atom_ignore_spacing

This commit is contained in:
lucasmerlin
2026-03-25 18:53:16 +01:00
parent 048f8ccd2a
commit 5217e9ac8f
10 changed files with 89 additions and 15 deletions

View File

@@ -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,
}

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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
}
}

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cbf68b6934dae0868bc9cf0891baf5acf110284d297cfa348e756237fca64a28
size 1564
oid sha256:b0b36160eff3335d10951d83cb20053bb15ff4b6fab68580710b509cd112b668
size 1563

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:231ceab75a602eedcd11f4f4ed34f38fb9d072f5cb54e135a7e02d33d257f86b
size 433973
oid sha256:82f4334e848e762aa28a29904b569ca2af12b38f308434bdf5968f607fc2be0a
size 433829

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cf00e99dbfdf7497688955feb8c417fab0a366588d92182eccee775abade5179
size 361876
oid sha256:2d018426f3014e79475d8d8f46a728632d7f15f2bfbf55b0ee648a56c792495c
size 370212

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e298d89e6fb434e5010d96661fca40bf119118b6b31fdd9fc13201bcd74c8ffd
size 15149
oid sha256:99f33211696d454e1fdbfa39e94af4c09239a38e00a3c4ef22e36adfbb2ee3c4
size 15073

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a0581d601f1e536298cb52bfc8a167aa37aebdf065fc910973a752c9c159223d
size 14733
oid sha256:6a4d8b64c9705e13e610a404b3eca780ecd26d7d10dba1c54207c6930018d06b
size 14659

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:337dcbf0b3a344c6cadaf9500376a627739e19e9c47b5da23786c98c612ef4dc
size 10028
oid sha256:d47e3584a09ea9b024dd36293acac5d97a1784eb87a469c2aec4b1db455b4e45
size 10514