Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ rev = "7a3251b9b89df11af653189f2be04c7304861acf"

[dependencies]
log = "0.4.11"

crochet-macros = { path = "crochet-macros" }
async-std = { version = "1.6.4", optional = true }
futures = "0.3.5"

Expand Down
2 changes: 2 additions & 0 deletions crochet-macros/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
Cargo.lock
13 changes: 13 additions & 0 deletions crochet-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
Comment thread
maan2003 marked this conversation as resolved.
name = "crochet-macros"
version = "0.1.0"
authors = ["Maan2003 <manmeetmann2003@gmail.com>"]
edition = "2018"

[lib]
proc-macro = true

[dependencies]
syn = { version = "1.0.41", features = ["full"] }
Comment thread
maan2003 marked this conversation as resolved.
quote = "1.0.7"
proc-macro2 = "1.0.22"
51 changes: 51 additions & 0 deletions crochet-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, FnArg, Ident, ItemFn, Pat, ReturnType, Type};

#[proc_macro_attribute]
pub fn component(_args: TokenStream, input: TokenStream) -> TokenStream {
let ItemFn { attrs, vis, sig, block } = parse_macro_input!(input as ItemFn);
let cx = name_of_cx(&sig.inputs).expect("No argument of type `&mut Cx` found");

// return_ty is explicit to produce better error for return type mismatch.
let return_ty = return_ty_or_unit(&sig.output);

let tokens = quote! {
#[track_caller]
#(#attrs)*
#vis #sig {
::crochet::Cx::with_loc(
#cx, ::std::panic::Location::caller(), move |#cx| #return_ty #block
)
}
};
tokens.into()
}

fn name_of_cx<'a>(args: impl IntoIterator<Item = &'a FnArg>) -> Option<Ident> {
for arg in args {
if let FnArg::Typed(arg) = arg {
if let Type::Reference(ty_ref) = &*arg.ty {
if let (Some(_), Type::Path(path)) = (ty_ref.mutability, &*ty_ref.elem) {
if let Some(seg) = path.path.segments.first() {
if seg.ident == "Cx" {
if let Pat::Ident(pat_ident) = &*arg.pat {
return Some(pat_ident.ident.clone());
}
}
}
}
}
}
}
None
}

fn return_ty_or_unit(ty: &ReturnType) -> proc_macro2::TokenStream {
if ty == &ReturnType::Default {
quote! { -> () }
} else {
quote! { #ty }
}
}
49 changes: 49 additions & 0 deletions examples/component.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! The classic counter example but split up into component

use druid::{AppLauncher, PlatformError, Widget, WindowDesc};

use crochet::{component, AppHolder, Button, Column, Cx, DruidAppData, Label, Row};

fn main() -> Result<(), PlatformError> {
let main_window = WindowDesc::new(ui_builder);
let data = Default::default();
AppLauncher::with_window(main_window)
.use_simple_logger()
.launch(data)
}

#[derive(Default)]
struct MyAppLogic {
count1: usize,
count2: usize,
}

impl MyAppLogic {
fn run(&mut self, cx: &mut Cx) {
Row::new().build(cx, |cx| {
counter(&mut self.count1, cx);
counter(&mut self.count2, cx);
});
}
}

#[component]
fn counter(count: &mut usize, cx: &mut Cx) {
cx.if_changed(*count, |cx| {
Column::new().build(cx, |cx| {
Label::new(format!("current count: {}", *count)).build(cx);
if Button::new("Increment").build(cx) {
*count += 1;
}
if *count > 3 && *count < 6 {
Label::new("You did it!").build(cx);
}
});
});
}

fn ui_builder() -> impl Widget<DruidAppData> {
let mut app_logic = MyAppLogic::default();

AppHolder::new(move |cx| app_logic.run(cx))
}
10 changes: 10 additions & 0 deletions src/cx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ impl<'a> Cx<'a> {
self.mut_cursor.begin_loc(body, loc)
}

/// Wrap the callback in begin_loc and end.
///
/// This method is used by component attribute macro.
pub fn with_loc<T>(&mut self, loc: &'static Location, cb: impl FnOnce(&mut Self) -> T) -> T {
self.mut_cursor.begin_loc(Payload::Placeholder, loc);
let result = cb(self);
self.mut_cursor.end();
result
}

/// Traverse into a subtree only if the data has changed.
///
/// The supplied callback *must* create only one widget. This is not
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod widget;

pub use any_widget::DruidAppData;
pub use app_holder::AppHolder;
pub use crochet_macros::component;
pub use cx::Cx;
pub use id::Id;
pub use list::{List, ListData};
Expand Down