iris: make span compaction explicit
This commit is contained in:
1 parent
9097744a7c
commit
9fe6aca1f1
6 files changed
+100
-20
No files matched your search
+1
-1
@@ -331,7 +331,7 @@ where
|
|||||||
None => Span::empty(Dir::DOWN).add(rsc),
|
None => Span::empty(Dir::DOWN).add(rsc),
|
||||||
};
|
};
|
||||||
|
|
||||||
let widget = (header, column.width(rest(1)))
|
let widget = (header, column)
|
||||||
.span(Dir::DOWN)
|
.span(Dir::DOWN)
|
||||||
.gap(dp(4))
|
.gap(dp(4))
|
||||||
.pad(dp(10))
|
.pad(dp(10))
|
||||||
|
|||||||
@@ -847,6 +847,15 @@ such as corner radius and text's position within horizontal overflow use
|
|||||||
`Len`. Flexible padding participates in the same proportional allocation as a
|
`Len`. Flexible padding participates in the same proportional allocation as a
|
||||||
span rather than silently discarding its `rest` component.
|
span rather than silently discarding its `rest` component.
|
||||||
|
|
||||||
|
**A `Span` leaves its children in the offered orthogonal region unless
|
||||||
|
explicitly compacted** (2026-09-12). It still reports the widest orthogonal
|
||||||
|
child as its intrinsic size, so a row nested in a column keeps its content
|
||||||
|
height rather than claiming the column's remaining height. By default it does
|
||||||
|
not squeeze child regions to that intrinsic size, because doing so makes child
|
||||||
|
alignment operate inside the widest child rather than a known-width container.
|
||||||
|
`.compact()` opts into that shrink-to-widest-child placement for deliberately
|
||||||
|
dense groups.
|
||||||
|
|
||||||
**Text overflow is treatment plus position, not alignment** (2026-09-12).
|
**Text overflow is treatment plus position, not alignment** (2026-09-12).
|
||||||
`TextOverflow` selects `Visible`, `Wrap`, `Hidden`, or the single `Ellipsis`
|
`TextOverflow` selects `Visible`, `Wrap`, `Hidden`, or the single `Ellipsis`
|
||||||
treatment. Unwrapped hidden and ellipsized text retains one canonical shaped
|
treatment. Unwrapped hidden and ellipsized text retains one canonical shaped
|
||||||
|
|||||||
@@ -100,10 +100,12 @@ where
|
|||||||
rect(PaintId::PURPLE).sized((50, 50)).align(Align::TOP),
|
rect(PaintId::PURPLE).sized((50, 50)).align(Align::TOP),
|
||||||
)
|
)
|
||||||
.span(Dir::RIGHT)
|
.span(Dir::RIGHT)
|
||||||
|
.compact()
|
||||||
.center(),
|
.center(),
|
||||||
wtext("pretty cool right?").size(50),
|
wtext("pretty cool right?").size(50),
|
||||||
)
|
)
|
||||||
.span(Dir::DOWN)
|
.span(Dir::DOWN)
|
||||||
|
.compact()
|
||||||
.add(rsc);
|
.add(rsc);
|
||||||
|
|
||||||
let texts = Span::empty(Dir::DOWN).gap(10).add(rsc);
|
let texts = Span::empty(Dir::DOWN).gap(10).add(rsc);
|
||||||
|
|||||||
+10
-18
@@ -38,24 +38,17 @@ where
|
|||||||
.add(Srgba8::new(53, 57, 66, 255).to_linear());
|
.add(Srgba8::new(53, 57, 66, 255).to_linear());
|
||||||
|
|
||||||
let styled = "Bold, italic, underlined, and colored spans";
|
let styled = "Bold, italic, underlined, and colored spans";
|
||||||
let styled = wtext(styled)
|
let styled = wtext(styled).size(20).spans(vec![
|
||||||
.size(20)
|
SpanStyle::new(0..4).bold(),
|
||||||
.spans(vec![
|
SpanStyle::new(6..12).italic(),
|
||||||
SpanStyle::new(0..4).bold(),
|
SpanStyle::new(14..24).underline(),
|
||||||
SpanStyle::new(6..12).italic(),
|
SpanStyle::new(30..37).color(PaintId::SKY),
|
||||||
SpanStyle::new(14..24).underline(),
|
]);
|
||||||
SpanStyle::new(30..37).color(PaintId::SKY),
|
|
||||||
])
|
|
||||||
.width(rest(1));
|
|
||||||
|
|
||||||
let aligned = (
|
let aligned = (
|
||||||
wtext("Left aligned")
|
wtext("Left aligned").text_align(Align::CENTER_LEFT),
|
||||||
.text_align(Align::CENTER_LEFT)
|
wtext("Centered").text_align(Align::CENTER),
|
||||||
.width(rest(1)),
|
wtext("Right aligned").text_align(Align::CENTER_RIGHT),
|
||||||
wtext("Centered").text_align(Align::CENTER).width(rest(1)),
|
|
||||||
wtext("Right aligned")
|
|
||||||
.text_align(Align::CENTER_RIGHT)
|
|
||||||
.width(rest(1)),
|
|
||||||
)
|
)
|
||||||
.span(Dir::DOWN)
|
.span(Dir::DOWN)
|
||||||
.gap(dp(4))
|
.gap(dp(4))
|
||||||
@@ -84,8 +77,7 @@ where
|
|||||||
.spans(vec![SpanStyle::new(0..9).bold()]),
|
.spans(vec![SpanStyle::new(0..9).bold()]),
|
||||||
wtext("Drag across display text to select it. The overflow markers select hidden source text, but are never copied.")
|
wtext("Drag across display text to select it. The overflow markers select hidden source text, but are never copied.")
|
||||||
.overflow(TextOverflow::Wrap)
|
.overflow(TextOverflow::Wrap)
|
||||||
.color(PaintId::GRAY)
|
.color(PaintId::GRAY),
|
||||||
.width(rest(1)),
|
|
||||||
)
|
)
|
||||||
.span(Dir::DOWN)
|
.span(Dir::DOWN)
|
||||||
.gap(dp(6))
|
.gap(dp(6))
|
||||||
|
|||||||
@@ -204,6 +204,7 @@ fn a_span_reuses_unchanged_sibling_sizes_when_only_its_along_extent_changes() {
|
|||||||
children: vec![changed.any(), sibling.any()],
|
children: vec![changed.any(), sibling.any()],
|
||||||
dir: Dir::DOWN,
|
dir: Dir::DOWN,
|
||||||
gap: LayoutLen::ZERO,
|
gap: LayoutLen::ZERO,
|
||||||
|
compact_orthogonal: false,
|
||||||
});
|
});
|
||||||
let root = rsc
|
let root = rsc
|
||||||
.ui
|
.ui
|
||||||
@@ -231,6 +232,61 @@ fn a_span_reuses_unchanged_sibling_sizes_when_only_its_along_extent_changes() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_span_leaves_children_in_its_offered_orthogonal_axis_by_default() {
|
||||||
|
let mut rsc = TestRsc { ui: Ui::default() };
|
||||||
|
let short = wtext("short").add(&mut rsc);
|
||||||
|
let root = (short, wtext("a much wider line"))
|
||||||
|
.span(Dir::DOWN)
|
||||||
|
.add_strong(&mut rsc);
|
||||||
|
let root_id = root.id();
|
||||||
|
let root = root.any();
|
||||||
|
let mut render = UiRenderState::new();
|
||||||
|
render.resize((200.0, 100.0));
|
||||||
|
render.update(&root, &mut rsc);
|
||||||
|
|
||||||
|
let width = render.active[&root_id].size.x;
|
||||||
|
assert_eq!(width.rel, 0.0);
|
||||||
|
assert_eq!(width.rest, 0.0);
|
||||||
|
assert!(width.abs > 0.0 && width.abs < 200.0);
|
||||||
|
assert_eq!(
|
||||||
|
render.active[&short.id()]
|
||||||
|
.region
|
||||||
|
.to_px(vec2(200.0, 100.0))
|
||||||
|
.size()
|
||||||
|
.x,
|
||||||
|
200.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_compact_span_shrinks_its_orthogonal_axis_to_its_widest_child() {
|
||||||
|
let mut rsc = TestRsc { ui: Ui::default() };
|
||||||
|
let short = wtext("short").add(&mut rsc);
|
||||||
|
let root = (short, wtext("a much wider line"))
|
||||||
|
.span(Dir::DOWN)
|
||||||
|
.compact()
|
||||||
|
.add_strong(&mut rsc);
|
||||||
|
let root_id = root.id();
|
||||||
|
let root = root.any();
|
||||||
|
let mut render = UiRenderState::new();
|
||||||
|
render.resize((200.0, 100.0));
|
||||||
|
render.update(&root, &mut rsc);
|
||||||
|
|
||||||
|
let width = render.active[&root_id].size.x;
|
||||||
|
assert_eq!(width.rel, 0.0);
|
||||||
|
assert_eq!(width.rest, 0.0);
|
||||||
|
assert!(width.abs > 0.0 && width.abs < 200.0);
|
||||||
|
assert_eq!(
|
||||||
|
render.active[&short.id()]
|
||||||
|
.region
|
||||||
|
.to_px(vec2(200.0, 100.0))
|
||||||
|
.size()
|
||||||
|
.x,
|
||||||
|
width.abs,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_child_coordinate_offset_moves_only_the_child_subtree() {
|
fn a_child_coordinate_offset_moves_only_the_child_subtree() {
|
||||||
let mut rsc = TestRsc { ui: Ui::default() };
|
let mut rsc = TestRsc { ui: Ui::default() };
|
||||||
@@ -1077,6 +1133,7 @@ fn a_span_of_padded_children_inside_a_span_draws_each_where_its_box_is() {
|
|||||||
children: vec![header.any(), inner.any()],
|
children: vec![header.any(), inner.any()],
|
||||||
dir: Dir::DOWN,
|
dir: Dir::DOWN,
|
||||||
gap: LayoutLen::ZERO,
|
gap: LayoutLen::ZERO,
|
||||||
|
compact_orthogonal: false,
|
||||||
});
|
});
|
||||||
let mut list = LazySpan::new(Dir::DOWN, Pin::End);
|
let mut list = LazySpan::new(Dir::DOWN, Pin::End);
|
||||||
list.push_back(LazyItem::new(0, outer.any()));
|
list.push_back(LazyItem::new(0, outer.any()));
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ pub struct Span {
|
|||||||
pub children: Vec<StrongWidget>,
|
pub children: Vec<StrongWidget>,
|
||||||
pub dir: Dir,
|
pub dir: Dir,
|
||||||
pub gap: LayoutLen,
|
pub gap: LayoutLen,
|
||||||
|
pub compact_orthogonal: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Span {
|
impl Widget for Span {
|
||||||
@@ -85,7 +86,7 @@ impl Widget for Span {
|
|||||||
}
|
}
|
||||||
if ortho_mixed {
|
if ortho_mixed {
|
||||||
ortho_len = LayoutLen::default();
|
ortho_len = LayoutLen::default();
|
||||||
} else {
|
} else if self.compact_orthogonal {
|
||||||
let ortho = ortho_len
|
let ortho = ortho_len
|
||||||
.apply_rest(painter.density())
|
.apply_rest(painter.density())
|
||||||
.align(AxisAlign::Neg);
|
.align(AxisAlign::Neg);
|
||||||
@@ -111,9 +112,18 @@ impl Span {
|
|||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
dir,
|
dir,
|
||||||
gap: LayoutLen::ZERO,
|
gap: LayoutLen::ZERO,
|
||||||
|
compact_orthogonal: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shrink the span and its children to the widest orthogonal child
|
||||||
|
/// instead of leaving children in the orthogonal space offered by its
|
||||||
|
/// parent.
|
||||||
|
pub fn compact(mut self) -> Self {
|
||||||
|
self.compact_orthogonal = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gap(mut self, gap: impl Into<LayoutLen>) -> Self {
|
pub fn gap(mut self, gap: impl Into<LayoutLen>) -> Self {
|
||||||
self.gap = gap.into();
|
self.gap = gap.into();
|
||||||
self
|
self
|
||||||
@@ -132,6 +142,7 @@ pub struct SpanBuilder<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Ta
|
|||||||
pub children: Wa,
|
pub children: Wa,
|
||||||
pub dir: Dir,
|
pub dir: Dir,
|
||||||
pub gap: LayoutLen,
|
pub gap: LayoutLen,
|
||||||
|
pub compact_orthogonal: bool,
|
||||||
_pd: PhantomData<(State, Tag)>,
|
_pd: PhantomData<(State, Tag)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,6 +157,7 @@ impl<Rsc, const LEN: usize, Wa: WidgetArrLike<Rsc, LEN, Tag>, Tag> WidgetFnTrait
|
|||||||
children: self.children.add(rsc).arr.into_iter().collect(),
|
children: self.children.add(rsc).arr.into_iter().collect(),
|
||||||
dir: self.dir,
|
dir: self.dir,
|
||||||
gap: self.gap,
|
gap: self.gap,
|
||||||
|
compact_orthogonal: self.compact_orthogonal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,6 +170,7 @@ impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag>
|
|||||||
children,
|
children,
|
||||||
dir,
|
dir,
|
||||||
gap: LayoutLen::ZERO,
|
gap: LayoutLen::ZERO,
|
||||||
|
compact_orthogonal: false,
|
||||||
_pd: PhantomData,
|
_pd: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,6 +179,13 @@ impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag>
|
|||||||
self.gap = gap.into();
|
self.gap = gap.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shrink the span and its children to the widest orthogonal child
|
||||||
|
/// instead of filling the orthogonal space offered by its parent.
|
||||||
|
pub fn compact(mut self) -> Self {
|
||||||
|
self.compact_orthogonal = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for Span {
|
impl std::ops::Deref for Span {
|
||||||
|
|||||||
Reference in new issue
Block a user