Tabs: the ruler and Repeat Tab stop at 256 tab stops

With 256 stops, a click on an empty part of the Tabs panel's ruler adds
no stop and shows "at most 256 tab stops" in the status bar; the stops
and the selection stay as they were. Repeat Tab adds copies only up to
256 stops and shows the same message when it stops early. Before, both
built a longer list that text.tabs.set refused, so nothing changed and
no message showed.
This commit is contained in:
dotCooCoo
2026-11-12 00:19:38 -08:00
parent eb608bb8b5
commit a6ff24d0c4
2 changed files with 51 additions and 10 deletions
+22 -10
View File
@@ -686,15 +686,22 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
// A click that lands on a stop's whole point or tick selects that stop and records no
// undo step.
let added = !stops.iter().any(|s| s.position == position);
if added {
stops.push(TabStop { position, align: ALIGNS[align_idx.min(3)].0, leader: String::new(), align_on: '.' });
stops.sort_by(|a, b| a.position.total_cmp(&b.position));
}
let i = stops.iter().position(|s| s.position == position).unwrap_or(0);
set_pstate::<Option<IndentMarker>>(ui.ctx(), "tabs-marker-sel", None);
set_pstate(ui.ctx(), "tabs-sel", i);
if added {
apply(app, &stops);
let max = TabStop::MAX_STOPS;
if added && stops.len() >= max {
// A full list takes no more stops: the click leaves the stops and the
// selection as they are and shows the limit.
app.status(format!("at most {max} tab stops"));
} else {
if added {
stops.push(TabStop { position, align: ALIGNS[align_idx.min(3)].0, leader: String::new(), align_on: '.' });
stops.sort_by(|a, b| a.position.total_cmp(&b.position));
}
let i = stops.iter().position(|s| s.position == position).unwrap_or(0);
set_pstate::<Option<IndentMarker>>(ui.ctx(), "tabs-marker-sel", None);
set_pstate(ui.ctx(), "tabs-sel", i);
if added {
apply(app, &stops);
}
}
}
}
@@ -729,7 +736,7 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) {
}
// Repeat Tab: copies of the selected stop across the ruler, spaced by its distance from the
// previous stop, or from the paragraph's left indent plus its list level's indent when it is
// the first stop.
// the first stop. The copies stop at the 256-stop limit, which the status bar then shows.
if menu_item(ui, tl!("Repeat Tab"), sel < stops.len() && selected_marker.is_none(), false) {
let step = match sel {
0 => (stops[0].position - left_indent).abs(),
@@ -738,8 +745,13 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) {
.max(1.0);
let base = stops[sel].clone();
stops.truncate(sel + 1);
let max = TabStop::MAX_STOPS;
let mut pos = base.position + step;
while pos <= base.position + step * 20.0 && pos < 2000.0 {
if stops.len() >= max {
app.status(format!("at most {max} tab stops"));
break;
}
stops.push(TabStop { position: pos, ..base.clone() });
pos += step;
}
+29
View File
@@ -1255,6 +1255,35 @@ fn panel_menu_does_not_change_stops_while_an_indent_marker_is_selected() {
assert_eq!(current(&app).unwrap().1, before);
}
/// With 256 stops, a click on an empty part of the ruler adds none and shows the limit in the
/// status bar. Repeat Tab near the end of the list adds copies only up to 256 stops and shows the
/// limit too.
#[test]
fn the_ruler_and_repeat_tab_stop_at_256_stops() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.session.execute("file.new", &json!({})).unwrap();
app.session.execute("text.create", &json!({"x": 10, "y": 20, "text": "a b", "area": {"width": 300, "height": 100}})).unwrap();
// 250 stops 1 pt apart from 0 pt, then six 2 pt apart from 260 pt.
let start: Vec<f64> = (0..250).map(f64::from).chain((0..6).map(|i| 260.0 + 2.0 * f64::from(i))).collect();
let stops: Vec<Value> = start.iter().map(|p| json!({"position": p})).collect();
app.session.execute("text.tabs.set", &json!({ "stops": stops })).unwrap();
let ctx = egui::Context::default();
let positions = |app: &VectorcraftApp| current(app).unwrap().1.iter().map(|s| s.position).collect::<Vec<_>>();
set_pstate(&ctx, "tabs-sel", 3usize);
drag_on_ruler(&ctx, &mut app, 290.0, 290.0, false);
assert_eq!(positions(&app), start);
assert_eq!(pstate::<usize>(&ctx, "tabs-sel"), 3);
assert_eq!(app.ui.status, "at most 256 tab stops");
// Repeat Tab on the stop at 260 pt (11 pt past the one before it): five copies, not 20.
app.status("");
set_pstate(&ctx, "tabs-sel", 250usize);
choose(&ctx, &mut app, "Repeat Tab");
let repeated = positions(&app);
assert_eq!(repeated.len(), 256);
assert_eq!(repeated[249..], [249.0, 260.0, 271.0, 282.0, 293.0, 304.0, 315.0]);
assert_eq!(app.ui.status, "at most 256 tab stops");
}
/// Repeat Tab on the first stop spaces the copies by the stop's distance from the paragraph's
/// left indent (18 pt further in for each list level past the first), from the paragraph the
/// caret is in while the Type tool edits text.