blockworx/tools/
add_port.rs1use crate::render::draw_port_outline;
2use crate::theme::{Role, Style};
3use blockworx_geom::{Pos2, Rect, pos2, vec2};
4use blockworx_paint::{Canvas, Cursor, Event, Interaction};
5
6use crate::{
7 grid::snap_to_grid,
8 shape::pin::PinSide,
9 tools::{
10 names::ToolName,
11 rename_pin::{Field, RenamePin},
12 tool::{Action, ToolTrait},
13 },
14 widget::drawing::Drawing,
15};
16
17pub enum AddPort {
27 Idle,
28 Dragging { start: Pos2 },
29}
30
31impl ToolTrait for AddPort {
32 fn name(&self) -> ToolName {
33 ToolName::AddPort
34 }
35
36 fn widget<C: Canvas>(
37 &mut self,
38 data: &mut Drawing,
39 interaction: &Interaction,
40 painter: &mut Style<'_, C>,
41 ) -> Option<Action> {
42 crate::widget::display::widget(data, interaction, painter);
43 painter.set_cursor(Cursor::Crosshair);
44
45 if data.current_locked() {
49 let placed = matches!(interaction.event, Some(Event::DragStopped { .. }));
50 return placed.then(Action::default);
51 }
52
53 match self {
54 AddPort::Idle => match interaction.event {
55 Some(Event::HoverAt(pos)) => preview_port(painter, port_box(pos, pos)),
56 Some(Event::DragStarted { pos }) => *self = AddPort::Dragging { start: pos },
57 _ => {}
58 },
59 AddPort::Dragging { start } => {
60 let start = *start;
61 if let Some(Event::Dragging { pos, .. }) = interaction.event {
62 preview_port(painter, port_box(start, pos));
63 } else if let Some(Event::DragStopped { pos }) = interaction.event {
64 *self = AddPort::Idle;
65 return Some(create_port(data, port_box(start, pos)));
66 }
67 }
68 }
69 None
70 }
71}
72
73fn port_box(a: Pos2, b: Pos2) -> Rect {
77 let min = pos2(a.x.min(b.x), a.y.min(b.y));
78 let default = crate::edit::create::stamped_port(min);
79 let width = round_to_grid_len((a.x - b.x).abs()).max(default.width());
80 Rect::from_min_size(snap_to_grid(min), vec2(width, default.height()))
81}
82
83fn round_to_grid_len(len: f32) -> f32 {
84 snap_to_grid(pos2(len, 0.0)).x
85}
86
87fn preview_port<C: Canvas>(painter: &mut Style<'_, C>, bbox: Rect) {
91 draw_port_outline(
92 bbox,
93 PinSide::East,
94 Role::Transparent,
95 (1.0, Role::NewPinPreviewStroke),
96 painter,
97 );
98}
99
100fn create_port(data: &mut Drawing, bbox: Rect) -> Action {
103 let anchor = data.add_port_auto_named(bbox);
104 RenamePin::action(data, anchor, Field::Name)
105}