Skip to content

Instantly share code, notes, and snippets.

@sagacity
Created July 25, 2017 15:46
Show Gist options
  • Save sagacity/0a23d6cd66553d321b128e7da84f0595 to your computer and use it in GitHub Desktop.
Save sagacity/0a23d6cd66553d321b128e7da84f0595 to your computer and use it in GitHub Desktop.
A draft of the weld component system
use std::collections::HashMap;
use std::rc::Rc;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct Component {
events: HashMap<Event, Box<StateCallback>>,
children: Vec<Component>,
}
impl Component {
fn invoke(&self, event: Event) -> Component {
let f = &self.events[&event];
f.invoke()
}
fn children(&self) -> &Vec<Component> {
&self.children
}
}
#[derive(PartialEq, Hash, Eq)]
enum Event {
Pressed,
Released
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
trait State where Self: Sized + Clone + 'static {
fn component(&self, context: BuildContext<Self>) -> Component;
fn build(&self) -> Component {
self.component(self.context())
}
fn context(&self) -> BuildContext<Self> {
BuildContext::new(self)
}
}
struct BuildContext<S: State> {
state_provider: Rc<StateProvider<S>>
}
impl<S: State> BuildContext<S> {
fn new(state: &S) -> BuildContext<S> {
BuildContext {
state_provider: Rc::new(StateProvider { state: state.clone() })
}
}
}
fn button<S: State>(context: &BuildContext<S>) -> ButtonBuilder<S> {
ButtonBuilder { builder: Builder::new(context) }
}
fn label<S: State>(context: &BuildContext<S>) -> LabelBuilder<S> {
LabelBuilder { builder: Builder::new(context) }
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct Builder<S: State> {
state_provider: Rc<StateProvider<S>>,
events: HashMap<Event, Box<StateCallback>>,
children: Vec<Component>,
}
type StateHandler<S> = Box<Fn(&S) -> S>;
impl<S: State> Builder<S> {
fn new(context: &BuildContext<S>) -> Builder<S> {
Builder {
state_provider: context.state_provider.clone(),
events: HashMap::new(),
children: Vec::new(),
}
}
fn register_event(&mut self, event: Event, handler: StateHandler<S>) {
let f = GenericStateCallback {
state_provider: self.state_provider.clone(),
handler,
};
self.events.insert(event, Box::new(f));
}
fn child(&mut self, child: Component) {
self.children.push(child);
}
fn build(self) -> Component {
Component {
events: self.events,
children: self.children
}
}
}
trait StateCallback {
fn invoke(&self) -> Component;
}
struct GenericStateCallback<S: State> {
state_provider: Rc<StateProvider<S>>,
handler: StateHandler<S>
}
impl<S: State> StateCallback for GenericStateCallback<S> {
fn invoke(&self) -> Component {
let state = self.state_provider.provide();
let new_state = (self.handler)(&state);
new_state.build()
}
}
struct StateProvider<S: State> {
state: S
}
impl<S: State> StateProvider<S> {
fn provide(&self) -> &S {
&self.state
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
trait GetInnerBuilder<S: State> {
fn inner_builder(&mut self) -> &mut Builder<S>;
}
trait OnBuilder<S: State> where Self: GetInnerBuilder<S> + Sized {
fn on(mut self, event: Event, handler: StateHandler<S>) -> Self {
self.inner_builder().register_event(event, handler);
self
}
}
trait ChildBuilder<S: State> where Self: GetInnerBuilder<S> + Sized {
fn child<C: Into<Component>>(mut self, child: C) -> Self {
self.inner_builder().child(child.into());
self
}
fn children<C: IntoIterator<Item=Component>>(mut self, children: C) -> Self {
for child in children {
self.inner_builder().child(child.into());
}
self
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
macro_rules! impl_builder {
($builder_name:ident) => {
impl_builder!($builder_name;);
};
($builder_name:ident; $($o:ty),*) => (
// Build component from inner builder
impl<S: State> From<$builder_name<S>> for Component {
fn from(c: $builder_name<S>) -> Self {
c.builder.build()
}
}
// Access inner builder (assumed to be in self.builder)
impl<S: State> GetInnerBuilder<S> for $builder_name<S> {
fn inner_builder(&mut self) -> &mut Builder<S> { &mut self.builder }
}
// Implement additional traits
$(impl<S: State> $o for $builder_name<S> {})*
);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct ButtonBuilder<S: State> {
builder: Builder<S>
}
impl_builder!(ButtonBuilder; OnBuilder<S>, ChildBuilder<S>);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct LabelBuilder<S: State> {
builder: Builder<S>
}
impl<S: State> LabelBuilder<S> {
fn caption<C: Into<String>>(self, caption: C) -> Self {
//self.caption = caption;
let _ = caption; // ignore for now
self
}
}
impl_builder!(LabelBuilder);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#[derive(Clone, Debug)]
struct MyAppState {
counter: u32
}
struct MyApp;
impl MyApp {
fn new() -> MyAppState {
MyAppState {
counter: 0
}
}
}
impl State for MyAppState {
fn component(&self, context: BuildContext<Self>) -> Component {
println!("Current state: {:?}", self);
button(&context)
.on(Event::Pressed, Box::new(|state| {
println!("pressed");
MyAppState {
counter: state.counter + 1
}
}))
.on(Event::Released, Box::new(|state| {
println!("released");
MyAppState {
counter: state.counter + 2
}
}))
.children(vec![
label(&context).caption(format!("You've clicked {} times!", self.counter)).into()
])
.into()
}
}
fn main() {
let app = MyApp::new();
let component = app.build()
.invoke(Event::Pressed)
.invoke(Event::Released)
.invoke(Event::Pressed);
println!("#children: {:?}", component.children().len());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment