winio 0.11.0

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

use winio::prelude::*;

use crate::{Error, Result};

pub struct ScrollViewPage {
    window: Child<TabViewItem>,
    scroll: Child<ScrollView>,
    radios: Child<RadioButtonGroup>,
    add_btn: Child<Button>,
    del_btn: Child<Button>,
    show_btn: Child<Button>,
    selected: Option<usize>,
}

#[derive(Debug)]
pub enum ScrollViewPageEvent {
    ShowMessage(MessageBox),
}

#[derive(Debug)]
pub enum ScrollViewPageMessage {
    Noop,
    Add,
    Del,
    Show,
    Select(usize),
}

impl Component for ScrollViewPage {
    type Error = Error;
    type Event = ScrollViewPageEvent;
    type Init<'a> = ();
    type Message = ScrollViewPageMessage;

    async fn init(_init: Self::Init<'_>, _sender: &ComponentSender<Self>) -> Result<Self> {
        init! {
            window: TabViewItem = (()) => {
                text: "ScrollView",
            },
            scroll: ScrollView = (&window) => {
                vscroll: true,
                hscroll: false,
            },
            add_btn: Button = (&window) => {
                text: "Add Radio",
            },
            del_btn: Button = (&window) => {
                text: "Delete Radio",
            },
            show_btn: Button = (&window) => {
                text: "Show Selected",
            },
            radios: RadioButtonGroup = ([]),
        }

        Ok(Self {
            window,
            scroll,
            radios,
            add_btn,
            del_btn,
            show_btn,
            selected: None,
        })
    }

    async fn start(&mut self, sender: &ComponentSender<Self>) -> ! {
        start! {
            sender, default: ScrollViewPageMessage::Noop,
            self.add_btn => {
                ButtonEvent::Click => ScrollViewPageMessage::Add,
            },
            self.del_btn => {
                ButtonEvent::Click => ScrollViewPageMessage::Del,
            },
            self.show_btn => {
                ButtonEvent::Click => ScrollViewPageMessage::Show,
            },
            self.scroll => {},
            self.radios => {
                RadioButtonGroupEvent::Click(i) => ScrollViewPageMessage::Select(i)
            }
        }
    }

    async fn update_children(&mut self) -> Result<bool> {
        update_children!(
            self.window,
            self.scroll,
            self.add_btn,
            self.del_btn,
            self.show_btn,
            self.radios
        )
    }

    async fn update(
        &mut self,
        message: Self::Message,
        sender: &ComponentSender<Self>,
    ) -> Result<bool> {
        match message {
            ScrollViewPageMessage::Noop => Ok(false),
            ScrollViewPageMessage::Add => {
                let idx = self.radios.len() + 1;
                init! {
                    radio: RadioButton = (&self.scroll) => {
                        text: format!("Radio {idx}"),
                        checked: false,
                    },
                }
                self.radios.push(radio);
                Ok(true)
            }
            ScrollViewPageMessage::Del => {
                if !self.radios.is_empty() {
                    self.radios.pop();
                }
                Ok(true)
            }
            ScrollViewPageMessage::Show => {
                let selected = self.radios.iter().find_map(|r| {
                    if r.is_checked().unwrap_or_default() {
                        Some(r.text().unwrap_or_default())
                    } else {
                        None
                    }
                });
                sender.output(ScrollViewPageEvent::ShowMessage(
                    MessageBox::new()
                        .title("Selected Radio")
                        .message(selected.unwrap_or("No selection".to_string()))
                        .buttons(MessageBoxButton::Ok),
                ));
                Ok(false)
            }
            ScrollViewPageMessage::Select(idx) => {
                self.selected = Some(idx);
                Ok(false)
            }
        }
    }

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

        let mut radios_panel = StackPanel::new(Orient::Vertical);
        for radio in self.radios.iter_mut() {
            radios_panel
                .push(radio)
                .margin(Margin::new_all_same(4.0))
                .finish();
        }

        radios_panel.set_size(csize)?;

        let mut buttons_panel = layout! {
            StackPanel::new(Orient::Vertical),
            self.add_btn  => { margin: Margin::new_all_same(4.0) },
            self.del_btn  => { margin: Margin::new_all_same(4.0) },
            self.show_btn => { margin: Margin::new_all_same(4.0) },
        };

        let mut root_panel = layout! {
            Grid::from_str("1*,auto", "1*").unwrap(),
            self.scroll   => { column: 0, row: 0 },
            buttons_panel => { column: 1, row: 0, halign: HAlign::Center, valign: VAlign::Top },
        };

        root_panel.set_size(csize)?;
        Ok(())
    }
}

impl Deref for ScrollViewPage {
    type Target = TabViewItem;

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