forked from 1b-agent/opencadstudio-upstream
feat(command): add MTP/M2P midpoint-between-two-points modifier
Add MTP (Midpoint Between Two Points) as a keyword that can be typed during any point-picking command (e.g. LINE) to suspend the current command, prompt for two points, and return the midpoint to the parent. - New Mid2PointCommand registered under both "MTP" and "M2P" aliases - ReturnPoint CmdResult variant feeds the resolved midpoint back to the suspended parent command - MTP/M2P typed during a point step suspends the active command and starts the midpoint modifier; Escape restores the parent command - Preview wires show a rubber-band line with a triangle marker at the midpoint - Character-by-character typing is routed correctly even with dynamic input active (digits no longer leak into dyn fields) - Three automation tests cover the full workflow, ESC restoration, and dynamic-input character routing
This commit is contained in:
1 parent
f4eeba75df
commit
f77c337af4
4 files changed
+330
-9
No files matched your search
@@ -671,6 +671,29 @@ impl OpenCADStudio {
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
let is_mtp = token.trim_start_matches('_').eq_ignore_ascii_case("MTP")
|
||||
|| token.trim_start_matches('_').eq_ignore_ascii_case("M2P");
|
||||
if is_mtp {
|
||||
let is_point_step = !self.tabs[i]
|
||||
.active_cmd
|
||||
.as_ref()
|
||||
.map(|c| c.input_kind().wants_text())
|
||||
.unwrap_or(true)
|
||||
|| self.tabs[i]
|
||||
.active_cmd
|
||||
.as_ref()
|
||||
.map(|c| c.point_step_accepts_keywords())
|
||||
.unwrap_or(false);
|
||||
let not_entity_pick = !self.tabs[i]
|
||||
.active_cmd
|
||||
.as_ref()
|
||||
.map(|c| c.needs_entity_pick())
|
||||
.unwrap_or(false);
|
||||
if is_point_step && not_entity_pick {
|
||||
self.start_mtp_modifier(i);
|
||||
return Task::none();
|
||||
}
|
||||
}
|
||||
if let Some((coord, kind)) = super::helpers::parse_coord(token) {
|
||||
// Match the GUI command line: typed coordinates are in the active
|
||||
// UCS (relative offsets are rotated by the UCS axes), so a multi-
|
||||
@@ -712,7 +735,22 @@ impl OpenCADStudio {
|
||||
/// pick-first selector relaunching MOVE on the picked set works this way).
|
||||
/// Pure-selection commands (SELECTALL, QSELECT, …) run without an active
|
||||
/// command, so `was_active` is false and their selection is preserved.
|
||||
pub(super) fn apply_cmd_result(&mut self, result: CmdResult) -> Task<Message> {
|
||||
pub(super) fn apply_cmd_result(&mut self, mut result: CmdResult) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
if let CmdResult::ReturnPoint(pt) = result {
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
if let Some(mut suspended) = self.tabs[i].suspended_cmd.take() {
|
||||
self.last_point = Some(pt);
|
||||
let next_res = suspended.on_point(pt);
|
||||
self.tabs[i].active_cmd = Some(suspended);
|
||||
result = next_res;
|
||||
} else {
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.restore_pre_cmd_tangent();
|
||||
return Task::none();
|
||||
}
|
||||
}
|
||||
let settings = self.tabs[self.active_tab]
|
||||
.active_cmd
|
||||
.as_ref()
|
||||
@@ -877,6 +915,18 @@ impl OpenCADStudio {
|
||||
TableCellEditStart::Started
|
||||
}
|
||||
|
||||
pub(in crate::app) fn start_mtp_modifier(&mut self, i: usize) {
|
||||
let parent = self.tabs[i].active_cmd.take();
|
||||
self.tabs[i].suspended_cmd = parent;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
let cmd = crate::command::Mid2PointCommand::new();
|
||||
let prompt = crate::command::CadCommand::prompt(&cmd);
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
self.command_line.push_info(&prompt);
|
||||
self.command_line.set_step_options(Vec::new());
|
||||
self.refresh_active_cmd_preview(i);
|
||||
}
|
||||
|
||||
fn apply_cmd_result_inner(&mut self, result: CmdResult) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let preserve_commit_layer = self.tabs[i]
|
||||
@@ -2439,14 +2489,30 @@ impl OpenCADStudio {
|
||||
cancel @ (CmdResult::Cancel | CmdResult::CancelForSpaceChange) => {
|
||||
let space_changed = matches!(cancel, CmdResult::CancelForSpaceChange);
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.restore_pre_cmd_tangent();
|
||||
self.command_line.push_info(crate::t!(if space_changed {
|
||||
"Command cancelled because the active drawing space changed."
|
||||
if !space_changed && self.tabs[i].suspended_cmd.is_some() {
|
||||
self.tabs[i].active_cmd = self.tabs[i].suspended_cmd.take();
|
||||
let prompt = self.tabs[i].active_cmd.as_ref().map(|c| c.prompt());
|
||||
if let Some(p) = prompt {
|
||||
self.command_line.push_info(&p);
|
||||
}
|
||||
let opts = self.tabs[i]
|
||||
.active_cmd
|
||||
.as_ref()
|
||||
.map(|c| c.options())
|
||||
.unwrap_or_default();
|
||||
self.command_line.set_step_options(opts);
|
||||
self.refresh_active_cmd_preview(i);
|
||||
} else {
|
||||
"Command cancelled."
|
||||
}).as_ref());
|
||||
self.tabs[i].suspended_cmd = None;
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.restore_pre_cmd_tangent();
|
||||
self.command_line.push_info(crate::t!(if space_changed {
|
||||
"Command cancelled because the active drawing space changed."
|
||||
} else {
|
||||
"Command cancelled."
|
||||
}).as_ref());
|
||||
}
|
||||
}
|
||||
CmdResult::SelectByPath {
|
||||
path,
|
||||
@@ -4751,6 +4817,12 @@ impl OpenCADStudio {
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.restore_pre_cmd_tangent();
|
||||
}
|
||||
CmdResult::ReturnPoint(_) => {
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.restore_pre_cmd_tangent();
|
||||
}
|
||||
}
|
||||
// When no command is running the ribbon tool button still has to
|
||||
// visually deactivate. Keyboard focus is assigned below to whichever
|
||||
|
||||
Reference in new issue
Block a user