| 1877 |
1877 |
|
///
|
| 1878 |
1878 |
|
/// # What it is not
|
| 1879 |
1879 |
|
///
|
| 1880 |
|
- |
/// Not a [`Node::List`] of forms, which is N submits. This is one submit
|
|
1880 |
+ |
/// Not a list of forms, which is N submits. This is one submit
|
| 1881 |
1881 |
|
/// carrying N values under one question, which is what makes the two hard parts
|
| 1882 |
1882 |
|
/// hard: the names have to come back apart, and a refusal has to be able to say
|
| 1883 |
1883 |
|
/// *which* answer is wrong.
|
| 5532 |
5532 |
|
.any(|placed| match &placed.node {
|
| 5533 |
5533 |
|
Node::Region(slot) => slot.chooses(),
|
| 5534 |
5534 |
|
Node::Table { rows, .. } => rows.iter().any(|row| row.chosen.is_some()),
|
| 5535 |
|
- |
Node::List { rows, .. } => rows.iter().any(|row| row.chosen.is_some()),
|
| 5536 |
5535 |
|
_ => false,
|
| 5537 |
5536 |
|
})
|
| 5538 |
5537 |
|
}
|
| 6928 |
6927 |
|
/// One cell of a table row.
|
| 6929 |
6928 |
|
///
|
| 6930 |
6929 |
|
/// A cell was a `String` until then, so a table whose rows carry a control
|
| 6931 |
|
- |
/// could not be described at all and had to become a
|
| 6932 |
|
- |
/// [`Node::List`], losing its column headers — which is what the MNW server's
|
|
6930 |
+ |
/// could not be described at all and had to become a column-less table,
|
|
6931 |
+ |
/// losing its headers -- which is what the MNW server's
|
| 6933 |
6932 |
|
/// SSH-keys tab did, and why it read worse than the Askama original it replaced.
|
| 6934 |
6933 |
|
///
|
| 6935 |
6934 |
|
/// # Why the acts sit on the cell and not on the row
|
| 6989 |
6988 |
|
/// [`key`](Self::key) saying which column it answers to.
|
| 6990 |
6989 |
|
///
|
| 6991 |
6990 |
|
/// A cell was a `String` before that, so a table whose rows carry a control
|
| 6992 |
|
- |
/// could not be described at all and had to become a [`Node::List`], losing its
|
| 6993 |
|
- |
/// column headers, which is what the MNW server's SSH-keys tab did and why it
|
|
6991 |
+ |
/// could not be described at all and had to become a column-less table, losing
|
|
6992 |
+ |
/// its headers, which is what the MNW server's SSH-keys tab did and why it
|
| 6994 |
6993 |
|
/// read worse than the Askama original it replaced.
|
| 6995 |
6994 |
|
///
|
| 6996 |
6995 |
|
/// # Why the acts sit on the cell and not on the row
|
| 7984 |
7983 |
|
/// The questions, in order.
|
| 7985 |
7984 |
|
fields: Vec<Field>,
|
| 7986 |
7985 |
|
},
|
| 7987 |
|
- |
/// Rows of the same kind of thing.
|
| 7988 |
|
- |
List {
|
| 7989 |
|
- |
/// The rows, in order.
|
| 7990 |
|
- |
rows: Vec<Row>,
|
| 7991 |
|
- |
/// What is not shown, if anything is.
|
| 7992 |
|
- |
///
|
| 7993 |
|
- |
/// A described list of the first 50 of 400 tasks was indistinguishable
|
| 7994 |
|
- |
/// from a described list of 50 tasks, so each app grew its own answer:
|
| 7995 |
|
- |
/// goingson a 159-line pagination manager with two consumers that had
|
| 7996 |
|
- |
/// each written it separately first, Balanced Breakfast four
|
| 7997 |
|
- |
/// `loadMore` sites. Two idioms for one fact, and the fact is what
|
| 7998 |
|
- |
/// belongs here — how much more there is and how to ask for it.
|
| 7999 |
|
- |
/// Whether that becomes numbered pages, a load-more button or an
|
| 8000 |
|
- |
/// infinite scroll is the renderer's.
|
| 8001 |
|
- |
more: Option<Rest>,
|
| 8002 |
|
- |
},
|
| 8003 |
7986 |
|
/// Rows with named columns.
|
| 8004 |
7987 |
|
///
|
| 8005 |
7988 |
|
/// # Which containers carry a [`Rest`], and which do not
|
| 8006 |
7989 |
|
///
|
| 8007 |
7990 |
|
/// A container whose contents came from a query that can be partial carries
|
| 8008 |
|
- |
/// one. That is this and [`List`](Self::List), and nothing else in this
|
| 8009 |
|
- |
/// enum: [`Timeline`](Self::Timeline) is bounded by its `Track`, so more of
|
|
7991 |
+ |
/// one. That is this one -- in both its arrangements, since a list is this
|
|
7992 |
+ |
/// node with no columns -- and nothing else in this enum:
|
|
7993 |
+ |
/// [`Timeline`](Self::Timeline) is bounded by its `Track`, so more of
|
| 8010 |
7994 |
|
/// it is a different window and that is navigation rather than paging;
|
| 8011 |
7995 |
|
/// [`Stats`](Self::Stats) is a fixed set of figures; [`Region`](Self::Region)
|
| 8012 |
7996 |
|
/// holds nodes rather than rows. Written down so the next container to
|
| 8018 |
8002 |
|
/// could not tell the control belonged to the table above it.
|
| 8019 |
8003 |
|
Table {
|
| 8020 |
8004 |
|
/// The columns, in order. A cell keyed by position answers these in order.
|
|
8005 |
+ |
///
|
|
8006 |
+ |
/// **Empty is a list.** That is the whole of what a list is since the
|
|
8007 |
+ |
/// 2026-09-06 collapse, and it is a fact the rows already carried rather
|
|
8008 |
+ |
/// than a flag added beside them: a row whose cells are keyed by
|
|
8009 |
+ |
/// [`CellKey::Role`] answers the default column set, and a table that
|
|
8010 |
+ |
/// declared no columns of its own is a table using that set. So there is
|
|
8011 |
+ |
/// no `look` member and no second variant to keep in step.
|
|
8012 |
+ |
///
|
|
8013 |
+ |
/// A renderer reads it to choose an arrangement, not to choose a
|
|
8014 |
+ |
/// meaning. `columns.is_empty()` draws the flowed, one-per-line form a
|
|
8015 |
+ |
/// list has always drawn -- `<ul>` in a webview -- and a declared column
|
|
8016 |
+ |
/// list draws a grid. Both are the same node holding the same rows.
|
| 8021 |
8017 |
|
columns: Vec<Column>,
|
| 8022 |
8018 |
|
/// The rows, in order.
|
| 8023 |
8019 |
|
rows: Vec<Row>,
|
| 8024 |
8020 |
|
/// What is not shown, if anything is.
|
|
8021 |
+ |
///
|
|
8022 |
+ |
/// A described list of the first 50 of 400 tasks was indistinguishable
|
|
8023 |
+ |
/// from a described list of 50 tasks, so each app grew its own answer:
|
|
8024 |
+ |
/// goingson a 159-line pagination manager with two consumers that had
|
|
8025 |
+ |
/// each written it separately first, Balanced Breakfast four
|
|
8026 |
+ |
/// `loadMore` sites. Two idioms for one fact, and the fact is what
|
|
8027 |
+ |
/// belongs here -- how much more there is and how to ask for it.
|
|
8028 |
+ |
/// Whether that becomes numbered pages, a load-more button or an
|
|
8029 |
+ |
/// infinite scroll is the renderer's.
|
| 8025 |
8030 |
|
more: Option<Rest>,
|
| 8026 |
8031 |
|
},
|
| 8027 |
8032 |
|
/// Rows placed by when they happen, rather than in order.
|
| 8028 |
8033 |
|
///
|
| 8029 |
|
- |
/// The third of the three ways this vocabulary says "several of the same
|
|
8034 |
+ |
/// The second of the two ways this vocabulary says "several of the same
|
| 8030 |
8035 |
|
/// kind of thing", and the last one to arrive.
|
| 8031 |
|
- |
/// [`List`](Self::List) puts them in order, [`Table`](Self::Table) lines
|
| 8032 |
|
- |
/// their parts up in columns, and this one puts them on a clock.
|
|
8036 |
+ |
/// [`Table`](Self::Table) puts them in order and, when it declares columns,
|
|
8037 |
+ |
/// lines their parts up under headings; this one puts them on a clock.
|
|
8038 |
+ |
///
|
|
8039 |
+ |
/// It was the third of three until the 2026-09-06 collapse, when a list
|
|
8040 |
+ |
/// stopped being a node of its own. A timeline stayed: what it adds is
|
|
8041 |
+ |
/// [`layout::Placement`] on every entry, which is a fact a `Row` does not
|
|
8042 |
+ |
/// carry and a column cannot supply.
|
| 8033 |
8043 |
|
///
|
| 8034 |
8044 |
|
/// A row here is an ordinary [`Row`] and gets no new members: the item
|
| 8035 |
8045 |
|
/// bodies on goingson's day view are a title, a time, a tag and a tone,
|
| 8090 |
8100 |
|
/// and a renderer handed one at a time cannot tell it is looking at a set.
|
| 8091 |
8101 |
|
/// The objection to that is real and is answered by what is already here: a
|
| 8092 |
8102 |
|
/// node whose value is its grouping sounds like a layout instruction, and
|
| 8093 |
|
- |
/// [`List`](Self::List) and [`Table`](Self::Table) have been exactly that
|
| 8094 |
|
- |
/// since the beginning without anyone calling them one.
|
|
8103 |
+ |
/// [`Table`](Self::Table) has been exactly that since the beginning without
|
|
8104 |
+ |
/// anyone calling it one.
|
| 8095 |
8105 |
|
///
|
| 8096 |
8106 |
|
/// # Why the action is here and not on the figure
|
| 8097 |
8107 |
|
///
|
| 8270 |
8280 |
|
// The markup is opaque and holds nothing this crate can find; what
|
| 8271 |
8281 |
|
// is walked is the app's own nodes inside the scope.
|
| 8272 |
8282 |
|
Self::Canvas(canvas) => canvas.within.iter().find_map(|node| node.holds(name)),
|
| 8273 |
|
- |
Self::List { rows, .. } => rows
|
| 8274 |
|
- |
.iter()
|
| 8275 |
|
- |
.find_map(|row| row.cells.iter().find_map(|cell| cell.content.iter().find_map(|n| n.holds(name)))),
|
| 8276 |
|
- |
Self::Table { rows, .. } => rows.iter().find_map(|cells| {
|
| 8277 |
|
- |
cells
|
| 8278 |
|
- |
.cells
|
|
8283 |
+ |
Self::Table { rows, .. } => rows.iter().find_map(|row| {
|
|
8284 |
+ |
row.cells
|
| 8279 |
8285 |
|
.iter()
|
| 8280 |
|
- |
.find_map(|cell| cell.content.iter().find_map(|part| part.holds(name)))
|
|
8286 |
+ |
.find_map(|cell| cell.content.iter().find_map(|node| node.holds(name)))
|
| 8281 |
8287 |
|
}),
|
| 8282 |
8288 |
|
Self::Timeline { entries, .. } => entries.iter().find_map(|placed| {
|
| 8283 |
8289 |
|
placed
|
| 8325 |
8331 |
|
}
|
| 8326 |
8332 |
|
Self::Form { fields, .. } => found.extend(fields),
|
| 8327 |
8333 |
|
Self::Region(slot) => found.extend(slot.questions()),
|
| 8328 |
|
- |
Self::List { rows, .. } => {
|
|
8334 |
+ |
Self::Table { rows, .. } => {
|
| 8329 |
8335 |
|
for row in rows {
|
| 8330 |
8336 |
|
for cell in &row.cells {
|
| 8331 |
8337 |
|
for node in &cell.content {
|
| 8334 |
8340 |
|
}
|
| 8335 |
8341 |
|
}
|
| 8336 |
8342 |
|
}
|
| 8337 |
|
- |
Self::Table { rows, .. } => {
|
| 8338 |
|
- |
for cells in rows {
|
| 8339 |
|
- |
for cell in &cells.cells {
|
| 8340 |
|
- |
for part in &cell.content {
|
| 8341 |
|
- |
part.questions(found);
|
| 8342 |
|
- |
}
|
| 8343 |
|
- |
}
|
| 8344 |
|
- |
}
|
| 8345 |
|
- |
}
|
| 8346 |
8343 |
|
Self::Timeline { entries, .. } => {
|
| 8347 |
8344 |
|
for placed in entries {
|
| 8348 |
8345 |
|
for cell in &placed.row.cells {
|
| 8393 |
8390 |
|
// A control inside the markup is the creator's and carries no id
|
| 8394 |
8391 |
|
// this crate handed out, so only the app's own nodes are walked.
|
| 8395 |
8392 |
|
Self::Canvas(canvas) => canvas.within.iter().any(|node| node.names(id)),
|
| 8396 |
|
- |
Self::List { rows, .. } => rows.iter().any(|row| row.names(id)),
|
| 8397 |
|
- |
Self::Table { rows, .. } => rows.iter().any(|cells| {
|
| 8398 |
|
- |
cells.menu.iter().any(|act| act.id.as_deref() == Some(id))
|
| 8399 |
|
- |
|| cells
|
| 8400 |
|
- |
.cells
|
| 8401 |
|
- |
.iter()
|
| 8402 |
|
- |
.any(|cell| cell.content.iter().any(|part| part.names(id)))
|
| 8403 |
|
- |
}),
|
|
8393 |
+ |
Self::Table { rows, .. } => rows.iter().any(|row| row.names(id)),
|
| 8404 |
8394 |
|
Self::Timeline { entries, .. } => entries.iter().any(|placed| placed.row.names(id)),
|
| 8405 |
8395 |
|
// Everything with no control under it. Written out rather than left
|
| 8406 |
8396 |
|
// to a wildcard, so a node kind that gains one stops compiling here.
|
| 8644 |
8634 |
|
}
|
| 8645 |
8635 |
|
|
| 8646 |
8636 |
|
/// A list of rows.
|
|
8637 |
+ |
///
|
|
8638 |
+ |
/// A [`Table`](Self::Table) that declares no columns, which is what a list
|
|
8639 |
+ |
/// is since the 2026-09-06 collapse. The constructor stays because "a list
|
|
8640 |
+ |
/// of rows" is what the caller means and `Table { columns: vec![], .. }` is
|
|
8641 |
+ |
/// how the vocabulary spells it, not something every caller should have to
|
|
8642 |
+ |
/// spell.
|
| 8647 |
8643 |
|
pub fn list(rows: impl IntoIterator<Item = Row>) -> Self {
|
| 8648 |
|
- |
Self::List {
|
|
8644 |
+ |
Self::Table {
|
|
8645 |
+ |
columns: Vec::new(),
|
| 8649 |
8646 |
|
rows: rows.into_iter().collect(),
|
| 8650 |
8647 |
|
more: None,
|
| 8651 |
8648 |
|
}
|
| 8653 |
8650 |
|
|
| 8654 |
8651 |
|
/// The same list, saying there is more of it.
|
| 8655 |
8652 |
|
///
|
| 8656 |
|
- |
/// A no-op on anything that is not a [`Self::List`], which is the one place
|
|
8653 |
+ |
/// A no-op on anything that is not a [`Self::Table`], which is the one place
|
| 8657 |
8654 |
|
/// this file allows that: the alternative is a constructor taking rows and a
|
| 8658 |
8655 |
|
/// `Rest` together, and every call site that has no more rows then passes a
|
| 8659 |
8656 |
|
/// `None` to say so.
|
| 8660 |
8657 |
|
#[must_use]
|
| 8661 |
8658 |
|
pub fn and_more(mut self, rest: Rest) -> Self {
|
| 8662 |
|
- |
if let Self::List { more, .. } = &mut self {
|
|
8659 |
+ |
if let Self::Table { more, .. } = &mut self {
|
| 8663 |
8660 |
|
*more = Some(rest);
|
| 8664 |
8661 |
|
}
|
| 8665 |
8662 |
|
self
|
| 8845 |
8842 |
|
return;
|
| 8846 |
8843 |
|
}
|
| 8847 |
8844 |
|
match self {
|
| 8848 |
|
- |
Self::List { rows, .. } => {
|
| 8849 |
|
- |
for row in rows {
|
| 8850 |
|
- |
row.clocks_into(found);
|
| 8851 |
|
- |
}
|
| 8852 |
|
- |
}
|
| 8853 |
8845 |
|
Self::Timeline { entries, .. } => {
|
| 8854 |
8846 |
|
for placed in entries {
|
| 8855 |
8847 |
|
placed.row.clocks_into(found);
|
| 8856 |
8848 |
|
}
|
| 8857 |
8849 |
|
}
|
| 8858 |
8850 |
|
Self::Table { rows, .. } => {
|
| 8859 |
|
- |
for cell in rows.iter().flat_map(|row| &row.cells) {
|
| 8860 |
|
- |
for part in &cell.content {
|
| 8861 |
|
- |
part.clocks_into(found);
|
| 8862 |
|
- |
}
|
|
8851 |
+ |
for row in rows {
|
|
8852 |
+ |
row.clocks_into(found);
|
| 8863 |
8853 |
|
}
|
| 8864 |
8854 |
|
}
|
| 8865 |
8855 |
|
Self::StandIn { .. } => {}
|
| 11223 |
11213 |
|
)),
|
| 11224 |
11214 |
|
);
|
| 11225 |
11215 |
|
let screen = Screen::new("Task", layout::Arrangement::sidebar_content()).with(
|
| 11226 |
|
- |
Slot::group("body").with(Node::List {
|
|
11216 |
+ |
Slot::group("body").with(Node::Table {
|
|
11217 |
+ |
columns: Vec::new(),
|
| 11227 |
11218 |
|
rows: vec![row],
|
| 11228 |
11219 |
|
more: None,
|
| 11229 |
11220 |
|
}),
|