blockworx/tools/
move_block.rs1use crate::theme::Style;
2use blockworx_geom::Vec2;
3use blockworx_paint::{Canvas, Cursor, Event, Interaction};
4
5use crate::{
6 grid::snap_offset,
7 shape::ShapeId,
8 state::RenderMode,
9 tools::{
10 names::ToolName,
11 resize_block::ResizeBlock,
12 tool::{Action, Supposing, ToolTrait},
13 },
14 widget::{DrawingPasses, drawing::Drawing},
15};
16
17pub enum MoveBlock {
18 Idle,
19 Dragging { shape: ShapeId, delta_pos: Vec2 },
20}
21
22impl ToolTrait for MoveBlock {
23 fn name(&self) -> ToolName {
24 ToolName::MoveBlock
25 }
26
27 fn suppose<C: Canvas>(
28 &mut self,
29 data: &mut Drawing,
30 interaction: &Interaction,
31 _painter: &mut Style<'_, C>,
32 phase: &Supposing,
33 ) {
34 let MoveBlock::Dragging { shape, delta_pos } = self else {
35 return;
36 };
37 if let Some(Event::Dragging { delta, .. }) = interaction.event {
38 *delta_pos += delta;
39 }
40 data.suppose_drag(phase, &[(*shape, snap_offset(*delta_pos))]);
44 }
45
46 fn widget<C: Canvas>(
47 &mut self,
48 data: &mut Drawing,
49 interaction: &Interaction,
50 painter: &mut Style<'_, C>,
51 ) -> Option<Action> {
52 self.render(data, interaction, painter);
53 match self {
54 MoveBlock::Idle => {
55 if let Some(Event::HoverAt(hover_pos)) = interaction.event
56 && data.shape_at_pos(hover_pos).is_some()
57 {
58 painter.set_cursor(Cursor::Move);
59 }
60 if let Some(Event::DragStarted { pos }) = interaction.event
61 && let Some(shape) = data.shape_at_pos(pos)
62 {
63 *self = MoveBlock::Dragging {
64 shape,
65 delta_pos: Vec2::ZERO,
66 };
67 }
68 }
69 MoveBlock::Dragging { .. } => {
70 if let Some(Event::DragStopped { .. }) = interaction.event {
71 let MoveBlock::Dragging { shape, delta_pos } = self else {
72 unreachable!()
73 };
74 let magnetic = if shape.is_artwork() {
78 crate::widget::alignment::snap_drag_offset(data, &[*shape], *delta_pos)
79 } else {
80 *delta_pos
81 };
82 let commit = data.constrain_move(*shape, magnetic);
83 data.move_shape(*shape, commit);
84 if shape.affects_routing() {
85 data.trim_partial_route_approaches(&[*shape]);
89 }
90 return Some(Action::SwitchTool(
92 ResizeBlock::Selected { shape: *shape }.into(),
93 ));
94 }
95 }
96 }
97 None
98 }
99}
100
101impl MoveBlock {
102 fn render<C: Canvas>(
103 &self,
104 data: &Drawing,
105 interaction: &Interaction,
106 painter: &mut Style<'_, C>,
107 ) {
108 match self {
109 MoveBlock::Idle => {
110 crate::widget::display::widget(data, interaction, painter);
111 }
112 MoveBlock::Dragging {
113 shape: dragging_id,
114 delta_pos,
115 } => {
116 let icon_id = dragging_id.block().map(ShapeId::Icon);
119 let raw = *delta_pos;
120 let dragging_id = *dragging_id;
121 let mut dragged_ids = vec![dragging_id];
122 dragged_ids.extend(icon_id);
123 let is_free = dragging_id.is_artwork();
127 let magnetic = if is_free {
128 crate::widget::alignment::snap_drag_offset(data, &dragged_ids, raw)
129 } else {
130 raw
131 };
132 let delta = data.constrain_move(dragging_id, magnetic);
135 let guide_offset = if is_free { delta } else { snap_offset(raw) };
136 let guides =
137 crate::widget::alignment::shape_drag_guides(data, &dragged_ids, guide_offset);
138 let icon_delta = snap_offset(raw);
142 DrawingPasses::new(data)
143 .shape_mode(move |id| {
144 if id == dragging_id {
145 RenderMode::Moving { delta }
146 } else if Some(id) == icon_id {
147 RenderMode::Moving { delta: icon_delta }
148 } else {
149 RenderMode::Normal
150 }
151 })
152 .draw(painter);
153 crate::widget::alignment::draw_guides(&guides, painter);
156 }
157 }
158 }
159}