blockworx_tools/
rename_title.rs1use blockworx_geom::{Align2, Angle, Rect, vec2};
2
3use crate::theme::{Role, Style};
4use crate::{
5 block_edit::{self, EditTarget},
6 grid::TITLE_TEXT_SIZE,
7 names::ToolName,
8 resize_block::ResizeBlock,
9 shape::ShapeId,
10 tool::{ToolTrait, Transition},
11 widget::drawing::Drawing,
12};
13use blockworx_paint::{Canvas, EditId, EditText, Event, Interaction, Renderer, TextOutcome};
14pub enum RenameTitle {
15 Idle,
16 Renaming { shape: ShapeId, label: String },
17}
18
19impl RenameTitle {
20 pub fn new_with_shape(data: &Drawing, shape: ShapeId) -> Option<Self> {
24 let shape_ref = data.shape(shape)?;
25 let title = shape_ref.title()?;
26 Some(RenameTitle::Renaming {
27 shape,
28 label: title.name.to_owned(),
29 })
30 }
31
32 pub fn action(data: &Drawing, shape: ShapeId) -> Transition {
35 Transition::SwitchTool(match RenameTitle::new_with_shape(data, shape) {
36 Some(tool) => tool.into(),
37 None => ResizeBlock::Selected { shape }.into(),
38 })
39 }
40}
41
42fn editor_position(
46 data: &Drawing,
47 shape: ShapeId,
48 painter: &Style<'_, impl Renderer>,
49) -> Option<(Rect, Align2)> {
50 let shape_ref = data.shape(shape)?;
51 let title = shape_ref.title()?;
52 let text_width = painter.text_size(title.name, &painter.theme().title_font).x;
53 let title_width = (text_width + 10.0).max(20.0);
54 let (title_pos, title_align) =
56 crate::render::clamped_block_title_position(shape_ref.gui_rect(), &title, text_width);
57 Some((
58 title_align.anchor_size(
59 title_pos,
60 vec2(title_width.max(60.0), TITLE_TEXT_SIZE * 1.5),
61 ),
62 title_align,
63 ))
64}
65
66impl ToolTrait for RenameTitle {
67 fn name(&self) -> ToolName {
68 ToolName::RenameTitle
69 }
70
71 fn widget<C: Canvas>(
72 &mut self,
73 data: &mut Drawing,
74 interaction: &Interaction,
75 painter: &mut Style<'_, C>,
76 ) -> Option<Transition> {
77 match self {
78 RenameTitle::Idle => {
79 crate::widget::display::widget(data, interaction, painter);
80 if let Some(Event::DoubleClicked { pos }) = interaction.event
81 && let Some(id) = data.title_at_pos(pos, painter)
82 && let Some(next) = RenameTitle::new_with_shape(data, id)
83 {
84 *self = next;
85 }
86 None
87 }
88 RenameTitle::Renaming { shape, label } => {
89 crate::widget::display::widget(data, interaction, painter);
90 let shape = *shape;
91 let id = EditId::of(("title_edit", shape));
92 let label = label.clone();
93 let Some((position, align)) = editor_position(data, shape, painter) else {
94 return Some(Transition::default());
96 };
97 match &interaction.text {
98 Some(TextOutcome::Cancelled) => {
99 return Some(Transition::SwitchTool(
101 ResizeBlock::Selected { shape }.into(),
102 ));
103 }
104 Some(TextOutcome::Tab(text)) if let ShapeId::Rect(block) = shape => {
105 commit_title(data, shape, text);
106 return block_edit::advance(data, block, EditTarget::Title(shape));
107 }
108 Some(TextOutcome::Committed(text)) => {
109 commit_title(data, shape, text);
110 return Some(Transition::SwitchTool(
113 ResizeBlock::Selected { shape }.into(),
114 ));
115 }
116 Some(TextOutcome::Tab(_)) | None => {}
118 }
119 painter.set_edit_text(EditText {
120 align,
121 position,
122 angle: Angle::ZERO,
123 text: label,
124 font: painter.theme().title_font.clone(),
125 id,
126 multiline: false,
127 char_limit: Some(crate::grid::MAX_LABEL_CHARS),
128 tab_cycle: true,
129 select_all_on_focus: false,
130 hint: Some(crate::render::ADD_TITLE_PLACEHOLDER),
131 colors: painter
132 .theme()
133 .editor_colors(Role::EditorText, Role::EditorFill),
134 wrap_width: None,
135 });
136 None
137 }
138 }
139 }
140}
141
142fn commit_title(data: &mut Drawing, shape: ShapeId, text: &str) {
144 data.set_title_text(shape, text);
145}