blockworx/tools/
edit_text_box.rs1use std::{cell::RefCell, rc::Rc};
2
3use blockworx_doc::id::TextId;
4use blockworx_geom::{Pos2, Rect, vec2};
5
6use crate::render::text_box::{box_width, max_box_height, text_inner_width};
7use crate::theme::Style;
8use crate::{
9 grid::{GRID_SIZE, px_point},
10 shape::ShapeId,
11 tools::{
12 names::ToolName,
13 tool::{Action, Deletable, ToolTrait},
14 },
15 widget::drawing::Drawing,
16};
17use blockworx_paint::{Canvas, EditId, EditText, Interaction};
18pub enum EditTextBox {
30 Editing {
31 id: TextId,
32 buffer: Rc<RefCell<String>>,
33 anchor: Pos2,
34 },
35}
36
37impl EditTextBox {
38 pub fn new_for(data: &Drawing, id: TextId) -> Option<Self> {
40 let tb = data.text_box(id)?;
41 Some(EditTextBox::Editing {
42 id,
43 buffer: Rc::new(RefCell::new(tb.text.clone())),
44 anchor: px_point(tb.pos),
45 })
46 }
47
48 pub fn action(data: &Drawing, id: TextId) -> Action {
50 match EditTextBox::new_for(data, id) {
51 Some(tool) => Action::SwitchTool(tool.into()),
52 None => Action::default(),
53 }
54 }
55}
56
57fn editor_rect<C: Canvas>(buffer: &RefCell<String>, anchor: Pos2, painter: &Style<'_, C>) -> Rect {
60 let font = painter.theme().title_font.clone();
61 let content_height = painter
62 .text_size_wrapped(buffer.borrow().as_str(), &font, text_inner_width())
63 .y;
64 let height = (content_height + 2.0 * GRID_SIZE)
65 .min(max_box_height())
66 .max(2.0 * GRID_SIZE);
67 Rect::from_min_size(anchor, vec2(box_width(), height))
68}
69
70impl ToolTrait for EditTextBox {
71 fn name(&self) -> ToolName {
72 ToolName::EditTextBox
73 }
74
75 fn widget<C: Canvas>(
76 &mut self,
77 data: &mut Drawing,
78 interaction: &Interaction,
79 painter: &mut Style<'_, C>,
80 ) -> Option<Action> {
81 let (id, buffer, anchor) = match self {
82 EditTextBox::Editing { id, buffer, anchor } => (*id, buffer.clone(), *anchor),
83 };
84 crate::widget::display::render_hidden(data, ShapeId::Text(id), painter);
87
88 if interaction.lost_focus || interaction.enter_pressed {
89 let text = buffer.borrow().clone();
90 if text.trim().is_empty() {
91 return Some(Action::Delete(Deletable::Shape(ShapeId::Text(id))));
93 }
94 data.set_text_box_content(id, &text);
95 return Some(Action::SwitchTool(
97 crate::tools::resize_block::ResizeBlock::Selected {
98 shape: ShapeId::Text(id),
99 }
100 .into(),
101 ));
102 }
103 painter.set_edit_text(EditText {
104 position: editor_rect(&buffer, anchor, painter),
105 buffer: buffer.clone(),
106 font: painter.theme().title_font.clone(),
107 id: EditId::of("text_box_edit"),
108 multiline: true,
109 char_limit: Some(crate::grid::MAX_TEXT_BOX_CHARS),
110 tab_cycle: false,
111 select_all_on_focus: false,
112 hint: Some(crate::render::ADD_TEXT_PLACEHOLDER),
113 colors: None,
114 wrap_width: Some(text_inner_width()),
117 });
118 None
119 }
120}