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