ferroui_native/
popup_impl.rs1use crate::helpers::ComResultExt;
2use crate::interop::*;
3use crate::top_level_impl::{
4 top_level_of, impl_top_level_contract, MacOSTopLevelHandle, TopLevelEvents, TopLevelImpl, TopLevelParent,
5};
6use crate::window_impl::WindowImpl;
7use crate::window_impl_base::{impl_window_base_contract, WindowBaseImpl, WindowBaseParent, WindowEventsParent};
8use ferroui_base::{PixelPoint, Size};
9use ferroui_controls::platform::{IPopupImpl, ITopLevelImpl, IWindowBaseImpl, PlatformThemeVariant};
10use ferroui_controls::primitives::popup_positioning::{
11 IPopupPositioner, ManagedPopupPositioner, ManagedPopupPositionerPopupImplHelper,
12};
13use ferroui_controls::WindowResizeReason;
14use ferroui_microcom::ComPtr;
15use std::cell::RefCell;
16use std::rc::{Rc, Weak};
17
18pub struct PopupImpl {
20 weak_self: Weak<PopupImpl>,
21 base: WindowBaseImpl,
22 parent: Rc<dyn ITopLevelImpl>,
23 native: RefCell<Option<ComPtr<IFrnPopup>>>,
26 popup_positioner: RefCell<Option<Rc<dyn IPopupPositioner>>>,
27}
28
29fn root_parent(mut parent: Rc<dyn ITopLevelImpl>) -> Rc<dyn ITopLevelImpl> {
31 loop {
32 let next = match parent.as_any().downcast_ref::<PopupImpl>() {
33 Some(popup) => popup.parent.clone(),
34 None => return parent,
35 };
36 parent = next;
37 }
38}
39
40impl PopupImpl {
41 pub(crate) fn new(factory: ComPtr<IFerroNativeFactory>, parent: Rc<dyn ITopLevelImpl>) -> Rc<PopupImpl> {
42 let this = Rc::new_cyclic(|weak_self| PopupImpl {
43 weak_self: weak_self.clone(),
44 base: WindowBaseImpl::new(factory.clone()),
45 parent: parent.clone(),
46 native: RefCell::new(None),
47 popup_positioner: RefCell::new(None),
48 });
49
50 let e = IFrnWindowEvents::from_impl(TopLevelEvents(this.clone()));
51 let native = factory.create_popup(Some(&e)).check().expect("the native popup");
52 *this.native.borrow_mut() = Some(native.clone());
53 this.base.init(MacOSTopLevelHandle::from_window_base(ComPtr::<IFrnWindowBase>::from_ref(&native)));
54
55 let weak = Rc::downgrade(&this);
56 let positioner_helper = ManagedPopupPositionerPopupImplHelper::new(
57 parent.clone(),
58 Rc::new(move |position, size, scaling| {
59 if let Some(this) = weak.upgrade() {
60 this.move_resize(position, size, scaling);
61 }
62 }),
63 );
64 let positioner: Rc<dyn IPopupPositioner> = Rc::new(ManagedPopupPositioner::new(Rc::new(positioner_helper)));
65 *this.popup_positioner.borrow_mut() = Some(positioner);
66
67 let parent = root_parent(parent);
68
69 if let Some(window) = parent.as_any().downcast_ref::<WindowImpl>() {
70 let parent_native = window.window_base().native();
71 native.set_parent(parent_native.as_deref()).check();
72 }
73
74 if let Some(parent_top_level) = top_level_of(&*parent) {
76 this.top_level().set_input_method(parent_top_level.input_method());
77 }
78
79 this
80 }
81
82 pub(crate) fn top_level(&self) -> &Rc<TopLevelImpl> {
83 self.base.top_level()
84 }
85
86 fn move_resize(&self, position: PixelPoint, size: Size, _scaling: f64) {
87 self.base.set_position(position);
88 self.base.resize(size, WindowResizeReason::Layout);
89 }
90
91 pub fn native(&self) -> Option<ComPtr<IFrnPopup>> {
93 self.native.borrow().clone()
94 }
95}
96
97impl TopLevelParent for PopupImpl {
98 fn top_level(&self) -> &Rc<TopLevelImpl> {
99 self.base.top_level()
100 }
101
102 fn dispose_top_level(&self) {
103 if let Some(native) = self.base.native() {
104 native.set_parent(None).check();
105 }
106
107 self.base.dispose();
108 let native = self.native.borrow_mut().take();
109 drop(native);
110 }
111
112 fn create_popup_core(&self) -> Option<Rc<dyn IPopupImpl>> {
113 let this: Rc<dyn ITopLevelImpl> = self.weak_self.upgrade()?;
114 Some(PopupImpl::new(self.top_level().factory().clone(), this))
115 }
116
117 fn set_frame_theme_variant_core(&self, theme_variant: Option<PlatformThemeVariant>) {
118 self.base.set_frame_theme_variant(theme_variant);
119 }
120}
121
122impl WindowBaseParent for PopupImpl {
123 fn window_base(&self) -> &WindowBaseImpl {
124 &self.base
125 }
126}
127
128impl WindowEventsParent for PopupImpl {
129 fn on_closing(&self) -> bool {
130 true
131 }
132
133 fn on_window_state_changed(&self, _state: FrnWindowState) {}
134
135 fn on_got_input_when_disabled(&self) {
136 }
138}
139
140impl_top_level_contract!(PopupImpl {
141 fn as_window_base_impl(&self) -> Option<&dyn IWindowBaseImpl> {
142 Some(self)
143 }
144
145 fn as_popup_impl(&self) -> Option<&dyn IPopupImpl> {
146 Some(self)
147 }
148});
149
150impl_window_base_contract!(PopupImpl);
151
152impl IPopupImpl for PopupImpl {
153 fn popup_positioner(&self) -> Option<Rc<dyn IPopupPositioner>> {
154 self.popup_positioner.borrow().clone()
155 }
156
157 fn set_window_manager_add_shadow_hint(&self, _enabled: bool) {}
158
159 fn take_focus(&self) {
160 let parent = root_parent(self.parent.clone());
161
162 if let Some(window) = parent.as_any().downcast_ref::<WindowImpl>() {
163 if let Some(native) = window.native() {
164 native.take_focus_from_children().check();
165 }
166 }
167 }
168
169 fn set_hit_test_visible(&self, is_hit_test_visible: bool) {
170 if let Some(native) = self.native() {
171 native.set_hit_test_visible(is_hit_test_visible).check();
172 }
173 }
174}