#![cfg_attr(feature = "gat", feature(generic_associated_types))]
pub mod draw;
mod event_loop;
pub mod options;
mod shared;
mod window;
use std::{error, fmt};
use kas::event::UpdateHandle;
use kas::WindowId;
use kas_theme::Theme;
use winit::error::OsError;
use winit::event_loop::{EventLoop, EventLoopProxy};
use crate::draw::{CustomPipe, CustomPipeBuilder, DrawPipe, DrawWindow};
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(Debug)]
pub enum Error {
NoAdapter,
#[doc(hidden)]
ShaderCompilation(shaderc::Error),
Window(OsError),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
Error::NoAdapter => write!(f, "no suitable graphics adapter found"),
Error::ShaderCompilation(e) => write!(f, "shader compilation failed: {}", e),
Error::Window(e) => write!(f, "window creation error: {}", e),
}
}
}
impl error::Error for Error {}
impl From<OsError> for Error {
fn from(ose: OsError) -> Self {
Error::Window(ose)
}
}
impl From<shaderc::Error> for Error {
fn from(e: shaderc::Error) -> Self {
Error::ShaderCompilation(e)
}
}
pub struct Toolkit<C: CustomPipe, T: Theme<DrawPipe<C>>>
where
T::Window: kas_theme::Window<DrawWindow<C::Window>>,
{
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<DrawWindow<()>>,
{
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<DrawWindow<C::Window>>,
{
pub fn new_custom<CB: CustomPipeBuilder<Pipe = C>>(
custom: CB,
theme: T,
options: Options,
) -> Result<Self, Error> {
let el = EventLoop::with_user_event();
let scale_factor = el.primary_monitor().scale_factor();
Ok(Toolkit {
el,
windows: vec![],
shared: SharedState::new(custom, theme, options, scale_factor)?,
})
}
pub fn add<W: kas::Window + 'static>(&mut self, window: W) -> Result<WindowId, Error> {
self.add_boxed(Box::new(window))
}
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)
}
pub fn create_proxy(&self) -> ToolkitProxy {
ToolkitProxy {
proxy: self.el.create_proxy(),
}
}
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))
}
}
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),
}