@effindomv2/fui-rs 0.1.12 → 0.1.14

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.
@@ -1,4 +1,6 @@
1
- use super::internal::pressable_labeled_control::PressableLabeledControlState;
1
+ use super::internal::pressable_labeled_control::{
2
+ PressableLabeledControlState, WeakPressableLabeledControl,
3
+ };
2
4
  use super::internal::radio_indicator_presenter::{
3
5
  create_default_radio_indicator_presenter, RadioIndicatorPresenter, RadioIndicatorTemplate,
4
6
  RadioIndicatorVisualState,
@@ -250,9 +252,24 @@ impl HasFlexBoxRoot for RadioButton {
250
252
  }
251
253
  }
252
254
 
255
+ impl ThemeBindable for RadioButton {
256
+ fn theme_binding_node(&self) -> NodeRef {
257
+ self.root.retained_node_ref()
258
+ }
259
+
260
+ fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
261
+ let target = RadioEventTarget::from_radio(self);
262
+ Box::new(move || target.upgrade())
263
+ }
264
+ }
265
+
253
266
  #[derive(Clone)]
254
267
  struct RadioEventTarget {
268
+ base: WeakPressableLabeledControl,
255
269
  presenter: Rc<RefCell<Rc<dyn RadioIndicatorPresenter>>>,
270
+ template_override: Rc<RefCell<Option<Rc<dyn RadioIndicatorTemplate>>>>,
271
+ sizing_value: Rc<Cell<Option<LabeledControlSizing>>>,
272
+ value_text: String,
256
273
  checked: Rc<Cell<bool>>,
257
274
  colors_value: Rc<Cell<Option<LabeledControlColors>>>,
258
275
  changed: Rc<RefCell<Option<RadioButtonChangedCallback>>>,
@@ -263,7 +280,11 @@ struct RadioEventTarget {
263
280
  impl RadioEventTarget {
264
281
  fn from_radio(radio: &RadioButton) -> Self {
265
282
  Self {
283
+ base: radio.base.downgrade(),
266
284
  presenter: radio.indicator_presenter.clone(),
285
+ template_override: radio.template_override.clone(),
286
+ sizing_value: radio.sizing_value.clone(),
287
+ value_text: radio.value_text.clone(),
267
288
  checked: radio.checked.clone(),
268
289
  colors_value: radio.colors_value.clone(),
269
290
  changed: radio.changed.clone(),
@@ -272,6 +293,23 @@ impl RadioEventTarget {
272
293
  }
273
294
  }
274
295
 
296
+ fn upgrade(&self) -> Option<RadioButton> {
297
+ let base = self.base.upgrade()?;
298
+ Some(RadioButton {
299
+ root: base.root(),
300
+ base,
301
+ indicator_presenter: self.presenter.clone(),
302
+ template_override: self.template_override.clone(),
303
+ sizing_value: self.sizing_value.clone(),
304
+ colors_value: self.colors_value.clone(),
305
+ value_text: self.value_text.clone(),
306
+ checked: self.checked.clone(),
307
+ changed: self.changed.clone(),
308
+ owner_group: self.owner_group.clone(),
309
+ weak_root: self.weak_root.clone(),
310
+ })
311
+ }
312
+
275
313
  fn activate(&self, base_state: PressableLabeledControlState) {
276
314
  if let Some(group) = self.owner_group() {
277
315
  if let Some(handle) = upgraded_handle(&self.weak_root) {
@@ -80,3 +80,20 @@ impl HasFlexBoxRoot for SelectionArea {
80
80
  &self.root
81
81
  }
82
82
  }
83
+
84
+ impl ThemeBindable for SelectionArea {
85
+ fn theme_binding_node(&self) -> NodeRef {
86
+ self.root.retained_node_ref()
87
+ }
88
+
89
+ fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
90
+ let weak_root = self.root.downgrade();
91
+ let signal = self.selected_text_signal.clone();
92
+ Box::new(move || {
93
+ Some(SelectionArea {
94
+ root: weak_root.upgrade()?,
95
+ selected_text_signal: signal.clone(),
96
+ })
97
+ })
98
+ }
99
+ }
@@ -415,11 +415,24 @@ impl HasFlexBoxRoot for Slider {
415
415
  }
416
416
  }
417
417
 
418
+ impl ThemeBindable for Slider {
419
+ fn theme_binding_node(&self) -> NodeRef {
420
+ self.root.retained_node_ref()
421
+ }
422
+
423
+ fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
424
+ let target = SliderEventTarget::from_slider(self);
425
+ Box::new(move || target.upgrade())
426
+ }
427
+ }
428
+
418
429
  #[derive(Clone)]
419
430
  struct SliderEventTarget {
420
431
  weak_root: Rc<WeakNodeRef>,
421
432
  weak_flex_root: WeakFlexBox,
422
433
  slider_presenter: Rc<RefCell<Rc<dyn SliderPresenter>>>,
434
+ template_override: Rc<RefCell<Option<Rc<dyn SliderTemplate>>>>,
435
+ sizing_value: Rc<Cell<Option<SliderSizing>>>,
423
436
  min: Rc<Cell<f32>>,
424
437
  max: Rc<Cell<f32>>,
425
438
  step: Rc<Cell<f32>>,
@@ -439,6 +452,8 @@ impl SliderEventTarget {
439
452
  weak_root: slider.weak_root.clone(),
440
453
  weak_flex_root: slider.weak_flex_root.clone(),
441
454
  slider_presenter: slider.slider_presenter.clone(),
455
+ template_override: slider.template_override.clone(),
456
+ sizing_value: slider.sizing_value.clone(),
442
457
  min: slider.min.clone(),
443
458
  max: slider.max.clone(),
444
459
  step: slider.step.clone(),
@@ -453,6 +468,28 @@ impl SliderEventTarget {
453
468
  }
454
469
  }
455
470
 
471
+ fn upgrade(&self) -> Option<Slider> {
472
+ Some(Slider {
473
+ root: self.weak_flex_root.upgrade()?,
474
+ slider_presenter: self.slider_presenter.clone(),
475
+ template_override: self.template_override.clone(),
476
+ sizing_value: self.sizing_value.clone(),
477
+ colors_value: self.colors_value.clone(),
478
+ min: self.min.clone(),
479
+ max: self.max.clone(),
480
+ step: self.step.clone(),
481
+ value: self.value.clone(),
482
+ length: self.length.clone(),
483
+ orientation: self.orientation.clone(),
484
+ changed: self.changed.clone(),
485
+ hovered_state: self.hovered_state.clone(),
486
+ dragging_state: self.dragging_state.clone(),
487
+ focused_state: self.focused_state.clone(),
488
+ weak_root: self.weak_root.clone(),
489
+ weak_flex_root: self.weak_flex_root.clone(),
490
+ })
491
+ }
492
+
456
493
  fn is_enabled(&self) -> bool {
457
494
  self.weak_flex_root
458
495
  .upgrade()
@@ -1,4 +1,6 @@
1
- use super::internal::pressable_labeled_control::PressableLabeledControlState;
1
+ use super::internal::pressable_labeled_control::{
2
+ PressableLabeledControlState, WeakPressableLabeledControl,
3
+ };
2
4
  use super::internal::switch_indicator_presenter::{
3
5
  create_default_switch_indicator_presenter, SwitchIndicatorPresenter, SwitchIndicatorTemplate,
4
6
  SwitchIndicatorVisualState,
@@ -241,9 +243,23 @@ impl HasFlexBoxRoot for Switch {
241
243
  }
242
244
  }
243
245
 
246
+ impl ThemeBindable for Switch {
247
+ fn theme_binding_node(&self) -> NodeRef {
248
+ self.root.retained_node_ref()
249
+ }
250
+
251
+ fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
252
+ let target = SwitchEventTarget::from_switch(self);
253
+ Box::new(move || target.upgrade())
254
+ }
255
+ }
256
+
244
257
  #[derive(Clone)]
245
258
  struct SwitchEventTarget {
259
+ base: WeakPressableLabeledControl,
246
260
  presenter: Rc<RefCell<Rc<dyn SwitchIndicatorPresenter>>>,
261
+ template_override: Rc<RefCell<Option<Rc<dyn SwitchIndicatorTemplate>>>>,
262
+ sizing_value: Rc<Cell<Option<LabeledControlSizing>>>,
247
263
  checked: Rc<Cell<bool>>,
248
264
  colors_value: Rc<Cell<Option<LabeledControlColors>>>,
249
265
  changed: Rc<RefCell<Option<SwitchChangedCallback>>>,
@@ -253,7 +269,10 @@ struct SwitchEventTarget {
253
269
  impl SwitchEventTarget {
254
270
  fn from_switch(switch: &Switch) -> Self {
255
271
  Self {
272
+ base: switch.base.downgrade(),
256
273
  presenter: switch.indicator_presenter.clone(),
274
+ template_override: switch.template_override.clone(),
275
+ sizing_value: switch.sizing_value.clone(),
257
276
  checked: switch.checked.clone(),
258
277
  colors_value: switch.colors_value.clone(),
259
278
  changed: switch.changed.clone(),
@@ -261,6 +280,21 @@ impl SwitchEventTarget {
261
280
  }
262
281
  }
263
282
 
283
+ fn upgrade(&self) -> Option<Switch> {
284
+ let base = self.base.upgrade()?;
285
+ Some(Switch {
286
+ root: base.root(),
287
+ base,
288
+ indicator_presenter: self.presenter.clone(),
289
+ template_override: self.template_override.clone(),
290
+ sizing_value: self.sizing_value.clone(),
291
+ colors_value: self.colors_value.clone(),
292
+ checked: self.checked.clone(),
293
+ changed: self.changed.clone(),
294
+ weak_root: self.weak_root.clone(),
295
+ })
296
+ }
297
+
264
298
  fn activate(&self, base_state: PressableLabeledControlState) {
265
299
  apply_switch_checked(
266
300
  &self.presenter,
@@ -1185,6 +1185,149 @@ fn nav_link_bind_theme_preserves_concrete_control_dx_and_retained_lifetime() {
1185
1185
  use_custom_theme(previous_theme);
1186
1186
  }
1187
1187
 
1188
+ #[test]
1189
+ fn button_bind_theme_preserves_concrete_control_dx_and_retained_lifetime() {
1190
+ ffi::test::reset();
1191
+ let previous_theme = current_theme();
1192
+ let parent = column();
1193
+ let control = button("Themed action");
1194
+ control.bind_theme(|button, theme| {
1195
+ button.colors(
1196
+ ButtonColors::new()
1197
+ .background(theme.colors.accent)
1198
+ .text_primary(theme.colors.text_on_accent),
1199
+ );
1200
+ });
1201
+ parent.child(&control);
1202
+ Application::mount(parent);
1203
+ let handle = control.handle().raw();
1204
+ drop(control);
1205
+ ffi::test::take_calls();
1206
+
1207
+ let changed = generate_theme(false, 0x2468ACFF);
1208
+ use_custom_theme(changed.clone());
1209
+ let calls = ffi::test::take_calls();
1210
+ assert!(calls.iter().any(|call| matches!(
1211
+ call,
1212
+ Call::SetBgColor { handle: actual, color }
1213
+ if *actual == handle && *color == changed.colors.accent
1214
+ )));
1215
+
1216
+ use_custom_theme(previous_theme);
1217
+ }
1218
+
1219
+ #[test]
1220
+ fn flex_box_backed_controls_preserve_concrete_bind_theme_dx() {
1221
+ ffi::test::reset();
1222
+ let invocations = Rc::new(Cell::new(0));
1223
+ let count = |invocations: &Rc<Cell<i32>>| {
1224
+ let invocations = invocations.clone();
1225
+ move || invocations.set(invocations.get() + 1)
1226
+ };
1227
+
1228
+ let checkbox = checkbox("Checkbox");
1229
+ checkbox.bind_theme({
1230
+ let count = count(&invocations);
1231
+ move |control, theme| {
1232
+ count();
1233
+ control.colors(LabeledControlColors::new().text_primary(theme.colors.text_primary));
1234
+ }
1235
+ });
1236
+ let switch = switch("Switch");
1237
+ switch.bind_theme({
1238
+ let count = count(&invocations);
1239
+ move |control, theme| {
1240
+ count();
1241
+ control.colors(LabeledControlColors::new().text_primary(theme.colors.text_primary));
1242
+ }
1243
+ });
1244
+ let radio = radio_button("Radio");
1245
+ radio.bind_theme({
1246
+ let count = count(&invocations);
1247
+ move |control, theme| {
1248
+ count();
1249
+ control.colors(LabeledControlColors::new().text_primary(theme.colors.text_primary));
1250
+ }
1251
+ });
1252
+ let slider = slider();
1253
+ slider.bind_theme({
1254
+ let count = count(&invocations);
1255
+ move |control, theme| {
1256
+ count();
1257
+ control.colors(SliderColors::new().fill(theme.colors.accent));
1258
+ }
1259
+ });
1260
+ let progress = progress_bar();
1261
+ progress.bind_theme({
1262
+ let count = count(&invocations);
1263
+ move |control, theme| {
1264
+ count();
1265
+ control.colors(ProgressBarColors::new().fill(theme.colors.accent));
1266
+ }
1267
+ });
1268
+ let dropdown = dropdown();
1269
+ dropdown.bind_theme({
1270
+ let count = count(&invocations);
1271
+ move |control, theme| {
1272
+ count();
1273
+ control.colors(DropdownColors::new().text_primary(theme.colors.text_primary));
1274
+ }
1275
+ });
1276
+ let combo = combo_box();
1277
+ combo.bind_theme({
1278
+ let count = count(&invocations);
1279
+ move |control, theme| {
1280
+ count();
1281
+ control.colors(DropdownColors::new().text_primary(theme.colors.text_primary));
1282
+ }
1283
+ });
1284
+ let input = text_input();
1285
+ input.bind_theme({
1286
+ let count = count(&invocations);
1287
+ move |control, theme| {
1288
+ count();
1289
+ control.colors(TextInputColors::new().text_primary(theme.colors.text_primary));
1290
+ }
1291
+ });
1292
+ let area = text_area();
1293
+ area.bind_theme({
1294
+ let count = count(&invocations);
1295
+ move |control, theme| {
1296
+ count();
1297
+ control.colors(TextInputColors::new().text_primary(theme.colors.text_primary));
1298
+ }
1299
+ });
1300
+ let selection = selection_area();
1301
+ selection.bind_theme({
1302
+ let count = count(&invocations);
1303
+ move |control, _theme| {
1304
+ count();
1305
+ let _ = control.selected_text();
1306
+ }
1307
+ });
1308
+ let barrier = anti_selection_area();
1309
+ barrier.bind_theme({
1310
+ let count = count(&invocations);
1311
+ move |control, _theme| {
1312
+ count();
1313
+ control.children(Vec::<Child>::new());
1314
+ }
1315
+ });
1316
+
1317
+ let scroll = crate::scroll_box();
1318
+ scroll.bind_theme({
1319
+ let count = count(&invocations);
1320
+ move |_control: &crate::ScrollBox, _theme| count()
1321
+ });
1322
+ let list = crate::virtual_list(10, 32.0);
1323
+ list.bind_theme({
1324
+ let count = count(&invocations);
1325
+ move |_control: &crate::VirtualList, _theme| count()
1326
+ });
1327
+
1328
+ assert_eq!(invocations.get(), 13);
1329
+ }
1330
+
1188
1331
  #[test]
1189
1332
  fn retained_pressable_and_slider_keep_theme_subscriptions_after_wrappers_drop() {
1190
1333
  ffi::test::reset();
@@ -3214,6 +3357,100 @@ fn progress_bar_invalid_sizing_warns_and_falls_back_to_defaults() {
3214
3357
  )));
3215
3358
  }
3216
3359
 
3360
+ #[test]
3361
+ fn progress_bar_direct_length_and_thickness_match_fui_as() {
3362
+ ffi::test::reset();
3363
+ let progress = progress_bar();
3364
+ progress.length(320.0).thickness(18.0);
3365
+ Application::mount(progress);
3366
+ let calls = ffi::test::take_calls();
3367
+ assert!(calls.iter().any(|call| matches!(
3368
+ call,
3369
+ Call::SetWidth { value, unit_enum, .. }
3370
+ if *value == 320.0 && *unit_enum == Unit::Pixel as u32
3371
+ )));
3372
+ assert!(calls.iter().any(|call| matches!(
3373
+ call,
3374
+ Call::SetHeight { value, unit_enum, .. }
3375
+ if *value == 18.0 && *unit_enum == Unit::Pixel as u32
3376
+ )));
3377
+
3378
+ ffi::test::reset();
3379
+ let progress = progress_bar();
3380
+ progress.length(0.0).thickness(-2.0);
3381
+ let calls = ffi::test::take_calls();
3382
+ assert!(calls.iter().any(|call| matches!(
3383
+ call,
3384
+ Call::Log { category, message }
3385
+ if category == "Warning/Layout"
3386
+ && message == "ProgressBar.length() received 0; clamping to 1.0."
3387
+ )));
3388
+ assert!(calls.iter().any(|call| matches!(
3389
+ call,
3390
+ Call::Log { category, message }
3391
+ if category == "Warning/Layout"
3392
+ && message == "ProgressBar.thickness() received -2; clamping to 1.0."
3393
+ )));
3394
+ }
3395
+
3396
+ #[test]
3397
+ fn progress_bar_maps_length_and_thickness_to_orientation() {
3398
+ ffi::test::reset();
3399
+ let progress = progress_bar();
3400
+ progress
3401
+ .value(25.0)
3402
+ .length(300.0)
3403
+ .thickness(18.0)
3404
+ .orientation(Orientation::Vertical);
3405
+ Application::mount(progress.clone());
3406
+ let calls = ffi::test::take_calls();
3407
+ let root_handle = progress.retained_node_ref().handle().raw();
3408
+ let fill_handle = child_handles_for_parent(&calls, root_handle)
3409
+ .into_iter()
3410
+ .next()
3411
+ .expect("fill handle");
3412
+
3413
+ assert!(calls.iter().any(|call| matches!(
3414
+ call,
3415
+ Call::SetWidth { handle, value, unit_enum }
3416
+ if *handle == root_handle && *value == 18.0 && *unit_enum == Unit::Pixel as u32
3417
+ )));
3418
+ assert!(calls.iter().any(|call| matches!(
3419
+ call,
3420
+ Call::SetHeight { handle, value, unit_enum }
3421
+ if *handle == root_handle && *value == 300.0 && *unit_enum == Unit::Pixel as u32
3422
+ )));
3423
+ assert!(calls.iter().any(|call| matches!(
3424
+ call,
3425
+ Call::SetWidth { handle, value, unit_enum }
3426
+ if *handle == fill_handle && *value == 18.0 && *unit_enum == Unit::Pixel as u32
3427
+ )));
3428
+ assert!(calls.iter().any(|call| matches!(
3429
+ call,
3430
+ Call::SetHeight { handle, value, unit_enum }
3431
+ if *handle == fill_handle && *value == 75.0 && *unit_enum == Unit::Pixel as u32
3432
+ )));
3433
+ assert!(calls.iter().any(|call| matches!(
3434
+ call,
3435
+ Call::SetSemanticOrientation { handle, orientation_enum }
3436
+ if *handle == root_handle && *orientation_enum == Orientation::Vertical as u32
3437
+ )));
3438
+
3439
+ ffi::test::reset();
3440
+ progress.orientation(Orientation::Horizontal);
3441
+ let calls = ffi::test::take_calls();
3442
+ assert!(calls.iter().any(|call| matches!(
3443
+ call,
3444
+ Call::SetWidth { handle, value, unit_enum }
3445
+ if *handle == root_handle && *value == 300.0 && *unit_enum == Unit::Pixel as u32
3446
+ )));
3447
+ assert!(calls.iter().any(|call| matches!(
3448
+ call,
3449
+ Call::SetHeight { handle, value, unit_enum }
3450
+ if *handle == root_handle && *value == 18.0 && *unit_enum == Unit::Pixel as u32
3451
+ )));
3452
+ }
3453
+
3217
3454
  #[test]
3218
3455
  fn progress_bar_theme_changes_update_default_colors() {
3219
3456
  ffi::test::reset();
@@ -198,6 +198,21 @@ impl HasFlexBoxRoot for TextArea {
198
198
  }
199
199
  }
200
200
 
201
+ impl crate::ThemeBindable for TextArea {
202
+ fn theme_binding_node(&self) -> NodeRef {
203
+ self.core.flex_box_root().retained_node_ref()
204
+ }
205
+
206
+ fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
207
+ let weak_core = Rc::downgrade(&self.core);
208
+ Box::new(move || {
209
+ Some(TextArea {
210
+ core: weak_core.upgrade()?,
211
+ })
212
+ })
213
+ }
214
+ }
215
+
201
216
  impl Node for TextArea {
202
217
  fn retained_node_ref(&self) -> NodeRef {
203
218
  let core = self.core.clone();
@@ -189,6 +189,21 @@ impl HasFlexBoxRoot for TextInput {
189
189
  }
190
190
  }
191
191
 
192
+ impl crate::ThemeBindable for TextInput {
193
+ fn theme_binding_node(&self) -> NodeRef {
194
+ self.core.flex_box_root().retained_node_ref()
195
+ }
196
+
197
+ fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
198
+ let weak_core = Rc::downgrade(&self.core);
199
+ Box::new(move || {
200
+ Some(TextInput {
201
+ core: weak_core.upgrade()?,
202
+ })
203
+ })
204
+ }
205
+ }
206
+
192
207
  impl Node for TextInput {
193
208
  fn retained_node_ref(&self) -> NodeRef {
194
209
  let core = self.core.clone();
@@ -19,56 +19,56 @@ unsafe extern "C" {
19
19
  }
20
20
 
21
21
  pub fn fui_get_accent_color() -> u32 {
22
- #[cfg(any(target_family = "wasm", feature = "native-runtime"))]
23
- {
24
- unsafe { __host_fui_get_accent_color() }
25
- }
26
- #[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
27
- {
28
- unsafe { crate::ffi::fui_get_accent_color() }
29
- }
22
+ #[cfg(any(target_family = "wasm", feature = "native-runtime"))]
23
+ {
24
+ unsafe { __host_fui_get_accent_color() }
25
+ }
26
+ #[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
27
+ {
28
+ unsafe { crate::ffi::fui_get_accent_color() }
29
+ }
30
30
  }
31
31
 
32
32
  pub fn fui_get_platform_family() -> u32 {
33
- #[cfg(any(target_family = "wasm", feature = "native-runtime"))]
34
- {
35
- unsafe { __host_fui_get_platform_family() }
36
- }
37
- #[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
38
- {
39
- unsafe { crate::ffi::fui_get_platform_family() }
40
- }
33
+ #[cfg(any(target_family = "wasm", feature = "native-runtime"))]
34
+ {
35
+ unsafe { __host_fui_get_platform_family() }
36
+ }
37
+ #[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
38
+ {
39
+ unsafe { crate::ffi::fui_get_platform_family() }
40
+ }
41
41
  }
42
42
 
43
43
  pub fn fui_is_coarse_pointer() -> bool {
44
- #[cfg(any(target_family = "wasm", feature = "native-runtime"))]
45
- {
46
- unsafe { __host_fui_is_coarse_pointer() }
47
- }
48
- #[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
49
- {
50
- unsafe { crate::ffi::fui_is_coarse_pointer() }
51
- }
44
+ #[cfg(any(target_family = "wasm", feature = "native-runtime"))]
45
+ {
46
+ unsafe { __host_fui_is_coarse_pointer() }
47
+ }
48
+ #[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
49
+ {
50
+ unsafe { crate::ffi::fui_is_coarse_pointer() }
51
+ }
52
52
  }
53
53
 
54
54
  pub fn fui_is_dark_mode() -> bool {
55
- #[cfg(any(target_family = "wasm", feature = "native-runtime"))]
56
- {
57
- unsafe { __host_fui_is_dark_mode() }
58
- }
59
- #[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
60
- {
61
- unsafe { crate::ffi::fui_is_dark_mode() }
62
- }
55
+ #[cfg(any(target_family = "wasm", feature = "native-runtime"))]
56
+ {
57
+ unsafe { __host_fui_is_dark_mode() }
58
+ }
59
+ #[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
60
+ {
61
+ unsafe { crate::ffi::fui_is_dark_mode() }
62
+ }
63
63
  }
64
64
 
65
65
  pub fn fui_now_ms() -> f64 {
66
- #[cfg(any(target_family = "wasm", feature = "native-runtime"))]
67
- {
68
- unsafe { __host_fui_now_ms() }
69
- }
70
- #[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
71
- {
72
- crate::ffi::test::host_now_ms()
73
- }
66
+ #[cfg(any(target_family = "wasm", feature = "native-runtime"))]
67
+ {
68
+ unsafe { __host_fui_now_ms() }
69
+ }
70
+ #[cfg(all(not(target_family = "wasm"), not(feature = "native-runtime")))]
71
+ {
72
+ crate::ffi::test::host_now_ms()
73
+ }
74
74
  }
package/src/lib.rs CHANGED
@@ -317,13 +317,22 @@ macro_rules! ui {
317
317
  #[macro_export]
318
318
  macro_rules! fui_component {
319
319
  ($component:ty => $root:ident) => {
320
+ $crate::fui_component!(@impl $component => $root, [root]);
321
+ };
322
+ ($component:ty => $root:ident, owner: $owner:ident) => {
323
+ $crate::fui_component!(@impl $component => $root, [owner $owner]);
324
+ };
325
+ ($component:ty => $root:ident, owners: [$($owner:ident),+ $(,)?]) => {
326
+ $crate::fui_component!(@impl $component => $root, [owners $($owner),+]);
327
+ };
328
+ (@impl $component:ty => $root:ident, $owner_spec:tt) => {
320
329
  impl $crate::Node for $component {
321
330
  fn retained_node_ref(&self) -> $crate::node::NodeRef {
322
331
  $crate::Node::retained_node_ref(&self.$root)
323
332
  }
324
333
 
325
334
  fn retained_owner_attachment(&self) -> Option<std::rc::Rc<dyn std::any::Any>> {
326
- $crate::Node::retained_owner_attachment(&self.$root)
335
+ $crate::fui_component!(@owner_attachment self, $root, $owner_spec)
327
336
  }
328
337
 
329
338
  fn build_self(&self) {
@@ -337,6 +346,16 @@ macro_rules! fui_component {
337
346
  }
338
347
  }
339
348
  };
349
+ (@owner_attachment $this:ident, $root:ident, [root]) => {
350
+ $crate::Node::retained_owner_attachment(&$this.$root)
351
+ };
352
+ (@owner_attachment $this:ident, $root:ident, [owner $owner:ident]) => {{
353
+ let owner: std::rc::Rc<dyn std::any::Any> = $this.$owner.clone();
354
+ Some(owner)
355
+ }};
356
+ (@owner_attachment $this:ident, $root:ident, [owners $($owner:ident),+]) => {
357
+ Some(std::rc::Rc::new(($($this.$owner.clone(),)+)))
358
+ };
340
359
  }
341
360
 
342
361
  pub mod prelude {
@@ -424,7 +443,7 @@ pub mod prelude {
424
443
  Child, ContextMenuEventArgs, Corners, CustomDrawable, EdgeInsets, FlexBox, FlexBoxSurface,
425
444
  GradientStop, Grid, GridTrack, HasFlexBoxRoot, Image, ImageNode, Length, Node,
426
445
  PresenterHostStyle, ScrollBar, ScrollBarVisibility, ScrollBox, ScrollState, ScrollView,
427
- Shadow, Svg, SvgNode, Text, TextCore, TextNode, VirtualList,
446
+ Shadow, Svg, SvgNode, Text, TextCore, TextNode, ThemeBindable, VirtualList,
428
447
  };
429
448
  pub use crate::persisted;
430
449
  pub use crate::platform;
@@ -542,7 +561,7 @@ pub use node::{
542
561
  ContextMenuEventArgs, Corners, CustomDrawable, EdgeInsets, FlexBox, FlexBoxSurface,
543
562
  GradientStop, Grid, GridTrack, HasFlexBoxRoot, Image, ImageNode, Length, Node,
544
563
  PresenterHostStyle, ScrollBar, ScrollBarVisibility, ScrollBox, ScrollState, ScrollView, Shadow,
545
- Svg, SvgNode, Text, TextCore, TextNode, VirtualList,
564
+ Svg, SvgNode, Text, TextCore, TextNode, ThemeBindable, VirtualList,
546
565
  };
547
566
  pub use persisted::*;
548
567
  pub use platform::*;