ferroui_browser/
ferro_view.rs1use crate::browser_top_level_impl::BrowserTopLevelImpl;
2use crate::interop::{dom_helper, input_helper, JsObject};
3use crate::windowing_platform::BrowserWindowingPlatform;
4use ferroui_base::input::{FocusChangedEventArgs, InputElement};
5use ferroui_base::interactivity::Interactive;
6use ferroui_base::Ref;
7use ferroui_controls::embedding::EmbeddableControlRoot;
8use ferroui_controls::platform::ITopLevelImpl;
9use ferroui_controls::{Control, TopLevel};
10use std::rc::Rc;
11
12pub struct FerroView {
14 top_level: Ref<EmbeddableControlRoot>,
15}
16
17impl FerroView {
18 pub fn new(div_id: &str) -> Rc<Self> {
26 match dom_helper::get_element_by_id(div_id, &BrowserWindowingPlatform::global_this()) {
27 Some(host) => Self::with_host(host),
28 None => panic!("Element with id '{div_id}' was not found in the html document."),
29 }
30 }
31
32 pub fn with_host(host: JsObject) -> Rc<Self> {
40 if host.is_null() || host.is_undefined() {
41 panic!("Value cannot be null. (Parameter 'host')");
42 }
43
44 let host_content = match dom_helper::create_ferro_host(&host) {
45 Some(host_content) => host_content,
46 None => panic!("The host of the view wasn't initialized."),
47 };
48
49 let native_controls_container = match host_content.native_host() {
50 Some(native_host) => native_host,
51 None => panic!("NativeHost cannot be null"),
52 };
53 let input_element = match host_content.input_element() {
54 Some(input_element) => input_element,
55 None => panic!("InputElement cannot be null"),
56 };
57
58 let browser_top_level = BrowserTopLevelImpl::new(host.clone(), native_controls_container, input_element);
59 let surface = browser_top_level.surface();
60 let top_level_impl: Rc<dyn ITopLevelImpl> = browser_top_level;
61 let top_level = EmbeddableControlRoot::with_impl(top_level_impl);
62
63 top_level.prepare();
64 top_level.add_handler(InputElement::got_focus_event(), {
65 let host = host.clone();
66 move |_: &Interactive, _: &FocusChangedEventArgs| input_helper::focus_element(&host)
67 });
68 top_level.renderer().start(); if let Some(surface) = surface {
78 surface.shared().on_first_frame(Box::new(move || {
79 let splash = dom_helper::get_elements_by_class_name("ferroui-splash", &host).or_else(|| {
82 dom_helper::get_element_by_id("ferroui-splash", &BrowserWindowingPlatform::global_this())
83 });
84 if let Some(splash) = splash {
85 dom_helper::add_css_class(&splash, "splash-close");
86 }
87 }));
88 }
89
90 Rc::new(Self { top_level })
91 }
92
93 pub fn content(&self) -> Option<Ref<Control>> {
99 let content = self.top_level.content()?;
100 match Control::from_boxed(&content) {
101 Some(control) => Some(control),
102 None => panic!("Unable to cast the content of the view to a control."),
103 }
104 }
105
106 pub fn set_content(&self, value: Option<Ref<Control>>) {
108 self.top_level.set_content(value.map(Control::boxed));
109 }
110
111 pub(crate) fn top_level(&self) -> Ref<TopLevel> {
112 self.top_level.clone().upcast()
113 }
114
115 pub fn dispose(&self) {
126 self.top_level.dispose();
127 }
128}