Skip to main content

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