ferroui_controls/
native_menu_export.rs1use crate::platform::{as_native_menu_exporter_provider, ITopLevelNativeMenuExporter};
2use crate::{NativeMenu, TopLevel};
3use ferroui_base::platform::IOptionalFeatureProvider;
4use ferroui_base::{ferro_properties, AttachedProperty, FerroObject, FerroProperty, Ref};
5use std::cell::Cell;
6use std::rc::Rc;
7
8struct NativeMenuInfo {
10 changing_is_exported: Cell<bool>,
11 exporter: Option<Rc<dyn ITopLevelNativeMenuExporter>>,
12}
13
14impl NativeMenuInfo {
15 fn new(target: &TopLevel) -> Rc<Self> {
16 let exporter = target.platform_impl().and_then(|platform_impl| {
17 let provider: &dyn IOptionalFeatureProvider = &*platform_impl;
18 provider.try_get::<dyn ITopLevelNativeMenuExporter>()
19 });
20 if let Some(exporter) = &exporter {
21 let target = target.to_ref().downgrade();
24 let weak_exporter = Rc::downgrade(exporter);
25 let _ = exporter.on_is_native_menu_exported_changed(Rc::new(move || {
26 if let (Some(target), Some(exporter)) = (target.upgrade(), weak_exporter.upgrade()) {
27 NativeMenu::set_is_native_menu_exported(&target, exporter.is_native_menu_exported());
28 }
29 }));
30 }
31 Rc::new(Self { changing_is_exported: Cell::new(false), exporter })
32 }
33}
34
35#[derive(Clone)]
38struct NativeMenuInfoRef(Rc<NativeMenuInfo>);
39
40impl PartialEq for NativeMenuInfoRef {
41 fn eq(&self, other: &Self) -> bool {
42 Rc::ptr_eq(&self.0, &other.0)
43 }
44}
45
46ferro_properties! {
47 impl NativeMenu, fn register_export_properties {
48 pub fn is_native_menu_exported_property() -> AttachedProperty<bool> {
50 FerroProperty::register_attached::<NativeMenu, TopLevel, _>("IsNativeMenuExported", false)
51 }
52
53 fn native_menu_info_property() -> AttachedProperty<Option<NativeMenuInfoRef>> {
54 FerroProperty::register_attached::<NativeMenu, TopLevel, _>("___NativeMenuInfo", None)
55 }
56
57 pub fn menu_property() -> AttachedProperty<Option<Ref<NativeMenu>>> {
59 FerroProperty::register_attached::<NativeMenu, FerroObject, _>("Menu", None)
60 }
61 }
62}
63
64impl NativeMenu {
65 pub fn get_is_native_menu_exported(tl: &TopLevel) -> bool {
68 tl.get_value(Self::is_native_menu_exported_property())
69 }
70
71 fn get_info(target: &TopLevel) -> Rc<NativeMenuInfo> {
72 match target.get_value(Self::native_menu_info_property()) {
73 Some(rv) => rv.0,
74 None => {
75 let rv = NativeMenuInfo::new(target);
76 target.set_value(Self::native_menu_info_property(), Some(NativeMenuInfoRef(rv.clone())));
77 Self::set_is_native_menu_exported(
78 target,
79 rv.exporter.as_ref().is_some_and(|exporter| exporter.is_native_menu_exported()),
80 );
81 rv
82 }
83 }
84 }
85
86 fn set_is_native_menu_exported(tl: &TopLevel, value: bool) {
87 Self::get_info(tl).changing_is_exported.set(true);
88 tl.set_value(Self::is_native_menu_exported_property(), value);
89 }
90
91 pub fn set_menu(o: &FerroObject, menu: Option<Ref<NativeMenu>>) {
94 o.set_value(Self::menu_property(), menu)
95 }
96
97 pub fn get_menu(o: &FerroObject) -> Option<Ref<NativeMenu>> {
99 o.get_value(Self::menu_property())
100 }
101
102 pub(crate) fn static_constructor_export() {
104 Self::is_native_menu_exported_property().changed().subscribe(|args| {
106 let sender = args.sender().downcast_ref::<TopLevel>().expect("the sender is a top-level");
107 let info = Self::get_info(sender);
108 if !info.changing_is_exported.get() {
109 panic!("IsNativeMenuExported property is read-only");
110 }
111 info.changing_is_exported.set(false);
112 });
113 Self::menu_property().changed().subscribe(|args| {
114 let new_value = args.get_new_value::<Option<Ref<NativeMenu>>>();
115 if let Some(tl) = args.sender().downcast_ref::<TopLevel>() {
116 if let Some(exporter) = &Self::get_info(tl).exporter {
117 exporter.set_native_menu(new_value);
118 }
119 } else if let Some(provider) = as_native_menu_exporter_provider(args.sender()) {
120 if let Some(exporter) = provider.native_menu_exporter() {
121 exporter.set_native_menu(new_value);
122 }
123 }
124 });
125 }
126}