Skip to main content

ferroui_diagnostics/diagnostics/views/
main_view.rs

1//! Port of `Diagnostics/Views/MainView.xaml.cs`: the class of the document
2//! `MainView.xaml`, with the tabs of the extension pages (an addition of the
3//! port).
4
5use crate::diagnostics::view_models::MainViewModel;
6use ferroui_base::controls::NameScopeExtensions;
7use ferroui_base::input::InputElementImpl;
8use ferroui_base::interactivity::InteractiveImpl;
9use ferroui_base::layout::LayoutableImpl;
10use ferroui_base::metadata::{from_markup_value, into_markup_value};
11use ferroui_base::{
12    ferro_class, ferro_class_info, ferro_impl_classes, instantiate, FerroObjectImpl, Ref,
13    StyledElementImpl, StyledElementImplExt, VisualImpl,
14};
15use ferroui_markup::data::Binding;
16use ferroui_controls::primitives::TemplatedControlImpl;
17use ferroui_controls::{ContentControl, TabControl, TabItem};
18use ferroui_controls::{ContentControlImpl, Control, ControlImpl, UserControl};
19use std::cell::RefCell;
20use std::rc::Rc;
21
22/// The main view of the developer tools: the menu, the tabs of the pages,
23/// the content of the selected tab and the status bar.
24#[repr(C)]
25pub struct MainView {
26    base: UserControl,
27    /// Addition of the port: the tabs of the extension pages of the view
28    /// model, after the tabs of the document.
29    extension_tabs: RefCell<Vec<Ref<TabItem>>>,
30}
31
32ferro_class!(MainView: UserControl);
33ferro_impl_classes!(
34    MainView: FerroObjectImpl,
35    VisualImpl,
36    LayoutableImpl,
37    InteractiveImpl,
38    InputElementImpl,
39    ControlImpl,
40    TemplatedControlImpl,
41    ContentControlImpl
42);
43ferro_class_info!(MainView { new: MainView::new });
44
45impl StyledElementImpl for MainView {
46    /// Addition of the port: the tabs of the extension pages follow the view
47    /// model.
48    fn on_data_context_changed(this: &Self) {
49        Self::parent_on_data_context_changed(this);
50        this.update_extension_tabs();
51    }
52}
53
54impl MainView {
55    /// The URI of the document of the class.
56    pub const DOCUMENT_URI: &'static str = "ferres://FerroUI.Diagnostics/diagnostics/views/MainView.xaml";
57
58    pub fn construct() -> Self {
59        Self { base: UserControl::construct(), extension_tabs: RefCell::new(Vec::new()) }
60    }
61
62    /// Creates the view and populates it from its document
63    /// (`InitializeComponent()`: the compiled markup of the document, which
64    /// the build of the crate generates).
65    ///
66    /// # Panics
67    /// Panics when the document fails to load (an exception of the
68    /// constructor in the original).
69    pub fn new() -> Ref<Self> {
70        crate::register_types();
71        let this = instantiate(Self::construct());
72        Self::initialize_component(&this);
73        this
74    }
75
76    /// `InitializeComponent()`.
77    fn initialize_component(this: &Ref<Self>) {
78        if let Err(error) = crate::compiled_diagnostics_views_main_view::populate(None, this) {
79            panic!("{}: {error}", Self::DOCUMENT_URI);
80        }
81    }
82
83    /// Addition of the port: replaces the tabs of the extension pages with
84    /// a tab for each extension page of the view model, in order, after the
85    /// tabs of the document (the tab of the page at index `i` is the tab
86    /// `MainViewModel::FIRST_EXTENSION_TAB + i`).
87    fn update_extension_tabs(&self) {
88        let Some(tab_control) = NameScopeExtensions::find_name_scope(self)
89            .and_then(|name_scope| name_scope.find_as::<TabControl>("tabControl"))
90        else {
91            // The document is not populated yet.
92            return;
93        };
94        let items = tab_control.items();
95
96        let old_tabs: Vec<Ref<TabItem>> = self.extension_tabs.borrow_mut().drain(..).collect();
97        for tab in old_tabs {
98            items.remove(&Some(Control::boxed(tab)));
99        }
100
101        let Some(view_model) = from_markup_value::<Rc<MainViewModel>>(&self.data_context()) else {
102            return;
103        };
104
105        let mut tabs = Vec::new();
106        for page in view_model.extension_pages() {
107            let tab = TabItem::new();
108            tab.set_header(into_markup_value(page.title().to_string()));
109            // The content of the view model, as the tabs of the document.
110            let _ = tab.bind_binding(ContentControl::content_property().as_property(), &*Binding::with_path("Content"));
111            items.add(Some(Control::boxed(tab.clone())));
112            tabs.push(tab);
113        }
114        *self.extension_tabs.borrow_mut() = tabs;
115    }
116}