rg3d_ui/
text.rs

1use crate::{
2    brush::Brush,
3    core::{algebra::Vector2, color::Color, pool::Handle},
4    define_constructor,
5    draw::DrawingContext,
6    formatted_text::{FormattedText, FormattedTextBuilder, WrapMode},
7    message::{MessageDirection, UiMessage},
8    ttf::SharedFont,
9    widget::{Widget, WidgetBuilder},
10    BuildContext, Control, HorizontalAlignment, UiNode, UserInterface, VerticalAlignment,
11};
12use std::{
13    any::{Any, TypeId},
14    cell::RefCell,
15    ops::{Deref, DerefMut},
16};
17
18#[derive(Debug, Clone, PartialEq)]
19pub enum TextMessage {
20    Text(String),
21    Wrap(WrapMode),
22    Font(SharedFont),
23    VerticalAlignment(VerticalAlignment),
24    HorizontalAlignment(HorizontalAlignment),
25}
26
27impl TextMessage {
28    define_constructor!(TextMessage:Text => fn text(String), layout: false);
29    define_constructor!(TextMessage:Wrap=> fn wrap(WrapMode), layout: false);
30    define_constructor!(TextMessage:Font => fn font(SharedFont), layout: false);
31    define_constructor!(TextMessage:VerticalAlignment => fn vertical_alignment(VerticalAlignment), layout: false);
32    define_constructor!(TextMessage:HorizontalAlignment => fn horizontal_alignment(HorizontalAlignment), layout: false);
33}
34
35#[derive(Clone)]
36pub struct Text {
37    widget: Widget,
38    formatted_text: RefCell<FormattedText>,
39}
40
41crate::define_widget_deref!(Text);
42
43impl Control for Text {
44    fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
45        if type_id == TypeId::of::<Self>() {
46            Some(self)
47        } else {
48            None
49        }
50    }
51
52    fn measure_override(&self, _: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
53        self.formatted_text
54            .borrow_mut()
55            .set_constraint(available_size)
56            .set_brush(self.widget.foreground())
57            .build()
58    }
59
60    fn draw(&self, drawing_context: &mut DrawingContext) {
61        let bounds = self.widget.screen_bounds();
62        drawing_context.draw_text(
63            self.clip_bounds(),
64            bounds.position,
65            &self.formatted_text.borrow(),
66        );
67    }
68
69    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
70        self.widget.handle_routed_message(ui, message);
71
72        if message.destination() == self.handle() {
73            if let Some(msg) = message.data::<TextMessage>() {
74                match msg {
75                    TextMessage::Text(text) => {
76                        self.formatted_text.borrow_mut().set_text(text);
77                        self.invalidate_layout();
78                    }
79                    &TextMessage::Wrap(wrap) => {
80                        if self.formatted_text.borrow().wrap_mode() != wrap {
81                            self.formatted_text.borrow_mut().set_wrap(wrap);
82                            self.invalidate_layout();
83                        }
84                    }
85                    TextMessage::Font(font) => {
86                        self.formatted_text.borrow_mut().set_font(font.clone());
87                        self.invalidate_layout();
88                    }
89                    &TextMessage::HorizontalAlignment(horizontal_alignment) => {
90                        self.formatted_text
91                            .borrow_mut()
92                            .set_horizontal_alignment(horizontal_alignment);
93                        self.invalidate_layout();
94                    }
95                    &TextMessage::VerticalAlignment(vertical_alignment) => {
96                        self.formatted_text
97                            .borrow_mut()
98                            .set_vertical_alignment(vertical_alignment);
99                        self.invalidate_layout();
100                    }
101                }
102            }
103        }
104    }
105}
106
107impl Text {
108    pub fn new(widget: Widget) -> Self {
109        Self {
110            widget,
111            formatted_text: RefCell::new(
112                FormattedTextBuilder::new()
113                    .with_font(crate::DEFAULT_FONT.clone())
114                    .build(),
115            ),
116        }
117    }
118
119    pub fn wrap_mode(&self) -> WrapMode {
120        self.formatted_text.borrow().wrap_mode()
121    }
122
123    pub fn text(&self) -> String {
124        self.formatted_text.borrow().text()
125    }
126
127    pub fn font(&self) -> SharedFont {
128        self.formatted_text.borrow().get_font()
129    }
130
131    pub fn vertical_alignment(&self) -> VerticalAlignment {
132        self.formatted_text.borrow().vertical_alignment()
133    }
134
135    pub fn horizontal_alignment(&self) -> HorizontalAlignment {
136        self.formatted_text.borrow().horizontal_alignment()
137    }
138}
139
140pub struct TextBuilder {
141    widget_builder: WidgetBuilder,
142    text: Option<String>,
143    font: Option<SharedFont>,
144    vertical_text_alignment: VerticalAlignment,
145    horizontal_text_alignment: HorizontalAlignment,
146    wrap: WrapMode,
147}
148
149impl TextBuilder {
150    pub fn new(widget_builder: WidgetBuilder) -> Self {
151        Self {
152            widget_builder,
153            text: None,
154            font: None,
155            vertical_text_alignment: VerticalAlignment::Top,
156            horizontal_text_alignment: HorizontalAlignment::Left,
157            wrap: WrapMode::NoWrap,
158        }
159    }
160
161    pub fn with_text<P: AsRef<str>>(mut self, text: P) -> Self {
162        self.text = Some(text.as_ref().to_owned());
163        self
164    }
165
166    pub fn with_font(mut self, font: SharedFont) -> Self {
167        self.font = Some(font);
168        self
169    }
170
171    pub fn with_opt_font(mut self, font: Option<SharedFont>) -> Self {
172        self.font = font;
173        self
174    }
175
176    pub fn with_vertical_text_alignment(mut self, valign: VerticalAlignment) -> Self {
177        self.vertical_text_alignment = valign;
178        self
179    }
180
181    pub fn with_horizontal_text_alignment(mut self, halign: HorizontalAlignment) -> Self {
182        self.horizontal_text_alignment = halign;
183        self
184    }
185
186    pub fn with_wrap(mut self, wrap: WrapMode) -> Self {
187        self.wrap = wrap;
188        self
189    }
190
191    pub fn build(mut self, ui: &mut BuildContext) -> Handle<UiNode> {
192        let font = if let Some(font) = self.font {
193            font
194        } else {
195            crate::DEFAULT_FONT.clone()
196        };
197
198        if self.widget_builder.foreground.is_none() {
199            self.widget_builder.foreground = Some(Brush::Solid(Color::opaque(220, 220, 220)));
200        }
201
202        let text = Text {
203            widget: self.widget_builder.build(),
204            formatted_text: RefCell::new(
205                FormattedTextBuilder::new()
206                    .with_text(self.text.unwrap_or_default())
207                    .with_vertical_alignment(self.vertical_text_alignment)
208                    .with_horizontal_alignment(self.horizontal_text_alignment)
209                    .with_font(font)
210                    .with_wrap(self.wrap)
211                    .build(),
212            ),
213        };
214        ui.add_node(UiNode::new(text))
215    }
216}