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}