Skip to main content

max / makeover-immediate

0.35.0: both ends of an interval are named by the one question 0.34.0 attached a field's label to its control and half-kept the fix for an interval. `Control::Spanned` draws two boxes and answers `low | high`, and a union response carries the first id, so labelling it outside the arm named the lower box and left the upper one announced as whatever number was in it. `control` takes the label's id now and the spanned arm names both ends with it. Found in audiofiles' filter panel, where six axes each kept one unnamed end after taking 0.34.0.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-22 19:35 UTC
Signed with PGP, not checked
Commit: 6931711975fa88be595de437a283e1e067adf7c0
Parent: 3f17c53
2 files changed, +55 insertions, -2 deletions
M Cargo.toml +1 -1
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makeover-immediate"
3 - version = "0.34.0"
3 + version = "0.35.0"
4 4 edition = "2024"
5 5 description = "The immediate-mode renderer for makeover-layout. Immediate mode is the constraint that matters, not the library: no cascade, no retained tree, one stroke per widget. Backed by egui."
6 6 license = "MIT"
M src/lib.rs +54 -1
@@ -931,6 +931,7 @@
931 931 filling: Filling<'_>,
932 932 palette: &Palette,
933 933 style: &FieldStyle,
934 + named_by: Option<egui::Id>,
934 935 ) -> Response {
935 936 // The mismatch path: described as one thing and filled as another. Nothing
936 937 // here can fix it, so it is drawn as the empty, inert version of what was
@@ -1020,6 +1021,15 @@
1020 1021 // in dBFS.
1021 1022 ui.label(RichText::new("to").color(palette.content_secondary));
1022 1023 let high = axis.end(ui, upper, Bound::High);
1024 + // Both ends, by name. A union response carries the first id, so
1025 + // labelling the union outside this arm names the lower box and
1026 + // leaves the upper one announced as whatever number is in it --
1027 + // which is how an interval half-kept the fix that gave every
1028 + // other shape its question.
1029 + if let Some(id) = named_by {
1030 + low.clone().labelled_by(id);
1031 + high.clone().labelled_by(id);
1032 + }
1023 1033 low | high
1024 1034 })
1025 1035 .inner
@@ -1204,7 +1214,16 @@
1204 1214 .then(|| ui.label(RichText::new(label_text(field, style)).color(text)));
1205 1215
1206 1216 let response = ui
1207 - .add_enabled_ui(enabled, |ui| control(ui, field, filling, palette, style))
1217 + .add_enabled_ui(enabled, |ui| {
1218 + control(
1219 + ui,
1220 + field,
1221 + filling,
1222 + palette,
1223 + style,
1224 + named_by.as_ref().map(|l| l.id),
1225 + )
1226 + })
1208 1227 .inner;
1209 1228
1210 1229 // The label, attached rather than merely adjacent.
@@ -1748,6 +1767,40 @@
1748 1767 assert_eq!(box_.1, "New name", "{drawn:?}");
1749 1768 }
1750 1769
1770 + #[test]
1771 + fn both_ends_of_an_interval_are_named_by_the_one_question() {
1772 + // A union response carries the first id, so labelling the pair outside
1773 + // the arm named the lower box and left the upper one announced as
1774 + // whatever number was in it.
1775 + let f = Field::new(FieldKind::Interval, "bpm", "BPM Range");
1776 + let p = palette(Color32::from_rgb(9, 9, 9));
1777 + let drawn = announced(|ui| {
1778 + let mut lower = String::from("90");
1779 + let mut upper = String::from("300");
1780 + field(
1781 + ui,
1782 + &f,
1783 + Filling::Between {
1784 + lower: &mut lower,
1785 + upper: &mut upper,
1786 + },
1787 + None,
1788 + &p,
1789 + &FieldStyle::default(),
1790 + );
1791 + });
1792 +
1793 + // A `SpinButton`: an interval's ends are drag values, not text boxes.
1794 + let ends: Vec<_> = drawn
1795 + .iter()
1796 + .filter(|(role, _)| *role == egui::accesskit::Role::SpinButton)
1797 + .collect();
1798 + assert_eq!(ends.len(), 2, "an interval draws two boxes: {drawn:?}");
1799 + for end in ends {
1800 + assert_eq!(end.1, "BPM Range", "{drawn:?}");
1801 + }
1802 + }
1803 +
1751 1804 #[test]
1752 1805 fn a_checkbox_keeps_naming_itself() {
1753 1806 // `FieldKind::labels_itself` routes past the label, and egui names a