Skip to main content

blockworx_tools/
new_image.rs

1use blockworx_geom::{Pos2, Rect, WorldPx};
2
3use crate::theme::Style;
4use crate::{
5    grid::GRID_SIZE,
6    names::ToolName,
7    theme::Role,
8    tool::{Action, ImageTarget, ToolTrait},
9    widget::drawing::Drawing,
10};
11use blockworx_paint::{Canvas, Cursor, Event, Interaction};
12
13/// Drag-to-create a free-floating [`Image`](blockworx_doc::block_model::Image):
14/// the same gesture as [`NewArea`](crate::NewArea), previewing the fit box as
15/// it is dragged. On release (if larger than one grid cell) the tool asks for
16/// an image with [`Action::ImageWanted`]; the shell runs the picker and sends
17/// [`Action::PlaceImage`], which is what places the image, fitted to the
18/// gesture's box.
19///
20/// A *drop* off the tool cluster places the image at its own size around the
21/// drop point instead, so [`Placement::Centered`] reaches this tool from the
22/// drag-out path rather than from a click.
23pub enum NewImage {
24    Idle,
25    Dragging { start: Pos2 },
26}
27
28/// How to size an image once it is picked: a drag fixes the box outright,
29/// while a drop defers to the image's intrinsic size around the drop point.
30pub enum Placement {
31    Box(Rect),
32    Centered(Pos2),
33}
34
35impl ToolTrait for NewImage {
36    fn name(&self) -> ToolName {
37        ToolName::NewImage
38    }
39    fn widget<C: Canvas>(
40        &mut self,
41        data: &mut Drawing,
42        interaction: &Interaction,
43        painter: &mut Style<'_, C>,
44    ) -> Option<Action> {
45        crate::widget::display::widget(data, interaction, painter);
46        painter.set_cursor(Cursor::Crosshair);
47        match self {
48            NewImage::Idle => {
49                if let Some(Event::DragStarted { pos }) = interaction.event {
50                    *self = NewImage::Dragging { start: pos };
51                }
52            }
53            NewImage::Dragging { start } => {
54                if let Some(Event::Dragging { pos, .. }) = interaction.event {
55                    let rect = Rect::from_two_pos(*start, pos);
56                    painter.rect(
57                        rect,
58                        WorldPx::ZERO,
59                        Role::Transparent,
60                        (1.0, Role::ImageDragBox),
61                    );
62                } else if let Some(Event::DragStopped { pos }) = interaction.event {
63                    let candidate = Rect::from_two_pos(*start, pos);
64                    *self = NewImage::Idle;
65                    if candidate.width() > GRID_SIZE && candidate.height() > GRID_SIZE {
66                        return Some(Action::ImageWanted(ImageTarget::Place(Placement::Box(
67                            candidate,
68                        ))));
69                    }
70                }
71            }
72        }
73        None
74    }
75}