fyrox_ui/text.rs
1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Text is a simple widget that allows you to print text on screen. See [`Text`] docs for more info and
22//! examples.
23
24#![warn(missing_docs)]
25
26use crate::style::StyledProperty;
27use crate::{
28 brush::Brush,
29 core::{
30 algebra::Vector2, color::Color, pool::Handle, reflect::prelude::*, type_traits::prelude::*,
31 uuid_provider, visitor::prelude::*,
32 },
33 define_constructor,
34 draw::DrawingContext,
35 font::FontResource,
36 formatted_text::{FormattedText, FormattedTextBuilder, WrapMode},
37 message::{MessageDirection, UiMessage},
38 style::{resource::StyleResourceExt, Style},
39 widget::{Widget, WidgetBuilder},
40 BuildContext, Control, HorizontalAlignment, UiNode, UserInterface, VerticalAlignment,
41};
42use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
43use std::{
44 cell::RefCell,
45 ops::{Deref, DerefMut},
46};
47
48/// Possible messages that can be used to alternate [`Text`] widget state at runtime.
49#[derive(Debug, Clone, PartialEq)]
50pub enum TextMessage {
51 /// Used to set new text of the widget.
52 Text(String),
53 /// Used to set new text wrapping mode of the widget. See [Text](Text#text-alignment-and-word-wrapping) for usage
54 /// examples.
55 Wrap(WrapMode),
56 /// Used to set new font of the widget. See [Text](Text#fonts-and_colors) for usage examples.
57 Font(FontResource),
58 /// Used to set new vertical alignment of the widget. See [Text](Text#text-alignment-and-word-wrapping) for usage
59 /// examples.
60 VerticalAlignment(VerticalAlignment),
61 /// Used to set new horizontal alignment of the widget. See [Text](Text#text-alignment-and-word-wrapping) for usage
62 /// examples.
63 HorizontalAlignment(HorizontalAlignment),
64 /// Used to enable/disable shadow casting of the widget. See [Text](Text#shadows) for usage examples.
65 Shadow(bool),
66 /// Used to set new dilation factor of the shadows. See [Text](Text#shadows) for usage examples.
67 ShadowDilation(f32),
68 /// Used to set new brush that will be used to draw the shadows. See [Text](Text#shadows) for usage examples.
69 ShadowBrush(Brush),
70 /// Used to set how much the shadows will be offset from the widget. See [Text](Text#shadows) for usage examples.
71 ShadowOffset(Vector2<f32>),
72 /// Used to set font height of the widget.
73 FontSize(StyledProperty<f32>),
74}
75
76impl TextMessage {
77 define_constructor!(
78 /// Creates new [`TextMessage::Text`] message.
79 TextMessage:Text => fn text(String), layout: false
80 );
81
82 define_constructor!(
83 /// Creates new [`TextMessage::Wrap`] message.
84 TextMessage:Wrap => fn wrap(WrapMode), layout: false
85 );
86
87 define_constructor!(
88 /// Creates new [`TextMessage::Font`] message.
89 TextMessage:Font => fn font(FontResource), layout: false
90 );
91
92 define_constructor!(
93 /// Creates new [`TextMessage::VerticalAlignment`] message.
94 TextMessage:VerticalAlignment => fn vertical_alignment(VerticalAlignment), layout: false
95 );
96
97 define_constructor!(
98 /// Creates new [`TextMessage::HorizontalAlignment`] message.
99 TextMessage:HorizontalAlignment => fn horizontal_alignment(HorizontalAlignment), layout: false
100 );
101
102 define_constructor!(
103 /// Creates new [`TextMessage::Shadow`] message.
104 TextMessage:Shadow => fn shadow(bool), layout: false
105 );
106
107 define_constructor!(
108 /// Creates new [`TextMessage::ShadowDilation`] message.
109 TextMessage:ShadowDilation => fn shadow_dilation(f32), layout: false
110 );
111
112 define_constructor!(
113 /// Creates new [`TextMessage::ShadowBrush`] message.
114 TextMessage:ShadowBrush => fn shadow_brush(Brush), layout: false
115 );
116
117 define_constructor!(
118 /// Creates new [`TextMessage::ShadowOffset`] message.
119 TextMessage:ShadowOffset => fn shadow_offset(Vector2<f32>), layout: false
120 );
121
122 define_constructor!(
123 /// Creates new [`TextMessage::FontSize`] message.
124 TextMessage:FontSize => fn font_size(StyledProperty<f32>), layout: false
125 );
126}
127
128/// Text is a simple widget that allows you to print text on screen. It has various options like word wrapping, text
129/// alignment, and so on.
130///
131/// ## How to create
132///
133/// An instance of the [`Text`] widget could be created like so:
134///
135/// ```rust
136/// # use fyrox_ui::{
137/// # core::pool::Handle,
138/// # text::TextBuilder, widget::WidgetBuilder, UiNode, UserInterface
139/// # };
140/// fn create_text(ui: &mut UserInterface, text: &str) -> Handle<UiNode> {
141/// TextBuilder::new(WidgetBuilder::new())
142/// .with_text(text)
143/// .build(&mut ui.build_ctx())
144/// }
145/// ```
146///
147/// ## Text alignment and word wrapping
148///
149/// There are various text alignment options for both vertical and horizontal axes. Typical alignment values are:
150/// [`HorizontalAlignment::Left`], [`HorizontalAlignment::Center`], [`HorizontalAlignment::Right`] for horizontal axis,
151/// and [`VerticalAlignment::Top`], [`VerticalAlignment::Center`], [`VerticalAlignment::Bottom`] for vertical axis. An
152/// instance of centered text could be created like so:
153///
154/// ```rust,no_run
155/// # use fyrox_ui::{
156/// # core::pool::Handle,
157/// # text::TextBuilder, widget::WidgetBuilder, HorizontalAlignment, UiNode, UserInterface,
158/// # VerticalAlignment,
159/// # };
160/// fn create_centered_text(ui: &mut UserInterface, text: &str) -> Handle<UiNode> {
161/// TextBuilder::new(WidgetBuilder::new())
162/// .with_horizontal_text_alignment(HorizontalAlignment::Center)
163/// .with_vertical_text_alignment(VerticalAlignment::Center)
164/// .with_text(text)
165/// .build(&mut ui.build_ctx())
166/// }
167/// ```
168///
169/// What's the difference between widget's alignment and text-specific? Widget's alignment operates on a bounding rectangle
170/// of the text and text-specific alignment operates on line-basis. This means that if you set [`HorizontalAlignment::Center`]
171/// as widget's alignment, your text lines won't be centered, instead they'll be aligned at the left and the entire text block
172/// will be aligned at center.
173///
174/// Long text is usually needs to wrap on available bounds, there are three possible options for word wrapping:
175/// [`WrapMode::NoWrap`], [`WrapMode::Letter`], [`WrapMode::Word`]. An instance of text with word-based wrapping could
176/// be created like so:
177///
178/// ```rust,no_run
179/// # use fyrox_ui::{
180/// # core::pool::Handle,
181/// # formatted_text::WrapMode, text::TextBuilder, widget::WidgetBuilder, UiNode,
182/// # UserInterface,
183/// # };
184/// fn create_text_with_word_wrap(ui: &mut UserInterface, text: &str) -> Handle<UiNode> {
185/// TextBuilder::new(WidgetBuilder::new())
186/// .with_wrap(WrapMode::Word)
187/// .with_text(text)
188/// .build(&mut ui.build_ctx())
189/// }
190/// ```
191///
192/// ## Background
193///
194/// If you need to have a text with some background, you should use [`crate::border::Border`] widget as a parent widget of your
195/// text. **Caveat:** [`WidgetBuilder::with_background`] is ignored for [`Text`] widget!
196///
197/// ```rust,no_run
198/// # use fyrox_ui::{
199/// # core::{color::Color, pool::Handle},
200/// # border::BorderBuilder, brush::Brush, text::TextBuilder, widget::WidgetBuilder, UiNode,
201/// # UserInterface,
202/// # };
203/// #
204/// fn create_text_with_background(ui: &mut UserInterface, text: &str) -> Handle<UiNode> {
205/// let text_widget =
206/// TextBuilder::new(WidgetBuilder::new().with_foreground(Brush::Solid(Color::RED).into()))
207/// .with_text(text)
208/// .build(&mut ui.build_ctx());
209/// BorderBuilder::new(
210/// WidgetBuilder::new()
211/// .with_child(text_widget) // <-- Text is now a child of the border
212/// .with_background(Brush::Solid(Color::opaque(50, 50, 50)).into()),
213/// )
214/// .build(&mut ui.build_ctx())
215/// }
216/// ```
217///
218/// Keep in mind that now the text widget is a child widget of the border, so if you need to position the text, you should
219/// position the border, not the text.
220///
221/// ## Fonts and colors
222///
223/// To set a color of the text just use [`WidgetBuilder::with_foreground`] while building the text instance:
224///
225/// ```rust,no_run
226/// # use fyrox_ui::{
227/// # core::{color::Color, pool::Handle},
228/// # brush::Brush, text::TextBuilder, widget::WidgetBuilder, UiNode, UserInterface
229/// # };
230/// fn create_text(ui: &mut UserInterface, text: &str) -> Handle<UiNode> {
231/// // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
232/// TextBuilder::new(WidgetBuilder::new().with_foreground(Brush::Solid(Color::RED).into()))
233/// .with_text(text)
234/// .build(&mut ui.build_ctx())
235/// }
236/// ```
237///
238/// By default, text is created with default font, however it is possible to set any custom font:
239///
240/// ```rust
241/// # use fyrox_resource::manager::ResourceManager;
242/// # use fyrox_ui::{
243/// # core::{futures::executor::block_on, pool::Handle},
244/// # text::TextBuilder,
245/// # font::{Font, FontResource},
246/// # widget::WidgetBuilder,
247/// # UiNode, UserInterface,
248/// # };
249///
250/// fn create_text(ui: &mut UserInterface, resource_manager: &ResourceManager, text: &str) -> Handle<UiNode> {
251/// TextBuilder::new(WidgetBuilder::new())
252/// .with_font(resource_manager.request::<Font>("path/to/your/font.ttf"))
253/// .with_text(text)
254/// .with_font_size(20.0f32.into())
255/// .build(&mut ui.build_ctx())
256/// }
257/// ```
258///
259/// Please refer to [`crate::font::Font`] chapter to learn more about fonts.
260///
261/// ### Font size
262///
263/// Use [`TextBuilder::with_font_size`] or send [`TextMessage::font_size`] to your Text widget instance
264/// to set the font size of it.
265///
266/// ## Shadows
267///
268/// Text widget supports shadows effect to add contrast to your text, which could be useful to make text readable independent
269/// on the background colors. This effect could be used for subtitles. Shadows are pretty easy to add, all you need to do
270/// is to enable them, setup desired thickness, offset and brush (solid color or gradient).
271///
272/// ```rust,no_run
273/// # use fyrox_ui::{
274/// # core::{algebra::Vector2, color::Color, pool::Handle},
275/// # brush::Brush, text::TextBuilder, widget::WidgetBuilder, UiNode, UserInterface
276/// # };
277/// #
278/// fn create_red_text_with_black_shadows(ui: &mut UserInterface, text: &str) -> Handle<UiNode> {
279/// TextBuilder::new(WidgetBuilder::new().with_foreground(Brush::Solid(Color::RED).into()))
280/// .with_text(text)
281/// // Enable shadows.
282/// .with_shadow(true)
283/// // Black shadows.
284/// .with_shadow_brush(Brush::Solid(Color::BLACK))
285/// // 1px thick.
286/// .with_shadow_dilation(1.0)
287/// // Offset the shadow slightly to the right-bottom.
288/// .with_shadow_offset(Vector2::new(1.0, 1.0))
289/// .build(&mut ui.build_ctx())
290/// }
291/// ```
292///
293/// ## Messages
294///
295/// Text widget can accept the following list of messages at runtime (respective constructors are name with small letter -
296/// `TextMessage::Text -> TextMessage::text(widget_handle, direction, text)`):
297///
298/// - [`TextMessage::Text`] - sets new text for a `Text` widget.
299/// - [`TextMessage::Wrap`] - sets new [wrapping mode](Text#text-alignment-and-word-wrapping).
300/// - [`TextMessage::Font`] - sets new [font](Text#fonts-and-colors)
301/// - [`TextMessage::VerticalAlignment`] and `TextMessage::HorizontalAlignment` sets
302/// [vertical and horizontal](Text#text-alignment-and-word-wrapping) text alignment respectively.
303/// - [`TextMessage::Shadow`] - enables or disables [shadow casting](Text#shadows)
304/// - [`TextMessage::ShadowDilation`] - sets "thickness" of the shadows under the tex.
305/// - [`TextMessage::ShadowBrush`] - sets shadow brush (allows you to change color and even make shadow with color gradients).
306/// - [`TextMessage::ShadowOffset`] - sets offset of the shadows.
307///
308/// An example of changing text at runtime could be something like this:
309///
310/// ```rust
311/// # use fyrox_ui::{
312/// # core::pool::Handle,
313/// # message::{MessageDirection},
314/// # UiNode, UserInterface,
315/// # text::TextMessage
316/// # };
317/// fn request_change_text(ui: &UserInterface, text_widget_handle: Handle<UiNode>, text: &str) {
318/// ui.send_message(TextMessage::text(
319/// text_widget_handle,
320/// MessageDirection::ToWidget,
321/// text.to_owned(),
322/// ))
323/// }
324/// ```
325///
326/// Please keep in mind, that like any other situation when you "changing" something via messages, you should remember
327/// that the change is **not** immediate.
328#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider)]
329pub struct Text {
330 /// Base widget of the Text widget.
331 pub widget: Widget,
332 /// [`FormattedText`] instance that is used to layout text and generate drawing commands.
333 pub formatted_text: RefCell<FormattedText>,
334}
335
336impl ConstructorProvider<UiNode, UserInterface> for Text {
337 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
338 GraphNodeConstructor::new::<Self>()
339 .with_variant("Text", |ui| {
340 TextBuilder::new(WidgetBuilder::new().with_name("Text"))
341 .with_text("Text")
342 .build(&mut ui.build_ctx())
343 .into()
344 })
345 .with_group("Visual")
346 }
347}
348
349crate::define_widget_deref!(Text);
350
351uuid_provider!(Text = "22f7f502-7622-4ecb-8c5f-ba436e7ee823");
352
353impl Control for Text {
354 fn measure_override(&self, _: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
355 self.formatted_text
356 .borrow_mut()
357 .set_super_sampling_scale(self.visual_max_scaling())
358 .set_constraint(available_size)
359 .build()
360 }
361
362 fn draw(&self, drawing_context: &mut DrawingContext) {
363 self.formatted_text
364 .borrow_mut()
365 .set_brush(self.widget.foreground());
366 let bounds = self.widget.bounding_rect();
367 drawing_context.draw_text(
368 self.clip_bounds(),
369 bounds.position,
370 &self.formatted_text.borrow(),
371 );
372 }
373
374 fn on_visual_transform_changed(&self) {
375 self.formatted_text
376 .borrow_mut()
377 .set_super_sampling_scale(self.visual_max_scaling())
378 .build();
379 }
380
381 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
382 self.widget.handle_routed_message(ui, message);
383
384 if message.destination() == self.handle() {
385 if let Some(msg) = message.data::<TextMessage>() {
386 let mut text_ref = self.formatted_text.borrow_mut();
387 match msg {
388 TextMessage::Text(text) => {
389 text_ref.set_text(text);
390 drop(text_ref);
391 self.invalidate_layout();
392 }
393 &TextMessage::Wrap(wrap) => {
394 if text_ref.wrap_mode() != wrap {
395 text_ref.set_wrap(wrap);
396 drop(text_ref);
397 self.invalidate_layout();
398 }
399 }
400 TextMessage::Font(font) => {
401 if &text_ref.get_font() != font {
402 text_ref.set_font(font.clone());
403 drop(text_ref);
404 self.invalidate_layout();
405 }
406 }
407 &TextMessage::HorizontalAlignment(horizontal_alignment) => {
408 if text_ref.horizontal_alignment() != horizontal_alignment {
409 text_ref.set_horizontal_alignment(horizontal_alignment);
410 drop(text_ref);
411 self.invalidate_layout();
412 }
413 }
414 &TextMessage::VerticalAlignment(vertical_alignment) => {
415 if text_ref.vertical_alignment() != vertical_alignment {
416 text_ref.set_vertical_alignment(vertical_alignment);
417 drop(text_ref);
418 self.invalidate_layout();
419 }
420 }
421 &TextMessage::Shadow(shadow) => {
422 if *text_ref.shadow != shadow {
423 text_ref.set_shadow(shadow);
424 drop(text_ref);
425 self.invalidate_layout();
426 }
427 }
428 TextMessage::ShadowBrush(brush) => {
429 if &*text_ref.shadow_brush != brush {
430 text_ref.set_shadow_brush(brush.clone());
431 drop(text_ref);
432 self.invalidate_layout();
433 }
434 }
435 &TextMessage::ShadowDilation(dilation) => {
436 if *text_ref.shadow_dilation != dilation {
437 text_ref.set_shadow_dilation(dilation);
438 drop(text_ref);
439 self.invalidate_layout();
440 }
441 }
442 &TextMessage::ShadowOffset(offset) => {
443 if *text_ref.shadow_offset != offset {
444 text_ref.set_shadow_offset(offset);
445 drop(text_ref);
446 self.invalidate_layout();
447 }
448 }
449 TextMessage::FontSize(height) => {
450 if text_ref.font_size() != height {
451 text_ref.set_font_size(height.clone());
452 drop(text_ref);
453 self.invalidate_layout();
454 }
455 }
456 }
457 }
458 }
459 }
460}
461
462impl Text {
463 /// Returns current text wrapping mode of the widget.
464 pub fn wrap_mode(&self) -> WrapMode {
465 self.formatted_text.borrow().wrap_mode()
466 }
467
468 /// Returns current text of the widget.
469 pub fn text(&self) -> String {
470 self.formatted_text.borrow().text()
471 }
472
473 /// Returns current font of the widget.
474 pub fn font(&self) -> FontResource {
475 self.formatted_text.borrow().get_font()
476 }
477
478 /// Returns current vertical alignment of the widget.
479 pub fn vertical_alignment(&self) -> VerticalAlignment {
480 self.formatted_text.borrow().vertical_alignment()
481 }
482
483 /// Returns current horizontal alignment of the widget.
484 pub fn horizontal_alignment(&self) -> HorizontalAlignment {
485 self.formatted_text.borrow().horizontal_alignment()
486 }
487}
488
489/// TextBuilder is used to create instances of [`Text`] widget and register them in the user interface.
490pub struct TextBuilder {
491 widget_builder: WidgetBuilder,
492 text: Option<String>,
493 font: Option<FontResource>,
494 vertical_text_alignment: VerticalAlignment,
495 horizontal_text_alignment: HorizontalAlignment,
496 wrap: WrapMode,
497 shadow: bool,
498 shadow_brush: Brush,
499 shadow_dilation: f32,
500 shadow_offset: Vector2<f32>,
501 font_size: Option<StyledProperty<f32>>,
502}
503
504impl TextBuilder {
505 /// Creates new [`TextBuilder`] instance using the provided base widget builder.
506 pub fn new(widget_builder: WidgetBuilder) -> Self {
507 Self {
508 widget_builder,
509 text: None,
510 font: None,
511 vertical_text_alignment: VerticalAlignment::Top,
512 horizontal_text_alignment: HorizontalAlignment::Left,
513 wrap: WrapMode::NoWrap,
514 shadow: false,
515 shadow_brush: Brush::Solid(Color::BLACK),
516 shadow_dilation: 1.0,
517 shadow_offset: Vector2::new(1.0, 1.0),
518 font_size: None,
519 }
520 }
521
522 /// Sets the desired text of the widget.
523 pub fn with_text<P: AsRef<str>>(mut self, text: P) -> Self {
524 self.text = Some(text.as_ref().to_owned());
525 self
526 }
527
528 /// Sets the desired font of the widget.
529 pub fn with_font(mut self, font: FontResource) -> Self {
530 self.font = Some(font);
531 self
532 }
533
534 /// Sets the desired font of the widget using font wrapped in [`Option`].
535 pub fn with_opt_font(mut self, font: Option<FontResource>) -> Self {
536 self.font = font;
537 self
538 }
539
540 /// Sets the desired vertical alignment of the widget.
541 pub fn with_vertical_text_alignment(mut self, valign: VerticalAlignment) -> Self {
542 self.vertical_text_alignment = valign;
543 self
544 }
545
546 /// Sets the desired height of the text.
547 pub fn with_font_size(mut self, font_size: StyledProperty<f32>) -> Self {
548 self.font_size = Some(font_size);
549 self
550 }
551
552 /// Sets the desired horizontal alignment of the widget.
553 pub fn with_horizontal_text_alignment(mut self, halign: HorizontalAlignment) -> Self {
554 self.horizontal_text_alignment = halign;
555 self
556 }
557
558 /// Sets the desired word wrapping mode of the widget.
559 pub fn with_wrap(mut self, wrap: WrapMode) -> Self {
560 self.wrap = wrap;
561 self
562 }
563
564 /// Whether the shadow enabled or not.
565 pub fn with_shadow(mut self, shadow: bool) -> Self {
566 self.shadow = shadow;
567 self
568 }
569
570 /// Sets desired shadow brush. It will be used to render the shadow.
571 pub fn with_shadow_brush(mut self, brush: Brush) -> Self {
572 self.shadow_brush = brush;
573 self
574 }
575
576 /// Sets desired shadow dilation in units. Keep in mind that the dilation is absolute,
577 /// not percentage-based.
578 pub fn with_shadow_dilation(mut self, thickness: f32) -> Self {
579 self.shadow_dilation = thickness;
580 self
581 }
582
583 /// Sets desired shadow offset in units.
584 pub fn with_shadow_offset(mut self, offset: Vector2<f32>) -> Self {
585 self.shadow_offset = offset;
586 self
587 }
588
589 /// Finishes text widget creation and registers it in the user interface, returning its handle to you.
590 pub fn build(mut self, ctx: &mut BuildContext) -> Handle<UiNode> {
591 let font = if let Some(font) = self.font {
592 font
593 } else {
594 ctx.default_font()
595 };
596
597 if self.widget_builder.foreground.is_none() {
598 self.widget_builder.foreground = Some(ctx.style.property(Style::BRUSH_TEXT));
599 }
600
601 let text = Text {
602 widget: self.widget_builder.build(ctx),
603 formatted_text: RefCell::new(
604 FormattedTextBuilder::new(font)
605 .with_text(self.text.unwrap_or_default())
606 .with_vertical_alignment(self.vertical_text_alignment)
607 .with_horizontal_alignment(self.horizontal_text_alignment)
608 .with_wrap(self.wrap)
609 .with_shadow(self.shadow)
610 .with_shadow_brush(self.shadow_brush)
611 .with_shadow_dilation(self.shadow_dilation)
612 .with_shadow_offset(self.shadow_offset)
613 .with_font_size(
614 self.font_size
615 .unwrap_or_else(|| ctx.style.property(Style::FONT_SIZE)),
616 )
617 .build(),
618 ),
619 };
620 ctx.add_node(UiNode::new(text))
621 }
622}
623
624#[cfg(test)]
625mod test {
626 use crate::text::TextBuilder;
627 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
628
629 #[test]
630 fn test_deletion() {
631 test_widget_deletion(|ctx| TextBuilder::new(WidgetBuilder::new()).build(ctx));
632 }
633}