freya-webview 0.4.0-rc.13

WebView support for Freya using WRY
use freya_core::{
    events::data::{
        Event,
        SizedEventData,
    },
    prelude::*,
};

use crate::{
    element::webview,
    lifecycle::WebViewLifecycleEvent,
    prelude::{
        WebViewConfig,
        WebViewId,
    },
    registry::WebViewCallback,
};

#[derive(Clone)]
pub struct WebView {
    webview_id: WebViewId,
    url: String,
    close_on_drop: bool,
    on_created: Option<WebViewCallback>,
    layout: LayoutData,
}

impl PartialEq for WebView {
    fn eq(&self, other: &Self) -> bool {
        self.webview_id == other.webview_id
            && self.url == other.url
            && self.close_on_drop == other.close_on_drop
            && match (&self.on_created, &other.on_created) {
                (None, None) => true,
                (Some(a), Some(b)) => std::sync::Arc::ptr_eq(a, b),
                _ => false,
            }
            && self.layout == other.layout
    }
}

impl WebView {
    pub fn new(url: impl Into<String>) -> Self {
        Self {
            webview_id: WebViewId::new(),
            url: url.into(),
            close_on_drop: true,
            on_created: None,
            layout: LayoutData::default(),
        }
    }

    pub fn url(mut self, url: impl Into<String>) -> Self {
        self.url = url.into();
        self
    }

    pub fn id(mut self, id: WebViewId) -> Self {
        self.webview_id = id;
        self
    }

    /// If you decide to not close the webview on drop you will need to manually close it with [WebViewManager::close](crate::lifecycle::WebViewManager::close).
    pub fn close_on_drop(mut self, close: bool) -> Self {
        self.close_on_drop = close;
        self
    }

    pub fn on_created(
        mut self,
        on_created: impl Fn(wry::WebViewBuilder) -> wry::WebViewBuilder + Send + 'static,
    ) -> Self {
        self.on_created = Some(WebViewCallback::new(Box::new(on_created)));
        self
    }
}

impl LayoutExt for WebView {
    fn get_layout(&mut self) -> &mut LayoutData {
        &mut self.layout
    }
}
impl ContainerExt for WebView {}

const WEBVIEW_CONTEXT_ERROR: &str = "
Error: Make sure to register the WebViewPlugin in your LaunchConfig:

LaunchConfig::new()
    .with_plugin(WebViewPlugin::new())
";

impl Component for WebView {
    fn render(&self) -> impl IntoElement {
        let events = try_consume_root_context::<crate::lifecycle::WebViewEvents>()
            .expect(WEBVIEW_CONTEXT_ERROR);

        let webview_id = self.webview_id;
        let url = self.url.clone();
        let close_on_drop = self.close_on_drop;
        let on_created = self.on_created.clone();

        let config = WebViewConfig {
            url: url.clone(),
            transparent: false,
            user_agent: None,
            on_created,
        };

        use_drop({
            let events = events.clone();
            move || {
                events.lock().unwrap().push(if close_on_drop {
                    WebViewLifecycleEvent::Close { id: webview_id }
                } else {
                    WebViewLifecycleEvent::Hide { id: webview_id }
                });
            }
        });

        webview(&url)
            .layout(self.layout.clone())
            .on_sized(move |event: Event<SizedEventData>| {
                events.lock().unwrap().push(WebViewLifecycleEvent::Resized {
                    id: webview_id,
                    area: event.area,
                    config: config.clone(),
                });
                Platform::get().send(UserEvent::RequestRedraw);
            })
    }

    fn render_key(&self) -> DiffKey {
        DiffKey::from(&self.webview_id)
    }
}