Draw from centre (Alt) now applies to the typed size of a rectangle or ellipse (#3309)

This commit is contained in:
MarekSpodBiedry
2026-10-21 16:46:38 +07:00
committed by bflatastic
parent b0056a127d
commit cde39698ff
3 changed files with 46 additions and 5 deletions
+18 -1
View File
@@ -201,7 +201,9 @@ impl Tool for ShapeTool {
self.began = false;
vec![Action::Commit]
} else {
vec![Action::Dialog(self.id.into(), json!({ "x": s.x, "y": s.y }))]
// Alt (draw from centre) is passed on, so the size dialog can centre the shape on the click.
let args = if ev.mods.alt { json!({ "x": s.x, "y": s.y, "alt": true }) } else { json!({ "x": s.x, "y": s.y }) };
vec![Action::Dialog(self.id.into(), args)]
}
}
_ => vec![],
@@ -385,6 +387,21 @@ mod tests {
);
}
#[test]
fn alt_click_passes_draw_from_centre_to_the_dialog() {
let (d, _) = doc_with_rect();
let s = Selection::default();
let p = paint();
let cx = cx(&d, &s, &p);
let alt = Mods { alt: true, ..Default::default() };
let mut t = ShapeTool::new("rectangle");
t.pointer(&cx, &PointerEvent::new(PointerKind::Down, 10.0, 10.0).with_mods(alt));
assert_eq!(
t.pointer(&cx, &PointerEvent::new(PointerKind::Up, 10.0, 10.0).with_mods(alt)),
vec![Action::Dialog("rectangle".into(), json!({"x": 10.0, "y": 10.0, "alt": true}))]
);
}
#[test]
fn arrow_keys_change_sides() {
let (d, _) = doc_with_rect();
+25 -2
View File
@@ -93,6 +93,16 @@ fn body(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool {
false
}
/// The top-left corner of a rectangle or ellipse: the click itself, or with Alt held (draw from centre) the click minus half the size.
fn box_origin(d: &Dialog) -> (f64, f64) {
let (x, y) = (d.f64("x", 0.0), d.f64("y", 0.0));
if d.fields.get("alt").and_then(Value::as_bool).unwrap_or(false) {
(x - d.f64("width", 100.0) / 2.0, y - d.f64("height", 100.0) / 2.0)
} else {
(x, y)
}
}
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
let count = match d.kind.as_str() {
"polygon" => Some(("sides", 6.0, 3.0)),
@@ -105,8 +115,9 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
values
});
let (x, y) = (d.f64("x", 0.0), d.f64("y", 0.0));
let (bx, by) = box_origin(d);
let (id, params) = match d.kind.as_str() {
"ellipse" => ("shape.ellipse", json!({"x": x, "y": y, "width": d.f64("width", 100.0), "height": d.f64("height", 100.0)})),
"ellipse" => ("shape.ellipse", json!({"x": bx, "y": by, "width": d.f64("width", 100.0), "height": d.f64("height", 100.0)})),
"polygon" => ("shape.polygon", json!({"cx": x, "cy": y, "radius": d.f64("radius", 50.0), "sides": d.f64("sides", 6.0) as u64})),
"star" => (
"shape.star",
@@ -131,7 +142,7 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
}
_ => (
"shape.rectangle",
json!({"x": x, "y": y, "width": d.f64("width", 100.0), "height": d.f64("height", 100.0), "radius": d.f64("radius", 0.0)}),
json!({"x": bx, "y": by, "width": d.f64("width", 100.0), "height": d.f64("height", 100.0), "radius": d.f64("radius", 0.0)}),
),
};
// On the active plane while the perspective grid shows, sized in plane units from the click.
@@ -145,3 +156,15 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alt_click_centres_the_typed_size_on_the_click() {
let fields = |alt: bool| Dialog::new("rectangle", json!({"x": 100.0, "y": 50.0, "alt": alt, "width": "40 pt", "height": "20 pt"}));
assert_eq!(box_origin(&fields(false)), (100.0, 50.0));
assert_eq!(box_origin(&fields(true)), (80.0, 40.0));
}
}
+3 -2
View File
@@ -11,9 +11,10 @@ use crate::state::Dialog;
pub fn open_tool_dialog(app: &mut VectorcraftApp, kind: &str, p: Value) {
let x = p.get("x").and_then(Value::as_f64).unwrap_or(0.0);
let y = p.get("y").and_then(Value::as_f64).unwrap_or(0.0);
let alt = p.get("alt").and_then(Value::as_bool).unwrap_or(false);
let d = match kind {
"rectangle" | "ellipse" => Dialog::new(kind, json!({"x": x, "y": y, "width": "100 pt", "height": "100 pt"})),
"roundedRectangle" => Dialog::new(kind, json!({"x": x, "y": y, "width": "100 pt", "height": "100 pt", "radius": "12 pt"})),
"rectangle" | "ellipse" => Dialog::new(kind, json!({"x": x, "y": y, "alt": alt, "width": "100 pt", "height": "100 pt"})),
"roundedRectangle" => Dialog::new(kind, json!({"x": x, "y": y, "alt": alt, "width": "100 pt", "height": "100 pt", "radius": "12 pt"})),
"polygon" => Dialog::new(kind, json!({"x": x, "y": y, "radius": "50 pt", "sides": app.session.tool_options_of(kind)["sides"]})),
"star" => {
Dialog::new(kind, json!({"x": x, "y": y, "radius1": "50 pt", "radius2": "25 pt", "points": app.session.tool_options_of(kind)["points"]}))