fui_controls 0.18.0

Standard controls for FUI UI Framework
Documentation
use crate::*;
use fui_core::*;
use fui_drawing::Color;
use fui_macros::ui;
use futures_channel::oneshot;
use futures_channel::oneshot::Canceled;
use std::cell::RefCell;
use std::future::Future;
use std::rc::Rc;
use typemap::TypeMap;

pub struct DialogButtonViewModel {
    pub text: Property<String>,
    pub callback: Callback<()>,
}

impl DialogButtonViewModel {
    pub fn new(text: String, callback: Callback<()>) -> Self {
        Self {
            text: Property::new(text),
            callback,
        }
    }
}

impl ViewModel for DialogButtonViewModel {
    fn create_view(self: &Rc<Self>) -> Rc<RefCell<dyn ControlObject>> {
        ui! {
            Button {
                clicked: self.callback.clone(),
                Text { text: &self.text }
            }
        }
    }
}

pub struct MessageBox {
    message: String,
    buttons: Vec<String>,
}

impl MessageBox {
    pub fn new<S: Into<String>>(message: S) -> Self {
        MessageBox {
            message: message.into(),
            buttons: Vec::new(),
        }
    }

    pub fn with_button<S: Into<String>>(mut self, text: S) -> Self {
        self.buttons.push(text.into());
        self
    }

    pub async fn show(self, window: &Rc<dyn WindowService>) -> i32 {
        self.show_internal(window).await.unwrap()
    }

    fn show_internal(
        self,
        window: &Rc<dyn WindowService>,
    ) -> impl Future<Output = Result<i32, Canceled>> + use<> {
        let buttons = ObservableVec::<Rc<DialogButtonViewModel>>::new();

        let (sender, receiver) = oneshot::channel::<i32>();
        let sender = Rc::new(RefCell::new(Some(sender)));

        let content = ui! {
            Border {
                border_type: BorderType::None,
                Style: Default { background_color: Color::rgba(1.0, 1.0, 1.0, 0.5), },
                HorizontalAlignment: Alignment::Stretch,
                VerticalAlignment: Alignment::Stretch,

                Shadow {
                    Style: Default { size: 12.0f32 },
                    HorizontalAlignment: Alignment::Center,
                    VerticalAlignment: Alignment::Center,

                    Border {
                        border_type: BorderType::None,
                        Style: Default { background_color: Color::rgba(0.0, 0.0, 0.0, 0.8), },

                        Vertical {
                            Margin: Thickness::all(10.0f32),

                            Text { text: self.message },

                            Grid {
                                Margin: Thickness::top(10.0f32),
                                rows: 1,
                                &buttons
                            }
                        }
                    }
                }
            }
        };

        for (button_index, text) in self.buttons.into_iter().enumerate() {
            // create new_callback that closes dialog layer
            // and calls button callback
            let window_clone = window.clone();
            let content_clone: Rc<RefCell<dyn ControlObject>> = content.clone();
            let new_callback = Callback::new_sync({
                let sender = sender.clone();
                move |_| {
                    window_clone.remove_layer(&content_clone);
                    sender
                        .borrow_mut()
                        .take()
                        .unwrap()
                        .send(button_index as i32)
                        .unwrap();
                }
            });

            buttons.push(Rc::new(DialogButtonViewModel::new(text, new_callback)));
        }

        window.add_layer(content);

        receiver
    }
}