blockworx_tools/
rename_route.rs1use blockworx_doc::id::{RouteId, RouteLabelId};
2use blockworx_geom::{Align2, Angle, Rect, vec2};
3
4use crate::theme::{Role, Style};
5use crate::{
6 grid::ROUTE_TEXT_SIZE,
7 names::ToolName,
8 tool::{ToolTrait, Transition},
9 widget::drawing::Drawing,
10};
11use blockworx_paint::{Canvas, EditId, EditText, Event, Interaction, Renderer, TextOutcome};
12pub enum RenameRoute {
13 Idle,
14 Renaming {
15 route_id: RouteId,
16 label_id: RouteLabelId,
17 text: String,
18 position: Rect,
19 },
20}
21
22impl RenameRoute {
23 pub fn action(
26 data: &Drawing,
27 route_id: RouteId,
28 label_id: RouteLabelId,
29 painter: &Style<'_, impl Renderer>,
30 ) -> Transition {
31 match RenameRoute::new_with_route_and_label(data, route_id, label_id, painter) {
32 Some(tool) => Transition::SwitchTool(tool.into()),
33 None => Transition::default(),
34 }
35 }
36
37 pub fn new_with_route_and_label(
38 data: &Drawing,
39 route_id: RouteId,
40 label_id: RouteLabelId,
41 painter: &Style<'_, impl Renderer>,
42 ) -> Option<Self> {
43 let wire = data.auto_route(route_id)?;
44 let geometry = data.route_geometry(route_id)?;
45 let name = wire.route.name.clone();
46 let dist = wire
47 .labels
48 .iter()
49 .find(|(id, _)| *id == label_id)
50 .map(|&(_, dist)| dist)?;
51 let label_pos = geometry.map_linear_distance_to_position(dist).location;
52 let editor_width =
53 (painter.text_size(&name, &painter.theme().route_font).x + 10.0).max(60.0);
54 let height = ROUTE_TEXT_SIZE * 1.5;
55 let anchor = crate::render::horizontal_label_anchor(label_pos);
59 let position =
60 Rect::from_center_size(anchor - vec2(0.0, height / 2.0), vec2(editor_width, height));
61 Some(RenameRoute::Renaming {
62 route_id,
63 label_id,
64 text: name,
65 position,
66 })
67 }
68}
69
70impl ToolTrait for RenameRoute {
71 fn name(&self) -> ToolName {
72 ToolName::RenameRoute
73 }
74
75 fn widget<C: Canvas>(
76 &mut self,
77 data: &mut Drawing,
78 interaction: &Interaction,
79 painter: &mut Style<'_, C>,
80 ) -> Option<Transition> {
81 match self {
82 RenameRoute::Idle => {
83 crate::widget::display::widget(data, interaction, painter);
84 if let Some(Event::DoubleClicked { pos }) = interaction.event
85 && let Some((route_id, label_id)) = data.route_label_at_pos(pos, painter)
86 && let Some(tool) =
87 RenameRoute::new_with_route_and_label(data, route_id, label_id, painter)
88 {
89 *self = tool;
90 }
91 None
92 }
93 RenameRoute::Renaming {
94 route_id,
95 label_id,
96 text,
97 position,
98 } => {
99 crate::widget::DrawingPasses::new(data)
100 .suppress_route_label(*route_id, *label_id)
101 .draw(painter);
102 let id = EditId::of("route_name_edit");
103 let Some(outcome) = &interaction.text else {
104 painter.set_edit_text(EditText {
105 align: Align2::CENTER_CENTER,
106 position: *position,
107 angle: Angle::ZERO,
108 text: text.clone(),
109 font: painter.theme().route_font.clone(),
110 id,
111 multiline: false,
112 char_limit: Some(crate::grid::MAX_LABEL_CHARS),
113 tab_cycle: false,
114 select_all_on_focus: true,
117 hint: Some(crate::render::ADD_ROUTE_LABEL_PLACEHOLDER),
118 colors: painter
119 .theme()
120 .editor_colors(Role::EditorText, Role::EditorFill),
121 wrap_width: None,
122 });
123 return None;
124 };
125 let name = match outcome {
128 TextOutcome::Committed(name) | TextOutcome::Tab(name) => name,
129 TextOutcome::Cancelled => &*text,
130 };
131 data.set_route_name(*route_id, *label_id, name);
132 Some(Transition::SwitchTool(
135 crate::EditRoute::Selected {
136 id: *route_id,
137 anchor: position.center(),
138 }
139 .into(),
140 ))
141 }
142 }
143 }
144}