forked from 1b-agent/opencadstudio-upstream
Add a Drafting page for the rotation angle and the polar increment
Two drafting preferences had no proper home. The Drafting Settings dialog displays the drafting rotation and offers only a Reset button — there was no way to type an angle short of `SETVAR SNAPANG`. The polar tracking increment existed in exactly one place: a pop-up hanging off the status-bar pill. The rotation is a text field over an edit buffer rather than the value itself, so clearing it to retype does not snap the crosshair to zero on the way through; the buffer is reseeded whenever the value is restored from behind it. Angles render without a trailing `.0`, matching how the polar pop-up has always shown them — same kind of number, same reader. Snap modes, grid spacing and object snap stay in Drafting Settings, where they belong; the page links to it. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
9c3f3625da
commit
b5f4b4d1cc
6 files changed
+123
No files matched your search
@@ -517,6 +517,9 @@ pub(super) struct OpenCADStudio {
|
||||
crosshair_color: Option<[u8; 3]>,
|
||||
/// Editable Options buffer for the crosshair colour.
|
||||
crosshair_color_input: String,
|
||||
/// Edit buffer for the SNAPANG field on the Options Drafting page. Kept
|
||||
/// separate from `snap_angle_deg` so a half-typed angle is not parsed.
|
||||
snap_angle_input: String,
|
||||
/// Model-space lineweight preview scale, in percent (25..=200).
|
||||
lineweight_display_scale: i32,
|
||||
/// Isometric drafting state and active axis pair.
|
||||
@@ -2029,6 +2032,10 @@ pub enum Message {
|
||||
QdimSnapPriorityChanged(u8),
|
||||
/// Change which annotative objects pick up a new scale (ANNOAUTOSCALE).
|
||||
AnnoAutoScaleChanged(i8),
|
||||
/// Edit the drafting rotation field; parsed when it holds a valid angle (SNAPANG).
|
||||
SnapAngleInputChanged(String),
|
||||
/// Change the polar tracking increment in degrees.
|
||||
PolarIncrementChanged(f32),
|
||||
/// Restore Model Space display/canvas appearance to defaults.
|
||||
RestoreModelSpaceDisplayDefaults,
|
||||
/// Restore Selection visual effect settings to defaults.
|
||||
@@ -3429,6 +3436,7 @@ impl OpenCADStudio {
|
||||
cursor_type: settings::CursorType::Crosshair,
|
||||
crosshair_color: None,
|
||||
crosshair_color_input: String::new(),
|
||||
snap_angle_input: "0".to_string(),
|
||||
lineweight_display_scale: 100,
|
||||
isometric_drafting: false,
|
||||
iso_plane: settings::IsoPlane::Left,
|
||||
|
||||
@@ -145,6 +145,18 @@ pub(crate) fn snaps_from_osmode(osmode: i32) -> (Vec<SnapType>, bool) {
|
||||
(modes, osmode & OSMODE_SUPPRESS == 0)
|
||||
}
|
||||
|
||||
/// Render a drafting angle without a trailing `.0`, so `22.5` but `30`.
|
||||
///
|
||||
/// The polar pop-up formats its presets the same way; both are showing the
|
||||
/// same kind of number to the same person.
|
||||
pub fn format_snap_angle(deg: f32) -> String {
|
||||
if (deg - deg.round()).abs() < 1e-4 {
|
||||
format!("{}", deg.round() as i32)
|
||||
} else {
|
||||
format!("{deg}")
|
||||
}
|
||||
}
|
||||
|
||||
/// GRIPOBJLIMIT default: past this many selected objects, no grips are drawn.
|
||||
pub const DEFAULT_GRIP_OBJECT_LIMIT: i32 = 100;
|
||||
|
||||
|
||||
@@ -452,6 +452,9 @@ impl OpenCADStudio {
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
// The Options field edits a buffer rather than the value, so it has to
|
||||
// be reseeded whenever the value is restored from behind it.
|
||||
self.snap_angle_input = crate::app::settings::format_snap_angle(self.snap_angle_deg);
|
||||
// Ortho + running OSNAP are per-drawing (adopted from the header on
|
||||
// open / tab switch), not app-global, so they are not applied here.
|
||||
self.snapper.otrack_enabled = s.otrack;
|
||||
|
||||
@@ -6266,6 +6266,27 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
// Typed into freely; only committed when it parses, so clearing
|
||||
// the field to retype does not snap the crosshair back to zero.
|
||||
Message::SnapAngleInputChanged(value) => {
|
||||
self.snap_angle_input = value;
|
||||
if let Ok(angle) = self.snap_angle_input.trim().parse::<f32>() {
|
||||
if angle.is_finite() {
|
||||
self.snap_angle_deg = angle.rem_euclid(360.0);
|
||||
self.persist_settings_if_changed();
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::PolarIncrementChanged(deg) => {
|
||||
if deg.is_finite() && deg > 0.0 {
|
||||
self.polar_increment_deg = deg;
|
||||
self.persist_settings_if_changed();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::PickDragRectToggled(rectangle) => {
|
||||
self.pick_drag_rect = rectangle;
|
||||
self.persist_settings_if_changed();
|
||||
|
||||
@@ -204,7 +204,9 @@ impl OpenCADStudio {
|
||||
dimension_continue_mode: self.dimension_continue_mode,
|
||||
qdim_snap_priority: self.quick_dimension_snap_priority,
|
||||
annotation_auto_scale: self.annotation_auto_scale,
|
||||
polar_increment_deg: self.polar_increment_deg,
|
||||
},
|
||||
&self.snap_angle_input,
|
||||
self.double_click_block_refedit,
|
||||
self.double_click_block_attedit,
|
||||
self.cursor_type,
|
||||
|
||||
@@ -27,6 +27,7 @@ pub enum OptionsTab {
|
||||
General,
|
||||
OpenAndSave,
|
||||
Display,
|
||||
Drafting,
|
||||
Selection,
|
||||
UserPreferences,
|
||||
Drawing,
|
||||
@@ -62,6 +63,8 @@ pub struct AppPrefs {
|
||||
/// ANNOAUTOSCALE, -4..=4. The sign is on/off; the magnitude selects which
|
||||
/// objects a newly added scale reaches.
|
||||
pub annotation_auto_scale: i8,
|
||||
/// Polar tracking increment in degrees.
|
||||
pub polar_increment_deg: f32,
|
||||
}
|
||||
|
||||
/// The Selection-card settings that live on `UserSettings` rather than on the
|
||||
@@ -105,6 +108,7 @@ pub fn view_window<'a>(
|
||||
cursor_size: i32,
|
||||
selection: SelectionPrefs,
|
||||
prefs: AppPrefs,
|
||||
snap_angle_input: &'a str,
|
||||
double_click_block_refedit: bool,
|
||||
double_click_block_attedit: bool,
|
||||
cursor_type: CursorType,
|
||||
@@ -1150,11 +1154,83 @@ pub fn view_window<'a>(
|
||||
.spacing(0)
|
||||
.width(sizing.width);
|
||||
|
||||
|
||||
// Snap modes, grid and object snap stay in the Drafting Settings dialog.
|
||||
// These two have no home: Drafting Settings shows the rotation angle but
|
||||
// offers only Reset, and the polar increment lives solely in a status-bar
|
||||
// pop-up.
|
||||
let polar_options = [90.0f32, 45.0, 30.0, 22.5, 18.0, 15.0, 10.0, 5.0, 1.0]
|
||||
.into_iter()
|
||||
.map(|value| Labelled {
|
||||
label: format!("{}°", crate::app::settings::format_snap_angle(value)),
|
||||
value,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let selected_polar = polar_options
|
||||
.iter()
|
||||
.find(|choice| (choice.value - prefs.polar_increment_deg).abs() < 1e-4)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Labelled {
|
||||
value: prefs.polar_increment_deg,
|
||||
label: format!(
|
||||
"{}°",
|
||||
crate::app::settings::format_snap_angle(prefs.polar_increment_deg)
|
||||
),
|
||||
});
|
||||
|
||||
let drafting = column![
|
||||
text(crate::t!("Drafting")).size(15),
|
||||
Space::new().height(10),
|
||||
row![
|
||||
text(crate::t!("Drafting rotation")).size(12).width(150),
|
||||
text_input("0", snap_angle_input)
|
||||
.on_input(Message::SnapAngleInputChanged)
|
||||
.width(110),
|
||||
text(crate::t!("degrees")).size(11),
|
||||
]
|
||||
.spacing(10)
|
||||
.align_y(iced::Center),
|
||||
Space::new().height(6),
|
||||
text(crate::t!(
|
||||
"Rotates the crosshair and the snap grid in the active UCS (SNAPANG)."
|
||||
))
|
||||
.size(11)
|
||||
.width(sizing.width),
|
||||
Space::new().height(14),
|
||||
row![
|
||||
text(crate::t!("Polar tracking increment")).size(12).width(150),
|
||||
iced::widget::pick_list(Some(selected_polar), polar_options, |choice| {
|
||||
choice.label.clone()
|
||||
})
|
||||
.on_select(|choice| Message::PolarIncrementChanged(choice.value))
|
||||
.width(Fill),
|
||||
]
|
||||
.spacing(10)
|
||||
.align_y(iced::Center),
|
||||
Space::new().height(24),
|
||||
text(crate::t!("Snap and Grid")).size(15),
|
||||
Space::new().height(10),
|
||||
row![
|
||||
text(crate::t!("Snap modes, grid spacing and object snap"))
|
||||
.size(12)
|
||||
.width(Fill),
|
||||
button(text(crate::t!("Drafting Settings…")).size(11))
|
||||
.on_press(Message::ToggleSnapPopup)
|
||||
.padding([4, 10])
|
||||
.style(button::secondary),
|
||||
]
|
||||
.spacing(10)
|
||||
.align_y(iced::Center),
|
||||
]
|
||||
.spacing(0)
|
||||
.width(sizing.width);
|
||||
|
||||
let content: Element<'a, Message> = match active_tab {
|
||||
OptionsTab::General => general.into(),
|
||||
OptionsTab::Display => display_element.into(),
|
||||
OptionsTab::Selection => selection.into(),
|
||||
OptionsTab::OpenAndSave => open_and_save.into(),
|
||||
OptionsTab::Drafting => drafting.into(),
|
||||
OptionsTab::UserPreferences => user_prefs.into(),
|
||||
OptionsTab::Drawing => drawing.into(),
|
||||
};
|
||||
@@ -1174,6 +1250,7 @@ pub fn view_window<'a>(
|
||||
tab_button(crate::t!("General"), OptionsTab::General),
|
||||
tab_button(crate::t!("Open and Save"), OptionsTab::OpenAndSave),
|
||||
tab_button(crate::t!("Display"), OptionsTab::Display),
|
||||
tab_button(crate::t!("Drafting"), OptionsTab::Drafting),
|
||||
tab_button(crate::t!("Selection"), OptionsTab::Selection),
|
||||
tab_button(crate::t!("User Preferences"), OptionsTab::UserPreferences),
|
||||
tab_button(crate::t!("Drawing"), OptionsTab::Drawing),
|
||||
|
||||
Reference in new issue
Block a user