freya_webview/
element.rs

1//! WebView element for rendering web content in Freya.
2
3use std::{
4    any::Any,
5    borrow::Cow,
6    collections::HashMap,
7    rc::Rc,
8};
9
10use freya_core::{
11    data::{
12        AccessibilityData,
13        EffectData,
14        LayoutData,
15        StyleState,
16        TextStyleData,
17    },
18    diff_key::DiffKey,
19    element::{
20        Element,
21        ElementExt,
22        EventHandlerType,
23    },
24    events::name::EventName,
25    layers::Layer,
26    prelude::{
27        AccessibilityExt,
28        ContainerExt,
29        EventHandlersExt,
30        KeyExt,
31        LayerExt,
32        LayoutExt,
33        MaybeExt,
34    },
35    tree::DiffModifies,
36};
37use rustc_hash::FxHashMap;
38
39use crate::registry::{
40    WebViewConfig,
41    WebViewId,
42};
43
44/// WebView element data.
45#[derive(Clone)]
46pub struct WebViewElement {
47    pub accessibility: AccessibilityData,
48    pub layout: LayoutData,
49    pub event_handlers: FxHashMap<EventName, EventHandlerType>,
50    pub webview_id: WebViewId,
51    pub config: WebViewConfig,
52    pub relative_layer: Layer,
53    pub effect: Option<EffectData>,
54}
55
56impl PartialEq for WebViewElement {
57    fn eq(&self, other: &Self) -> bool {
58        self.accessibility == other.accessibility
59            && self.layout == other.layout
60            && self.webview_id == other.webview_id
61            && self.config == other.config
62            && self.relative_layer == other.relative_layer
63            && self.effect == other.effect
64    }
65}
66
67impl ElementExt for WebViewElement {
68    fn changed(&self, other: &Rc<dyn ElementExt>) -> bool {
69        let Some(webview) = (other.as_ref() as &dyn Any).downcast_ref::<WebViewElement>() else {
70            return false;
71        };
72        self != webview
73    }
74
75    fn diff(&self, other: &Rc<dyn ElementExt>) -> DiffModifies {
76        let Some(webview) = (other.as_ref() as &dyn Any).downcast_ref::<WebViewElement>() else {
77            return DiffModifies::all();
78        };
79
80        let mut diff = DiffModifies::empty();
81
82        if self.accessibility != webview.accessibility {
83            diff.insert(DiffModifies::ACCESSIBILITY);
84        }
85
86        if self.relative_layer != webview.relative_layer {
87            diff.insert(DiffModifies::LAYER);
88        }
89
90        if self.layout != webview.layout || self.config != webview.config {
91            diff.insert(DiffModifies::LAYOUT);
92        }
93
94        if self.effect != webview.effect {
95            diff.insert(DiffModifies::EFFECT);
96        }
97
98        diff
99    }
100
101    fn layout(&'_ self) -> Cow<'_, LayoutData> {
102        Cow::Borrowed(&self.layout)
103    }
104
105    fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
106        self.effect.as_ref().map(Cow::Borrowed)
107    }
108
109    fn style(&'_ self) -> Cow<'_, StyleState> {
110        Cow::Owned(StyleState::default())
111    }
112
113    fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
114        Cow::Owned(TextStyleData::default())
115    }
116
117    fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
118        Cow::Borrowed(&self.accessibility)
119    }
120
121    fn layer(&self) -> Layer {
122        self.relative_layer
123    }
124
125    fn should_measure_inner_children(&self) -> bool {
126        false
127    }
128
129    fn events_handlers(&'_ self) -> Option<Cow<'_, FxHashMap<EventName, EventHandlerType>>> {
130        Some(Cow::Borrowed(&self.event_handlers))
131    }
132}
133
134impl From<WebView> for Element {
135    fn from(value: WebView) -> Self {
136        Element::Element {
137            key: value.key,
138            element: Rc::new(value.element),
139            elements: vec![],
140        }
141    }
142}
143
144/// WebView element builder.
145///
146/// Use [`webview()`] to create a new WebView element.
147pub struct WebView {
148    pub(crate) key: DiffKey,
149    pub(crate) element: WebViewElement,
150}
151
152impl WebView {
153    /// Try to downcast an ElementExt to a WebViewElement.
154    pub fn try_downcast(element: &dyn ElementExt) -> Option<WebViewElement> {
155        (element as &dyn Any)
156            .downcast_ref::<WebViewElement>()
157            .cloned()
158    }
159
160    /// Set the URL to load in the WebView.
161    pub fn url(mut self, url: impl Into<String>) -> Self {
162        self.element.config.url = url.into();
163        self
164    }
165
166    /// Set whether the WebView background should be transparent.
167    pub fn transparent(mut self, transparent: bool) -> Self {
168        self.element.config.transparent = transparent;
169        self
170    }
171
172    /// Set a custom user agent string.
173    pub fn user_agent(mut self, user_agent: impl Into<String>) -> Self {
174        self.element.config.user_agent = Some(user_agent.into());
175        self
176    }
177}
178
179impl KeyExt for WebView {
180    fn write_key(&mut self) -> &mut DiffKey {
181        &mut self.key
182    }
183}
184
185impl EventHandlersExt for WebView {
186    fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType> {
187        &mut self.element.event_handlers
188    }
189}
190
191impl LayoutExt for WebView {
192    fn get_layout(&mut self) -> &mut LayoutData {
193        &mut self.element.layout
194    }
195}
196
197impl ContainerExt for WebView {}
198
199impl AccessibilityExt for WebView {
200    fn get_accessibility_data(&mut self) -> &mut AccessibilityData {
201        &mut self.element.accessibility
202    }
203}
204
205impl MaybeExt for WebView {}
206
207impl LayerExt for WebView {
208    fn get_layer(&mut self) -> &mut Layer {
209        &mut self.element.relative_layer
210    }
211}
212
213pub fn webview(url: impl Into<String>) -> WebView {
214    let mut accessibility = AccessibilityData::default();
215    accessibility.builder.set_role(accesskit::Role::WebView);
216
217    let webview_id = WebViewId::new();
218    let config = WebViewConfig {
219        url: url.into(),
220        transparent: false,
221        user_agent: None,
222        on_created: None,
223    };
224
225    WebView {
226        key: DiffKey::None,
227        element: WebViewElement {
228            accessibility,
229            layout: LayoutData::default(),
230            event_handlers: HashMap::default(),
231            webview_id,
232            config,
233            relative_layer: Layer::default(),
234            effect: None,
235        },
236    }
237}