Skip to main content

zng_wgt_text/
lib.rs

1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3//!
4//! Text widgets and properties.
5//!
6//! # Crate
7//!
8#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9// suppress nag about very simple boxed closure signatures.
10#![expect(clippy::type_complexity)]
11#![warn(unused_extern_crates)]
12#![warn(missing_docs)]
13
14zng_wgt::enable_widget_macros!();
15
16use zng_wgt::prelude::*;
17
18#[macro_use]
19extern crate bitflags;
20
21pub mod cmd;
22pub mod node;
23mod text_properties;
24pub use text_properties::*;
25
26#[doc(hidden)]
27pub use zng_wgt::prelude::formatx as __formatx;
28
29pub mod icon;
30
31/// A configured text run.
32///
33/// # Examples
34///
35/// ```
36/// # zng_wgt::enable_widget_macros!();
37/// # use zng_wgt_text::*;
38/// # fn main() {
39/// let hello_txt = Text! {
40///     font_family = "Arial";
41///     font_size = 18;
42///     txt = "Hello!";
43/// };
44/// # }
45/// ```
46/// # Shorthand
47///
48/// The `Text!` macro provides shorthand syntax that matches the [`formatx!`] input, but outputs a text widget:
49///
50/// ```
51/// # zng_wgt::enable_widget_macros!();
52/// # use zng_wgt_text::*;
53/// # fn main() {
54/// let txt = Text!("Hello!");
55///
56/// let name = "World";
57/// let fmt = Text!("Hello {}!", name);
58///
59/// let expr = Text!({
60///     let mut s = String::new();
61///     s.push('a');
62///     s
63/// });
64/// # }
65/// ```
66///
67/// The code abode is equivalent to:
68///
69/// ```
70/// # zng_wgt::enable_widget_macros!();
71/// # use zng_wgt_text::*;
72/// # fn main() {
73/// # use zng_wgt::prelude::*;
74/// let txt = Text! {
75///     txt = formatx!("Hello!");
76/// };
77///
78/// let name = "World";
79/// let fmt = Text! {
80///     txt = formatx!("Hello {}!", name);
81/// };
82///
83/// let expr = Text! {
84///     txt = {
85///         let mut s = String::new();
86///         s.push('a');
87///         s
88///     };
89/// };
90/// # }
91/// ```
92///
93/// [`formatx!`]: zng_wgt::prelude::formatx
94#[widget($crate::Text {
95    ($txt:literal) => {
96        txt = $crate::__formatx!($txt);
97    };
98    ($txt:expr) => {
99        txt = $txt;
100    };
101    ($txt:tt, $($format:tt)*) => {
102        txt = $crate::__formatx!($txt, $($format)*);
103    };
104})]
105#[rustfmt::skip]
106pub struct Text(
107    FontMix<
108    TextFillMix<
109    TextAlignMix<
110    TextWrapMix<
111    TextDecorationMix<
112    TextSpacingMix<
113    TextTransformMix<
114    LangMix<
115    FontFeaturesMix<
116    ParagraphMix<
117    TextEditMix<
118    SelectionToolbarMix<
119    TextInspectMix<
120    WidgetBase
121    >>>>>>>>>>>>>
122);
123
124impl Text {
125    /// Context variables used by properties in text.
126    pub fn context_vars_set(set: &mut ContextValueSet) {
127        Self::context_vars_set_except_lang(set);
128        LangMix::<()>::context_vars_set(set);
129    }
130
131    /// Context variables used by properties in text, except [`LangMix::context_vars_set`].
132    pub fn context_vars_set_except_lang(set: &mut ContextValueSet) {
133        FontMix::<()>::context_vars_set(set);
134        TextFillMix::<()>::context_vars_set(set);
135        TextAlignMix::<()>::context_vars_set(set);
136        TextWrapMix::<()>::context_vars_set(set);
137        TextDecorationMix::<()>::context_vars_set(set);
138        TextSpacingMix::<()>::context_vars_set(set);
139        TextTransformMix::<()>::context_vars_set(set);
140        FontFeaturesMix::<()>::context_vars_set(set);
141        TextEditMix::<()>::context_vars_set(set);
142        SelectionToolbarMix::<()>::context_vars_set(set);
143        TextInspectMix::<()>::context_vars_set(set);
144    }
145}
146
147/// The text string.
148///
149/// In a text widget this property is captured, in other widgets sets the child node to a text widget.
150#[property(CHILD, default(""), widget_impl(Text))]
151pub fn txt(wgt: &mut WidgetBuilding, txt: impl IntoVar<Txt>) {
152    wgt.set_child(Text!(txt));
153}
154
155/// Value that is parsed from the text and displayed as the text.
156///
157/// This is an alternative to [`txt`] that converts to and from `T` if it can be formatted to display text and can parse, with
158/// parse error that can display.
159///
160/// If the parse operation fails the value variable is not updated and the error display text is set in [`DATA.invalidate`], you
161/// can use [`has_data_error`] and [`get_data_error_txt`] to display the error.
162///
163/// See also [`txt_parse_live`] for manually controlling when parse happens.
164///
165/// See also [`txt_parse_on_stop`] for only parsing when the user stops typing.
166///
167/// [`txt`]: fn@txt
168/// [`txt_parse_live`]: fn@txt_parse_live
169/// [`txt_parse_on_stop`]: fn@txt_parse_on_stop
170/// [`DATA.invalidate`]: zng_wgt_data::DATA::invalidate
171/// [`has_data_error`]: fn@zng_wgt_data::has_data_error
172/// [`get_data_error_txt`]: fn@zng_wgt_data::get_data_error_txt
173#[property(CHILD, widget_impl(Text))]
174pub fn txt_parse<T>(child: impl IntoUiNode, value: impl IntoVar<T>) -> UiNode
175where
176    T: TxtParseValue,
177{
178    node::parse_text(child, value)
179}
180
181/// Represents a type that can be a var value, parse and display.
182///
183/// This trait is used by [`txt_parse`]. It is implemented for all types that are
184/// `VarValue + FromStr + Display where FromStr::Err: Display`.
185///
186/// [`txt_parse`]: fn@txt_parse
187#[diagnostic::on_unimplemented(note = "`TxtParseValue` is implemented for all `T: VarValue + Display + FromStr<Error: Display>")]
188pub trait TxtParseValue: VarValue {
189    /// Try parse `Self` from `txt`, formats the error for display.
190    ///
191    /// Note that the widget context is not available here as this method is called in the app context.
192    fn from_txt(txt: &Txt) -> Result<Self, Txt>;
193    /// Display the value, the returned text can be parsed back to an equal value.
194    ///
195    /// Note that the widget context is not available here as this method is called in the app context.
196    fn to_txt(&self) -> Txt;
197}
198impl<T> TxtParseValue for T
199where
200    T: VarValue + std::str::FromStr + std::fmt::Display,
201    <Self as std::str::FromStr>::Err: std::fmt::Display,
202{
203    fn from_txt(txt: &Txt) -> Result<Self, Txt> {
204        T::from_str(txt).map_err(|e| e.to_txt())
205    }
206
207    fn to_txt(&self) -> Txt {
208        ToTxt::to_txt(self)
209    }
210}
211
212impl Text {
213    fn widget_intrinsic(&mut self) {
214        self.widget_builder().push_build_action(|wgt| {
215            let child = node::render_text();
216            let child = node::non_interactive_caret(child);
217            let child = node::interactive_carets(child);
218            let child = node::render_overlines(child);
219            let child = node::render_strikethroughs(child);
220            let child = node::render_underlines(child);
221            let child = node::render_ime_preview_underlines(child);
222            let child = node::render_selection(child);
223            wgt.set_child(child);
224
225            wgt.push_intrinsic(NestGroup::CHILD_LAYOUT + 100, "layout_text", |child| {
226                let child = node::selection_toolbar_node(child);
227                node::layout_text(child)
228            });
229
230            let text = if wgt.property(property_id!(Self::txt_parse)).is_some() {
231                wgt.capture_var(property_id!(Self::txt)).unwrap_or_else(|| var(Txt::from_str("")))
232            } else {
233                wgt.capture_var_or_default(property_id!(Self::txt))
234            };
235            wgt.push_intrinsic(NestGroup::EVENT, "resolve_text", |child| {
236                let child = node::rich_text_component(child, "text");
237                node::resolve_text(child, text)
238            });
239        });
240    }
241}
242
243#[doc(hidden)]
244pub use zng_ext_font::{FontStyle as __FontStyle, FontWeight as __FontWeight};
245
246///<span data-del-macro-root></span> A simple text run with **bold** font weight.
247///
248/// The input syntax is the same as the shorthand [`Text!`].
249///
250/// # Configure
251///
252/// Apart from the font weight this widget can be configured with contextual properties like [`Text!`].
253///
254/// [`Text!`]: struct@Text
255#[macro_export]
256macro_rules! Strong {
257    ($txt:expr) => {
258        $crate::Text! {
259            txt = $txt;
260            font_weight = $crate::__FontWeight::BOLD;
261        }
262    };
263    ($txt:tt, $($format:tt)*) => {
264        $crate::Text! {
265            txt = $crate::__formatx!($txt, $($format)*);
266            font_weight = $crate::__FontWeight::BOLD;
267        }
268    };
269}
270
271///<span data-del-macro-root></span> A simple text run with *italic* font style.
272///
273/// The input syntax is the same as the shorthand [`Text!`].
274///
275/// # Configure
276///
277/// Apart from the font style this widget can be configured with contextual properties like [`Text!`].
278///
279/// [`Text!`]: struct@Text
280#[macro_export]
281macro_rules! Em {
282    ($txt:expr) => {
283        $crate::Text! {
284            txt = $txt;
285            font_style = $crate::__FontStyle::Italic;
286        }
287    };
288    ($txt:tt, $($format:tt)*) => {
289        $crate::Text! {
290            txt = $crate::__formatx!($txt, $($format)*);
291            font_style = $crate::__FontStyle::Italic;
292        }
293    };
294}