Skip to main content

ferroui_controls/presentation_source/
presentation_source_layout.rs

1use super::PresentationSource;
2use ferroui_base::layout::{ILayoutManager, ILayoutRoot, LayoutManager, Layoutable};
3use ferroui_base::reactive::IDisposable;
4use ferroui_base::rendering::{RendererDebugOverlays, RendererDiagnostics};
5use ferroui_base::Ref;
6use std::cell::{Cell, RefCell};
7use std::rc::{Rc, Weak};
8
9impl PresentationSource {
10    /// The scaling factor to use in layout.
11    pub fn layout_scaling(&self) -> f64 {
12        self.render_scaling()
13    }
14
15    /// The layout manager of the tree.
16    pub fn layout_manager(&self) -> &Rc<LayoutManager> {
17        &self.layout_manager
18    }
19
20    pub(super) fn create_layout_manager(owner: Weak<dyn ILayoutRoot>) -> Rc<LayoutManager> {
21        LayoutManager::new(owner)
22    }
23
24    /// The second half of C# `CreateLayoutManager`: the bridge from the
25    /// layout manager to the diagnostics of the renderer. The layout manager
26    /// is created with the source, before the renderer exists, so the bridge
27    /// is created once the renderer has been.
28    pub(super) fn create_layout_diagnostic_bridge(&self) {
29        let bridge = LayoutDiagnosticBridge::new(self.typed_renderer().diagnostics(), &self.layout_manager);
30        bridge.setup_bridge();
31        *self.layout_diagnostic_bridge.borrow_mut() = Some(bridge);
32    }
33}
34
35impl ILayoutRoot for PresentationSource {
36    fn layout_scaling(&self) -> f64 {
37        PresentationSource::layout_scaling(self)
38    }
39
40    fn layout_manager(&self) -> Rc<dyn ILayoutManager> {
41        self.layout_manager.clone()
42    }
43
44    fn root_visual(&self) -> Ref<Layoutable> {
45        self.root_element().upcast()
46    }
47}
48
49/// Provides layout pass timing from the layout manager to the renderer, for
50/// diagnostics purposes.
51pub(super) struct LayoutDiagnosticBridge {
52    state: Rc<BridgeState>,
53    property_changed_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
54}
55
56struct BridgeState {
57    diagnostics: Rc<RendererDiagnostics>,
58    layout_manager: Weak<LayoutManager>,
59    is_handling: Cell<bool>,
60}
61
62impl LayoutDiagnosticBridge {
63    pub fn new(diagnostics: Rc<RendererDiagnostics>, layout_manager: &Rc<LayoutManager>) -> Self {
64        let state = Rc::new(BridgeState {
65            diagnostics: diagnostics.clone(),
66            layout_manager: Rc::downgrade(layout_manager),
67            is_handling: Cell::new(false),
68        });
69
70        let weak = Rc::downgrade(&state);
71        let subscription = diagnostics.property_changed(move |_, property_name| {
72            if let Some(state) = weak.upgrade() {
73                state.on_diagnostics_property_changed(property_name);
74            }
75        });
76
77        Self { state, property_changed_subscription: RefCell::new(Some(subscription)) }
78    }
79
80    pub fn setup_bridge(&self) {
81        self.state.setup_bridge();
82    }
83
84    pub fn dispose(&self) {
85        if let Some(subscription) = self.property_changed_subscription.borrow_mut().take() {
86            subscription.dispose();
87        }
88        if let Some(layout_manager) = self.state.layout_manager.upgrade() {
89            layout_manager.set_layout_pass_timed(None);
90        }
91    }
92}
93
94impl BridgeState {
95    fn setup_bridge(&self) {
96        let needs_handling = self.diagnostics.debug_overlays().contains(RendererDebugOverlays::LAYOUT_TIME_GRAPH);
97        if needs_handling != self.is_handling.get() {
98            self.is_handling.set(needs_handling);
99            let Some(layout_manager) = self.layout_manager.upgrade() else { return };
100            layout_manager.set_layout_pass_timed(if needs_handling {
101                let diagnostics = Rc::downgrade(&self.diagnostics);
102                Some(Rc::new(move |timing| {
103                    if let Some(diagnostics) = diagnostics.upgrade() {
104                        diagnostics.set_last_layout_pass_timing(timing);
105                    }
106                }))
107            } else {
108                None
109            });
110        }
111    }
112
113    fn on_diagnostics_property_changed(&self, property_name: &str) {
114        if property_name == RendererDiagnostics::DEBUG_OVERLAYS_PROPERTY_NAME {
115            self.setup_bridge();
116        }
117    }
118}