vizia_core/views/
label.rs

1use crate::prelude::*;
2
3/// A label used to display text.
4///
5/// # Examples
6///
7/// ## Basic label
8///
9/// A label can be used to simply display some text on the screen.
10///
11/// ```
12/// # use vizia_core::prelude::*;
13/// #
14/// # let cx = &mut Context::default();
15/// #
16/// Label::new(cx, "Text");
17/// ```
18///
19/// ## Label bound to data
20///
21/// A label can be bound to data using a lens which automatically updates the text whenever the underlying data changes.
22///
23/// ```
24/// # use vizia_core::prelude::*;
25/// #
26/// # let cx = &mut Context::default();
27/// #
28/// #[derive(Lens)]
29/// struct AppData {
30///     text: String,
31/// }
32///
33/// impl Model for AppData {}
34///
35/// AppData {
36///     text: String::from("Text"),
37/// }
38/// .build(cx);
39///
40/// Label::new(cx, AppData::text);
41/// ```
42///
43/// ## Label with text wrapping
44///
45/// A label automatically wraps the text if it doesn't fit inside of the width of the label.
46///
47/// ```
48/// # use vizia_core::prelude::*;
49/// #
50/// # let mut cx = &mut Context::default();
51/// #
52/// Label::new(
53///     cx,
54///     "This is a really long text to showcase the text wrapping support of a label.",
55/// )
56/// .width(Pixels(100.0));
57/// ```
58///
59/// ## Label without text wrapping
60///
61/// A label can also be configured to never wrap the text by using the [`text_wrap`](crate::prelude::Handle::text_wrap) method.
62///
63/// ```
64/// # use vizia_core::prelude::*;
65/// #
66/// # let mut cx = &mut Context::default();
67/// #
68/// Label::new(
69///     cx,
70///     "This is a really long text to showcase disabled text wrapping of a label.",
71/// )
72/// .width(Pixels(100.0))
73/// .text_wrap(false);
74/// ```
75///
76/// ## Label for a button
77///
78/// A label can also be used inside of a button to be able to add text to it.
79///
80/// ```
81/// # use vizia_core::prelude::*;
82/// # let cx = &mut Context::default();
83/// #
84/// Button::new(cx, |_| {}, |cx| Label::new(cx, "Text"));
85/// ```
86pub struct Label {
87    describing: Option<String>,
88}
89
90impl Label {
91    /// Creates a new [Label] view.
92    ///
93    /// # Examples
94    ///
95    /// ```
96    /// # use vizia_core::prelude::*;
97    /// #
98    /// # let cx = &mut Context::default();
99    /// #
100    /// Label::new(cx, "Text");
101    /// ```
102    pub fn new<T>(cx: &mut Context, text: impl Res<T> + Clone) -> Handle<Self>
103    where
104        T: ToStringLocalized,
105    {
106        Self { describing: None }.build(cx, |_| {}).text(text.clone()).role(Role::Label).name(text)
107    }
108
109    /// Creates a new rich [Label] view.
110    pub fn rich<T>(
111        cx: &mut Context,
112        text: impl Res<T> + Clone,
113        children: impl Fn(&mut Context),
114    ) -> Handle<Self>
115    where
116        T: ToStringLocalized,
117    {
118        Self { describing: None }
119            .build(cx, |cx| {
120                children(cx);
121            })
122            .text(text.clone())
123            .role(Role::Label)
124            .name(text)
125    }
126}
127
128impl Handle<'_, Label> {
129    /// Which form element does this label describe.
130    ///
131    /// # Examples
132    ///
133    /// ```
134    /// # use vizia_core::prelude::*;
135    /// #
136    /// # #[derive(Lens)]
137    /// # struct AppData {
138    /// #     value: bool,
139    /// # }
140    /// #
141    /// # impl Model for AppData {}
142    /// #
143    /// # enum AppEvent {
144    /// #     ToggleValue,
145    /// # }
146    /// #
147    /// # let cx = &mut Context::default();
148    /// #
149    /// # AppData { value: false }.build(cx);
150    /// #
151    /// Checkbox::new(cx, AppData::value).on_toggle(|cx| cx.emit(AppEvent::ToggleValue)).id("checkbox_identifier");
152    /// Label::new(cx, "hello").describing("checkbox_identifier");
153    /// ```
154    pub fn describing(self, entity_identifier: impl Into<String>) -> Self {
155        let identifier = entity_identifier.into();
156        if let Some(id) = self.cx.resolve_entity_identifier(&identifier) {
157            self.cx.style.labelled_by.insert(id, self.entity);
158        }
159        self.modify(|label| label.describing = Some(identifier)).class("describing").hidden(true)
160    }
161}
162
163impl View for Label {
164    fn element(&self) -> Option<&'static str> {
165        Some("label")
166    }
167
168    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
169        event.map(|window_event, meta| match window_event {
170            WindowEvent::Press { .. } | WindowEvent::PressDown { .. } => {
171                if cx.current() == cx.mouse.left.pressed && meta.target == cx.current() {
172                    if let Some(describing) = self
173                        .describing
174                        .as_ref()
175                        .and_then(|identity| cx.resolve_entity_identifier(identity))
176                    {
177                        let old = cx.current;
178                        cx.current = describing;
179                        cx.focus_with_visibility(false);
180                        let message = if matches!(window_event, WindowEvent::Press { .. }) {
181                            WindowEvent::Press { mouse: false }
182                        } else {
183                            WindowEvent::PressDown { mouse: false }
184                        };
185                        cx.emit_to(describing, message);
186                        cx.current = old;
187                    }
188                }
189            }
190            _ => {}
191        });
192    }
193}
194
195/// A view which represents a span of text within a label.
196pub struct TextSpan {}
197
198impl TextSpan {
199    /// Create a new [TextSpan] view.
200    pub fn new<'a>(
201        cx: &'a mut Context,
202        text: &str,
203        children: impl Fn(&mut Context),
204    ) -> Handle<'a, Self> {
205        Self {}
206            .build(cx, |cx| {
207                cx.style.text_span.insert(cx.current(), true);
208                children(cx);
209            })
210            .text(text)
211            .display(Display::None)
212            .pointer_events(PointerEvents::None)
213    }
214}
215
216impl View for TextSpan {
217    fn element(&self) -> Option<&'static str> {
218        Some("text-span")
219    }
220}