@effindomv2/fui-rs 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/COMMERCIAL.md +7 -0
  2. package/Cargo.toml +34 -0
  3. package/LICENSE.md +9 -0
  4. package/README.md +88 -0
  5. package/package.json +54 -0
  6. package/scripts/build.sh +227 -0
  7. package/scripts/check-runtime-dependency.sh +41 -0
  8. package/scripts/framework-host-services.ts +40 -0
  9. package/scripts/generate-host-events.ts +17 -0
  10. package/scripts/generate-host-services.ts +28 -0
  11. package/scripts/hostgen/common.ts +73 -0
  12. package/scripts/hostgen/registry.ts +159 -0
  13. package/scripts/hostgen/rust-host-events.ts +171 -0
  14. package/scripts/hostgen/rust-host-services.ts +257 -0
  15. package/src/animation.rs +625 -0
  16. package/src/app.rs +324 -0
  17. package/src/assets.rs +572 -0
  18. package/src/bindings/mod.rs +1 -0
  19. package/src/bindings/ui.rs +705 -0
  20. package/src/bitmap.rs +317 -0
  21. package/src/bridge_callbacks.rs +242 -0
  22. package/src/context_menu_manager.rs +332 -0
  23. package/src/controls/anti_selection_area.rs +54 -0
  24. package/src/controls/button.rs +542 -0
  25. package/src/controls/checkbox.rs +372 -0
  26. package/src/controls/combobox.rs +1574 -0
  27. package/src/controls/context_menu.rs +1367 -0
  28. package/src/controls/control_template_set.rs +46 -0
  29. package/src/controls/control_tokens.rs +596 -0
  30. package/src/controls/dialog.rs +590 -0
  31. package/src/controls/dropdown.rs +1010 -0
  32. package/src/controls/form.rs +229 -0
  33. package/src/controls/internal/button_presenter.rs +142 -0
  34. package/src/controls/internal/checkbox_indicator_presenter.rs +210 -0
  35. package/src/controls/internal/dropdown_chevron_presenter.rs +134 -0
  36. package/src/controls/internal/dropdown_field_presenter.rs +269 -0
  37. package/src/controls/internal/dropdown_option_row_presenter.rs +184 -0
  38. package/src/controls/internal/mod.rs +13 -0
  39. package/src/controls/internal/pressable_indicator_presenter.rs +26 -0
  40. package/src/controls/internal/pressable_labeled_control.rs +492 -0
  41. package/src/controls/internal/radio_indicator_presenter.rs +208 -0
  42. package/src/controls/internal/selectable_popup_list.rs +597 -0
  43. package/src/controls/internal/slider_presenter.rs +282 -0
  44. package/src/controls/internal/switch_indicator_presenter.rs +235 -0
  45. package/src/controls/internal/text_input_core.rs +1074 -0
  46. package/src/controls/internal/text_input_presenter.rs +108 -0
  47. package/src/controls/mod.rs +235 -0
  48. package/src/controls/nav_link.rs +395 -0
  49. package/src/controls/popup.rs +191 -0
  50. package/src/controls/progress_bar.rs +287 -0
  51. package/src/controls/radio_button.rs +348 -0
  52. package/src/controls/radio_group.rs +283 -0
  53. package/src/controls/selection_area.rs +82 -0
  54. package/src/controls/shared.rs +68 -0
  55. package/src/controls/slider.rs +701 -0
  56. package/src/controls/switch.rs +293 -0
  57. package/src/controls/templating.rs +49 -0
  58. package/src/controls/tests.rs +3905 -0
  59. package/src/controls/text_area.rs +207 -0
  60. package/src/controls/text_input.rs +192 -0
  61. package/src/debug.rs +146 -0
  62. package/src/drag_drop.rs +424 -0
  63. package/src/drag_gesture.rs +258 -0
  64. package/src/drawing.rs +385 -0
  65. package/src/event.rs +1603 -0
  66. package/src/external_drop.rs +467 -0
  67. package/src/fetch.rs +500 -0
  68. package/src/ffi.rs +3542 -0
  69. package/src/file.rs +1677 -0
  70. package/src/focus_adorner.rs +307 -0
  71. package/src/focus_visibility.rs +121 -0
  72. package/src/frame_scheduler.rs +159 -0
  73. package/src/frame_signal.rs +64 -0
  74. package/src/generated/ffi.rs +798 -0
  75. package/src/generated/framework_host_services.rs +74 -0
  76. package/src/generated/mod.rs +2 -0
  77. package/src/host_services.rs +162 -0
  78. package/src/image_sampling.rs +78 -0
  79. package/src/keyboard_scroll.rs +137 -0
  80. package/src/keyboard_scroll_tracker.rs +385 -0
  81. package/src/lib.rs +547 -0
  82. package/src/logger.rs +56 -0
  83. package/src/mobile_text_selection_toolbar.rs +1034 -0
  84. package/src/navigation.rs +48 -0
  85. package/src/node/core.rs +2210 -0
  86. package/src/node/custom_drawable.rs +149 -0
  87. package/src/node/flex_box.rs +874 -0
  88. package/src/node/grid.rs +304 -0
  89. package/src/node/helpers.rs +442 -0
  90. package/src/node/image.rs +506 -0
  91. package/src/node/mod.rs +315 -0
  92. package/src/node/scroll_bar.rs +845 -0
  93. package/src/node/scroll_box.rs +514 -0
  94. package/src/node/scroll_state.rs +121 -0
  95. package/src/node/scroll_view.rs +557 -0
  96. package/src/node/svg_node.rs +474 -0
  97. package/src/node/text_node.rs +633 -0
  98. package/src/node/virtual_list.rs +678 -0
  99. package/src/panic_hook.rs +36 -0
  100. package/src/persisted.rs +274 -0
  101. package/src/platform.rs +556 -0
  102. package/src/popup_presenter.rs +344 -0
  103. package/src/selection_handle_adorner.rs +838 -0
  104. package/src/signal.rs +134 -0
  105. package/src/text.rs +1065 -0
  106. package/src/theme.rs +571 -0
  107. package/src/timers.rs +106 -0
  108. package/src/tool_tip.rs +167 -0
  109. package/src/tool_tip_manager.rs +691 -0
  110. package/src/transitions.rs +41 -0
  111. package/src/typography.rs +520 -0
  112. package/src/viewport.rs +108 -0
  113. package/src/worker.rs +308 -0
  114. package/src/worker_job.rs +82 -0
  115. package/src/worker_runtime.rs +172 -0
@@ -0,0 +1,633 @@
1
+ use super::core::*;
2
+ use super::*;
3
+ use crate::event::{SelectionChangedEventArgs, TextChangedEventArgs};
4
+ use crate::{FontFamily, FontStack, FontStyle, FontWeight};
5
+ use std::ops::Deref;
6
+
7
+ #[derive(Clone)]
8
+ pub struct TextCore {
9
+ inner: TextNode,
10
+ }
11
+
12
+ impl TextCore {
13
+ pub fn new(content: impl Into<String>) -> Self {
14
+ Self {
15
+ inner: TextNode::new_core(content),
16
+ }
17
+ }
18
+ }
19
+
20
+ impl Deref for TextCore {
21
+ type Target = TextNode;
22
+
23
+ fn deref(&self) -> &Self::Target {
24
+ &self.inner
25
+ }
26
+ }
27
+
28
+ impl Node for TextCore {
29
+ fn retained_node_ref(&self) -> NodeRef {
30
+ self.inner.retained_node_ref()
31
+ }
32
+
33
+ fn build_self(&self) {
34
+ self.inner.build_self();
35
+ }
36
+ }
37
+
38
+ #[derive(Clone)]
39
+ pub struct TextNode {
40
+ core: Rc<RefCell<NodeCore>>,
41
+ props: Rc<RefCell<TextProps>>,
42
+ }
43
+
44
+ impl TextNode {
45
+ pub fn new(content: impl Into<String>) -> Self {
46
+ Self::new_with_defaults(content, true)
47
+ }
48
+
49
+ fn new_core(content: impl Into<String>) -> Self {
50
+ Self::new_with_defaults(content, false)
51
+ }
52
+
53
+ fn new_with_defaults(content: impl Into<String>, selectable_by_default: bool) -> Self {
54
+ let content = content.into();
55
+ let core = Rc::new(RefCell::new(NodeCore::new(NodeKind::Text)));
56
+ {
57
+ let mut core_mut = core.borrow_mut();
58
+ if selectable_by_default {
59
+ core_mut.behavior.cursor = Some(CursorStyle::Text);
60
+ core_mut.behavior.selectable_text = true;
61
+ }
62
+ core_mut.behavior.text_content = Some(content.clone());
63
+ }
64
+ let theme = theme::current_theme();
65
+ let node = Self {
66
+ core,
67
+ props: Rc::new(RefCell::new(TextProps {
68
+ content,
69
+ font_size: theme.fonts.size_body,
70
+ has_font: true,
71
+ selectable: selectable_by_default.then_some((true, theme.colors.selection)),
72
+ uses_theme_selection_color: selectable_by_default,
73
+ ..TextProps::default()
74
+ })),
75
+ };
76
+ node.bind_theme_defaults();
77
+ node
78
+ }
79
+
80
+ pub fn key(&self, _key: u64) -> &Self {
81
+ self
82
+ }
83
+
84
+ pub fn width(&self, width: f32, unit: Unit) -> &Self {
85
+ self.props.borrow_mut().width = Some((width, unit));
86
+ {
87
+ let mut core = self.core.borrow_mut();
88
+ core.behavior.fill_width = false;
89
+ core.behavior.fill_width_percent = None;
90
+ }
91
+ if self.has_built_handle() {
92
+ ui::set_width(self.handle().raw(), width, unit as u32);
93
+ self.notify_retained_layout_mutation();
94
+ }
95
+ self
96
+ }
97
+
98
+ pub fn height(&self, height: f32, unit: Unit) -> &Self {
99
+ self.props.borrow_mut().height = Some((height, unit));
100
+ {
101
+ let mut core = self.core.borrow_mut();
102
+ core.behavior.fill_height = false;
103
+ core.behavior.fill_height_percent = None;
104
+ }
105
+ if self.has_built_handle() {
106
+ ui::set_height(self.handle().raw(), height, unit as u32);
107
+ self.notify_retained_layout_mutation();
108
+ }
109
+ self
110
+ }
111
+
112
+ pub fn fill_width(&self) -> &Self {
113
+ self.props.borrow_mut().width = None;
114
+ {
115
+ let mut core = self.core.borrow_mut();
116
+ core.behavior.fill_width = true;
117
+ core.behavior.fill_width_percent = None;
118
+ }
119
+ if self.has_built_handle() {
120
+ ui::set_fill_width(self.handle().raw(), true);
121
+ self.notify_retained_layout_mutation();
122
+ }
123
+ self
124
+ }
125
+
126
+ pub fn fill_height(&self) -> &Self {
127
+ self.props.borrow_mut().height = None;
128
+ {
129
+ let mut core = self.core.borrow_mut();
130
+ core.behavior.fill_height = true;
131
+ core.behavior.fill_height_percent = None;
132
+ }
133
+ if self.has_built_handle() {
134
+ ui::set_fill_height(self.handle().raw(), true);
135
+ self.notify_retained_layout_mutation();
136
+ }
137
+ self
138
+ }
139
+
140
+ pub fn fill_size(&self) -> &Self {
141
+ self.fill_width();
142
+ self.fill_height();
143
+ self
144
+ }
145
+
146
+ pub fn text(&self, content: impl Into<String>) -> &Self {
147
+ let content = content.into();
148
+ self.props.borrow_mut().content = content.clone();
149
+ self.retained_node_ref()
150
+ .set_text_content_for_routing(Some(content.clone()));
151
+ if self.has_built_handle() {
152
+ ui::set_text(self.handle().raw(), &content);
153
+ self.notify_retained_layout_mutation();
154
+ }
155
+ self
156
+ }
157
+
158
+ pub fn text_color(&self, color: u32) -> &Self {
159
+ self.props.borrow_mut().text_color = Some(color);
160
+ if self.has_built_handle() {
161
+ ui::set_text_color(self.handle().raw(), color);
162
+ self.notify_retained_mutation();
163
+ }
164
+ self
165
+ }
166
+
167
+ pub fn style_runs(&self, words: Vec<u32>) -> &Self {
168
+ {
169
+ let mut props = self.props.borrow_mut();
170
+ props.style_runs = words;
171
+ props.has_style_runs = true;
172
+ }
173
+ if self.has_built_handle() {
174
+ let props = self.props.borrow();
175
+ ui::set_text_style_runs(self.handle().raw(), &props.style_runs);
176
+ self.notify_retained_layout_mutation();
177
+ }
178
+ self
179
+ }
180
+
181
+ pub(crate) fn font_id(&self, font_id: u32, size: f32) -> &Self {
182
+ let mut props = self.props.borrow_mut();
183
+ props.uses_direct_font_id = true;
184
+ props.font_family = None;
185
+ props.font_id = font_id;
186
+ props.font_size = size;
187
+ props.has_font = true;
188
+ drop(props);
189
+ if self.has_built_handle() {
190
+ self.apply_resolved_font();
191
+ }
192
+ self
193
+ }
194
+
195
+ pub fn font_stack(&self, stack: FontStack, size: f32) -> &Self {
196
+ self.font_id(stack.id(), size)
197
+ }
198
+
199
+ pub fn font_family(&self, family: FontFamily) -> &Self {
200
+ let mut props = self.props.borrow_mut();
201
+ props.font_family = Some(family);
202
+ props.uses_direct_font_id = false;
203
+ drop(props);
204
+ if self.has_built_handle() && self.props.borrow().has_font {
205
+ self.apply_resolved_font();
206
+ }
207
+ self
208
+ }
209
+
210
+ pub fn font_weight(&self, weight: FontWeight) -> &Self {
211
+ self.props.borrow_mut().font_weight = weight;
212
+ if self.has_built_handle() && self.props.borrow().has_font {
213
+ self.apply_resolved_font();
214
+ }
215
+ self
216
+ }
217
+
218
+ pub fn font_style(&self, style: FontStyle) -> &Self {
219
+ self.props.borrow_mut().font_style = style;
220
+ if self.has_built_handle() && self.props.borrow().has_font {
221
+ self.apply_resolved_font();
222
+ }
223
+ self
224
+ }
225
+
226
+ pub fn font_size(&self, size: f32) -> &Self {
227
+ let mut props = self.props.borrow_mut();
228
+ props.font_size = size;
229
+ props.has_font = true;
230
+ drop(props);
231
+ if self.has_built_handle() {
232
+ self.apply_resolved_font();
233
+ }
234
+ self
235
+ }
236
+
237
+ pub fn line_height(&self, line_height: f32) -> &Self {
238
+ self.props.borrow_mut().line_height = Some(line_height);
239
+ if self.has_built_handle() {
240
+ ui::set_line_height(self.handle().raw(), line_height);
241
+ self.notify_retained_layout_mutation();
242
+ }
243
+ self
244
+ }
245
+
246
+ pub fn text_align(&self, align: TextAlign) -> &Self {
247
+ self.props.borrow_mut().text_align = Some(align);
248
+ self
249
+ }
250
+
251
+ pub fn text_vertical_align(&self, align: TextVerticalAlign) -> &Self {
252
+ self.props.borrow_mut().text_vertical_align = Some(align);
253
+ self
254
+ }
255
+
256
+ pub fn text_limits(&self, max_chars: i32, max_lines: i32) -> &Self {
257
+ self.props.borrow_mut().text_limits = Some((max_chars, max_lines));
258
+ if self.has_built_handle() {
259
+ ui::set_text_limits(self.handle().raw(), max_chars, max_lines);
260
+ self.notify_retained_layout_mutation();
261
+ }
262
+ self
263
+ }
264
+
265
+ pub fn wrapping(&self, wrap: bool) -> &Self {
266
+ self.props.borrow_mut().wrapping = Some(wrap);
267
+ if self.has_built_handle() {
268
+ ui::set_text_wrapping(self.handle().raw(), wrap);
269
+ self.notify_retained_layout_mutation();
270
+ }
271
+ self
272
+ }
273
+
274
+ pub fn text_overflow(&self, overflow: TextOverflow) -> &Self {
275
+ self.props.borrow_mut().overflow = Some(overflow);
276
+ self
277
+ }
278
+
279
+ pub fn text_overflow_fade(&self, horizontal: bool, vertical: bool) -> &Self {
280
+ self.props.borrow_mut().overflow_fade = Some((horizontal, vertical));
281
+ self
282
+ }
283
+
284
+ pub fn selectable(&self, selectable: bool, selection_color: u32) -> &Self {
285
+ let resolved_selection_color = if selection_color == 0 {
286
+ theme::current_theme().colors.selection
287
+ } else {
288
+ selection_color
289
+ };
290
+ let mut props = self.props.borrow_mut();
291
+ props.selectable = Some((selectable, resolved_selection_color));
292
+ props.uses_theme_selection_color = selection_color == 0;
293
+ drop(props);
294
+ self.core.borrow_mut().behavior.selectable_text = selectable;
295
+ if self.has_built_handle() {
296
+ ui::set_selectable(self.handle().raw(), selectable, resolved_selection_color);
297
+ self.notify_retained_mutation();
298
+ }
299
+ self
300
+ }
301
+
302
+ pub fn editable(&self, editable: bool) -> &Self {
303
+ if editable {
304
+ let selection_color = self
305
+ .props
306
+ .borrow()
307
+ .selectable
308
+ .map(|(_, color)| color)
309
+ .unwrap_or(theme::current_theme().colors.selection);
310
+ self.props.borrow_mut().selectable = Some((true, selection_color));
311
+ self.core.borrow_mut().behavior.selectable_text = true;
312
+ }
313
+ self.props.borrow_mut().editable = Some(editable);
314
+ self.core.borrow_mut().behavior.editable_text = editable;
315
+ if self.has_built_handle() {
316
+ ui::set_editable(self.handle().raw(), editable);
317
+ self.notify_retained_mutation();
318
+ }
319
+ self
320
+ }
321
+
322
+ pub(crate) fn editor_command_keys(&self, enabled: bool) -> &Self {
323
+ self.props.borrow_mut().editor_command_keys = Some(enabled);
324
+ if self.has_built_handle() {
325
+ ui::set_editor_command_keys(self.handle().raw(), enabled);
326
+ self.notify_retained_mutation();
327
+ }
328
+ self
329
+ }
330
+
331
+ pub(crate) fn editor_accepts_tab(&self, enabled: bool) -> &Self {
332
+ self.props.borrow_mut().editor_accepts_tab = Some(enabled);
333
+ if self.has_built_handle() {
334
+ ui::set_editor_accepts_tab(self.handle().raw(), enabled);
335
+ self.notify_retained_mutation();
336
+ }
337
+ self
338
+ }
339
+
340
+ pub fn obscured(&self, obscured: bool) -> &Self {
341
+ self.props.borrow_mut().obscured = Some(obscured);
342
+ if self.has_built_handle() {
343
+ ui::set_text_obscured(self.handle().raw(), obscured);
344
+ self.notify_retained_mutation();
345
+ }
346
+ self
347
+ }
348
+
349
+ pub fn caret_color(&self, color: u32) -> &Self {
350
+ self.props.borrow_mut().caret_color = Some(color);
351
+ if self.has_built_handle() {
352
+ ui::set_caret_color(self.handle().raw(), color);
353
+ self.notify_retained_mutation();
354
+ }
355
+ self
356
+ }
357
+
358
+ pub fn selection_range(&self, start: u32, end: u32) -> &Self {
359
+ if self.has_built_handle() {
360
+ ui::set_text_selection_range(self.handle().raw(), start, end);
361
+ self.notify_retained_mutation();
362
+ }
363
+ self
364
+ }
365
+
366
+ pub fn focus_now(&self) -> &Self {
367
+ if self.has_built_handle() {
368
+ ui::request_focus(self.handle().raw());
369
+ }
370
+ self
371
+ }
372
+
373
+ pub(crate) fn default_semantic_label(&self, label: impl Into<String>) -> &Self {
374
+ let label = label.into();
375
+ let mut core = self.core.borrow_mut();
376
+ core.behavior.default_semantic_label = Some(label.clone());
377
+ if core.behavior.semantic_label.is_none() && core.handle != NodeHandle::INVALID {
378
+ ui::set_semantic_label(core.handle.raw(), &label);
379
+ drop(core);
380
+ self.notify_retained_mutation();
381
+ }
382
+ self
383
+ }
384
+
385
+ pub fn interactive(&self, interactive: bool) -> &Self {
386
+ self.core.borrow_mut().behavior.interactive = interactive;
387
+ self
388
+ }
389
+
390
+ pub fn cursor(&self, style: CursorStyle) -> &Self {
391
+ self.core.borrow_mut().behavior.cursor = Some(style);
392
+ crate::event::handle_cursor_style_changed(self.handle());
393
+ self
394
+ }
395
+
396
+ pub fn focusable(&self, enabled: bool, tab_index: i32) -> &Self {
397
+ let mut core = self.core.borrow_mut();
398
+ core.behavior.focusable = Some((enabled, tab_index));
399
+ let interactive = core.behavior.enabled && core.behavior.inherited_enabled;
400
+ let handle = core.handle;
401
+ drop(core);
402
+ if handle != NodeHandle::INVALID {
403
+ ui::set_focusable(handle.raw(), interactive && enabled, tab_index);
404
+ self.notify_retained_mutation();
405
+ }
406
+ self
407
+ }
408
+
409
+ pub(crate) fn reflect_semantic_disabled_from_enabled(&self) -> &Self {
410
+ let mut core = self.core.borrow_mut();
411
+ core.behavior.track_semantic_disabled_from_enabled = true;
412
+ let effective_enabled = core.behavior.enabled && core.behavior.inherited_enabled;
413
+ let handle = core.handle;
414
+ drop(core);
415
+ if handle != NodeHandle::INVALID {
416
+ ui::set_semantic_disabled(handle.raw(), true, !effective_enabled);
417
+ self.notify_retained_mutation();
418
+ }
419
+ self
420
+ }
421
+
422
+ pub fn on_click(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self {
423
+ self.core.borrow_mut().handlers.pointer_click = Some(Rc::new(handler));
424
+ self.retained_node_ref().require_interactive();
425
+ self
426
+ }
427
+
428
+ pub fn on_pointer_down(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self {
429
+ self.core.borrow_mut().handlers.pointer_down = Some(Rc::new(handler));
430
+ self.retained_node_ref().require_interactive();
431
+ self
432
+ }
433
+
434
+ pub fn on_pointer_up(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self {
435
+ self.core.borrow_mut().handlers.pointer_up = Some(Rc::new(handler));
436
+ self.retained_node_ref().require_interactive();
437
+ self
438
+ }
439
+
440
+ pub fn on_focus_changed(&self, handler: impl Fn(FocusChangedEventArgs) + 'static) -> &Self {
441
+ self.core.borrow_mut().handlers.focus_changed = Some(Rc::new(handler));
442
+ self
443
+ }
444
+
445
+ pub fn on_text_changed(&self, handler: impl Fn(TextChangedEventArgs) + 'static) -> &Self {
446
+ let node = self.clone();
447
+ self.core.borrow_mut().handlers.text_changed = Some(Rc::new(move |event| {
448
+ node.sync_text_from_runtime(event.text.clone());
449
+ handler(event);
450
+ }));
451
+ self
452
+ }
453
+
454
+ pub(crate) fn on_text_replaced(&self, handler: impl Fn(u32, u32, String) + 'static) -> &Self {
455
+ let node = self.clone();
456
+ self.core.borrow_mut().handlers.text_replaced = Some(Rc::new(move |start, end, text| {
457
+ node.apply_text_replacement(start, end, &text);
458
+ handler(start, end, text);
459
+ }));
460
+ self
461
+ }
462
+
463
+ pub fn on_selection_changed(
464
+ &self,
465
+ handler: impl Fn(SelectionChangedEventArgs) + 'static,
466
+ ) -> &Self {
467
+ self.core.borrow_mut().handlers.selection_changed = Some(Rc::new(handler));
468
+ self
469
+ }
470
+
471
+ pub fn text_value(&self) -> String {
472
+ self.props.borrow().content.clone()
473
+ }
474
+
475
+ pub fn on_pan_gesture(&self, handler: impl Fn(&mut GestureEventArgs) + 'static) -> &Self {
476
+ self.core.borrow_mut().handlers.pan_gesture = Some(Rc::new(handler));
477
+ self
478
+ }
479
+
480
+ pub fn on_pinch_gesture(&self, handler: impl Fn(&mut GestureEventArgs) + 'static) -> &Self {
481
+ self.core.borrow_mut().handlers.pinch_gesture = Some(Rc::new(handler));
482
+ self
483
+ }
484
+
485
+ pub fn long_press_options(&self, minimum_duration_ms: i32, movement_tolerance: f32) -> &Self {
486
+ let mut core = self.core.borrow_mut();
487
+ core.handlers.long_press_minimum_duration_ms = minimum_duration_ms.max(0);
488
+ core.handlers.long_press_movement_tolerance = movement_tolerance.max(0.0);
489
+ self
490
+ }
491
+
492
+ pub fn on_long_press(&self, handler: impl Fn(&mut LongPressEventArgs) + 'static) -> &Self {
493
+ self.core.borrow_mut().handlers.long_press = Some(Rc::new(handler));
494
+ self
495
+ }
496
+
497
+ pub(crate) fn required_font_ids(&self) -> Vec<u32> {
498
+ let props = self.props.borrow();
499
+ let mut font_ids = Vec::new();
500
+ if props.has_font && props.font_id != 0 {
501
+ font_ids.push(props.font_id);
502
+ }
503
+ for chunk in props.style_runs.chunks_exact(7) {
504
+ let font_id = chunk[2];
505
+ if !font_ids.contains(&font_id) {
506
+ font_ids.push(font_id);
507
+ }
508
+ }
509
+ font_ids
510
+ }
511
+
512
+ fn bind_theme_defaults(&self) {
513
+ let weak_core = Rc::downgrade(&self.core);
514
+ let weak_props = Rc::downgrade(&self.props);
515
+ let guard = theme::subscribe(move |theme| {
516
+ let Some(core) = weak_core.upgrade() else {
517
+ return;
518
+ };
519
+ let Some(props) = weak_props.upgrade() else {
520
+ return;
521
+ };
522
+ TextNode::handle_theme_changed(&core, &props, &theme);
523
+ });
524
+ self.retained_node_ref().retain_attachment(Rc::new(guard));
525
+ }
526
+
527
+ fn handle_theme_changed(
528
+ core: &Rc<RefCell<NodeCore>>,
529
+ props: &Rc<RefCell<TextProps>>,
530
+ theme: &crate::theme::Theme,
531
+ ) {
532
+ let handle = core.borrow().handle;
533
+ if handle == NodeHandle::INVALID {
534
+ if props.borrow().has_font
535
+ && !props.borrow().uses_direct_font_id
536
+ && props.borrow().font_family.is_none()
537
+ {
538
+ let resolved_font_id = theme
539
+ .fonts
540
+ .body_family
541
+ .resolve(props.borrow().font_weight, props.borrow().font_style);
542
+ props.borrow_mut().font_id = resolved_font_id;
543
+ }
544
+ return;
545
+ }
546
+
547
+ let mut should_request_render = false;
548
+ {
549
+ let mut text_props = props.borrow_mut();
550
+ if text_props.text_color.is_none() {
551
+ ui::set_text_color(handle.raw(), theme.colors.text_primary);
552
+ should_request_render = true;
553
+ }
554
+ if text_props.has_font
555
+ && !text_props.uses_direct_font_id
556
+ && text_props.font_family.is_none()
557
+ {
558
+ text_props.font_id = theme
559
+ .fonts
560
+ .body_family
561
+ .resolve(text_props.font_weight, text_props.font_style);
562
+ ui::set_font(handle.raw(), text_props.font_id, text_props.font_size);
563
+ should_request_render = true;
564
+ }
565
+ if text_props.uses_theme_selection_color {
566
+ if let Some((selectable, _)) = text_props.selectable {
567
+ text_props.selectable = Some((selectable, theme.colors.selection));
568
+ ui::set_selectable(handle.raw(), selectable, theme.colors.selection);
569
+ should_request_render = true;
570
+ }
571
+ }
572
+ }
573
+ if should_request_render {
574
+ crate::frame_scheduler::mark_needs_commit();
575
+ }
576
+ }
577
+
578
+ fn resolve_font_id(&self) -> u32 {
579
+ let mut props = self.props.borrow_mut();
580
+ if props.uses_direct_font_id {
581
+ return props.font_id;
582
+ }
583
+ let family = props
584
+ .font_family
585
+ .clone()
586
+ .unwrap_or_else(|| theme::current_theme().fonts.body_family.clone());
587
+ props.font_id = family.resolve(props.font_weight, props.font_style);
588
+ props.font_id
589
+ }
590
+
591
+ fn apply_resolved_font(&self) {
592
+ let font_id = self.resolve_font_id();
593
+ let font_size = self.props.borrow().font_size;
594
+ ui::set_font(self.handle().raw(), font_id, font_size);
595
+ self.notify_retained_layout_mutation();
596
+ }
597
+
598
+ fn sync_text_from_runtime(&self, content: String) {
599
+ self.props.borrow_mut().content = content.clone();
600
+ self.retained_node_ref()
601
+ .set_text_content_for_routing(Some(content));
602
+ }
603
+
604
+ fn apply_text_replacement(&self, start: u32, end: u32, replacement: &str) {
605
+ let mut props = self.props.borrow_mut();
606
+ let start = start.min(props.content.len() as u32) as usize;
607
+ let end = end.min(props.content.len() as u32) as usize;
608
+ if start <= end
609
+ && props.content.is_char_boundary(start)
610
+ && props.content.is_char_boundary(end)
611
+ {
612
+ props.content.replace_range(start..end, replacement);
613
+ }
614
+ let content = props.content.clone();
615
+ drop(props);
616
+ self.retained_node_ref()
617
+ .set_text_content_for_routing(Some(content));
618
+ }
619
+ }
620
+
621
+ impl Node for TextNode {
622
+ fn retained_node_ref(&self) -> NodeRef {
623
+ NodeRef::from_node(self.core.clone(), self.clone())
624
+ }
625
+
626
+ fn build_self(&self) {
627
+ apply_text_props(
628
+ self.handle(),
629
+ &self.props.borrow(),
630
+ self.core.borrow().behavior.clone(),
631
+ );
632
+ }
633
+ }