tengri/src/draw.rs
2026-07-15 12:57:53 +03:00

144 lines
3.7 KiB
Rust

use crate::*;
/// Output target.
///
/// ```
/// use tengri::*;
/// struct TestOut { w: u16, h: u16 };
/// impl Wide<u16> for TestOut {}
/// impl Tall<u16> for TestOut {}
/// impl Xy<u16> for TestOut { fn x (&self) -> u16 { 0 } fn y (&self) -> u16 { 0 } }
/// impl Screen for TestOut {
/// type Unit = u16;
/// fn show <D: Draw<Self>> (&mut self, _: D) -> Drawn<u16> {
/// println!("placed");
/// Ok(None)
/// }
/// }
///
/// impl_draw!(|self: String, to: TestOut|{
/// to.w = self.len() as u16;
/// Ok(None)
/// });
/// ```
pub trait Screen: Xy<Self::Unit> + Wh<Self::Unit> + Send + Sync + Sized {
type Unit: Coord;
/// Render drawable in subarea specified by `area`
fn show (&mut self, content: impl Draw<Self>) -> Perhaps<XYWH<Self::Unit>>;
/// Get current clipping area
fn area (&self) -> XYWH<Self::Unit>;
/// Set clipping area
fn clip <T> (
&mut self,
area: impl Into<Option<XYWH<Self::Unit>>>,
draw: impl FnOnce(&mut Self)->T
) -> T;
}
/// Implement the [Draw] trait for a particular drawable and [Screen].
///
/// ```
/// use tengri::*;
/// struct MyDrawable;
/// impl_draw!(|self: MyDrawable, to: Tui|{
/// todo!("your draw logic")
/// });
/// ```
#[macro_export] macro_rules! impl_draw (
($(<$($T:ident: $Trait:path,)+>)?|
$self:ident:$Self:path, $to:ident:$To:ty
|$draw:block)=>{ impl$(<$($T:$Trait),+>)? Draw<$To> for $Self {
fn draw ($self, $to: &mut $To) -> Perhaps<XYWH<<$To as Screen>::Unit>> $draw
} };
($(<$($T:ident: $Trait:path,)+>)?|
$self:ident:$Self:ty, $to:ident:$To:ty
|$draw:block)=>{ impl$(<$($T:$Trait),+>)? Draw<$To> for $Self {
fn draw ($self, $to: &mut $To) -> Perhaps<XYWH<<$To as Screen>::Unit>> $draw
} }
);
/// Drawable that supports dynamic dispatch.
///
/// Drawables are composable, e.g. the [when] and [either] conditionals
/// or the layout constraints.
///
/// Drawables are consumable, i.e. the [Draw::draw] method receives an
/// owned `self` and does not return it, consuming the drawable.
///
/// To draw a thing multiple times, instead of explicitly constructing it
/// every time, implement the [View] trait instead, which will construct
/// a [Draw]able.
///
/// ```
/// use tengri::*;
/// struct MyWidget(bool);
/// impl Draw<Tui> for MyWidget {
/// fn draw (self, to: &mut Tui) -> Perhaps<XYWH<u16>> {
/// todo!("your draw logic")
/// }
/// }
/// ```
pub trait Draw<S: Screen> {
fn draw (self, to: &mut S) -> Drawn<S::Unit>;
fn layout (&self, area: XYWH<S::Unit>) -> Drawn<S::Unit> {
Ok(Some(area))
}
}
pub type Drawn<U> = Perhaps<XYWH<U>>;
impl<S: Screen> Draw<S> for () {
fn draw (self, _: &mut S) -> Drawn<S::Unit> {
Ok(None)
}
}
impl_draw!(<S: Screen, D: Draw<S>,>|self: Option<D>, to: S|{
self.map(|it|it.draw(to)).transpose().map(Option::unwrap_or_default)
});
//impl<S: Screen, D: Draw<S>> Draw<S> for RwLock<D> {
//fn draw (self, __: &mut S) -> Drawn<S::Unit> {
//todo!()
//}
//}
//impl<T: Screen, D: Draw<T>> Draw<T> for Arc<D> {
//fn draw (self, __: &mut T) -> Perhaps<XYWH<T::Unit>> {
//todo!()
//}
//}
/// Emit a [Draw]able.
///
/// Speculative. How to avoid conflicts with [Draw] proper?
pub trait View<T: Screen> {
fn view (&self) -> impl Draw<T>;
}
impl<T: Screen> View<T> for () {
fn view (&self) -> impl Draw<T> {
()
}
}
impl<T: Screen, V: View<T>> Draw<T> for &V {
fn draw (self, to: &mut T) -> Perhaps<XYWH<T::Unit>> {
self.view().draw(to)
}
}
features! {
"draw": [
color,
coord,
iter,
layout,
lrtb,
sizer,
space,
split,
thunk,
xywh
]
}