Merge pull request #1647 from ramox81/codex/automation-select-layout

This commit is contained in:
Hakan Seven committed 2026-10-07 12:55:08 +03:00
commit 5a1788775b
3 files changed
+93 -15

No files matched your search

+79 -15
View File
@@ -200,6 +200,42 @@ fn serve_socket_with_idle(
}
}
/// The entities inside block definitions: owned by (or listed in) a block
/// record that is not a layout's model or paper space.
fn definition_members(document: &codec::CadDocument) -> std::collections::HashSet<codec::Handle> {
let mut layouts: std::collections::HashSet<codec::Handle> = document
.objects
.values()
.filter_map(|object| match object {
codec::objects::ObjectType::Layout(layout) if !layout.block_record.is_null() => Some(layout.block_record),
_ => None,
})
.collect();
layouts.extend(document.block_records.iter().filter_map(|record| {
let name = record.name.to_ascii_uppercase();
(name.starts_with("*MODEL_SPACE") || name.starts_with("*PAPER_SPACE")).then_some(record.handle)
}));
let definitions: std::collections::HashSet<codec::Handle> = document
.block_records
.iter()
.filter(|record| !layouts.contains(&record.handle))
.map(|record| record.handle)
.collect();
let mut members: std::collections::HashSet<codec::Handle> = document
.block_records
.iter()
.filter(|record| definitions.contains(&record.handle))
.flat_map(|record| record.entity_handles.iter().copied())
.collect();
members.extend(
document
.entities()
.filter(|e| definitions.contains(&e.common().owner_handle))
.map(|e| e.common().handle),
);
members
}
fn err(msg: impl std::fmt::Display) -> Value {
json!({ "ok": false, "error": msg.to_string() })
}
@@ -794,30 +830,58 @@ impl OpenCADStudio {
}
"select" => {
let i = self.active_tab;
// By explicit handles (hex, as returned by `query`). An entity
// inside a block definition is not selectable on its own.
let explicit: Vec<codec::Handle> = req["handles"]
.as_array()
.map(|arr| {
arr.iter()
.filter_map(|h| h.as_str())
.filter_map(|h| {
let h = h.strip_prefix("0x").or_else(|| h.strip_prefix("0X")).unwrap_or(h);
u64::from_str_radix(h, 16).ok().map(codec::Handle::new)
})
.collect()
})
.unwrap_or_default();
if req["clear"].as_bool() != Some(true) {
let inside = definition_members(&self.tabs[i].scene.document);
let refused: Vec<String> = explicit
.iter()
.filter(|h| inside.contains(h))
.map(|h| format!("{:X}", h.value()))
.collect();
if !refused.is_empty() {
return err(format!(
"select: inside a block definition, not selectable: {}",
refused.join(", ")
));
}
}
self.tabs[i].scene.deselect_all();
if req["clear"].as_bool() != Some(true) {
// By explicit handles (hex, as returned by `query`).
if let Some(arr) = req["handles"].as_array() {
for h in arr.iter().filter_map(|h| h.as_str()) {
let h = h
.strip_prefix("0x")
.or_else(|| h.strip_prefix("0X"))
.unwrap_or(h);
if let Ok(v) = u64::from_str_radix(h, 16) {
self.tabs[i]
.scene
.select_entity(codec::Handle::new(v), false);
}
}
for h in explicit {
self.tabs[i].scene.select_entity(h, false);
}
// Or by type / layer.
let type_filter = req["type"].as_str();
let layer_filter = req["layer"].as_str();
if type_filter.is_some() || layer_filter.is_some() {
let handles: Vec<codec::Handle> = self.tabs[i]
.scene
// Only what the current space draws: an entity inside a
// block definition is not selectable on its own.
let scene = &self.tabs[i].scene;
let space = scene.current_layout_block_handle_pub();
let listed: std::collections::HashSet<codec::Handle> = scene
.document
.block_records
.iter()
.find(|record| record.handle == space)
.map(|record| record.entity_handles.iter().copied().collect())
.unwrap_or_default();
let handles: Vec<codec::Handle> = scene
.document
.entities()
.filter(|e| e.common().owner_handle == space || listed.contains(&e.common().handle))
.filter(|e| type_filter.is_none_or(|t| entity_type_matches(e, t)))
.filter(|e| layer_filter.is_none_or(|l| e.common().layer == l))
.map(|e| e.common().handle)
+5
View File
@@ -798,9 +798,14 @@ impl OpenCADStudio {
{
return self.dispatch_command(cmd);
}
// Close an earlier step first so only what the probe opens is dropped below.
self.finish_pending_history(i);
let start_task = self.dispatch_command(tokens[0]);
if self.tabs[i].active_cmd.is_none() {
// Not an interactive tool — an inline-argument command (`PDMODE 3`).
// The bare word only probed it: a step it kept without changing
// anything is not an undo step of its own.
self.drop_unchanged_pending_history(i);
return self.dispatch_command(cmd);
}
let toks: Vec<String> = tokens.iter().map(|s| s.to_string()).collect();
+9
View File
@@ -341,6 +341,15 @@ impl OpenCADStudio {
self.push_undo_entry(i, HistorySnapshot::Delta(Box::new(delta)));
}
/// Finish the pending history step, dropping it when it changed nothing
/// even if it was opened as a mark ([`Self::push_undo_mark`]).
pub(super) fn drop_unchanged_pending_history(&mut self, i: usize) {
if let Some(pending) = self.tabs[i].history.pending.as_mut() {
pending.keep_empty = false;
}
self.finish_pending_history(i);
}
pub(super) fn finish_all_pending_history(&mut self) {
for i in 0..self.tabs.len() {
self.finish_pending_history(i);