forked from 1b-agent/opencadstudio-upstream
feat(properties): navigate polyline vertices from the panel
The Current Vertex row is now a ◀ / ▶ stepper (new PropValue::Stepper) showing "i / N". Stepping it moves the panel's focus through the polyline's vertices — Vertex X/Y and the per-vertex start/end widths show and edit the focused vertex instead of always the first — and the focused vertex's grip is drawn hot (filled) in the drawing so it's visible while navigating. Focus wraps around and resets to the first vertex when the selection changes. Adds Area and Length to the LwPolyline Geometry group (Polyline2D already had them). Applies to LwPolyline and Polyline2D. The focused vertex is threaded to the per-entity property builder/editor through a dispatch thread-local (like the curve-tolerance override), so the PropertyEditable trait signatures are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
a4f7ee376f
commit
b3c8c0a2f1
12 files changed
+208
-22
No files matched your search
@@ -1526,6 +1526,8 @@ pub enum Message {
|
||||
PropLinetypeChanged(String),
|
||||
/// User toggled a boolean property (e.g. Invisible).
|
||||
PropBoolToggle(&'static str),
|
||||
/// User stepped the Current Vertex selector by ±1 (polyline vertex nav).
|
||||
PropVertexStep(i8),
|
||||
/// User selected a hatch pattern from the pattern pick_list in Properties.
|
||||
PropHatchPatternChanged(String),
|
||||
/// User selected a generic choice field in the Properties panel.
|
||||
|
||||
@@ -64,6 +64,22 @@ impl OpenCADStudio {
|
||||
.filter(|name| !name.is_empty())
|
||||
.collect();
|
||||
|
||||
// Current-Vertex focus survives only while the same object stays
|
||||
// selected; a changed selection resets to the first vertex. Seed the
|
||||
// per-thread focus so the polyline property builder / editor targets it.
|
||||
let cur_handles: Vec<acadrust::Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(h, _)| *h)
|
||||
.collect();
|
||||
let prop_vertex = if cur_handles == prev_handles {
|
||||
self.tabs[i].properties.prop_vertex
|
||||
} else {
|
||||
0
|
||||
};
|
||||
crate::scene::view::dispatch::set_prop_current_vertex(prop_vertex);
|
||||
|
||||
let new_panel = {
|
||||
let selected = self.tabs[i].scene.selected_entities();
|
||||
let mut panel = match selected.len() {
|
||||
@@ -475,6 +491,7 @@ impl OpenCADStudio {
|
||||
Default::default()
|
||||
};
|
||||
panel.source_handles = new_handles;
|
||||
panel.prop_vertex = prop_vertex;
|
||||
panel
|
||||
};
|
||||
|
||||
|
||||
@@ -1278,6 +1278,11 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Per-vertex geometry edits (vertex_x/y, widths) target
|
||||
// the vertex the Current Vertex stepper is on. (polyline)
|
||||
crate::scene::view::dispatch::set_prop_current_vertex(
|
||||
self.tabs[i].properties.prop_vertex,
|
||||
);
|
||||
for &handle in &handles {
|
||||
// Skip objects on a locked layer.
|
||||
if self.tabs[i].scene.is_layer_locked(handle) {
|
||||
|
||||
@@ -2200,6 +2200,29 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::PropVertexStep(delta) => {
|
||||
let i = self.active_tab;
|
||||
let handles = self.property_target_handles(i);
|
||||
// Vertex navigation applies to a single selected polyline.
|
||||
let n = if handles.len() == 1 {
|
||||
match self.tabs[i].scene.document.get_entity(handles[0]) {
|
||||
Some(acadrust::EntityType::LwPolyline(p)) => p.vertices.len(),
|
||||
Some(acadrust::EntityType::Polyline2D(p)) => p.vertices.len(),
|
||||
_ => 0,
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if n > 0 {
|
||||
let cur = self.tabs[i].properties.prop_vertex.min(n - 1) as i64;
|
||||
// Wrap around so ◀ from the first vertex lands on the last.
|
||||
let next = (cur + delta as i64).rem_euclid(n as i64) as usize;
|
||||
self.tabs[i].properties.prop_vertex = next;
|
||||
self.refresh_properties();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::PropGeomChoiceChanged { field, value } => {
|
||||
self.on_prop_geom_choice_changed(field, value)
|
||||
}
|
||||
|
||||
+14
-1
@@ -219,6 +219,18 @@ impl OpenCADStudio {
|
||||
None => tab.scene.active_model_tile_bounds(vw, vh),
|
||||
};
|
||||
let sel_h = tab.selected_handle;
|
||||
// The Current Vertex the Properties panel is focused on:
|
||||
// mark that grip hot so the navigated vertex is visible in
|
||||
// the drawing. Only for a single selected polyline, whose
|
||||
// vertex grips are ids 0..n. (Properties vertex stepper)
|
||||
let current_vertex_grip: Option<usize> = sel_h.and_then(|h| {
|
||||
matches!(
|
||||
tab.scene.document.get_entity(h),
|
||||
Some(acadrust::EntityType::LwPolyline(_))
|
||||
| Some(acadrust::EntityType::Polyline2D(_))
|
||||
)
|
||||
.then_some(tab.properties.prop_vertex)
|
||||
});
|
||||
// In-viewport grips are model-space; project them with the
|
||||
// viewport camera so they sit on the wire the GPU draws.
|
||||
// Paper entities use the 2-D paper transform; the model tab
|
||||
@@ -257,7 +269,8 @@ impl OpenCADStudio {
|
||||
let is_hot = tab
|
||||
.active_grip
|
||||
.as_ref()
|
||||
.map_or(false, |g| Some(g.handle) == sel_h && g.grip_id == grip_id);
|
||||
.map_or(false, |g| Some(g.handle) == sel_h && g.grip_id == grip_id)
|
||||
|| Some(grip_id) == current_vertex_grip;
|
||||
crate::ui::overlay::GripMarker {
|
||||
pos: screen,
|
||||
shape,
|
||||
|
||||
Reference in new issue
Block a user