max / audiofiles
5 files changed,
+88 insertions,
-19 deletions
| @@ -66,7 +66,7 @@ | |||
| 66 | 66 | use quasi_router::layout::{FieldKind, Notice, Selector, Tone}; | |
| 67 | 67 | use quasi_router::{ | |
| 68 | 68 | Act, Action, Cell, Cells, Choice, Column, Field, Node, Outcome, RegionKind, Request, Response, | |
| 69 | - | RouteError, Router, Screen, Slot, Tag, | |
| 69 | + | Rest, RouteError, Router, Screen, Slot, Tag, | |
| 70 | 70 | }; | |
| 71 | 71 | ||
| 72 | 72 | use super::{Chosen, Panels}; | |
| @@ -122,12 +122,14 @@ | |||
| 122 | 122 | /// cost — "egui materialises every cell every frame; for a 500-row rename this | |
| 123 | 123 | /// matters" — which is renderer policy and not the description's business. Here | |
| 124 | 124 | /// it is honesty: a described list of the first fifty of five hundred is | |
| 125 | - | /// otherwise indistinguishable from a list of fifty. | |
| 125 | + | /// otherwise indistinguishable from a list of fifty, and [`Rest`] is what says | |
| 126 | + | /// which it is. | |
| 126 | 127 | /// | |
| 127 | - | /// `Node::List` has `more: Option<Rest>` for exactly this and **`Node::Table` | |
| 128 | - | /// has nothing**, which is why the preview table says its overflow in prose | |
| 129 | - | /// beside itself and the chosen-names list does the same. A small gap, noted | |
| 130 | - | /// rather than filed: the fix is the member `List` already has. | |
| 128 | + | /// This port was written against 0.14, where `Node::List` had `more` and | |
| 129 | + | /// `Node::Table` had nothing, so both overflows were sentences at the end of a | |
| 130 | + | /// list. **0.15 gave `Table` the same member** and both now say it properly. | |
| 131 | + | /// Neither carries a `forward`: the cap is a rendering budget, the operation | |
| 132 | + | /// acts on every chosen item either way, and there is no next page to ask for. | |
| 131 | 133 | const SHOWN: usize = 50; | |
| 132 | 134 | ||
| 133 | 135 | /// Register the three modals' routes. | |
| @@ -295,6 +297,9 @@ | |||
| 295 | 297 | .with(Node::Table { | |
| 296 | 298 | columns: vec![Column::new("Folder")], | |
| 297 | 299 | rows, | |
| 300 | + | // Every folder in the vault, because a destination the picker does | |
| 301 | + | // not show is a destination you cannot choose. | |
| 302 | + | more: None, | |
| 298 | 303 | }); | |
| 299 | 304 | ||
| 300 | 305 | closing(subjects(body, &chosen.names)) | |
| @@ -422,8 +427,19 @@ | |||
| 422 | 427 | *seen.entry(new.as_str()).or_insert(0) += 1; | |
| 423 | 428 | } | |
| 424 | 429 | ||
| 430 | + | let total = previews.len(); | |
| 425 | 431 | Node::Table { | |
| 426 | 432 | columns: vec![Column::new("Old"), Column::new("New")], | |
| 433 | + | // Said rather than implied, as of quasi 0.15: a described table of the | |
| 434 | + | // first fifty of five hundred was indistinguishable from a table of | |
| 435 | + | // fifty until `Node::Table` grew `more`, which is the gap this port | |
| 436 | + | // noted at 65abb3c. No `forward`, because there is nowhere to ask -- | |
| 437 | + | // the cap is a rendering budget and the rename acts on all of them. | |
| 438 | + | more: (total > SHOWN).then(|| Rest { | |
| 439 | + | paging: quasi_router::layout::Paging::more(SHOWN).of(total), | |
| 440 | + | forward: None, | |
| 441 | + | back: None, | |
| 442 | + | }), | |
| 427 | 443 | rows: previews | |
| 428 | 444 | .iter() | |
| 429 | 445 | .take(SHOWN) | |
| @@ -449,16 +465,23 @@ | |||
| 449 | 465 | /// Every name the operation touches. | |
| 450 | 466 | /// | |
| 451 | 467 | /// The shipped modals each scroll this list at a fixed height; how much of it | |
| 452 | - | /// fits is the host's, and how many there are is the description's. | |
| 468 | + | /// fits is the host's, and how many there are is the description's. `Rest` is | |
| 469 | + | /// what says the second part, so the overflow is a described fact rather than a | |
| 470 | + | /// sentence at the end of the list. | |
| 453 | 471 | fn subjects(body: Slot, names: &[String]) -> Slot { | |
| 454 | - | let mut body = body.with(Node::section(format!("{} chosen", names.len()))); | |
| 455 | - | for name in names.iter().take(SHOWN) { | |
| 456 | - | body = body.with(Node::text(name)); | |
| 457 | - | } | |
| 458 | - | if names.len() > SHOWN { | |
| 459 | - | body = body.with(Node::text(format!("...and {} more", names.len() - SHOWN))); | |
| 460 | - | } | |
| 461 | - | body | |
| 472 | + | body.with(Node::section(format!("{} chosen", names.len()))) | |
| 473 | + | .with(Node::List { | |
| 474 | + | rows: names | |
| 475 | + | .iter() | |
| 476 | + | .take(SHOWN) | |
| 477 | + | .map(quasi_router::Row::new) | |
| 478 | + | .collect(), | |
| 479 | + | more: (names.len() > SHOWN).then(|| Rest { | |
| 480 | + | paging: quasi_router::layout::Paging::more(SHOWN).of(names.len()), | |
| 481 | + | forward: None, | |
| 482 | + | back: None, | |
| 483 | + | }), | |
| 484 | + | }) | |
| 462 | 485 | } | |
| 463 | 486 | ||
| 464 | 487 | /// The screen a finished modal leaves behind. |
| @@ -278,6 +278,8 @@ | |||
| 278 | 278 | .into_iter() | |
| 279 | 279 | .map(|(field, value)| Cells::new(vec![Cell::new(field), Cell::new(value)])) | |
| 280 | 280 | .collect(), | |
| 281 | + | // Nine fields at most, and every one that was found is here. | |
| 282 | + | more: None, | |
| 281 | 283 | }) | |
| 282 | 284 | } | |
| 283 | 285 | ||
| @@ -473,6 +475,7 @@ | |||
| 473 | 475 | .into_iter() | |
| 474 | 476 | .map(|(field, value)| Cells::new(vec![Cell::new(field), Cell::new(reads(value))])) | |
| 475 | 477 | .collect(), | |
| 478 | + | more: None, | |
| 476 | 479 | }) | |
| 477 | 480 | } | |
| 478 | 481 |
| @@ -143,6 +143,10 @@ | |||
| 143 | 143 | .iter() | |
| 144 | 144 | .map(|sample| row(sample, shown, current)) | |
| 145 | 145 | .collect(), | |
| 146 | + | // Everything the app has loaded and filtered is here. Windowing | |
| 147 | + | // rows it already holds is a renderer's job, which is the note in | |
| 148 | + | // this module's header. | |
| 149 | + | more: None, | |
| 146 | 150 | }) | |
| 147 | 151 | }; | |
| 148 | 152 |
| @@ -175,6 +175,10 @@ | |||
| 175 | 175 | .activate(binding.action.clone()) | |
| 176 | 176 | }) | |
| 177 | 177 | .collect(), | |
| 178 | + | // Every key that is bound is listed, which is the whole claim of this | |
| 179 | + | // screen. A shortcuts table with something withheld would be the drift | |
| 180 | + | // it exists to end. | |
| 181 | + | more: None, | |
| 178 | 182 | } | |
| 179 | 183 | } | |
| 180 | 184 |
| @@ -286,7 +286,9 @@ | |||
| 286 | 286 | fn find(body: &[Node]) -> Option<(Vec<quasi_router::Column>, Vec<quasi_router::Cells>)> { | |
| 287 | 287 | for node in body { | |
| 288 | 288 | match node { | |
| 289 | - | Node::Table { columns, rows } => return Some((columns.clone(), rows.clone())), | |
| 289 | + | Node::Table { columns, rows, .. } => { | |
| 290 | + | return Some((columns.clone(), rows.clone())); | |
| 291 | + | } | |
| 290 | 292 | Node::Region(slot) => { | |
| 291 | 293 | if let Some(found) = find(&slot.body) { | |
| 292 | 294 | return Some(found); | |
| @@ -2354,7 +2356,10 @@ | |||
| 2354 | 2356 | let screen = overlay(&response); | |
| 2355 | 2357 | ||
| 2356 | 2358 | assert!(said(screen).contains("Tag 2 samples")); | |
| 2357 | - | assert!(said(screen).contains("kick.wav")); | |
| 2359 | + | // The subjects are rows rather than prose, so a host can scroll them as a | |
| 2360 | + | // list and the description can say how many were withheld. | |
| 2361 | + | let named: Vec<String> = list_of(screen).iter().filter_map(primary_of).collect(); | |
| 2362 | + | assert_eq!(named, ["kick.wav", "snare.wav"]); | |
| 2358 | 2363 | ||
| 2359 | 2364 | // The known tags, as badges. THE FINDING is that a field cannot say what | |
| 2360 | 2365 | // completes it, so the set is named beside it -- the same workaround the | |
| @@ -2633,8 +2638,19 @@ | |||
| 2633 | 2638 | let bulk = FakeBulk::of(&names, 60); | |
| 2634 | 2639 | let response = bulking(&bulk, Request::get("/bulk/tag")).unwrap(); | |
| 2635 | 2640 | ||
| 2636 | - | assert!(said(overlay(&response)).contains("60 chosen")); | |
| 2637 | - | assert!(said(overlay(&response)).contains("and 10 more")); | |
| 2641 | + | let screen = overlay(&response); | |
| 2642 | + | assert!(said(screen).contains("60 chosen")); | |
| 2643 | + | ||
| 2644 | + | // The overflow is a described fact as of quasi 0.15, not a sentence at the | |
| 2645 | + | // end of the list: fifty rows, and `Rest` saying there are sixty. | |
| 2646 | + | let rows = list_of(screen); | |
| 2647 | + | assert_eq!(rows.len(), 50); | |
| 2648 | + | let more = more_of(screen).expect("the list says it is not all of them"); | |
| 2649 | + | assert_eq!(more.paging.window.of, Some(60)); | |
| 2650 | + | assert_eq!(more.paging.window.count, 50); | |
| 2651 | + | // Nothing to ask for: the cap is a rendering budget and the operation acts | |
| 2652 | + | // on all sixty either way. | |
| 2653 | + | assert!(more.forward.is_none()); | |
| 2638 | 2654 | } | |
| 2639 | 2655 | ||
| 2640 | 2656 | // The help overlay. | |
| @@ -2790,3 +2806,22 @@ | |||
| 2790 | 2806 | }); | |
| 2791 | 2807 | assert_eq!(chosen.as_deref(), Some("shortcuts")); | |
| 2792 | 2808 | } | |
| 2809 | + | ||
| 2810 | + | /// A row's primary part. | |
| 2811 | + | fn primary_of(row: &quasi_router::Row) -> Option<String> { | |
| 2812 | + | row.parts | |
| 2813 | + | .iter() | |
| 2814 | + | .find(|part| part.role == quasi_router::layout::RowPart::Primary) | |
| 2815 | + | .and_then(|part| match &part.node { | |
| 2816 | + | Node::Text { text, .. } => Some(text.clone()), | |
| 2817 | + | _ => None, | |
| 2818 | + | }) | |
| 2819 | + | } | |
| 2820 | + | ||
| 2821 | + | /// What the list on a screen says it is not showing. | |
| 2822 | + | fn more_of(screen: &Screen) -> Option<quasi_router::Rest> { | |
| 2823 | + | nodes(screen).iter().find_map(|node| match node { | |
| 2824 | + | Node::List { more, .. } => more.clone(), | |
| 2825 | + | _ => None, | |
| 2826 | + | }) | |
| 2827 | + | } |