popout 0.1.1

Small and simple modal popups powered by egui
Documentation
#![doc = include_str!("../README.md")]

use std::cell::RefCell;

use winit::{
    event_loop::{ControlFlow, EventLoop},
    platform::run_on_demand::EventLoopExtRunOnDemand,
};

pub use dialog::Dialog;
pub use egui;
pub use egui::{Color32, RichText};
pub use winit::{
    self,
    dpi::{LogicalSize, PhysicalSize},
    window::{Icon, WindowAttributes},
};

pub mod dialog;
mod renderer;
mod window;

thread_local! {
static EVENT_LOOP: RefCell<Option<EventLoop<()>>> = const { RefCell::new(None) };
}

/// An error emitted by popout
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum Error {
    #[error("failed to run event loop {0:?}")]
    EventLoop(#[from] winit::error::EventLoopError),
    #[error("failed to find an appropriate wgpu adapter")]
    FailedToFindAdapter,

    #[error("failed select appropriate surface texture format")]
    FailedToSelectSurfaceTextureFormat,

    #[error("failed to request wgpu device {0:?}")]
    FailedToRequestDevice(#[from] egui_wgpu::wgpu::RequestDeviceError),

    #[error("failed to create wgpu surface {0:?}")]
    FailedToCreateSurface(#[from] egui_wgpu::wgpu::CreateSurfaceError),

    #[error("failed to acquire next swapchain texture {0:?}")]
    SwapchainError(#[from] egui_wgpu::wgpu::SurfaceError),
}
pub type Result<T, E = Error> = std::result::Result<T, E>;

/// Create a window with a provided drawing function
///
/// This is a lower level API for when you want to have full control over what is drawn
/// in the window. If you just want to display some text and buttons check out [`Dialog`]
///
/// # Draw function
///
/// The window will be closed once the `draw` function returns `None` or the user clicks the close
/// decoration (assuming decorations are enabled). If the user clicks the close decoration then
/// this function will return `None`, otherwise it will return whatever was returned from `draw`.
pub fn create_window<F, R>(draw: F, info: WindowAttributes) -> Result<Option<R>>
where
    F: FnMut(&mut egui::Ui) -> Option<R>,
{
    // Note we have to do some fuckery here because the event loop can only be created once
    EVENT_LOOP
        .try_with(|ev_cell| {
            let mut ev_cell = ev_cell.borrow_mut();
            if ev_cell.is_none() {
                let ev = EventLoop::new()?;
                ev.set_control_flow(ControlFlow::Poll);
                ev_cell.replace(ev);
            }
            let mut app = window::PopupWindow::new(draw, info);

            ev_cell.as_mut().unwrap().run_app_on_demand(&mut app)?;

            if let Some(e) = app.error {
                Err(e)
            } else {
                Ok(app.draw_result)
            }
        })
        .unwrap()
}