forked from 1b-agent/opencadstudio-upstream
Add dynamic input for grip-based geometry editing (#1138)
This commit is contained in:
1 parent
5ffbf598a9
commit
d47e3234d8
9 files changed
+378
-39
No files matched your search
+123
-12
@@ -199,6 +199,16 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
.buffer
|
||||
.get_or_insert_with(String::new)
|
||||
.push_str(&s);
|
||||
if self.tabs[i].active_grip.as_ref().is_some_and(|grip| {
|
||||
matches!(
|
||||
grip.mode,
|
||||
GripEditMode::Lengthen
|
||||
| GripEditMode::Radius
|
||||
| GripEditMode::ArcLength
|
||||
)
|
||||
}) {
|
||||
self.command_line.input.push_str(&s);
|
||||
}
|
||||
} else {
|
||||
// Command-line entry is shown uppercase — except in
|
||||
// free-form text prompts, where the typed case is the
|
||||
@@ -242,6 +252,16 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
if buf.is_empty() {
|
||||
self.tabs[i].dyn_fields[a].buffer = None;
|
||||
}
|
||||
if self.tabs[i].active_grip.as_ref().is_some_and(|grip| {
|
||||
matches!(
|
||||
grip.mode,
|
||||
GripEditMode::Lengthen
|
||||
| GripEditMode::Radius
|
||||
| GripEditMode::ArcLength
|
||||
)
|
||||
}) {
|
||||
self.command_line.input.pop();
|
||||
}
|
||||
return self.focus_cmd_input();
|
||||
}
|
||||
}
|
||||
@@ -277,13 +297,49 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
}
|
||||
// Grip-menu value prompt — consume the typed number and
|
||||
// route it through `apply_grip_menu_value`.
|
||||
// Interactive grip prompts are valid only while their matching
|
||||
// grip edit is alive. Do not let a stale Radius / Lengthen /
|
||||
// Arc Length prompt consume Enter or coordinates from a later
|
||||
// drawing command.
|
||||
let stale_interactive_grip_prompt = self.grip_pending.as_ref().is_some_and(|pending| {
|
||||
let expected_mode = match pending.action {
|
||||
crate::scene::model::object::GripMenuAction::Lengthen => {
|
||||
Some(GripEditMode::Lengthen)
|
||||
}
|
||||
crate::scene::model::object::GripMenuAction::Radius => {
|
||||
Some(GripEditMode::Radius)
|
||||
}
|
||||
crate::scene::model::object::GripMenuAction::ArcLength => {
|
||||
Some(GripEditMode::ArcLength)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
expected_mode.is_some_and(|mode| {
|
||||
!self.tabs[self.active_tab].active_grip.as_ref().is_some_and(|grip| {
|
||||
grip.mode == mode
|
||||
&& grip.handle == pending.handle
|
||||
&& grip.grip_id == pending.grip_id
|
||||
})
|
||||
})
|
||||
});
|
||||
if stale_interactive_grip_prompt {
|
||||
self.grip_pending = None;
|
||||
}
|
||||
if let Some(pending) = self.grip_pending.take() {
|
||||
let i = self.active_tab;
|
||||
if self.reject_locked_edit(i, pending.handle) {
|
||||
self.cancel_active_grip_edit();
|
||||
return Task::none();
|
||||
}
|
||||
let raw = crate::app::expr_eval::eval_to_string(self.command_line.input.trim());
|
||||
// Dynamic Input keeps numeric typing in its focused field.
|
||||
// Fall back to the command-line buffer so the established
|
||||
// prompt workflow remains unchanged when DYN is disabled.
|
||||
let dyn_value = self.tabs[i]
|
||||
.dyn_fields
|
||||
.iter()
|
||||
.find_map(|field| field.buffer.as_deref());
|
||||
let entered = dyn_value.unwrap_or(self.command_line.input.trim());
|
||||
let raw = crate::app::expr_eval::eval_to_string(entered);
|
||||
self.command_line.input.clear();
|
||||
let Ok(v) = raw.parse::<f64>() else {
|
||||
self.command_line.push_error(crate::tf!(
|
||||
@@ -293,15 +349,27 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
self.grip_pending = Some(pending);
|
||||
return self.focus_cmd_input();
|
||||
};
|
||||
let interactive_lengthen = self.tabs[i]
|
||||
let interactive_value_grip = self.tabs[i]
|
||||
.active_grip
|
||||
.as_ref()
|
||||
.is_some_and(|grip| {
|
||||
grip.mode == GripEditMode::Lengthen
|
||||
matches!(
|
||||
(grip.mode, pending.action),
|
||||
(
|
||||
GripEditMode::Lengthen,
|
||||
crate::scene::model::object::GripMenuAction::Lengthen,
|
||||
) | (
|
||||
GripEditMode::Radius,
|
||||
crate::scene::model::object::GripMenuAction::Radius,
|
||||
) | (
|
||||
GripEditMode::ArcLength,
|
||||
crate::scene::model::object::GripMenuAction::ArcLength,
|
||||
)
|
||||
)
|
||||
&& grip.handle == pending.handle
|
||||
&& grip.grip_id == pending.grip_id
|
||||
});
|
||||
if interactive_lengthen {
|
||||
if interactive_value_grip {
|
||||
self.cancel_active_grip_edit();
|
||||
}
|
||||
use crate::entities::traits::EntityTypeOps;
|
||||
@@ -1306,7 +1374,12 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
action: item.action,
|
||||
label,
|
||||
});
|
||||
if matches!(item.action, GripMenuAction::Lengthen) {
|
||||
if matches!(
|
||||
item.action,
|
||||
GripMenuAction::Lengthen
|
||||
| GripMenuAction::Radius
|
||||
| GripMenuAction::ArcLength
|
||||
) {
|
||||
if let Some((_, grip)) = self.tabs[i]
|
||||
.selected_grip_handles
|
||||
.iter()
|
||||
@@ -1315,14 +1388,52 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
**owner == popup.handle && grip.id == popup.grip_id
|
||||
})
|
||||
{
|
||||
self.tabs[i].active_grip = Some(GripEdit::lengthen(
|
||||
popup.handle,
|
||||
popup.grip_id,
|
||||
grip.world,
|
||||
));
|
||||
if self.grip_originals.is_empty() {
|
||||
self.grip_originals = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(popup.handle)
|
||||
.cloned()
|
||||
.map(|entity| vec![(popup.handle, entity)])
|
||||
.unwrap_or_default();
|
||||
}
|
||||
self.tabs[i].active_grip = Some(match item.action {
|
||||
GripMenuAction::Radius => GripEdit::radius(
|
||||
popup.handle,
|
||||
popup.grip_id,
|
||||
grip.world,
|
||||
),
|
||||
GripMenuAction::ArcLength => GripEdit::arc_length(
|
||||
popup.handle,
|
||||
popup.grip_id,
|
||||
grip.world,
|
||||
),
|
||||
_ => GripEdit::lengthen(
|
||||
popup.handle,
|
||||
popup.grip_id,
|
||||
grip.world,
|
||||
),
|
||||
});
|
||||
}
|
||||
// Popup actions do not pass through the normal grip
|
||||
// press handler, which is where dynamic fields are
|
||||
// usually seeded. Build the value field
|
||||
// immediately so it is visible before the next mouse
|
||||
// move (and so keyboard input has a field to target).
|
||||
self.sync_dyn_fields();
|
||||
if matches!(item.action, GripMenuAction::Radius) {
|
||||
self.command_line.push_info(
|
||||
crate::t!("Specify point or enter radius:").as_ref(),
|
||||
);
|
||||
} else if matches!(item.action, GripMenuAction::ArcLength) {
|
||||
self.command_line.push_info(
|
||||
crate::t!("Specify point or enter arc length:").as_ref(),
|
||||
);
|
||||
} else {
|
||||
self.command_line.push_info(
|
||||
crate::t!("Specify point or enter distance:").as_ref(),
|
||||
);
|
||||
}
|
||||
self.command_line
|
||||
.push_info(crate::t!("Specify point or enter distance:").as_ref());
|
||||
} else {
|
||||
self.command_line.push_info(crate::tf!("{label}:").as_ref());
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user