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