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}