blockworx/tools/
rename_title.rs1use std::{cell::RefCell, rc::Rc};
2
3use blockworx_geom::{Rect, vec2};
4
5use crate::theme::Style;
6use crate::{
7 grid::TITLE_TEXT_SIZE,
8 shape::ShapeId,
9 tools::{
10 block_edit::{self, EditTarget},
11 names::ToolName,
12 resize_block::ResizeBlock,
13 tool::{Action, ToolTrait},
14 },
15 widget::drawing::Drawing,
16};
17use blockworx_paint::{Canvas, EditId, EditText, Event, Interaction, Renderer};
18pub enum RenameTitle {
19 Idle,
20 Renaming {
21 shape: ShapeId,
22 label: Rc<RefCell<String>>,
23 },
24}
25
26impl RenameTitle {
27 pub fn new_with_shape(data: &Drawing, shape: ShapeId) -> Option<Self> {
31 let shape_ref = data.shape(shape)?;
32 let title = shape_ref.title()?;
33 Some(RenameTitle::Renaming {
34 shape,
35 label: Rc::new(RefCell::new(title.name.to_owned())),
36 })
37 }
38
39 pub fn action(data: &Drawing, shape: ShapeId) -> Action {
43 Action::SwitchTool(match RenameTitle::new_with_shape(data, shape) {
44 Some(tool) => tool.into(),
45 None => ResizeBlock::Selected { shape }.into(),
46 })
47 }
48}
49
50fn editor_position(
54 data: &Drawing,
55 shape: ShapeId,
56 painter: &Style<'_, impl Renderer>,
57) -> Option<Rect> {
58 let shape_ref = data.shape(shape)?;
59 let title = shape_ref.title()?;
60 let text_width = painter.text_size(title.name, &painter.theme().title_font).x;
61 let title_width = (text_width + 10.0).max(20.0);
62 let (title_pos, title_align) =
64 crate::render::clamped_block_title_position(shape_ref.gui_rect(), &title, text_width);
65 Some(title_align.anchor_size(
66 title_pos,
67 vec2(title_width.max(60.0), TITLE_TEXT_SIZE * 1.5),
68 ))
69}
70
71impl ToolTrait for RenameTitle {
72 fn name(&self) -> ToolName {
73 ToolName::RenameTitle
74 }
75
76 fn widget<C: Canvas>(
77 &mut self,
78 data: &mut Drawing,
79 interaction: &Interaction,
80 painter: &mut Style<'_, C>,
81 ) -> Option<Action> {
82 match self {
83 RenameTitle::Idle => {
84 crate::widget::display::widget(data, interaction, painter);
85 if let Some(Event::DoubleClicked { pos }) = interaction.event
86 && let Some(id) = data.title_at_pos(pos, painter)
87 && let Some(next) = RenameTitle::new_with_shape(data, id)
88 {
89 *self = next;
90 }
91 None
92 }
93 RenameTitle::Renaming { shape, label } => {
94 crate::widget::display::widget(data, interaction, painter);
95 let shape = *shape;
96 let label = label.clone();
97 let Some(position) = editor_position(data, shape, painter) else {
98 return Some(Action::default());
100 };
101 if interaction.escape_pressed {
102 return Some(Action::SwitchTool(ResizeBlock::Selected { shape }.into()));
104 }
105 if interaction.tab_pressed
106 && let ShapeId::Rect(block) = shape
107 {
108 commit_title(data, shape, &label.borrow().clone());
109 return block_edit::advance(data, block, EditTarget::Name);
110 }
111 if interaction.lost_focus || interaction.enter_pressed {
112 commit_title(data, shape, &label.borrow().clone());
113 return Some(Action::SwitchTool(ResizeBlock::Selected { shape }.into()));
116 }
117 painter.set_edit_text(EditText {
118 position,
119 buffer: label,
120 font: painter.theme().title_font.clone(),
121 id: EditId::of(("title_edit", shape)),
122 multiline: false,
123 char_limit: Some(crate::grid::MAX_LABEL_CHARS),
124 tab_cycle: true,
125 select_all_on_focus: false,
126 hint: Some(crate::render::ADD_TITLE_PLACEHOLDER),
127 colors: None,
128 wrap_width: None,
129 });
130 None
131 }
132 }
133 }
134}
135
136fn commit_title(data: &mut Drawing, shape: ShapeId, text: &str) {
138 data.set_title_text(shape, text);
139}