Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(macros): 🎸 implement same name macro for widget derive Declare #651

Merged
merged 1 commit into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he

### Features

- **macros**: Every widget that derives `Declare` will automatically implement a macro with the same name to declare a function widget using it as the root widget. (#651 @M-Adoo)
- **core**: Added the smooth widgets for transitioning the layout position and size. (#645 @M-Adoo)
- **widgets**: Added three widgets `FractionallyWidthBox`, `FractionallyHeightBox`, and `FractionallySizedBox` to enable fractional sizing of widgets. (#647 @M-Adoo)
- **widgets**: Add widget of radio button (#649 @wjian23)
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ web-sys = { version = "0.3.69", features = ["HtmlCollection"] }
web-time = "1.1.0"
wasm-bindgen-futures = "0.4.42"
getrandom = { version = "0.2.12", features = ["js"] }
heck = "0.5.0"

[workspace.metadata.release]
shared-version = true
Expand Down
2 changes: 1 addition & 1 deletion core/src/context/app_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ mod tests {
.await;
finish.fetch_add(1, Ordering::SeqCst);
});
assert!(Instant::now().duration_since(start).as_millis() > 100);
assert!(Instant::now().duration_since(start).as_millis() >= 100);
assert_eq!(waker.cnt.load(Ordering::Relaxed), 1);

start = Instant::now();
Expand Down
6 changes: 3 additions & 3 deletions core/src/widget_children/compose_child_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,9 @@
}

#[derive(Declare)]
struct X;
struct XX;

impl<'c> ComposeChild<'c> for X {
impl<'c> ComposeChild<'c> for XX {
type Child = Widget<'c>;

fn compose_child(_: impl StateWriter<Value = Self>, _: Self::Child) -> Widget<'c> {
Expand All @@ -277,7 +277,7 @@
fn pair_compose_child() {
let _ = |_: &BuildCtx| -> Widget {
MockBox { size: ZERO_SIZE }
.with_child(X.with_child(Void {}))
.with_child(XX.with_child(Void {}))

Check warning on line 280 in core/src/widget_children/compose_child_impl.rs

View check run for this annotation

Codecov / codecov/patch

core/src/widget_children/compose_child_impl.rs#L280

Added line #L280 was not covered by tests
.into_widget()
};
}
Expand Down
2 changes: 1 addition & 1 deletion docs/en/understanding_ribir/without_dsl.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ In the previous example, we used a method similar to the Builder pattern to crea
First, let's look at the complete definition of `FillButton`:

```rust
use ribir::prelude::*;
use ribir::core::prelude::*;

#[derive(Declare, Default)]
pub struct FilledButton {
Expand Down
2 changes: 1 addition & 1 deletion docs/zh/understanding_ribir/without_dsl.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ fn button_demo(ctx: &BuildCtx) {
首先,我们来看一下 `FillButton` 的完整定义:

```rust
use ribir::prelude::*;
use ribir::core::prelude::*;

#[derive(Declare, Default)]
pub struct FilledButton {
Expand Down
16 changes: 7 additions & 9 deletions examples/counter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ use ribir::prelude::*;

pub fn counter() -> Widget<'static> {
let cnt = Stateful::new(0);
let f = fn_widget! {
@Row {
@FilledButton {
on_tap: move |_| *$cnt.write() += 1,
@{ Label::new("Inc") }
}
@H1 { text: pipe!($cnt.to_string()) }
row! {
@FilledButton {
on_tap: move |_| *$cnt.write() += 1,
@{ Label::new("Inc") }
}
};
f()
@H1 { text: pipe!($cnt.to_string()) }
}
.into_widget()
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen)]
Expand Down
2 changes: 1 addition & 1 deletion examples/storybook/src/storybook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ fn content() -> Widget<'static> {
margin: EdgeInsets::all(20.),
@Lists {
margin: EdgeInsets::only_top(20.),
@Link {
@UrlLink {
url: "https://ribir.org",
@ListItem {
@Leading(EdgeWidget::Icon(svgs::CHECK_BOX_OUTLINE_BLANK.into_widget()))
Expand Down
1 change: 1 addition & 0 deletions macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ ribir_painter = {path = "../painter", version = "0.4.0-alpha.14" }
smallvec = { workspace = true, features= ["drain_filter"] }
syn = { workspace = true, features = ["fold", "full", "extra-traits"]}
phf = { workspace = true, features = ["macros"] }
heck.workspace = true


[features]
Expand Down
72 changes: 49 additions & 23 deletions macros/src/declare_derive.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use heck::ToSnakeCase;
use proc_macro2::TokenStream;
use quote::{ToTokens, quote, quote_spanned};
use syn::{Fields, Ident, Visibility, spanned::Spanned};
Expand All @@ -14,25 +15,24 @@ pub(crate) fn declare_derive(input: &mut syn::DeriveInput) -> syn::Result<TokenS
let syn::DeriveInput { vis, ident: host, generics, data, .. } = input;
let stt = data_struct_unwrap(data, DECLARE)?;

if stt.fields.is_empty() {
return empty_impl(host, &stt.fields);
}

let declarer = Declarer::new(host, &mut stt.fields)?;
let Declarer { name, fields, .. } = &declarer;
// reverse name check.
fields
.iter()
.try_for_each(DeclareField::check_reserve)?;
let set_methods = declarer_set_methods(fields, vis);

let field_names = declarer.fields.iter().map(DeclareField::member);
let field_names2 = field_names.clone();

let (builder_f_names, builder_f_tys) = declarer.declare_names_tys();
let field_values = field_values(&declarer.fields, host);
let (g_impl, g_ty, g_where) = generics.split_for_impl();
let tokens = quote! {
let mut tokens = if stt.fields.is_empty() {
empty_impl(host, &stt.fields)
} else {
let declarer = Declarer::new(host, &mut stt.fields)?;
let Declarer { name, fields, .. } = &declarer;
// reverse name check.
fields
.iter()
.try_for_each(DeclareField::check_reserve)?;
let set_methods = declarer_set_methods(fields, vis);

let field_names = declarer.fields.iter().map(DeclareField::member);
let field_names2 = field_names.clone();

let (builder_f_names, builder_f_tys) = declarer.declare_names_tys();
let field_values = field_values(&declarer.fields, host);
let (g_impl, g_ty, g_where) = generics.split_for_impl();
quote! {
#vis struct #name #generics #g_where {
#(
#[allow(clippy::type_complexity)]
Expand Down Expand Up @@ -540,11 +540,38 @@ pub(crate) fn declare_derive(input: &mut syn::DeriveInput) -> syn::Result<TokenS
self
}
}
}
};

widget_macro_to_tokens(host, vis, &mut tokens);

Ok(tokens)
}

fn widget_macro_to_tokens(name: &Ident, vis: &Visibility, tokens: &mut TokenStream) {
let macro_name = name.to_string().to_snake_case();
let doc =
format!("Macro used to generate a function widget using `{}` as the root widget.", macro_name);
let macro_name = Ident::new(&macro_name, name.span());
let export_attr = if matches!(vis, Visibility::Public(_)) {
quote! { #[macro_export] }
} else {
quote! { #[allow(unused_macros)] }
};
tokens.extend(quote! {
#[allow(unused_macros)]
#export_attr
#[doc = #doc]
macro_rules! #macro_name {
($($t: tt)*) => {
fn_widget! { @ #name { $($t)* } }
};
}
#[allow(unused_imports)]
#vis use #macro_name;
})
}

fn declarer_set_methods<'a>(
fields: &'a [DeclareField], vis: &'a Visibility,
) -> impl Iterator<Item = TokenStream> + 'a {
Expand Down Expand Up @@ -644,17 +671,16 @@ To avoid name conflicts during declaration, use the `rename` meta, like so:
}
}

fn empty_impl(name: &Ident, fields: &Fields) -> syn::Result<TokenStream> {
fn empty_impl(name: &Ident, fields: &Fields) -> TokenStream {
let construct = match fields {
Fields::Named(_) => quote!(#name {}),
Fields::Unnamed(_) => quote!(#name()),
Fields::Unit => quote!(#name),
};
let tokens = quote! {
quote! {
impl Declare for #name {
type Builder = FatObj<#name>;
fn declarer() -> Self::Builder { FatObj::new(#construct) }
}
};
Ok(tokens)
}
}
13 changes: 13 additions & 0 deletions widgets/src/layout/flex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ pub struct Row;
/// A type help to declare flex widget as Vertical.
pub struct Column;

#[macro_export]
macro_rules! row {
($($t: tt)*) => { fn_widget! { @Row { $($t)* } } };
}

#[macro_export]
macro_rules! column {
($($t: tt)*) => { fn_widget! { @Column { $($t)* } } };
}

pub use column;
pub use row;

impl Declare for Row {
type Builder = FlexDeclarer;
fn declarer() -> Self::Builder { Flex::declarer().direction(Direction::Horizontal) }
Expand Down
4 changes: 2 additions & 2 deletions widgets/src/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ use ribir_core::prelude::*;
use webbrowser::{Browser, open_browser as open};

#[derive(Declare)]
pub struct Link {
pub struct UrlLink {
/// Want to open url
url: CowArc<str>,
/// Select the browser software you expect to open
#[declare(default=Browser::Default)]
browser: Browser,
}

impl<'c> ComposeChild<'c> for Link {
impl<'c> ComposeChild<'c> for UrlLink {
type Child = Widget<'c>;
fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
FatObj::new(child)
Expand Down
Loading