mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
Tabs: a refused tab stop move leaves the ruler as it was
When text.tabs.set refused a new X (past 100,000 pt), the panel kept the stops it had changed and sorted, so later in the same frame the ruler painted the refused position and highlighted another stop. apply_moved now moves and sorts a copy of the stops and keeps it only when the stops are set.
This commit is contained in:
@@ -102,15 +102,19 @@ fn apply(app: &mut VectorcraftApp, stops: &[TabStop]) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sort `stops` by position and set them, then select the first stop within 1e-6 pt of `position`
|
||||
/// on the ruler, the stop `text.tabs.set` keeps of those it merges. Returns its index, or `None`
|
||||
/// when the stops are not set and the selection stays as it was.
|
||||
fn apply_moved(app: &mut VectorcraftApp, ctx: &egui::Context, stops: &mut [TabStop], position: f64) -> Option<usize> {
|
||||
stops.sort_by(|a, b| a.position.total_cmp(&b.position));
|
||||
let i = stops.iter().position(|s| (s.position - position).abs() < 1e-6).unwrap_or(0);
|
||||
apply(app, stops).then(|| {
|
||||
set_pstate(ctx, "tabs-sel", i);
|
||||
i
|
||||
/// Move stop `i` to `position`, sort the stops by position and set them, then select the first
|
||||
/// stop within 1e-6 pt of `position` on the ruler, the stop `text.tabs.set` keeps of those it
|
||||
/// merges. Returns its index, or `None` when the stops are not set and `stops` and the selection
|
||||
/// stay as they were.
|
||||
fn apply_moved(app: &mut VectorcraftApp, ctx: &egui::Context, stops: &mut Vec<TabStop>, i: usize, position: f64) -> Option<usize> {
|
||||
let mut moved = stops.clone();
|
||||
moved.get_mut(i)?.position = position;
|
||||
moved.sort_by(|a, b| a.position.total_cmp(&b.position));
|
||||
let sel = moved.iter().position(|s| (s.position - position).abs() < 1e-6).unwrap_or(0);
|
||||
apply(app, &moved).then(|| {
|
||||
*stops = moved;
|
||||
set_pstate(ctx, "tabs-sel", sel);
|
||||
sel
|
||||
})
|
||||
}
|
||||
|
||||
@@ -153,13 +157,9 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
ui.label(egui::RichText::new("X:").color(t.text));
|
||||
let x = stops.get(sel).map(|s| s.position);
|
||||
if let Some(v) = widgets::spin_field(ui, "tabs-x", x, app.session.general_unit(), 90.0, 1.0, 0.0, &[])
|
||||
&& let Some(s) = stops.get_mut(sel)
|
||||
&& let Some(i) = apply_moved(app, ui.ctx(), &mut stops, sel, v.max(0.0))
|
||||
{
|
||||
s.position = v.max(0.0);
|
||||
let position = s.position;
|
||||
if let Some(i) = apply_moved(app, ui.ctx(), &mut stops, position) {
|
||||
sel = i;
|
||||
}
|
||||
sel = i;
|
||||
}
|
||||
ui.end_row();
|
||||
ui.label(egui::RichText::new(tl!("Leader:")).color(t.text));
|
||||
@@ -259,9 +259,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
stops.remove(i);
|
||||
apply(app, &stops);
|
||||
} else {
|
||||
let position = place(pos.x);
|
||||
stops[i].position = position;
|
||||
apply_moved(app, ui.ctx(), &mut stops, position);
|
||||
apply_moved(app, ui.ctx(), &mut stops, i, place(pos.x));
|
||||
}
|
||||
}
|
||||
} else if resp.clicked()
|
||||
@@ -692,6 +690,47 @@ mod tests {
|
||||
assert_eq!(pstate::<usize>(&ctx, "tabs-sel"), 0);
|
||||
}
|
||||
|
||||
/// One frame of the panel with `events` → the stops' stems on the ruler, left to right, with
|
||||
/// their colors.
|
||||
fn stems_frame(ctx: &egui::Context, app: &mut VectorcraftApp, events: Vec<egui::Event>) -> Vec<(f32, egui::Color32)> {
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, vec2(400.0, 400.0));
|
||||
let mut out = ctx.run_ui(egui::RawInput { screen_rect: Some(screen), events, ..Default::default() }, |ui| show(app, ui));
|
||||
out.textures_delta.clear();
|
||||
out.shapes
|
||||
.iter()
|
||||
.filter_map(|c| match &c.shape {
|
||||
egui::Shape::LineSegment { points: [a, b], stroke } if stroke.width == 1.5 && a.x == b.x => Some((a.x, stroke.color)),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// In the frame where Enter commits an X the stops cannot take (past 100,000 pt), the ruler
|
||||
/// shows the stops and the selected stop as they were.
|
||||
#[test]
|
||||
fn a_refused_x_leaves_the_ruler_as_it_was() {
|
||||
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();
|
||||
app.session.execute("text.tabs.set", &json!({"stops": [{"position": 30}, {"position": 50}]})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
set_pstate(&ctx, "tabs-sel", 1usize);
|
||||
let before = stems_frame(&ctx, &mut app, vec![]);
|
||||
assert_eq!(before.len(), 2, "{before:?}");
|
||||
let texts = panel_frame(&ctx, &mut app, vec![]);
|
||||
let at = texts.iter().find(|(t, _)| t == "50 pt").unwrap_or_else(|| panic!("50 pt in {texts:?}")).1.center();
|
||||
let button = |pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() };
|
||||
let key = |key, modifiers| egui::Event::Key { key, physical_key: None, pressed: true, repeat: false, modifiers };
|
||||
panel_frame(&ctx, &mut app, vec![egui::Event::PointerMoved(at), button(true)]);
|
||||
panel_frame(&ctx, &mut app, vec![button(false)]);
|
||||
panel_frame(&ctx, &mut app, vec![key(egui::Key::A, egui::Modifiers::COMMAND)]);
|
||||
panel_frame(&ctx, &mut app, vec![egui::Event::Text("200000".into())]);
|
||||
let committed = stems_frame(&ctx, &mut app, vec![key(egui::Key::Enter, egui::Modifiers::NONE)]);
|
||||
assert_eq!(current(&app).unwrap().1.iter().map(|s| s.position).collect::<Vec<_>>(), [30.0, 50.0]);
|
||||
assert_eq!(pstate::<usize>(&ctx, "tabs-sel"), 1);
|
||||
assert_eq!(committed, before);
|
||||
}
|
||||
|
||||
/// One frame of the panel menu with `events` → the rows' texts and where they are painted.
|
||||
fn menu_frame(ctx: &egui::Context, app: &mut VectorcraftApp, events: Vec<egui::Event>) -> Vec<(String, egui::Rect)> {
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, vec2(320.0, 400.0));
|
||||
|
||||
Reference in New Issue
Block a user