winio 0.11.0

Single-threaded async GUI runtime based on compio.
Documentation
use std::ops::Deref;

use winio::prelude::*;

use crate::{Error, FailableWebView, Result};

pub struct WebViewPage {
    window: Child<TabViewItem>,
    go_button: Child<Button>,
    back_button: Child<Button>,
    forward_button: Child<Button>,
    reload_button: Child<Button>,
    can_reload: bool,
    entry: Child<Edit>,
    webview: Child<FailableWebView>,
}

impl WebViewPage {
    fn set_reload_button(&mut self, enabled: bool) -> Result<()> {
        self.back_button.set_enabled(self.webview.can_go_back()?)?;
        self.forward_button
            .set_enabled(self.webview.can_go_forward()?)?;

        self.can_reload = enabled;
        if enabled {
            self.reload_button.set_text("🔄")?;
        } else {
            self.reload_button.set_text("⏹️")?;
        }
        Ok(())
    }
}

#[derive(Debug)]
pub enum WebViewPageEvent {}

#[derive(Debug)]
pub enum WebViewPageMessage {
    Noop,
    Go,
    Back,
    Forward,
    Reload,
    Navigating,
    Navigated,
}

impl Component for WebViewPage {
    type Error = Error;
    type Event = WebViewPageEvent;
    type Init<'a> = ();
    type Message = WebViewPageMessage;

    async fn init(_init: Self::Init<'_>, sender: &ComponentSender<Self>) -> Result<Self> {
        let url = "https://www.example.com/";
        init! {
            window: TabViewItem = (()) => {
                text: "WebView",
            },
            webview: FailableWebView = (&window) => {
                source: url
            },
            go_button: Button = (&window) => {
                text: "⬇️",
            },
            back_button: Button = (&window) => {
                text: "⬅️",
                enabled: false,
            },
            forward_button: Button = (&window) => {
                text: "➡️",
                enabled: false,
            },
            reload_button: Button = (&window) => {
                text: "🔄",
            },
            entry: Edit = (&window) => {
                text: url,
            },
        }

        sender.post(WebViewPageMessage::Go);

        Ok(Self {
            window,
            go_button,
            back_button,
            forward_button,
            reload_button,
            can_reload: true,
            entry,
            webview,
        })
    }

    async fn start(&mut self, sender: &ComponentSender<Self>) -> ! {
        start! {
            sender, default: WebViewPageMessage::Noop,
            self.go_button => {
                ButtonEvent::Click => WebViewPageMessage::Go,
            },
            self.back_button => {
                ButtonEvent::Click => WebViewPageMessage::Back,
            },
            self.forward_button => {
                ButtonEvent::Click => WebViewPageMessage::Forward,
            },
            self.reload_button => {
                ButtonEvent::Click => WebViewPageMessage::Reload,
            },
            self.entry => {},
            self.webview => {
                WebViewEvent::Navigating => WebViewPageMessage::Navigating,
                WebViewEvent::Navigated => WebViewPageMessage::Navigated,
            }
        }
    }

    async fn update_children(&mut self) -> Result<bool> {
        update_children!(
            self.window,
            self.webview,
            self.go_button,
            self.back_button,
            self.forward_button,
            self.reload_button,
            self.entry
        )
    }

    async fn update(
        &mut self,
        message: Self::Message,
        _sender: &ComponentSender<Self>,
    ) -> Result<bool> {
        match message {
            WebViewPageMessage::Noop => Ok(false),
            WebViewPageMessage::Go => {
                self.webview.navigate(self.entry.text()?)?;
                self.set_reload_button(false)?;
                Ok(false)
            }
            WebViewPageMessage::Back => {
                self.webview.go_back()?;
                self.set_reload_button(false)?;
                Ok(false)
            }
            WebViewPageMessage::Forward => {
                self.webview.go_forward()?;
                self.set_reload_button(false)?;
                Ok(false)
            }
            WebViewPageMessage::Reload => {
                if self.can_reload {
                    self.webview.reload()?;
                    self.set_reload_button(false)?;
                } else {
                    self.webview.stop()?;
                    self.set_reload_button(true)?;
                }
                Ok(false)
            }
            WebViewPageMessage::Navigating => {
                self.entry.set_text(self.webview.source()?)?;
                self.set_reload_button(false)?;
                Ok(false)
            }
            WebViewPageMessage::Navigated => {
                self.entry.set_text(self.webview.source()?)?;
                self.set_reload_button(true)?;
                Ok(true)
            }
        }
    }

    fn render(&mut self, _sender: &ComponentSender<Self>) -> Result<()> {
        let csize = self.window.size()?;

        {
            let mut header_panel = layout! {
                StackPanel::new(Orient::Horizontal),
                self.back_button,
                self.forward_button,
                self.reload_button,
                self.entry => { grow: true },
                self.go_button,
            };
            let mut root_panel = layout! {
                StackPanel::new(Orient::Vertical),
                header_panel,
                self.webview => { grow: true },
            };
            root_panel.set_size(csize)?;
        }
        Ok(())
    }
}

impl Deref for WebViewPage {
    type Target = TabViewItem;

    fn deref(&self) -> &Self::Target {
        &self.window
    }
}