max / audiofiles
15 files changed,
+94 insertions,
-94 deletions
| @@ -4256,7 +4256,7 @@ | |||
| 4256 | 4256 | ||
| 4257 | 4257 | [[package]] | |
| 4258 | 4258 | name = "quasi-immediate" | |
| 4259 | - | version = "0.75.0" | |
| 4259 | + | version = "0.76.0" | |
| 4260 | 4260 | dependencies = [ | |
| 4261 | 4261 | "docengine", | |
| 4262 | 4262 | "egui", | |
| @@ -4267,7 +4267,7 @@ | |||
| 4267 | 4267 | ||
| 4268 | 4268 | [[package]] | |
| 4269 | 4269 | name = "quasi-router" | |
| 4270 | - | version = "0.75.0" | |
| 4270 | + | version = "0.76.0" | |
| 4271 | 4271 | dependencies = [ | |
| 4272 | 4272 | "makeover-layout", | |
| 4273 | 4273 | ] | |
| @@ -7556,34 +7556,6 @@ | |||
| 7556 | 7556 | "winnow 1.0.4", | |
| 7557 | 7557 | ] | |
| 7558 | 7558 | ||
| 7559 | - | [[patch.unused]] | |
| 7560 | - | name = "quasi-axum" | |
| 7561 | - | version = "0.75.0" | |
| 7562 | - | ||
| 7563 | - | [[patch.unused]] | |
| 7564 | - | name = "quasi-basics" | |
| 7565 | - | version = "0.75.0" | |
| 7566 | - | ||
| 7567 | - | [[patch.unused]] | |
| 7568 | - | name = "quasi-http" | |
| 7569 | - | version = "0.75.0" | |
| 7570 | - | ||
| 7571 | - | [[patch.unused]] | |
| 7572 | - | name = "quasi-notifs" | |
| 7573 | - | version = "0.75.0" | |
| 7574 | - | ||
| 7575 | - | [[patch.unused]] | |
| 7576 | - | name = "quasi-store" | |
| 7577 | - | version = "0.1.0" | |
| 7578 | - | ||
| 7579 | - | [[patch.unused]] | |
| 7580 | - | name = "quasi-tauri" | |
| 7581 | - | version = "0.75.0" | |
| 7582 | - | ||
| 7583 | - | [[patch.unused]] | |
| 7584 | - | name = "quasi-webview" | |
| 7585 | - | version = "0.75.0" | |
| 7586 | - | ||
| 7587 | 7559 | [[patch.unused]] | |
| 7588 | 7560 | name = "kberg" | |
| 7589 | 7561 | version = "0.1.0" | |
| @@ -7599,3 +7571,31 @@ | |||
| 7599 | 7571 | [[patch.unused]] | |
| 7600 | 7572 | name = "quasi-type" | |
| 7601 | 7573 | version = "0.1.2" | |
| 7574 | + | ||
| 7575 | + | [[patch.unused]] | |
| 7576 | + | name = "quasi-axum" | |
| 7577 | + | version = "0.76.0" | |
| 7578 | + | ||
| 7579 | + | [[patch.unused]] | |
| 7580 | + | name = "quasi-basics" | |
| 7581 | + | version = "0.76.0" | |
| 7582 | + | ||
| 7583 | + | [[patch.unused]] | |
| 7584 | + | name = "quasi-http" | |
| 7585 | + | version = "0.76.0" | |
| 7586 | + | ||
| 7587 | + | [[patch.unused]] | |
| 7588 | + | name = "quasi-notifs" | |
| 7589 | + | version = "0.76.0" | |
| 7590 | + | ||
| 7591 | + | [[patch.unused]] | |
| 7592 | + | name = "quasi-store" | |
| 7593 | + | version = "0.1.0" | |
| 7594 | + | ||
| 7595 | + | [[patch.unused]] | |
| 7596 | + | name = "quasi-tauri" | |
| 7597 | + | version = "0.76.0" | |
| 7598 | + | ||
| 7599 | + | [[patch.unused]] | |
| 7600 | + | name = "quasi-webview" | |
| 7601 | + | version = "0.76.0" |
| @@ -26,8 +26,8 @@ | |||
| 26 | 26 | # The described screens, which are audiofiles-browser's screens. By git URL with | |
| 27 | 27 | # a version requirement, per the tree's rule for cross-repo deps. They were | |
| 28 | 28 | # behind a `quasi` feature until 2026-08-25, when the last flip landed. | |
| 29 | - | quasi-router = { git = "https://makenot.work/git/max/quasi.git", version = "0.75" } | |
| 30 | - | quasi-immediate = { git = "https://makenot.work/git/max/quasi.git", version = "0.75" } | |
| 29 | + | quasi-router = { git = "https://makenot.work/git/max/quasi.git", version = "0.76" } | |
| 30 | + | quasi-immediate = { git = "https://makenot.work/git/max/quasi.git", version = "0.76" } | |
| 31 | 31 | egui = { version = "0.35", default-features = false, features = ["default_fonts"] } | |
| 32 | 32 | egui_extras = { version = "0.35", default-features = false } | |
| 33 | 33 | eframe = { version = "0.35", default-features = false, features = ["default_fonts", "glow"] } |
| @@ -170,7 +170,7 @@ | |||
| 170 | 170 | .with(Node::Field(Box::new( | |
| 171 | 171 | Field::new(FieldKind::Checkbox, "enabled", "Enable library mirror") | |
| 172 | 172 | .value(if mirror.enabled { "on" } else { "" }) | |
| 173 | - | .changes(Action::post("/settings/advanced/mirror/enabled")), | |
| 173 | + | .writes(Action::post("/settings/advanced/mirror/enabled")), | |
| 174 | 174 | ))) | |
| 175 | 175 | // Said before it is enabled rather than after, which is the shipped | |
| 176 | 176 | // section's own reasoning: the reader should know where the tree will |
| @@ -406,7 +406,7 @@ | |||
| 406 | 406 | Field::new(FieldKind::Text, PATTERN, "Pattern") | |
| 407 | 407 | .value(pattern) | |
| 408 | 408 | .hint("{name}_{bpm}") | |
| 409 | - | .changes(Action::post("/bulk/rename/preview")), | |
| 409 | + | .writes(Action::post("/bulk/rename/preview")), | |
| 410 | 410 | ], | |
| 411 | 411 | submit: "Rename".to_owned(), | |
| 412 | 412 | action: Action::post("/bulk/rename"), |
| @@ -69,7 +69,7 @@ | |||
| 69 | 69 | //! | |
| 70 | 70 | //! The shipped thresholds and the layer weights both commit through | |
| 71 | 71 | //! `widgets::settled`, so a drag across a slider is one write rather than one | |
| 72 | - | //! per frame. [`Field::changes`] is the only thing the vocabulary has and it | |
| 72 | + | //! per frame. [`Field::writes`] is the only thing the vocabulary has and it | |
| 73 | 73 | //! means every change. Three sliders here take it, so a drag is now a write per | |
| 74 | 74 | //! step: correct, and chattier than the shipped screen against a store that is | |
| 75 | 75 | //! local. | |
| @@ -114,7 +114,7 @@ | |||
| 114 | 114 | //! | |
| 115 | 115 | //! [`Act::confirm`]: quasi_router::Act::confirm | |
| 116 | 116 | //! [`Classifier::testable`]: super::Classifier::testable | |
| 117 | - | //! [`Field::changes`]: quasi_router::Field::changes | |
| 117 | + | //! [`Field::writes`]: quasi_router::Field::writes | |
| 118 | 118 | //! [`Field::repeats`]: quasi_router::Field::repeats | |
| 119 | 119 | //! [`Outcome::File`]: quasi_router::Outcome::File | |
| 120 | 120 | //! [`Repeat`]: quasi_router::Repeat | |
| @@ -664,7 +664,7 @@ | |||
| 664 | 664 | /// Whether a checkbox came back ticked. | |
| 665 | 665 | /// | |
| 666 | 666 | /// A renderer sends the name with a value when the box is on and sends nothing | |
| 667 | - | /// when it is off, which is the shape every `Field::changes` checkbox in this | |
| 667 | + | /// when it is off, which is the shape every `Field::writes` checkbox in this | |
| 668 | 668 | /// app already answers with. | |
| 669 | 669 | fn ticked(request: &Request, name: &str) -> bool { | |
| 670 | 670 | !request.payload.get(name).unwrap_or_default().is_empty() | |
| @@ -811,7 +811,7 @@ | |||
| 811 | 811 | fn editor(body: Slot, state: &Panels<'_>, draft: &super::Authoring) -> Slot { | |
| 812 | 812 | let mut name = Field::new(FieldKind::Text, "name", "Name") | |
| 813 | 813 | .value(draft.name.clone()) | |
| 814 | - | .changes(Action::post("/classifier/draft/name")); | |
| 814 | + | .writes(Action::post("/classifier/draft/name")); | |
| 815 | 815 | name.required = true; | |
| 816 | 816 | name.placeholder = Some("Kick drums".to_owned()); | |
| 817 | 817 | // Gated on a refusal rather than on emptiness: a freshly opened editor | |
| @@ -825,7 +825,7 @@ | |||
| 825 | 825 | .with(Node::Field(Box::new( | |
| 826 | 826 | Field::new(FieldKind::Checkbox, "enabled", "Enabled") | |
| 827 | 827 | .value(if draft.enabled { "on" } else { "" }) | |
| 828 | - | .changes(Action::post("/classifier/draft/enabled")), | |
| 828 | + | .writes(Action::post("/classifier/draft/enabled")), | |
| 829 | 829 | ))) | |
| 830 | 830 | .with(Node::Field(Box::new( | |
| 831 | 831 | Field::radio( | |
| @@ -837,7 +837,7 @@ | |||
| 837 | 837 | ], | |
| 838 | 838 | ) | |
| 839 | 839 | .value(if draft.all { "all" } else { "any" }) | |
| 840 | - | .changes(Action::post("/classifier/draft/match")), | |
| 840 | + | .writes(Action::post("/classifier/draft/match")), | |
| 841 | 841 | ))) | |
| 842 | 842 | .with(Node::section("When")); | |
| 843 | 843 | ||
| @@ -903,7 +903,7 @@ | |||
| 903 | 903 | .collect(), | |
| 904 | 904 | ) | |
| 905 | 905 | .value(condition.field.clone()) | |
| 906 | - | .changes(Action::post(format!( | |
| 906 | + | .writes(Action::post(format!( | |
| 907 | 907 | "/classifier/draft/conditions/{at}/set/field" | |
| 908 | 908 | ))); | |
| 909 | 909 | ||
| @@ -918,7 +918,7 @@ | |||
| 918 | 918 | .collect(), | |
| 919 | 919 | ) | |
| 920 | 920 | .value(condition.op.clone()) | |
| 921 | - | .changes(Action::post(format!( | |
| 921 | + | .writes(Action::post(format!( | |
| 922 | 922 | "/classifier/draft/conditions/{at}/set/op" | |
| 923 | 923 | ))); | |
| 924 | 924 | ||
| @@ -937,7 +937,7 @@ | |||
| 937 | 937 | region = region.with(Node::Field(Box::new( | |
| 938 | 938 | Field::new(FieldKind::Text, "value", "Value") | |
| 939 | 939 | .value(condition.value.clone()) | |
| 940 | - | .changes(Action::post(format!( | |
| 940 | + | .writes(Action::post(format!( | |
| 941 | 941 | "/classifier/draft/conditions/{at}/set/value" | |
| 942 | 942 | ))), | |
| 943 | 943 | ))); | |
| @@ -967,7 +967,7 @@ | |||
| 967 | 967 | ], | |
| 968 | 968 | ) | |
| 969 | 969 | .value(action.kind.clone()) | |
| 970 | - | .changes(Action::post(format!( | |
| 970 | + | .writes(Action::post(format!( | |
| 971 | 971 | "/classifier/draft/actions/{at}/set/kind" | |
| 972 | 972 | ))); | |
| 973 | 973 | ||
| @@ -977,7 +977,7 @@ | |||
| 977 | 977 | if action.kind != "stop" { | |
| 978 | 978 | let mut tag = Field::new(FieldKind::Text, "tag", "Tag") | |
| 979 | 979 | .value(action.tag.clone()) | |
| 980 | - | .changes(Action::post(format!( | |
| 980 | + | .writes(Action::post(format!( | |
| 981 | 981 | "/classifier/draft/actions/{at}/set/tag" | |
| 982 | 982 | ))); | |
| 983 | 983 | tag.placeholder = Some("instrument.drum.kick".to_owned()); | |
| @@ -1142,7 +1142,7 @@ | |||
| 1142 | 1142 | body.with(Node::Field(Box::new( | |
| 1143 | 1143 | Field::new(FieldKind::Text, "tag", "Tag to configure") | |
| 1144 | 1144 | .value(state.classifier.policy_typing()) | |
| 1145 | - | .changes(Action::post("/classifier/policies/typing")), | |
| 1145 | + | .writes(Action::post("/classifier/policies/typing")), | |
| 1146 | 1146 | ))) | |
| 1147 | 1147 | .with(Node::Act(Act::new( | |
| 1148 | 1148 | "Add threshold", | |
| @@ -1159,7 +1159,7 @@ | |||
| 1159 | 1159 | Field::range(name, label, "0", "1") | |
| 1160 | 1160 | .value(format!("{value:.2}")) | |
| 1161 | 1161 | .step("0.01") | |
| 1162 | - | .changes(Action::post(at.to_owned()).with(name, format!("{value:.2}"))) | |
| 1162 | + | .writes(Action::post(at.to_owned()).with(name, format!("{value:.2}"))) | |
| 1163 | 1163 | } | |
| 1164 | 1164 | ||
| 1165 | 1165 | /// Clustering: the cold-start grouping. | |
| @@ -1176,7 +1176,7 @@ | |||
| 1176 | 1176 | Field::range("cluster_k", "Groups", "2", "24") | |
| 1177 | 1177 | .value(state.classifier.cluster_k().to_string()) | |
| 1178 | 1178 | .step("1") | |
| 1179 | - | .changes(Action::post("/classifier/clusters/k")), | |
| 1179 | + | .writes(Action::post("/classifier/clusters/k")), | |
| 1180 | 1180 | ))) | |
| 1181 | 1181 | .with(Node::Act(unless( | |
| 1182 | 1182 | Act::new("Find clusters", Action::post("/classifier/clusters/find")), | |
| @@ -1198,7 +1198,7 @@ | |||
| 1198 | 1198 | .with(Node::Field(Box::new( | |
| 1199 | 1199 | Field::new(FieldKind::Text, "name", "Name this group") | |
| 1200 | 1200 | .value(pile.name.clone()) | |
| 1201 | - | .changes(Action::post(format!("/classifier/clusters/{at}/name"))), | |
| 1201 | + | .writes(Action::post(format!("/classifier/clusters/{at}/name"))), | |
| 1202 | 1202 | ))) | |
| 1203 | 1203 | // The representative can be gone: deleting a sample must not take | |
| 1204 | 1204 | // the pile or its name with it. | |
| @@ -1265,7 +1265,7 @@ | |||
| 1265 | 1265 | .with(Node::Field(Box::new( | |
| 1266 | 1266 | Field::new(FieldKind::Text, "tag", "Tag") | |
| 1267 | 1267 | .value(folder.tag.clone()) | |
| 1268 | - | .changes(Action::post(format!("/classifier/folders/{at}/tag"))), | |
| 1268 | + | .writes(Action::post(format!("/classifier/folders/{at}/tag"))), | |
| 1269 | 1269 | ))) | |
| 1270 | 1270 | .with(Node::Act(unless( | |
| 1271 | 1271 | Act::new( | |
| @@ -1283,7 +1283,7 @@ | |||
| 1283 | 1283 | fn export_name(current: &str) -> Field { | |
| 1284 | 1284 | let mut field = Field::new(FieldKind::Text, "export_name", "Name") | |
| 1285 | 1285 | .value(current.to_owned()) | |
| 1286 | - | .changes(Action::post("/classifier/sharing/name")); | |
| 1286 | + | .writes(Action::post("/classifier/sharing/name")); | |
| 1287 | 1287 | field.placeholder = Some("My classifier".to_owned()); | |
| 1288 | 1288 | field | |
| 1289 | 1289 | } | |
| @@ -1307,7 +1307,7 @@ | |||
| 1307 | 1307 | ] { | |
| 1308 | 1308 | let mut field = Field::new(FieldKind::Checkbox, name, label) | |
| 1309 | 1309 | .value(if part.wanted { "on" } else { "" }) | |
| 1310 | - | .changes(Action::post(format!("/classifier/sharing/include/{name}"))); | |
| 1310 | + | .writes(Action::post(format!("/classifier/sharing/include/{name}"))); | |
| 1311 | 1311 | // Said rather than silently contributing nothing: a part that is ticked | |
| 1312 | 1312 | // and does not exist is why `worth_writing` asks about both. | |
| 1313 | 1313 | if !part.available { | |
| @@ -1367,7 +1367,7 @@ | |||
| 1367 | 1367 | .with(Node::Field(Box::new( | |
| 1368 | 1368 | Field::new(FieldKind::Checkbox, "on", "Use when auto-tagging") | |
| 1369 | 1369 | .value(if layer.enabled { "on" } else { "" }) | |
| 1370 | - | .changes(Action::post(format!( | |
| 1370 | + | .writes(Action::post(format!( | |
| 1371 | 1371 | "/classifier/layers/{}/enabled", | |
| 1372 | 1372 | layer.id | |
| 1373 | 1373 | ))), | |
| @@ -1377,7 +1377,7 @@ | |||
| 1377 | 1377 | .value(format!("{:.2}", layer.weight)) | |
| 1378 | 1378 | .hint("Your own tags always count 1.0; lower means weaker.") | |
| 1379 | 1379 | .step("0.01") | |
| 1380 | - | .changes(Action::post(format!( | |
| 1380 | + | .writes(Action::post(format!( | |
| 1381 | 1381 | "/classifier/layers/{}/weight", | |
| 1382 | 1382 | layer.id | |
| 1383 | 1383 | ))), |
| @@ -70,7 +70,7 @@ | |||
| 70 | 70 | //! `Field`'s two message slots are help and error with nothing between, and this | |
| 71 | 71 | //! is its third measured site after the export screen's re-encoding warning. | |
| 72 | 72 | //! | |
| 73 | - | //! The workaround is the rename preview's: `Field::changes` answers a fragment | |
| 73 | + | //! The workaround is the rename preview's: `Field::writes` answers a fragment | |
| 74 | 74 | //! into a region beside the field. It costs a route and a region for what wants | |
| 75 | 75 | //! to be a member. | |
| 76 | 76 | //! | |
| @@ -315,7 +315,7 @@ | |||
| 315 | 315 | .value("0") | |
| 316 | 316 | // The clipping consequence, which the field cannot carry | |
| 317 | 317 | // itself. See the module header, `5672cad4`. | |
| 318 | - | .changes(Action::post("/edit/gain/preview")), | |
| 318 | + | .writes(Action::post("/edit/gain/preview")), | |
| 319 | 319 | ], | |
| 320 | 320 | submit: "Apply gain".to_owned(), | |
| 321 | 321 | action: Action::post("/edit/gain"), | |
| @@ -352,7 +352,7 @@ | |||
| 352 | 352 | ||
| 353 | 353 | /// What the gain about to be applied would do to the peak. | |
| 354 | 354 | /// | |
| 355 | - | /// Its own node so `Field::changes` can answer it as a fragment while the | |
| 355 | + | /// Its own node so `Field::writes` can answer it as a fragment while the | |
| 356 | 356 | /// control is being moved, which is the rename preview's arrangement. | |
| 357 | 357 | fn clips(peak: Option<f64>, gain: f64) -> Node { | |
| 358 | 358 | let Some(peak) = peak else { | |
| @@ -446,7 +446,7 @@ | |||
| 446 | 446 | /// What happens to the edited sample, and what happened to the last one. | |
| 447 | 447 | fn result(body: Slot, sample: &Editing) -> Slot { | |
| 448 | 448 | let mut field = | |
| 449 | - | Field::radio(RESULT, "Result", result_modes()).changes(Action::post("/edit/result")); | |
| 449 | + | Field::radio(RESULT, "Result", result_modes()).writes(Action::post("/edit/result")); | |
| 450 | 450 | if let Some(chosen) = &sample.result { | |
| 451 | 451 | field = field.value(chosen.clone()); | |
| 452 | 452 | } |
| @@ -189,7 +189,7 @@ | |||
| 189 | 189 | fn destination(_state: &Panels<'_>, _request: Request) -> Result<Response, RouteError> { | |
| 190 | 190 | Ok(Response::locate(Locating::folder( | |
| 191 | 191 | "Export Destination", | |
| 192 | - | writes(Setting::Destination), | |
| 192 | + | setting_route(Setting::Destination), | |
| 193 | 193 | Setting::Destination.as_str(), | |
| 194 | 194 | ))) | |
| 195 | 195 | } | |
| @@ -304,7 +304,7 @@ | |||
| 304 | 304 | "Include metadata (.audiofiles.json)", | |
| 305 | 305 | ) | |
| 306 | 306 | .value(if settings.sidecar { "on" } else { "" }) | |
| 307 | - | .changes(writes(Setting::Sidecar)), | |
| 307 | + | .writes(setting_route(Setting::Sidecar)), | |
| 308 | 308 | ))); | |
| 309 | 309 | ||
| 310 | 310 | if settings.flatten { | |
| @@ -563,7 +563,7 @@ | |||
| 563 | 563 | })); | |
| 564 | 564 | ||
| 565 | 565 | let mut field = Field::select(Setting::DeviceProfile.as_str(), "Device profile", options) | |
| 566 | - | .changes(writes(Setting::DeviceProfile)); | |
| 566 | + | .writes(setting_route(Setting::DeviceProfile)); | |
| 567 | 567 | field.value = Some(chosen.unwrap_or_default().to_owned()); | |
| 568 | 568 | Node::Field(Box::new(field)) | |
| 569 | 569 | } | |
| @@ -681,7 +681,7 @@ | |||
| 681 | 681 | .collect(); | |
| 682 | 682 | Field::radio(setting.as_str(), label, options) | |
| 683 | 683 | .value(chosen) | |
| 684 | - | .changes(writes(setting)) | |
| 684 | + | .writes(setting_route(setting)) | |
| 685 | 685 | } | |
| 686 | 686 | ||
| 687 | 687 | /// How to name the output files. | |
| @@ -697,7 +697,7 @@ | |||
| 697 | 697 | ) | |
| 698 | 698 | .value(pattern.unwrap_or_default()) | |
| 699 | 699 | .hint("Tokens: {name} {bpm} {key} {class} {duration} {n} {nn} {nnn} {ext}") | |
| 700 | - | .changes(writes(Setting::NamingPattern)), | |
| 700 | + | .writes(setting_route(Setting::NamingPattern)), | |
| 701 | 701 | )) | |
| 702 | 702 | } | |
| 703 | 703 | ||
| @@ -732,7 +732,7 @@ | |||
| 732 | 732 | } | |
| 733 | 733 | ||
| 734 | 734 | /// The address a control changing this setting calls. | |
| 735 | - | fn writes(setting: Setting) -> Action { | |
| 735 | + | fn setting_route(setting: Setting) -> Action { | |
| 736 | 736 | Action::post(format!("/export/set/{}", setting.as_str())) | |
| 737 | 737 | } | |
| 738 | 738 |
| @@ -49,7 +49,7 @@ | |||
| 49 | 49 | //! asked. | |
| 50 | 50 | //! - **The debounce.** `requery_now` defers the query to a drag's settle point, | |
| 51 | 51 | //! which is renderer policy the way the footer's fade timer was, and is | |
| 52 | - | //! already `Field::changes`'s own behaviour: a described field answers when it | |
| 52 | + | //! already `Field::writes`'s own behaviour: a described field answers when it | |
| 53 | 53 | //! settles (`audiofiles@a3e59e3`). | |
| 54 | 54 | //! - **The per-section collapse.** `widgets::filter_section` is a disclosure per | |
| 55 | 55 | //! axis. [`Field::extended`](quasi_router::Field::extended) says a field lives | |
| @@ -522,7 +522,7 @@ | |||
| 522 | 522 | ..Field::new(FieldKind::Text, "tag", "Require a tag") | |
| 523 | 523 | } | |
| 524 | 524 | .value(state.filters.typing()) | |
| 525 | - | .changes(Action::post("/filters/tags/typing")), | |
| 525 | + | .writes(Action::post("/filters/tags/typing")), | |
| 526 | 526 | ], | |
| 527 | 527 | }); | |
| 528 | 528 |
| @@ -272,7 +272,7 @@ | |||
| 272 | 272 | .collect(), | |
| 273 | 273 | ) | |
| 274 | 274 | .value(forging.how.as_str()) | |
| 275 | - | .changes(Action::post("/forge/slice")), | |
| 275 | + | .writes(Action::post("/forge/slice")), | |
| 276 | 276 | ))); | |
| 277 | 277 | ||
| 278 | 278 | // Only the parameters the chosen method reads, which is the shipped | |
| @@ -361,7 +361,7 @@ | |||
| 361 | 361 | .map(|device| Choice::new(device.name.clone(), describe(device))) | |
| 362 | 362 | .collect(), | |
| 363 | 363 | ) | |
| 364 | - | .changes(Action::post("/forge/device")); | |
| 364 | + | .writes(Action::post("/forge/device")); | |
| 365 | 365 | // The instruction is the picker's ghost text rather than a disabled button's | |
| 366 | 366 | // job, which is the call the shipped screen already made: a select with | |
| 367 | 367 | // nothing chosen reads as an empty box, and the greyed control beside it is | |
| @@ -398,7 +398,7 @@ | |||
| 398 | 398 | .unit("dBFS") | |
| 399 | 399 | .step("1") | |
| 400 | 400 | .value(format!("{:.0}", forging.threshold_db)) | |
| 401 | - | .changes(writes(Knob::Threshold)), | |
| 401 | + | .writes(knob_route(Knob::Threshold)), | |
| 402 | 402 | ))) | |
| 403 | 403 | .with(Node::Act(live( | |
| 404 | 404 | forging, | |
| @@ -423,7 +423,7 @@ | |||
| 423 | 423 | let _ = forging; | |
| 424 | 424 | let name = field.name.clone(); | |
| 425 | 425 | Node::Field(Box::new( | |
| 426 | - | field.changes(Action::post(format!("/forge/set/{name}"))), | |
| 426 | + | field.writes(Action::post(format!("/forge/set/{name}"))), | |
| 427 | 427 | )) | |
| 428 | 428 | } | |
| 429 | 429 | ||
| @@ -446,12 +446,12 @@ | |||
| 446 | 446 | Node::Field(Box::new( | |
| 447 | 447 | Field::radio(knob.as_str(), label, options) | |
| 448 | 448 | .value(chosen) | |
| 449 | - | .changes(writes(knob)), | |
| 449 | + | .writes(knob_route(knob)), | |
| 450 | 450 | )) | |
| 451 | 451 | } | |
| 452 | 452 | ||
| 453 | 453 | /// The address a control changing this number calls. | |
| 454 | - | fn writes(knob: Knob) -> Action { | |
| 454 | + | fn knob_route(knob: Knob) -> Action { | |
| 455 | 455 | Action::post(format!("/forge/set/{}", knob.as_str())) | |
| 456 | 456 | } | |
| 457 | 457 |
| @@ -924,7 +924,7 @@ | |||
| 924 | 924 | .required() | |
| 925 | 925 | .hint("e.g. Drum Kits") | |
| 926 | 926 | .value(vault_name) | |
| 927 | - | .changes(writes(Decision::VaultName)); | |
| 927 | + | .writes(decision_route(Decision::VaultName)); | |
| 928 | 928 | // The state the shipped screen used to leave unexplained: Import | |
| 929 | 929 | // disabled itself and said why only to a pointer that hovered it. | |
| 930 | 930 | if vault_name.trim().is_empty() { | |
| @@ -948,7 +948,7 @@ | |||
| 948 | 948 | .map(|(at, vault)| Choice::new(at.to_string(), vault.name.clone())) | |
| 949 | 949 | .collect(); | |
| 950 | 950 | let mut field = Field::select(Decision::MergeVault.as_str(), "Merge into", options) | |
| 951 | - | .changes(writes(Decision::MergeVault)); | |
| 951 | + | .writes(decision_route(Decision::MergeVault)); | |
| 952 | 952 | field.value = Some(merging_into.to_string()); | |
| 953 | 953 | body = body.with(Node::Field(Box::new(field))); | |
| 954 | 954 | } | |
| @@ -1011,7 +1011,7 @@ | |||
| 1011 | 1011 | Node::Field(Box::new( | |
| 1012 | 1012 | Field::radio(Decision::Strategy.as_str(), "Import strategy", options) | |
| 1013 | 1013 | .value(strategy.as_str()) | |
| 1014 | - | .changes(writes(Decision::Strategy)), | |
| 1014 | + | .writes(decision_route(Decision::Strategy)), | |
| 1015 | 1015 | )) | |
| 1016 | 1016 | } | |
| 1017 | 1017 | ||
| @@ -1116,7 +1116,7 @@ | |||
| 1116 | 1116 | .with(Node::Field(Box::new( | |
| 1117 | 1117 | Field::new(FieldKind::Text, TAGS, "Tags") | |
| 1118 | 1118 | .value(&folder.typed) | |
| 1119 | - | .changes(Action::post(format!("/import/folders/{at}/tags"))), | |
| 1119 | + | .writes(Action::post(format!("/import/folders/{at}/tags"))), | |
| 1120 | 1120 | ))); | |
| 1121 | 1121 | if !folder.invalid.is_empty() { | |
| 1122 | 1122 | group = group.with(Node::banner( | |
| @@ -1151,7 +1151,7 @@ | |||
| 1151 | 1151 | body = body.with(Node::Field(Box::new( | |
| 1152 | 1152 | Field::new(FieldKind::Checkbox, measure.as_str(), measure.label()) | |
| 1153 | 1153 | .value(if measure.read(&measures) { "on" } else { "" }) | |
| 1154 | - | .changes(Action::post(format!( | |
| 1154 | + | .writes(Action::post(format!( | |
| 1155 | 1155 | "/import/measure/{}", | |
| 1156 | 1156 | measure.as_str() | |
| 1157 | 1157 | ))), | |
| @@ -1339,7 +1339,7 @@ | |||
| 1339 | 1339 | .collect(), | |
| 1340 | 1340 | ) | |
| 1341 | 1341 | .value(order.as_str()) | |
| 1342 | - | .changes(Action::post("/import/review/order")), | |
| 1342 | + | .writes(Action::post("/import/review/order")), | |
| 1343 | 1343 | ))); | |
| 1344 | 1344 | ||
| 1345 | 1345 | if items.is_empty() { | |
| @@ -1581,7 +1581,7 @@ | |||
| 1581 | 1581 | } | |
| 1582 | 1582 | ||
| 1583 | 1583 | /// The address a control changing this answers calls. | |
| 1584 | - | fn writes(decision: Decision) -> Action { | |
| 1584 | + | fn decision_route(decision: Decision) -> Action { | |
| 1585 | 1585 | Action::post(format!("/import/set/{}", decision.as_str())) | |
| 1586 | 1586 | } | |
| 1587 | 1587 |
| @@ -209,7 +209,7 @@ | |||
| 209 | 209 | } else { | |
| 210 | 210 | "" | |
| 211 | 211 | }) | |
| 212 | - | .changes(Action::post(format!("/settings/config/{name}"))), | |
| 212 | + | .writes(Action::post(format!("/settings/config/{name}"))), | |
| 213 | 213 | ))); | |
| 214 | 214 | } | |
| 215 | 215 | ||
| @@ -335,7 +335,7 @@ | |||
| 335 | 335 | }) | |
| 336 | 336 | .collect(); | |
| 337 | 337 | let mut field = Field::theme(ConfigKey::Theme.as_str(), "Theme", themes) | |
| 338 | - | .changes(Action::post("/settings/config/theme")); | |
| 338 | + | .writes(Action::post("/settings/config/theme")); | |
| 339 | 339 | field.value = chosen; | |
| 340 | 340 | Node::Field(Box::new(field)) | |
| 341 | 341 | } | |
| @@ -368,7 +368,7 @@ | |||
| 368 | 368 | ||
| 369 | 369 | /// A boolean setting as a checkbox that writes when it changes. | |
| 370 | 370 | /// | |
| 371 | - | /// `Field::changes` rather than a form: this screen has no submit and never | |
| 371 | + | /// `Field::writes` rather than a form: this screen has no submit and never | |
| 372 | 372 | /// should, which is the shape `14612ed8` was recounted for. Thirteen of | |
| 373 | 373 | /// goingson's nineteen change-sites were standalone controls, and every control | |
| 374 | 374 | /// here is one. | |
| @@ -377,7 +377,7 @@ | |||
| 377 | 377 | Ok(Node::Field(Box::new( | |
| 378 | 378 | Field::new(FieldKind::Checkbox, key.as_str(), label) | |
| 379 | 379 | .value(if on { "on" } else { "" }) | |
| 380 | - | .changes(Action::post(format!("/settings/config/{}", key.as_str()))), | |
| 380 | + | .writes(Action::post(format!("/settings/config/{}", key.as_str()))), | |
| 381 | 381 | ))) | |
| 382 | 382 | } | |
| 383 | 383 | ||
| @@ -396,7 +396,7 @@ | |||
| 396 | 396 | ) | |
| 397 | 397 | .value(current) | |
| 398 | 398 | .hint("Between 20 and 32 pixels.") | |
| 399 | - | .changes(Action::post(format!( | |
| 399 | + | .writes(Action::post(format!( | |
| 400 | 400 | "/settings/config/{}", | |
| 401 | 401 | ConfigKey::RowHeight.as_str() | |
| 402 | 402 | )))) |
| @@ -496,7 +496,7 @@ | |||
| 496 | 496 | // where Create New disabled itself and said nothing about why. | |
| 497 | 497 | let mut name = Field::new(FieldKind::Text, "create_name", "Name") | |
| 498 | 498 | .value(draft.name.clone()) | |
| 499 | - | .changes(Action::post("/settings/storage/draft/name")); | |
| 499 | + | .writes(Action::post("/settings/storage/draft/name")); | |
| 500 | 500 | name.required = true; | |
| 501 | 501 | if draft.folder.is_some() && draft.name.trim().is_empty() { | |
| 502 | 502 | name = name.error("A library needs a name."); | |
| @@ -532,7 +532,7 @@ | |||
| 532 | 532 | "copy" | |
| 533 | 533 | }) | |
| 534 | 534 | .hint(STYLE_HINT) | |
| 535 | - | .changes(Action::post("/settings/storage/draft/style")); | |
| 535 | + | .writes(Action::post("/settings/storage/draft/style")); | |
| 536 | 536 | body = body.with(Node::Field(Box::new(style))); | |
| 537 | 537 | ||
| 538 | 538 | if draft.reference_in_place { |
| @@ -69,7 +69,7 @@ | |||
| 69 | 69 | //! **A price cannot follow a slider.** The shipped picker is a logarithmic | |
| 70 | 70 | //! slider with a live quote beside it, recomputed per drag frame from | |
| 71 | 71 | //! `AppPricing::quote_cents` — a *server* pricing model the app happens to hold | |
| 72 | - | //! a copy of. A described field carries a value and `Field::changes` fires a | |
| 72 | + | //! a copy of. A described field carries a value and `Field::writes` fires a | |
| 73 | 73 | //! route when it changes, so following the drag means a request per step. The | |
| 74 | 74 | //! port therefore quotes what is **committed**, not what is under the thumb, and | |
| 75 | 75 | //! the finding is the general one: **nothing describes a display derived from a | |
| @@ -433,7 +433,7 @@ | |||
| 433 | 433 | .with(Node::Field(Box::new( | |
| 434 | 434 | Field::new(FieldKind::Checkbox, "auto", "Sync on a schedule") | |
| 435 | 435 | .value(if status.auto_sync_enabled { "on" } else { "" }) | |
| 436 | - | .changes(Action::post("/sync/auto")), | |
| 436 | + | .writes(Action::post("/sync/auto")), | |
| 437 | 437 | ))) | |
| 438 | 438 | // A label at last: the "Auto-sync" heading above names the checkbox, so | |
| 439 | 439 | // as a bare strip this control asked its question without ever saying | |
| @@ -448,7 +448,7 @@ | |||
| 448 | 448 | .collect(), | |
| 449 | 449 | ) | |
| 450 | 450 | .value(status.sync_interval_minutes.to_string()) | |
| 451 | - | .changes(Action::post("/sync/interval")), | |
| 451 | + | .writes(Action::post("/sync/interval")), | |
| 452 | 452 | ))) | |
| 453 | 453 | .with(Node::section("Audio file sync")) | |
| 454 | 454 | .with(subscription(sync)) |
| @@ -11161,7 +11161,7 @@ | |||
| 11161 | 11161 | classifying(&classifier, request).expect("answered"); | |
| 11162 | 11162 | classifying(&classifier, Request::post("/classifier/rules/r1/enabled")).expect("answered"); | |
| 11163 | 11163 | // Nothing sent is a box that came back unticked, which is what every | |
| 11164 | - | // `Field::changes` checkbox in this app answers with. | |
| 11164 | + | // `Field::writes` checkbox in this app answers with. | |
| 11165 | 11165 | assert_eq!( | |
| 11166 | 11166 | classifier.asked(), | |
| 11167 | 11167 | ["enable_rule r1 true", "enable_rule r1 false"] | |
| @@ -11244,7 +11244,7 @@ | |||
| 11244 | 11244 | let has_tag = |classifier: &FakeClassifier| { | |
| 11245 | 11245 | deep_fields(&classified(classifier)).iter().any(|field| { | |
| 11246 | 11246 | field.name == "tag" | |
| 11247 | - | && field.changes.as_ref().and_then(quasi_router::Action::route) | |
| 11247 | + | && field.writes.as_ref().and_then(quasi_router::Action::route) | |
| 11248 | 11248 | == Some("/classifier/draft/actions/0/set/tag") | |
| 11249 | 11249 | }) | |
| 11250 | 11250 | }; |
| @@ -42,7 +42,7 @@ | |||
| 42 | 42 | //! | |
| 43 | 43 | //! # THE FINDING: a field cannot say that firing it is expensive | |
| 44 | 44 | //! | |
| 45 | - | //! [`Field::changes`](quasi_router::Field::changes) names an address to call | |
| 45 | + | //! [`Field::writes`](quasi_router::Field::writes) names an address to call | |
| 46 | 46 | //! when a value changes and says nothing about how often. The shipped search box | |
| 47 | 47 | //! cannot afford per-keystroke, and its comment is explicit: "each keystroke | |
| 48 | 48 | //! would otherwise run a blocking DB query + re-sort on the GUI thread". So it | |
| @@ -509,7 +509,7 @@ | |||
| 509 | 509 | // no pixel -- and it is the difference between a row that | |
| 510 | 510 | // happens to look right and one that says what it means. | |
| 511 | 511 | .width(Width::Fill) | |
| 512 | - | .changes(Action::post("/search")), | |
| 512 | + | .writes(Action::post("/search")), | |
| 513 | 513 | )), | |
| 514 | 514 | Priority::Essential, | |
| 515 | 515 | ) | |
| @@ -533,7 +533,7 @@ | |||
| 533 | 533 | } else { | |
| 534 | 534 | "folder" | |
| 535 | 535 | }) | |
| 536 | - | .changes(Action::post("/search/scope")), | |
| 536 | + | .writes(Action::post("/search/scope")), | |
| 537 | 537 | )), | |
| 538 | 538 | Priority::Secondary, | |
| 539 | 539 | ); |