#![cfg_attr(feature = "gat", feature(generic_associated_types))]
pub mod draw;
mod event_loop;
pub mod options;
mod shared;
mod window;
use std::cell::RefCell;
use std::rc::Rc;
use thiserror::Error;
use kas::event::UpdateHandle;
use kas::WindowId;
use kas_theme::Theme;
use winit::error::OsError;
use winit::event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
use crate::draw::{CustomPipe, CustomPipeBuilder, DrawPipe};
use crate::shared::SharedState;
use window::Window;
pub use options::Options;
pub use kas;
pub use kas_theme as theme;
pub use wgpu;
pub use wgpu_glyph as glyph;
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum Error {
#[error("no graphics adapter found")]
NoAdapter,
#[error("config load/save error")]
Config(#[from] kas::event::ConfigError),
#[doc(hidden)]
#[error("operating system error")]
Window(#[from] OsError),
}
impl From<wgpu::RequestDeviceError> for Error {
fn from(_: wgpu::RequestDeviceError) -> Self {
Error::NoAdapter
}
}
pub struct Toolkit<C: CustomPipe, T: Theme<DrawPipe<C>>> {
el: EventLoop<ProxyAction>,
windows: Vec<Window<C::Window, T::Window>>,
shared: SharedState<C, T>,
}
impl<T: Theme<DrawPipe<()>> + 'static> Toolkit<(), T>
where
T::Window: kas_theme::Window,
{
#[inline]
pub fn new(theme: T) -> Result<Self, Error> {
Self::new_custom((), theme, Options::from_env())
}
}
impl<C: CustomPipe + 'static, T: Theme<DrawPipe<C>> + 'static> Toolkit<C, T>
where
T::Window: kas_theme::Window,
{
#[inline]
pub fn new_custom<CB: CustomPipeBuilder<Pipe = C>>(
custom: CB,
theme: T,
options: Options,
) -> Result<Self, Error> {
let el = EventLoop::with_user_event();
let config = Rc::new(RefCell::new(options.config()?));
let scale_factor = find_scale_factor(&el);
Ok(Toolkit {
el,
windows: vec![],
shared: SharedState::new(custom, theme, options, config, scale_factor)?,
})
}
#[inline]
pub fn new_custom_config<CB: CustomPipeBuilder<Pipe = C>>(
custom: CB,
theme: T,
options: Options,
config: Rc<RefCell<kas::event::Config>>,
) -> Result<Self, Error> {
let el = EventLoop::with_user_event();
let scale_factor = find_scale_factor(&el);
Ok(Toolkit {
el,
windows: vec![],
shared: SharedState::new(custom, theme, options, config, scale_factor)?,
})
}
#[inline]
pub fn add<W: kas::Window + 'static>(&mut self, window: W) -> Result<WindowId, Error> {
self.add_boxed(Box::new(window))
}
#[inline]
pub fn with<W: kas::Window + 'static>(mut self, window: W) -> Result<Self, Error> {
self.add_boxed(Box::new(window))?;
Ok(self)
}
pub fn add_boxed(&mut self, widget: Box<dyn kas::Window>) -> Result<WindowId, Error> {
let id = self.shared.next_window_id();
let win = Window::new(&mut self.shared, &self.el, id, widget)?;
self.windows.push(win);
Ok(id)
}
#[inline]
pub fn with_boxed(mut self, widget: Box<dyn kas::Window>) -> Result<Self, Error> {
self.add_boxed(widget)?;
Ok(self)
}
pub fn create_proxy(&self) -> ToolkitProxy {
ToolkitProxy {
proxy: self.el.create_proxy(),
}
}
#[inline]
pub fn run(self) -> ! {
let mut el = event_loop::Loop::new(self.windows, self.shared);
self.el
.run(move |event, elwt, control_flow| el.handle(event, elwt, control_flow))
}
}
fn find_scale_factor<T>(el: &EventLoopWindowTarget<T>) -> f64 {
if let Some(mon) = el.primary_monitor() {
return mon.scale_factor();
}
if let Some(mon) = el.available_monitors().next() {
return mon.scale_factor();
}
1.0
}
pub struct ToolkitProxy {
proxy: EventLoopProxy<ProxyAction>,
}
pub struct ClosedError;
impl ToolkitProxy {
pub fn close(&self, id: WindowId) -> Result<(), ClosedError> {
self.proxy
.send_event(ProxyAction::Close(id))
.map_err(|_| ClosedError)
}
pub fn close_all(&self) -> Result<(), ClosedError> {
self.proxy
.send_event(ProxyAction::CloseAll)
.map_err(|_| ClosedError)
}
pub fn trigger_update(&self, handle: UpdateHandle, payload: u64) -> Result<(), ClosedError> {
self.proxy
.send_event(ProxyAction::Update(handle, payload))
.map_err(|_| ClosedError)
}
}
#[derive(Debug)]
enum ProxyAction {
CloseAll,
Close(WindowId),
Update(UpdateHandle, u64),
}