forked from 1b-agent/opencadstudio-upstream
fix(plugin): drive headless tasks, invalidate layer cache, and handle cancel event
This commit is contained in:
1 parent
bc6ad1a5fe
commit
270b6102de
10 files changed
+208
-20
No files matched your search
@@ -4872,7 +4872,7 @@ impl OpenCADStudio {
|
||||
self.tabs[i].snap_result = None;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.restore_pre_cmd_tangent();
|
||||
dispatched = self.dispatch_command(&cmd);
|
||||
dispatched = self.dispatch_command_without_plugins(&cmd);
|
||||
}
|
||||
CmdResult::EditTableCell { handle, point } => {
|
||||
// TABLEDIT's pick: end the pick phase and hand (table, point)
|
||||
|
||||
@@ -244,7 +244,7 @@ impl OpenCADStudio {
|
||||
/// `Some(task)` for an arm it owns (early-returning or falling through to
|
||||
/// `finish_dispatch`), or `None` to defer to the next — equivalent to one
|
||||
/// sequential `match` over all arms.
|
||||
fn dispatch_families(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||
pub(crate) fn dispatch_families(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||
if let Some(t) = self.dispatch_fileops(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
|
||||
@@ -2710,8 +2710,19 @@ impl OpenCADStudio {
|
||||
.push_output(crate::tf!("CLAYER = \"{cur}\"").as_ref());
|
||||
} else {
|
||||
if self.tabs[i].scene.document.layers.contains(name_arg) {
|
||||
let handle = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.layers
|
||||
.get(name_arg)
|
||||
.map(|l| l.handle)
|
||||
.unwrap_or(acadrust::types::Handle::NULL);
|
||||
self.tabs[i].scene.document.header.current_layer_name =
|
||||
name_arg.to_string();
|
||||
self.tabs[i].scene.document.header.current_layer_handle = handle;
|
||||
self.tabs[i].active_layer = name_arg.to_string();
|
||||
self.tabs[i].layers.current_layer = name_arg.to_string();
|
||||
self.ribbon.active_layer = name_arg.to_string();
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(crate::tf!("CLAYER set to \"{name_arg}\"").as_ref());
|
||||
|
||||
+93
-6
@@ -429,10 +429,23 @@ impl<'a> HostSession<'a> {
|
||||
let tab = self.tab;
|
||||
let is_new_cmd = self.app.tabs[tab].active_cmd.is_none();
|
||||
if is_new_cmd {
|
||||
// Check if the complete command string is handled directly by command families
|
||||
// (e.g. "CLAYER TEST", "ZOOM EXTENTS", "COLOR RED", "VSCURRENT FLATSHADED", etc.) without needing token-by-token feeding
|
||||
let full_resolved = self.app.resolve_alias(trimmed);
|
||||
let full_effective = full_resolved.as_deref().unwrap_or(trimmed);
|
||||
if let Some(task) = self.app.dispatch_families(full_effective, tab) {
|
||||
let _ = self.app.drive_headless_task(task);
|
||||
self.app.command_line.record_recent(full_effective);
|
||||
self.app.refresh_layer_panel();
|
||||
self.publish_document_view();
|
||||
return true;
|
||||
}
|
||||
|
||||
let cmd_name = parts[0];
|
||||
let resolved = self.app.resolve_alias(cmd_name);
|
||||
let effective = resolved.as_deref().unwrap_or(cmd_name);
|
||||
let _ = self.app.dispatch_command_without_plugins(effective);
|
||||
let task = self.app.dispatch_command_without_plugins(effective);
|
||||
let _ = self.app.drive_headless_task(task);
|
||||
}
|
||||
|
||||
let mut paused = false;
|
||||
@@ -451,15 +464,17 @@ impl<'a> HostSession<'a> {
|
||||
paused = true;
|
||||
break;
|
||||
}
|
||||
if part.eq_ignore_ascii_case("ENTER") || part.eq_ignore_ascii_case("RETURN") {
|
||||
let _ = self.app.feed_command(crate::command::StepInput::Enter);
|
||||
let task = if part.eq_ignore_ascii_case("ENTER") || part.eq_ignore_ascii_case("RETURN") {
|
||||
self.app.feed_command(crate::command::StepInput::Enter)
|
||||
} else {
|
||||
let _ = self.app.feed_active_cmd(part);
|
||||
}
|
||||
self.app.feed_active_cmd(part)
|
||||
};
|
||||
let _ = self.app.drive_headless_task(task);
|
||||
}
|
||||
|
||||
if !paused && has_newline && self.app.tabs[tab].active_cmd.is_some() {
|
||||
let _ = self.app.feed_command(crate::command::StepInput::Enter);
|
||||
let task = self.app.feed_command(crate::command::StepInput::Enter);
|
||||
let _ = self.app.drive_headless_task(task);
|
||||
}
|
||||
|
||||
self.app.refresh_layer_panel();
|
||||
@@ -646,6 +661,7 @@ pub(crate) struct PluginProcessInteractiveAdapter {
|
||||
pub command_id: u64,
|
||||
prompt: Option<String>,
|
||||
needs_entity_pick: Option<bool>,
|
||||
is_done: bool,
|
||||
}
|
||||
|
||||
impl PluginProcessInteractiveAdapter {
|
||||
@@ -660,6 +676,7 @@ impl PluginProcessInteractiveAdapter {
|
||||
command_id,
|
||||
prompt,
|
||||
needs_entity_pick,
|
||||
is_done: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,6 +684,20 @@ impl PluginProcessInteractiveAdapter {
|
||||
self.prompt = self.process.get_prompt(self.command_id).ok();
|
||||
self.needs_entity_pick = self.process.needs_entity_pick(self.command_id).ok();
|
||||
}
|
||||
|
||||
fn cancel(&mut self) {
|
||||
if !self.is_done {
|
||||
self.is_done = true;
|
||||
use ocs_plugin_api::ipc::protocol::InteractiveEvent;
|
||||
let _ = self.process.interactive_event(self.command_id, InteractiveEvent::Cancel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PluginProcessInteractiveAdapter {
|
||||
fn drop(&mut self) {
|
||||
self.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::command::CadCommand for PluginProcessInteractiveAdapter {
|
||||
@@ -686,6 +717,9 @@ impl crate::command::CadCommand for PluginProcessInteractiveAdapter {
|
||||
)
|
||||
.map(plugin_step_to_result)
|
||||
.unwrap_or(crate::command::CmdResult::Cancel);
|
||||
if matches!(result, crate::command::CmdResult::CommitAndExit(_) | crate::command::CmdResult::Cancel) {
|
||||
self.is_done = true;
|
||||
}
|
||||
self.refresh();
|
||||
result
|
||||
}
|
||||
@@ -696,9 +730,16 @@ impl crate::command::CadCommand for PluginProcessInteractiveAdapter {
|
||||
.interactive_event(self.command_id, InteractiveEvent::Enter)
|
||||
.map(plugin_step_to_result)
|
||||
.unwrap_or(crate::command::CmdResult::Cancel);
|
||||
if matches!(result, crate::command::CmdResult::CommitAndExit(_) | crate::command::CmdResult::Cancel) {
|
||||
self.is_done = true;
|
||||
}
|
||||
self.refresh();
|
||||
result
|
||||
}
|
||||
fn on_escape(&mut self) -> crate::command::CmdResult {
|
||||
self.cancel();
|
||||
crate::command::CmdResult::Cancel
|
||||
}
|
||||
fn needs_entity_pick(&self) -> bool {
|
||||
self.needs_entity_pick.unwrap_or(false)
|
||||
}
|
||||
@@ -715,6 +756,9 @@ impl crate::command::CadCommand for PluginProcessInteractiveAdapter {
|
||||
)
|
||||
.map(plugin_step_to_result)
|
||||
.unwrap_or(crate::command::CmdResult::Cancel);
|
||||
if matches!(result, crate::command::CmdResult::CommitAndExit(_) | crate::command::CmdResult::Cancel) {
|
||||
self.is_done = true;
|
||||
}
|
||||
self.refresh();
|
||||
result
|
||||
}
|
||||
@@ -1200,5 +1244,48 @@ mod tests {
|
||||
assert!(host.execute_command("ESC"));
|
||||
assert!(host.app.tabs[0].active_cmd.is_none());
|
||||
assert_eq!(host.app.tabs[0].pending_pause_tokens, None);
|
||||
|
||||
// 5. CLAYER command
|
||||
host.add_layer(ocs_plugin_api::host::LayerConfig {
|
||||
name: "TEST".to_string(),
|
||||
..Default::default()
|
||||
});
|
||||
assert!(host.execute_command("CLAYER TEST\n"));
|
||||
assert_eq!(host.document().header.current_layer_name, "TEST");
|
||||
|
||||
// 6. Non-typical command: POINT
|
||||
assert!(host.execute_command("POINT 50,50\n"));
|
||||
assert!(host.app.tabs[0].active_cmd.is_none());
|
||||
assert_eq!(host.document().entities().count(), 4);
|
||||
|
||||
// 7. Non-typical command: DONUT
|
||||
assert!(host.execute_command("DONUT 10 30 100,100 \n"));
|
||||
assert!(host.app.tabs[0].active_cmd.is_none());
|
||||
assert!(host.document().entities().count() >= 5);
|
||||
|
||||
// 8. Non-typical command: ELLIPSE
|
||||
assert!(host.execute_command("ELLIPSE 0,0 80,0 30\n"));
|
||||
assert!(host.app.tabs[0].active_cmd.is_none());
|
||||
|
||||
// 9. Non-typical inline commands: UCS
|
||||
assert!(host.execute_command("UCS ORIGIN 50,50,0\n"));
|
||||
assert!(host.app.tabs[0].active_ucs.is_some());
|
||||
assert!(host.execute_command("UCS W\n"));
|
||||
assert!(host.app.tabs[0].active_ucs.is_none());
|
||||
|
||||
// 10. Non-typical inline commands: SETVAR
|
||||
assert!(host.execute_command("SETVAR PDMODE 35\n"));
|
||||
|
||||
// 11. Viewport visual style via drive_headless_task
|
||||
assert!(host.execute_command("VSCURRENT FLATSHADED\n"));
|
||||
assert_eq!(
|
||||
host.app.tabs[0].render_mode,
|
||||
ocs_plugin_api::host::acadrust::entities::ViewportRenderMode::FlatShaded
|
||||
);
|
||||
|
||||
// 12. Drafting aids toggle via drive_headless_task
|
||||
let initial_grid = host.app.show_grid;
|
||||
assert!(host.execute_command("GRID\n"));
|
||||
assert_eq!(host.app.show_grid, !initial_grid);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user