1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-31 13:50:04 -04:00

Implement shrinking

This commit is contained in:
lucasmerlin
2025-04-09 15:40:58 +02:00
parent 026e47d939
commit 5c5cd32fd5
2 changed files with 94 additions and 28 deletions

View File

@@ -74,8 +74,8 @@ impl<'a> WidgetLayout<'a> {
.unwrap_or_else(|| ui.style().visuals.text_color()); .unwrap_or_else(|| ui.style().visuals.text_color());
let gap = self.gap.unwrap_or(ui.spacing().icon_spacing); let gap = self.gap.unwrap_or(ui.spacing().icon_spacing);
let available_size = ui.available_size(); // The size available for the content
let available_width = available_size.x; let available_inner_size = ui.available_size() - self.frame.total_margin().sum();
let mut desired_width = 0.0; let mut desired_width = 0.0;
let mut preferred_width = 0.0; let mut preferred_width = 0.0;
@@ -86,27 +86,29 @@ impl<'a> WidgetLayout<'a> {
let mut grow_count = 0; let mut grow_count = 0;
for (item) in self.atomics.0 { let mut shrink_item = None;
let (preferred_size, sized) = match item.kind {
AtomicKind::Text(text) => { if self.atomics.0.len() > 1 {
let galley = text.into_galley(ui, None, available_width, TextStyle::Button); let gap_space = gap * (self.atomics.0.len() as f32 - 1.0);
( desired_width += gap_space;
galley.size(), // TODO preferred_width += gap_space;
SizedAtomicKind::Text(galley), }
)
for ((idx, item)) in self.atomics.0.into_iter().enumerate() {
if item.shrink {
debug_assert!(
shrink_item.is_none(),
"Only one atomic may be marked as shrink"
);
if shrink_item.is_none() {
shrink_item = Some((idx, item));
continue;
} }
AtomicKind::Image(image) => { }
let size = if item.grow {
image.load_and_calc_size(ui, Vec2::min(available_size, Vec2::splat(16.0))); grow_count += 1;
let size = size.unwrap_or_default(); }
(size, SizedAtomicKind::Image(image, size)) let (preferred_size, sized) = item.kind.into_sized(ui, available_inner_size);
}
AtomicKind::Custom(id, size) => (size, SizedAtomicKind::Custom(id, size)),
AtomicKind::Grow => {
grow_count += 1;
(Vec2::ZERO, SizedAtomicKind::Grow)
}
};
let size = sized.size(); let size = sized.size();
desired_width += size.x; desired_width += size.x;
@@ -117,10 +119,21 @@ impl<'a> WidgetLayout<'a> {
sized_items.push(sized); sized_items.push(sized);
} }
if sized_items.len() > 1 { if let Some((index, item)) = shrink_item {
let gap_space = gap * (sized_items.len() as f32 - 1.0); // The `shrink` item gets the remaining space
desired_width += gap_space; let mut shrunk_size = Vec2::new(
preferred_width += gap_space; available_inner_size.x - desired_width,
available_inner_size.y,
);
let (preferred_size, sized) = item.kind.into_sized(ui, shrunk_size);
let size = sized.size();
desired_width += size.x;
preferred_width += preferred_size.x;
height = height.max(size.y);
sized_items.insert(index, sized);
} }
let margin = self.frame.total_margin(); let margin = self.frame.total_margin();
@@ -146,6 +159,7 @@ impl<'a> WidgetLayout<'a> {
for sized in sized_items { for sized in sized_items {
let size = sized.size(); let size = sized.size();
let width = match sized { let width = match sized {
// TODO: check for atomic.grow here
SizedAtomicKind::Grow => grow_width, SizedAtomicKind::Grow => grow_width,
_ => size.x, _ => size.x,
}; };
@@ -246,9 +260,33 @@ pub enum AtomicKind<'a> {
Grow, Grow,
} }
impl<'a> AtomicKind<'a> {
/// First returned argument is the preferred size.
pub fn into_sized(self, ui: &Ui, available_size: Vec2) -> (Vec2, SizedAtomicKind<'a>) {
match self {
AtomicKind::Text(text) => {
let galley = text.into_galley(ui, None, available_size.x, TextStyle::Button);
(
galley.size(), // TODO
SizedAtomicKind::Text(galley),
)
}
AtomicKind::Image(image) => {
let size =
image.load_and_calc_size(ui, Vec2::min(available_size, Vec2::splat(16.0)));
let size = size.unwrap_or_default();
(size, SizedAtomicKind::Image(image, size))
}
AtomicKind::Custom(id, size) => (size, SizedAtomicKind::Custom(id, size)),
AtomicKind::Grow => (Vec2::ZERO, SizedAtomicKind::Grow),
}
}
}
pub struct Atomic<'a> { pub struct Atomic<'a> {
size: Option<Vec2>, pub size: Option<Vec2>,
grow: bool, pub grow: bool,
pub shrink: bool,
pub kind: AtomicKind<'a>, pub kind: AtomicKind<'a>,
} }
@@ -256,6 +294,7 @@ pub fn a<'a>(i: impl Into<AtomicKind<'a>>) -> Atomic<'a> {
Atomic { Atomic {
size: None, size: None,
grow: false, grow: false,
shrink: false,
kind: i.into(), kind: i.into(),
} }
} }
@@ -275,6 +314,7 @@ impl Atomic<'_> {
pub trait AtomicExt<'a> { pub trait AtomicExt<'a> {
fn a_size(self, size: Vec2) -> Atomic<'a>; fn a_size(self, size: Vec2) -> Atomic<'a>;
fn a_grow(self, grow: bool) -> Atomic<'a>; fn a_grow(self, grow: bool) -> Atomic<'a>;
fn a_shrink(self, shrink: bool) -> Atomic<'a>;
} }
impl<'a, T> AtomicExt<'a> for T impl<'a, T> AtomicExt<'a> for T
@@ -292,6 +332,13 @@ where
atomic.grow = grow; atomic.grow = grow;
atomic atomic
} }
/// NOTE: Only a single atomic may be marked as shrink
fn a_shrink(self, shrink: bool) -> Atomic<'a> {
let mut atomic = self.into();
atomic.shrink = shrink;
atomic
}
} }
impl<'a, T> From<T> for Atomic<'a> impl<'a, T> From<T> for Atomic<'a>
@@ -302,6 +349,7 @@ where
Atomic { Atomic {
size: None, size: None,
grow: false, grow: false,
shrink: false,
kind: value.into(), kind: value.into(),
} }
} }

View File

@@ -223,6 +223,7 @@ impl<'a> Button<'a> {
} = self; } = self;
let has_frame = frame.unwrap_or_else(|| ui.visuals().button_frame); let has_frame = frame.unwrap_or_else(|| ui.visuals().button_frame);
let wrap_mode = wrap_mode.unwrap_or_else(|| ui.wrap_mode());
let mut button_padding = if has_frame { let mut button_padding = if has_frame {
ui.spacing().button_padding ui.spacing().button_padding
@@ -239,6 +240,23 @@ impl<'a> Button<'a> {
}) })
} }
// TODO: Pass TextWrapMode to AtomicLayout
// TODO: Should this rather be part of AtomicLayout?
// If the TextWrapMode is not Extend, ensure there is some item marked as `shrink`.
if !matches!(wrap_mode, TextWrapMode::Extend) {
let any_shrink = wl.atomics.iter_mut().any(|a| a.shrink);
if !any_shrink {
let first_text = wl
.atomics
.iter_mut()
.filter(|a| matches!(a.kind, AtomicKind::Text(..)))
.next();
if let Some(atomic) = first_text {
atomic.shrink = true;
}
}
}
let response = ui.ctx().read_response(ui.next_auto_id()); let response = ui.ctx().read_response(ui.next_auto_id());
let visuals = response.map_or(&ui.style().visuals.widgets.inactive, |response| { let visuals = response.map_or(&ui.style().visuals.widgets.inactive, |response| {