forked from 1b-agent/opencadstudio-upstream
210 lines
6.6 KiB
Rust
210 lines
6.6 KiB
Rust
//! 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()
|
||
}
|