File reorganization

This commit is contained in:
Andrey Tkachenko 2018-08-25 13:18:33 +04:00
parent fca8a56cb3
commit b10c5c96b1
50 changed files with 1249 additions and 174 deletions

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
/target
target
**/*.rs.bk
Cargo.lock

3
maple-core/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock

View File

@ -1,6 +1,9 @@
cargo-features = ["edition"]
[package]
name = "vdom_lib"
name = "maple-core"
version = "0.1.0"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2018"
[dependencies]

View File

@ -1,5 +1,7 @@
use std::marker::PhantomData;
use crate::component::ComponentMsg;
pub trait Callback {
type Arg;

View File

@ -1,3 +1,7 @@
use crate::engine::Engine;
use crate::renderable::Renderable;
use std::marker::PhantomData;
#[macro_export]
macro_rules! children {
($w:expr) => ($w);
@ -6,8 +10,8 @@ macro_rules! children {
pub struct Children2<E, CH0, CH1>
where E: Engine,
CH0: Renderable<Engine = E>,
CH1: Renderable<Engine = E>
CH0: Renderable<E>,
CH1: Renderable<E>
{
_e: PhantomData<E>,
@ -17,8 +21,8 @@ pub struct Children2<E, CH0, CH1>
impl<E, CH0, CH1> Children2<E, CH0, CH1>
where E: Engine,
CH0: Renderable<Engine = E>,
CH1: Renderable<Engine = E>
CH0: Renderable<E>,
CH1: Renderable<E>
{
pub fn new(child0: CH0, child1: CH1) -> Self {
Self {
@ -29,14 +33,12 @@ impl<E, CH0, CH1> Children2<E, CH0, CH1>
}
}
impl<E, CH0, CH1> Renderable for Children2<E, CH0, CH1>
impl<E, CH0, CH1> Renderable<E> for Children2<E, CH0, CH1>
where E: Engine,
CH0: Renderable<Engine = E>,
CH1: Renderable<Engine = E>
CH0: Renderable<E>,
CH1: Renderable<E>
{
type Engine = E;
fn render(&self, eng: &Self::Engine) {
fn render(&self, eng: &E) {
self.child0.render(eng);
self.child1.render(eng);
}

View File

@ -1,4 +1,6 @@
use std::default::Default;
pub trait Component {
type Props: Default;
type Msg = !;
@ -10,6 +12,11 @@ pub trait Component {
}
}
pub trait ComponentMsg {
type Component: Component;
}
impl Component for ! {
type Props = ();
@ -18,10 +25,6 @@ impl Component for ! {
}
}
pub trait ComponentMsg {
type Component: Component;
}
impl ComponentMsg for () {
type Component = !;
}

25
maple-core/src/lib.rs Normal file
View File

@ -0,0 +1,25 @@
#![feature(associated_type_defaults, never_type, unsize, specialization)]
pub mod convert;
pub mod engine;
pub mod context;
pub mod callback;
pub mod component;
#[macro_use]
pub mod children;
pub mod renderable;
pub mod node;
pub mod view;
pub mod prelude {
pub use crate::convert::{MyFrom, MyInto};
pub use crate::engine::Engine;
pub use crate::context::{Context, DefaultContext};
pub use crate::callback::{Callback, Callback1};
pub use crate::component::{Component, ComponentMsg};
pub use crate::children::{Children2};
pub use crate::renderable::{Renderable, RenderImplementation, Stub};
pub use crate::node::Node;
pub use crate::view::View;
}

46
maple-core/src/node.rs Normal file
View File

@ -0,0 +1,46 @@
use crate::engine::Engine;
use crate::renderable::{Renderable, RenderImplementation};
use std::marker::PhantomData;
///
///
///
///
pub struct Node <E, I, CHE, CH>
where
E: Engine,
CHE: Engine,
I: RenderImplementation<E, CHE>,
CH: Renderable<CHE>
{
item: I,
children: CH,
_m: PhantomData<(E, CHE)>
}
impl<E, I, CHE, CH> Node<E, I, CHE, CH>
where
E: Engine,
CHE: Engine,
I: RenderImplementation<E, CHE>,
CH: Renderable<CHE>
{
pub fn new(item: I, children: CH) -> Self {
Self {
item,
children,
_m: Default::default()
}
}
}
impl<E, I, CHE, CH> Renderable<E> for Node<E, I, CHE, CH>
where
E: Engine,
CHE: Engine,
I: RenderImplementation<E, CHE>,
CH: Renderable<CHE>
{
fn render(&self, eng: &E) {
self.item.render_impl(eng, &self.children)
}
}

View File

@ -0,0 +1,76 @@
use std::marker::PhantomData;
use crate::engine::Engine;
/// defines implementation of how the children elements binds to the component
/// in dom backend it actually attaches/detaches elements to each other
///
///
pub trait RenderImplementation<E, CE>
where E: Engine,
CE: Engine,
{
fn render_impl<C: Renderable<CE>>(&self, eng: &E, children: &C);
}
/// Trait Renderable
/// defines implementation of how the component should be rendered itself
///
///
///
pub trait Renderable<E>
where E: Engine,
{
fn render(&self, eng: &E);
}
///
/// for nodes without children
///
///
///
#[derive(Default)]
pub struct Stub<E: Engine> {
_e: PhantomData<E>
}
impl<E: Engine> Renderable<E> for Stub<E> {
fn render(&self, _eng: &E) {}
}
// default impl<E, T> Renderable<E> for T
// where E: Engine,
// T: RenderImplementation<E, E>,
// {
// fn render(&self, eng: &E) {
// self.render_impl(eng, &Stub::default())
// }
// }
impl<E: Engine> Renderable<E> for Box<dyn Renderable<E>> {
fn render(&self, eng: &E) {
(**self).render(eng);
}
}
impl <E, T> Renderable<E> for Vec<T>
where E: Engine,
T: Renderable<E>
{
fn render(&self, eng: &E) {
for child in self.iter() {
child.render(eng);
}
}
}
impl <E, T> Renderable<E> for Option<T>
where E: Engine,
T: Renderable<E>
{
fn render(&self, eng: &E) {
if let Some(inner) = self {
inner.render(eng);
}
}
}

View File

@ -1,21 +1,21 @@
use crate::engine::Engine;
use crate::renderable::Renderable;
use crate::context::Context;
///
///
///
///
///
pub trait View {
pub trait View<E, CE>
where E: Engine,
CE: Engine,
{
type InputContext: Context;
type OutputContext: Context;
type Renderable: Renderable<E>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext;
}
pub trait ViewX {
type Engine: Engine;
type ChildrenEngine: Engine = Self::Engine;
type Renderable: Renderable<Engine = Self::Engine>;
fn build<C: Renderable<Engine = Self::ChildrenEngine> + 'static>(self, children: Option<C>) -> Self::Renderable;
fn build<C: Renderable<CE> + 'static>(self, children: Option<C>) -> Self::Renderable;
}
// It will have sense when `specialization` will be ready to use

3
maple-examples/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock

View File

@ -0,0 +1,8 @@
[package]
name = "maple-examples"
version = "0.1.0"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
[dependencies]
maple = {path = "../maple"}
maple-stdweb-dom = {path = "../maple-stdweb-dom"}

View File

@ -0,0 +1,82 @@
#![feature(proc_macro_non_items)]
#![allow(dead_code)]
#[macro_use]
extern crate maple;
extern crate maple_stdweb_dom;
macro_rules! children {
($w:expr) => ($w);
($w1:expr, $($rest:tt)*) => (Children2::new($w1, children!($($rest)*)));
}
use maple::prelude::*;
use std::default::Default;
use maple::prelude::tabs::*;
use maple::prelude::prelude::Panel;
use maple_stdweb_dom::*;
// fn get_rest(count: usize) -> impl Renderable<HtmlEngine> {
// let mut vec = Vec::new();
// for i in 0..count {
// vec.push(view! { <Span> "Number " {format!("{}", i + 1)} </Span> });
// }
// return vec;
// }
fn main() {
let x = {
let ctx_default = DefaultContext;
let mut p0_props = <Div as Component>::Props::default();
let mut comp_p0 = Div::create(p0_props);
// let ctx_p0 = comp_p0.receive_context(ctx_default.clone());
// let p0 = comp_p0.build::<Stub<_>>(None);
// p0
0
};
// let x = view! {
// <Div>
// <Div>
// <Canvas>
// <Circle cx={95} cy={70} r={20} />
// <Rect x1={20} y1={20} x2={150} y2={100} />
// </Canvas>
// </Div>
// <Panel>
// <Tabs on_tab_change={|idx| println!("Tab has changed! New tab idx {}", idx)}>
// <Header />
// <Body>
// <Tab title="Tab 1">
// <Span>"Tab 1 Content"</Span>
// </Tab>
// <Tab title="Tab 2">
// <Button>"But in Tab " {1 + 1} </Button>
// </Tab>
// <Tab title="Tab 3">
// { ... get_rest(5) }
// </Tab>
// </Body>
// </Tabs>
// </Panel>
// </Div>
// };
let eng = HtmlEngine::new();
// x.render(&eng);
println!("{}", eng.to_string());
}

View File

@ -0,0 +1,7 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

10
maple-macro/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk

16
maple-macro/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
cargo-features = ["edition"]
[package]
name = "maple-macro"
version = "0.1.1"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2018"
[dependencies]
syn = {version = "*", features=["full", "extra-traits"]}
quote = "*"
proc-macro2 = {version = "*", features=["nightly", "proc-macro"]}
maple-core = { path="../maple-core" }
[lib]
proc-macro = true

1
maple-macro/README.md Normal file
View File

@ -0,0 +1 @@
# maple-macro

View File

@ -0,0 +1,46 @@
use syn::{Expr, ExprLit, Ident};
use syn::synom::Synom;
use quote::ToTokens;
#[derive(Debug)]
crate struct Attribute {
pub name: Ident,
pub value: AttributeValue
}
impl Synom for Attribute {
named!(parse -> Self, do_parse!(
name: syn!(Ident) >>
punct!(=) >>
value: syn!(AttributeValue) >>
(Attribute { name, value })
));
}
#[derive(Debug)]
crate enum AttributeValue {
Expr(Expr),
Literal(ExprLit)
}
impl Synom for AttributeValue {
named!(parse -> Self, do_parse!(
value: alt!(
syn!(ExprLit) => { |d| AttributeValue::Literal(d) }
|
braces!(syn!(Expr)) => { |d| AttributeValue::Expr(d.1) }
) >>
(value)
));
}
impl ToTokens for AttributeValue {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self {
AttributeValue::Literal(s) => s.to_tokens(tokens),
AttributeValue::Expr(e) => e.to_tokens(tokens)
};
}
}

134
maple-macro/src/lib.rs Normal file
View File

@ -0,0 +1,134 @@
mod attribute;
mod node;
mod tag;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::{TokenStream};
use syn::synom::Synom;
use syn::{TypePath, Ident, Expr};
use syn::buffer::{TokenBuffer};
// use syn::parsers::{named, do_parse, punct, syn, call, alt, braces, many0};
use quote::*;
use self::node::Node;
use self::attribute::{Attribute, AttributeValue};
fn visit_attribute(ident: &Ident, attr: &Attribute, _ctx: &TypePath) -> impl ToTokens {
let attr_name = &attr.name;
let attr_value = &attr.value;
match attr_value {
AttributeValue::Expr(Expr::Closure(closure)) => quote! {
#ident.#attr_name = Callback1::new({#closure}).my_into();
},
_ => quote! { #ident.#attr_name = {#attr_value}.my_into(); }
}
}
fn visit_node(parent: &str, idx: &mut u32, node: &Node, stream: &mut proc_macro2::TokenStream, thread: &mut proc_macro2::TokenStream) {
let ii_name = &("p".to_string() + &idx.to_string());
let ii = Ident::new(ii_name, proc_macro2::Span::call_site());
let ctx_ii = Ident::new(&("ctx_".to_string() + ii_name), proc_macro2::Span::call_site());
let comp_ii = Ident::new(&("comp_".to_string() + ii_name), proc_macro2::Span::call_site());
let pp = Ident::new(&("ctx_".to_string() + parent), proc_macro2::Span::call_site());
match node {
Node::Text(txt) => {
let toks = quote!{
let #ii = Text::create(<Text as Component>::Props::new(#txt));
};
toks.to_tokens(stream);
},
Node::Expr(exr) => {
let toks = quote!{
let #ii = Text::create(<Text as Component>::Props::new(format!("{:?}", #exr)));
};
toks.to_tokens(stream);
},
Node::Embed(exr) => {
let toks = quote!{
let #ii = #exr;
};
toks.to_tokens(stream);
},
Node::Tag(tag) => {
let iip = Ident::new(&(ii_name.to_string() + "_props"), proc_macro2::Span::call_site());
let path = &tag.path;
#[allow(unused_mut)]
let mut children = Vec::new();
let attributes = tag.attributes
.iter()
.map(|attr| visit_attribute(&iip, &attr, &path));
let toks = quote!{
#[allow(unused_mut)]
let mut #iip = <#path as Component>::Props::default();
#(#attributes;)*
let mut #comp_ii = #path::create(#iip);
#[allow(unused_variables)]
let #ctx_ii = #comp_ii.receive_context(#pp.clone());
};
toks.to_tokens(stream);
for child in tag.body.iter() {
*idx += 1;
let ch_name = "p".to_string() + &idx.to_string();
let ch_ii = Ident::new(&ch_name, proc_macro2::Span::call_site());
children.push(ch_ii);
visit_node(ii_name, idx, child, stream, thread);
}
let toks = if children.len() > 0 {
quote!{
let #ii = #comp_ii.build(Some(children![#(#children),*]));
}
} else {
quote!{
let #ii = #comp_ii.build::<Stub<_>>(None);
}
};
toks.to_tokens(thread);
}
};
}
#[proc_macro]
pub fn view(input: TokenStream) -> TokenStream {
let buffer = TokenBuffer::new(input);
let cursor = buffer.begin();
let res = Node::parse(cursor);
let mut output = proc_macro2::TokenStream::new();
let mut thread = proc_macro2::TokenStream::new();
let mut idx = 0;
match res {
Ok((root, _)) => visit_node("default", &mut idx, &root, &mut output, &mut thread),
Err(err) => panic!("Error: {:?}", err)
};
let toks = quote! {{
let ctx_default = DefaultContext;
#output
#thread
p0
}};
toks.into()
}

34
maple-macro/src/node.rs Normal file
View File

@ -0,0 +1,34 @@
use syn::synom::Synom;
use syn::{LitStr, Expr};
use crate::tag::Tag;
#[derive(Debug)]
crate enum Node {
Tag(Tag),
Text(LitStr),
Expr(Expr),
Embed(Expr)
}
impl Synom for Node {
named!(parse -> Self, do_parse!(
value: alt!(
syn!(Tag) => { |tag| Node::Tag(tag) }
|
braces!(
do_parse!(
punct!(.) >>
punct!(.) >>
punct!(.) >>
tt: syn!(Expr) >> (tt)
)) => { |d| Node::Embed(d.1) }
|
braces!(syn!(Expr)) => { |d| Node::Expr(d.1) }
|
syn!(LitStr) => { |_str| Node::Text(_str) }
) >>
(value)
));
}

58
maple-macro/src/tag.rs Normal file
View File

@ -0,0 +1,58 @@
use crate::attribute::Attribute;
use crate::node::Node;
use syn::TypePath;
use syn::synom::Synom;
#[derive(Debug)]
crate struct Tag {
pub path: TypePath,
pub attributes: Vec<Attribute>,
pub body: Vec<Node>
}
impl Tag {
pub fn new(open: TypePath, close: Option<TypePath>, attrs: Vec<Attribute>, body: Vec<Node>) -> Result<Self, String> {
if let Some(close_ref) = &close {
if &open != close_ref {
return Err(format!("Open({}) and closing({}) tags are not matching!", quote!{#open}, quote!{#close_ref}))
}
}
Ok(Tag {
path: open,
attributes: attrs,
body
})
}
}
impl Synom for Tag {
named!(parse -> Self, do_parse!(
t: alt!(
do_parse!(
punct!(<) >>
open: syn!(TypePath) >>
attrs: many0!(syn!(Attribute)) >>
punct!(>) >>
body: many0!(syn!(Node)) >>
punct!(<) >> punct!(/) >>
close: syn!(TypePath) >>
punct!(>) >>
(match Tag::new(open, Some(close), attrs, body) {
Ok(v) => v,
Err(e) => panic!("{:?}", e)
})
) => {|x|x}
|
do_parse!(
punct!(<) >>
open: syn!(TypePath) >>
attrs: many0!(syn!(Attribute)) >>
punct!(/) >>
punct!(>) >>
(Tag::new(open, None, attrs, vec![]).unwrap())
) => {|x|x}
) >> (t)
));
}

10
maple-stdui/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk

11
maple-stdui/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
cargo-features = ["edition"]
[package]
name = "maple-stdui"
version = "0.1.0"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2018"
[dependencies]
maple-macro = {path = "../maple-macro"}
maple-core = {path = "../maple-core"}

9
maple-stdui/src/lib.rs Normal file
View File

@ -0,0 +1,9 @@
pub mod panel;
pub mod tabs;
pub mod text;
pub mod prelude {
pub use super::panel::*;
pub use super::tabs;
pub use super::text::*;
}

16
maple-stdui/src/panel.rs Normal file
View File

@ -0,0 +1,16 @@
use maple_core::prelude::*;
pub struct Panel {
props: PanelProps
}
#[derive(Default)]
pub struct PanelProps {}
impl Component for Panel {
type Props = PanelProps;
fn create(props: PanelProps) -> Self {
Panel { props }
}
}

168
maple-stdui/src/tabs.rs Normal file
View File

@ -0,0 +1,168 @@
use std::ops::Deref;
use maple_core::prelude::*;
use std::rc::Rc;
use std::cell::RefCell;
#[derive(Clone)]
pub struct TabsContext<T: Context> {
inner: T,
tabs: Rc<RefCell<Vec<Option<&'static str>>>>
}
impl<T: Context> TabsContext<T> {
pub fn add_tab(&self, tab_title: Option<&'static str>) {
self.tabs.borrow_mut().push(tab_title);
}
pub fn get_tabs(&self) -> Vec<Option<&'static str>> {
return self.tabs.borrow().clone()
}
}
impl<T: Context> Context for TabsContext<T> {
type Context = T;
fn unwrap(self) -> Self::Context {
self.inner
}
fn wrap(ctx: Self::Context) -> Self {
Self {
inner: ctx,
tabs: Default::default()
}
}
}
#[derive(Clone)]
pub struct TabsBodyContext<T: Context> { inner: TabsContext<T> }
impl<T: Context> Context for TabsBodyContext<T> {
type Context = TabsContext<T>;
fn unwrap(self) -> Self::Context {
self.inner
}
fn wrap(ctx: Self::Context) -> Self {
Self {
inner: ctx
}
}
}
impl<T: Context> Deref for TabsBodyContext<T> {
type Target = TabsContext<T>;
fn deref(&self) -> &Self::Target {
return &self.inner;
}
}
pub struct Tabs {
props: TabsProps
}
pub enum TabsEvent {
TabChange(usize)
}
#[derive(Default)]
pub struct TabsProps {
pub on_tab_change: Option<Box<dyn Callback<Arg=usize>>>
}
impl Component for Tabs {
type Props = TabsProps;
type Msg = TabsEvent;
fn create(props: TabsProps) -> Self {
Tabs { props }
}
fn update(&mut self, msg: Self::Msg) -> bool {
match msg {
TabsEvent::TabChange(idx) => {
if let Some(cb) = &self.props.on_tab_change {
cb.call(idx);
}
}
};
false
}
}
pub struct Header {
props: HeaderProps,
tabs_ctx: Option<TabsContext<DefaultContext>>
}
pub enum HeaderEvent {
Click(usize)
}
impl ComponentMsg for HeaderEvent {
type Component = Header;
}
#[derive(Default)]
pub struct HeaderProps {
pub on_click: Option<Box<dyn Callback<Arg=usize>>>
}
impl Component for Header {
type Props = HeaderProps;
type Msg = HeaderEvent;
fn create(props: HeaderProps) -> Self {
Header {
props,
tabs_ctx: Default::default()
}
}
fn update(&mut self, msg: Self::Msg) -> bool {
match msg {
HeaderEvent::Click(idx) => {
if let Some(cb) = &self.props.on_click {
cb.call(idx);
}
}
};
false
}
}
pub struct Body {
props: BodyProps
}
#[derive(Default)]
pub struct BodyProps {}
impl Component for Body {
type Props = BodyProps;
fn create(props: BodyProps) -> Self {
Body { props }
}
}
pub struct Tab {
props: TabProps
}
#[derive(Default)]
pub struct TabProps {
pub title: Option<&'static str>
}
impl Component for Tab {
type Props = TabProps;
fn create(props: TabProps) -> Self {
Tab { props }
}
}

27
maple-stdui/src/text.rs Normal file
View File

@ -0,0 +1,27 @@
use std::borrow::Cow;
use maple_core::prelude::Component;
pub struct Text {
pub props: TextProps
}
#[derive(Default)]
pub struct TextProps {
pub text: Option<Cow<'static, str>>
}
impl TextProps {
pub fn new<T: Into<Cow<'static, str>>>(txt: T) -> Self {
TextProps {
text: Some(txt.into())
}
}
}
impl Component for Text {
type Props = TextProps;
fn create(props: TextProps) -> Self {
Self { props }
}
}

3
maple-stdweb-ui/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock

View File

@ -0,0 +1,14 @@
cargo-features = ["edition"]
[package]
name = "maple-stdweb-ui"
version = "0.1.0"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2018"
[dependencies]
maple-core = {path = "../maple-core"}
maple-stdweb = {path = "../maple-stdweb"}
maple-stdui = {path = "../maple-stdui"}
maple-macro = {path = "../maple-macro"}
maple-stdweb-ui = {path = "../maple-stdweb-ui"}

View File

@ -0,0 +1,9 @@
#![feature(proc_macro_non_items, existential_type)]
pub mod panel;
pub mod tabs;
pub use self::panel::*;
pub use self::tabs::*;
pub use maple_stdweb_ui::HtmlEngine;

View File

@ -0,0 +1,30 @@
use maple_core::prelude::*;
use super::HtmlEngine;
use maple_macro::view;
use maple_stdweb::*;
use maple_stdui::prelude::Panel;
impl View for Panel {
type InputContext = DefaultContext;
type OutputContext = DefaultContext;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
ctx
}
}
impl ViewX for Panel {
type Engine = HtmlEngine;
type ChildrenEngine = HtmlEngine;
existential type Renderable: Renderable<Engine = HtmlEngine>;
fn build<C>(self, children: Option<C>) -> Self::Renderable
where C: Renderable<Engine = Self::ChildrenEngine> + 'static
{
view! {
<Div class="panel">
{ ...children }
</Div>
}
}
}

111
maple-stdweb-ui/src/tabs.rs Normal file
View File

@ -0,0 +1,111 @@
use maple_core::prelude::*;
use maple_macro::view;
use maple_stdweb::*;
use maple_stdui::prelude::tabs::*;
use super::HtmlEngine;
use std::ops::Deref;
use std::rc::Rc;
use std::cell::RefCell;
impl View for Tabs {
type InputContext = DefaultContext;
type OutputContext = TabsContext<DefaultContext>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
TabsContext::wrap(ctx)
}
}
impl Tabs {
pub fn build<C>(self, children: Option<C>) -> impl Renderable<Engine = HtmlEngine>
where C: Renderable<Engine = HtmlEngine> + 'static
{
view! {
<Div class="tabs">
{ ... children }
</Div>
}
}
}
impl View for Header {
type InputContext = TabsContext<DefaultContext>;
type OutputContext = DefaultContext;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
self.tabs_ctx = Some(ctx.clone());
ctx.unwrap()
}
}
impl Header {
pub fn build<C>(self, _children: Option<C>) -> impl Renderable<Engine = HtmlEngine>
where C: Renderable<Engine = HtmlEngine> + 'static
{
let tabs = self.tabs_ctx.map(|ctx|
ctx.get_tabs()
.iter()
.enumerate()
.map(|(i, tab)| view! {
<Span class="tab-title" click={move |_| HeaderEvent::Click(i)}>
{match tab {
Some(val) => val,
None => "<No title>"
}}
</Span>
})
.collect::<Vec<_>>());
view! {
<Div class="tabs-header">
{ ...tabs }
</Div>
}
}
}
impl View for Body {
type InputContext = TabsContext<DefaultContext>;
type OutputContext = TabsBodyContext<DefaultContext>;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
TabsBodyContext::wrap(ctx)
}
}
impl Body {
pub fn build<C>(self, children: Option<C>) -> impl Renderable<Engine = HtmlEngine>
where C: Renderable<Engine = HtmlEngine> + 'static
{
view! {
<Div class="tab">
{ ... children }
</Div>
}
}
}
impl View for Tab {
type InputContext = TabsBodyContext<DefaultContext>;
type OutputContext = DefaultContext;
fn receive_context(&mut self, ctx: Self::InputContext) -> Self::OutputContext {
ctx.add_tab(self.props.title);
ctx.unwrap().unwrap()
}
}
impl Tab {
pub fn build<C>(self, children: Option<C>) -> impl Renderable<Engine = HtmlEngine>
where C: Renderable<Engine = HtmlEngine> + 'static
{
view! {
<Div class="tab">
{ ... children }
</Div>
}
}
}

3
maple-stdweb/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock

10
maple-stdweb/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
cargo-features = ["edition"]
[package]
name = "maple-stdweb"
version = "0.1.0"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2018"
[dependencies]
maple-core = {path = '../maple-core'}

View File

@ -0,0 +1,30 @@
use maple_core::prelude::Component;
pub enum ButtonEvents {
Click
}
pub struct Button {
props: ButtonProps
}
#[derive(Default)]
pub struct ButtonProps {
pub id: Option<&'static str>,
pub name: Option<&'static str>,
pub class: Option<&'static str>,
pub click: Option<Box<Fn() -> ButtonEvents>>
}
impl Component for Button {
type Props = ButtonProps;
type Msg = ButtonEvents;
fn create(props: ButtonProps) -> Self {
Button { props }
}
fn update(&mut self, msg: Self::Msg) -> bool {
false
}
}

View File

@ -0,0 +1,30 @@
use maple_core::prelude::Component;
pub enum CanvasEvents {
Click
}
pub struct Canvas {
pub props: CanvasProps
}
#[derive(Default)]
pub struct CanvasProps {
pub id: Option<&'static str>,
pub name: Option<&'static str>,
pub class: Option<&'static str>,
pub click: Option<Box<Fn() -> CanvasEvents>>
}
impl Component for Canvas {
type Props = CanvasProps;
type Msg = CanvasEvents;
fn create(props: CanvasProps) -> Self {
Canvas { props }
}
fn update(&mut self, msg: Self::Msg) -> bool {
true
}
}

View File

@ -0,0 +1,23 @@
use maple_core::prelude::Component;
pub struct Circle {
pub props: CircleProps
}
#[derive(Default)]
pub struct CircleProps {
pub id: Option<&'static str>,
pub fill: Option<&'static str>,
pub stroke: Option<&'static str>,
pub cx: Option<u32>,
pub cy: Option<u32>,
pub r: Option<u32>
}
impl Component for Circle {
type Props = CircleProps;
fn create(props: CircleProps) -> Self {
Circle { props }
}
}

View File

@ -0,0 +1,5 @@
mod circle;
mod rect;
pub use self::circle::{Circle, CircleProps};
pub use self::rect::{Rect, RectProps};

View File

@ -0,0 +1,25 @@
use maple_core::prelude::Component;
pub struct Rect {
pub props: RectProps
}
#[derive(Default)]
pub struct RectProps {
pub id: Option<&'static str>,
pub name: Option<&'static str>,
pub fill: Option<&'static str>,
pub stroke: Option<&'static str>,
pub x1: Option<u32>,
pub y1: Option<u32>,
pub x2: Option<u32>,
pub y2: Option<u32>
}
impl Component for Rect {
type Props = RectProps;
fn create(props: RectProps) -> Self {
Rect { props }
}
}

32
maple-stdweb/src/div.rs Normal file
View File

@ -0,0 +1,32 @@
use maple_core::prelude::*;
pub enum DivEvents {
Click
}
pub struct Div {
pub props: DivProps
}
#[derive(Default)]
pub struct DivProps {
pub id: Option<&'static str>,
pub name: Option<&'static str>,
pub class: Option<&'static str>,
pub click: Option<Box<Fn() -> DivEvents>>
}
impl Component for Div {
type Props = DivProps;
type Msg = DivEvents;
fn create(props: DivProps) -> Self {
Div {
props
}
}
fn update(&mut self, msg: Self::Msg) -> bool {
false
}
}

39
maple-stdweb/src/lib.rs Normal file
View File

@ -0,0 +1,39 @@
mod div;
mod span;
mod button;
mod canvas;
mod canvas_comp;
pub use self::canvas_comp::*;
pub use self::div::{Div, DivEvents, DivProps};
pub use self::span::{Span, SpanProps};
pub use self::button::{Button, ButtonEvents, ButtonProps};
pub use self::canvas::{Canvas, CanvasEvents, CanvasProps};
use std::borrow::Cow;
use maple_core::prelude::Component;
pub struct Text {
pub props: TextProps
}
#[derive(Default)]
pub struct TextProps {
pub text: Option<Cow<'static, str>>
}
impl TextProps {
pub fn new<T: Into<Cow<'static, str>>>(txt: T) -> Self {
TextProps {
text: Some(txt.into())
}
}
}
impl Component for Text {
type Props = TextProps;
fn create(props: TextProps) -> Self {
Self { props }
}
}

38
maple-stdweb/src/span.rs Normal file
View File

@ -0,0 +1,38 @@
use maple_core::prelude::*;
pub enum SpanEvents {
Click
}
pub struct Span {
props: SpanProps
}
#[derive(Default)]
pub struct SpanProps {
pub id: Option<&'static str>,
pub name: Option<&'static str>,
pub class: Option<&'static str>,
pub click: Option<Box<dyn Callback<Arg=()>>>
}
impl Component for Span {
type Props = SpanProps;
type Msg = SpanEvents;
fn create(props: SpanProps) -> Self {
Span { props }
}
fn update(&mut self, msg: Self::Msg) -> bool {
match msg {
SpanEvents::Click => {
if let Some(cb) = &self.props.click {
cb.call(());
}
}
};
false
}
}

13
maple/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
cargo-features = ["edition"]
[package]
name = "maple"
version = "0.1.0"
authors = ["Andrey Tkachenko <andreytkachenko64@gmail.com>"]
edition = "2018"
[dependencies]
maple-core = {path = "../maple-core"}
maple-macro = {path = "../maple-macro"}
maple-stdui = {path = "../maple-stdui"}
maple-stdweb = {path = "../maple-stdweb"}

9
maple/src/lib.rs Normal file
View File

@ -0,0 +1,9 @@
#[macro_use]
extern crate maple_core;
pub mod prelude {
pub use maple_core::prelude::*;
pub use maple_macro::view;
pub use maple_stdweb::*;
pub use maple_stdui::*;
}

View File

@ -1,5 +0,0 @@
#![feature(associated_type_defaults, never_type, unsize)]
mod convert;
use std::marker::PhantomData;
pub use convert::*;

View File

@ -1,43 +0,0 @@
///
///
///
///
pub struct Node <E, I, CHE, CH>
where
E: Engine,
CHE: Engine,
I: RenderImplementation<Engine = E, ChildrenEngine = CHE>,
CH: Renderable<Engine = CHE>
{
item: I,
children: CH
}
impl<E, I, CHE, CH> Node<E, I, CHE, CH>
where
E: Engine,
CHE: Engine,
I: RenderImplementation<Engine = E, ChildrenEngine = CHE>,
CH: Renderable<Engine = CHE>
{
pub fn new(item: I, children: CH) -> Self {
Self {
item,
children
}
}
}
impl<E, I, CHE, CH> Renderable for Node<E, I, CHE, CH>
where
E: Engine,
CHE: Engine,
I: RenderImplementation<Engine = E, ChildrenEngine = CHE>,
CH: Renderable<Engine = CHE>
{
type Engine = E;
fn render(&self, eng: &Self::Engine) {
self.item.render_impl(eng, &self.children)
}
}

View File

@ -1,101 +0,0 @@
/// defines implementation of how the children elements binds to the component
/// in dom backend it actually attaches/detaches elements to each other
///
///
pub trait RenderImplementation {
type Engine: Engine;
type ChildrenEngine: Engine = Self::Engine;
fn render_impl<C>(&self, eng: &Self::Engine, children: &C)
where C: Renderable<Engine = Self::ChildrenEngine>;
}
/// Trait Renderable
/// defines implementation of how the component should be rendered itself
///
///
///
pub trait Renderable {
type Engine: Engine;
fn render(&self, eng: &Self::Engine);
}
///
/// for nodes without children
///
///
pub struct Stub<E: Engine> {
_e: PhantomData<E>
}
impl<E: Engine> Renderable for Stub<E> {
type Engine = E;
fn render(&self, _eng: &Self::Engine) {}
}
impl<E: Engine> Stub<E> {
pub fn new() -> Self {
Self {
_e: PhantomData
}
}
}
impl<E, T> Renderable for T
where E: Engine,
T: RenderImplementation<Engine = E, ChildrenEngine = E>
{
type Engine = E;
fn render(&self, eng: &Self::Engine) {
self.render_impl(eng, &Stub::new())
}
}
impl<E: Engine> Renderable for Box<dyn Renderable<Engine = E>> {
type Engine = E;
fn render(&self, eng: &Self::Engine) {
(**self).render(eng);
}
}
impl <'a, E> Renderable for Vec<&'a (dyn Renderable<Engine = E> + 'a)>
where E: Engine + 'a
{
type Engine = E;
fn render(&self, eng: &Self::Engine) {
for child in self.iter() {
child.render(eng);
}
}
}
impl <E, T> Renderable for Vec<T>
where E: Engine,
T: Renderable<Engine = E>
{
type Engine = E;
fn render(&self, eng: &Self::Engine) {
for child in self.iter() {
child.render(eng);
}
}
}
impl <E, T> Renderable for Option<T>
where E: Engine,
T: Renderable<Engine = E>
{
type Engine = E;
fn render(&self, eng: &Self::Engine) {
if let Some(inner) = self {
inner.render(eng);
}
}
}