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