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(&MOUSE_INPUT_EVENT)
74                .sub_event(&TOUCH_INPUT_EVENT)
75                .sub_event(&ACCESS_CLICK_EVENT);
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::Event { update } => {
89            c.event(update);
90
91            if let Some(args) = MOUSE_INPUT_EVENT.on(update) {
92                if args.is_mouse_down() {
93                    FOCUS.focus_widget_or_enter(target.get(), true, false);
94                }
95            } else if let Some(args) = TOUCH_INPUT_EVENT.on(update) {
96                if args.is_touch_start() {
97                    FOCUS.focus_widget_or_enter(target.get(), true, false);
98                }
99            } else if let Some(args) = ACCESS_CLICK_EVENT.on(update)
100                && args.is_primary
101            {
102                FOCUS.focus_widget_or_enter(target.get(), true, false);
103            }
104        }
105        UiNodeOp::Update { .. } => {
106            if let Some(id) = target.get_new() {
107                if let Some(id) = prev_target.take() {
108                    UPDATES.update_info(id);
109                }
110                UPDATES.update_info(id);
111                WIDGET.update_info();
112            }
113        }
114        _ => {}
115    })
116}