use crate::helpers::ComResultExt;
use crate::interop::*;
use crate::top_level_impl::{
top_level_of, impl_top_level_contract, MacOSTopLevelHandle, TopLevelEvents, TopLevelImpl, TopLevelParent,
};
use crate::window_impl::WindowImpl;
use crate::window_impl_base::{impl_window_base_contract, WindowBaseImpl, WindowBaseParent, WindowEventsParent};
use ferroui_base::{PixelPoint, Size};
use ferroui_controls::platform::{IPopupImpl, ITopLevelImpl, IWindowBaseImpl, PlatformThemeVariant};
use ferroui_controls::primitives::popup_positioning::{
IPopupPositioner, ManagedPopupPositioner, ManagedPopupPositionerPopupImplHelper,
};
use ferroui_controls::WindowResizeReason;
use ferroui_microcom::ComPtr;
use std::cell::RefCell;
use std::rc::{Rc, Weak};
pub struct PopupImpl {
weak_self: Weak<PopupImpl>,
base: WindowBaseImpl,
parent: Rc<dyn ITopLevelImpl>,
native: RefCell<Option<ComPtr<IFrnPopup>>>,
popup_positioner: RefCell<Option<Rc<dyn IPopupPositioner>>>,
}
fn root_parent(mut parent: Rc<dyn ITopLevelImpl>) -> Rc<dyn ITopLevelImpl> {
loop {
let next = match parent.as_any().downcast_ref::<PopupImpl>() {
Some(popup) => popup.parent.clone(),
None => return parent,
};
parent = next;
}
}
impl PopupImpl {
pub(crate) fn new(factory: ComPtr<IFerroNativeFactory>, parent: Rc<dyn ITopLevelImpl>) -> Rc<PopupImpl> {
let this = Rc::new_cyclic(|weak_self| PopupImpl {
weak_self: weak_self.clone(),
base: WindowBaseImpl::new(factory.clone()),
parent: parent.clone(),
native: RefCell::new(None),
popup_positioner: RefCell::new(None),
});
let e = IFrnWindowEvents::from_impl(TopLevelEvents(this.clone()));
let native = factory.create_popup(Some(&e)).check().expect("the native popup");
*this.native.borrow_mut() = Some(native.clone());
this.base.init(MacOSTopLevelHandle::from_window_base(ComPtr::<IFrnWindowBase>::from_ref(&native)));
let weak = Rc::downgrade(&this);
let positioner_helper = ManagedPopupPositionerPopupImplHelper::new(
parent.clone(),
Rc::new(move |position, size, scaling| {
if let Some(this) = weak.upgrade() {
this.move_resize(position, size, scaling);
}
}),
);
let positioner: Rc<dyn IPopupPositioner> = Rc::new(ManagedPopupPositioner::new(Rc::new(positioner_helper)));
*this.popup_positioner.borrow_mut() = Some(positioner);
let parent = root_parent(parent);
if let Some(window) = parent.as_any().downcast_ref::<WindowImpl>() {
let parent_native = window.window_base().native();
native.set_parent(parent_native.as_deref()).check();
}
if let Some(parent_top_level) = top_level_of(&*parent) {
this.top_level().set_input_method(parent_top_level.input_method());
}
this
}
pub(crate) fn top_level(&self) -> &Rc<TopLevelImpl> {
self.base.top_level()
}
fn move_resize(&self, position: PixelPoint, size: Size, _scaling: f64) {
self.base.set_position(position);
self.base.resize(size, WindowResizeReason::Layout);
}
pub fn native(&self) -> Option<ComPtr<IFrnPopup>> {
self.native.borrow().clone()
}
}
impl TopLevelParent for PopupImpl {
fn top_level(&self) -> &Rc<TopLevelImpl> {
self.base.top_level()
}
fn dispose_top_level(&self) {
if let Some(native) = self.base.native() {
native.set_parent(None).check();
}
self.base.dispose();
let native = self.native.borrow_mut().take();
drop(native);
}
fn create_popup_core(&self) -> Option<Rc<dyn IPopupImpl>> {
let this: Rc<dyn ITopLevelImpl> = self.weak_self.upgrade()?;
Some(PopupImpl::new(self.top_level().factory().clone(), this))
}
fn set_frame_theme_variant_core(&self, theme_variant: Option<PlatformThemeVariant>) {
self.base.set_frame_theme_variant(theme_variant);
}
}
impl WindowBaseParent for PopupImpl {
fn window_base(&self) -> &WindowBaseImpl {
&self.base
}
}
impl WindowEventsParent for PopupImpl {
fn on_closing(&self) -> bool {
true
}
fn on_window_state_changed(&self, _state: FrnWindowState) {}
fn on_got_input_when_disabled(&self) {
}
}
impl_top_level_contract!(PopupImpl {
fn as_window_base_impl(&self) -> Option<&dyn IWindowBaseImpl> {
Some(self)
}
fn as_popup_impl(&self) -> Option<&dyn IPopupImpl> {
Some(self)
}
});
impl_window_base_contract!(PopupImpl);
impl IPopupImpl for PopupImpl {
fn popup_positioner(&self) -> Option<Rc<dyn IPopupPositioner>> {
self.popup_positioner.borrow().clone()
}
fn set_window_manager_add_shadow_hint(&self, _enabled: bool) {}
fn take_focus(&self) {
let parent = root_parent(self.parent.clone());
if let Some(window) = parent.as_any().downcast_ref::<WindowImpl>() {
if let Some(native) = window.native() {
native.take_focus_from_children().check();
}
}
}
fn set_hit_test_visible(&self, is_hit_test_visible: bool) {
if let Some(native) = self.native() {
native.set_hit_test_visible(is_hit_test_visible).check();
}
}
}