use crate::config::{AutoFactory, Config, ConfigFactory};
use crate::draw::DrawSharedImpl;
use crate::theme::Theme;
use crate::window::{Window, WindowId};
pub use kas_core::runner::{AppData, ClosedError, Error, Platform, Proxy, ReadMessage, Result};
use kas_core::runner::{GraphicsInstance, PreLaunchState};
#[allow(unused)]
use kas_core::theme::{FlatTheme, SimpleTheme};
#[cfg(feature = "wgpu")]
use kas_wgpu::draw::CustomPipeBuilder;
use std::cell::{Ref, RefMut};
#[cfg(not(any(feature = "wgpu", feature = "soft")))]
compile_error!("At least one of the following features must be enabled: wgpu, soft");
#[cfg(feature = "wgpu")]
type DefaultTheme = FlatTheme;
#[cfg(all(not(feature = "wgpu"), feature = "soft"))]
type DefaultTheme = SimpleTheme;
#[cfg(feature = "wgpu")]
pub struct WgpuBuilder<CB: CustomPipeBuilder> {
custom: CB,
options: kas_wgpu::Options,
read_env_vars: bool,
}
#[cfg(feature = "wgpu")]
impl<CB: CustomPipeBuilder> WgpuBuilder<CB> {
#[inline]
fn new(cb: CB) -> Self {
WgpuBuilder {
custom: cb,
options: kas_wgpu::Options::default(),
read_env_vars: true,
}
}
#[inline]
pub fn with_wgpu_options(mut self, options: kas_wgpu::Options) -> Self {
self.options = options;
self
}
#[inline]
pub fn read_env_vars(mut self, read_env_vars: bool) -> Self {
self.read_env_vars = read_env_vars;
self
}
#[inline]
pub fn with_default_theme(self) -> Builder<FlatTheme, kas_wgpu::Instance<CB>> {
self.with_theme(FlatTheme::new())
}
#[inline]
pub fn with_theme<T>(mut self, theme: T) -> Builder<T, kas_wgpu::Instance<CB>>
where
T: Theme<<kas_wgpu::Instance<CB> as GraphicsInstance>::Shared>,
{
if self.read_env_vars {
self.options.load_from_env();
}
Builder {
graphics: kas_wgpu::Instance::new(self.options, self.custom),
theme,
config: AutoFactory::default(),
}
}
}
#[cfg(feature = "soft")]
pub struct SoftBuilder;
#[cfg(feature = "soft")]
impl SoftBuilder {
#[inline]
pub fn with_default_theme(self) -> Builder<SimpleTheme, kas_soft::Instance> {
self.with_theme(SimpleTheme::new())
}
#[inline]
pub fn with_theme<T>(self, theme: T) -> Builder<T, kas_soft::Instance>
where
T: Theme<<kas_soft::Instance as GraphicsInstance>::Shared>,
{
Builder {
graphics: kas_soft::Instance::new(),
theme,
config: AutoFactory::default(),
}
}
}
#[derive(Default)]
pub struct Builder<
T = DefaultTheme,
#[cfg(feature = "wgpu")] G = kas_wgpu::Instance<()>,
#[cfg(all(not(feature = "wgpu"), feature = "soft"))] G = kas_soft::Instance,
C = AutoFactory,
> where
T: Theme<G::Shared> + 'static,
G: GraphicsInstance,
C: ConfigFactory,
{
graphics: G,
theme: T,
config: C,
}
impl<T: Theme<G::Shared>, G: GraphicsInstance, C: ConfigFactory> Builder<T, G, C> {
#[inline]
pub fn with_config<CF: ConfigFactory>(self, config: CF) -> Builder<T, G, CF> {
Builder {
graphics: self.graphics,
theme: self.theme,
config,
}
}
pub fn build<Data: AppData>(mut self, data: Data) -> Result<Runner<Data, T, G>> {
let state = PreLaunchState::new(self.config)?;
self.theme.init(state.config());
Ok(Runner {
data,
graphics: self.graphics,
theme: self.theme,
state,
windows: vec![],
})
}
}
pub struct Runner<
Data: AppData,
T: Theme<G::Shared> = DefaultTheme,
#[cfg(feature = "wgpu")] G: GraphicsInstance = kas_wgpu::Instance<()>,
#[cfg(all(not(feature = "wgpu"), feature = "soft"))] G: GraphicsInstance = kas_soft::Instance,
> {
data: Data,
graphics: G,
state: PreLaunchState,
theme: T,
windows: Vec<Box<kas_core::runner::Window<Data, G, T>>>,
}
pub trait RunnerInherent {
type DrawShared: DrawSharedImpl;
}
impl<A: AppData, G: GraphicsInstance, T> RunnerInherent for Runner<A, T, G>
where
T: Theme<G::Shared> + 'static,
{
type DrawShared = G::Shared;
}
impl<Data: AppData> Runner<Data> {
#[inline]
pub fn new(data: Data) -> Result<Self> {
#[cfg(feature = "wgpu")]
{
WgpuBuilder::new(())
.with_theme(Default::default())
.build(data)
}
#[cfg(all(not(feature = "wgpu"), feature = "soft"))]
{
SoftBuilder.with_theme(Default::default()).build(data)
}
}
}
#[cfg(feature = "wgpu")]
impl<T: Theme<kas_wgpu::draw::DrawPipe<()>>> Runner<(), T> {
#[inline]
pub fn with_theme(theme: T) -> Builder<T> {
WgpuBuilder::new(()).with_theme(theme)
}
}
#[cfg(all(not(feature = "wgpu"), feature = "soft"))]
impl<T: Theme<kas_soft::Shared>> Runner<(), T> {
#[inline]
pub fn with_theme(theme: T) -> Builder<T> {
SoftBuilder.with_theme(theme)
}
}
impl Runner<()> {
#[inline]
pub fn with_default_theme() -> Builder {
#[cfg(feature = "wgpu")]
{
WgpuBuilder::new(()).with_theme(Default::default())
}
#[cfg(all(not(feature = "wgpu"), feature = "soft"))]
{
SoftBuilder.with_theme(Default::default())
}
}
#[cfg(feature = "wgpu")]
pub fn with_wgpu_pipe<CB: CustomPipeBuilder>(cb: CB) -> WgpuBuilder<CB> {
WgpuBuilder::new(cb)
}
}
impl<Data: AppData, G: GraphicsInstance + 'static, T> Runner<Data, T, G>
where
T: Theme<G::Shared> + 'static,
{
#[inline]
pub fn config(&self) -> Ref<'_, Config> {
self.state.config().borrow()
}
#[inline]
pub fn config_mut(&mut self) -> RefMut<'_, Config> {
self.state.config().borrow_mut()
}
#[inline]
pub fn theme(&self) -> &T {
&self.theme
}
#[inline]
pub fn theme_mut(&mut self) -> &mut T {
&mut self.theme
}
#[inline]
pub fn add(&mut self, window: Window<Data>) -> WindowId {
let id = self.state.next_window_id();
let win = Box::new(kas_core::runner::Window::new(
self.state.config().clone(),
self.state.platform(),
id,
window.boxed(),
));
self.windows.push(win);
id
}
#[inline]
pub fn with(mut self, window: Window<Data>) -> Self {
let _ = self.add(window);
self
}
pub fn create_proxy(&self) -> Proxy {
self.state.create_proxy()
}
#[inline]
pub fn run(self) -> Result<()> {
self.state
.run(self.data, self.graphics, self.theme, self.windows)
}
}