| 56 |
56 |
|
//!
|
| 57 |
57 |
|
//! # One more thing this screen could not say
|
| 58 |
58 |
|
//!
|
| 59 |
|
- |
//! - **A `Binding` has no group.** The shipped tab sorts twenty-six rows into
|
| 60 |
|
- |
//! Navigation, Selection, Bulk, Search, Discovery, Toggles and System, which
|
| 61 |
|
- |
//! for a list that long is the difference between a reference and a wall. The
|
| 62 |
|
- |
//! described one is flat, and at thirteen rows it has started to want them.
|
|
59 |
+ |
//! - **A `Binding` had no group, and now has one.** The shipped tab sorts
|
|
60 |
+ |
//! twenty-six rows into Navigation, Selection, Bulk, Search, Discovery,
|
|
61 |
+ |
//! Toggles and System, which for a list that long is the difference between a
|
|
62 |
+ |
//! reference and a wall. The described one was flat, and at thirteen rows it
|
|
63 |
+ |
//! started to want them. `cf7872dc` answered it: `Chrome::bind_in` says the
|
|
64 |
+ |
//! heading and `Chrome::grouped` gathers the runs, so the table below is four
|
|
65 |
+ |
//! sections in the order this file bound them. Three of the shipped headings
|
|
66 |
+ |
//! have no rows here, which is the nine-of-sixteen above rather than anything
|
|
67 |
+ |
//! the member cannot say.
|
| 63 |
68 |
|
//! - **An action cannot sit inside a sentence.** The features tab writes "Use
|
| 64 |
69 |
|
//! `/` to focus the search bar" with `/` as a live link that closes the help
|
| 65 |
70 |
|
//! and focuses the field. `Node::Link` is a leaf and prose is a `Node::Text`,
|
| 88 |
93 |
|
const BODY: &str = "help-body";
|
| 89 |
94 |
|
/// The region a tab's contents land in.
|
| 90 |
95 |
|
const TAB: &str = "help-tab";
|
|
96 |
+ |
/// The region the grouped key tables sit in.
|
|
97 |
+ |
///
|
|
98 |
+ |
/// A region rather than a bare run because a tab's contents are one node, and
|
|
99 |
+ |
/// grouping turned this tab from one table into a heading and a table per
|
|
100 |
+ |
/// group.
|
|
101 |
+ |
const SHORTCUTS_BODY: &str = "help-shortcuts";
|
| 91 |
102 |
|
|
| 92 |
103 |
|
/// Which tab is showing.
|
| 93 |
104 |
|
const SHORTCUTS: &str = "shortcuts";
|
| 106 |
117 |
|
#[must_use]
|
| 107 |
118 |
|
pub fn chrome() -> Chrome {
|
| 108 |
119 |
|
Chrome::new()
|
| 109 |
|
- |
.bind("f1", "Show this help", Action::get("/help"))
|
| 110 |
|
- |
.bind("f2", "Rename the selection", Action::get("/bulk/rename"))
|
| 111 |
|
- |
.bind("ctrl+t", "Tag the selection", Action::get("/bulk/tag"))
|
| 112 |
|
- |
.bind(
|
|
120 |
+ |
// Four of the shipped tab's seven headings, in its order. `cf7872dc`
|
|
121 |
+ |
// landed `Chrome::bind_in`, and the reason this table wanted it is the
|
|
122 |
+ |
// reason the shipped one has arrays: thirteen rows read as a wall, and
|
|
123 |
+ |
// a reader looking for the tagging key should not have to scan past the
|
|
124 |
+ |
// panel toggles to find it.
|
|
125 |
+ |
//
|
|
126 |
+ |
// Navigation, Selection and Search have no rows here. See this module's
|
|
127 |
+ |
// header for the seven shortcuts that are not the guard's business, and
|
|
128 |
+ |
// for `/` -- the one absence that is a gap rather than a shape.
|
|
129 |
+ |
.bind_in(
|
|
130 |
+ |
BULK,
|
|
131 |
+ |
"f2",
|
|
132 |
+ |
"Rename the selection",
|
|
133 |
+ |
Action::get("/bulk/rename"),
|
|
134 |
+ |
)
|
|
135 |
+ |
.bind_in(
|
|
136 |
+ |
BULK,
|
|
137 |
+ |
"ctrl+t",
|
|
138 |
+ |
"Tag the selection",
|
|
139 |
+ |
Action::get("/bulk/tag"),
|
|
140 |
+ |
)
|
|
141 |
+ |
.bind_in(
|
|
142 |
+ |
BULK,
|
| 113 |
143 |
|
"ctrl+shift+m",
|
| 114 |
144 |
|
"Move the selection",
|
| 115 |
145 |
|
Action::get("/bulk/move"),
|
| 116 |
146 |
|
)
|
| 117 |
|
- |
.bind("ctrl+z", "Undo the last bulk action", Action::post("/undo"))
|
| 118 |
|
- |
// The five panels the toolbar toggles, by the name an address is built
|
| 119 |
|
- |
// from. `Panel::as_str` is that name, so a panel renamed here and there
|
| 120 |
|
- |
// is one edit rather than two.
|
| 121 |
|
- |
.bind("s", "Toggle the sidebar", toggling(Panel::Sidebar))
|
| 122 |
|
- |
.bind("d", "Toggle the detail panel", toggling(Panel::Detail))
|
| 123 |
|
- |
.bind("e", "Toggle the sample editor", toggling(Panel::Edit))
|
| 124 |
|
- |
.bind(
|
| 125 |
|
- |
"i",
|
| 126 |
|
- |
"Toggle the instrument panel",
|
| 127 |
|
- |
toggling(Panel::Instrument),
|
|
147 |
+ |
.bind_in(
|
|
148 |
+ |
BULK,
|
|
149 |
+ |
"ctrl+z",
|
|
150 |
+ |
"Undo the last bulk action",
|
|
151 |
+ |
Action::post("/undo"),
|
| 128 |
152 |
|
)
|
| 129 |
|
- |
.bind("l", "Toggle loop", toggling(Panel::Loop))
|
| 130 |
|
- |
// The forge is a screen rather than a panel, which is why this one is
|
| 131 |
|
- |
// not built the same way as the five above it.
|
| 132 |
|
- |
.bind("f", "Open the sample forge", Action::get("/forge"))
|
| 133 |
|
- |
// Shift's two, and the pair that made the renderer's matching exact:
|
| 134 |
|
- |
// `shift+f` sits directly behind `f` in this table.
|
| 135 |
|
- |
.bind(
|
|
153 |
+ |
// Shift's two. They were declared beside `f` while this table was flat,
|
|
154 |
+ |
// because `shift+f` sitting directly behind it is what made the
|
|
155 |
+ |
// renderer's matching exact (quasi-immediate 0.52.0); the grouping
|
|
156 |
+ |
// separates them and the collision is unchanged, since matching reads
|
|
157 |
+ |
// the key and not the neighbour.
|
|
158 |
+ |
.bind_in(
|
|
159 |
+ |
DISCOVERY,
|
| 136 |
160 |
|
"shift+f",
|
| 137 |
161 |
|
"Find similar samples",
|
| 138 |
162 |
|
Action::post("/detail/similar"),
|
| 139 |
163 |
|
)
|
| 140 |
|
- |
.bind(
|
|
164 |
+ |
.bind_in(
|
|
165 |
+ |
DISCOVERY,
|
| 141 |
166 |
|
"shift+d",
|
| 142 |
167 |
|
"Find duplicates",
|
| 143 |
168 |
|
Action::post("/detail/duplicates"),
|
| 144 |
169 |
|
)
|
|
170 |
+ |
// The five panels the toolbar toggles, by the name an address is built
|
|
171 |
+ |
// from. `Panel::as_str` is that name, so a panel renamed here and there
|
|
172 |
+ |
// is one edit rather than two.
|
|
173 |
+ |
.bind_in(TOGGLES, "s", "Toggle the sidebar", toggling(Panel::Sidebar))
|
|
174 |
+ |
.bind_in(
|
|
175 |
+ |
TOGGLES,
|
|
176 |
+ |
"d",
|
|
177 |
+ |
"Toggle the detail panel",
|
|
178 |
+ |
toggling(Panel::Detail),
|
|
179 |
+ |
)
|
|
180 |
+ |
.bind_in(
|
|
181 |
+ |
TOGGLES,
|
|
182 |
+ |
"e",
|
|
183 |
+ |
"Toggle the sample editor",
|
|
184 |
+ |
toggling(Panel::Edit),
|
|
185 |
+ |
)
|
|
186 |
+ |
.bind_in(
|
|
187 |
+ |
TOGGLES,
|
|
188 |
+ |
"i",
|
|
189 |
+ |
"Toggle the instrument panel",
|
|
190 |
+ |
toggling(Panel::Instrument),
|
|
191 |
+ |
)
|
|
192 |
+ |
.bind_in(TOGGLES, "l", "Toggle loop", toggling(Panel::Loop))
|
|
193 |
+ |
// The forge is a screen rather than a panel, which is why this one is
|
|
194 |
+ |
// not built the same way as the five above it. It is still a Toggle to
|
|
195 |
+ |
// a reader, which is the whole reason the group is said rather than
|
|
196 |
+ |
// derived from the address: `/panels/sidebar` and `/forge` are siblings
|
|
197 |
+ |
// in nothing.
|
|
198 |
+ |
.bind_in(TOGGLES, "f", "Open the sample forge", Action::get("/forge"))
|
|
199 |
+ |
.bind_in(SYSTEM, "f1", "Show this help", Action::get("/help"))
|
| 145 |
200 |
|
}
|
| 146 |
201 |
|
|
|
202 |
+ |
/// The shipped tab's headings, for the four this table has rows under.
|
|
203 |
+ |
///
|
|
204 |
+ |
/// Named rather than written at each call site: a heading spelt two ways is two
|
|
205 |
+ |
/// groups, and the failure is a table that reads almost right.
|
|
206 |
+ |
const BULK: &str = "Bulk";
|
|
207 |
+ |
/// Finding samples by what they are like.
|
|
208 |
+ |
const DISCOVERY: &str = "Discovery";
|
|
209 |
+ |
/// Showing and hiding the app's own furniture.
|
|
210 |
+ |
const TOGGLES: &str = "Toggles";
|
|
211 |
+ |
/// The app itself.
|
|
212 |
+ |
const SYSTEM: &str = "System";
|
|
213 |
+ |
|
| 147 |
214 |
|
/// Toggling one panel, addressed by the name the panel answers to.
|
| 148 |
215 |
|
///
|
| 149 |
216 |
|
/// Named for what it does rather than for its argument, so it does not read as
|
| 213 |
280 |
|
/// what a host does, which is the rule the bulk port's tag completions and
|
| 214 |
281 |
|
/// folder filter both follow.
|
| 215 |
282 |
|
fn shortcuts() -> Node {
|
| 216 |
|
- |
Node::Table {
|
| 217 |
|
- |
columns: vec![Column::new("Key"), Column::new("Does")],
|
| 218 |
|
- |
rows: chrome()
|
| 219 |
|
- |
.bindings
|
| 220 |
|
- |
.iter()
|
| 221 |
|
- |
.map(|binding| {
|
| 222 |
|
- |
Cells::new(vec![Cell::new(&binding.key), Cell::new(&binding.label)])
|
| 223 |
|
- |
.activate(binding.action.clone())
|
| 224 |
|
- |
})
|
| 225 |
|
- |
.collect(),
|
| 226 |
|
- |
// Every key that is bound is listed, which is the whole claim of this
|
| 227 |
|
- |
// screen. A shortcuts table with something withheld would be the drift
|
| 228 |
|
- |
// it exists to end.
|
| 229 |
|
- |
more: None,
|
|
283 |
+ |
// A heading and a table per group, in the order the app bound them, which
|
|
284 |
+ |
// is what `Chrome::grouped` answers. Not sorted here: the reading order is
|
|
285 |
+ |
// the shipped tab's and belongs to whoever wrote the table.
|
|
286 |
+ |
//
|
|
287 |
+ |
// Several tables rather than one with a group column, because a group is a
|
|
288 |
+ |
// heading and not a value: a column repeating "Toggles" six times says the
|
|
289 |
+ |
// same thing six times and is still one wall.
|
|
290 |
+ |
let mut sections = Vec::new();
|
|
291 |
+ |
for (group, bindings) in chrome().grouped() {
|
|
292 |
+ |
// `None` is the ungrouped run, which this table has none of today and
|
|
293 |
+ |
// would have again the moment somebody added a `bind`. Drawn without a
|
|
294 |
+ |
// heading rather than under an invented one.
|
|
295 |
+ |
if let Some(name) = group {
|
|
296 |
+ |
sections.push(Node::section(name));
|
|
297 |
+ |
}
|
|
298 |
+ |
sections.push(Node::Table {
|
|
299 |
+ |
columns: vec![Column::new("Key"), Column::new("Does")],
|
|
300 |
+ |
rows: bindings
|
|
301 |
+ |
.iter()
|
|
302 |
+ |
.map(|binding| {
|
|
303 |
+ |
Cells::new(vec![Cell::new(&binding.key), Cell::new(&binding.label)])
|
|
304 |
+ |
.activate(binding.action.clone())
|
|
305 |
+ |
})
|
|
306 |
+ |
.collect(),
|
|
307 |
+ |
// Every key that is bound is listed, which is the whole claim of
|
|
308 |
+ |
// this screen. A shortcuts table with something withheld would be
|
|
309 |
+ |
// the drift it exists to end.
|
|
310 |
+ |
more: None,
|
|
311 |
+ |
});
|
| 230 |
312 |
|
}
|
|
313 |
+ |
let mut body = Slot::new(SHORTCUTS_BODY, RegionKind::Group);
|
|
314 |
+ |
for node in sections {
|
|
315 |
+ |
body = body.with(node);
|
|
316 |
+ |
}
|
|
317 |
+ |
Node::Region(body)
|
| 231 |
318 |
|
}
|
| 232 |
319 |
|
|
| 233 |
320 |
|
/// What the app does, in prose.
|