From ddfb4884e597d4b0d22f70983173c005e36776bd Mon Sep 17 00:00:00 2001 From: Adoo Date: Sat, 22 Jun 2024 15:47:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(macros):=20=F0=9F=8E=B8=20support=20path?= =?UTF-8?q?=20to=20declare=20widget=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 17 +++++++++ docs/en/get_started/quick_start.md | 12 +++++++ docs/zh/get_started/quick_start.md | 12 +++++++ examples/todos/src/ui.rs | 12 +++---- macros/src/symbol_process.rs | 55 +++++++++++++----------------- 5 files changed, 70 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac1b2f822..110bf6103 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,23 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he - **core**: Added support to query a `WriteRef` from a state, enabling users to modify the state after attaching it to a widget. (#601 @M-Adoo) - **core**: Introduced the `DeclareInto` trait for any type that implements `DeclareFrom`. (#604 @M-Adoo) +- **macros**: Improved widget declaration to allow specifying widget types via path. (#606 @M-Adoo) + ```rust + // Previously, a widget type could only be specified using an identifier, requiring prior import of `Row`. + use ribir::prelude::*; + fn_widget! { + @Row { + ... + } + } + + // Now, the widget type can be specified using a path, removing the need for a prior import. + fn_widget! { + @ribir::prelude::Row { + ... + } + } +``` ### Changed diff --git a/docs/en/get_started/quick_start.md b/docs/en/get_started/quick_start.md index 9618552ab..586902bbd 100644 --- a/docs/en/get_started/quick_start.md +++ b/docs/en/get_started/quick_start.md @@ -259,6 +259,18 @@ fn main() { }); } ``` +For expression widgets that are simple function calls, the `{}` can be omitted for brevity: + +```rust ignore +fn_widget! { + // Instead of using braces: + let label = @ { Label::new("Increment") }; + // You can directly write: + let label = @Label::new("Increment"); + ... +}; +``` + ## State -- make data watchable and shareable Although we have created a counter, it always shows `0` and does not respond to the button. In this section, you will learn how to make your counter work through state. diff --git a/docs/zh/get_started/quick_start.md b/docs/zh/get_started/quick_start.md index 4ad861d05..912fd147f 100644 --- a/docs/zh/get_started/quick_start.md +++ b/docs/zh/get_started/quick_start.md @@ -259,6 +259,18 @@ fn main() { } ``` +对于简单的函数调用形式的表达式 widget ,可以省略 `{}` 以简化书写: + +```rust ignore +fn_widget! { + // 使用大括号的写法: + let label = @ { Label::new("Increment") }; + // 可以直接写为: + let label = @Label::new("Increment"); + ... +}; +``` + ## 状态——让数据变得可被侦和共享 我们虽然创建了一个计数器,但它总是显示 `0`,也不响应按钮做任何事情。在这一节中,你将会了解到如何通过状态让你的计数器工作。 diff --git a/examples/todos/src/ui.rs b/examples/todos/src/ui.rs index 283c3e68e..0223bfbd6 100644 --- a/examples/todos/src/ui.rs +++ b/examples/todos/src/ui.rs @@ -9,15 +9,13 @@ impl Compose for Todos { align_items: Align::Center, item_gap: 12., @H1 { text: "Todo" } - @ { - input(None, move |text| { - $this.write().new_task(text.to_string()); - }) - } + @input(None, move |text| { + $this.write().new_task(text.to_string()); + }) @Tabs { @Tab { - @TabItem { @{ Label::new("ALL") } } - @TabPane { @{ task_lists(&this, |_| true) } } + @TabItem { @Label::new("ALL") } + @TabPane { @task_lists(&this, |_| true) } } @Tab { @TabItem { @{ Label::new("ACTIVE") } } diff --git a/macros/src/symbol_process.rs b/macros/src/symbol_process.rs index e7127922a..c328c771f 100644 --- a/macros/src/symbol_process.rs +++ b/macros/src/symbol_process.rs @@ -113,47 +113,40 @@ mod tokens_pre_process { { tokens.push(TokenTree::Ident(Ident::new(KW_RDL, at.span()))); tokens.push(not_token(at.span())); - - let body = match iter.next() { - // declare a new widget: `@ SizedBox { ... }` - Some(TokenTree::Ident(name)) => { - let next = iter.next(); - let Some(TokenTree::Group(body)) = next else { - return rdl_syntax_err( - at.span(), - next.map(|n| n.span()).or_else(|| Some(name.span())) - ) - }; - let tokens = TokenStream::from_iter([TokenTree::Ident(name), TokenTree::Group(body)]); - Group::new(Delimiter::Brace, tokens) - } + let mut rdl_group = smallvec::SmallVec::<[TokenTree; 3]>::default(); + match iter.next() { // declare a variable widget as parent, `@ $var { ... }` Some(TokenTree::Punct(dollar)) if dollar.as_char() == '$' => { if let Some(TokenTree::Ident(var)) = iter.next() { - let next = iter.next(); - let Some(TokenTree::Group(body)) = next else { - return rdl_syntax_err( - at.span(), - next.map(|n| n.span()).or_else(|| Some(var.span())) - ) + rdl_group.push(TokenTree::Punct(dollar)); + rdl_group.push(TokenTree::Ident(var)); + if let Some(g) = iter.next() { + rdl_group.push(g); }; - let tokens = TokenStream::from_iter([ - TokenTree::Punct(dollar), - TokenTree::Ident(var), - TokenTree::Group(body), - ]); - Group::new(Delimiter::Brace, tokens) } else { return dollar_err(dollar.span()); } } // declare a expression widget `@ { ... }` - Some(TokenTree::Group(g)) => g, - n => { - return rdl_syntax_err(at.span(), n.map(|n| n.span())); - } + Some(TokenTree::Group(g)) => rdl_group.push(TokenTree::Group(g)) , + // declare a new widget: `@ SizedBox { ... }` + mut n => { + while let Some(t) = n.take() { + let is_group = matches!(t, TokenTree::Group(_)); + rdl_group.push(t); + if is_group { + break + } + n = iter.next(); + }; + }, }; - tokens.push(TokenTree::Group(body)); + if let Some(TokenTree::Group(_)) = rdl_group.last() { + let rdl_group = TokenStream::from_iter(rdl_group); + tokens.push(TokenTree::Group(Group::new(Delimiter::Brace, rdl_group))); + } else { + return rdl_syntax_err(at.span(), rdl_group.last().map(|n| n.span())); + } } Some(TokenTree::Punct(p)) if p.as_char() == '$' => { match iter.next() {