zng_wgt_text_input/
label.rs1use zng_app::property_args;
4use zng_ext_input::{focus::FOCUS, gesture::CLICK_EVENT};
5use zng_wgt::prelude::*;
6use zng_wgt_input::gesture::{Mnemonic, get_mnemonic, get_mnemonic_char, mnemonic_txt};
7use zng_wgt_style::{Style, StyleMix, impl_style_fn};
8
9#[doc(hidden)]
10pub use zng_wgt::prelude::formatx as __formatx;
11use zng_wgt_text::node::TEXT;
12
13#[widget($crate::label::Label {
32 ($txt:expr, $target:expr $(,)?) => {
33 txt = $txt;
34 target = $target;
35 };
36 ($txt:literal) => {
37 txt = $crate::label::__formatx!($txt);
38 };
39 ($txt:expr) => {
40 txt = $txt;
41 };
42})]
43pub struct Label(StyleMix<zng_wgt_text::Text>);
44impl Label {
45 fn widget_intrinsic(&mut self) {
46 self.style_intrinsic(STYLE_FN_VAR, property_id!(self::style_fn));
47
48 self.widget_builder().push_pre_build_action(|wgt| {
50 let mut mnemonic_data = None;
51 if let Some(txt) = wgt.property_mut(property_id!(zng_wgt_text::txt))
52 && !*txt.captured
53 {
54 let t = txt.args.downcast_var::<Txt>(0);
55
56 let mnemonic = var(Mnemonic::None);
57 mnemonic_data = Some((t.clone(), mnemonic.clone()));
58 let t = expr_var! {
59 let t = #{t};
60 if let Mnemonic::FromTxt { marker, .. } = #{mnemonic} {
61 let mut prev_is_marker = false;
62 for (i, c) in t.char_indices() {
63 if c == *marker {
64 prev_is_marker = true;
65 } else if prev_is_marker && c.is_alphanumeric() {
66 return formatx!("{}{}", &t[..i - 1], &t[i..]);
67 }
68 }
69 }
70 t.clone()
71 };
72 *txt.args = property_args!(zng_wgt_text::txt = t);
73 }
74 if let Some((raw_txt, mnemonic)) = mnemonic_data {
75 wgt.push_intrinsic(NestGroup::WIDGET_INNER, "get_mnemonic", move |c| get_mnemonic(c, mnemonic.clone()));
76 wgt.push_intrinsic(NestGroup::CHILD_LAYOUT + 101, "mnemonic_underline", move |c| {
77 mnemonic_underline_node(c)
78 });
79 wgt.push_intrinsic(NestGroup::CHILD, "mnemonic_txt", move |c| mnemonic_txt(c, raw_txt.clone()));
80 }
81 });
82 }
83}
84impl_style_fn!(Label, DefaultStyle);
85
86#[widget($crate::label::DefaultStyle)]
88pub struct DefaultStyle(Style);
89impl DefaultStyle {
90 fn widget_intrinsic(&mut self) {
91 widget_set! {
92 self;
93 replace = true;
94 }
95 }
96}
97
98#[property(CONTEXT, widget_impl(Label))]
103pub fn target(child: impl IntoUiNode, target: impl IntoVar<WidgetId>) -> UiNode {
104 let target = target.into_var();
105 let mut prev_target = None::<WidgetId>;
106
107 match_node(child, move |c, op| match op {
108 UiNodeOp::Init => {
109 WIDGET.sub_var(&target).sub_event_when(&CLICK_EVENT, |a| a.is_primary());
110 }
111 UiNodeOp::Info { info } => {
112 c.info(info);
113 if let Some(mut a) = info.access() {
114 let target = target.get();
115 prev_target = Some(target);
116 a.set_labels(target);
117 }
118 }
119 UiNodeOp::Update { updates } => {
120 if let Some(id) = target.get_new() {
121 if let Some(id) = prev_target.take() {
122 UPDATES.update_info(id);
123 }
124 UPDATES.update_info(id);
125 WIDGET.update_info();
126 }
127
128 c.update(updates);
129
130 if CLICK_EVENT.any_update(true, |a| a.is_primary()) {
131 FOCUS.focus_widget_or_enter(target.get(), true, false);
132 }
133 }
134 _ => {}
135 })
136}
137
138context_var! {
139 pub static MNEMONIC_UNDERLINE_VAR: bool = false;
141}
142
143#[property(CONTEXT, default(MNEMONIC_UNDERLINE_VAR), widget_impl(Label))]
152pub fn mnemonic_underline(child: impl IntoUiNode, enabled: impl IntoVar<bool>) -> UiNode {
153 with_context_var(child, MNEMONIC_UNDERLINE_VAR, enabled)
154}
155
156fn mnemonic_underline_node(child: impl IntoUiNode) -> UiNode {
157 let m_char = var(None);
158 let child = get_mnemonic_char(child, m_char.clone());
159
160 match_node(child, move |c, op| match op {
161 UiNodeOp::Init => {
162 WIDGET.sub_var_layout(&MNEMONIC_UNDERLINE_VAR).sub_var_layout(&m_char);
163 }
164 UiNodeOp::Layout { wl, final_size } => {
165 *final_size = c.layout(wl);
166
167 if MNEMONIC_UNDERLINE_VAR.get() {
168 if let Some(c) = m_char.get() {
169 let r = TEXT.resolved();
170 let mut ci = None;
171 for (i, tc) in r.segmented_text.text().char_indices() {
173 if c == tc {
174 ci = Some(i);
175 break;
176 }
177 }
178 if ci.is_none() {
179 for (i, tc) in r.segmented_text.text().char_indices() {
181 if c.to_lowercase().eq(tc.to_lowercase()) {
182 ci = Some(i);
183 break;
184 }
185 }
186 }
187 if let Some(i) = ci {
188 let l = TEXT.laidout();
189 let start = l.shaped_text.snap_caret_line(i.into());
190 let mut end = start;
191 end.index += c.len_utf8();
192 let u = l.shaped_text.highlight_underlines(start..end, r.segmented_text.text()).collect();
193 drop(l);
194 TEXT.set_underlines(u);
195 } else {
196 TEXT.set_underlines(vec![]);
197 }
198 } else {
199 TEXT.set_underlines(vec![]);
201 }
202 }
203 }
204 _ => {}
205 })
206}