| 3546 |
3546 |
|
Fill,
|
| 3547 |
3547 |
|
}
|
| 3548 |
3548 |
|
|
| 3549 |
|
- |
/// What a part is worth when there is not room for all of them.
|
|
3549 |
+ |
/// What a member is worth when there is not room for all of them.
|
| 3550 |
3550 |
|
///
|
| 3551 |
|
- |
/// Written for table columns and no longer only theirs: a row is an inline run
|
| 3552 |
|
- |
/// and a terminal 40 columns wide has to drop something out of it, which is the
|
| 3553 |
|
- |
/// same question a narrow table asks. The doc below is the column argument,
|
| 3554 |
|
- |
/// which is where the type was measured; the sentence that gave it away is
|
| 3555 |
|
- |
/// [`Priority::Essential`]'s, which was already written about a row.
|
|
3551 |
+ |
/// Written for table columns and no longer only theirs. Three shapes ask the
|
|
3552 |
+ |
/// same question and this answers all three: a table too narrow for its
|
|
3553 |
+ |
/// columns, a row too narrow for its parts (see [`RowPart::priority`]), and a
|
|
3554 |
+ |
/// group of regions sharing one run of room -- goingson's tab strip and the
|
|
3555 |
+ |
/// [`Region::Band`] beside it, which is the case wiki `layout-room-and-fallback`
|
|
3556 |
+ |
/// was ruled on. It is what any member of a group is worth, not a table
|
|
3557 |
+ |
/// concept, and [`Fallback::Shed`] is what reads it.
|
|
3558 |
+ |
///
|
|
3559 |
+ |
/// The doc below is the column argument, which is where the type was measured;
|
|
3560 |
+ |
/// the sentence that gave it away is [`Priority::Essential`]'s, which was
|
|
3561 |
+ |
/// already written about a row.
|
| 3556 |
3562 |
|
///
|
| 3557 |
3563 |
|
/// Ordered: [`Priority::Optional`] drops first, [`Priority::Essential`] never
|
| 3558 |
3564 |
|
/// drops. This replaces addressing columns by position, which is what both
|
| 3567 |
3573 |
|
pub enum Priority {
|
| 3568 |
3574 |
|
/// Dropped first.
|
| 3569 |
3575 |
|
Optional,
|
| 3570 |
|
- |
/// Dropped once the optional columns are gone.
|
|
3576 |
+ |
/// Dropped once the optional members are gone.
|
| 3571 |
3577 |
|
Secondary,
|
| 3572 |
|
- |
/// Never dropped. Without it the row does not identify itself.
|
|
3578 |
+ |
/// Never dropped. Without it the group does not identify itself.
|
| 3573 |
3579 |
|
Essential,
|
| 3574 |
3580 |
|
}
|
| 3575 |
3581 |
|
|
|
3582 |
+ |
/// How much room a group has, measured against its own allocation.
|
|
3583 |
+ |
///
|
|
3584 |
+ |
/// Never authored. A renderer computes it from what the group was given and
|
|
3585 |
+ |
/// what the group's own contents ask for, in that renderer's units: a webview
|
|
3586 |
+ |
/// from `min-content` under a container query, a terminal from cell widths,
|
|
3587 |
+ |
/// egui from the galley. Nothing in the description says a number, which is the
|
|
3588 |
+ |
/// point -- an authored breakpoint rots and this cannot.
|
|
3589 |
+ |
///
|
|
3590 |
+ |
/// # Why not [`Depth`]-style two members and no more
|
|
3591 |
+ |
///
|
|
3592 |
+ |
/// Two is what the measurement supports. The goingson case that produced this
|
|
3593 |
+ |
/// type is a window 913px wide -- makeover-geometry's `SizeClass::Expanded` --
|
|
3594 |
+ |
/// holding a group that has run out of room. A third tier would be a guess
|
|
3595 |
+ |
/// about a shape nothing in the tree has yet.
|
|
3596 |
+ |
///
|
|
3597 |
+ |
/// # Why it is not `SizeClass`
|
|
3598 |
+ |
///
|
|
3599 |
+ |
/// Because 913 is exactly the case that proves they are different facts. The
|
|
3600 |
+ |
/// window is roomy and the group is not, so a type that answered for both would
|
|
3601 |
+ |
/// have to be wrong about one of them. Sharing the name would also invite
|
|
3602 |
+ |
/// `@media` thinking straight back in, which is what put a `position: absolute`
|
|
3603 |
+ |
/// in goingson's stylesheet in the first place. Container semantics instead: a
|
|
3604 |
+ |
/// group narrowed by a sidebar behaves the same as one narrowed by the window,
|
|
3605 |
+ |
/// and there is one code path rather than two.
|
|
3606 |
+ |
///
|
|
3607 |
+ |
/// Ordered least room first, [`Priority`]'s convention, so a group nesting
|
|
3608 |
+ |
/// another takes the minimum of the two and relief still resolves inside-out.
|
|
3609 |
+ |
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
3610 |
+ |
#[non_exhaustive]
|
|
3611 |
+ |
pub enum Room {
|
|
3612 |
+ |
/// Not everything the group contains fits, and the group's [`Fallback`]
|
|
3613 |
+ |
/// decides what happens.
|
|
3614 |
+ |
Tight,
|
|
3615 |
+ |
/// Everything fits as described.
|
|
3616 |
+ |
Ample,
|
|
3617 |
+ |
}
|
|
3618 |
+ |
|
|
3619 |
+ |
/// What a group does when it is [`Room::Tight`].
|
|
3620 |
+ |
///
|
|
3621 |
+ |
/// Authored, and required: the field carrying this has no `Default` and a group
|
|
3622 |
+ |
/// cannot be described without saying what it does when it runs out of room.
|
|
3623 |
+ |
/// Max ruled on that 2026-08-18 -- more intentionality from layout designers is
|
|
3624 |
+ |
/// acceptable so long as the constraints are solvable, because the goal is
|
|
3625 |
+ |
/// enabling good layouts rather than rescuing bad ones. A default here would be
|
|
3626 |
+ |
/// the crate guessing, and the guess would be silently wrong on the screens
|
|
3627 |
+ |
/// that matter.
|
|
3628 |
+ |
///
|
|
3629 |
+ |
/// Relief resolves inside-out. A group asks its children to fall back before
|
|
3630 |
+ |
/// falling back itself, or an outer group collapses while an inner one still
|
|
3631 |
+ |
/// had slack.
|
|
3632 |
+ |
///
|
|
3633 |
+ |
/// # No `Swap`
|
|
3634 |
+ |
///
|
|
3635 |
+ |
/// An authored alternate group for the tight case is deliberately out of the
|
|
3636 |
+ |
/// first cut. It doubles the description for that group and the two halves can
|
|
3637 |
+ |
/// drift, which is the failure this vocabulary exists to end. Add it when a
|
|
3638 |
+ |
/// site proves it needs one.
|
|
3639 |
+ |
///
|
|
3640 |
+ |
/// `#[non_exhaustive]`, [`Width`]'s reasoning. Unlike [`Priority`] there is no
|
|
3641 |
+ |
/// order to preserve, so a member can be appended.
|
|
3642 |
+ |
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
3643 |
+ |
#[non_exhaustive]
|
|
3644 |
+ |
pub enum Fallback {
|
|
3645 |
+ |
/// One row becomes two. Every member stays, in the order described.
|
|
3646 |
+ |
Wrap,
|
|
3647 |
+ |
/// A row becomes a column. Every member stays, full width.
|
|
3648 |
+ |
Stack,
|
|
3649 |
+ |
/// Members drop by [`Priority`], down to [`Priority::Essential`].
|
|
3650 |
+ |
///
|
|
3651 |
+ |
/// What a narrow table already does with its columns, applied to a group.
|
|
3652 |
+ |
/// What drops is gone from the screen, so this is right when the dropped
|
|
3653 |
+ |
/// members are facts the reader can do without and wrong when they are the
|
|
3654 |
+ |
/// only way to act.
|
|
3655 |
+ |
Shed,
|
|
3656 |
+ |
/// The members [`Shed`](Self::Shed) would drop move into one overflow
|
|
3657 |
+ |
/// control instead.
|
|
3658 |
+ |
///
|
|
3659 |
+ |
/// The answer when a group holds actions. A control is not a fact: dropping
|
|
3660 |
+ |
/// it does not cost the reader a detail, it costs them the only way to act,
|
|
3661 |
+ |
/// which is [`RowPart::priority`]'s argument one level up.
|
|
3662 |
+ |
Menu,
|
|
3663 |
+ |
}
|
|
3664 |
+ |
|
| 3576 |
3665 |
|
/// One column of a table.
|
| 3577 |
3666 |
|
///
|
| 3578 |
3667 |
|
/// Described once. The grid track, the cell order and the drop behaviour are
|
| 5089 |
5178 |
|
assert_eq!(Paging::more(500).of(400).remaining(), Some(0));
|
| 5090 |
5179 |
|
}
|
| 5091 |
5180 |
|
|
|
5181 |
+ |
#[test]
|
|
5182 |
+ |
fn a_group_out_of_room_is_not_the_same_fact_as_a_narrow_window() {
|
|
5183 |
+ |
// The case the type exists for: 913px is a roomy window holding a group
|
|
5184 |
+ |
// that has run out of room, so room is measured against the group's own
|
|
5185 |
+ |
// allocation and never against the viewport.
|
|
5186 |
+ |
assert!(Room::Tight < Room::Ample);
|
|
5187 |
+ |
// A group nesting another has whichever room is scarcer, which is what
|
|
5188 |
+ |
// makes relief resolve inside-out rather than by declaration order.
|
|
5189 |
+ |
assert_eq!(Room::Ample.min(Room::Tight), Room::Tight);
|
|
5190 |
+ |
}
|
|
5191 |
+ |
|
|
5192 |
+ |
#[test]
|
|
5193 |
+ |
fn a_fallback_is_authored_and_a_group_cannot_omit_it() {
|
|
5194 |
+ |
// No `Default`. The compiler is what enforces rule 2, so the assertion
|
|
5195 |
+ |
// that matters is one this file cannot write; what it can say is that
|
|
5196 |
+ |
// the four authored answers are distinct and none is privileged.
|
|
5197 |
+ |
let all = [
|
|
5198 |
+ |
Fallback::Wrap,
|
|
5199 |
+ |
Fallback::Stack,
|
|
5200 |
+ |
Fallback::Shed,
|
|
5201 |
+ |
Fallback::Menu,
|
|
5202 |
+ |
];
|
|
5203 |
+ |
for (i, a) in all.iter().enumerate() {
|
|
5204 |
+ |
for b in &all[i + 1..] {
|
|
5205 |
+ |
assert_ne!(a, b);
|
|
5206 |
+ |
}
|
|
5207 |
+ |
}
|
|
5208 |
+ |
}
|
|
5209 |
+ |
|
|
5210 |
+ |
#[test]
|
|
5211 |
+ |
fn shedding_stops_at_essential_whatever_the_group_holds() {
|
|
5212 |
+ |
// Priority is read the same way for a group member as for a column,
|
|
5213 |
+ |
// which is the whole claim of generalising it off `Column`.
|
|
5214 |
+ |
let members = [
|
|
5215 |
+ |
("tabs", Priority::Essential),
|
|
5216 |
+ |
("search", Priority::Secondary),
|
|
5217 |
+ |
("count", Priority::Optional),
|
|
5218 |
+ |
];
|
|
5219 |
+ |
let kept: Vec<_> = members
|
|
5220 |
+ |
.iter()
|
|
5221 |
+ |
.filter(|(_, p)| *p >= Priority::Essential)
|
|
5222 |
+ |
.map(|(n, _)| *n)
|
|
5223 |
+ |
.collect();
|
|
5224 |
+ |
assert_eq!(kept, ["tabs"]);
|
|
5225 |
+ |
}
|
|
5226 |
+ |
|
| 5092 |
5227 |
|
#[test]
|
| 5093 |
5228 |
|
fn a_role_says_what_a_part_is_worth_when_the_run_does_not_fit() {
|
| 5094 |
5229 |
|
// The row still identifies itself after everything droppable has gone,
|