mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-30 12:47:13 -06:00
Properties: selected ruler guides show their position in a Transform section
With ruler guides selected on their own (a click on one), the Properties panel showed the Document sections, so a guide's position could be typed only in the Transform panel. The panel now has a Guides heading and a Transform section with the Transform panel's guide fields (#1597): X moves the vertical guides and Y the horizontal ones, together. With art selected too, it shows the art's sections as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EjfPR4a1jPek7r3nyFFNYv
This commit is contained in:
committed by
bflatastic
co-authored by
Claude Opus 5.5
parent
d93657c631
commit
a7db76bdfb
@@ -24,13 +24,9 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
let first = first_selected(app);
|
||||
// Edit Artboards (the Artboard tool): the active artboard, whatever is selected.
|
||||
let editing_artboards = app.session.tool_id() == "artboard";
|
||||
let label = match (&first, n_sel) {
|
||||
_ if editing_artboards => tl!("Artboard").to_string(),
|
||||
(None, _) => tl!("Document").to_string(),
|
||||
(_, n) if n > 1 => crate::i18n::tn(n as u64, "{n} Object", "{n} Objects"),
|
||||
(Some(n), _) if crate::panels::image_trace::is_trace(n) => tl!("Image Tracing").to_string(),
|
||||
(Some(n), _) => tl!(n.kind_label()).to_string(),
|
||||
};
|
||||
// Ruler guides selected on their own (a click on one): their position.
|
||||
let guides = super::transform::selected_guides(app);
|
||||
let label = heading(first.as_ref(), n_sel, editing_artboards, guides.is_some());
|
||||
ui.label(egui::RichText::new(label).size(11.5).color(t.text_dim));
|
||||
ui.add_space(4.0);
|
||||
if editing_artboards {
|
||||
@@ -38,7 +34,10 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
return;
|
||||
}
|
||||
if first.is_none() {
|
||||
document_sections(app, ui);
|
||||
match guides {
|
||||
Some(guides) => guide_sections(app, ui, guides),
|
||||
None => document_sections(app, ui),
|
||||
}
|
||||
return;
|
||||
}
|
||||
if n_sel == 1
|
||||
@@ -592,6 +591,27 @@ fn artboard_fill_row(app: &mut VectorcraftApp, ui: &mut Ui, i: usize, color: Opt
|
||||
}
|
||||
}
|
||||
|
||||
/// The panel's heading: what the sections are for (the artboards with the Artboard tool, the
|
||||
/// selected ruler guides, the document with nothing selected), else the selection's kind.
|
||||
fn heading(first: Option<&vectorcraft_doc::Node>, n_sel: usize, editing_artboards: bool, guides: bool) -> String {
|
||||
match (first, n_sel) {
|
||||
_ if editing_artboards => tl!("Artboard").to_string(),
|
||||
(None, _) if guides => tl!("Guides").to_string(),
|
||||
(None, _) => tl!("Document").to_string(),
|
||||
(_, n) if n > 1 => crate::i18n::tn(n as u64, "{n} Object", "{n} Objects"),
|
||||
(Some(n), _) if crate::panels::image_trace::is_trace(n) => tl!("Image Tracing").to_string(),
|
||||
(Some(n), _) => tl!(n.kind_label()).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ruler guides selected on their own: a Transform section with the X of the vertical ones and
|
||||
/// the Y of the horizontal ones, which move them, as the Transform panel shows them (#1597).
|
||||
fn guide_sections(app: &mut VectorcraftApp, ui: &mut Ui, guides: (Option<f64>, Option<f64>)) {
|
||||
section_header(ui, tl!("Transform"));
|
||||
let fw = super::field_width(ui);
|
||||
super::transform::guide_fields(app, ui, guides, fw);
|
||||
}
|
||||
|
||||
fn document_sections(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
let units = app.session.general_unit();
|
||||
section_header(ui, tl!("Document"));
|
||||
|
||||
@@ -1600,3 +1600,50 @@ fn a_symbol_instance_turns_about_its_registration_point() {
|
||||
assert!((bx.angle - 45.0).abs() < 1e-6, "turned to {}", bx.angle);
|
||||
assert!(near(bx.reference_point(8), vectorcraft_geom::Point::new(200.0, 160.0)), "the registration point moved to {:?}", bx.reference_point(8));
|
||||
}
|
||||
|
||||
/// Ruler guides selected on their own (a click on one) show a Guides heading and a Transform
|
||||
/// section, where X moves the vertical ones and Y the horizontal ones; with art selected too, the
|
||||
/// panel shows the art's sections.
|
||||
#[test]
|
||||
fn selected_guides_show_their_position_in_a_transform_section() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 200, "height": 100})).unwrap();
|
||||
let v = app.run("guide.add", json!({"vertical": true, "pos": 30})).unwrap()["index"].as_u64().unwrap();
|
||||
let h = app.run("guide.add", json!({"vertical": false, "pos": 20})).unwrap()["index"].as_u64().unwrap();
|
||||
let pos = |app: &VectorcraftApp, i: u64| app.session.doc().unwrap().doc.guides[i as usize].pos;
|
||||
let ctx = egui::Context::default();
|
||||
// Steps the field labelled `label` up by one with ↑.
|
||||
let step = |app: &mut VectorcraftApp, label: &str| {
|
||||
let texts = frame(app, &ctx, vec![]);
|
||||
let r = texts.iter().find(|(t, _)| t == label).unwrap_or_else(|| panic!("no {label:?} in {texts:?}")).1;
|
||||
let at = egui::pos2(r.right() + 40.0, r.center().y);
|
||||
let press = |pressed| Event::PointerButton { pos: at, button: PointerButton::Primary, pressed, modifiers: Default::default() };
|
||||
frame(app, &ctx, vec![Event::PointerMoved(at), press(true)]);
|
||||
frame(app, &ctx, vec![press(false)]);
|
||||
let up = Event::Key { key: egui::Key::ArrowUp, physical_key: None, pressed: true, repeat: false, modifiers: Default::default() };
|
||||
frame(app, &ctx, vec![up]);
|
||||
frame(
|
||||
app,
|
||||
&ctx,
|
||||
vec![Event::PointerButton { pos: egui::pos2(5.0, 690.0), button: PointerButton::Primary, pressed: true, modifiers: Default::default() }],
|
||||
);
|
||||
};
|
||||
for (index, label, before) in [(v, "X:", 30.0), (h, "Y:", 20.0)] {
|
||||
app.run("guide.select", json!({"indexes": [index]})).unwrap();
|
||||
frame(&mut app, &ctx, vec![]);
|
||||
let texts: Vec<String> = frame(&mut app, &ctx, vec![]).into_iter().map(|(t, _)| t).collect();
|
||||
for shown in ["Guides", "Transform", "X:", "Y:"] {
|
||||
assert!(texts.iter().any(|t| t == shown), "{shown:?} in {texts:?}");
|
||||
}
|
||||
assert!(!texts.iter().any(|t| t == "Document"), "{texts:?}");
|
||||
step(&mut app, label);
|
||||
assert_eq!((pos(&app, index) - before).abs(), 1.0, "{label} moves the selected guide");
|
||||
}
|
||||
// A guide and a rectangle: the rectangle's sections.
|
||||
let id = app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 30, "height": 40})).unwrap()["id"].as_u64().unwrap();
|
||||
app.run("select.set", json!({"ids": [id]})).unwrap();
|
||||
app.run("guide.select", json!({"indexes": [v], "toggle": true})).unwrap();
|
||||
frame(&mut app, &ctx, vec![]);
|
||||
let texts: Vec<String> = frame(&mut app, &ctx, vec![]).into_iter().map(|(t, _)| t).collect();
|
||||
assert!(!texts.iter().any(|t| t == "Guides") && texts.iter().any(|t| t == "W:"), "{texts:?}");
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ fn artboard_fields(app: &mut VectorcraftApp, ui: &mut Ui, index: usize, r: Rect)
|
||||
|
||||
/// With ruler guides selected and no art: the X of the vertical ones and the Y of the horizontal
|
||||
/// ones (the least of each; none where no such guide is selected).
|
||||
fn selected_guides(app: &VectorcraftApp) -> Option<(Option<f64>, Option<f64>)> {
|
||||
pub(crate) fn selected_guides(app: &VectorcraftApp) -> Option<(Option<f64>, Option<f64>)> {
|
||||
let st = app.session.active()?;
|
||||
if !st.selection.objects.is_empty() || st.selection.guides.is_empty() {
|
||||
return None;
|
||||
@@ -226,16 +226,17 @@ fn selected_guides(app: &VectorcraftApp) -> Option<(Option<f64>, Option<f64>)> {
|
||||
Some((least(true), least(false)))
|
||||
}
|
||||
|
||||
/// The selected ruler guides' view of the panel (#1597): X moves the vertical ones and Y the
|
||||
/// horizontal ones, together.
|
||||
fn guide_fields(app: &mut VectorcraftApp, ui: &mut Ui, (x, y): (Option<f64>, Option<f64>)) {
|
||||
/// The selected ruler guides' view of the panel (#1597), also the Properties panel's Transform
|
||||
/// section for them: X moves the vertical ones and Y the horizontal ones, together. The fields
|
||||
/// are `width` wide.
|
||||
pub(crate) fn guide_fields(app: &mut VectorcraftApp, ui: &mut Ui, (x, y): (Option<f64>, Option<f64>), width: f32) {
|
||||
let units = app.session.general_unit();
|
||||
let origin = app.ruler_origin();
|
||||
egui::Grid::new("xfp-guide-grid").num_columns(2).spacing([4.0, 6.0]).min_col_width(0.0).show(ui, |ui| {
|
||||
for (label, id, pos, origin, by) in [("X:", "xfp-guide-x", x, origin.x, "dx"), ("Y:", "xfp-guide-y", y, origin.y, "dy")] {
|
||||
widgets::dim_label(ui, label);
|
||||
ui.add_enabled_ui(pos.is_some(), |ui| {
|
||||
if let (Some(v), Some(pos)) = (widgets::num_field_named(ui, id, Some(tl!(label)), pos.map(|p| p - origin), units, 80.0), pos)
|
||||
if let (Some(v), Some(pos)) = (widgets::num_field_named(ui, id, Some(tl!(label)), pos.map(|p| p - origin), units, width), pos)
|
||||
&& let Err(e) = app.run("guide.move", json!({ by: v + origin - pos }))
|
||||
{
|
||||
app.status(e);
|
||||
@@ -257,7 +258,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
return;
|
||||
}
|
||||
if let Some(guides) = selected_guides(app) {
|
||||
guide_fields(app, ui, guides);
|
||||
guide_fields(app, ui, guides, 80.0);
|
||||
return;
|
||||
}
|
||||
let units = app.session.general_unit();
|
||||
|
||||
Reference in New Issue
Block a user