Skip to main content

popout/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::cell::RefCell;
4
5use winit::{
6    event_loop::{ControlFlow, EventLoop},
7    platform::run_on_demand::EventLoopExtRunOnDemand,
8};
9
10pub use dialog::Dialog;
11pub use egui;
12pub use egui::{Color32, RichText};
13pub use winit::{
14    self,
15    dpi::{LogicalSize, PhysicalSize},
16    window::{Icon, WindowAttributes},
17};
18
19pub mod dialog;
20mod renderer;
21mod window;
22
23thread_local! {
24static EVENT_LOOP: RefCell<Option<EventLoop<()>>> = const { RefCell::new(None) };
25}
26
27/// An error emitted by popout
28#[derive(thiserror::Error, Debug)]
29#[non_exhaustive]
30pub enum Error {
31    #[error("failed to run event loop {0:?}")]
32    EventLoop(#[from] winit::error::EventLoopError),
33    #[error("failed to find an appropriate wgpu adapter")]
34    FailedToFindAdapter,
35
36    #[error("failed select appropriate surface texture format")]
37    FailedToSelectSurfaceTextureFormat,
38
39    #[error("failed to request wgpu device {0:?}")]
40    FailedToRequestDevice(#[from] egui_wgpu::wgpu::RequestDeviceError),
41
42    #[error("failed to create wgpu surface {0:?}")]
43    FailedToCreateSurface(#[from] egui_wgpu::wgpu::CreateSurfaceError),
44
45    #[error("failed to acquire next swapchain texture {0:?}")]
46    SwapchainError(#[from] egui_wgpu::wgpu::SurfaceError),
47}
48pub type Result<T, E = Error> = std::result::Result<T, E>;
49
50/// Create a window with a provided drawing function
51///
52/// This is a lower level API for when you want to have full control over what is drawn
53/// in the window. If you just want to display some text and buttons check out [`Dialog`]
54///
55/// # Draw function
56///
57/// The window will be closed once the `draw` function returns `None` or the user clicks the close
58/// decoration (assuming decorations are enabled). If the user clicks the close decoration then
59/// this function will return `None`, otherwise it will return whatever was returned from `draw`.
60pub fn create_window<F, R>(draw: F, info: WindowAttributes) -> Result<Option<R>>
61where
62    F: FnMut(&mut egui::Ui) -> Option<R>,
63{
64    // Note we have to do some fuckery here because the event loop can only be created once
65    EVENT_LOOP
66        .try_with(|ev_cell| {
67            let mut ev_cell = ev_cell.borrow_mut();
68            if ev_cell.is_none() {
69                let ev = EventLoop::new()?;
70                ev.set_control_flow(ControlFlow::Poll);
71                ev_cell.replace(ev);
72            }
73            let mut app = window::PopupWindow::new(draw, info);
74
75            ev_cell.as_mut().unwrap().run_app_on_demand(&mut app)?;
76
77            if let Some(e) = app.error {
78                Err(e)
79            } else {
80                Ok(app.draw_result)
81            }
82        })
83        .unwrap()
84}