mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
233 lines
7.9 KiB
Rust
233 lines
7.9 KiB
Rust
use crate::*;
|
|
/// Entry point for main loop
|
|
pub trait App<T: Engine> { fn run (self, context: T) -> Usually<T>; }
|
|
/// Platform backend.
|
|
pub trait Engine: Send + Sync + Sized {
|
|
/// Input event type
|
|
type Input: Input<Self>;
|
|
/// Result of handling input
|
|
type Handled;
|
|
/// Render target
|
|
type Output: Output<Self>;
|
|
/// Unit of length
|
|
type Unit: Number;
|
|
/// Rectangle without offset
|
|
type Size: Size<Self::Unit> + From<[Self::Unit;2]> + Debug + Copy;
|
|
/// Rectangle with offset
|
|
type Area: Area<Self::Unit> + From<[Self::Unit;4]> + Debug + Copy;
|
|
/// Prepare before run
|
|
fn setup (&mut self) -> Usually<()> { Ok(()) }
|
|
/// True if done
|
|
fn exited (&self) -> bool;
|
|
/// Clean up after run
|
|
fn teardown (&mut self) -> Usually<()> { Ok(()) }
|
|
}
|
|
/// Current input state
|
|
pub trait Input<E: Engine> {
|
|
/// Type of input event
|
|
type Event;
|
|
/// Currently handled event
|
|
fn event (&self) -> &Self::Event;
|
|
/// Whether component should exit
|
|
fn is_done (&self) -> bool;
|
|
/// Mark component as done
|
|
fn done (&self);
|
|
}
|
|
/// Rendering target
|
|
pub trait Output<E: Engine> {
|
|
/// Current output area
|
|
fn area (&self) -> E::Area;
|
|
/// Mutable pointer to area
|
|
fn area_mut (&mut self) -> &mut E::Area;
|
|
/// Render widget in area
|
|
fn render_in (&mut self, area: E::Area, widget: &dyn Widget<Engine = E>) -> Usually<()>;
|
|
}
|
|
/// Cast to dynamic pointer
|
|
pub fn widget <E: Engine, T: Widget<Engine = E>> (w: &T) -> &dyn Widget<Engine = E> {
|
|
w as &dyn Widget<Engine = E>
|
|
}
|
|
/// A renderable component
|
|
pub trait Widget: Send + Sync {
|
|
/// Engine for which this component is implemented
|
|
type Engine: Engine;
|
|
/// Minimum size to use
|
|
fn layout (&self, to: <Self::Engine as Engine>::Size)
|
|
-> Perhaps<<Self::Engine as Engine>::Size>
|
|
{
|
|
Ok(Some(to))
|
|
}
|
|
/// Draw to output render target
|
|
fn render (&self,to: &mut <Self::Engine as Engine>::Output) -> Usually<()>;
|
|
}
|
|
impl<E: Engine> Widget for &dyn Widget<Engine = E> {
|
|
type Engine = E;
|
|
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
|
|
(*self).layout(to)
|
|
}
|
|
fn render (&self, to: &mut E::Output) -> Usually<()> {
|
|
(*self).render(to)
|
|
}
|
|
}
|
|
impl<E: Engine> Widget for &mut dyn Widget<Engine = E> {
|
|
type Engine = E;
|
|
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
|
|
(**self).layout(to)
|
|
}
|
|
fn render (&self, to: &mut E::Output) -> Usually<()> {
|
|
(**self).render(to)
|
|
}
|
|
}
|
|
impl<'a, E: Engine> Widget for Box<dyn Widget<Engine = E> + 'a> {
|
|
type Engine = E;
|
|
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
|
|
(**self).layout(to)
|
|
}
|
|
fn render (&self, to: &mut E::Output) -> Usually<()> {
|
|
(**self).render(to)
|
|
}
|
|
}
|
|
impl<E: Engine, W: Widget<Engine = E>> Widget for Arc<W> {
|
|
type Engine = E;
|
|
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
|
|
self.as_ref().layout(to)
|
|
}
|
|
fn render (&self, to: &mut E::Output) -> Usually<()> {
|
|
self.as_ref().render(to)
|
|
}
|
|
}
|
|
impl<E: Engine, W: Widget<Engine = E>> Widget for Mutex<W> {
|
|
type Engine = E;
|
|
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
|
|
self.lock().unwrap().layout(to)
|
|
}
|
|
fn render (&self, to: &mut E::Output) -> Usually<()> {
|
|
self.lock().unwrap().render(to)
|
|
}
|
|
}
|
|
impl<E: Engine, W: Widget<Engine = E>> Widget for RwLock<W> {
|
|
type Engine = E;
|
|
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
|
|
self.read().unwrap().layout(to)
|
|
}
|
|
fn render (&self, to: &mut E::Output) -> Usually<()> {
|
|
self.read().unwrap().render(to)
|
|
}
|
|
}
|
|
impl<E: Engine, W: Widget<Engine = E>> Widget for Option<W> {
|
|
type Engine = E;
|
|
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
|
|
Ok(self.as_ref().map(|widget|widget.layout(to)).transpose()?.flatten())
|
|
}
|
|
fn render (&self, to: &mut E::Output) -> Usually<()> {
|
|
self.as_ref().map(|widget|widget.render(to)).unwrap_or(Ok(()))
|
|
}
|
|
}
|
|
/// A custom [Widget] defined by passing layout and render closures in place.
|
|
pub struct CustomWidget<
|
|
E: Engine,
|
|
L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
|
|
R: Send + Sync + Fn(&mut E::Output)->Usually<()>
|
|
>(L, R, PhantomData<E>);
|
|
impl<
|
|
E: Engine,
|
|
L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
|
|
R: Send + Sync + Fn(&mut E::Output)->Usually<()>
|
|
> CustomWidget<E, L, R> {
|
|
pub fn new (layout: L, render: R) -> Self {
|
|
Self(layout, render, Default::default())
|
|
}
|
|
}
|
|
impl<
|
|
E: Engine,
|
|
L: Send + Sync + Fn(E::Size)->Perhaps<E::Size>,
|
|
R: Send + Sync + Fn(&mut E::Output)->Usually<()>
|
|
> Widget for CustomWidget<E, L, R> {
|
|
type Engine = E;
|
|
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
|
|
self.0(to)
|
|
}
|
|
fn render (&self, to: &mut E::Output) -> Usually<()> {
|
|
self.1(to)
|
|
}
|
|
}
|
|
/// A [Widget] that contains other [Widget]s
|
|
pub trait Content: Send + Sync {
|
|
type Engine: Engine;
|
|
fn content (&self) -> impl Widget<Engine = <Self as Content>::Engine>;
|
|
}
|
|
/// Every struct that has [Content] is a renderable [Widget].
|
|
impl<E: Engine, W: Content<Engine = E>> Widget for W {
|
|
type Engine = E;
|
|
fn layout (&self, to: E::Size) -> Perhaps<E::Size> {
|
|
self.content().layout(to)
|
|
}
|
|
fn render (&self, to: &mut E::Output) -> Usually<()> {
|
|
match self.layout(to.area().wh().into())? {
|
|
Some(wh) => to.render_in(to.area().clip(wh).into(), &self.content()),
|
|
None => Ok(())
|
|
}
|
|
}
|
|
}
|
|
/// Handle input
|
|
pub trait Handle<E: Engine>: Send + Sync {
|
|
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled>;
|
|
}
|
|
impl<E: Engine, H: Handle<E>> Handle<E> for &mut H {
|
|
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
|
|
(*self).handle(context)
|
|
}
|
|
}
|
|
impl<E: Engine, H: Handle<E>> Handle<E> for Option<H> {
|
|
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
|
|
if let Some(ref mut handle) = self {
|
|
handle.handle(context)
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
}
|
|
}
|
|
impl<H, E: Engine> Handle<E> for Mutex<H> where H: Handle<E> {
|
|
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
|
|
self.lock().unwrap().handle(context)
|
|
}
|
|
}
|
|
impl<H, E: Engine> Handle<E> for Arc<Mutex<H>> where H: Handle<E> {
|
|
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
|
|
self.lock().unwrap().handle(context)
|
|
}
|
|
}
|
|
impl<H, E: Engine> Handle<E> for RwLock<H> where H: Handle<E> {
|
|
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
|
|
self.write().unwrap().handle(context)
|
|
}
|
|
}
|
|
impl<H, E: Engine> Handle<E> for Arc<RwLock<H>> where H: Handle<E> {
|
|
fn handle (&mut self, context: &E::Input) -> Perhaps<E::Handled> {
|
|
self.write().unwrap().handle(context)
|
|
}
|
|
}
|
|
/// A UI component that can render itself as a [Widget], and [Handle] input.
|
|
pub trait Component<E: Engine>: Widget<Engine = E> + Handle<E> {}
|
|
/// Everything that implements [Widget] and [Handle] is a [Component].
|
|
impl<E: Engine, C: Widget<Engine = E> + Handle<E>> Component<E> for C {}
|
|
/// A UI component that has [Content] and can [Handle] input.
|
|
pub trait ContentComponent<E: Engine>: Widget<Engine = E> + Handle<E> {}
|
|
/// Everything that implements [Content] and [Handle] is a [Component].
|
|
impl<E: Engine, C: Content<Engine = E> + Handle<E>> ContentComponent<E> for C {}
|
|
/// A component that can exit.
|
|
pub trait Exit: Send {
|
|
fn exited (&self) -> bool;
|
|
fn exit (&mut self);
|
|
fn boxed (self) -> Box<dyn Exit> where Self: Sized + 'static {
|
|
Box::new(self)
|
|
}
|
|
}
|
|
/// Marker trait for [Component]s that can [Exit].
|
|
pub trait ExitableComponent<E>: Exit + Component<E> where E: Engine {
|
|
/// Perform type erasure for collecting heterogeneous components.
|
|
fn boxed (self) -> Box<dyn ExitableComponent<E>> where Self: Sized + 'static {
|
|
Box::new(self)
|
|
}
|
|
}
|
|
/// All [Components]s that implement [Exit] implement [ExitableComponent].
|
|
impl<E: Engine, C: Component<E> + Exit> ExitableComponent<E> for C {}
|