Skip to main content

ferroui_controls/embedding/
embeddable_control_root.rs

1use crate::platform::{ITopLevelImpl, PlatformManager};
2use crate::primitives::TemplatedControlImpl;
3use crate::{ContentControlImpl, ControlImpl, TopLevel, TopLevelImpl};
4use ferroui_base::input::{IFocusScope, InputElementImpl};
5use ferroui_base::interactivity::InteractiveImpl;
6use ferroui_base::layout::{ILayoutManager, LayoutableImpl, LayoutableImplExt};
7use ferroui_base::{
8    ferro_class, ferro_impl_classes, instantiate, FerroObjectImpl, Ref, Size, StaticType, StyledElementImpl, TypeInfo,
9    VisualImpl,
10};
11use std::cell::Cell;
12use std::rc::Rc;
13
14/// A top-level that hosts a control tree inside a surface provided by
15/// someone else: a native control of another toolkit, a single-view
16/// platform, an offscreen buffer.
17#[repr(C)]
18pub struct EmbeddableControlRoot {
19    base: TopLevel,
20    enforce_client_size: Cell<bool>,
21}
22
23ferro_class! {
24    EmbeddableControlRoot: TopLevel, virtuals EmbeddableControlRootImpl: TopLevelImpl {
25        /// Releases the platform implementation and tears the root down.
26        fn dispose(this);
27    }
28}
29ferroui_base::ferro_class_info!(EmbeddableControlRoot { new: EmbeddableControlRoot::new });
30
31ferro_impl_classes!(
32    EmbeddableControlRoot: FerroObjectImpl,
33    VisualImpl,
34    InteractiveImpl,
35    ContentControlImpl,
36    TopLevelImpl
37);
38
39impl ControlImpl for EmbeddableControlRoot {
40    fn on_create_automation_peer(this: &Self) -> Ref<crate::automation::peers::AutomationPeer> {
41        crate::automation::peers::EmbeddableControlRootAutomationPeer::new(this).upcast()
42    }
43}
44
45impl TemplatedControlImpl for EmbeddableControlRoot {
46    fn on_apply_template(this: &Self, e: &crate::primitives::TemplateAppliedEventArgs) {
47        use crate::primitives::TemplatedControlImplExt;
48        Self::parent_on_apply_template(this, e);
49        this.enable_visual_layer_manager_layers();
50    }
51}
52
53impl IFocusScope for EmbeddableControlRoot {}
54
55impl InputElementImpl for EmbeddableControlRoot {
56    fn is_focus_scope(_this: &Self) -> bool {
57        true
58    }
59}
60
61impl StyledElementImpl for EmbeddableControlRoot {
62    fn style_key_override(_this: &Self) -> &'static TypeInfo {
63        <EmbeddableControlRoot as StaticType>::TYPE
64    }
65}
66
67impl LayoutableImpl for EmbeddableControlRoot {
68    fn measure_override(this: &Self, mut available_size: Size) -> Size {
69        if this.enforce_client_size.get() {
70            available_size = this.platform_impl().map(|platform_impl| platform_impl.client_size()).unwrap_or_default();
71        }
72        let rv = Self::parent_measure_override(this, available_size);
73        if this.enforce_client_size.get() {
74            return available_size;
75        }
76        rv
77    }
78}
79
80impl EmbeddableControlRootImpl for EmbeddableControlRoot {
81    fn dispose(this: &Self) {
82        if let Some(platform_impl) = this.platform_impl() {
83            platform_impl.dispose();
84        }
85        this.ensure_closed();
86    }
87}
88
89impl EmbeddableControlRoot {
90    /// Field initialisation of a root over a platform implementation.
91    pub fn construct(platform_impl: Rc<dyn ITopLevelImpl>) -> Self {
92        Self { base: TopLevel::construct(platform_impl), enforce_client_size: Cell::new(true) }
93    }
94
95    /// Creates a root over a platform implementation.
96    pub fn with_impl(platform_impl: Rc<dyn ITopLevelImpl>) -> Ref<Self> {
97        instantiate(Self::construct(platform_impl))
98    }
99
100    /// Creates a root over an embeddable top-level of the windowing
101    /// platform.
102    pub fn new() -> Ref<Self> {
103        Self::with_impl(PlatformManager::create_embeddable_top_level())
104    }
105
106    /// Whether the root is always measured with, and sized to, the client
107    /// size of the platform implementation (protected upstream: for
108    /// derived classes).
109    pub fn enforce_client_size(&self) -> bool {
110        self.enforce_client_size.get()
111    }
112
113    pub fn set_enforce_client_size(&self, value: bool) {
114        self.enforce_client_size.set(value)
115    }
116
117    /// Initializes the root, applies its template and runs the initial
118    /// layout pass.
119    pub fn prepare(&self) {
120        self.ensure_initialized();
121        self.apply_template();
122        self.layout_manager().execute_initial_layout_pass();
123    }
124
125    fn ensure_initialized(&self) {
126        if !self.is_initialized() {
127            self.begin_init();
128            self.end_init();
129        }
130    }
131}