reactivity base
This commit is contained in:
@@ -1,66 +1,8 @@
|
||||
mod node;
|
||||
pub use node::*;
|
||||
mod ui;
|
||||
mod widget;
|
||||
|
||||
use crate::primitive::{Color, Painter};
|
||||
pub use ui::*;
|
||||
pub use widget::*;
|
||||
|
||||
use crate::primitive::Color;
|
||||
pub type UIColor = Color<u8>;
|
||||
|
||||
pub trait UINode: 'static {
|
||||
fn draw(&self, painter: &mut Painter);
|
||||
}
|
||||
|
||||
pub struct UI {
|
||||
base: Box<dyn UINode>,
|
||||
}
|
||||
|
||||
impl UI {
|
||||
pub fn to_primitives(&self) -> Painter {
|
||||
let mut painter = Painter::default();
|
||||
self.base.draw(&mut painter);
|
||||
painter
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: UINode> From<N> for UI {
|
||||
fn from(node: N) -> Self {
|
||||
Self {
|
||||
base: Box::new(node),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UINode for Box<dyn UINode> {
|
||||
fn draw(&self, painter: &mut Painter) {
|
||||
self.as_ref().draw(painter);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NodeArray<const LEN: usize> {
|
||||
fn to_arr(self) -> [Box<dyn UINode>; LEN];
|
||||
}
|
||||
|
||||
// I hate this language it's so bad why do I even use it
|
||||
macro_rules! impl_node_arr {
|
||||
($n:expr;$($T:tt)+) => {
|
||||
impl<$($T: UINode,)*> NodeArray<$n> for ($($T,)*) {
|
||||
fn to_arr(self) -> [Box<dyn UINode>; $n] {
|
||||
#[allow(non_snake_case)]
|
||||
let ($($T,)*) = self;
|
||||
[$(Box::new($T),)*]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_node_arr!(1;A);
|
||||
impl_node_arr!(2;A B);
|
||||
impl_node_arr!(3;A B C);
|
||||
impl_node_arr!(4;A B C D);
|
||||
impl_node_arr!(5;A B C D E);
|
||||
impl_node_arr!(6;A B C D E F);
|
||||
impl_node_arr!(7;A B C D E F G);
|
||||
impl_node_arr!(8;A B C D E F G H);
|
||||
impl_node_arr!(9;A B C D E F G H I);
|
||||
impl_node_arr!(10;A B C D E F G H I J);
|
||||
impl_node_arr!(11;A B C D E F G H I J K);
|
||||
impl_node_arr!(12;A B C D E F G H I J K L);
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::{
|
||||
primitive::{Axis, Painter, RoundedRectData, UIRegion},
|
||||
NodeArray, UIColor, UINode,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct RoundedRect {
|
||||
pub color: UIColor,
|
||||
pub radius: f32,
|
||||
pub thickness: f32,
|
||||
pub inner_radius: f32,
|
||||
}
|
||||
|
||||
impl RoundedRect {
|
||||
pub fn color(mut self, color: UIColor) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl UINode for RoundedRect {
|
||||
fn draw(&self, painter: &mut Painter) {
|
||||
painter.write(RoundedRectData {
|
||||
color: self.color,
|
||||
radius: self.radius,
|
||||
thickness: self.thickness,
|
||||
inner_radius: self.inner_radius,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Span {
|
||||
pub elements: Vec<(Range<f32>, Box<dyn UINode>)>,
|
||||
pub axis: Axis,
|
||||
}
|
||||
|
||||
impl UINode for Span {
|
||||
fn draw(&self, painter: &mut Painter) {
|
||||
for (span, child) in &self.elements {
|
||||
let mut sub_region = UIRegion::full();
|
||||
let view = sub_region.axis_mut(self.axis);
|
||||
*view.top_left.anchor = span.start;
|
||||
*view.bot_right.anchor = span.end;
|
||||
painter.draw_within(child, sub_region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub fn proportioned<const LEN: usize>(
|
||||
axis: Axis,
|
||||
ratios: [impl UINum; LEN],
|
||||
elements: impl NodeArray<LEN>,
|
||||
) -> Self {
|
||||
let ratios = ratios.map(|r| r.to_f32());
|
||||
let total: f32 = ratios.iter().sum();
|
||||
let mut start = 0.0;
|
||||
Self {
|
||||
elements: elements
|
||||
.to_arr()
|
||||
.into_iter()
|
||||
.zip(ratios)
|
||||
.map(|(e, r)| {
|
||||
let end = start + r / total;
|
||||
let res = (start..end, e);
|
||||
start = end;
|
||||
res
|
||||
})
|
||||
.collect(),
|
||||
axis,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Regioned<N: UINode> {
|
||||
region: UIRegion,
|
||||
inner: N,
|
||||
}
|
||||
|
||||
impl<N: UINode> UINode for Regioned<N> {
|
||||
fn draw(&self, painter: &mut Painter) {
|
||||
painter.region.select(&self.region);
|
||||
self.inner.draw(painter);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Padding {
|
||||
left: f32,
|
||||
right: f32,
|
||||
top: f32,
|
||||
bottom: f32,
|
||||
}
|
||||
|
||||
impl Padding {
|
||||
pub fn uniform(amt: f32) -> Self {
|
||||
Self {
|
||||
left: amt,
|
||||
right: amt,
|
||||
top: amt,
|
||||
bottom: amt,
|
||||
}
|
||||
}
|
||||
pub fn region(&self) -> UIRegion {
|
||||
let mut region = UIRegion::full();
|
||||
region.top_left.offset.x += self.left;
|
||||
region.top_left.offset.y += self.top;
|
||||
region.bot_right.offset.x -= self.right;
|
||||
region.bot_right.offset.y -= self.bottom;
|
||||
region
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UINum> From<T> for Padding {
|
||||
fn from(amt: T) -> Self {
|
||||
Self::uniform(amt.to_f32())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NodeUtil: Sized + UINode {
|
||||
fn pad(self, padding: impl Into<Padding>) -> Regioned<Self>;
|
||||
}
|
||||
|
||||
impl<T: UINode> NodeUtil for T {
|
||||
fn pad(self, padding: impl Into<Padding>) -> Regioned<Self> {
|
||||
Regioned {
|
||||
region: padding.into().region(),
|
||||
inner: self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NodeArrayUtil<const LEN: usize> {
|
||||
fn proportioned(self, axis: Axis, ratios: [impl UINum; LEN]) -> Span;
|
||||
}
|
||||
|
||||
impl<T: NodeArray<LEN>, const LEN: usize> NodeArrayUtil<LEN> for T {
|
||||
fn proportioned(self, axis: Axis, ratios: [impl UINum; LEN]) -> Span {
|
||||
Span::proportioned(axis, ratios, self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UINum {
|
||||
fn to_f32(self) -> f32;
|
||||
}
|
||||
|
||||
impl UINum for f32 {
|
||||
fn to_f32(self) -> f32 {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl UINum for u32 {
|
||||
fn to_f32(self) -> f32 {
|
||||
self as f32
|
||||
}
|
||||
}
|
||||
|
||||
impl UINum for i32 {
|
||||
fn to_f32(self) -> f32 {
|
||||
self as f32
|
||||
}
|
||||
}
|
||||
101
src/layout/ui.rs
Normal file
101
src/layout/ui.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use crate::{
|
||||
primitive::{Painter, Primitives},
|
||||
util::{IDTracker, ID},
|
||||
HashMap, Widget, WidgetId, WidgetLike, WidgetRef,
|
||||
};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::RefCell,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
pub struct UI {
|
||||
ids: IDTracker,
|
||||
base: Option<WidgetId>,
|
||||
pub widgets: Widgets,
|
||||
}
|
||||
|
||||
pub struct Widgets(HashMap<ID, Box<dyn Widget>>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UIBuilder {
|
||||
ui: Rc<RefCell<UI>>,
|
||||
}
|
||||
|
||||
impl From<UI> for UIBuilder {
|
||||
fn from(ui: UI) -> Self {
|
||||
UIBuilder {
|
||||
ui: Rc::new(RefCell::new(ui)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UIBuilder {
|
||||
pub fn add<W: Widget>(&mut self, w: W) -> WidgetRef<W> {
|
||||
WidgetRef::new(self.clone(), [self.push(w)])
|
||||
}
|
||||
|
||||
pub fn push<W: Widget>(&mut self, w: W) -> WidgetId {
|
||||
let mut ui = self.ui.borrow_mut();
|
||||
let id = ui.ids.next();
|
||||
ui.widgets.insert(id.duplicate(), w);
|
||||
WidgetId::new(id, TypeId::of::<W>())
|
||||
}
|
||||
|
||||
pub fn finish<WL: WidgetLike<W>, W>(mut self, base: WL) -> UI {
|
||||
let base = base.id(&mut self).erase_type();
|
||||
let mut ui = Rc::into_inner(self.ui).unwrap().into_inner();
|
||||
ui.base = Some(base);
|
||||
ui
|
||||
}
|
||||
}
|
||||
|
||||
impl UI {
|
||||
pub fn build() -> UIBuilder {
|
||||
Self::empty().into()
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
ids: IDTracker::new(),
|
||||
base: None,
|
||||
widgets: Widgets::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_primitives(&self) -> Primitives {
|
||||
let mut painter = Painter::new(&self.widgets);
|
||||
if let Some(base) = &self.base {
|
||||
painter.draw(base);
|
||||
}
|
||||
painter.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Widgets {
|
||||
fn new() -> Self {
|
||||
Self(HashMap::new())
|
||||
}
|
||||
|
||||
pub fn get(&self, id: &WidgetId) -> &dyn Widget {
|
||||
self.0.get(&id.id).unwrap().as_ref()
|
||||
}
|
||||
|
||||
pub fn get_mut<W: Widget>(&mut self, id: &WidgetId<W>) -> Option<&mut W> {
|
||||
self.0.get_mut(&id.id).unwrap().as_any_mut().downcast_mut()
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, id: ID, widget: impl Widget) {
|
||||
self.0.insert(id, Box::new(widget));
|
||||
}
|
||||
|
||||
pub fn insert_any(&mut self, id: ID, widget: Box<dyn Widget>) {
|
||||
self.0.insert(id, widget);
|
||||
}
|
||||
}
|
||||
|
||||
impl dyn Widget {
|
||||
pub fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
157
src/layout/widget.rs
Normal file
157
src/layout/widget.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use crate::{primitive::Painter, util::ID, UIBuilder};
|
||||
|
||||
pub trait Widget: 'static + Any {
|
||||
fn draw(&self, painter: &mut Painter);
|
||||
}
|
||||
|
||||
impl<W: Widget> Widget for (W,) {
|
||||
fn draw(&self, painter: &mut Painter) {
|
||||
self.0.draw(painter);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, Hash, PartialEq, Debug)]
|
||||
pub struct WidgetId<W = ()> {
|
||||
pub(super) ty: TypeId,
|
||||
pub(super) id: ID,
|
||||
_pd: PhantomData<W>,
|
||||
}
|
||||
|
||||
// TODO: temp
|
||||
impl Clone for WidgetId {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
ty: self.ty,
|
||||
id: self.id.duplicate(),
|
||||
_pd: self._pd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> WidgetId<W> {
|
||||
pub(super) fn new(id: ID, ty: TypeId) -> Self {
|
||||
Self {
|
||||
ty,
|
||||
id,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn erase_type(self) -> WidgetId<()> {
|
||||
self.cast_type()
|
||||
}
|
||||
|
||||
fn cast_type<W2>(self) -> WidgetId<W2> {
|
||||
WidgetId {
|
||||
ty: self.ty,
|
||||
id: self.id,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WidgetLike<W> {
|
||||
fn id(self, ui: &mut UIBuilder) -> WidgetId<W>;
|
||||
}
|
||||
|
||||
/// wouldn't be needed if negative trait bounds & disjoint impls existed
|
||||
pub struct WidgetFn<F: FnOnce(&mut UIBuilder) -> W, W>(pub F);
|
||||
|
||||
impl<W: Widget, F: FnOnce(&mut UIBuilder) -> W> WidgetLike<W> for WidgetFn<F, W> {
|
||||
fn id(self, ui: &mut UIBuilder) -> WidgetId<W> {
|
||||
let w = (self.0)(ui);
|
||||
ui.add(w).to_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget> WidgetLike<W> for W {
|
||||
fn id(self, ui: &mut UIBuilder) -> WidgetId<W> {
|
||||
ui.add(self).to_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> WidgetLike<W> for WidgetId<W> {
|
||||
fn id(self, _: &mut UIBuilder) -> WidgetId<W> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> WidgetLike<W> for WidgetArr<1, (W,)> {
|
||||
fn id(self, _: &mut UIBuilder) -> WidgetId<W> {
|
||||
let [id] = self.arr;
|
||||
id.cast_type()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WidgetArr<const LEN: usize, Ws> {
|
||||
pub ui: UIBuilder,
|
||||
pub arr: [WidgetId<()>; LEN],
|
||||
_pd: PhantomData<Ws>,
|
||||
}
|
||||
|
||||
impl<const LEN: usize, Ws> WidgetArr<LEN, Ws> {
|
||||
pub fn new(ui: UIBuilder, arr: [WidgetId<()>; LEN]) -> Self {
|
||||
Self {
|
||||
ui,
|
||||
arr,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type WidgetRef<W> = WidgetArr<1, (W,)>;
|
||||
|
||||
impl<W> WidgetRef<W> {
|
||||
pub fn handle(&self) -> WidgetId<W> {
|
||||
let [id] = &self.arr;
|
||||
id.clone().cast_type()
|
||||
}
|
||||
pub fn to_id(self) -> WidgetId<W> {
|
||||
let [id] = self.arr;
|
||||
id.cast_type()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WidgetArrLike<const LEN: usize, Ws> {
|
||||
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<LEN, Ws>;
|
||||
}
|
||||
|
||||
impl<const LEN: usize, Ws> WidgetArrLike<LEN, Ws> for WidgetArr<LEN, Ws> {
|
||||
fn ui(self, _: &mut UIBuilder) -> WidgetArr<LEN, Ws> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// I hate this language it's so bad why do I even use it
|
||||
macro_rules! impl_node_arr {
|
||||
($n:expr;$($T:tt)*) => {
|
||||
impl<$($T,${concat($T,$T)}: WidgetLike<$T>,)*> WidgetArrLike<$n, ($($T,)*)> for ($(${concat($T,$T)},)*) {
|
||||
#[allow(unused_variables)]
|
||||
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<$n, ($($T,)*)> {
|
||||
#[allow(non_snake_case)]
|
||||
let ($($T,)*) = self;
|
||||
WidgetArr::new(
|
||||
ui.clone(),
|
||||
[$($T.id(ui).cast_type(),)*],
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_node_arr!(1;A);
|
||||
impl_node_arr!(2;A B);
|
||||
impl_node_arr!(3;A B C);
|
||||
impl_node_arr!(4;A B C D);
|
||||
impl_node_arr!(5;A B C D E);
|
||||
impl_node_arr!(6;A B C D E F);
|
||||
impl_node_arr!(7;A B C D E F G);
|
||||
impl_node_arr!(8;A B C D E F G H);
|
||||
impl_node_arr!(9;A B C D E F G H I);
|
||||
impl_node_arr!(10;A B C D E F G H I J);
|
||||
impl_node_arr!(11;A B C D E F G H I J K);
|
||||
impl_node_arr!(12;A B C D E F G H I J K L);
|
||||
Reference in New Issue
Block a user