blockworx_tools/
new_block.rs1use crate::theme::Style;
2use blockworx_geom::{Pos2, Rect, WorldPx};
3use blockworx_paint::{Canvas, Cursor, Event, Interaction};
4
5use crate::{
6 RenameTitle,
7 grid::GRID_SIZE,
8 names::ToolName,
9 shape::ShapeId,
10 theme::Role,
11 tool::{ToolTrait, Transition},
12 widget::drawing::Drawing,
13};
14
15pub enum NewBlock {
18 Idle,
19 Dragging {
21 start: Pos2,
22 },
23 Placing {
26 start: Pos2,
27 corner: Pos2,
28 },
29}
30
31impl ToolTrait for NewBlock {
32 fn name(&self) -> ToolName {
33 ToolName::NewBlock
34 }
35 fn widget<C: Canvas>(
36 &mut self,
37 data: &mut Drawing,
38 interaction: &Interaction,
39 painter: &mut Style<'_, C>,
40 ) -> Option<Transition> {
41 crate::widget::display::widget(data, interaction, painter);
43 painter.set_cursor(Cursor::Crosshair);
44 if interaction.escape_pressed {
45 *self = NewBlock::Idle;
46 return None;
47 }
48 match self {
49 NewBlock::Idle => {
50 match interaction.event {
51 Some(Event::DragStarted { pos }) => *self = NewBlock::Dragging { start: pos },
52 Some(Event::Clicked { pos }) => {
53 *self = NewBlock::Placing {
54 start: pos,
55 corner: pos,
56 }
57 }
58 _ => {}
59 }
60 None
61 }
62 NewBlock::Dragging { start } => {
63 let start = *start;
64 match interaction.event {
65 Some(Event::Dragging { pos, .. }) => {
66 preview(painter, start, pos);
67 None
68 }
69 Some(Event::DragStopped { pos }) => {
71 *self = NewBlock::Idle;
72 create(data, start, pos)
73 }
74 _ => None,
75 }
76 }
77 NewBlock::Placing { start, corner } => {
78 let start = *start;
79 match interaction.event {
80 Some(Event::HoverAt(pos) | Event::Dragging { pos, .. }) => *corner = pos,
81 Some(Event::Clicked { pos } | Event::DragStopped { pos }) => {
85 if let Some(action) = create(data, start, pos) {
86 *self = NewBlock::Idle;
87 return Some(action);
88 }
89 *corner = pos;
90 }
91 _ => {}
92 }
93 let corner = *corner;
94 preview(painter, start, corner);
95 None
96 }
97 }
98 }
99}
100
101fn preview<C: Canvas>(painter: &mut Style<'_, C>, start: Pos2, corner: Pos2) {
102 painter.rect(
103 Rect::from_two_pos(start, corner),
104 WorldPx::ZERO,
105 Role::Transparent,
106 (1.0, Role::NewBlockPreviewStroke),
107 );
108}
109
110fn create(data: &mut Drawing, start: Pos2, corner: Pos2) -> Option<Transition> {
114 let candidate = Rect::from_two_pos(start, corner);
115 if candidate.width() <= GRID_SIZE || candidate.height() < 4.0 * GRID_SIZE {
116 return None;
117 }
118 let shape = ShapeId::Rect(data.add_rect_box(start, corner));
119 Some(RenameTitle::action(data, shape))
120}