Files
OneCAD-Draft/src/ui/popup/snap_popup.rs
T
Hakan Seven 1738f9a79b feat(cursor): add crosshair options
Add persistent cursor sizing, pick aperture, color, pointer mode, isometric drafting planes, and SNAPANG-aligned grid behavior.\n\nRefs #413
2026-08-08 21:32:44 +03:00

126 lines
3.8 KiB
Rust

//! OpenCADStudio-style OSNAP status menu.
use iced::widget::{button, checkbox, column, container, row, text};
use iced::{Background, Element, Fill, Length, Theme};
use crate::app::Message;
use crate::app::settings::IsoPlane;
use crate::snap::{SnapType, Snapper, ALL_SNAP_MODES};
use crate::ui::statusbar::status_menu::Entry;
use crate::t;
pub fn menu_entries<'a>(
snapper: &'a Snapper,
isometric: bool,
iso_plane: IsoPlane,
) -> Vec<Entry<'a>> {
let all_on = snapper.all_on();
let none_on = snapper.none_on();
let header = row![
header_btn("Select All", Message::SnapSelectAll, !all_on),
header_btn("Clear All", Message::SnapClearAll, !none_on),
]
.spacing(1)
.padding([4u16, 8]);
// Divider
let divider = container(iced::widget::Space::new().height(1))
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(
theme.palette().background.weak.color,
)),
..Default::default()
})
.width(Fill)
.padding([0, 4]);
let mut iso_planes = row![].spacing(3);
for plane in IsoPlane::ALL {
iso_planes = iso_planes.push(
button(text(t!(plane.label())).size(10))
.on_press(Message::SetIsoPlane(plane))
.style(if isometric && iso_plane == plane {
button::primary
} else {
button::secondary
})
.padding([3, 8]),
);
}
let drafting = column![
row![
checkbox(isometric)
.on_toggle(|_| Message::ToggleIsometricDrafting)
.size(14),
text(t!("Isometric drafting")).size(11),
]
.spacing(6)
.align_y(iced::Center),
iso_planes,
text(t!("F5 cycles the active plane.")).size(10),
]
.spacing(5)
.padding([5, 8]);
let drafting_divider = container(iced::widget::Space::new().height(1))
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(
theme.palette().background.weak.color,
)),
..Default::default()
})
.width(Fill)
.padding([0, 4]);
let mut entries = vec![
Entry::stay(header),
Entry::stay(divider),
Entry::stay(drafting),
Entry::stay(drafting_divider),
];
for &(snap_type, _glyph, label) in ALL_SNAP_MODES {
entries.push(Entry::stay(snap_row(
snap_type,
label,
snapper.is_on(snap_type),
)));
}
entries
}
// ── Individual snap row ───────────────────────────────────────────────────
fn snap_row<'a>(snap_type: SnapType, label: &'a str, active: bool) -> Element<'a, Message> {
let checkmark = crate::ui::icons::themed_check_cell(active);
// SVG marker (not a Unicode glyph) so the symbols render on the web build,
// whose bundled font lacks them and showed tofu boxes. (#138)
let icon_el = container(crate::ui::icons::themed_success::<Message>(
crate::ui::icons::osnap(snap_type),
13.0,
))
.width(Length::Fixed(16.0))
.align_x(iced::Center);
let label_el = text(t!(label)).size(11);
let content = row![checkmark, icon_el, label_el]
.spacing(4)
.align_y(iced::Center);
button(content)
.on_press(Message::ToggleSnap(snap_type))
.style(button::subtle)
.width(Fill)
.padding([3, 8])
.into()
}
fn header_btn(label: &str, msg: Message, enabled: bool) -> Element<'_, Message> {
let b = button(text(t!(label)).size(10));
let b = if enabled { b.on_press(msg) } else { b };
b.style(button::secondary)
.padding([3, 8])
.into()
}