1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/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)]
1314zng_wgt::enable_widget_macros!();
1516use zng_wgt::prelude::*;
1718#[macro_use]
19extern crate bitflags;
2021pub mod cmd;
22pub mod node;
23mod text_properties;
24pub use text_properties::*;
2526#[doc(hidden)]
27pub use zng_wgt::prelude::formatx as __formatx;
2829pub mod icon;
3031/// 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 TextEditMix<
117 SelectionToolbarMix<
118 TextInspectMix<
119 WidgetBase
120 >>>>>>>>>>>>
121);
122123impl Text {
124/// Context variables used by properties in text.
125pub fn context_vars_set(set: &mut ContextValueSet) {
126 FontMix::<()>::context_vars_set(set);
127 TextFillMix::<()>::context_vars_set(set);
128 TextAlignMix::<()>::context_vars_set(set);
129 TextWrapMix::<()>::context_vars_set(set);
130 TextDecorationMix::<()>::context_vars_set(set);
131 TextSpacingMix::<()>::context_vars_set(set);
132 TextTransformMix::<()>::context_vars_set(set);
133 FontFeaturesMix::<()>::context_vars_set(set);
134 TextEditMix::<()>::context_vars_set(set);
135 SelectionToolbarMix::<()>::context_vars_set(set);
136 TextInspectMix::<()>::context_vars_set(set);
137138 LangMix::<()>::context_vars_set(set);
139 }
140}
141142/// The text string.
143///
144/// Set to an empty string (`""`) by default.
145#[property(CHILD, capture, default(""), widget_impl(Text))]
146pub fn txt(txt: impl IntoVar<Txt>) {}
147148/// Value that is parsed from the text and displayed as the text.
149///
150/// This is an alternative to [`txt`] that converts to and from `T` if it can be formatted to display text and can parse, with
151/// parse error that can display.
152///
153/// If the parse operation fails the value variable is not updated and the error display text is set in [`DATA.invalidate`], you
154/// can use [`has_data_error`] and [`get_data_error_txt`] to display the error.
155///
156/// See also [`txt_parse_live`] for ways to control when the parse attempt happens.
157///
158/// [`txt`]: fn@txt
159/// [`txt_parse_live`]: fn@txt_parse_live
160/// [`DATA.invalidate`]: zng_wgt_data::DATA::invalidate
161/// [`has_data_error`]: fn@zng_wgt_data::has_data_error
162/// [`get_data_error_txt`]: fn@zng_wgt_data::get_data_error_txt
163#[property(CHILD, widget_impl(Text))]
164pub fn txt_parse<T>(child: impl UiNode, value: impl IntoVar<T>) -> impl UiNode
165where
166T: TxtParseValue,
167{
168 node::parse_text(child, value)
169}
170171/// Represents a type that can be a var value, parse and display.
172///
173/// This trait is used by [`txt_parse`]. It is implemented for all types that are
174/// `VarValue + FromStr + Display where FromStr::Err: Display`.
175///
176/// [`txt_parse`]: fn@txt_parse
177#[diagnostic::on_unimplemented(note = "`TxtParseValue` is implemented for all `T: VarValue + Display + FromStr<Error: Display>")]
178pub trait TxtParseValue: VarValue {
179/// Try parse `Self` from `txt`, formats the error for display.
180 ///
181 /// Note that the widget context is not available here as this method is called in the app context.
182fn from_txt(txt: &Txt) -> Result<Self, Txt>;
183/// Display the value, the returned text can be parsed back to an equal value.
184 ///
185 /// Note that the widget context is not available here as this method is called in the app context.
186fn to_txt(&self) -> Txt;
187}
188impl<T> TxtParseValue for T
189where
190T: VarValue + std::str::FromStr + std::fmt::Display,
191 <Self as std::str::FromStr>::Err: std::fmt::Display,
192{
193fn from_txt(txt: &Txt) -> Result<Self, Txt> {
194 T::from_str(txt).map_err(|e| e.to_txt())
195 }
196197fn to_txt(&self) -> Txt {
198 ToTxt::to_txt(self)
199 }
200}
201202impl Text {
203fn widget_intrinsic(&mut self) {
204self.widget_builder().push_build_action(|wgt| {
205let child = node::render_text();
206let child = node::non_interactive_caret(child);
207let child = node::interactive_carets(child);
208let child = node::render_overlines(child);
209let child = node::render_strikethroughs(child);
210let child = node::render_underlines(child);
211let child = node::render_ime_preview_underlines(child);
212let child = node::render_selection(child);
213 wgt.set_child(child.boxed());
214215 wgt.push_intrinsic(NestGroup::CHILD_LAYOUT + 100, "layout_text", |child| {
216let child = node::selection_toolbar_node(child);
217 node::layout_text(child)
218 });
219220let text = if wgt.property(property_id!(Self::txt_parse)).is_some() {
221 wgt.capture_var(property_id!(Self::txt))
222 .unwrap_or_else(|| var(Txt::from_str("")).boxed())
223 } else {
224 wgt.capture_var_or_default(property_id!(Self::txt))
225 };
226 wgt.push_intrinsic(NestGroup::EVENT, "resolve_text", |child| node::resolve_text(child, text));
227 });
228 }
229}
230231#[doc(hidden)]
232pub use zng_ext_font::{FontStyle as __FontStyle, FontWeight as __FontWeight};
233234///<span data-del-macro-root></span> A simple text run with **bold** font weight.
235///
236/// The input syntax is the same as the shorthand [`Text!`].
237///
238/// # Configure
239///
240/// Apart from the font weight this widget can be configured with contextual properties like [`Text!`].
241///
242/// [`Text!`]: struct@Text
243#[macro_export]
244macro_rules! Strong {
245 ($txt:expr) => {
246$crate::Text! {
247txt = $txt;
248 font_weight = $crate::__FontWeight::BOLD;
249 }
250 };
251 ($txt:tt, $($format:tt)*) => {
252$crate::Text! {
253txt = $crate::__formatx!($txt, $($format)*);
254 font_weight = $crate::__FontWeight::BOLD;
255 }
256 };
257}
258259///<span data-del-macro-root></span> A simple text run with *italic* font style.
260///
261/// The input syntax is the same as the shorthand [`Text!`].
262///
263/// # Configure
264///
265/// Apart from the font style this widget can be configured with contextual properties like [`Text!`].
266///
267/// [`Text!`]: struct@Text
268#[macro_export]
269macro_rules! Em {
270 ($txt:expr) => {
271$crate::Text! {
272txt = $txt;
273 font_style = $crate::__FontStyle::Italic;
274 }
275 };
276 ($txt:tt, $($format:tt)*) => {
277$crate::Text! {
278txt = $crate::__formatx!($txt, $($format)*);
279 font_style = $crate::__FontStyle::Italic;
280 }
281 };
282}