Skip to main content

blockworx/tools/
new_area.rs

1use crate::theme::Style;
2use blockworx_geom::{Pos2, Rect, WorldPx};
3use blockworx_paint::{Canvas, Cursor, Event, Interaction};
4
5use crate::{
6    grid::GRID_SIZE,
7    theme::Role,
8    tools::{
9        RenameTitle,
10        names::ToolName,
11        tool::{Action, ToolTrait},
12    },
13    widget::drawing::Drawing,
14};
15
16/// Drag-to-create a boundary [`Area`](blockworx_doc::block_model::Area):
17/// the same gesture as [`NewBlock`](crate::tools::NewBlock), previewing the
18/// outline as it is dragged and committing it on release if it is larger than
19/// one grid cell.
20pub enum NewArea {
21    Idle,
22    Dragging { start: Pos2 },
23}
24
25impl ToolTrait for NewArea {
26    fn name(&self) -> ToolName {
27        ToolName::NewArea
28    }
29    fn widget<C: Canvas>(
30        &mut self,
31        data: &mut Drawing,
32        interaction: &Interaction,
33        painter: &mut Style<'_, C>,
34    ) -> Option<Action> {
35        crate::widget::display::widget(data, interaction, painter);
36        painter.set_cursor(Cursor::Crosshair);
37        match self {
38            NewArea::Idle => {
39                if let Some(Event::DragStarted { pos }) = interaction.event {
40                    *self = NewArea::Dragging { start: pos };
41                }
42            }
43            NewArea::Dragging { start } => {
44                if let Some(Event::Dragging { pos, .. }) = interaction.event {
45                    let rect = Rect::from_two_pos(*start, pos);
46                    painter.rect(
47                        rect,
48                        WorldPx::ZERO,
49                        Role::Transparent,
50                        (1.0, Role::AreaStroke),
51                    );
52                } else if let Some(Event::DragStopped { pos }) = interaction.event {
53                    let start_pos = *start;
54                    let candidate = Rect::from_two_pos(start_pos, pos);
55                    *self = NewArea::Idle;
56                    if candidate.width() > GRID_SIZE && candidate.height() > GRID_SIZE {
57                        let shape = data.add_area(start_pos, pos);
58                        // Drop straight into editing the new area's text.
59                        return Some(RenameTitle::action(data, shape));
60                    }
61                }
62            }
63        }
64        None
65    }
66}