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