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}