popout 0.1.1

Small and simple modal popups powered by egui
Documentation
//! Contains utilities for creating dialog boxes

use egui::RichText;
use winit::{
    dpi::Size,
    window::{Icon, WindowAttributes},
};

use crate::create_window;

/// A rich text line for display in a [`Dialog`]
#[derive(Default, Debug)]
pub struct DialogLine {
    line: Vec<RichText>,
}

impl DialogLine {
    /// Create a new [`DialogLine`]
    pub fn new() -> Self {
        Self::default()
    }
    /// Add a new piece of rich text to the dialog line
    pub fn with(mut self, txt: impl Into<RichText>) -> Self {
        self.line.push(txt.into());
        self
    }
}

/// A dialog box with text and buttons
#[derive(Default, Debug)]
pub struct Dialog {
    texts: Vec<Vec<RichText>>,
    buttons: Vec<RichText>,
    attrs: WindowAttributes,
}

impl Dialog {
    /// Create a new [`Dialog`]
    ///
    /// Call [`Dialog::show`] to display it
    pub fn new() -> Self {
        Self::default()
    }
    /// Set the title of the dialog
    pub fn with_title(mut self, title: impl Into<String>) -> Self {
        self.attrs.title = title.into();
        self
    }

    /// Set the window attributes of the [`Dialog`] directly
    pub fn with_window_attributes(mut self, attrs: WindowAttributes) -> Self {
        self.attrs = attrs;
        self
    }

    /// Get the window attributes of the [`Dialog`]
    pub fn window_attributes(&self) -> &WindowAttributes {
        &self.attrs
    }

    /// Add a line to the [`Dialog`] that may contain nested [`RichText`]
    pub fn with_rich_line(mut self, line: DialogLine) -> Self {
        self.texts.push(line.line);
        self
    }

    /// Add a single [`RichText`] as a full line
    pub fn with_line(mut self, line: impl Into<RichText>) -> Self {
        self.texts.push(vec![line.into()]);
        self
    }
    /// Add a button
    ///
    /// # Ordering
    /// Note that ordering does matter here, as the returned value from [`Dialog::show`] will be an index
    /// of the buttons
    pub fn with_button(mut self, btn: impl Into<RichText>) -> Self {
        self.buttons.push(btn.into());
        self
    }
    /// Set the minimum size for the dialog window
    pub fn with_min_size(mut self, sz: impl Into<Size>) -> Self {
        self.attrs.min_inner_size = Some(sz.into());
        self
    }

    /// Set the initial size for the dialog window
    pub fn with_size(mut self, sz: impl Into<Size>) -> Self {
        self.attrs.inner_size = Some(sz.into());
        self
    }

    /// Set the max size for the dialog window
    pub fn with_max_size(mut self, sz: impl Into<Size>) -> Self {
        self.attrs.max_inner_size = Some(sz.into());
        self
    }
    /// Set whether decorations are enabled on the window (platform dependent)
    pub fn with_decorations(mut self, decorations: bool) -> Self {
        self.attrs.decorations = decorations;
        self
    }

    /// Set the window icon
    pub fn with_window_icon(mut self, icon: Option<Icon>) -> Self {
        self.attrs.window_icon = icon;
        self
    }

    /// Set whether the window can be resized
    pub fn with_resizeable(mut self, resizeable: bool) -> Self {
        self.attrs.resizable = resizeable;
        self
    }

    /// Display the dialog box
    ///
    /// The returned value is the index of the clicked button, or None if the user closed the window
    /// without selecting anything
    pub fn show(self) -> Result<Option<usize>, crate::Error> {
        create_window(
            |ui| {
                for line in &self.texts {
                    ui.horizontal(|ui| {
                        for txt in line {
                            ui.label(txt.clone());
                        }
                    });
                }
                ui.horizontal(|ui| {
                    for (i, btn) in self.buttons.iter().enumerate() {
                        if ui.button(btn.clone()).clicked() {
                            return Some(i);
                        }
                    }
                    None
                })
                .inner
            },
            self.attrs,
        )
    }
}