Say when a drawing cannot be taken out of its box, rather than guessing
`lerp_inv` returns `Option`. Inverting a lerp over a range of zero length has no one answer, and `div_or`'s fallback picked one: the start of the range. Measured, that is not a wrong number so much as a plausible one -- taking a part out of a box fixed at the top of the window hands back exactly what went in, and out of a box fixed at the middle hands back the parent's own `rel` of 0.5 as if it were the child's fraction. Either way the caller cannot tell that nothing was recovered, which is the defect; the previous commit's claim that it "returns a rel of 0" is right only for the first case. `UiScalar::outside` and `UiSpan::outside` follow it to `Option`, and `Remap` is the answer at the region level: `new` says whether a drawing in one box can be put in another and `apply` then cannot fail, so `try_reuse` asks once for a whole subtree and `reusable` goes back to reporting only what the widget claims. That replaces the predicate the last commit put in `reusable`, which stated the same rule in a second place. `DivOr` existed only for the fallback and is gone, along with `UiRegion::outside` and `UiVec2::outside`, which had no callers once `Remap` owned the operation. `UiRegion::axis` took `&mut self` to return a shared reference; `Remap::new` needs it on a shared one. This does not redraw less. `Remap::new` refuses exactly what the predicate refused; what it buys is one statement of the rule and a remap that cannot half-apply. Counted on a resize, with the old wipe-everything for comparison: 20 padded rows, 101 widgets 101 draws -> 0 the `tabs` example 73 draws -> 0 the `text` example 49 draws -> 48 So the saving is whole where a resize does not change any widget's size, and nil in `text`, where both paragraphs rewrap to a different height and the relayout that forces reaches the root. The last commit's message oversold that case. Checked: fmt, clippy and 35 tests. `tabs` (with the image replay), `view` and `minimal` still byte-identical to `upstream/main`, `text` unchanged, and the live sway resize round trip still matches a cold start at each size.
This commit is contained in:
1 parent
984f482a7f
commit
53e61289f5
5 files changed
+106
-77
No files matched your search
+54
-22
@@ -56,13 +56,6 @@ impl UiVec2 {
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn outside(&self, region: &UiRegion) -> UiVec2 {
|
||||
UiVec2 {
|
||||
x: self.x.outside(®ion.x),
|
||||
y: self.y.outside(®ion.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn axis_mut(&mut self, axis: Axis) -> &mut UiScalar {
|
||||
match axis {
|
||||
Axis::X => &mut self.x,
|
||||
@@ -209,10 +202,12 @@ impl UiScalar {
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn outside(&self, span: &UiSpan) -> Self {
|
||||
let rel = self.rel.lerp_inv(span.start.rel, span.end.rel);
|
||||
/// Undoes `within`, and `None` where the span has a fixed length: every
|
||||
/// fraction of it lands on the same `rel`, so none can be told apart.
|
||||
pub fn outside(&self, span: &UiSpan) -> Option<Self> {
|
||||
let rel = self.rel.lerp_inv(span.start.rel, span.end.rel)?;
|
||||
let abs = self.abs - rel.lerp(span.start.abs, span.end.abs);
|
||||
Self { rel, abs }
|
||||
Some(Self { rel, abs })
|
||||
}
|
||||
|
||||
pub fn within_len(&self, len: UiScalar) -> Self {
|
||||
@@ -283,10 +278,10 @@ impl UiSpan {
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn outside(&self, parent: &Self) -> Self {
|
||||
Self {
|
||||
start: self.start.outside(parent),
|
||||
end: self.end.outside(parent),
|
||||
pub fn outside(&self, parent: &Self) -> Option<Self> {
|
||||
match (self.start.outside(parent), self.end.outside(parent)) {
|
||||
(Some(start), Some(end)) => Some(Self { start, end }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,14 +319,7 @@ impl UiRegion {
|
||||
y: self.y.within(&parent.y),
|
||||
}
|
||||
}
|
||||
pub const fn outside(&self, parent: &Self) -> Self {
|
||||
Self {
|
||||
x: self.x.outside(&parent.x),
|
||||
y: self.y.outside(&parent.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn axis(&mut self, axis: Axis) -> &UiSpan {
|
||||
pub const fn axis(&self, axis: Axis) -> &UiSpan {
|
||||
match axis {
|
||||
Axis::X => &self.x,
|
||||
Axis::Y => &self.y,
|
||||
@@ -409,6 +397,50 @@ impl UiRegion {
|
||||
}
|
||||
}
|
||||
|
||||
/// Taking a drawing out of one box and putting it in another, checked once
|
||||
/// for a whole subtree so that applying it cannot fail.
|
||||
///
|
||||
/// A box of a fixed length holds each part as an offset from its start rather
|
||||
/// than as a fraction of it, so those parts can be carried to a box of the
|
||||
/// same length but never stretched to a different one.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Remap {
|
||||
from: UiRegion,
|
||||
to: UiRegion,
|
||||
}
|
||||
|
||||
impl Remap {
|
||||
pub fn new(from: UiRegion, to: UiRegion) -> Option<Self> {
|
||||
[Axis::X, Axis::Y]
|
||||
.into_iter()
|
||||
.all(|axis| {
|
||||
let (from, to) = (from.axis(axis), to.axis(axis));
|
||||
from.start.rel != from.end.rel || from.len() == to.len()
|
||||
})
|
||||
.then_some(Self { from, to })
|
||||
}
|
||||
|
||||
pub fn apply(&self, region: UiRegion) -> UiRegion {
|
||||
UiRegion {
|
||||
x: Self::span(region.x, self.from.x, self.to.x),
|
||||
y: Self::span(region.y, self.from.y, self.to.y),
|
||||
}
|
||||
}
|
||||
|
||||
fn span(span: UiSpan, from: UiSpan, to: UiSpan) -> UiSpan {
|
||||
match span.outside(&from) {
|
||||
Some(out) => out.within(&to),
|
||||
// `new` admits this only where the two are the same length, so
|
||||
// the difference between their starts is the whole move.
|
||||
None => {
|
||||
let mut span = span;
|
||||
span.shift(to.start - from.start);
|
||||
span
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for UiRegion {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
|
||||
Reference in new issue
Block a user