forked from 1b-agent/opencadstudio-upstream
Complete automatic constraint behavior
This commit is contained in:
1 parent
d738ebfcc6
commit
66be530655
25 files changed
+1494
-102
No files matched your search
+26
-1
@@ -94,6 +94,27 @@ fn constraint_glyph_offsets(glyphs: &[(Point, [f32; 2], String, bool)]) -> Vec<f
|
||||
offsets
|
||||
}
|
||||
|
||||
pub(crate) fn constraint_glyph_hit(
|
||||
glyphs: &[(Point, [f32; 2], String, bool)],
|
||||
cursor: Point,
|
||||
) -> Option<usize> {
|
||||
let offsets = constraint_glyph_offsets(glyphs);
|
||||
glyphs
|
||||
.iter()
|
||||
.zip(offsets)
|
||||
.enumerate()
|
||||
.rev()
|
||||
.find_map(|(index, ((anchor, outward, label, _), tangent_offset))| {
|
||||
let (top_left, size) =
|
||||
constraint_glyph_box(*anchor, *outward, label, tangent_offset);
|
||||
(cursor.x >= top_left.x
|
||||
&& cursor.x <= top_left.x + size.width
|
||||
&& cursor.y >= top_left.y
|
||||
&& cursor.y <= top_left.y + size.height)
|
||||
.then_some(index)
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert CURSORSIZE to a screen-space arm length while keeping the original
|
||||
/// 60 px cursor at the default value and the full-viewport result at 100.
|
||||
pub(crate) fn crosshair_arm_px(bounds: iced::Rectangle, value: i32) -> f32 {
|
||||
@@ -989,6 +1010,11 @@ impl canvas::Program<Message> for SelectionCanvas {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(pos) = cursor.position_in(bounds) {
|
||||
if constraint_glyph_hit(&self.constraint_glyphs, pos).is_some() {
|
||||
return mouse::Interaction::Pointer;
|
||||
}
|
||||
}
|
||||
// The resize cursor over a divider is supplied by the input pane_grid
|
||||
// layered above this overlay; `draw` only suppresses the CAD crosshair
|
||||
// there (see `divider_under`).
|
||||
@@ -1703,7 +1729,6 @@ impl canvas::Program<Message> for SelectionCanvas {
|
||||
frame.stroke(&b1, stroke.clone());
|
||||
frame.stroke(&b2, stroke);
|
||||
}
|
||||
// Constraint glyphs are visual-only and have no hit testing.
|
||||
if !self.constraint_glyphs.is_empty() {
|
||||
let offsets = constraint_glyph_offsets(&self.constraint_glyphs);
|
||||
let normal_bg = theme.palette().primary.base.color;
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
use crate::app::settings::AutoConstrainSettings;
|
||||
use crate::app::Message;
|
||||
use iced::widget::{button, checkbox, column, container, row, scrollable, text, text_input, Space};
|
||||
use iced::{Element, Fill};
|
||||
|
||||
pub fn view_window<'a>(
|
||||
settings: &'a AutoConstrainSettings,
|
||||
selected_row: usize,
|
||||
distance_input: &'a str,
|
||||
angle_input: &'a str,
|
||||
sizing: crate::ui::modal::ModalSizing,
|
||||
) -> Element<'a, Message> {
|
||||
let heading = row![
|
||||
text(crate::t!("Priority")).width(70),
|
||||
text(crate::t!("Constraint Type")).width(Fill),
|
||||
text(crate::t!("Apply")).width(60),
|
||||
]
|
||||
.spacing(8);
|
||||
|
||||
let mut list = column![heading].spacing(3);
|
||||
for (index, kind) in settings.priority.iter().copied().enumerate() {
|
||||
let enabled = settings.enabled.contains(&kind);
|
||||
let label = row![
|
||||
text(format!("{}", index + 1)).width(70),
|
||||
text(crate::t!(kind.label())).width(Fill),
|
||||
checkbox(enabled)
|
||||
.on_toggle(move |_| Message::AutoConstrainToggleKind(kind))
|
||||
.width(60),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center);
|
||||
list = list.push(
|
||||
button(label)
|
||||
.on_press(Message::AutoConstrainSelectRow(index))
|
||||
.style(if selected_row == index {
|
||||
button::primary
|
||||
} else {
|
||||
button::text
|
||||
})
|
||||
.width(Fill)
|
||||
.padding([5, 8]),
|
||||
);
|
||||
}
|
||||
|
||||
let reorder = column![
|
||||
button(text(crate::t!("Move Up")))
|
||||
.on_press_maybe((selected_row > 0).then_some(Message::AutoConstrainMoveUp))
|
||||
.width(Fill),
|
||||
button(text(crate::t!("Move Down")))
|
||||
.on_press_maybe(
|
||||
(selected_row + 1 < settings.priority.len())
|
||||
.then_some(Message::AutoConstrainMoveDown)
|
||||
)
|
||||
.width(Fill),
|
||||
Space::new().height(8),
|
||||
button(text(crate::t!("Select All")))
|
||||
.on_press(Message::AutoConstrainSelectAll)
|
||||
.width(Fill),
|
||||
button(text(crate::t!("Clear All")))
|
||||
.on_press(Message::AutoConstrainClearAll)
|
||||
.width(Fill),
|
||||
button(text(crate::t!("Reset")))
|
||||
.on_press(Message::AutoConstrainReset)
|
||||
.width(Fill),
|
||||
]
|
||||
.spacing(6)
|
||||
.width(125);
|
||||
|
||||
let types = row![
|
||||
container(scrollable(list).height(260))
|
||||
.padding(6)
|
||||
.width(Fill),
|
||||
reorder
|
||||
]
|
||||
.spacing(12);
|
||||
|
||||
let conditions = column![
|
||||
checkbox(settings.tangent_must_share_point)
|
||||
.label(crate::t!("Tangent objects must share an intersection point"))
|
||||
.on_toggle(|_| Message::AutoConstrainToggleTangentPoint),
|
||||
checkbox(settings.perpendicular_must_intersect)
|
||||
.label(crate::t!("Perpendicular objects must share an intersection point"))
|
||||
.on_toggle(|_| Message::AutoConstrainTogglePerpendicularIntersection),
|
||||
]
|
||||
.spacing(8);
|
||||
|
||||
let tolerances = column![
|
||||
text(crate::t!("Tolerances")).size(14),
|
||||
row![
|
||||
text(crate::t!("Distance")).width(130),
|
||||
text_input("0.05", distance_input)
|
||||
.on_input(Message::AutoConstrainDistanceChanged)
|
||||
.width(150),
|
||||
]
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
text(crate::t!("Angle")).width(130),
|
||||
text_input("1", angle_input)
|
||||
.on_input(Message::AutoConstrainAngleChanged)
|
||||
.width(150),
|
||||
text("°"),
|
||||
]
|
||||
.align_y(iced::Center),
|
||||
]
|
||||
.spacing(7);
|
||||
|
||||
let actions = row![
|
||||
Space::new().width(Fill),
|
||||
button(text(crate::t!("OK")))
|
||||
.on_press(Message::AutoConstrainOk)
|
||||
.style(button::primary)
|
||||
.padding([6, 18]),
|
||||
button(text(crate::t!("Apply")))
|
||||
.on_press(Message::AutoConstrainApply)
|
||||
.style(button::secondary)
|
||||
.padding([6, 18]),
|
||||
button(text(crate::t!("Cancel")))
|
||||
.on_press(Message::AutoConstrainCancel)
|
||||
.style(button::secondary)
|
||||
.padding([6, 18]),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center);
|
||||
|
||||
container(
|
||||
column![
|
||||
text(crate::t!("Auto Constrain")).size(16),
|
||||
types,
|
||||
conditions,
|
||||
tolerances,
|
||||
Space::new().height(Fill),
|
||||
actions,
|
||||
]
|
||||
.spacing(14)
|
||||
.width(sizing.width)
|
||||
.height(sizing.height),
|
||||
)
|
||||
.padding([14, 16])
|
||||
.width(sizing.width)
|
||||
.height(sizing.height)
|
||||
.style(container::rounded_box)
|
||||
.into()
|
||||
}
|
||||
@@ -5,6 +5,7 @@ pub mod layer_state_manager;
|
||||
pub mod drawing_units;
|
||||
pub mod geometric_tolerance;
|
||||
pub mod drafting_settings;
|
||||
pub mod auto_constrain_settings;
|
||||
pub mod layer_translator;
|
||||
pub mod plot;
|
||||
pub mod print_all;
|
||||
|
||||
Reference in new issue
Block a user