@effindomv2/fui-rs 0.1.8 → 0.1.9

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 (36) hide show
  1. package/Cargo.toml +1 -1
  2. package/package.json +1 -1
  3. package/src/bridge_callbacks.rs +1 -0
  4. package/src/controls/appearance.rs +200 -0
  5. package/src/controls/button.rs +48 -122
  6. package/src/controls/checkbox.rs +27 -3
  7. package/src/controls/combobox.rs +61 -23
  8. package/src/controls/context_menu.rs +156 -256
  9. package/src/controls/control_template_set.rs +2 -3
  10. package/src/controls/control_tokens.rs +76 -0
  11. package/src/controls/dialog.rs +80 -78
  12. package/src/controls/dropdown.rs +46 -5
  13. package/src/controls/form.rs +6 -0
  14. package/src/controls/internal/button_presenter.rs +21 -20
  15. package/src/controls/internal/pressable_labeled_control.rs +11 -11
  16. package/src/controls/internal/text_input_core.rs +40 -22
  17. package/src/controls/internal/text_input_presenter.rs +29 -23
  18. package/src/controls/mod.rs +7 -2
  19. package/src/controls/nav_link.rs +15 -11
  20. package/src/controls/popup.rs +24 -40
  21. package/src/controls/progress_bar.rs +80 -109
  22. package/src/controls/radio_button.rs +27 -3
  23. package/src/controls/radio_group.rs +6 -0
  24. package/src/controls/slider.rs +38 -14
  25. package/src/controls/switch.rs +27 -3
  26. package/src/controls/tests.rs +796 -56
  27. package/src/controls/text_area.rs +12 -2
  28. package/src/controls/text_input.rs +19 -3
  29. package/src/lib.rs +44 -38
  30. package/src/node/core.rs +2 -0
  31. package/src/node/flex_box.rs +245 -2
  32. package/src/node/grid.rs +5 -0
  33. package/src/node/mod.rs +48 -0
  34. package/src/node/presenter_host_style.rs +177 -0
  35. package/src/node/text_node.rs +35 -0
  36. package/src/popup_presenter.rs +32 -0
@@ -1,3 +1,4 @@
1
+ use super::ContextMenuAppearance;
1
2
  use crate::bindings::ui;
2
3
  use crate::event::{self, PointerEventArgs, PointerType};
3
4
  use crate::ffi::{
@@ -11,9 +12,8 @@ use crate::node::{
11
12
  WeakFlexBox,
12
13
  };
13
14
  use crate::popup_presenter::{PopupPresenter, PopupPresenterEventTarget};
14
- use crate::signal::SubscriptionGuard;
15
15
  use crate::theme::{current_theme, subscribe, Theme};
16
- use crate::FontFamily;
16
+ use crate::{FontFamily, FontStyle, FontWeight};
17
17
  use std::cell::{Cell, RefCell};
18
18
  use std::rc::Rc;
19
19
 
@@ -302,11 +302,16 @@ struct ContextMenuEntryStyle {
302
302
  padding_top: f32,
303
303
  padding_right: f32,
304
304
  padding_bottom: f32,
305
- corner_radius: f32,
305
+ corner_top_left: f32,
306
+ corner_top_right: f32,
307
+ corner_bottom_right: f32,
308
+ corner_bottom_left: f32,
306
309
  text_color: u32,
307
310
  background_color: u32,
308
311
  hover_background_color: u32,
309
312
  font_family: FontFamily,
313
+ font_weight: FontWeight,
314
+ font_style: FontStyle,
310
315
  font_size: f32,
311
316
  }
312
317
 
@@ -318,11 +323,16 @@ impl ContextMenuEntryStyle {
318
323
  padding_top: theme.context_menu.item.padding_top,
319
324
  padding_right: theme.context_menu.item.padding_right,
320
325
  padding_bottom: theme.context_menu.item.padding_bottom,
321
- corner_radius: theme.context_menu.item.corner_radius,
326
+ corner_top_left: theme.context_menu.item.corner_radius,
327
+ corner_top_right: theme.context_menu.item.corner_radius,
328
+ corner_bottom_right: theme.context_menu.item.corner_radius,
329
+ corner_bottom_left: theme.context_menu.item.corner_radius,
322
330
  text_color: theme.context_menu.item.text_color,
323
331
  background_color: theme.context_menu.item.background,
324
332
  hover_background_color: theme.context_menu.item.hover_background,
325
333
  font_family: theme.context_menu.item.font_family.clone(),
334
+ font_weight: FontWeight::Regular,
335
+ font_style: FontStyle::Normal,
326
336
  font_size: theme.context_menu.item.font_size,
327
337
  }
328
338
  }
@@ -484,7 +494,12 @@ impl ContextMenuEntry {
484
494
  style.padding_right,
485
495
  style.padding_bottom,
486
496
  )
487
- .corner_radius(style.corner_radius)
497
+ .corners(
498
+ style.corner_top_left,
499
+ style.corner_top_right,
500
+ style.corner_bottom_right,
501
+ style.corner_bottom_left,
502
+ )
488
503
  .bg_color(if self.hovered.get() && !self.disabled.get() {
489
504
  style.hover_background_color
490
505
  } else {
@@ -492,6 +507,8 @@ impl ContextMenuEntry {
492
507
  });
493
508
  self.label_node
494
509
  .font_family(style.font_family.clone())
510
+ .font_weight(style.font_weight)
511
+ .font_style(style.font_style)
495
512
  .font_size(style.font_size)
496
513
  .line_height(context_menu_entry_line_height(&style))
497
514
  .text_color(if self.disabled.get() {
@@ -501,6 +518,8 @@ impl ContextMenuEntry {
501
518
  });
502
519
  self.shortcut_node
503
520
  .font_family(style.font_family.clone())
521
+ .font_weight(style.font_weight)
522
+ .font_style(style.font_style)
504
523
  .font_size(style.font_size)
505
524
  .line_height(context_menu_entry_line_height(&style))
506
525
  .text_color(theme.colors.text_muted);
@@ -597,6 +616,7 @@ impl ContextMenuSeparator {
597
616
  }
598
617
 
599
618
  struct ContextMenuState {
619
+ appearance: Option<ContextMenuAppearance>,
600
620
  visible: bool,
601
621
  suppress_next_pointer_up_activation: bool,
602
622
  key_filter_token: u32,
@@ -606,31 +626,26 @@ struct ContextMenuState {
606
626
  panel_border_width: f32,
607
627
  panel_border_color: u32,
608
628
  panel_border_style: BorderStyle,
609
- panel_corner_radius: f32,
629
+ panel_corner_top_left: f32,
630
+ panel_corner_top_right: f32,
631
+ panel_corner_bottom_right: f32,
632
+ panel_corner_bottom_left: f32,
610
633
  separator_color: u32,
611
634
  panel_shadow_color: u32,
635
+ panel_shadow_offset_x: f32,
612
636
  panel_shadow_offset_y: f32,
613
637
  panel_shadow_blur: f32,
614
638
  panel_shadow_spread: f32,
615
639
  panel_background_blur_sigma: f32,
616
- panel_background_overridden: bool,
617
- panel_border_overridden: bool,
618
- panel_corner_radius_overridden: bool,
619
- separator_color_overridden: bool,
620
- panel_shadow_overridden: bool,
621
- panel_background_blur_overridden: bool,
622
- item_metrics_overridden: bool,
623
- item_text_color_overridden: bool,
624
- item_background_overridden: bool,
625
- item_hover_color_overridden: bool,
626
- item_corner_radius_overridden: bool,
627
- item_font_overridden: bool,
640
+ panel_border_dash_on: f32,
641
+ panel_border_dash_off: f32,
628
642
  visibility_changed_callback: Option<VisibilityChangedCallback>,
629
643
  }
630
644
 
631
645
  impl ContextMenuState {
632
646
  fn from_theme(theme: Theme) -> Self {
633
647
  Self {
648
+ appearance: None,
634
649
  visible: false,
635
650
  suppress_next_pointer_up_activation: false,
636
651
  key_filter_token: 0,
@@ -640,25 +655,19 @@ impl ContextMenuState {
640
655
  panel_border_width: 1.0,
641
656
  panel_border_color: theme.context_menu.panel_border_color,
642
657
  panel_border_style: BorderStyle::Solid,
643
- panel_corner_radius: theme.context_menu.panel_corner_radius,
658
+ panel_corner_top_left: theme.context_menu.panel_corner_radius,
659
+ panel_corner_top_right: theme.context_menu.panel_corner_radius,
660
+ panel_corner_bottom_right: theme.context_menu.panel_corner_radius,
661
+ panel_corner_bottom_left: theme.context_menu.panel_corner_radius,
644
662
  separator_color: theme.context_menu.separator_color,
645
663
  panel_shadow_color: theme.context_menu.panel_shadow_color,
664
+ panel_shadow_offset_x: 0.0,
646
665
  panel_shadow_offset_y: theme.context_menu.shadow_offset_y,
647
666
  panel_shadow_blur: theme.context_menu.shadow_blur,
648
667
  panel_shadow_spread: theme.context_menu.shadow_spread,
649
668
  panel_background_blur_sigma: DEFAULT_PANEL_BACKGROUND_BLUR_SIGMA,
650
- panel_background_overridden: false,
651
- panel_border_overridden: false,
652
- panel_corner_radius_overridden: false,
653
- separator_color_overridden: false,
654
- panel_shadow_overridden: false,
655
- panel_background_blur_overridden: false,
656
- item_metrics_overridden: false,
657
- item_text_color_overridden: false,
658
- item_background_overridden: false,
659
- item_hover_color_overridden: false,
660
- item_corner_radius_overridden: false,
661
- item_font_overridden: false,
669
+ panel_border_dash_on: 0.0,
670
+ panel_border_dash_off: 0.0,
662
671
  visibility_changed_callback: None,
663
672
  }
664
673
  }
@@ -668,7 +677,6 @@ impl ContextMenuState {
668
677
  struct ContextMenuEventTarget {
669
678
  panel: WeakFlexBox,
670
679
  presenter: PopupPresenterEventTarget,
671
- presenter_owner: PopupPresenter,
672
680
  entries: Vec<ContextMenuEntry>,
673
681
  separators: Vec<ContextMenuSeparator>,
674
682
  current_items: Rc<RefCell<Vec<MenuItem>>>,
@@ -691,7 +699,7 @@ impl ContextMenuEventTarget {
691
699
  }
692
700
 
693
701
  fn hide(&self) {
694
- let was_visible = self.state.borrow().visible || self.presenter_owner.is_open();
702
+ let was_visible = self.state.borrow().visible || self.presenter.is_open();
695
703
  if !was_visible {
696
704
  return;
697
705
  }
@@ -765,8 +773,8 @@ impl ContextMenuEventTarget {
765
773
  return;
766
774
  }
767
775
  }
768
- let local_x = event.scene_x - self.presenter_owner.surface_x();
769
- let local_y = event.scene_y - self.presenter_owner.surface_y();
776
+ let local_x = event.scene_x - self.presenter.surface_x();
777
+ let local_y = event.scene_y - self.presenter.surface_y();
770
778
  if local_x < 0.0 || local_x > self.state.borrow().menu_width || local_y < 0.0 {
771
779
  event.handled = true;
772
780
  self.hide();
@@ -813,17 +821,22 @@ impl ContextMenuEventTarget {
813
821
  .width(state.menu_width, Unit::Pixel)
814
822
  .bg_color(state.panel_background_color)
815
823
  .background_blur(state.panel_background_blur_sigma)
816
- .corner_radius(state.panel_corner_radius)
824
+ .corners(
825
+ state.panel_corner_top_left,
826
+ state.panel_corner_top_right,
827
+ state.panel_corner_bottom_right,
828
+ state.panel_corner_bottom_left,
829
+ )
817
830
  .border_config(Border {
818
831
  width: state.panel_border_width,
819
832
  color: state.panel_border_color,
820
833
  style: state.panel_border_style,
821
- dash_on: 0.0,
822
- dash_off: 0.0,
834
+ dash_on: state.panel_border_dash_on,
835
+ dash_off: state.panel_border_dash_off,
823
836
  })
824
837
  .drop_shadow(
825
838
  state.panel_shadow_color,
826
- 0.0,
839
+ state.panel_shadow_offset_x,
827
840
  state.panel_shadow_offset_y,
828
841
  state.panel_shadow_blur,
829
842
  state.panel_shadow_spread,
@@ -842,53 +855,90 @@ impl ContextMenuEventTarget {
842
855
  fn handle_theme_changed(&self) {
843
856
  let theme = current_theme();
844
857
  let mut state = self.state.borrow_mut();
845
- if !state.panel_background_overridden {
846
- state.panel_background_color = theme.context_menu.panel_background;
847
- }
848
- if !state.panel_border_overridden {
849
- state.panel_border_width = 1.0;
850
- state.panel_border_color = theme.context_menu.panel_border_color;
851
- state.panel_border_style = BorderStyle::Solid;
852
- }
853
- if !state.panel_corner_radius_overridden {
854
- state.panel_corner_radius = theme.context_menu.panel_corner_radius;
855
- }
856
- if !state.separator_color_overridden {
857
- state.separator_color = theme.context_menu.separator_color;
858
- }
859
- if !state.panel_shadow_overridden {
860
- state.panel_shadow_color = theme.context_menu.panel_shadow_color;
861
- state.panel_shadow_offset_y = theme.context_menu.shadow_offset_y;
862
- state.panel_shadow_blur = theme.context_menu.shadow_blur;
863
- state.panel_shadow_spread = theme.context_menu.shadow_spread;
864
- }
865
- if !state.panel_background_blur_overridden {
866
- state.panel_background_blur_sigma = DEFAULT_PANEL_BACKGROUND_BLUR_SIGMA;
867
- }
868
- if !state.item_metrics_overridden {
869
- state.item_style.item_height = theme.context_menu.item.height;
870
- state.item_style.padding_left = theme.context_menu.item.padding_left;
871
- state.item_style.padding_top = theme.context_menu.item.padding_top;
872
- state.item_style.padding_right = theme.context_menu.item.padding_right;
873
- state.item_style.padding_bottom = theme.context_menu.item.padding_bottom;
874
- }
875
- if !state.item_text_color_overridden {
876
- state.item_style.text_color = theme.context_menu.item.text_color;
877
- }
878
- if !state.item_background_overridden {
879
- state.item_style.background_color = theme.context_menu.item.background;
880
- }
881
- if !state.item_hover_color_overridden {
882
- state.item_style.hover_background_color = theme.context_menu.item.hover_background;
883
- }
884
- if !state.item_corner_radius_overridden {
885
- state.item_style.corner_radius = theme.context_menu.item.corner_radius;
886
- }
887
- if !state.item_font_overridden {
888
- state.item_style.font_family = theme.context_menu.item.font_family.clone();
889
- state.item_style.font_size = theme.context_menu.item.font_size;
890
- }
858
+ let appearance = state.appearance.clone().unwrap_or_default();
859
+ let panel = appearance.panel.unwrap_or_default();
860
+ let backdrop = appearance.backdrop.unwrap_or_default();
861
+ let item = appearance.item.unwrap_or_default();
862
+
863
+ state.menu_width = appearance.width.unwrap_or(MENU_WIDTH);
864
+ state.panel_background_color = panel
865
+ .background
866
+ .unwrap_or(theme.context_menu.panel_background);
867
+ let border = panel
868
+ .border
869
+ .unwrap_or_else(|| Border::solid(1.0, theme.context_menu.panel_border_color));
870
+ state.panel_border_width = border.width;
871
+ state.panel_border_color = border.color;
872
+ state.panel_border_style = border.style;
873
+ state.panel_border_dash_on = border.dash_on;
874
+ state.panel_border_dash_off = border.dash_off;
875
+ let corners = panel
876
+ .corners
877
+ .unwrap_or_else(|| crate::Corners::all(theme.context_menu.panel_corner_radius));
878
+ state.panel_corner_top_left = corners.top_left;
879
+ state.panel_corner_top_right = corners.top_right;
880
+ state.panel_corner_bottom_right = corners.bottom_right;
881
+ state.panel_corner_bottom_left = corners.bottom_left;
882
+ let shadow = panel.shadow.unwrap_or_else(|| {
883
+ crate::Shadow::new(
884
+ theme.context_menu.panel_shadow_color,
885
+ 0.0,
886
+ theme.context_menu.shadow_offset_y,
887
+ theme.context_menu.shadow_blur,
888
+ theme.context_menu.shadow_spread,
889
+ )
890
+ });
891
+ state.panel_shadow_color = shadow.color;
892
+ state.panel_shadow_offset_x = shadow.offset_x;
893
+ state.panel_shadow_offset_y = shadow.offset_y;
894
+ state.panel_shadow_blur = shadow.blur_sigma;
895
+ state.panel_shadow_spread = shadow.spread;
896
+ state.panel_background_blur_sigma = panel
897
+ .background_blur
898
+ .unwrap_or(DEFAULT_PANEL_BACKGROUND_BLUR_SIGMA);
899
+ state.separator_color = appearance
900
+ .separator_color
901
+ .unwrap_or(theme.context_menu.separator_color);
902
+
903
+ state.item_style.item_height = item.height.unwrap_or(theme.context_menu.item.height);
904
+ let padding = item.padding.unwrap_or_else(|| {
905
+ crate::EdgeInsets::new(
906
+ theme.context_menu.item.padding_left,
907
+ theme.context_menu.item.padding_top,
908
+ theme.context_menu.item.padding_right,
909
+ theme.context_menu.item.padding_bottom,
910
+ )
911
+ });
912
+ state.item_style.padding_left = padding.left;
913
+ state.item_style.padding_top = padding.top;
914
+ state.item_style.padding_right = padding.right;
915
+ state.item_style.padding_bottom = padding.bottom;
916
+ state.item_style.text_color = item
917
+ .text_color
918
+ .unwrap_or(theme.context_menu.item.text_color);
919
+ state.item_style.background_color = item
920
+ .background
921
+ .unwrap_or(theme.context_menu.item.background);
922
+ state.item_style.hover_background_color = item
923
+ .hover_background
924
+ .unwrap_or(theme.context_menu.item.hover_background);
925
+ let item_corners = item
926
+ .corners
927
+ .unwrap_or_else(|| crate::Corners::all(theme.context_menu.item.corner_radius));
928
+ state.item_style.corner_top_left = item_corners.top_left;
929
+ state.item_style.corner_top_right = item_corners.top_right;
930
+ state.item_style.corner_bottom_right = item_corners.bottom_right;
931
+ state.item_style.corner_bottom_left = item_corners.bottom_left;
932
+ state.item_style.font_family = item
933
+ .font_family
934
+ .unwrap_or_else(|| theme.context_menu.item.font_family.clone());
935
+ state.item_style.font_weight = item.font_weight.unwrap_or(FontWeight::Regular);
936
+ state.item_style.font_style = item.font_style.unwrap_or(FontStyle::Normal);
937
+ state.item_style.font_size = item.font_size.unwrap_or(theme.context_menu.item.font_size);
891
938
  drop(state);
939
+ self.presenter
940
+ .backdrop_color(backdrop.color.unwrap_or(0x00000000));
941
+ self.presenter.background_blur(backdrop.blur.unwrap_or(0.0));
892
942
  self.apply_theme();
893
943
  }
894
944
  }
@@ -905,7 +955,6 @@ pub struct ContextMenu {
905
955
  current_item_tops: Rc<RefCell<Vec<f32>>>,
906
956
  current_item_heights: Rc<RefCell<Vec<f32>>>,
907
957
  state: Rc<RefCell<ContextMenuState>>,
908
- theme_guard: Rc<RefCell<Option<SubscriptionGuard>>>,
909
958
  }
910
959
 
911
960
  impl Default for ContextMenu {
@@ -944,7 +993,6 @@ impl ContextMenu {
944
993
  current_item_tops: Rc::new(RefCell::new(Vec::new())),
945
994
  current_item_heights: Rc::new(RefCell::new(Vec::new())),
946
995
  state: Rc::new(RefCell::new(ContextMenuState::from_theme(theme))),
947
- theme_guard: Rc::new(RefCell::new(None)),
948
996
  };
949
997
  menu.bind_events();
950
998
  menu.install_theme_subscription();
@@ -1014,174 +1062,15 @@ impl ContextMenu {
1014
1062
  self
1015
1063
  }
1016
1064
 
1017
- pub fn menu_width(&self, value: f32) -> &Self {
1018
- if value <= 0.0 {
1019
- warn(
1020
- "Layout",
1021
- &format!("ContextMenu.menu_width() received {value}; clamping to 1.0."),
1022
- );
1023
- }
1024
- self.state.borrow_mut().menu_width = value.max(1.0);
1025
- self.apply_theme();
1065
+ pub fn appearance(&self, appearance: ContextMenuAppearance) -> &Self {
1066
+ self.state.borrow_mut().appearance = Some(appearance);
1067
+ self.event_target().handle_theme_changed();
1026
1068
  self
1027
1069
  }
1028
1070
 
1029
- pub fn item_height(&self, value: f32) -> &Self {
1030
- if value <= 0.0 {
1031
- warn(
1032
- "Layout",
1033
- &format!("ContextMenu.item_height() received {value}; clamping to 1.0."),
1034
- );
1035
- }
1036
- let mut state = self.state.borrow_mut();
1037
- state.item_metrics_overridden = true;
1038
- state.item_style.item_height = value.max(1.0);
1039
- drop(state);
1040
- self.apply_theme();
1041
- self
1042
- }
1043
-
1044
- pub fn item_padding(&self, left: f32, top: f32, right: f32, bottom: f32) -> &Self {
1045
- let mut state = self.state.borrow_mut();
1046
- state.item_metrics_overridden = true;
1047
- state.item_style.padding_left = left;
1048
- state.item_style.padding_top = top;
1049
- state.item_style.padding_right = right;
1050
- state.item_style.padding_bottom = bottom;
1051
- drop(state);
1052
- self.apply_theme();
1053
- self
1054
- }
1055
-
1056
- pub fn panel_color(&self, color: u32) -> &Self {
1057
- let mut state = self.state.borrow_mut();
1058
- state.panel_background_overridden = true;
1059
- state.panel_background_color = color;
1060
- drop(state);
1061
- self.apply_theme();
1062
- self
1063
- }
1064
-
1065
- pub fn panel_border(&self, width: f32, color: u32) -> &Self {
1066
- let mut state = self.state.borrow_mut();
1067
- state.panel_border_overridden = true;
1068
- state.panel_border_width = width;
1069
- state.panel_border_color = color;
1070
- state.panel_border_style = BorderStyle::Solid;
1071
- drop(state);
1072
- self.apply_theme();
1073
- self
1074
- }
1075
-
1076
- pub fn panel_border_config(&self, border: Border) -> &Self {
1077
- let mut state = self.state.borrow_mut();
1078
- state.panel_border_overridden = true;
1079
- state.panel_border_width = border.width;
1080
- state.panel_border_color = border.color;
1081
- state.panel_border_style = border.style;
1082
- drop(state);
1083
- self.apply_theme();
1084
- self
1085
- }
1086
-
1087
- pub fn panel_corner_radius(&self, radius: f32) -> &Self {
1088
- let mut state = self.state.borrow_mut();
1089
- state.panel_corner_radius_overridden = true;
1090
- state.panel_corner_radius = radius.max(0.0);
1091
- drop(state);
1092
- self.apply_theme();
1093
- self
1094
- }
1095
-
1096
- pub fn panel_shadow(&self, color: u32, offset_y: f32, blur_sigma: f32, spread: f32) -> &Self {
1097
- let mut state = self.state.borrow_mut();
1098
- state.panel_shadow_overridden = true;
1099
- state.panel_shadow_color = color;
1100
- state.panel_shadow_offset_y = offset_y;
1101
- state.panel_shadow_blur = blur_sigma;
1102
- state.panel_shadow_spread = spread;
1103
- drop(state);
1104
- self.apply_theme();
1105
- self
1106
- }
1107
-
1108
- pub fn panel_background_blur(&self, sigma: f32) -> &Self {
1109
- if sigma < 0.0 {
1110
- warn(
1111
- "Layout",
1112
- &format!("ContextMenu.panel_background_blur() received {sigma}; clamping to 0.0."),
1113
- );
1114
- }
1115
- let mut state = self.state.borrow_mut();
1116
- state.panel_background_blur_overridden = true;
1117
- state.panel_background_blur_sigma = sigma.max(0.0);
1118
- drop(state);
1119
- self.apply_theme();
1120
- self
1121
- }
1122
-
1123
- pub fn backdrop_color(&self, color: u32) -> &Self {
1124
- self.popup_presenter.backdrop_color(color);
1125
- self
1126
- }
1127
-
1128
- pub fn background_blur(&self, sigma: f32) -> &Self {
1129
- self.popup_presenter.background_blur(sigma);
1130
- self
1131
- }
1132
-
1133
- pub fn item_color(&self, color: u32) -> &Self {
1134
- let mut state = self.state.borrow_mut();
1135
- state.item_background_overridden = true;
1136
- state.item_style.background_color = color;
1137
- drop(state);
1138
- self.apply_theme();
1139
- self
1140
- }
1141
-
1142
- pub fn item_hover_color(&self, color: u32) -> &Self {
1143
- let mut state = self.state.borrow_mut();
1144
- state.item_hover_color_overridden = true;
1145
- state.item_style.hover_background_color = color;
1146
- drop(state);
1147
- self.apply_theme();
1148
- self
1149
- }
1150
-
1151
- pub fn item_text_color(&self, color: u32) -> &Self {
1152
- let mut state = self.state.borrow_mut();
1153
- state.item_text_color_overridden = true;
1154
- state.item_style.text_color = color;
1155
- drop(state);
1156
- self.apply_theme();
1157
- self
1158
- }
1159
-
1160
- pub fn item_corner_radius(&self, radius: f32) -> &Self {
1161
- let mut state = self.state.borrow_mut();
1162
- state.item_corner_radius_overridden = true;
1163
- state.item_style.corner_radius = radius.max(0.0);
1164
- drop(state);
1165
- self.apply_theme();
1166
- self
1167
- }
1168
-
1169
- pub fn item_font(&self, font_family: FontFamily, font_size: f32) -> &Self {
1170
- let mut state = self.state.borrow_mut();
1171
- state.item_font_overridden = true;
1172
- state.item_style.font_family = font_family;
1173
- state.item_style.font_size = font_size;
1174
- drop(state);
1175
- self.apply_theme();
1176
- self
1177
- }
1178
-
1179
- pub fn separator_color(&self, color: u32) -> &Self {
1180
- let mut state = self.state.borrow_mut();
1181
- state.separator_color_overridden = true;
1182
- state.separator_color = color;
1183
- drop(state);
1184
- self.apply_theme();
1071
+ pub fn clear_appearance(&self) -> &Self {
1072
+ self.state.borrow_mut().appearance = None;
1073
+ self.event_target().handle_theme_changed();
1185
1074
  self
1186
1075
  }
1187
1076
 
@@ -1311,9 +1200,12 @@ impl ContextMenu {
1311
1200
 
1312
1201
  fn install_theme_subscription(&self) {
1313
1202
  let event_target = self.event_target();
1314
- *self.theme_guard.borrow_mut() = Some(subscribe(move |_theme| {
1203
+ let guard = subscribe(move |_theme| {
1315
1204
  event_target.handle_theme_changed();
1316
- }));
1205
+ });
1206
+ self.root
1207
+ .retained_node_ref()
1208
+ .retain_attachment(Rc::new(guard));
1317
1209
  }
1318
1210
 
1319
1211
  fn clear_panel(&self) {
@@ -1328,7 +1220,6 @@ impl ContextMenu {
1328
1220
  ContextMenuEventTarget {
1329
1221
  panel: self.panel.downgrade(),
1330
1222
  presenter: self.popup_presenter.event_target(),
1331
- presenter_owner: self.popup_presenter.clone(),
1332
1223
  entries: self.entries.clone(),
1333
1224
  separators: self.separators.clone(),
1334
1225
  current_items: self.current_items.clone(),
@@ -1344,13 +1235,16 @@ impl Node for ContextMenu {
1344
1235
  self.root.retained_node_ref()
1345
1236
  }
1346
1237
 
1238
+ fn retained_owner_attachment(&self) -> Option<Rc<dyn std::any::Any>> {
1239
+ Some(Rc::new(self.clone()))
1240
+ }
1241
+
1347
1242
  fn build_self(&self) {
1348
1243
  self.root.build_self();
1349
1244
  }
1350
1245
 
1351
1246
  fn dispose(&self) {
1352
1247
  self.hide();
1353
- self.theme_guard.borrow_mut().take();
1354
1248
  self.popup_presenter.dispose();
1355
1249
  for entry in &self.entries {
1356
1250
  if entry.root.handle() != crate::node::NodeHandle::INVALID {
@@ -1365,3 +1259,9 @@ impl Node for ContextMenu {
1365
1259
  self.root.dispose();
1366
1260
  }
1367
1261
  }
1262
+
1263
+ impl crate::node::HasFlexBoxRoot for ContextMenu {
1264
+ fn flex_box_root(&self) -> &FlexBox {
1265
+ &self.root
1266
+ }
1267
+ }
@@ -32,10 +32,9 @@ pub fn get_control_templates() -> Option<ControlTemplateSet> {
32
32
  ACTIVE_CONTROL_TEMPLATES.with(|slot| slot.borrow().clone())
33
33
  }
34
34
 
35
- pub fn use_control_templates(templates: Option<ControlTemplateSet>) -> Option<ControlTemplateSet> {
35
+ pub fn use_control_templates(templates: ControlTemplateSet) {
36
36
  ACTIVE_CONTROL_TEMPLATES.with(|slot| {
37
- *slot.borrow_mut() = templates;
38
- slot.borrow().clone()
37
+ *slot.borrow_mut() = Some(templates);
39
38
  })
40
39
  }
41
40