use super::{AdornerLayer, LightDismissOverlayLayer, OverlayLayer, PopupOverlayLayer, TextSelectorLayer};
use crate::{Control, ControlImpl, Decorator, Panel};
use ferroui_base::controls::ResourcesChangedEventArgs;
use ferroui_base::input::InputElementImpl;
use ferroui_base::interactivity::InteractiveImpl;
use ferroui_base::layout::{LayoutableImpl, LayoutableImplExt};
use ferroui_base::logical_tree::LogicalTreeAttachmentEventArgs;
use ferroui_base::{
ferro_class, ferro_impl_classes, instantiate, FerroObjectImpl, ObjectType, Rect, Ref, Size, StyledElement,
StyledElementImpl, StyledElementImplExt, VisualImpl, WeakRef,
};
use std::cell::{Cell, RefCell};
const ADORNER_Z_INDEX: i32 = i32::MAX - 100;
const OVERLAY_Z_INDEX: i32 = i32::MAX - 98;
const LIGHT_DISMISS_OVERLAY_Z_INDEX: i32 = i32::MAX - 97;
const TEXT_SELECTOR_LAYER_Z_INDEX: i32 = i32::MAX - 96;
const POPUP_OVERLAY_Z_INDEX: i32 = i32::MAX - 95;
#[repr(C)]
pub struct VisualLayerManager {
base: Decorator,
logical_root: RefCell<Option<WeakRef<StyledElement>>>,
layers: RefCell<Vec<Ref<Control>>>,
overlay_layer: RefCell<Option<Ref<OverlayLayer>>>,
enable_adorner_layer: Cell<bool>,
enable_overlay_layer: Cell<bool>,
enable_popup_overlay_layer: Cell<bool>,
enable_text_selector_layer: Cell<bool>,
}
ferro_class!(VisualLayerManager: Decorator);
ferroui_base::ferro_class_info!(VisualLayerManager { new: VisualLayerManager::new });
ferro_impl_classes!(VisualLayerManager: FerroObjectImpl, VisualImpl, InteractiveImpl, InputElementImpl, ControlImpl);
impl StyledElementImpl for VisualLayerManager {
fn notify_child_resources_changed(this: &Self, e: ResourcesChangedEventArgs) {
for layer in this.layers_snapshot() {
layer.notify_resources_changed(e, true);
}
Self::parent_notify_child_resources_changed(this, e);
}
fn on_attached_to_logical_tree(this: &Self, e: &LogicalTreeAttachmentEventArgs) {
Self::parent_on_attached_to_logical_tree(this, e);
*this.logical_root.borrow_mut() = Some(e.root().downgrade());
for layer in this.layers_snapshot() {
layer.notify_attached_to_logical_tree(e);
}
}
fn on_detached_from_logical_tree(this: &Self, e: &LogicalTreeAttachmentEventArgs) {
*this.logical_root.borrow_mut() = None;
Self::parent_on_detached_from_logical_tree(this, e);
for layer in this.layers_snapshot() {
layer.notify_detached_from_logical_tree(e);
}
}
}
impl LayoutableImpl for VisualLayerManager {
fn measure_override(this: &Self, available_size: Size) -> Size {
for layer in this.layers_snapshot() {
layer.measure(available_size);
}
Self::parent_measure_override(this, available_size)
}
fn arrange_override(this: &Self, final_size: Size) -> Size {
for layer in this.layers_snapshot() {
layer.arrange(Rect::from_size(final_size));
}
Self::parent_arrange_override(this, final_size)
}
}
impl VisualLayerManager {
pub fn construct() -> Self {
Self {
base: Decorator::construct(),
logical_root: RefCell::new(None),
layers: RefCell::new(Vec::new()),
overlay_layer: RefCell::new(None),
enable_adorner_layer: Cell::new(true),
enable_overlay_layer: Cell::new(false),
enable_popup_overlay_layer: Cell::new(false),
enable_text_selector_layer: Cell::new(false),
}
}
pub fn new() -> Ref<Self> {
instantiate(Self::construct())
}
pub fn enable_adorner_layer(&self) -> bool {
self.enable_adorner_layer.get()
}
pub fn set_enable_adorner_layer(&self, value: bool) {
self.enable_adorner_layer.set(value)
}
pub fn enable_overlay_layer(&self) -> bool {
self.enable_overlay_layer.get()
}
pub fn set_enable_overlay_layer(&self, value: bool) {
self.enable_overlay_layer.set(value)
}
pub fn enable_popup_overlay_layer(&self) -> bool {
self.enable_popup_overlay_layer.get()
}
pub fn set_enable_popup_overlay_layer(&self, value: bool) {
self.enable_popup_overlay_layer.set(value)
}
pub fn enable_text_selector_layer(&self) -> bool {
self.enable_text_selector_layer.get()
}
pub fn set_enable_text_selector_layer(&self, value: bool) {
self.enable_text_selector_layer.set(value)
}
pub fn adorner_layer(&self) -> Option<Ref<AdornerLayer>> {
if !self.enable_adorner_layer() {
return None;
}
let mut rv = self.find_layer::<AdornerLayer>();
if rv.is_none() {
let layer = AdornerLayer::new();
self.add_layer(layer.clone().upcast(), ADORNER_Z_INDEX);
rv = Some(layer);
}
rv
}
pub fn popup_overlay_layer(&self) -> Option<Ref<PopupOverlayLayer>> {
if !self.enable_popup_overlay_layer() {
return None;
}
let mut rv = self.find_layer::<PopupOverlayLayer>();
if rv.is_none() {
let layer = PopupOverlayLayer::new();
self.add_layer(layer.clone().upcast(), POPUP_OVERLAY_Z_INDEX);
rv = Some(layer);
}
rv
}
pub fn overlay_layer(&self) -> Option<Ref<OverlayLayer>> {
if !self.enable_overlay_layer() {
return None;
}
let existing = self.overlay_layer.borrow().clone();
if existing.is_some() {
return existing;
}
let overlay_layer = OverlayLayer::new();
*self.overlay_layer.borrow_mut() = Some(overlay_layer.clone());
let adorner = AdornerLayer::new();
overlay_layer.set_adorner_layer(Some(adorner.clone()));
let panel = Panel::new();
panel.children().add(overlay_layer.clone());
panel.children().add(adorner);
self.add_layer(panel.upcast(), OVERLAY_Z_INDEX);
Some(overlay_layer)
}
pub fn text_selector_layer(&self) -> Option<Ref<TextSelectorLayer>> {
if !self.enable_text_selector_layer() {
return None;
}
let mut rv = self.find_layer::<TextSelectorLayer>();
if rv.is_none() {
let layer = TextSelectorLayer::new();
self.add_layer(layer.clone().upcast(), TEXT_SELECTOR_LAYER_Z_INDEX);
rv = Some(layer);
}
rv
}
pub fn light_dismiss_overlay_layer(&self) -> Ref<LightDismissOverlayLayer> {
match self.find_layer::<LightDismissOverlayLayer>() {
Some(rv) => rv,
None => {
let rv = LightDismissOverlayLayer::new();
self.add_layer(rv.clone().upcast(), LIGHT_DISMISS_OVERLAY_Z_INDEX);
rv
}
}
}
fn layers_snapshot(&self) -> Vec<Ref<Control>> {
self.layers.borrow().clone()
}
fn find_layer<T: ObjectType>(&self) -> Option<Ref<T>> {
self.layers.borrow().iter().find_map(|layer| layer.cast::<T>())
}
fn add_layer(&self, layer: Ref<Control>, z_index: i32) {
self.layers.borrow_mut().push(layer.clone());
layer.set_parent(self.to_ref());
layer.set_z_index(z_index);
self.visual_children().add(layer.clone().upcast());
if self.is_attached_to_logical_tree() {
let logical_root = self
.logical_root
.borrow()
.as_ref()
.and_then(WeakRef::upgrade)
.expect("an attached layer manager knows its logical root");
layer.notify_attached_to_logical_tree(&LogicalTreeAttachmentEventArgs::new(
logical_root,
layer.clone().upcast(),
Some(self.to_ref().upcast()),
));
}
self.invalidate_arrange();
}
}