Skip to main content

blockworx_tools/
move_label.rs

1use crate::render::{RouteRenderMode, render_route_label_at, route_text_color};
2use crate::theme::Style;
3use blockworx_doc::id::{RouteId, RouteLabelId};
4use blockworx_paint::{Canvas, Cursor, Event, Interaction};
5
6use crate::{
7    names::ToolName,
8    tool::{ToolTrait, Transition},
9    widget::{DrawingPasses, drawing::Drawing},
10};
11
12pub enum MoveLabel {
13    Idle,
14    Dragging {
15        route_id: RouteId,
16        label_id: RouteLabelId,
17        /// Where the drag has slid the label to, in arc length along the wire.
18        /// The document keeps the pre-drag distance until the release commits
19        /// this one.
20        preview: blockworx_doc::geometry::FracVal,
21    },
22}
23
24impl MoveLabel {
25    /// Begin dragging `label_id`, seeding the preview from its stored distance.
26    /// `None` if the label no longer resolves on `route_id`.
27    pub(crate) fn drag_from(
28        data: &Drawing,
29        route_id: RouteId,
30        label_id: RouteLabelId,
31    ) -> Option<Self> {
32        let preview = data.auto_route(route_id).and_then(|wire| {
33            wire.labels
34                .iter()
35                .find(|(id, _)| *id == label_id)
36                .map(|&(_, dist)| dist)
37        })?;
38        Some(MoveLabel::Dragging {
39            route_id,
40            label_id,
41            preview,
42        })
43    }
44}
45
46impl ToolTrait for MoveLabel {
47    fn name(&self) -> ToolName {
48        ToolName::MoveLabel
49    }
50
51    fn widget<C: Canvas>(
52        &mut self,
53        data: &mut Drawing,
54        interaction: &Interaction,
55        painter: &mut Style<'_, C>,
56    ) -> Option<Transition> {
57        match self {
58            MoveLabel::Idle => {
59                crate::widget::display::widget(data, interaction, painter);
60                if let Some(Event::HoverAt(pos)) = interaction.event {
61                    if data.route_label_at_pos(pos, painter).is_some() {
62                        painter.set_cursor(Cursor::Grab);
63                    }
64                } else if let Some(Event::DragStarted { pos }) = interaction.event
65                    && let Some((route_id, label_id)) = data.route_label_at_pos(pos, painter)
66                    && let Some(dragging) = MoveLabel::drag_from(data, route_id, label_id)
67                {
68                    *self = dragging;
69                }
70            }
71            MoveLabel::Dragging {
72                route_id,
73                label_id,
74                preview,
75            } => {
76                render_dragging(data, painter, *route_id, *label_id, *preview);
77                painter.set_cursor(Cursor::Grabbing);
78                if let Some(Event::DragStopped { .. }) = interaction.event {
79                    data.place_route_label(*label_id, *preview);
80                    return Some(Transition::default());
81                }
82                if let Some(Event::Dragging { delta, .. }) = interaction.event
83                    && let Some(geometry) = data.route_geometry(*route_id)
84                {
85                    *preview = geometry.slide_along(*preview, delta);
86                }
87            }
88        }
89        None
90    }
91}
92
93/// Draw the drag frame: the stored label distance is stale until the release
94/// commits, so suppress it and draw the previewed one in its place.
95fn render_dragging<C: Canvas>(
96    data: &Drawing,
97    painter: &mut Style<'_, C>,
98    route_id: RouteId,
99    label_id: RouteLabelId,
100    preview: blockworx_doc::geometry::FracVal,
101) {
102    DrawingPasses::new(data)
103        .route_mode(move |id| {
104            if id == route_id {
105                RouteRenderMode::Highlighted
106            } else {
107                RouteRenderMode::Normal
108            }
109        })
110        .suppress_route_label(route_id, label_id)
111        .overlay(move |painter| {
112            if let (Some(wire), Some(geometry)) =
113                (data.auto_route(route_id), data.route_geometry(route_id))
114            {
115                let at = geometry.map_linear_distance_to_position(preview);
116                render_route_label_at(
117                    painter,
118                    wire.route,
119                    at,
120                    route_text_color(wire.route, RouteRenderMode::Highlighted),
121                );
122            }
123        })
124        .draw(painter);
125}