Files
OneCAD-Draft/src/ui/style/point_style.rs
T

210 lines
6.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Point Style (DDPTYPE) dialog — pick a PDMODE glyph from a grid and set the
//! point size (PDSIZE), relative to the screen or in absolute units.
//!
//! Changes apply live to the active document header; the renderer rebuilds the
//! point glyphs (see `entities::point`). Command entry (`PDMODE` / `PDSIZE`)
//! does the same without the dialog.
use crate::app::Message;
use crate::t;
use crate::ui::style::common::muted_style;
use crate::ui::style::form::{dialog_button, field_style_with_radius, form_radio};
use iced::widget::{button, canvas, column, container, row, text, text_input, Space};
use iced::{mouse, Background, Border, Element, Length, Point, Rectangle, Size, Theme};
use std::borrow::Cow;
const CELL_PX: f32 = 44.0;
/// Glyph-grid columns (low-nibble shape) × rows (enclosure bits):
/// shapes: 0=dot, 1=none, 2='+', 3='×', 4='|'
/// enclosures: 0=none, 32=circle, 64=square, 96=both
const ENCLOSURES: [i16; 4] = [0, 32, 64, 96];
const SHAPES: [i16; 5] = [0, 1, 2, 3, 4];
/// Canvas that renders a single PDMODE glyph inside a grid cell.
struct GlyphCanvas {
mode: i16,
}
impl canvas::Program<Message> for GlyphCanvas {
type State = ();
fn draw(
&self,
_state: &(),
renderer: &iced::Renderer,
theme: &Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
) -> Vec<canvas::Geometry> {
let mut frame = canvas::Frame::new(renderer, bounds.size());
let glyph = theme.palette().background.base.text;
let (cx, cy) = (bounds.width * 0.5, bounds.height * 0.5);
let r = bounds.width.min(bounds.height) * 0.30;
let stroke = canvas::Stroke {
width: 1.4,
style: canvas::Style::Solid(glyph),
..Default::default()
};
let line = |a: Point, b: Point| canvas::Path::line(a, b);
match self.mode & 0x0F {
0 => frame.fill(&canvas::Path::circle(Point::new(cx, cy), 2.4), glyph),
1 => {}
2 => {
frame.stroke(
&line(Point::new(cx - r, cy), Point::new(cx + r, cy)),
stroke.clone(),
);
frame.stroke(
&line(Point::new(cx, cy - r), Point::new(cx, cy + r)),
stroke.clone(),
);
}
3 => {
frame.stroke(
&line(Point::new(cx - r, cy - r), Point::new(cx + r, cy + r)),
stroke.clone(),
);
frame.stroke(
&line(Point::new(cx - r, cy + r), Point::new(cx + r, cy - r)),
stroke.clone(),
);
}
4 => frame.stroke(
&line(Point::new(cx, cy), Point::new(cx, cy - r)),
stroke.clone(),
),
_ => {}
}
if self.mode & 32 != 0 {
frame.stroke(&canvas::Path::circle(Point::new(cx, cy), r), stroke.clone());
}
if self.mode & 64 != 0 {
let sq =
canvas::Path::rectangle(Point::new(cx - r, cy - r), Size::new(2.0 * r, 2.0 * r));
frame.stroke(&sq, stroke.clone());
}
vec![frame.into_geometry()]
}
}
fn cell<'a>(value: i16, selected: bool) -> Element<'a, Message> {
let glyph = canvas(GlyphCanvas { mode: value })
.width(Length::Fixed(CELL_PX))
.height(Length::Fixed(CELL_PX));
button(glyph)
.padding(0)
.on_press(Message::PointStyleSetMode(value))
.style(move |theme: &Theme, status| {
let palette = theme.palette();
let pair = if selected {
palette.primary.strong
} else if matches!(status, button::Status::Hovered | button::Status::Pressed) {
palette.background.strong
} else {
palette.background.weak
};
button::Style {
background: Some(Background::Color(pair.color)),
border: Border {
color: palette.background.neutral.color,
width: 1.0,
radius: 3.0.into(),
},
text_color: pair.text,
..Default::default()
}
})
.into()
}
pub fn view_window<'a>(
pdmode: i16,
relative: bool,
size_buf: &str,
sizing: crate::ui::modal::ModalSizing,
) -> Element<'a, Message> {
let width = sizing.width;
let height = sizing.height;
// Glyph grid: a row per enclosure, a cell per shape.
let mut grid = column![].spacing(6);
for enc in ENCLOSURES {
let mut r = row![].spacing(6);
for sh in SHAPES {
let value = enc + sh;
r = r.push(cell(value, value == pdmode));
}
grid = grid.push(r);
}
let size_row = row![
text(t!("Point Size:")).size(13),
Space::new().width(10),
text_input("0", size_buf)
.on_input(Message::PointStyleSizeInput)
.on_submit(Message::PointStyleApplySize)
.style(|theme, status| field_style_with_radius(theme, status, 4.0))
.size(13)
.width(110),
Space::new().width(6),
text(if relative {
Cow::Borrowed("%")
} else {
t!("units")
})
.size(12)
.style(muted_style),
]
.align_y(iced::Center);
let radios = column![
form_radio(
t!("Set Size Relative to Screen"),
true,
Some(relative),
Message::PointStyleSizeRelative,
),
form_radio(
t!("Set Size in Absolute Units"),
false,
Some(relative),
Message::PointStyleSizeRelative,
),
]
.spacing(6);
let actions = row![
Space::new().width(width),
dialog_button(t!("OK"), Message::PointStyleOk, true),
dialog_button(t!("Cancel"), Message::CloseModal, false),
]
.spacing(8)
.align_y(iced::Center);
container(
column![
text(t!("Point Style")).size(18),
Space::new().height(6),
grid,
Space::new().height(12),
size_row,
Space::new().height(8),
radios,
Space::new().height(12),
actions.width(width),
]
.spacing(4)
.padding(20)
.width(width)
.height(height),
)
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(theme.palette().background.base.color)),
..Default::default()
})
.width(width)
.height(height)
.into()
}