You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
3.8 KiB
Rust

use std::{
any::Any,
sync::atomic::{AtomicU64, Ordering},
};
use crate::backend::{Backend, Combine, Render, RenderResult, TargetBackend};
pub struct View<B: Backend = TargetBackend> {
id: Id,
view: SomeView<B>,
children: Option<Vec<Self>>,
}
pub enum SomeView<B: Backend> {
Native(Box<dyn NativeComponent<B>>),
Custom(Box<dyn CustomComponent<B>>),
}
pub trait Construct<B: Backend>: Sized {
type Params: Default;
fn construct(params: Self::Params) -> Self;
fn into_view(self) -> SomeView<B>;
}
pub trait ConstructGroup<B: Backend>: Construct<B> {
fn construct_group<'a, I>(params: Self::Params, children: I) -> Self
where
I: Iterator<Item = &'a View<B>> + 'a;
}
impl<B: Backend> View<B> {
pub fn new<T: Construct<B>>(params: T::Params) -> Self {
Self {
id: Id::new(),
view: T::construct(params).into_view(),
children: None,
}
}
pub fn new_group<T, I>(params: T::Params, children: I) -> Self
where
T: ConstructGroup<B>,
I: IntoIterator<Item = Self>,
{
let children: Vec<_> = children.into_iter().collect();
Self {
id: Id::new(),
view: T::construct_group(params, children.iter()).into_view(),
children: Some(children),
}
}
pub fn id(&self) -> Id {
self.id
}
}
impl<B: Backend> Render<B> for View<B> {
fn render(&self, context: &mut B::Context) -> RenderResult<B> {
match &self.view {
SomeView::Native(native) => native.render(context),
SomeView::Custom(wrapper) => todo!(),
}
}
}
pub struct Template<B: Backend = TargetBackend> {
content: Vec<View<B>>,
}
impl<B: Backend> FromIterator<View<B>> for Template<B> {
fn from_iter<T: IntoIterator<Item = View<B>>>(iter: T) -> Self {
Self {
content: iter.into_iter().collect(),
}
}
}
impl<B: Backend> Render<B> for Template<B>
where
View<B>: Render<B>,
{
fn render(&self, context: &mut B::Context) -> RenderResult<B> {
let mut result: Option<B::Output> = None;
for view in &self.content {
if let Some(x) = view.render(context)? {
result = Some(match result {
Some(result) => result.combine(x),
None => x,
})
}
}
Ok(result)
}
}
pub trait NativeComponent<B: Backend = TargetBackend>: Any + Send + Render<B> {}
pub trait NewNative<B: Backend = TargetBackend>: NativeComponent<B> + Sized {
type Params: Default;
fn new(params: Self::Params) -> Self;
}
pub trait NewNativeGroup<B: Backend = TargetBackend>: NewNative<B> {
fn new_group<'a, I>(params: Self::Params, children: I) -> Self
where
I: Iterator<Item = &'a View<B>>;
}
/// Globally unique ID for any instance of [`View`].
/// No ID will be issued twice for the entire lifetime of the application,
/// even IDs of views that have been destroyed. You shouldn't make any
/// assumptions about this type beyond that.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Id(u64);
static ID_COUNTER: AtomicU64 = AtomicU64::new(Id::SENTINEL);
impl Id {
const SENTINEL: u64 = 0;
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
let id = ID_COUNTER
.fetch_add(1, Ordering::Relaxed)
.checked_add(1)
.expect("ID counter should never overflow");
Self(id)
}
pub(crate) const fn sentinel() -> Self {
Self(Self::SENTINEL)
}
pub(crate) const fn is_sentinel(&self) -> bool {
self.0 == Self::SENTINEL
}
}
pub mod custom;
pub use custom::*;
pub mod button;
pub use button::Button;
pub mod text;
pub use text::Text;
pub mod stack;
pub use stack::{HStack, VStack};