zng_wgt_text_input/
label.rs

1//! Label text.
2
3use zng_app::access::ACCESS_CLICK_EVENT;
4use zng_ext_input::{
5    focus::{FOCUS, FocusInfoBuilder},
6    mouse::MOUSE_INPUT_EVENT,
7    touch::TOUCH_INPUT_EVENT,
8};
9use zng_wgt::prelude::*;
10use zng_wgt_input::focus::FocusableMix;
11use zng_wgt_style::{Style, StyleMix, impl_style_fn};
12
13/// Styleable and focusable read-only text widget.
14///
15/// Optionally can be the label of a [`target`](#method.target) widget, in this case the label is not focusable, it transfers focus
16/// to the target.
17///
18/// # Shorthand
19///
20/// The widget macro supports the shorthand that sets the `txt` and `target` properties.
21///
22/// ```
23/// # zng_wgt::enable_widget_macros!();
24/// # use zng_wgt::prelude::*;
25/// # use zng_wgt_text_input::label::*;
26/// #
27/// # fn main() {
28/// # let _scope = zng_app::APP.minimal();
29/// let label = Label!("txt", "target");
30/// # }
31/// ```
32#[widget($crate::label::Label {
33    ($txt:expr, $target:expr $(,)?) => {
34        txt = $txt;
35        target = $target;
36    };
37})]
38pub struct Label(FocusableMix<StyleMix<zng_wgt_text::Text>>);
39impl Label {
40    fn widget_intrinsic(&mut self) {
41        self.style_intrinsic(STYLE_FN_VAR, property_id!(self::style_fn));
42    }
43}
44impl_style_fn!(Label, DefaultStyle);
45
46/// Default label style.
47#[widget($crate::label::DefaultStyle)]
48pub struct DefaultStyle(Style);
49impl DefaultStyle {
50    fn widget_intrinsic(&mut self) {
51        widget_set! {
52            self;
53            replace = true;
54        }
55    }
56}
57
58/// Defines the widget the label is for.
59///
60/// When the label is pressed the widget or the first focusable child of the widget is focused.
61/// Accessibility metadata is also set so the target widget is marked as *labelled-by* this widget in the view-process.
62///
63/// If this is set focusable is disabled on the label widget.
64#[property(CONTEXT, widget_impl(Label))]
65pub fn target(child: impl IntoUiNode, target: impl IntoVar<WidgetId>) -> UiNode {
66    let target = target.into_var();
67    let mut prev_target = None::<WidgetId>;
68
69    match_node(child, move |c, op| match op {
70        UiNodeOp::Init => {
71            WIDGET
72                .sub_var(&target)
73                .sub_event_when(&MOUSE_INPUT_EVENT, |a| a.is_mouse_down())
74                .sub_event_when(&TOUCH_INPUT_EVENT, |a| a.is_touch_start())
75                .sub_event_when(&ACCESS_CLICK_EVENT, |a| a.is_primary);
76        }
77        UiNodeOp::Info { info } => {
78            c.info(info);
79
80            FocusInfoBuilder::new(info).focusable(false);
81
82            if let Some(mut a) = info.access() {
83                let target = target.get();
84                prev_target = Some(target);
85                a.set_labels(target);
86            }
87        }
88        UiNodeOp::Update { updates } => {
89            if let Some(id) = target.get_new() {
90                if let Some(id) = prev_target.take() {
91                    UPDATES.update_info(id);
92                }
93                UPDATES.update_info(id);
94                WIDGET.update_info();
95            }
96
97            c.update(updates);
98
99            if MOUSE_INPUT_EVENT.any_update(true, |a| a.is_mouse_down())
100                || TOUCH_INPUT_EVENT.any_update(true, |a| a.is_touch_start())
101                || ACCESS_CLICK_EVENT.any_update(true, |a| a.is_primary)
102            {
103                FOCUS.focus_widget_or_enter(target.get(), true, false);
104            }
105        }
106        _ => {}
107    })
108}