From 562bed3a4bb1bcc4fcbd4addc5ae025fff04201e Mon Sep 17 00:00:00 2001 From: Adoo Date: Thu, 14 Sep 2023 15:36:10 +0800 Subject: [PATCH] =?UTF-8?q?refactor(ribir):=20=F0=9F=92=A1=20migrate=20ful?= =?UTF-8?q?l=20project=20to=20new=20syntax=20and=20remove=20the=20old=20co?= =?UTF-8?q?mpiler.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 1 - core/src/animation/transition.rs | 4 +- core/src/builtin_widgets/align.rs | 16 +- core/src/builtin_widgets/anchor.rs | 48 +- core/src/builtin_widgets/box_decoration.rs | 51 +- core/src/builtin_widgets/clip.rs | 2 +- core/src/builtin_widgets/cursor.rs | 16 +- core/src/builtin_widgets/delay_drop.rs | 2 +- core/src/builtin_widgets/fitted_box.rs | 6 +- core/src/builtin_widgets/focus_node.rs | 20 +- core/src/builtin_widgets/focus_scope.rs | 76 +- core/src/builtin_widgets/has_focus.rs | 2 +- core/src/builtin_widgets/ignore_pointer.rs | 2 +- core/src/builtin_widgets/key.rs | 3 +- core/src/builtin_widgets/layout_box.rs | 14 +- core/src/builtin_widgets/lifecycle.rs | 40 +- core/src/builtin_widgets/margin.rs | 6 +- core/src/builtin_widgets/mouse_hover.rs | 2 +- core/src/builtin_widgets/opacity.rs | 2 +- core/src/builtin_widgets/padding.rs | 8 +- core/src/builtin_widgets/pointer_pressed.rs | 2 +- core/src/builtin_widgets/scrollable.rs | 16 +- core/src/builtin_widgets/theme.rs | 2 +- .../theme/compose_decorators.rs | 10 +- core/src/builtin_widgets/transform_widget.rs | 8 +- core/src/builtin_widgets/unconstrained_box.rs | 18 +- core/src/builtin_widgets/visibility.rs | 4 +- core/src/builtin_widgets/void.rs | 2 +- core/src/context/widget_ctx.rs | 10 +- core/src/declare.rs | 45 - core/src/events/character.rs | 10 +- core/src/events/dispatcher.rs | 56 +- core/src/events/focus_mgr.rs | 46 +- core/src/events/listener_impl_helper.rs | 33 +- core/src/events/pointers.rs | 46 - core/src/events/wheel.rs | 6 +- core/src/lib.rs | 2 +- core/src/pipe.rs | 28 +- core/src/state/stateful.rs | 2 +- core/src/test_helper.rs | 6 +- core/src/widget_children.rs | 51 +- .../src/widget_children/compose_child_impl.rs | 2 +- core/src/widget_tree.rs | 8 +- core/src/widget_tree/layout_info.rs | 109 ++- core/src/widget_tree/widget_id.rs | 23 +- core/src/window.rs | 9 +- dev-helper/src/widget_test.rs | 6 +- macros/src/child_template.rs | 6 - macros/src/declare_derive.rs | 381 --------- macros/src/declare_derive2.rs | 10 +- macros/src/declare_obj.rs | 7 +- macros/src/error.rs | 156 ---- macros/src/lib.rs | 28 +- macros/src/rdl_macro.rs | 17 +- macros/src/symbol_process.rs | 2 +- .../src/{widget_macro => }/variable_names.rs | 37 +- macros/src/widget_macro.rs | 188 ----- macros/src/widget_macro/code_gen.rs | 652 --------------- macros/src/widget_macro/desugar.rs | 487 ----------- macros/src/widget_macro/name_used_info.rs | 236 ------ macros/src/widget_macro/parser.rs | 479 ----------- macros/src/widget_macro/visit_mut.rs | 790 ------------------ ribir/tests/timer_test.rs | 33 +- tests/Cargo.toml | 5 +- tests/child_template_derive_test.rs | 6 +- .../declare/circular_dependency_fail.rs | 33 - .../declare/circular_dependency_fail.stderr | 78 -- .../declare/declare_syntax_pass.rs | 67 -- .../declare/dollar_position_fail.rs | 11 - .../declare/dollar_position_fail.stderr | 16 - .../fix_attr_indirect_follow_host_fail.rs | 33 - .../fix_attr_indirect_follow_host_fail.stderr | 41 - ...ildren_of_nested_named_widget_miss_pass.rs | 14 - .../declare/fix_decoration_ref_pass.rs | 16 - .../declare/fix_follow_wrap_widget_pass.rs | 17 - .../declare/let_watch_desugar_syntax_fail.rs | 10 - .../let_watch_desugar_syntax_fail.stderr | 5 - .../declare/multi_track_allow_pass.rs | 15 - .../ref_attach_before_self_widget_pass.rs | 14 - .../declare/widget_before_field_fail.rs | 11 - .../declare/widget_before_field_fail.stderr | 5 - tests/compile_msg/declare_builder_fail.rs | 20 - tests/compile_msg/declare_builder_fail.stderr | 10 - tests/declare_builder_test.rs | 29 +- ...compile_message.rs => include_svg_test.rs} | 7 - tests/rdl_macro_test.rs | 123 ++- themes/material/src/ripple.rs | 4 +- themes/material/src/state_layer.rs | 4 +- widgets/src/avatar.rs | 2 +- widgets/src/buttons.rs | 6 +- widgets/src/buttons/button.rs | 4 +- widgets/src/buttons/fab_button.rs | 4 +- widgets/src/buttons/filled_button.rs | 4 +- widgets/src/buttons/outlined_button.rs | 4 +- widgets/src/checkbox.rs | 12 +- widgets/src/common_widget.rs | 4 +- widgets/src/divider.rs | 24 +- widgets/src/grid_view.rs | 2 +- widgets/src/icon.rs | 2 +- widgets/src/input.rs | 4 +- widgets/src/input/caret.rs | 2 +- widgets/src/input/editarea.rs | 2 +- widgets/src/input/selected_text.rs | 2 +- widgets/src/input/text_selectable.rs | 2 +- widgets/src/layout/constrained_box.rs | 26 +- widgets/src/layout/container.rs | 4 +- widgets/src/layout/expanded.rs | 56 +- widgets/src/layout/flex.rs | 100 +-- widgets/src/layout/sized_box.rs | 20 +- widgets/src/layout/stack.rs | 10 +- widgets/src/link.rs | 3 +- widgets/src/lists.rs | 8 +- widgets/src/path.rs | 6 +- widgets/src/scrollbar.rs | 32 +- widgets/src/tabs.rs | 10 +- widgets/src/text.rs | 16 +- widgets/src/text_field.rs | 6 +- widgets/src/transform_box.rs | 8 +- 118 files changed, 638 insertions(+), 4731 deletions(-) delete mode 100644 macros/src/declare_derive.rs delete mode 100644 macros/src/error.rs rename macros/src/{widget_macro => }/variable_names.rs (55%) delete mode 100644 macros/src/widget_macro.rs delete mode 100644 macros/src/widget_macro/code_gen.rs delete mode 100644 macros/src/widget_macro/desugar.rs delete mode 100644 macros/src/widget_macro/name_used_info.rs delete mode 100644 macros/src/widget_macro/parser.rs delete mode 100644 macros/src/widget_macro/visit_mut.rs delete mode 100644 tests/compile_msg/declare/circular_dependency_fail.rs delete mode 100644 tests/compile_msg/declare/circular_dependency_fail.stderr delete mode 100644 tests/compile_msg/declare/declare_syntax_pass.rs delete mode 100644 tests/compile_msg/declare/dollar_position_fail.rs delete mode 100644 tests/compile_msg/declare/dollar_position_fail.stderr delete mode 100644 tests/compile_msg/declare/fix_attr_indirect_follow_host_fail.rs delete mode 100644 tests/compile_msg/declare/fix_attr_indirect_follow_host_fail.stderr delete mode 100644 tests/compile_msg/declare/fix_children_of_nested_named_widget_miss_pass.rs delete mode 100644 tests/compile_msg/declare/fix_decoration_ref_pass.rs delete mode 100644 tests/compile_msg/declare/fix_follow_wrap_widget_pass.rs delete mode 100644 tests/compile_msg/declare/let_watch_desugar_syntax_fail.rs delete mode 100644 tests/compile_msg/declare/let_watch_desugar_syntax_fail.stderr delete mode 100644 tests/compile_msg/declare/multi_track_allow_pass.rs delete mode 100644 tests/compile_msg/declare/ref_attach_before_self_widget_pass.rs delete mode 100644 tests/compile_msg/declare/widget_before_field_fail.rs delete mode 100644 tests/compile_msg/declare/widget_before_field_fail.stderr delete mode 100644 tests/compile_msg/declare_builder_fail.rs delete mode 100644 tests/compile_msg/declare_builder_fail.stderr rename tests/{compile_message.rs => include_svg_test.rs} (50%) diff --git a/Cargo.toml b/Cargo.toml index d7ff51800..3e287e292 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,6 @@ serde_json = "1.0.82" smallvec = "1.8.0" syn = "1.0.109" tiny-skia-path = {version = "0.11.0"} -trybuild = "1.0.77" unicode-bidi = "0.3.7" unicode-script = "0.5.4" unicode-segmentation = "1.9.0" diff --git a/core/src/animation/transition.rs b/core/src/animation/transition.rs index ffb038ff9..e2bd1d6fc 100644 --- a/core/src/animation/transition.rs +++ b/core/src/animation/transition.rs @@ -6,12 +6,12 @@ use std::{ops::Deref, rc::Rc, time::Duration}; /// smoothly. #[derive(Declare2, Clone, Debug, PartialEq)] pub struct Transition { - #[declare(default, convert=strip_option)] + #[declare(default)] pub delay: Option, pub duration: Duration, #[declare(strict)] pub easing: E, - #[declare(default, convert=strip_option)] + #[declare(default)] pub repeat: Option, } diff --git a/core/src/builtin_widgets/align.rs b/core/src/builtin_widgets/align.rs index 979ea3992..b058b5b5e 100644 --- a/core/src/builtin_widgets/align.rs +++ b/core/src/builtin_widgets/align.rs @@ -54,14 +54,14 @@ pub enum VAlign { } /// A widget that align its child in x-axis, base on child's width. -#[derive(Declare, Declare2, SingleChild)] +#[derive(Declare2, SingleChild)] pub struct HAlignWidget { #[declare(default, builtin)] pub h_align: HAlign, } /// A widget that align its child in y-axis, base on child's height. -#[derive(Declare, Declare2, SingleChild)] +#[derive(Declare2, SingleChild)] pub struct VAlignWidget { #[declare(default, builtin)] pub v_align: VAlign, @@ -162,10 +162,10 @@ mod tests { const WND_SIZE: Size = Size::new(100., 100.); fn h_align(h_align: HAlign) -> Widget { - widget! { - HAlignWidget { + fn_widget! { + @HAlignWidget { h_align, - MockBox { size: CHILD_SIZE } + @MockBox { size: CHILD_SIZE } } } .into() @@ -203,10 +203,10 @@ mod tests { ); fn v_align(v_align: VAlign) -> Widget { - widget! { - VAlignWidget { + fn_widget! { + @VAlignWidget { v_align, - MockBox { size: CHILD_SIZE } + @MockBox { size: CHILD_SIZE } } } .into() diff --git a/core/src/builtin_widgets/anchor.rs b/core/src/builtin_widgets/anchor.rs index ae3be6b3b..b5cdded4c 100644 --- a/core/src/builtin_widgets/anchor.rs +++ b/core/src/builtin_widgets/anchor.rs @@ -11,31 +11,31 @@ pub enum PositionUnit { } /// Widget use to anchor child constraints with the left edge of parent widget. -#[derive(Declare, Declare2, SingleChild)] +#[derive(Declare2, SingleChild)] pub struct LeftAnchor { - #[declare(convert=into, builtin, default=0.)] + #[declare(builtin, default = 0.)] pub left_anchor: PositionUnit, } /// Widget use to anchor child constraints with the right edge of parent widget. -#[derive(Declare, Declare2, SingleChild)] +#[derive(Declare2, SingleChild)] pub struct RightAnchor { - #[declare(convert=into, builtin, default=0.)] + #[declare(builtin, default = 0.)] pub right_anchor: PositionUnit, } /// Widget use to anchor child constraints with the top edge of parent widget. -#[derive(Declare, Declare2, SingleChild)] +#[derive(Declare2, SingleChild)] pub struct TopAnchor { - #[declare(convert=into, builtin, default=0.)] + #[declare(builtin, default = 0.)] pub top_anchor: PositionUnit, } /// Widget use to anchor child constraints with the bottom edge of parent /// widget. -#[derive(Declare, Declare2, SingleChild)] +#[derive(Declare2, SingleChild)] pub struct BottomAnchor { - #[declare(convert=into, builtin, default=0.)] + #[declare(builtin, default = 0.)] pub bottom_anchor: PositionUnit, } @@ -141,8 +141,8 @@ mod test { const WND_SIZE: Size = Size::new(100., 100.); fn pixel_left_top() -> Widget { - widget! { - MockBox { + fn_widget! { + @MockBox { size: CHILD_SIZE, left_anchor: 1., top_anchor: 1., @@ -158,8 +158,8 @@ mod test { ); fn pixel_left_bottom() -> Widget { - widget! { - MockBox { + fn_widget! { + @MockBox { size: CHILD_SIZE, left_anchor: 1., bottom_anchor: 1., @@ -175,8 +175,8 @@ mod test { ); fn pixel_top_right() -> Widget { - widget! { - MockBox { + fn_widget! { + @MockBox { size: CHILD_SIZE, right_anchor: 1., top_anchor: 1., @@ -192,8 +192,8 @@ mod test { ); fn pixel_bottom_right() -> Widget { - widget! { - MockBox { + fn_widget! { + @MockBox { size: CHILD_SIZE, right_anchor: 1., bottom_anchor: 1., @@ -209,8 +209,8 @@ mod test { ); fn percent_left_top() -> Widget { - widget! { - MockBox { + fn_widget! { + @MockBox { size: CHILD_SIZE, left_anchor: Percent(10.), top_anchor: Percent(10.), @@ -226,8 +226,8 @@ mod test { ); fn percent_left_bottom() -> Widget { - widget! { - MockBox { + fn_widget! { + @MockBox { size: CHILD_SIZE, left_anchor: Percent( 10.), bottom_anchor: Percent( 10.), @@ -243,8 +243,8 @@ mod test { } fn percent_top_right() -> Widget { - widget! { - MockBox { + fn_widget! { + @MockBox { size: CHILD_SIZE, right_anchor: Percent(10.), top_anchor: Percent(10.), @@ -260,8 +260,8 @@ mod test { ); fn percent_bottom_right() -> Widget { - widget! { - MockBox { + fn_widget! { + @MockBox { size: CHILD_SIZE, right_anchor: Percent(10.), bottom_anchor: Percent(10.), diff --git a/core/src/builtin_widgets/box_decoration.rs b/core/src/builtin_widgets/box_decoration.rs index 3ab7fbb78..d42d7e795 100644 --- a/core/src/builtin_widgets/box_decoration.rs +++ b/core/src/builtin_widgets/box_decoration.rs @@ -1,17 +1,17 @@ use crate::{impl_query_self_only, prelude::*}; /// The BoxDecoration provides a variety of ways to draw a box. -#[derive(SingleChild, Default, Clone, Declare, Declare2)] +#[derive(SingleChild, Default, Clone, Declare2)] pub struct BoxDecoration { /// The background of the box. - #[declare(builtin, default, convert=custom)] + #[declare(builtin, default)] pub background: Option, /// A border to draw above the background - #[declare(builtin, default, convert=strip_option)] + #[declare(builtin, default)] pub border: Option, /// The corners of this box are rounded by this `BorderRadius`. The round /// corner only work if the two borders beside it are same style. - #[declare(builtin, default, convert=strip_option)] + #[declare(builtin, default)] pub border_radius: Option, } @@ -61,35 +61,6 @@ impl Render for BoxDecoration { impl_query_self_only!(BoxDecoration); -pub trait IntoBackground { - fn into_background(self) -> Option; -} - -impl> IntoBackground for T { - #[inline] - fn into_background(self) -> Option { Some(self.into()) } -} - -impl IntoBackground> for Option { - #[inline] - fn into_background(self) -> Option { self } -} - -impl BoxDecorationDeclarer { - #[inline] - pub fn background(mut self, b: impl IntoBackground) -> Self { - self.background = Some(b.into_background()); - self - } -} - -impl BoxDecoration { - #[inline] - pub fn set_declare_background(&mut self, b: impl IntoBackground) { - self.background = b.into_background(); - } -} - impl BoxDecoration { fn paint_border(&self, painter: &mut Painter, rect: &Rect) { if self.border.is_none() { @@ -228,17 +199,19 @@ mod tests { let dummy = std::mem::MaybeUninit::uninit(); // just for test, we know BoxDecoration not use `ctx` to build. let ctx: BuildCtx<'static> = unsafe { dummy.assume_init() }; - let w = BoxDecoration::declare_builder().build_declare(&ctx); + let w = BoxDecoration::declare2_builder().build_declare(&ctx); + + assert_eq!(w.read().border, None); + assert_eq!(w.read().border_radius, None); + assert_eq!(w.read().background, None); - assert_eq!(w.border, None); - assert_eq!(w.border_radius, None); - assert_eq!(w.background, None); + std::mem::forget(ctx); } const SIZE: Size = Size::new(100., 100.); fn with_border() -> Widget { - widget! { - MockBox { + fn_widget! { + @MockBox { size: SIZE, border: Border { left: BorderSide::new(1., Color::BLACK.into()), diff --git a/core/src/builtin_widgets/clip.rs b/core/src/builtin_widgets/clip.rs index fcf234b7e..006ffa209 100644 --- a/core/src/builtin_widgets/clip.rs +++ b/core/src/builtin_widgets/clip.rs @@ -7,7 +7,7 @@ pub enum ClipType { Path(Path), } -#[derive(SingleChild, Clone, Declare, Declare2)] +#[derive(SingleChild, Clone, Declare2)] pub struct Clip { #[declare(default)] pub clip: ClipType, diff --git a/core/src/builtin_widgets/cursor.rs b/core/src/builtin_widgets/cursor.rs index af0fad5ea..0fb90dd5f 100644 --- a/core/src/builtin_widgets/cursor.rs +++ b/core/src/builtin_widgets/cursor.rs @@ -3,7 +3,7 @@ use winit::window::CursorIcon; /// `Cursor` is an attribute to assign an `cursor` to a widget. -#[derive(Declare, Default, Debug, Declare2)] +#[derive(Default, Debug, Declare2)] pub struct Cursor { #[declare(builtin, default)] pub cursor: CursorIcon, @@ -40,22 +40,22 @@ impl Cursor { #[cfg(test)] mod tests { use super::*; - use crate::test_helper::*; + use crate::{reset_test_env, test_helper::*}; use winit::event::{DeviceId, WindowEvent}; #[test] fn tree_down_up() { - let _guard = unsafe { AppCtx::new_lock_scope() }; + reset_test_env!(); - let row_tree = widget! { - MockBox { + let row_tree = fn_widget! { + @MockBox { size: Size::new(f32::INFINITY, f32::INFINITY), cursor: CursorIcon::AllScroll, - MockMulti{ - MockBox { + @MockMulti{ + @MockBox { size: Size::new(200., 200.), cursor: CursorIcon::Hand, - MockBox { + @MockBox { size: Size::new(100., 100.), cursor: CursorIcon::Help, } diff --git a/core/src/builtin_widgets/delay_drop.rs b/core/src/builtin_widgets/delay_drop.rs index 44d77cde8..455ee5874 100644 --- a/core/src/builtin_widgets/delay_drop.rs +++ b/core/src/builtin_widgets/delay_drop.rs @@ -12,7 +12,7 @@ use crate::{impl_query_self_only, prelude::*}; /// dropped. /// /// It's useful when you need run a leave animation for a widget. -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct DelayDrop { #[declare(builtin)] pub delay_drop_until: bool, diff --git a/core/src/builtin_widgets/fitted_box.rs b/core/src/builtin_widgets/fitted_box.rs index ecb364ae0..1a3eb64d5 100644 --- a/core/src/builtin_widgets/fitted_box.rs +++ b/core/src/builtin_widgets/fitted_box.rs @@ -19,7 +19,7 @@ pub enum BoxFit { } /// Widget set how its child should be scale to fit its box. -#[derive(Declare, Declare2, SingleChild)] +#[derive(Declare2, SingleChild)] pub struct FittedBox { #[declare(builtin)] pub box_fit: BoxFit, @@ -161,8 +161,8 @@ mod tests { } fn as_builtin_field() -> Widget { - widget! { - MockBox { + fn_widget! { + @MockBox { size: Size::new(200., 200.), box_fit: BoxFit::Fill, } diff --git a/core/src/builtin_widgets/focus_node.rs b/core/src/builtin_widgets/focus_node.rs index f636d1120..0fb2d73ed 100644 --- a/core/src/builtin_widgets/focus_node.rs +++ b/core/src/builtin_widgets/focus_node.rs @@ -4,7 +4,7 @@ use crate::{ prelude::*, }; -#[derive(Default, Declare, Declare2)] +#[derive(Default, Declare2)] pub struct FocusNode { /// Indicates that `widget` can be focused, and where it participates in /// sequential keyboard navigation (usually with the Tab key, hence the name. @@ -94,7 +94,7 @@ pub(crate) fn dynamic_compose_focus_node(widget: Widget) -> Widget { }) .into() } -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct RequestFocus { #[declare(default)] handle: Option, @@ -135,13 +135,13 @@ impl_query_self_only!(RequestFocus); #[cfg(test)] mod tests { use super::*; - use crate::test_helper::*; + use crate::{reset_test_env, test_helper::*}; #[test] fn dynamic_focus_node() { - let _guard = unsafe { AppCtx::new_lock_scope() }; + reset_test_env!(); - #[derive(Declare)] + #[derive(Declare2)] struct AutoFocusNode {} impl ComposeChild for AutoFocusNode { @@ -151,11 +151,11 @@ mod tests { dynamic_compose_focus_node(child) } } - let widget = widget! { - AutoFocusNode{ - AutoFocusNode{ - AutoFocusNode { - MockBox { + let widget = fn_widget! { + @AutoFocusNode{ + @AutoFocusNode{ + @AutoFocusNode { + @MockBox { size: Size::default(), } } diff --git a/core/src/builtin_widgets/focus_scope.rs b/core/src/builtin_widgets/focus_scope.rs index 0f6fa58ce..cb505b95f 100644 --- a/core/src/builtin_widgets/focus_scope.rs +++ b/core/src/builtin_widgets/focus_scope.rs @@ -1,6 +1,6 @@ use crate::{events::focus_mgr::FocusType, impl_query_self_only, prelude::*}; -#[derive(Declare, Declare2, Clone, Default)] +#[derive(Declare2, Clone, Default)] pub struct FocusScope { /// If true, the descendants can not be focused. /// Default value is false, then the hold FocusScope subtree can be focused @@ -31,8 +31,6 @@ impl_query_self_only!(FocusScope); #[cfg(test)] mod tests { - use std::{cell::RefCell, rc::Rc}; - use winit::{ dpi::LogicalPosition, event::{DeviceId, ElementState, KeyboardInput, MouseButton, WindowEvent}, @@ -46,19 +44,19 @@ mod tests { let _guard = unsafe { AppCtx::new_lock_scope() }; let size = Size::zero(); - let widget = widget! { - MockMulti { - MockBox { size, tab_index: 0, auto_focus: true } - FocusScope { + let widget = fn_widget! { + @MockMulti { + @MockBox { size, tab_index: 0i16, auto_focus: true } + @FocusScope { skip_descendants: false, - tab_index: 3, - MockMulti { - MockBox { size, tab_index: 1, } - MockBox { size, tab_index: 2, } - MockBox { size, tab_index: 3, } + tab_index: 3i16, + @MockMulti { + @MockBox { size, tab_index: 1i16, } + @MockBox { size, tab_index: 2i16, } + @MockBox { size, tab_index: 3i16, } } } - MockBox { size, tab_index: 1 } + @MockBox { size, tab_index: 1i16 } } }; @@ -105,20 +103,20 @@ mod tests { let _guard = unsafe { AppCtx::new_lock_scope() }; let size = Size::zero(); - let widget = widget! { - MockMulti { - MockBox { size, tab_index: 0, auto_focus: true } - FocusScope { + let widget = fn_widget! { + @MockMulti { + @MockBox { size, tab_index: 0i16, auto_focus: true } + @FocusScope { can_focus: true, skip_descendants: true, - tab_index: 3, - MockMulti { - MockBox { size, tab_index: 1, } - MockBox { size, tab_index: 2, } - MockBox { size, tab_index: 3, } + tab_index: 3i16, + @MockMulti { + @MockBox { size, tab_index: 1i16, } + @MockBox { size, tab_index: 2i16, } + @MockBox { size, tab_index: 3i16, } } } - MockBox { size, tab_index: 1 } + @MockBox { size, tab_index: 1i16 } } }; @@ -154,22 +152,24 @@ mod tests { let _guard = unsafe { AppCtx::new_lock_scope() }; let size = Size::new(50., 50.); - let tap_cnt = Rc::new(RefCell::new(0)); - let result = tap_cnt.clone(); - let widget = widget! { - init { - let tap_cnt2 = tap_cnt.clone(); - } - MockMulti { - FocusScope { - id: host, - can_focus: false, - on_key_down: move |_| *tap_cnt.borrow_mut() += 1, - MockMulti { - MockBox { size, on_key_down: move |_| *tap_cnt2.borrow_mut() += 1, } + let tap_cnt = Stateful::new(0); + let result = tap_cnt.clone_reader(); + let widget = fn_widget! { + let mut host = @FocusScope { + can_focus: false, + on_key_down: move |_| *$tap_cnt.write() += 1, + }; + let request_focus_box = @MockBox { + size, + on_pointer_down: move |_| $host.request_focus() + }; + @MockMulti { + @$host { + @MockMulti { + @MockBox { size, on_key_down: move |_| *$tap_cnt.write() += 1, } } } - MockBox { size, on_pointer_down: move |_| host.request_focus(),} + @ { request_focus_box } } }; @@ -208,6 +208,6 @@ mod tests { wnd.run_frame_tasks(); wnd.draw_frame(); - assert_eq!(*result.borrow(), 2); + assert_eq!(*result.read(), 2); } } diff --git a/core/src/builtin_widgets/has_focus.rs b/core/src/builtin_widgets/has_focus.rs index 39a21792b..b3f6d332d 100644 --- a/core/src/builtin_widgets/has_focus.rs +++ b/core/src/builtin_widgets/has_focus.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -#[derive(PartialEq, Clone, Declare, Declare2)] +#[derive(PartialEq, Clone, Declare2)] pub struct HasFocus { #[declare(skip, default)] focused: bool, diff --git a/core/src/builtin_widgets/ignore_pointer.rs b/core/src/builtin_widgets/ignore_pointer.rs index 4fe23d940..b13c9385a 100644 --- a/core/src/builtin_widgets/ignore_pointer.rs +++ b/core/src/builtin_widgets/ignore_pointer.rs @@ -1,6 +1,6 @@ use crate::{impl_query_self_only, prelude::*}; -#[derive(Declare, Declare2, SingleChild, Clone)] +#[derive(Declare2, SingleChild, Clone)] pub struct IgnorePointer { #[declare(default = true)] pub ignore: bool, diff --git a/core/src/builtin_widgets/key.rs b/core/src/builtin_widgets/key.rs index e275d2336..d134f6045 100644 --- a/core/src/builtin_widgets/key.rs +++ b/core/src/builtin_widgets/key.rs @@ -41,9 +41,8 @@ impl Default for KeyChange { /// frames by its key. If two widget has same parent and key in two frames, the /// new widget in the next frame will be treated as the same widget in the last /// frame. -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct KeyWidget { - #[declare(convert=into)] pub key: Key, #[declare(default, strict)] pub value: V, diff --git a/core/src/builtin_widgets/layout_box.rs b/core/src/builtin_widgets/layout_box.rs index 21102f9b1..65d75ecb7 100644 --- a/core/src/builtin_widgets/layout_box.rs +++ b/core/src/builtin_widgets/layout_box.rs @@ -1,7 +1,7 @@ use crate::prelude::*; /// Widget let user to access the layout result of its child. -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct LayoutBox { #[declare(skip)] /// the rect box of its child and the coordinate is relative to its parent. @@ -62,13 +62,11 @@ mod tests { use ribir_dev_helper::*; fn smoke() -> Widget { - widget! { - MockMulti { - LayoutBox { - id: layout_box, - MockBox { size: Size::new(100., 200.) } - } - MockBox { size: layout_box.rect.size } + fn_widget! { + let mut first_box = @MockBox { size: Size::new(100., 200.) }; + let second_box = @MockBox { size: pipe!($first_box.layout_size()) }; + @MockMulti { + @ { [first_box, second_box ] } } } .into() diff --git a/core/src/builtin_widgets/lifecycle.rs b/core/src/builtin_widgets/lifecycle.rs index 7a2200442..ef1dce14e 100644 --- a/core/src/builtin_widgets/lifecycle.rs +++ b/core/src/builtin_widgets/lifecycle.rs @@ -9,7 +9,7 @@ define_widget_context!(LifecycleEvent); pub type LifecycleSubject = MutRefItemSubject<'static, AllLifecycle, Infallible>; -#[derive(Declare, Declare2, Default)] +#[derive(Declare2, Default)] pub struct LifecycleListener { #[declare(skip)] lifecycle: LifecycleSubject, @@ -42,44 +42,6 @@ macro_rules! match_closure { }; } -impl LifecycleListenerDeclarer { - pub fn on_mounted(mut self, handler: impl FnMut(&mut LifecycleEvent) + 'static) -> Self { - let _ = self - .subject() - .filter_map(match_closure!(Mounted)) - .take(1) - .subscribe(handler); - - self - } - - pub fn on_performed_layout(mut self, handler: impl FnMut(&mut LifecycleEvent) + 'static) -> Self { - let _ = self - .subject() - .filter_map(match_closure!(PerformedLayout)) - .subscribe(handler); - - self - } - - pub fn on_disposed(mut self, handler: impl FnMut(&mut LifecycleEvent) + 'static) -> Self { - let _ = self - .subject() - .filter_map(match_closure!(Disposed)) - .take(1) - .subscribe(handler); - - self - } - - fn subject(&mut self) -> LifecycleSubject { - self - .lifecycle - .get_or_insert_with(LifecycleSubject::default) - .clone() - } -} - impl LifecycleListenerDeclarer2 { pub fn on_mounted(mut self, handler: impl FnMut(&mut LifecycleEvent) + 'static) -> Self { let _ = self diff --git a/core/src/builtin_widgets/margin.rs b/core/src/builtin_widgets/margin.rs index 8369f087b..79f686acd 100644 --- a/core/src/builtin_widgets/margin.rs +++ b/core/src/builtin_widgets/margin.rs @@ -9,7 +9,7 @@ pub struct EdgeInsets { } /// A widget that create space around its child. -#[derive(SingleChild, Default, Clone, PartialEq, Declare, Declare2)] +#[derive(SingleChild, Default, Clone, PartialEq, Declare2)] pub struct Margin { #[declare(builtin, default)] pub margin: EdgeInsets, @@ -131,8 +131,8 @@ mod tests { use ribir_dev_helper::*; fn smoke() -> Widget { - widget! { - MockBox { + fn_widget! { + @MockBox { margin: EdgeInsets::symmetrical(1., 1.), size: Size::new(100., 100.) } diff --git a/core/src/builtin_widgets/mouse_hover.rs b/core/src/builtin_widgets/mouse_hover.rs index 29e7726e1..77ae24366 100644 --- a/core/src/builtin_widgets/mouse_hover.rs +++ b/core/src/builtin_widgets/mouse_hover.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -#[derive(PartialEq, Clone, Declare, Declare2)] +#[derive(PartialEq, Clone, Declare2)] pub struct MouseHover { #[declare(skip, default)] hover: bool, diff --git a/core/src/builtin_widgets/opacity.rs b/core/src/builtin_widgets/opacity.rs index b2d1a2fca..c2976bef9 100644 --- a/core/src/builtin_widgets/opacity.rs +++ b/core/src/builtin_widgets/opacity.rs @@ -1,7 +1,7 @@ use crate::impl_query_self_only; use crate::prelude::*; -#[derive(Declare, Declare2, Default, Clone, SingleChild)] +#[derive(Declare2, Default, Clone, SingleChild)] pub struct Opacity { #[declare(builtin, default = 1.)] pub opacity: f32, diff --git a/core/src/builtin_widgets/padding.rs b/core/src/builtin_widgets/padding.rs index ad33362e7..a3b64e4f9 100644 --- a/core/src/builtin_widgets/padding.rs +++ b/core/src/builtin_widgets/padding.rs @@ -1,7 +1,7 @@ use crate::{impl_query_self_only, prelude::*}; /// A widget that insets its child by the given padding. -#[derive(SingleChild, Clone, Declare, Declare2)] +#[derive(SingleChild, Clone, Declare2)] pub struct Padding { #[declare(builtin)] pub padding: EdgeInsets, @@ -66,10 +66,10 @@ mod tests { use ribir_dev_helper::*; fn smoke() -> Widget { - widget! { - MockMulti { + fn_widget! { + @MockMulti { padding: EdgeInsets::only_left(1.), - MockBox { + @MockBox { size: Size::new(100., 100.), } } diff --git a/core/src/builtin_widgets/pointer_pressed.rs b/core/src/builtin_widgets/pointer_pressed.rs index 3a70daccd..ca983d10b 100644 --- a/core/src/builtin_widgets/pointer_pressed.rs +++ b/core/src/builtin_widgets/pointer_pressed.rs @@ -2,7 +2,7 @@ use crate::prelude::*; /// Widget keep the pointer press state of its child. As a builtin widget, user /// can call `pointer_pressed` method to get the pressed state of a widget. -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct PointerPressed { #[declare(skip, builtin)] pointer_pressed: bool, diff --git a/core/src/builtin_widgets/scrollable.rs b/core/src/builtin_widgets/scrollable.rs index a470617db..aed8b4602 100644 --- a/core/src/builtin_widgets/scrollable.rs +++ b/core/src/builtin_widgets/scrollable.rs @@ -15,7 +15,7 @@ pub enum Scrollable { } /// Helper struct for builtin scrollable field. -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct ScrollableWidget { #[declare(builtin, default)] pub scrollable: Scrollable, @@ -126,8 +126,8 @@ mod tests { use winit::event::{DeviceId, ModifiersState, MouseScrollDelta, TouchPhase, WindowEvent}; fn test_assert(scrollable: Scrollable, delta_x: f32, delta_y: f32, expect_x: f32, expect_y: f32) { - let w = widget! { - MockBox { + let w = fn_widget! { + @MockBox { size: Size::new(1000., 1000.), scrollable, } @@ -179,7 +179,7 @@ mod tests { test_assert(Scrollable::Both, 100., 100., 0., 0.); } - #[derive(SingleChild, Declare, Clone)] + #[derive(SingleChild, Declare2, Clone)] pub struct FixedBox { pub size: Size, } @@ -201,15 +201,15 @@ mod tests { fn scroll_content_expand() { let _guard = unsafe { AppCtx::new_lock_scope() }; - let w = widget! { - FixedBox { + let w = fn_widget! { + @FixedBox { size: Size::new(200., 200.), - ScrollableWidget { + @ScrollableWidget { scrollable: Scrollable::Both, on_performed_layout: move |ctx| { assert_eq!(ctx.box_size(), Some(Size::new(200., 200.))); }, - MockBox { + @MockBox { size: Size::new(100., 100.), on_performed_layout: move |ctx| { assert_eq!(ctx.box_size(), Some(Size::new(200., 200.))); diff --git a/core/src/builtin_widgets/theme.rs b/core/src/builtin_widgets/theme.rs index 6850e046c..dfe1776a8 100644 --- a/core/src/builtin_widgets/theme.rs +++ b/core/src/builtin_widgets/theme.rs @@ -66,7 +66,7 @@ pub enum Theme { Inherit(InheritTheme), } -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct ThemeWidget { pub theme: Rc, } diff --git a/core/src/builtin_widgets/theme/compose_decorators.rs b/core/src/builtin_widgets/theme/compose_decorators.rs index 211dff454..11acab4da 100644 --- a/core/src/builtin_widgets/theme/compose_decorators.rs +++ b/core/src/builtin_widgets/theme/compose_decorators.rs @@ -71,16 +71,16 @@ impl ComposeDecorators { #[cfg(test)] mod tests { - use crate::{prelude::*, test_helper::*}; + use crate::{prelude::*, reset_test_env, test_helper::*}; use ribir_dev_helper::*; #[test] fn compose_decorator_smoke() { - let _guard = unsafe { AppCtx::new_lock_scope() }; + reset_test_env!(); let mut theme = FullTheme::default(); - #[derive(Declare)] + #[derive(Declare2)] struct Size100Style; impl ComposeDecorator for Size100Style { @@ -99,8 +99,8 @@ mod tests { .into() }); - let w = widget! { - Size100Style { MockBox { + let w = fn_widget! { + @Size100Style { @MockBox { size: Size::zero(), }} }; diff --git a/core/src/builtin_widgets/transform_widget.rs b/core/src/builtin_widgets/transform_widget.rs index 89ebc0a04..db7890ce5 100644 --- a/core/src/builtin_widgets/transform_widget.rs +++ b/core/src/builtin_widgets/transform_widget.rs @@ -1,6 +1,6 @@ use crate::{impl_query_self_only, prelude::*, widget::hit_test_impl}; -#[derive(SingleChild, Declare, Declare2, Clone)] +#[derive(SingleChild, Declare2, Clone)] pub struct TransformWidget { #[declare(builtin, default)] pub transform: Transform, @@ -41,10 +41,10 @@ mod tests { use ribir_dev_helper::*; fn smoke() -> Widget { - widget! { - TransformWidget { + fn_widget! { + @TransformWidget { transform: Transform::new(2., 0., 0., 2., 0., 0.), - MockBox { + @MockBox { size: Size::new(100., 100.) } } diff --git a/core/src/builtin_widgets/unconstrained_box.rs b/core/src/builtin_widgets/unconstrained_box.rs index d8b5ac9a8..8338f8f84 100644 --- a/core/src/builtin_widgets/unconstrained_box.rs +++ b/core/src/builtin_widgets/unconstrained_box.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -#[derive(Declare, Declare2, SingleChild)] +#[derive(Declare2, SingleChild)] /// A widget that imposes no constraints on its child, allowing it to layout and /// display as its "natural" size. Its size is equal to its child then clamp by /// parent. @@ -82,18 +82,18 @@ mod tests { fn smoke() -> Widget { let size = Size::new(200., 200.); - widget! { - MockMulti { - UnconstrainedBox { - MockBox { size} + fn_widget! { + @MockMulti { + @UnconstrainedBox { + @MockBox { size} } - UnconstrainedBox { + @UnconstrainedBox { dir: UnconstrainedDir::X, - MockBox { size } + @MockBox { size } } - UnconstrainedBox { + @UnconstrainedBox { dir: UnconstrainedDir::Y, - MockBox { size } + @MockBox { size } } } } diff --git a/core/src/builtin_widgets/visibility.rs b/core/src/builtin_widgets/visibility.rs index 98ca5b307..9f05b5692 100644 --- a/core/src/builtin_widgets/visibility.rs +++ b/core/src/builtin_widgets/visibility.rs @@ -1,6 +1,6 @@ use crate::{impl_query_self_only, prelude::*}; -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct Visibility { #[declare(builtin)] pub visible: bool, @@ -23,7 +23,7 @@ impl ComposeChild for Visibility { } } -#[derive(SingleChild, Declare, Declare2, Clone)] +#[derive(SingleChild, Declare2, Clone)] struct VisibilityRender { display: bool, } diff --git a/core/src/builtin_widgets/void.rs b/core/src/builtin_widgets/void.rs index 334306bac..8580ac9e8 100644 --- a/core/src/builtin_widgets/void.rs +++ b/core/src/builtin_widgets/void.rs @@ -4,7 +4,7 @@ use crate::{impl_query_self_only, prelude::*}; /// node in `widget!` macro, or hold a place in tree. When it have a child /// itself will be dropped when build tree, otherwise as a render widget but do /// nothing. -#[derive(Declare, SingleChild, Declare2)] +#[derive(SingleChild, Declare2)] pub struct Void; impl Render for Void { diff --git a/core/src/context/widget_ctx.rs b/core/src/context/widget_ctx.rs index 591d37ec4..3d0a56bdd 100644 --- a/core/src/context/widget_ctx.rs +++ b/core/src/context/widget_ctx.rs @@ -216,8 +216,8 @@ mod tests { fn map_self_eq_self() { let _guard = unsafe { AppCtx::new_lock_scope() }; - let w = widget! { - MockBox { + let w = fn_widget! { + @MockBox { size: Size::zero(), margin: EdgeInsets::all(2.), } @@ -239,10 +239,10 @@ mod tests { fn map_transform_test() { let _guard = unsafe { AppCtx::new_lock_scope() }; - let w = widget! { - MockBox { + let w = fn_widget! { + @MockBox { size: Size::new(100., 100.), - MockBox { + @MockBox { transform: Transform::scale(0.5, 0.5), left_anchor: 30., top_anchor: 30., diff --git a/core/src/declare.rs b/core/src/declare.rs index df23d01a9..587f9e62f 100644 --- a/core/src/declare.rs +++ b/core/src/declare.rs @@ -2,11 +2,6 @@ use crate::{context::BuildCtx, prelude::Pipe, state::ModifyScope}; use rxrust::ops::box_it::BoxOp; use std::convert::Infallible; -pub trait Declare { - type Builder: DeclareBuilder; - fn declare_builder() -> Self::Builder; -} - /// The next version of `Declare` trait. It will replace the `Declare` trait /// after it is stable. pub trait Declare2 { @@ -76,46 +71,6 @@ impl + 'static> DeclareFrom, Pipe<()>> for Declar fn declare_from(value: Pipe) -> Self { Self::Pipe(value.map(U::from)) } } -#[derive(Debug, PartialEq, Hash)] -pub struct DeclareStripOption(O); - -impl From for DeclareStripOption> { - #[inline] - fn from(value: V) -> Self { Self(Some(value)) } -} - -impl From> for DeclareStripOption> { - #[inline] - fn from(value: Option) -> Self { Self(value) } -} - -impl DeclareStripOption> { - #[inline] - pub fn into_option_value(self) -> Option { self.0 } -} - -#[cfg(test)] -mod tests { - use super::*; - use ribir_painter::{Brush, Color}; - - #[test] - fn inner_value_into() { - assert_eq!( - DeclareStripOption::from(Brush::from(Color::RED)), - DeclareStripOption(Some(Brush::from(Color::RED))) - ); - } - - #[test] - fn option_self_can_use_with_strip() { - assert_eq!( - DeclareStripOption::from(Some(Brush::from(Color::RED))), - DeclareStripOption(Some(Brush::from(Color::RED))) - ) - } -} - /// struct help the generate code have better type hint. #[derive(Clone)] pub struct DeclareFieldValue(F); diff --git a/core/src/events/character.rs b/core/src/events/character.rs index d8b9f5c17..15600b33e 100644 --- a/core/src/events/character.rs +++ b/core/src/events/character.rs @@ -48,8 +48,8 @@ mod tests { let receive = Rc::new(RefCell::new("".to_string())); let c_receive = receive.clone(); - let widget = widget! { - MockBox { + let widget = fn_widget! { + @MockBox { size: ZERO_SIZE, auto_focus: true, on_chars: move |event| c_receive.borrow_mut().push_str(&event.chars) @@ -75,8 +75,8 @@ mod tests { let chars_receive = receive.clone(); let capture_receive = receive.clone(); - let widget = widget! { - MockBox { + let widget = fn_widget! { + @MockBox { size: ZERO_SIZE, on_chars_capture: move |event| { let chars = event.chars.to_string(); @@ -84,7 +84,7 @@ mod tests { let char = (chars.parse::().unwrap() * 2).to_string(); capture_receive.borrow_mut().push_str(&char); }, - MockBox { + @MockBox { size: ZERO_SIZE, auto_focus: true, on_chars: move |event| chars_receive.borrow_mut().push_str(&event.chars), diff --git a/core/src/events/dispatcher.rs b/core/src/events/dispatcher.rs index 95be391c2..c32719793 100644 --- a/core/src/events/dispatcher.rs +++ b/core/src/events/dispatcher.rs @@ -311,7 +311,7 @@ mod tests { let event_record = Rc::new(RefCell::new(vec![])); let record = record_pointer( event_record.clone(), - widget! { MockBox { size: Size::new(100., 30.) } }.into(), + MockBox { size: Size::new(100., 30.) }.into(), ); let root = record_pointer( event_record.clone(), @@ -425,7 +425,7 @@ mod tests { let event_record = Rc::new(RefCell::new(vec![])); let root = record_pointer( event_record.clone(), - widget! { MockBox { size: Size::new(100., 30.) } }.into(), + MockBox { size: Size::new(100., 30.) }.into(), ); let mut wnd = TestWindow::new(root); wnd.draw_frame(); @@ -628,16 +628,16 @@ mod tests { reset_test_env!(); let click_path = Stateful::new(vec![]) as Stateful>; - let w = widget! { - states { click_path: click_path.clone_stateful() } - MockBox { + let c_click_path = click_path.clone_writer(); + let w = fn_widget! { + @MockBox { size: Size::new(100., 100.), - on_tap: move |_| (*click_path).push(4), - on_tap_capture: move |_| (*click_path).push(1), - MockBox { + on_tap: move |_| $c_click_path.write().push(4), + on_tap_capture: move |_| $c_click_path.write().push(1), + @MockBox { size: Size::new(100., 100.), - on_tap: move |_| (*click_path).push(3), - on_tap_capture: move |_| (*click_path).push(2), + on_tap: move |_| $c_click_path.write().push(3), + on_tap_capture: move |_| $c_click_path.write().push(2), } } }; @@ -681,15 +681,15 @@ mod tests { reset_test_env!(); let click_path = Stateful::new(0); - let w = widget! { - states { click_path: click_path.clone_stateful() } - MockMulti { - on_tap: move |_| *click_path += 1, - MockBox { + let c_click_path = click_path.clone_writer(); + let w = fn_widget! { + @MockMulti { + on_tap: move |_| *$c_click_path.write() += 1, + @MockBox { size: Size::new(100., 100.), - on_tap: move |_| *click_path += 1, + on_tap: move |_| *$c_click_path.write() += 1, } - MockBox { size: Size::new(100., 400.) } + @MockBox { size: Size::new(100., 400.) } } }; @@ -723,7 +723,7 @@ mod tests { wnd.run_frame_tasks(); { - let mut clicked = click_path.state_ref(); + let mut clicked = click_path.write(); assert_eq!(*clicked, 2); *clicked = 0; } @@ -765,13 +765,13 @@ mod tests { fn focus_change_by_event() { reset_test_env!(); - let w = widget! { - MockMulti { - MockBox { + let w = fn_widget! { + @MockMulti { + @MockBox { size: Size::new(50., 50.), - tab_index: 0 + tab_index: 0i16 } - MockBox { + @MockBox { size: Size::new(50., 50.) } } @@ -849,21 +849,21 @@ mod tests { let data1 = data.clone(); let data2 = data.clone(); - let w = widget! { - MockBox { + let w = fn_widget! { + @MockBox { size: Size::new(200., 200.), - MockStack { + @MockStack { child_pos: vec![ Point::new(50., 50.), Point::new(100., 100.), ], - MockBox { + @MockBox { on_mounted: move |ctx| { data1.borrow_mut().wid1 = Some(ctx.id); }, size: Size::new(100., 100.), } - MockBox { + @MockBox { on_mounted: move |ctx| { data2.borrow_mut().wid2 = Some(ctx.id); }, diff --git a/core/src/events/focus_mgr.rs b/core/src/events/focus_mgr.rs index cd76f338e..bd2bd65fe 100644 --- a/core/src/events/focus_mgr.rs +++ b/core/src/events/focus_mgr.rs @@ -540,10 +540,10 @@ mod tests { // two auto focus widget let size = Size::zero(); - let widget = widget! { - MockMulti { - MockBox { size, auto_focus: true, } - MockBox { size, auto_focus: true, } + let widget = fn_widget! { + @MockMulti { + @MockBox { size, auto_focus: true, } + @MockBox { size, auto_focus: true, } } }; @@ -563,10 +563,10 @@ mod tests { reset_test_env!(); // one auto focus widget let size = Size::zero(); - let widget = widget! { - MockMulti { - MockBox { size } - MockBox { size, auto_focus: true} + let widget = fn_widget! { + @MockMulti { + @MockBox { size } + @MockBox { size, auto_focus: true} } }; @@ -588,14 +588,14 @@ mod tests { reset_test_env!(); let size = Size::zero(); - let widget = widget! { - MockMulti { - MockBox { size, tab_index: -1, } - MockBox { size, tab_index: 0, } - MockBox { size, tab_index: 1, auto_focus: true} - MockBox { size, tab_index: 2, } - MockMulti { tab_index: 4, MockBox { size, tab_index: 3, } } - MockBox { size, tab_index: 0 } + let widget = fn_widget! { + @MockMulti { + @MockBox { size, tab_index: -1i16, } + @MockBox { size, tab_index: 0i16, } + @MockBox { size, tab_index: 1i16, auto_focus: true} + @MockBox { size, tab_index: 2i16, } + @MockMulti { tab_index: 4i16, @MockBox { size, tab_index: 3i16, } } + @MockBox { size, tab_index: 0i16 } } }; @@ -758,22 +758,22 @@ mod tests { fn scope_node_request_focus() { reset_test_env!(); - let w = widget! { - MockMulti{ - MockBox{ + let w = fn_widget! { + @MockMulti{ + @MockBox{ size: Size::zero(), on_key_down: move |_| {} } - FocusScope { - MockBox{ + @FocusScope { + @MockBox{ size: Size::zero(), - MockBox{ + @MockBox{ size: Size::zero(), on_key_down: move |_| {} } } } - MockBox{ + @MockBox{ size: Size::zero(), on_key_down: move |_| {} } diff --git a/core/src/events/listener_impl_helper.rs b/core/src/events/listener_impl_helper.rs index 042fd9d5c..2725162a4 100644 --- a/core/src/events/listener_impl_helper.rs +++ b/core/src/events/listener_impl_helper.rs @@ -43,21 +43,12 @@ macro_rules! impl_listener { ($doc: literal, $name: ident, $event_ty: ident) => { paste::paste! { #[doc= $doc] - #[derive(Declare, Declare2)] + #[derive(Declare2)] pub struct [<$name Listener>]{ #[declare(skip)] [<$name:snake _subject>]: [<$name Subject>] } - impl [<$name ListenerDeclarer>] { - fn subject(&mut self) -> [<$name Subject>] { - self - .[<$name:snake _subject>] - .get_or_insert_with([<$name Subject>]::default) - .clone() - } - } - impl [<$name ListenerDeclarer2>] { fn subject(&mut self) -> [<$name Subject>] { self @@ -119,28 +110,6 @@ macro_rules! impl_multi_event_listener { } )+ } - - impl [<$name ListenerDeclarer>] { - $( - #[doc = "Sets up a function that will be called \ - whenever the `" $event_ty "` is delivered"] - pub fn []( - mut self, - handler: impl FnMut(&mut [<$name Event>]) + 'static - ) -> Self { - self - .subject() - .filter_map( - (|e| match e { - []::$event_ty(e) => Some(e), - _ => None, - }) as fn(&mut []) -> Option<&mut [<$name Event>]> - ) - .subscribe(handler); - self - } - )+ - } } }; } diff --git a/core/src/events/pointers.rs b/core/src/events/pointers.rs index 5751dce4e..63e2e5e16 100644 --- a/core/src/events/pointers.rs +++ b/core/src/events/pointers.rs @@ -161,52 +161,6 @@ fn x_times_tap_map_filter( } } -impl PointerListenerDeclarer { - pub fn on_double_tap(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { - self.on_x_times_tap((2, handler)) - } - - pub fn on_double_tap_capture(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { - self.on_x_times_tap_capture((2, handler)) - } - - pub fn on_triple_tap(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { - self.on_x_times_tap((3, handler)) - } - - pub fn on_triple_tap_capture(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { - self.on_x_times_tap_capture((3, handler)) - } - - pub fn on_x_times_tap( - self, - (times, handler): (usize, impl FnMut(&mut PointerEvent) + 'static), - ) -> Self { - self.on_x_times_tap_impl(times, MULTI_TAP_DURATION, false, handler) - } - - pub fn on_x_times_tap_capture( - self, - (times, handler): (usize, impl FnMut(&mut PointerEvent) + 'static), - ) -> Self { - self.on_x_times_tap_impl(times, MULTI_TAP_DURATION, true, handler) - } - - fn on_x_times_tap_impl( - mut self, - times: usize, - dur: Duration, - capture: bool, - handler: impl FnMut(&mut PointerEvent) + 'static, - ) -> Self { - self - .subject() - .filter_map(x_times_tap_map_filter(times, dur, capture)) - .subscribe(handler); - self - } -} - impl PointerListenerDeclarer2 { pub fn on_double_tap(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { self.on_x_times_tap((2, handler)) diff --git a/core/src/events/wheel.rs b/core/src/events/wheel.rs index 6a1c09571..534af4f6f 100644 --- a/core/src/events/wheel.rs +++ b/core/src/events/wheel.rs @@ -56,14 +56,14 @@ mod tests { let bubble_event_order = event_order.clone(); let capture_event_order = event_order.clone(); - let widget = widget! { - MockBox { + let widget = fn_widget! { + @MockBox { size: Size::new(200., 200.), on_wheel_capture: move |wheel| { *capture_receive.borrow_mut() = (wheel.delta_x, wheel.delta_y); (*capture_event_order.borrow_mut()).push("capture"); }, - MockBox { + @MockBox { size: Size::new(100., 100.), auto_focus: true, on_wheel: move |wheel| { diff --git a/core/src/lib.rs b/core/src/lib.rs index 283a537d9..12c93d958 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -58,7 +58,7 @@ pub mod prelude { #[doc(no_inline)] pub use ribir_macros::{ ctx, fn_widget, include_svg, map_writer, pipe, rdl, ribir_expanded_ಠ_ಠ, set_build_ctx, - split_writer, watch, widget, Declare, Declare2, Lerp, MultiChild, SingleChild, Template, + split_writer, watch, Declare2, Lerp, MultiChild, SingleChild, Template, }; #[doc(no_inline)] pub use ribir_painter::*; diff --git a/core/src/pipe.rs b/core/src/pipe.rs index c0d6f3520..7268a76d9 100644 --- a/core/src/pipe.rs +++ b/core/src/pipe.rs @@ -849,21 +849,19 @@ mod tests { wid: Option, } - fn build(item: Writer) -> Widget { - let item = item.into_inner(); - widget! { - states { task: item.clone_stateful() } - TaskWidget { - delay_drop_until: !task.pin, - layout_cnt: task.layout_cnt.clone(), - paint_cnt: task.paint_cnt.clone(), - trigger: task.trigger, + fn build(task: Writer) -> Widget { + fn_widget! { + @TaskWidget { + delay_drop_until: pipe!(!$task.pin), + layout_cnt: pipe!($task.layout_cnt.clone()), + paint_cnt: pipe!($task.paint_cnt.clone()), + trigger: pipe!($task.trigger), on_mounted: move |ctx| { - task.mounted += 1; - task.wid = Some(ctx.id); + $task.write().mounted += 1; + $task.write().wid = Some(ctx.id); }, on_disposed: move |ctx| { - let wid = task.wid.take(); + let wid = $task.write().wid.take(); assert_eq!(wid, Some(ctx.id)); } } @@ -871,7 +869,7 @@ mod tests { .into() } - #[derive(Declare)] + #[derive(Declare2)] struct TaskWidget { trigger: u32, paint_cnt: Rc>, @@ -933,7 +931,7 @@ mod tests { // the remove pined widget only mark self dirty let first_layout_cnt = removed[0].state_ref().layout_cnt.get(); - let secord_layout_cnt = removed[1].state_ref().layout_cnt.get(); + let second_layout_cnt = removed[1].state_ref().layout_cnt.get(); let host_layout_cnt = tasks.state_ref()[0].state_ref().layout_cnt.get(); removed[0].state_ref().trigger += 1; wnd.draw_frame(); @@ -942,7 +940,7 @@ mod tests { first_layout_cnt + 1 ); assert_eq!(removed[0].state_ref().paint_cnt.get(), 4); - assert_eq!(removed[1].state_ref().layout_cnt.get(), secord_layout_cnt); + assert_eq!(removed[1].state_ref().layout_cnt.get(), second_layout_cnt); assert_eq!( tasks.state_ref()[0].state_ref().layout_cnt.get(), host_layout_cnt diff --git a/core/src/state/stateful.rs b/core/src/state/stateful.rs index 9032da3d4..ab62429a1 100644 --- a/core/src/state/stateful.rs +++ b/core/src/state/stateful.rs @@ -462,7 +462,7 @@ mod tests { fn fix_pin_widget_node() { crate::reset_test_env!(); - let mut wnd = TestWindow::new(widget! { MockBox { size: Size::new(100., 100.) } }); + let mut wnd = TestWindow::new(MockBox { size: Size::new(100., 100.) }); wnd.draw_frame(); let tree = wnd.widget_tree.borrow(); assert_eq!(tree.root().descendants(&tree.arena).count(), 1); diff --git a/core/src/test_helper.rs b/core/src/test_helper.rs index e8619ba22..e6a5b3b00 100644 --- a/core/src/test_helper.rs +++ b/core/src/test_helper.rs @@ -144,7 +144,7 @@ impl TestShellWindow { } } -#[derive(Declare, MultiChild)] +#[derive(Declare2, MultiChild)] pub struct MockStack { child_pos: Vec, } @@ -175,10 +175,10 @@ impl Render for MockStack { impl_query_self_only!(MockStack); -#[derive(Declare, Declare2, MultiChild)] +#[derive(Declare2, MultiChild)] pub struct MockMulti; -#[derive(Declare, Declare2, Clone, SingleChild)] +#[derive(Declare2, Clone, SingleChild)] pub struct MockBox { pub size: Size, } diff --git a/core/src/widget_children.rs b/core/src/widget_children.rs index 52d52bb14..1335fa6ce 100644 --- a/core/src/widget_children.rs +++ b/core/src/widget_children.rs @@ -163,13 +163,13 @@ mod tests { #[test] fn compose_template_child() { reset_test_env!(); - #[derive(Declare)] + #[derive(Declare2)] struct Page; - #[derive(Declare, SingleChild)] + #[derive(Declare2, SingleChild)] struct Header; - #[derive(Declare, SingleChild)] + #[derive(Declare2, SingleChild)] struct Content; - #[derive(Declare, SingleChild)] + #[derive(Declare2, SingleChild)] struct Footer; #[derive(Template)] @@ -187,11 +187,11 @@ mod tests { } } - widget! { - Page { - Header { Void {} } - Content { Void {} } - Footer { Void {} } + fn_widget! { + @Page { + @Header { @Void {} } + @Content { @Void {} } + @Footer { @Void {} } } }; } @@ -200,9 +200,9 @@ mod tests { fn compose_option_child() { reset_test_env!(); - #[derive(Declare)] + #[derive(Declare2)] struct Parent; - #[derive(Declare, SingleChild)] + #[derive(Declare2, SingleChild)] struct Child; impl ComposeChild for Parent { @@ -213,9 +213,9 @@ mod tests { } } - widget! { - Parent { - Child { Void {} } + fn_widget! { + @Parent { + @Child { @Void {} } } }; } @@ -234,9 +234,9 @@ mod tests { fn tuple_as_vec() { reset_test_env!(); - #[derive(Declare)] + #[derive(Declare2)] struct A; - #[derive(Declare)] + #[derive(Declare2)] struct B; impl ComposeChild for A { @@ -246,10 +246,11 @@ mod tests { unreachable!("Only for syntax support check"); } } - widget! { - A { - B {} - B {} + let a = A; + fn_widget! { + @$a { + @ { B} + @ { B } } }; } @@ -311,16 +312,16 @@ mod tests { fn pair_to_pair() { reset_test_env!(); - #[derive(Declare)] + #[derive(Declare2)] struct P; impl ComposeChild for P { - type Child = WidgetOf; + type Child = WidgetOf>; fn compose_child(_: State, _: Self::Child) -> Widget { unreachable!() } } - let _ = widget! { - P { MockBox { Void {} } } + let _ = fn_widget! { + @P { @MockBox { @Void {} } } }; } @@ -348,7 +349,7 @@ mod tests { pub struct ConfigTml { _field: Option, } - #[derive(Declare, Declare2)] + #[derive(Declare2)] struct Host {} impl ComposeChild for Host { diff --git a/core/src/widget_children/compose_child_impl.rs b/core/src/widget_children/compose_child_impl.rs index bccc532e0..258cca153 100644 --- a/core/src/widget_children/compose_child_impl.rs +++ b/core/src/widget_children/compose_child_impl.rs @@ -191,7 +191,7 @@ mod tests { fn compose_child(_: State, _: Self::Child) -> Widget { Void.into() } } - #[derive(Declare)] + #[derive(Declare2)] struct X; impl ComposeChild for X { diff --git a/core/src/widget_tree.rs b/core/src/widget_tree.rs index 7efaf0453..b96bb09aa 100644 --- a/core/src/widget_tree.rs +++ b/core/src/widget_tree.rs @@ -530,10 +530,10 @@ mod tests { @MockMulti { @ { (0..100).map(|_| - widget! { MockBox { + @MockBox { size: Size::new(150., 50.), background: Color::BLUE, - }}) + }) } }}; let mut wnd = TestWindow::new_with_size(w1, win_size); @@ -545,10 +545,10 @@ mod tests { @MockMulti { @ { (0..1).map(|_| - widget! { MockBox { + @MockBox { size: Size::new(150., 50.), background: Color::BLUE, - }}) + }) } }}; diff --git a/core/src/widget_tree/layout_info.rs b/core/src/widget_tree/layout_info.rs index b98b6ffeb..f1d8af832 100644 --- a/core/src/widget_tree/layout_info.rs +++ b/core/src/widget_tree/layout_info.rs @@ -305,9 +305,9 @@ mod tests { use super::*; use crate::{impl_query_self_only, prelude::*, reset_test_env, test_helper::*}; use ribir_dev_helper::*; - use std::{cell::RefCell, rc::Rc}; + use std::{cell::Cell, rc::Rc}; - #[derive(Declare, Clone, SingleChild)] + #[derive(Declare2, Clone, SingleChild)] struct OffsetBox { pub offset: Point, pub size: Size, @@ -369,20 +369,18 @@ mod tests { let layout_order = Stateful::new(vec![]); let trigger = Stateful::new(Size::zero()); - let w = widget! { - states { - layout_order: layout_order.clone_stateful(), - trigger: trigger.clone_stateful() - } - MockBox { - size: *trigger, - on_performed_layout: move |_| layout_order.push(1), - MockBox { - size: *trigger, - on_performed_layout: move |_| layout_order.push(2), - MockBox { - size: *trigger, - on_performed_layout: move |_| layout_order.push(3), + let order = layout_order.clone_writer(); + let size = trigger.clone_reader(); + let w = fn_widget! { + @MockBox { + size: pipe!(*$size), + on_performed_layout: move |_| $order.write().push(1), + @MockBox { + size: pipe!(*$size), + on_performed_layout: move |_| $order.write().push(2), + @MockBox { + size: pipe!(*$size), + on_performed_layout: move |_| $order.write().push(3), } } } @@ -390,12 +388,12 @@ mod tests { let mut wnd = TestWindow::new(w); wnd.draw_frame(); - assert_eq!([3, 2, 1], &**layout_order.state_ref()); + assert_eq!([3, 2, 1], &**layout_order.read()); { - *trigger.state_ref() = Size::new(1., 1.); + *trigger.write() = Size::new(1., 1.); } wnd.draw_frame(); - assert_eq!([3, 2, 1, 3, 2, 1], &**layout_order.state_ref()); + assert_eq!([3, 2, 1, 3, 2, 1], &**layout_order.read()); } #[test] @@ -403,16 +401,14 @@ mod tests { reset_test_env!(); let trigger = Stateful::new(Size::zero()); - let w = widget! { - states {trigger: trigger.clone_stateful()} - OffsetBox { + let size = trigger.clone_reader(); + let w = fn_widget! { + @OffsetBox { size: Size::new(100., 100.), offset: Point::new(50., 50.), - MockBox { + @MockBox { size: Size::new(50., 50.), - MockBox { - size: *trigger, - } + @MockBox { size: pipe!(*$size) } } } }; @@ -429,7 +425,7 @@ mod tests { ); { - *trigger.state_ref() = Size::new(10., 10.); + *trigger.write() = Size::new(10., 10.); } wnd.draw_frame(); @@ -448,44 +444,41 @@ mod tests { reset_test_env!(); let trigger = Stateful::new(Size::zero()); - let cnt = Rc::new(RefCell::new(0)); + let cnt = Rc::new(Cell::new(0)); let cnt2 = cnt.clone(); - let w = widget! { - states {trigger: trigger.clone_stateful()} - init { let cnt = cnt2; } - MockBox { + let size = trigger.clone_reader(); + let w = fn_widget! { + @MockBox { size: Size::new(50., 50.), - on_performed_layout: move |_| *cnt.borrow_mut() += 1, - MockBox { - size: *trigger, - } + on_performed_layout: move |_| cnt2.set(cnt2.get() + 1), + @MockBox { size: pipe!(*$size) } } }; let mut wnd = TestWindow::new(w); wnd.draw_frame(); - assert_eq!(*cnt.borrow(), 1); + assert_eq!(cnt.get(), 1); { - *trigger.state_ref() = Size::new(10., 10.); + *trigger.write() = Size::new(10., 10.); } wnd.draw_frame(); - assert_eq!(*cnt.borrow(), 2); + assert_eq!(cnt.get(), 2); } #[test] fn layout_visit_prev_position() { reset_test_env!(); - #[derive(Declare)] + #[derive(Declare2)] struct MockWidget { - pos: RefCell, + pos: Cell, size: Size, } impl Render for MockWidget { fn perform_layout(&self, _: BoxClamp, ctx: &mut LayoutCtx) -> Size { - *self.pos.borrow_mut() = ctx.box_pos().unwrap_or_default(); + self.pos.set(ctx.box_pos().unwrap_or_default()); self.size } #[inline] @@ -497,33 +490,27 @@ mod tests { impl_query_self_only!(MockWidget); - let pos = Rc::new(RefCell::new(Point::zero())); + let pos = Rc::new(Cell::new(Point::zero())); let pos2 = pos.clone(); let trigger = Stateful::new(Size::zero()); - let w = widget! { - states {trigger: trigger.clone_stateful()} - init { - let pos = pos2.clone(); - } - MockMulti { - MockBox{ - size: Size::new(50., 50.), - } - MockWidget { - id: w, - size: *trigger, - pos: RefCell::new(Point::zero()), - on_performed_layout: move |_| { - *pos.borrow_mut() = *w.pos.borrow(); - } + let size = trigger.clone_reader(); + let w = fn_widget! { + let w = @MockWidget { + size: pipe!(*$size), + pos: Cell::new(Point::zero()), + }; + @MockMulti { + @MockBox{ size: Size::new(50., 50.) } + @$w { + on_performed_layout: move |_| pos2.set($w.pos.get()) } } }; let mut wnd = TestWindow::new(w); wnd.draw_frame(); - *trigger.state_ref() = Size::new(1., 1.); + *trigger.write() = Size::new(1., 1.); wnd.draw_frame(); - assert_eq!(*pos.borrow(), Point::new(50., 0.)); + assert_eq!(pos.get(), Point::new(50., 0.)); } } diff --git a/core/src/widget_tree/widget_id.rs b/core/src/widget_tree/widget_id.rs index a3997c05d..46acebe1f 100644 --- a/core/src/widget_tree/widget_id.rs +++ b/core/src/widget_tree/widget_id.rs @@ -5,6 +5,7 @@ use super::WidgetTree; use crate::{ builtin_widgets::Void, context::{PaintingCtx, WidgetCtx}, + prelude::{AnonymousData, DataWidget}, state::{ModifyScope, Notifier}, widget::{QueryOrder, Render}, window::DelayEvent, @@ -101,27 +102,39 @@ impl WidgetId { self.0.descendants(tree).map(WidgetId) } - pub(crate) fn on_mounted_subtree(self, tree: &WidgetTree) { + pub(crate) fn on_mounted_subtree(self, tree: &mut WidgetTree) { + // safety: just + let tree2 = unsafe { &mut *(tree as *mut _) }; self .descendants(&tree.arena) - .for_each(|w| w.on_mounted(tree)); + .for_each(|w| w.on_mounted(tree2)); } - pub(crate) fn on_mounted(self, tree: &WidgetTree) { + pub(crate) fn on_mounted(self, tree: &mut WidgetTree) { + let mut handles = vec![]; self.assert_get(&tree.arena).query_all_type( |notifier: &Notifier| { let state_changed = tree.dirty_set.clone(); - notifier + let h = notifier .raw_modifies() .filter(|b| b.contains(ModifyScope::FRAMEWORK)) .subscribe(move |_| { state_changed.borrow_mut().insert(self); - }); + }) + .unsubscribe_when_dropped(); + handles.push(h); true }, QueryOrder::OutsideFirst, ); + // will auto cancel subscription when node removed. + self.wrap_node(&mut tree.arena, move |node| { + Box::new(DataWidget::new( + node, + AnonymousData::new(Box::new(handles.into_boxed_slice())), + )) + }); tree.window().add_delay_event(DelayEvent::Mounted(self)); } diff --git a/core/src/window.rs b/core/src/window.rs index b031fb0d2..2a69fab5d 100644 --- a/core/src/window.rs +++ b/core/src/window.rs @@ -625,18 +625,15 @@ impl From for u64 { #[cfg(test)] mod tests { use super::*; - use crate::test_helper::*; + use crate::{reset_test_env, test_helper::*}; use ribir_dev_helper::assert_layout_result_by_path; #[test] fn layout_after_wnd_resize() { - let _guard = unsafe { AppCtx::new_lock_scope() }; + reset_test_env!(); - let w = widget! { - MockBox { size: INFINITY_SIZE } - }; let size = Size::new(100., 100.); - let mut wnd = TestWindow::new_with_size(w, size); + let mut wnd = TestWindow::new_with_size(MockBox { size: INFINITY_SIZE }, size); wnd.draw_frame(); assert_layout_result_by_path!(wnd, { path = [0], size == size, }); diff --git a/dev-helper/src/widget_test.rs b/dev-helper/src/widget_test.rs index c12eb665b..20c7fb689 100644 --- a/dev-helper/src/widget_test.rs +++ b/dev-helper/src/widget_test.rs @@ -91,10 +91,10 @@ macro_rules! widget_test_suit { /// use ribir_dev_helper::*; /// /// fn my_widget() -> Widget { -/// widget!{ -/// MockBox { +/// fn_widget!{ +/// @MockBox { /// size: Size::new(100., 100.), -/// MockBox { +/// @MockBox { /// size: Size::new(50., 50.) /// } /// } diff --git a/macros/src/child_template.rs b/macros/src/child_template.rs index ca89b5294..e9378aea2 100644 --- a/macros/src/child_template.rs +++ b/macros/src/child_template.rs @@ -56,12 +56,6 @@ pub(crate) fn derive_child_template(input: &mut syn::DeriveInput) -> syn::Result fn builder() -> Self::Builder { <_>::default() } } - impl #g_impl Declare for #name #g_ty #g_where { - type Builder = #builder #g_ty; - #[inline] - fn declare_builder() -> Self::Builder { #name::builder() } - } - impl #g_impl Declare2 for #name #g_ty #g_where { type Builder = #builder #g_ty; #[inline] diff --git a/macros/src/declare_derive.rs b/macros/src/declare_derive.rs deleted file mode 100644 index b3a72dfaf..000000000 --- a/macros/src/declare_derive.rs +++ /dev/null @@ -1,381 +0,0 @@ -use crate::util::data_struct_unwrap; -use proc_macro::{Diagnostic, Level}; -use proc_macro2::TokenStream; -use quote::{quote, quote_spanned, ToTokens}; -use syn::{ - parse::{discouraged::Speculative, Parse}, - parse_quote, - punctuated::Punctuated, - spanned::Spanned, - token, DataStruct, Fields, Ident, Result, -}; - -const DECLARE: &str = "Declare"; -pub const DECLARER: &str = "Declarer"; -const DECLARE_ATTR: &str = "declare"; - -struct DefaultMeta { - _default_kw: kw::default, - _eq_token: Option, - value: Option, -} - -enum ConvertValue { - Into(kw::into), - Custom(kw::custom), - Stipe(kw::strip_option), -} -struct ConvertMeta { - _convert_kw: kw::convert, - _eq_token: Option, - value: ConvertValue, -} - -#[derive(Default)] -struct DeclareAttr { - rename: Option, - builtin: Option, - default: Option, - convert: Option, - // field with `skip` attr, will not generate setter method and use default to init value. - skip: Option, -} - -struct DeclareField<'a> { - attr: Option, - field: &'a syn::Field, -} -mod kw { - use syn::custom_keyword; - custom_keyword!(rename); - custom_keyword!(builtin); - custom_keyword!(default); - custom_keyword!(convert); - custom_keyword!(into); - custom_keyword!(custom); - custom_keyword!(skip); - custom_keyword!(strict); - custom_keyword!(strip_option); -} - -#[inline] -pub fn declare_field_name(field_name: &Ident) -> Ident { - let name = if field_name.to_string().starts_with('_') { - format!("set_declare{field_name}",) - } else { - format!("set_declare_{field_name}",) - }; - Ident::new(&name, field_name.span()) -} - -impl Parse for DefaultMeta { - fn parse(input: syn::parse::ParseStream) -> Result { - Ok(Self { - _default_kw: input.parse()?, - _eq_token: input.parse()?, - value: { - let ahead = input.fork(); - let expr = ahead.parse::(); - if expr.is_ok() { - input.advance_to(&ahead); - } - expr.ok() - }, - }) - } -} - -impl Parse for ConvertMeta { - fn parse(input: syn::parse::ParseStream) -> Result { - Ok(Self { - _convert_kw: input.parse()?, - _eq_token: input.parse()?, - value: input.parse()?, - }) - } -} - -impl Parse for ConvertValue { - fn parse(input: syn::parse::ParseStream) -> Result { - let lk = input.lookahead1(); - if lk.peek(kw::into) { - input.parse().map(ConvertValue::Into) - } else if lk.peek(kw::custom) { - input.parse().map(ConvertValue::Custom) - } else if lk.peek(kw::strip_option) { - input.parse().map(ConvertValue::Stipe) - } else { - Err(lk.error()) - } - } -} - -impl Parse for DeclareAttr { - fn parse(input: syn::parse::ParseStream) -> Result { - let mut attr = DeclareAttr::default(); - while !input.is_empty() { - let lookahead = input.lookahead1(); - - // use input instead of lookahead to peek builtin, because need't complicate in - // compile error. - if input.peek(kw::builtin) { - attr.builtin = Some(input.parse()?); - } else if lookahead.peek(kw::rename) { - input.parse::()?; - input.parse::()?; - attr.rename = Some(input.parse()?); - } else if lookahead.peek(kw::convert) { - attr.convert = Some(input.parse()?); - } else if lookahead.peek(kw::default) { - attr.default = Some(input.parse()?); - } else if lookahead.peek(kw::skip) { - attr.skip = Some(input.parse()?); - } else if lookahead.peek(kw::strict) { - let _ = input.parse::(); - } else { - return Err(lookahead.error()); - } - if let (Some(rename), Some(builtin)) = (attr.rename.as_ref(), attr.builtin.as_ref()) { - let mut d = Diagnostic::new( - Level::Error, - "`rename` and `builtin` can not be used in same time.", - ); - d.set_spans(vec![rename.span().unwrap(), builtin.span().unwrap()]); - d.emit(); - } - if !input.is_empty() { - input.parse::()?; - } - } - Ok(attr) - } -} - -pub(crate) fn declare_derive(input: &mut syn::DeriveInput) -> syn::Result { - let syn::DeriveInput { vis, ident: name, generics, data, .. } = input; - let (g_impl, g_ty, g_where) = generics.split_for_impl(); - - let stt = data_struct_unwrap(data, DECLARE)?; - let mut builder_fields = collect_filed_and_attrs(stt)?; - - // reverse name check. - builder_fields - .iter_mut() - .for_each(DeclareField::check_reserve); - - let declarer = Ident::new(&format!("{name}{DECLARER}"), name.span()); - - let mut builder_methods = quote! {}; - let mut methods = quote! {}; - builder_fields - .iter() - .filter(|f| f.attr.as_ref().map_or(true, |attr| attr.skip.is_none())) - .for_each(|f| { - let syn::Field { vis: f_vis, ident, .. } = &f.field; - let field_name = ident.as_ref().unwrap(); - let set_method = f.set_method_name(); - let declare_set = declare_field_name(set_method); - if let Some((value_arg, expr)) = f.setter_value_arg_and_expr() { - builder_methods.extend(quote! { - #[inline] - #vis fn #set_method(mut self, #value_arg) -> Self { - self.#field_name = Some(#expr); - self - } - }); - methods.extend(quote! { - #[inline] - #f_vis fn #declare_set(&mut self, #value_arg) { - self.#field_name = #expr; - } - }); - } - }); - - // builder define - let def_fields = builder_fields.pairs().map(|p| { - let (f, c) = p.into_tuple(); - let mut f = f.field.clone(); - let ty = &f.ty; - f.ty = parse_quote!(Option<#ty>); - syn::punctuated::Pair::new(f, c) - }); - - // implement declare trait - let fields_ident = builder_fields.iter().map(|f| f.field.ident.as_ref()); - let builder_fields_ident = fields_ident.clone(); - - let fill_default = builder_fields.iter().filter_map(|f| { - let field_name = f.member(); - - f.attr.as_ref().and_then(|attr| { - let set_default_value = match (&attr.default, &attr.skip) { - (Some(df), None) if df.value.is_some() => { - let v = df.value.as_ref(); - let method = f.set_method_name(); - Some(quote! { self = self.#method(#v); }) - } - (Some(df), Some(_)) if df.value.is_some() => { - let v = df.value.as_ref(); - Some(quote! { self.#field_name = Some(#v); }) - } - (Some(_), _) | (_, Some(_)) => Some(quote! { self.#field_name = Some(<_>::default()) }), - (None, None) => None, - }; - set_default_value.map(|set_default_value| { - quote! { - if self.#field_name.is_none() { - #set_default_value - } - } - }) - }) - }); - - let builder_values = builder_fields.iter().map(|df| { - let field_name = df.field.ident.as_ref().unwrap(); - let method = df.set_method_name(); - quote_spanned! { field_name.span() => - self.#field_name.expect(&format!( - "Required field `{}::{}` not set, use method `{}` init it", - stringify!(#name), stringify!(#field_name), stringify!(#method) - )) - } - }); - - let tokens = quote! { - #vis struct #declarer #g_impl #g_where { - #(#def_fields)* - } - - impl #g_impl Declare for #name #g_ty #g_where { - type Builder = #declarer #g_ty; - - fn declare_builder() -> Self::Builder { - #declarer { #(#builder_fields_ident : None ),*} - } - } - - impl #g_impl #name #g_ty #g_where { - #methods - } - - impl #g_impl #declarer #g_ty #g_where { - #builder_methods - } - - impl #g_impl DeclareBuilder for #declarer #g_ty #g_where { - type Target = #name #g_ty; - fn build_declare(mut self, ctx: &BuildCtx) -> #name #g_ty { - #(#fill_default)* - #name { - #(#fields_ident : #builder_values),* } - } - } - }; - Ok(tokens) -} - -fn collect_filed_and_attrs(stt: &mut DataStruct) -> Result> { - let mut builder_fields = Punctuated::default(); - match &mut stt.fields { - Fields::Named(named) => { - named - .named - .pairs_mut() - .try_for_each::<_, syn::Result<()>>(|pair| { - let (field, comma) = pair.into_tuple(); - let idx = field - .attrs - .iter() - .position(|attr| attr.path.is_ident(DECLARE_ATTR)); - let builder_attr = if let Some(idx) = idx { - let attr = field.attrs.remove(idx); - let args: DeclareAttr = attr.parse_args()?; - Some(args) - } else { - None - }; - - builder_fields.push(DeclareField { attr: builder_attr, field }); - if let Some(c) = comma { - builder_fields.push_punct(*c); - } - - Ok(()) - })?; - } - Fields::Unit => <_>::default(), - Fields::Unnamed(unnamed) => { - let err = syn::Error::new( - unnamed.span(), - format!("`{DECLARE}` not be supported to derive for tuple struct"), - ); - return Err(err); - } - }; - Ok(builder_fields) -} - -impl<'a> DeclareField<'a> { - fn member(&self) -> &Ident { self.field.ident.as_ref().unwrap() } - - fn set_method_name(&self) -> &Ident { - self - .attr - .as_ref() - .and_then(|attr| attr.rename.as_ref()) - .or(self.field.ident.as_ref()) - .unwrap() - } - - fn setter_value_arg_and_expr(&self) -> Option<(TokenStream, TokenStream)> { - let ty = &self.field.ty; - let cv = self - .attr - .as_ref() - .and_then(|attr| attr.convert.as_ref()) - .map(|meta| &meta.value); - match cv { - Some(ConvertValue::Into(_)) => Some(( - quote! { v: impl std::convert::Into<#ty> }, - quote! { v.into() }, - )), - Some(ConvertValue::Stipe(_)) => Some(( - quote! { v: impl std::convert::Into> }, - quote! { v.into().into_option_value() }, - )), - // custom - Some(ConvertValue::Custom(_)) => None, - None => Some((quote! { v: #ty}, quote! {v})), - } - } - - fn check_reserve(&mut self) { - // reverse name check. - let reserve_ident = &crate::widget_macro::RESERVE_IDENT; - - let not_builtin = self - .attr - .as_ref() - .map_or(true, |attr| attr.builtin.is_none()); - - if not_builtin { - let method_name = self.set_method_name(); - if let Some(r) = reserve_ident.get(method_name.to_string().as_str()) { - let msg = format!("the identify `{}` is reserved to {}", method_name, &r); - let mut field = self.field.clone(); - // not display the attrs in the help code. - - field.attrs.clear(); - Diagnostic::spanned(vec![method_name.span().unwrap()], Level::Error, msg) - .help(format! { - "use `rename` meta to avoid the name conflict in `widget!` macro.\n\n\ - #[declare(rename = xxx)] \n\ - {}", field.into_token_stream() - }) - .emit(); - } - } - } -} diff --git a/macros/src/declare_derive2.rs b/macros/src/declare_derive2.rs index 2fb962fe0..b27f67edb 100644 --- a/macros/src/declare_derive2.rs +++ b/macros/src/declare_derive2.rs @@ -43,8 +43,6 @@ mod kw { custom_keyword!(custom); custom_keyword!(skip); custom_keyword!(strict); - // todo: tmp code, only for compatibility. - custom_keyword!(convert); } impl Parse for DefaultMeta { @@ -86,10 +84,6 @@ impl Parse for DeclareAttr { attr.skip = Some(input.parse()?); } else if lookahead.peek(kw::strict) { attr.strict = Some(input.parse()?); - } else if lookahead.peek(kw::convert) { - input.parse::()?; - input.parse::()?; - input.parse::()?; } else { return Err(lookahead.error()); } @@ -232,7 +226,7 @@ fn struct_with_fields_gen( let unzip_fields = builder_fields.iter().map(|df| { let field_name = df.field.ident.as_ref().unwrap(); - let err = format!("Required field `{name}::{field_name}` not init"); + let err = format!("Required field `{name}::{field_name}` not set"); quote_spanned! { field_name.span() => let #field_name = self.#field_name.expect(#err).unzip(); } @@ -367,7 +361,7 @@ impl<'a> DeclareField<'a> { fn check_reserve(&mut self) { // reverse name check. - let reserve_ident = &crate::widget_macro::RESERVE_IDENT; + let reserve_ident = &crate::variable_names::RESERVE_IDENT; let not_builtin = self .attr diff --git a/macros/src/declare_obj.rs b/macros/src/declare_obj.rs index e02c38b62..e5cc25175 100644 --- a/macros/src/declare_obj.rs +++ b/macros/src/declare_obj.rs @@ -1,6 +1,6 @@ use crate::{ rdl_macro::{DeclareField, RdlParent, StructLiteral}, - widget_macro::{WIDGETS, WIDGET_OF_BUILTIN_FIELD}, + variable_names::{WIDGETS, WIDGET_OF_BUILTIN_FIELD}, }; use inflector::Inflector; use proc_macro2::{Span, TokenStream}; @@ -28,7 +28,8 @@ enum ObjNode<'a> { impl<'a> DeclareObj<'a> { pub fn from_literal(mac: &'a StructLiteral) -> Result { - let StructLiteral { parent, brace, fields, children } = mac; + let StructLiteral { span, parent, fields, children } = mac; + let span = *span; let mut builtin: Vec<(&'static str, SmallVec<[&'a DeclareField; 1]>)> = vec![]; let mut self_fields = SmallVec::default(); @@ -58,7 +59,6 @@ impl<'a> DeclareObj<'a> { match parent { RdlParent::Type(ty) => { - let span = ty.span().join(brace.span).unwrap(); if WIDGETS.iter().any(|w| ty.is_ident(w.ty)) { invalid_member_err( &self_fields, @@ -76,7 +76,6 @@ impl<'a> DeclareObj<'a> { "only allow to declare builtin fields in a variable parent.", )?; let this = Some(ObjNode::Var(name)); - let span = name.span().join(brace.span).unwrap(); Ok(Self { this, span, builtin, children }) } } diff --git a/macros/src/error.rs b/macros/src/error.rs deleted file mode 100644 index dfa015ca7..000000000 --- a/macros/src/error.rs +++ /dev/null @@ -1,156 +0,0 @@ -use crate::widget_macro::NameUsedInfo; -use proc_macro::{Diagnostic, Level, Span}; -use proc_macro2::TokenStream; - -use quote::ToTokens; -use syn::Ident; - -#[derive(Debug)] -pub struct CircleUsedPath { - pub obj: Ident, - pub member: Option, - pub used_widget: Ident, - pub used_info: NameUsedInfo, -} - -#[derive(Debug)] -pub enum DeclareError { - DuplicateID([Ident; 2]), - CircleDepends(Box<[CircleUsedPath]>), - WatchNothing(Span), - PropInvalidTarget(proc_macro2::Span), - TransitionByConflict(Span), - LetWatchWrongPlace(Span), - SynErr(syn::Error), -} - -#[derive(Debug)] -pub enum DeclareWarning { - UnusedName(Span), - DefObjWithoutId(Span), -} - -impl DeclareError { - pub fn to_compile_error(&self, tokens: &mut TokenStream) { - let mut diagnostic = Diagnostic::new(Level::Error, ""); - match self { - DeclareError::DuplicateID([id1, id2]) => { - assert_eq!(id1, id2); - diagnostic.set_spans(vec![id1.span().unwrap(), id2.span().unwrap()]); - diagnostic.set_message(format!( - "Same `id: {id1}` assign to multiple objects, id must be unique.", - )); - } - DeclareError::CircleDepends(path) => { - let (msg, spans, note_spans) = path_info(path); - let msg = format!( - "There is a directly circle depends exist, this will cause infinite loop: {msg}", - ); - diagnostic.set_spans(spans); - diagnostic.set_message(msg); - let note_msg = "You should manual watch expression and add operator \ - to break the circular, then debounce subscribe it avoid to mut borrow panic. \ - For example \n -``` - let_watch!(...) - .distinct_until_changed() - .debounce(Duration::ZERO, ctx.wnd_ctx().frame_scheduler()) - .subscribe(...) -```"; - diagnostic = diagnostic.span_note(note_spans, note_msg); - } - DeclareError::TransitionByConflict(span) => { - diagnostic.set_spans(*span); - diagnostic.set_message("field conflict with `by`, To config transition property."); - - diagnostic = diagnostic.span_help( - *span, - "When you use `by` field provide a whole `Transition`\ - obj, you can not config other field of `Transition`", - ); - } - DeclareError::WatchNothing(span) => { - diagnostic.set_spans(*span); - diagnostic.set_message("try to watch a expression without any stateful target."); - } - DeclareError::PropInvalidTarget(span) => { - *tokens = syn::Error::new(*span, "is not a stateful target.").into_compile_error(); - } - DeclareError::LetWatchWrongPlace(span) => { - diagnostic.set_spans(*span); - diagnostic.set_message( - "`let_watch` only allow start as a statement to help auto\ - unsubscribe a subscribed stream when the root of `widget!` dropped.", - ); - } - DeclareError::SynErr(err) => err.clone().into_compile_error().to_tokens(tokens), - }; - - diagnostic.emit(); - } -} - -// return a tuple compose by the string display of path, the path follow spans -// and the spans of where `#[skip_nc]` can be added. -fn path_info(path: &[CircleUsedPath]) -> (String, Vec, Vec) { - let msg = path - .iter() - .map(|info| { - if let Some(m) = info.member.as_ref() { - format!("{}.{} ~> {} ", info.obj, m, info.used_widget) - } else { - format!("{} ~> {} ", info.obj, info.used_widget) - } - }) - .collect::>() - .join(", "); - - let spans = path.iter().fold(vec![], |mut res, info| { - res.push(info.obj.span().unwrap()); - if let Some(m) = info.member.as_ref() { - res.push(m.span().unwrap()); - } - - res.push(info.used_widget.span().unwrap()); - let t_spans = info.used_info.spans.iter().map(|s| s.unwrap()); - res.extend(t_spans); - res - }); - - let note_spans = path - .iter() - .map(|info| { - if let Some(m) = info.member.as_ref() { - m.span().unwrap() - } else { - info - .used_info - .spans - .iter() - .fold(info.obj.span(), |s1, s2| s2.join(s1).unwrap()) - .unwrap() - } - }) - .collect::>(); - - (msg, spans, note_spans) -} - -impl DeclareWarning { - pub fn emit_warning(&self) { - let mut d = Diagnostic::new(Level::Warning, ""); - match self { - DeclareWarning::UnusedName(span) => { - d.set_spans(*span); - d.set_message("assigned id but not be used in anywhere."); - d = d.span_help(*span, "Remove this line."); - } - DeclareWarning::DefObjWithoutId(span) => { - d.set_spans(*span); - d.set_message("Define an object without id."); - d = d.help("Try to assign an `id` for it."); - } - }; - d.emit(); - } -} diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 615b9160a..1aae55b31 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,22 +1,19 @@ #![feature(proc_macro_diagnostic, proc_macro_span)] extern crate proc_macro; -mod declare_derive; mod declare_derive2; -mod error; mod lerp_derive; mod util; -mod widget_macro; use fn_widget_macro::FnWidgetMacro; use proc_macro::TokenStream; use quote::quote; use symbol_process::DollarRefsCtx; use syn::{parse_macro_input, DeriveInput}; -use widget_macro::gen_widget_macro; mod child_template; mod fn_widget_macro; mod pipe_macro; mod rdl_macro; +pub(crate) mod variable_names; mod watch_macro; mod writer_map_macro; pub(crate) use rdl_macro::*; @@ -26,14 +23,6 @@ use crate::watch_macro::WatchMacro; pub(crate) mod declare_obj; pub(crate) mod symbol_process; -pub(crate) const WIDGET_MACRO_NAME: &str = "widget"; -pub(crate) const MOVE_TO_WIDGET_MACRO_NAME: &str = "move_to_widget"; -pub(crate) const WATCH_MACRO_NAME: &str = "watch"; -pub(crate) const NO_WATCH_MACRO_NAME: &str = "no_watch"; -pub(crate) const ASSIGN_WATCH_MACRO_NAME: &str = "assign_watch"; -pub(crate) const LET_WATCH_MACRO_NAME: &str = "let_watch"; -pub(crate) const PROP_MACRO_NAME: &str = "prop"; - macro_rules! ok { ($e: expr) => { match $e { @@ -84,20 +73,8 @@ pub fn lerp_derive(input: TokenStream) -> TokenStream { /// - implement `DeclareBuilder` for `XXXBuilder` which build `XXX` and used by /// `declare!` to build the `XXX` widget. /// - for every field of `XXXBuilder` -/// - implement an associate method `into_xxx` use to convert a value to the -/// `xxx` field type, which effect by the `convert` meta. `widget!` will use -/// it to convert the field value /// - implement method with same name of the field and use to init the field. -/// /// [declare]: ../ribir/declare/index.html -#[proc_macro_derive(Declare, attributes(declare))] -pub fn declare_trait_macro_derive(input: TokenStream) -> TokenStream { - let mut input = parse_macro_input!(input as DeriveInput); - declare_derive::declare_derive(&mut input) - .unwrap_or_else(|e| e.into_compile_error()) - .into() -} - #[proc_macro_derive(Declare2, attributes(declare))] pub fn declare_trait_macro_derive2(input: TokenStream) -> TokenStream { let mut input = parse_macro_input!(input as DeriveInput); @@ -114,9 +91,6 @@ pub fn child_template_trait_derive(input: TokenStream) -> TokenStream { .into() } -#[proc_macro] -pub fn widget(input: TokenStream) -> TokenStream { gen_widget_macro(input, None) } - /// The macro use to declare a object, this macro will use `ctx!()` to access /// the `BuildCtx`, so it can only use in the `fn_widget!` macro, or any scope /// that called `set_build_ctx!` macro. diff --git a/macros/src/rdl_macro.rs b/macros/src/rdl_macro.rs index fade30cef..e88cebf8e 100644 --- a/macros/src/rdl_macro.rs +++ b/macros/src/rdl_macro.rs @@ -14,7 +14,7 @@ use syn::{ parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, - token::{At, Bang, Brace, Colon, Comma, Dollar}, + token::{Bang, Brace, Colon, Comma, Dollar}, Expr, Ident, Macro, Path, Result as SynResult, Stmt, }; @@ -30,14 +30,18 @@ pub enum RdlMacro { /// Declare a object use struct literal, like `rdl! { Row { ... } }` or /// `@parent { ... }` pub struct StructLiteral { + pub span: Span, pub parent: RdlParent, - pub brace: Brace, pub fields: Punctuated, /// Declare a child in `rdl!` can use `rdl!` macro or `@` symbol. /// `rdl! { Row { rdl! { SizedBox {...} } } }` /// or /// `rdl! { Row { @ SizedBox{ ... } } }` - /// but will be all processed as `rdl! { ... }` + /// and the second case will be instead by + /// ```ignore + /// rdl! { Row { rdl! { SizedBox {...} } } } + /// ``` + /// in preprocessor. pub children: Vec, } @@ -100,9 +104,10 @@ impl Parse for RdlMacro { impl Parse for StructLiteral { fn parse(input: ParseStream) -> SynResult { + let span = input.span(); let parent = input.parse()?; let content; - let brace = braced!(content in input); + let _ = braced!(content in input); let mut children = vec![]; let mut fields = Punctuated::default(); loop { @@ -110,7 +115,7 @@ impl Parse for StructLiteral { break; } - if content.peek(At) || content.peek(kw::rdl) && content.peek2(Bang) { + if content.peek(kw::rdl) && content.peek2(Bang) { children.push(content.parse()?); } else if content.peek(Ident) { let f: DeclareField = content.parse()?; @@ -131,7 +136,7 @@ impl Parse for StructLiteral { } check_duplicate_field(&fields)?; - Ok(StructLiteral { parent, brace, fields, children }) + Ok(StructLiteral { span, parent, fields, children }) } } diff --git a/macros/src/symbol_process.rs b/macros/src/symbol_process.rs index 0dc82e890..9da29ce29 100644 --- a/macros/src/symbol_process.rs +++ b/macros/src/symbol_process.rs @@ -3,8 +3,8 @@ use crate::pipe_macro::PipeMacro; use crate::rdl_macro::RdlMacro; use crate::writer_map_macro::{gen_map_path_writer, gen_split_path_writer}; use crate::{ + variable_names::{ribir_suffix_variable, WIDGET_OF_BUILTIN_FIELD, WIDGET_OF_BUILTIN_METHOD}, watch_macro::WatchMacro, - widget_macro::{ribir_suffix_variable, WIDGET_OF_BUILTIN_FIELD, WIDGET_OF_BUILTIN_METHOD}, }; use inflector::Inflector; use proc_macro2::{Ident, Span, TokenStream}; diff --git a/macros/src/widget_macro/variable_names.rs b/macros/src/variable_names.rs similarity index 55% rename from macros/src/widget_macro/variable_names.rs rename to macros/src/variable_names.rs index af038f978..687be3e24 100644 --- a/macros/src/widget_macro/variable_names.rs +++ b/macros/src/variable_names.rs @@ -1,11 +1,10 @@ use ::ribir_builtin::builtin; use inflector::Inflector; use lazy_static::lazy_static; -use proc_macro2::Span; use std::collections::HashMap; use syn::Ident; -include!("../builtin_fields_list.rs"); +include!("./builtin_fields_list.rs"); lazy_static! { pub static ref RESERVE_IDENT: HashMap<&'static str, &'static str, ahash::RandomState> = WIDGETS @@ -31,15 +30,6 @@ lazy_static! { pub(crate) const AVOID_CONFLICT_SUFFIX: &str = "ಠ_ಠ"; -pub fn child_variable(name: &Ident, idx: usize) -> Ident { - ribir_suffix_variable(name, &format!("c_{idx}")) -} - -pub fn ribir_variable(name: &str, span: Span) -> Ident { - let name = format!("{name}_{AVOID_CONFLICT_SUFFIX}"); - Ident::new(&name, span) -} - pub fn ribir_suffix_variable(from: &Ident, suffix: &str) -> Ident { let name_str = from.to_string(); let prefix_size = if name_str.ends_with(AVOID_CONFLICT_SUFFIX) { @@ -51,28 +41,3 @@ pub fn ribir_suffix_variable(from: &Ident, suffix: &str) -> Ident { let name = format!("{prefix}_{suffix}_{AVOID_CONFLICT_SUFFIX}"); Ident::new(&name, from.span()) } - -pub fn ctx_ident() -> Ident { ribir_variable("ctx", Span::call_site()) } - -pub fn builtin_var_name(host: &Ident, span: Span, ty: &str) -> Ident { - let suffix = BUILTIN_WIDGET_SUFFIX - .get(ty) - .expect("The suffix of {ty} not found, should use a builtin type to query suffix."); - - let mut name = ribir_suffix_variable(host, suffix); - name.set_span(span); - name -} - -pub fn guard_vec_ident() -> Ident { ribir_variable("guard_vec", Span::call_site()) } -pub fn guard_ident(span: Span) -> Ident { ribir_variable("guard", span) } - -// We only check `DynWidget` and `DynWidget<_>`. -pub fn is_dyn_widget(path: &syn::Path) -> bool { - if path.leading_colon.is_some() || path.segments.len() != 1 { - return false; - } - - let seg = &path.segments[0]; - seg.ident == "DynWidget" -} diff --git a/macros/src/widget_macro.rs b/macros/src/widget_macro.rs deleted file mode 100644 index 9f8e22662..000000000 --- a/macros/src/widget_macro.rs +++ /dev/null @@ -1,188 +0,0 @@ -use ahash::HashSet; -use proc_macro2::TokenStream; -use quote::{quote, quote_spanned, ToTokens}; -use syn::{parse_macro_input, token::Brace, Expr, Ident}; - -mod code_gen; -mod desugar; -mod parser; -pub use desugar::Desugared; -pub use parser::{MacroSyntax, StateField}; -mod visit_mut; -pub use visit_mut::*; -mod name_used_info; -pub use name_used_info::*; -mod variable_names; -pub use variable_names::*; - -use self::desugar::{builtin_obj, NamedObj}; -use crate::error::DeclareError; - -fn capture_widget(widget: &Ident) -> TokenStream { - quote_spanned!(widget.span() => let #widget = #widget.clone_stateful();) -} - -#[derive(Debug, Clone)] -pub struct TrackExpr { - pub expr: Expr, - pub used_name_info: ScopeUsedInfo, -} - -pub fn gen_widget_macro( - input: proc_macro::TokenStream, - outside_ctx: Option<&mut VisitCtx>, -) -> proc_macro::TokenStream { - let macro_syntax = parse_macro_input! { input as MacroSyntax }; - let mut desugar = macro_syntax.desugar(); - - let states: HashSet<_> = desugar - .states - .iter() - .flat_map(|t| t.states.iter().map(|sf| sf.member.clone())) - .collect(); - - let mut ctx = VisitCtx { states, ..<_>::default() }; - let mut ctx = ctx.stack_push(); - - use DeclareError::DuplicateID; - let errors = &mut desugar.errors; - if let Some(ref outside_ctx) = outside_ctx { - ctx.declare_objs.extend(outside_ctx.declare_objs.clone()); - - if !outside_ctx.states.is_empty() { - if !ctx.states.is_empty() { - ctx.states.iter().for_each(|name| { - if let Some(other) = outside_ctx.states.get(name) { - errors.push(DuplicateID([name.clone(), other.clone()])) - } - }) - } - ctx.states.extend(outside_ctx.states.clone()); - } - - ctx.analyze_stack = outside_ctx.analyze_stack.clone(); - // maybe some variable has the same name as variables in `states`, we push - // states in variable stack to avoid this situation. - let local_vars = ctx - .states - .iter() - .map(|name| LocalVariable::new(name.clone(), name.clone())) - .collect(); - ctx.analyze_stack.push(local_vars); - } - - if let Some(init) = desugar.init.as_mut() { - ctx.visit_init_stmts_mut(init); - if let Some(ctx_name) = init.ctx_name.clone() { - if let Some(other) = ctx.states.get(&ctx_name) { - errors.push(DuplicateID([ctx_name.clone(), other.clone()])) - } - } - } - - if !desugar.named_objs.is_empty() { - desugar - .named_objs - .objs() - .map(|obj| (obj.name().clone(), obj.ty().clone())) - .for_each(|(name, path)| { - if let Some((other, _)) = ctx.declare_objs.get_key_value(&name) { - errors.push(DuplicateID([name.clone(), other.clone()])) - } - if let Some(other) = ctx.states.get(&name) { - errors.push(DuplicateID([name.clone(), other.clone()])) - } - ctx.declare_objs.insert(name, path); - }); - }; - - if !ctx.declare_objs.is_empty() && !ctx.states.is_empty() { - ctx.states.iter().for_each(|name| { - if let Some((other, _)) = ctx.declare_objs.get_key_value(name) { - errors.push(DuplicateID([name.clone(), other.clone()])) - } - }) - } - - ctx.visit_desugared_syntax_mut(&mut desugar); - - ctx - .used_objs - .iter() - .for_each(|(name, UsedInfo { builtin, .. })| { - // add default builtin widget, which used by others but but declared. - if let Some(builtin) = builtin { - if !desugar.named_objs.contains(name) && desugar.named_objs.contains(&builtin.src_name) { - let BuiltinUsed { src_name, builtin_ty } = builtin; - let obj = builtin_obj(src_name, builtin_ty, <_>::default()); - desugar.add_named_builtin_obj(src_name.clone(), obj); - } - } - - if let Some(obj) = desugar.named_objs.get_mut(name) { - // named obj used by other should force be stateful - match obj { - NamedObj::Host(obj) | NamedObj::Builtin { obj, .. } => obj.stateful = true, - } - } - }); - - desugar.collect_warnings(&ctx); - let mut tokens = quote! {}; - desugar.circle_detect(); - Brace::default().surround(&mut tokens, |tokens| { - Brace::default().surround(tokens, |tokens| { - if outside_ctx.is_none() { - quote! { - #![allow( - unused_mut, - clippy::redundant_clone, - clippy::clone_on_copy, - clippy::let_and_return - )] - } - .to_tokens(tokens); - } - desugar.gen_tokens(tokens, &ctx); - }); - }); - - if let Some(outside_ctx) = outside_ctx { - outside_ctx.visit_error_occur |= ctx.visit_error_occur || !desugar.errors.is_empty(); - let used_outsides = ctx - .used_objs - .iter() - .filter(|(name, _)| { - !desugar.named_objs.contains(name) - && desugar - .states - .as_ref() - .map_or(true, |track| !track.track_names().any(|n| &n == name)) - }) - .collect::>(); - if !used_outsides.is_empty() { - let captures = used_outsides.iter().map(|(name, _)| capture_widget(name)); - tokens = quote! {{ - #(#captures)* - #tokens - }}; - } - used_outsides.into_iter().for_each(|(name, used_info)| { - outside_ctx.add_used_widget( - name.clone(), - used_info.builtin.clone(), - UsedType::SCOPE_CAPTURE, - ) - }); - } - - tokens.into() -} - -impl TrackExpr { - pub fn new(expr: Expr) -> Self { Self { expr, used_name_info: <_>::default() } } -} - -impl ToTokens for TrackExpr { - fn to_tokens(&self, tokens: &mut TokenStream) { self.expr.to_tokens(tokens) } -} diff --git a/macros/src/widget_macro/code_gen.rs b/macros/src/widget_macro/code_gen.rs deleted file mode 100644 index 7ab0225f4..000000000 --- a/macros/src/widget_macro/code_gen.rs +++ /dev/null @@ -1,652 +0,0 @@ -use crate::{ - error::{DeclareError, DeclareWarning}, - widget_macro::WIDGET_OF_BUILTIN_FIELD, -}; -use ahash::RandomState; -use proc_macro2::TokenStream; -use quote::{quote, quote_spanned, ToTokens, TokenStreamExt}; -use smallvec::{smallvec, SmallVec}; -use std::collections::{BTreeMap, HashMap, HashSet}; -use syn::{ - parse_macro_input, parse_quote, - spanned::Spanned, - token::{Brace, Comma, Dot, Paren, Semi}, - visit_mut::VisitMut, - Expr, Ident, -}; - -use super::{ - builtin_var_name, closure_surround_refs, ctx_ident, - desugar::{ - ComposeItem, DeclareObj, Field, FieldValue, FinallyBlock, FinallyStmt, InitStmts, NamedObj, - NamedObjMap, WidgetNode, - }, - guard_vec_ident, is_dyn_widget, - parser::{PropMacro, Property, StateField, States}, - Desugared, ObjectUsed, ObjectUsedPath, TrackExpr, UsedPart, UsedType, VisitCtx, WIDGETS, -}; - -fn visit_watch_expr_as_observable(watch_expr: &mut TrackExpr, ctx: &mut VisitCtx) -> TokenStream { - ctx.new_scope_visit( - |ctx| ctx.visit_track_expr_mut(watch_expr), - |scope| { - scope.iter_mut().for_each(|(_, info)| { - // watch will manually subscribe the expression. - info.used_type.remove(UsedType::SUBSCRIBE); - info.used_type |= UsedType::SCOPE_CAPTURE - }); - }, - ); - - if let Some(upstream) = watch_expr.used_name_info.upstream_modifies_tokens(false) { - let map_closure = closure_surround_refs( - &watch_expr.used_name_info, - &mut parse_quote!( move |_| #watch_expr), - ) - .unwrap(); - - quote_spanned! { watch_expr.span() => #upstream.map(#map_closure) } - } else { - let mut tokens = quote! {}; - DeclareError::WatchNothing(watch_expr.span().unwrap()).to_compile_error(&mut tokens); - ctx.visit_error_occur = true; - tokens - } -} - -pub(crate) fn gen_watch_macro(input: TokenStream, ctx: &mut VisitCtx) -> proc_macro::TokenStream { - let input = input.into(); - let mut watch_expr = TrackExpr::new(parse_macro_input! { input as Expr }); - visit_watch_expr_as_observable(&mut watch_expr, ctx).into() -} - -pub(crate) fn gen_no_watch_macro( - input: TokenStream, - ctx: &mut VisitCtx, -) -> proc_macro::TokenStream { - let input = input.into(); - let mut watch_expr = TrackExpr::new(parse_macro_input! { input as Expr }); - ctx.new_scope_visit( - |ctx| ctx.visit_track_expr_mut(&mut watch_expr), - |scope| { - scope.iter_mut().for_each(|(_, info)| { - info.used_type.remove(UsedType::SUBSCRIBE); - }); - }, - ); - watch_expr.into_token_stream().into() -} - -pub(crate) fn gen_assign_watch(input: TokenStream, ctx: &mut VisitCtx) -> proc_macro::TokenStream { - let input = input.into(); - let mut watch_expr = TrackExpr::new(parse_macro_input! { input as Expr }); - let tokens = visit_watch_expr_as_observable(&mut watch_expr, ctx); - let mut value = quote! {}; - Brace(watch_expr.span()).surround(&mut value, |tokens| { - watch_expr.used_name_info.state_refs_tokens(tokens); - watch_expr.to_tokens(tokens); - }); - quote_spanned!(watch_expr.span() => Pipe::new(#value, #tokens.box_it())).into() -} - -pub(crate) fn gen_prop_macro( - input: proc_macro::TokenStream, - ctx: &mut VisitCtx, -) -> proc_macro::TokenStream { - let mut prop_macro = parse_macro_input! { input as PropMacro }; - let PropMacro { prop, lerp_fn, .. } = &mut prop_macro; - - let name = match &prop { - Property::Name(name) => name, - Property::Member { target, .. } => target, - }; - - if ctx.find_named_obj(name).is_none() { - let mut tokens = quote!(); - DeclareError::PropInvalidTarget(name.span()).to_compile_error(&mut tokens); - return tokens.into(); - }; - - ctx.new_scope_visit( - |ctx| match prop { - Property::Name(name) => { - if let Some(name) = ctx.find_named_obj(name).cloned() { - ctx.add_used_widget(name, None, UsedType::USED) - } - } - Property::Member { target, member, .. } => { - if let Some(builtin_ty) = WIDGET_OF_BUILTIN_FIELD.get(member.to_string().as_str()) { - let span = target.span().join(member.span()).unwrap(); - if let Some(name) = ctx.visit_builtin_name_mut(target, span, builtin_ty) { - *target = name; - } - } else if let Some(name) = ctx.find_named_obj(target).cloned() { - ctx.add_used_widget(name, None, UsedType::USED) - } - } - }, - |scope| { - scope - .iter_mut() - .for_each(|(_, info)| info.used_type.remove(UsedType::SUBSCRIBE)) - }, - ); - if let Some(lerp_fn) = lerp_fn { - ctx.visit_expr_as_value_mut(lerp_fn); - } - - prop_macro.to_token_stream().into() -} - -pub(crate) fn gen_move_to_widget_macro(input: &TokenStream, ctx: &mut VisitCtx) -> TokenStream { - let mut expr: Expr = parse_quote!(#input); - ctx.visit_expr_mut(&mut expr); - ctx.has_guards_data = true; - let guards = guard_vec_ident(); - quote_spanned!(expr.span() => #guards.push(AnonymousData::new(Box::new(#expr)))) -} - -impl Desugared { - pub fn gen_tokens(&self, tokens: &mut TokenStream, ctx: &VisitCtx) { - let Self { - init, - named_objs, - widget, - states, - finally, - errors, - warnings, - .. - } = &self; - - if !errors.is_empty() { - Brace::default().surround(tokens, |tokens| { - errors.iter().for_each(|err| err.to_compile_error(tokens)); - quote! { Void.into() }.to_tokens(tokens); - }); - - return; - } - if !ctx.visit_error_occur { - warnings.iter().for_each(|w| w.emit_warning()); - } - - let sorted_named_objs = self.order_named_objs(); - - states.to_tokens(tokens); - let name = widget.as_ref().unwrap().node.name(); - let ctx_name = ctx_ident(); - quote! { let #name = move |#ctx_name: &BuildCtx| }.to_tokens(tokens); - - Brace::default().surround(tokens, |tokens| { - if ctx.has_guards_data { - let guards_vec = guard_vec_ident(); - quote! { let mut #guards_vec: Vec = vec![]; }.to_tokens(tokens); - } - init.to_tokens(tokens); - - // deep first declare named obj by their dependencies - // circular may exist widget attr follow widget self to init. - sorted_named_objs.iter().for_each(|name| { - if let Some(obj) = named_objs.get(name) { - obj.to_tokens(tokens) - } - }); - - let w = widget.as_ref().unwrap(); - w.gen_node_objs(tokens); - finally.to_tokens(tokens); - - if ctx.has_guards_data { - quote! { DataWidget::attach }.to_tokens(tokens); - Paren::default().surround(tokens, |tokens| { - w.gen_compose_node(named_objs, tokens); - let guards_vec = guard_vec_ident(); - quote! { .into(), #guards_vec }.to_tokens(tokens) - }); - } else { - w.gen_compose_node(named_objs, tokens); - } - }); - quote! { ; FnWidget::new(#name) }.to_tokens(tokens); - } - - pub fn collect_warnings(&mut self, ctx: &VisitCtx) { self.collect_unused_declare_obj(ctx); } - - pub fn circle_detect(&mut self) { - fn used_part_iter(obj: &DeclareObj) -> impl Iterator + '_ { - obj.fields.iter().flat_map(|f| match &f.value { - FieldValue::Expr(e) | FieldValue::Observable(e) => { - e.used_name_info.used_part(Some(&f.member)) - } - // embed object must be an anonymous object never construct a circle. - FieldValue::Obj(_) => None, - }) - } - - let mut depends = BTreeMap::default(); - let Self { named_objs, errors, .. } = self; - named_objs.iter().for_each(|(name, obj)| { - let obj_used: ObjectUsed = match obj { - NamedObj::Host(obj) | NamedObj::Builtin { obj, .. } => used_part_iter(obj).collect(), - }; - if !obj_used.is_empty() { - depends.insert(name, obj_used); - } - }); - - #[derive(PartialEq, Eq, Debug, Clone, Copy)] - enum CheckState { - Checking, - Checked, - } - - let mut circles = vec![]; - let mut check_info: HashMap<_, _, RandomState> = HashMap::default(); - let mut edges: Vec = vec![]; - depends.keys().for_each(|name| { - loop { - let node = edges.last().map_or(*name, |e| e.used_obj); - let check_state = check_info.get(node).copied(); - if check_state.is_none() { - let edge_size = edges.len(); - if let Some(depends) = depends.get(node) { - edges.extend(depends.used_full_path_iter(node)); - } - if edges.len() > edge_size { - check_info.insert(node, CheckState::Checking); - } else { - check_info.insert(node, CheckState::Checked); - } - continue; - } - - if let Some(CheckState::Checking) = check_state { - let mut circle = vec![edges.last().cloned().unwrap()]; - for edge in edges.iter().rev() { - let circle_last = circle.last().unwrap(); - if circle_last.obj != edge.obj { - if edge.used_obj == circle_last.obj { - circle.push(edge.clone()); - } else { - break; - } - } - } - - circles.push(circle); - } - - let edge = edges.pop(); - let next = edges.last(); - match (edge, next) { - (e, None) => { - check_info.insert(node, CheckState::Checked); - if let Some(e) = e { - check_info.insert(e.obj, CheckState::Checked); - } - break; - } - (None, Some(_)) => unreachable!(), - (Some(e), Some(n)) => { - if e.obj != n.obj { - check_info.insert(e.obj, CheckState::Checked); - } - } - } - } - }); - - circles.iter().for_each(|path| { - let circle = - DeclareError::CircleDepends(path.iter().map(|c| c.to_used_path(named_objs)).collect()); - errors.push(circle); - }); - } - - fn order_named_objs(&self) -> Vec { - let mut orders = vec![]; - let mut visit_state: HashSet<_, RandomState> = HashSet::default(); - self - .named_objs - .names() - .for_each(|name| self.obj_deep_first(name, &mut visit_state, &mut orders)); - orders - } - - fn obj_deep_first<'a>( - &'a self, - name: &'a Ident, - visit_state: &mut HashSet<&'a Ident, RandomState>, - orders: &mut Vec, - ) { - if !visit_state.contains(name) { - visit_state.insert(name); - if let Some(obj) = self.named_objs.get(name) { - match obj { - NamedObj::Host(obj) | NamedObj::Builtin { obj, .. } => obj - .fields - .iter() - .for_each(|f| self.deep_in_field(f, visit_state, orders)), - } - - orders.push(name.clone()); - } - } - } - - fn deep_in_field<'a>( - &'a self, - f: &'a Field, - visit_state: &mut HashSet<&'a Ident, RandomState>, - orders: &mut Vec, - ) { - match &f.value { - FieldValue::Expr(e) | FieldValue::Observable(e) => { - if let Some(all) = e.used_name_info.all_used() { - all.for_each(|name| self.obj_deep_first(name, visit_state, orders)) - } - } - FieldValue::Obj(obj) => obj - .fields - .iter() - .for_each(|f| self.deep_in_field(f, visit_state, orders)), - } - } - - fn collect_unused_declare_obj(&mut self, ctx: &VisitCtx) { - let used_ids = ctx - .used_objs - .iter() - .map(|(name, info)| { - if let Some(builtin) = info.builtin.as_ref() { - &builtin.src_name - } else { - name - } - }) - .collect::>(); - self - .named_objs - .iter() - .filter(|(name, obj)| { - // Needn't check builtin named widget, shared id with host in user side. - matches!(obj, NamedObj::Host(_)) - && !used_ids.contains(name) - && !name.to_string().starts_with('_') - }) - .for_each(|(name, _)| { - self - .warnings - .push(DeclareWarning::UnusedName(name.span().unwrap())); - }); - } -} - -impl ToTokens for FieldValue { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - FieldValue::Expr(e) | FieldValue::Observable(e) => e.to_tokens(tokens), - FieldValue::Obj(obj) => Brace(obj.span()).surround(tokens, |tokens| { - obj.to_tokens(tokens); - obj.name.to_tokens(tokens) - }), - } - } -} - -impl ToTokens for DeclareObj { - fn to_tokens(&self, tokens: &mut TokenStream) { - let DeclareObj { ty, name, watch_stmts, .. } = self; - let span = ty.span(); - if watch_stmts.is_empty() { - self.build_tokens(tokens); - } else { - quote_spanned! { span => let #name = }.to_tokens(tokens); - Brace(ty.span()).surround(tokens, |tokens| { - watch_stmts.iter().for_each(|f| { - f.pre_def.to_tokens(tokens); - }); - self.build_tokens(tokens); - watch_stmts - .iter() - .for_each(|f| f.watch_update.to_tokens(tokens)); - name.to_tokens(tokens); - }); - Semi(span).to_tokens(tokens); - } - } -} - -impl DeclareObj { - fn build_tokens(&self, tokens: &mut TokenStream) { - let DeclareObj { - fields, - ty, - name, - watch_stmts, - stateful, - used_name_info, - } = self; - let span = ty.span(); - quote_spanned! { span => - let #name = - } - .to_tokens(tokens); - let build_widget = |tokens: &mut TokenStream| { - quote_spanned! { span => #ty::declare_builder() }.to_tokens(tokens); - fields.iter().for_each(|f| { - let Field { member, value, .. } = f; - Dot(value.span()).to_tokens(tokens); - member.to_tokens(tokens); - Paren(value.span()).surround(tokens, |tokens| value.to_tokens(tokens)) - }); - let build_ctx = ctx_ident(); - tokens.extend(quote_spanned! { span => .build_declare(#build_ctx) }); - }; - let builder = |tokens: &mut TokenStream| { - let is_stateful = *stateful || !watch_stmts.is_empty(); - if is_stateful { - let mut stream = TokenStream::new(); - build_widget(&mut stream); - quote_spanned! { span => Stateful::new(#stream) }.to_tokens(tokens); - } else { - build_widget(tokens); - // we stripe the `DynWidget` if it stateless. - if is_dyn_widget(ty) { - quote_spanned! { span => .into_inner() }.to_tokens(tokens); - } - } - }; - - if used_name_info.ref_widgets().is_some() { - Brace(span).surround(tokens, |tokens| { - used_name_info.state_refs_tokens(tokens); - builder(tokens); - }) - } else { - builder(tokens); - } - - Semi(span).to_tokens(tokens); - } -} - -impl ToTokens for StateField { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let StateField { member, expr, .. } = self; - syn::token::Let(member.span()).to_tokens(tokens); - member.to_tokens(tokens); - syn::token::Eq(member.span()).to_tokens(tokens); - expr.to_tokens(tokens); - Semi(member.span()).to_tokens(tokens); - } -} - -impl ToTokens for States { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - self - .states - .iter() - .filter(|f| f.colon_token.is_some()) - .for_each(|field| field.to_tokens(tokens)); - } -} - -impl WidgetNode { - fn gen_node_objs(&self, tokens: &mut TokenStream) { - let WidgetNode { node: parent, children } = self; - if let ComposeItem::ChainObjs(objs) = parent { - objs.iter().for_each(|obj| obj.to_tokens(tokens)); - } - - children.iter().for_each(|node| node.gen_node_objs(tokens)); - } - - fn gen_compose_node(&self, named_objs: &NamedObjMap, tokens: &mut TokenStream) { - fn recursive_compose( - nodes: &[&Ident], - children: &[WidgetNode], - named_objs: &NamedObjMap, - tokens: &mut TokenStream, - ) { - let first = &nodes[0]; - first.to_tokens(tokens); - let span = first.span(); - let ctx_name = ctx_ident(); - - if nodes.len() > 1 { - quote_spanned! { span => .with_child}.to_tokens(tokens); - Paren(span).surround(tokens, |tokens| { - recursive_compose(&nodes[1..], children, named_objs, tokens); - Comma::default().to_tokens(tokens); - ctx_name.to_tokens(tokens); - }); - } else { - children.iter().for_each(|c| { - quote_spanned!(span => .with_child).to_tokens(tokens); - Paren(span).surround(tokens, |tokens| { - c.gen_compose_node(named_objs, tokens); - Comma::default().to_tokens(tokens); - ctx_name.to_tokens(tokens); - }) - }); - } - } - - let WidgetNode { node, children } = self; - let nodes = node.node_compose_list(named_objs); - recursive_compose(&nodes, children, named_objs, tokens); - } -} - -impl ComposeItem { - fn node_compose_list<'a>(&'a self, named_objs: &'a NamedObjMap) -> SmallVec<[&'a Ident; 1]> { - let mut list = smallvec![]; - match self { - ComposeItem::ChainObjs(objs) => { - assert!(!objs.is_empty()); - list.extend(objs.iter().map(|obj| &obj.name)); - } - ComposeItem::Id(name) => { - WIDGETS - .iter() - .rev() - .filter_map(|builtin| { - let var_name = builtin_var_name(name, name.span(), builtin.ty); - named_objs.get_name_obj(&var_name) - }) - .for_each(|(var_name, obj)| match obj { - NamedObj::Builtin { .. } => list.push(var_name), - NamedObj::Host(..) => unreachable!("builtin object type not match."), - }); - - list.push(name); - } - }; - list - } -} - -impl ToTokens for NamedObj { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - NamedObj::Host(obj) | NamedObj::Builtin { obj, .. } => obj.to_tokens(tokens), - } - } -} - -impl States { - pub fn track_names(&self) -> impl Iterator { - self.states.iter().map(|f| &f.member) - } -} - -impl ToTokens for InitStmts { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - if let Some(refs) = self.used_name_info.ref_widgets() { - let refs = refs.map(|name| { - quote_spanned! { name.span() => - let mut #name = #name.state_ref(); - } - }); - tokens.append_all(refs); - } - tokens.append_all(&self.stmts); - } -} - -impl ToTokens for FinallyBlock { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - self.brace_token.surround(tokens, |tokens| { - // finally block can directly define a variable but `init` can't, because - // `finally` not leak its variable outside. - if let Some(ctx) = self.ctx_name.as_ref() { - let real_ctx = ctx_ident(); - quote_spanned! { ctx.span() => let #ctx = #real_ctx; }.to_tokens(tokens); - } - self.used_name_info.state_refs_tokens(tokens); - tokens.append_all(&self.stmts) - }) - } -} -impl ToTokens for FinallyStmt { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - FinallyStmt::Stmt(stmt) => stmt.to_tokens(tokens), - FinallyStmt::Obj(obj) => obj.to_tokens(tokens), - } - } -} - -impl ToTokens for PropMacro { - fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { prop, lerp_fn, .. } = self; - if let Some(lerp_fn) = lerp_fn { - let span = lerp_fn.span(); - quote_spanned!(span => LerpProp::new (#prop, #lerp_fn)).to_tokens(tokens); - } else { - prop.to_tokens(tokens) - } - } -} - -impl ToTokens for Property { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Property::Name(name) => quote_spanned! {name.span() => - Prop::new(#name.clone_stateful(), |v| v.clone(), |this, v| *this = v) - } - .to_tokens(tokens), - Property::Member { target, dot, member } => quote_spanned! { - target.span().join(member.span()).unwrap() => - Prop::new( - #target.clone_stateful(), - |this| this #dot #member.clone(), - |this, v| this #dot #member = v - ) - } - .to_tokens(tokens), - } - } -} diff --git a/macros/src/widget_macro/desugar.rs b/macros/src/widget_macro/desugar.rs deleted file mode 100644 index 58244aab1..000000000 --- a/macros/src/widget_macro/desugar.rs +++ /dev/null @@ -1,487 +0,0 @@ -use proc_macro2::{Span, TokenStream}; -use quote::quote; -use smallvec::{smallvec, SmallVec}; -use std::collections::HashMap; -use syn::{ - parse_quote, parse_quote_spanned, spanned::Spanned, token::Brace, Block, Expr, Ident, Path, Stmt, -}; - -use super::{ - child_variable, guard_ident, - parser::{ - DeclareField, DeclareSingle, DeclareWidget, FieldColon, Item, MacroSyntax, States, TransProps, - }, - ribir_variable, ScopeUsedInfo, TrackExpr, WIDGETS, WIDGET_OF_BUILTIN_FIELD, -}; -use crate::{ - error::{DeclareError, DeclareWarning}, - widget_macro::builtin_var_name, -}; - -#[derive(Default)] -pub struct NamedObjMap(HashMap); - -pub const ID: &str = "id"; -pub struct Desugared { - pub init: Option, - pub states: Option, - pub named_objs: NamedObjMap, - pub widget: Option, - pub finally: Option, - pub errors: Vec, - pub warnings: Vec, -} - -pub struct InitStmts { - pub ctx_name: Option, - pub stmts: Vec, - pub used_name_info: ScopeUsedInfo, -} - -#[derive(Default)] -pub struct FinallyBlock { - pub ctx_name: Option, - pub brace_token: Brace, - pub stmts: Vec, - pub used_name_info: ScopeUsedInfo, -} - -pub enum FinallyStmt { - Stmt(Stmt), - Obj(DeclareObj), -} - -#[derive(Debug, Clone)] -pub struct DeclareObj { - pub ty: Path, - pub name: Ident, - pub fields: SmallVec<[Field; 1]>, - pub stateful: bool, - pub watch_stmts: SmallVec<[WatchField; 1]>, - pub used_name_info: ScopeUsedInfo, -} - -#[derive(Debug, Clone)] -pub struct WatchField { - pub pre_def: TokenStream, - pub watch_update: Stmt, -} - -#[derive(Debug)] -pub struct BuiltinObj { - pub obj: DeclareObj, - pub src_name: Option, -} - -#[derive(Debug)] -pub enum NamedObj { - Host(DeclareObj), - Builtin { src_name: Ident, obj: DeclareObj }, -} - -#[derive(Debug, Clone)] -pub struct Field { - pub member: Ident, - pub value: FieldValue, -} - -#[derive(Debug, Clone)] -pub enum FieldValue { - Observable(TrackExpr), - Expr(TrackExpr), - Obj(Box), -} -pub struct WidgetNode { - pub node: ComposeItem, - pub children: Vec, -} - -pub enum ComposeItem { - ChainObjs(SmallVec<[DeclareObj; 1]>), - Id(Ident), -} - -impl MacroSyntax { - pub fn desugar(self) -> Desugared { - let named_objs = NamedObjMap::default(); - let MacroSyntax { init, states, widget, items, finally } = self; - - let mut desugared = Desugared { - init: init.map(|init| InitStmts { - ctx_name: init.ctx_name, - stmts: init.block.stmts, - used_name_info: <_>::default(), - }), - states, - named_objs, - widget: None, - finally: finally.map(|f| { - let Block { brace_token, stmts } = f.block; - FinallyBlock { - ctx_name: f.ctx_name, - brace_token, - stmts: stmts.into_iter().map(FinallyStmt::Stmt).collect(), - used_name_info: <_>::default(), - } - }), - errors: vec![], - warnings: vec![], - }; - let default_name = ribir_variable("ribir", widget.ty_path().span()); - let widget = widget.desugar(default_name, &mut desugared); - desugared.widget = Some(widget); - - items - .into_iter() - .for_each(|item| item.desugar(&mut desugared)); - desugared - } -} - -impl DeclareWidget { - fn desugar(self, default_name: Ident, desugared: &mut Desugared) -> WidgetNode { - match self { - DeclareWidget::Literal { - ty, fields: declare_fields, children, .. - } => { - let mut id = None; - let mut fields = smallvec![]; - let mut builtin_widgets: HashMap<_, SmallVec<[Field; 1]>, ahash::RandomState> = - <_>::default(); - declare_fields - .into_iter() - .for_each(|f| match pick_id(f, &mut desugared.errors) { - Ok(name) => id = Some(name), - Err(f) => { - let field = f.desugar(); - if let Some(ty) = WIDGET_OF_BUILTIN_FIELD - .get(field.member.to_string().as_str()) - .filter(|builtin_ty| !ty.is_ident(builtin_ty)) - { - builtin_widgets.entry(*ty).or_default().push(field); - } else { - fields.push(field) - } - } - }); - - let parent = if let Some(name) = id { - desugared.add_named_host_obj(DeclareObj::new(ty, name.clone(), fields)); - builtin_widgets.into_iter().for_each(|(ty, fields)| { - let obj = builtin_obj(&name, ty, fields); - desugared.add_named_builtin_obj(name.clone(), obj); - }); - ComposeItem::Id(name) - } else { - let mut objs = WIDGETS - .iter() - .rev() - .filter_map(|b_widget| builtin_widgets.remove_entry(b_widget.ty)) - .map(|(ty, fields)| { - let span = builtin_span(&default_name, &fields); - let name = builtin_var_name(&default_name, span, ty); - let ty = Ident::new(ty, name.span()).into(); - DeclareObj::new(ty, name, fields) - }) - .collect::>(); - assert!(builtin_widgets.is_empty()); - objs.push(DeclareObj::new(ty, default_name, fields)); - ComposeItem::ChainObjs(objs) - }; - - let children = children - .into_iter() - .enumerate() - .map(|(idx, w)| { - let mut name = child_variable(parent.name(), idx); - name.set_span(w.ty_path().span()); - w.desugar(name, desugared) - }) - .collect(); - - WidgetNode { node: parent, children } - } - DeclareWidget::Call(call) => { - let expr: Expr = parse_quote!(#call); - expr_as_widget_node(expr, default_name) - } - DeclareWidget::Path(path) => { - let expr: Expr = parse_quote!(#path); - expr_as_widget_node(expr, default_name) - } - DeclareWidget::EmbedWidget(expr) => { - let expr: Expr = Expr::Macro(expr); - expr_as_widget_node(expr, default_name) - } - } - } -} - -fn expr_as_widget_node(expr: Expr, default_name: Ident) -> WidgetNode { - let ty = parse_quote_spanned!(expr.span() => DynWidget); - let field = Field { - member: parse_quote_spanned!(expr.span() => dyns), - value: FieldValue::Expr(expr.into()), - }; - let obj = DeclareObj::new(ty, default_name, smallvec![field]); - WidgetNode { - node: ComposeItem::ChainObjs(smallvec![obj]), - children: vec![], - } -} -impl DeclareObj { - pub fn new(ty: Path, name: Ident, fields: SmallVec<[Field; 1]>) -> Self { - Self { - ty, - name, - fields, - stateful: false, - watch_stmts: <_>::default(), - used_name_info: <_>::default(), - } - } -} - -impl Item { - fn desugar(self, desugared: &mut Desugared) { - match self { - Item::TransProps(TransProps { transition, props, fields, .. }) => { - let by = fields.iter().enumerate().find(|(_, f)| f.member == "by"); - let transition = if let Some(by) = by { - if fields.len() > 1 { - desugared - .errors - .push(DeclareError::TransitionByConflict(by.1.span().unwrap())); - return; - } - FieldValue::Expr(by.1.expr.clone().into()) - } else { - let fields = fields.iter().map(|f| f.clone().desugar()).collect(); - let name = ribir_variable("transition", transition.span()); - let mut obj = DeclareObj::new(parse_quote!(Transition), name, fields); - obj.stateful = true; - FieldValue::Obj(Box::new(obj)) - }; - - let stmts = &mut desugared - .finally - .get_or_insert_with(FinallyBlock::default) - .stmts; - - props.into_iter().for_each(|p| { - let span = p.span(); - let prop = ribir_variable("prop", span); - stmts.push(FinallyStmt::Stmt( - parse_quote_spanned! { span => let #prop = { #p };}, - )); - - let prop_changes = ribir_variable("prop_changes", span); - stmts.push(FinallyStmt::Stmt( - parse_quote_spanned! { span => let #prop_changes = #prop.changes();}, - )); - let from = ribir_variable("from", span); - stmts.push(FinallyStmt::Stmt( - parse_quote_spanned! { span => let #from = #prop.get();}, - )); - let name = ribir_variable("animate", transition.span()); - let transition = Field { - member: Ident::new("transition", span), - value: transition.clone(), - }; - let from_value: Expr = parse_quote!(#from); - let from = Field { - member: Ident::new("from", span), - value: FieldValue::Expr(from_value.into()), - }; - let prop_value: Expr = parse_quote!(#prop); - let prop = Field { - member: Ident::new("prop", span), - value: FieldValue::Expr(prop_value.into()), - }; - - let mut obj = DeclareObj::new( - parse_quote_spanned!(span => Animate), - name.clone(), - smallvec![transition, prop, from], - ); - - obj.stateful = true; - stmts.push(FinallyStmt::Obj(obj)); - let guard = guard_ident(span); - stmts.push(FinallyStmt::Stmt(parse_quote_spanned! { span => - let #guard = #prop_changes.pairwise().subscribe(move |(old, _)| { - #name.state_ref().from = old; - #name.state_ref().run(); - }) - .unsubscribe_when_dropped(); - })); - stmts.push(FinallyStmt::Stmt( - parse_quote_spanned! { span => move_to_widget!(#guard); }, - )); - }); - } - Item::Transition(d) | Item::Animate(d) => { - if let DesugaredObj::Obj(obj) = d.desugar(desugared) { - let warning = DeclareWarning::DefObjWithoutId(obj.span().unwrap()); - desugared.warnings.push(warning) - } - } - } - } -} - -enum DesugaredObj { - Name(Ident), - Obj(DeclareObj), -} - -impl DeclareSingle { - fn desugar(self, desugared: &mut Desugared) -> DesugaredObj { - let Self { ty, fields, .. } = self; - let mut id = None; - let fields = fields - .into_iter() - .filter_map(|f| match pick_id(f, &mut desugared.errors) { - Ok(name) => { - assert!(id.is_none(), "only once `id` allow."); - id = Some(name); - None - } - Err(f) => Some(f.desugar()), - }) - .collect(); - - if let Some(name) = id { - let c_name = name.clone(); - desugared.add_named_host_obj(DeclareObj::new(ty, name, fields)); - DesugaredObj::Name(c_name) - } else { - let name = ribir_variable("obj", ty.span()); - DesugaredObj::Obj(DeclareObj::new(ty, name, fields)) - } - } -} - -impl ComposeItem { - pub fn name(&self) -> &Ident { - match self { - ComposeItem::ChainObjs(objs) => &objs.last().expect("at least have one obj").name, - ComposeItem::Id(name) => name, - } - } -} - -impl Desugared { - pub fn add_named_host_obj(&mut self, obj: DeclareObj) { - if let Err(err) = self.named_objs.add_host_obj(obj) { - self.errors.push(err); - } - } - - pub fn add_named_builtin_obj(&mut self, src_name: Ident, obj: DeclareObj) { - self.named_objs.add_builtin_obj(src_name, obj) - } -} - -impl DeclareField { - fn desugar(self) -> Field { - let Self { member, colon, expr } = self; - let value = if let Some(FieldColon::AssignColon(_)) = colon { - FieldValue::Observable(expr.into()) - } else { - FieldValue::Expr(expr.into()) - }; - Field { member, value } - } -} - -impl From for TrackExpr { - fn from(expr: Expr) -> Self { TrackExpr { expr, used_name_info: <_>::default() } } -} - -fn pick_id(f: DeclareField, errors: &mut Vec) -> Result { - let DeclareField { member, expr, .. } = &f; - if member == ID { - let name = syn::parse2::(quote! {#expr}); - name.map_err(move |err| { - errors.push(DeclareError::SynErr(err)); - f - }) - } else { - Err(f) - } -} - -impl NamedObjMap { - pub fn is_empty(&self) -> bool { self.0.is_empty() } - - pub fn get(&self, name: &Ident) -> Option<&NamedObj> { self.0.get(name) } - - pub fn contains(&self, name: &Ident) -> bool { self.0.contains_key(name) } - - pub fn get_mut(&mut self, name: &Ident) -> Option<&mut NamedObj> { self.0.get_mut(name) } - - pub fn names(&self) -> impl Iterator { self.0.keys() } - - pub fn objs(&self) -> impl Iterator { self.0.values() } - - pub fn objs_mut(&mut self) -> impl Iterator { self.0.values_mut() } - - pub fn iter(&self) -> impl Iterator { self.0.iter() } - - pub fn get_name_obj(&self, name: &Ident) -> Option<(&Ident, &NamedObj)> { - self.0.get_key_value(name) - } - - fn add_host_obj(&mut self, obj: DeclareObj) -> Result<(), DeclareError> { - if let Some((name, _)) = self.0.get_key_value(&obj.name) { - let err = DeclareError::DuplicateID([name.clone(), obj.name.clone()]); - Err(err) - } else { - self.0.insert(obj.name.clone(), NamedObj::Host(obj)); - Ok(()) - } - } - - fn add_builtin_obj(&mut self, src_name: Ident, obj: DeclareObj) { - let v = self - .0 - .insert(obj.name.clone(), NamedObj::Builtin { src_name, obj }); - - assert!(v.is_none(), "builtin widget already have"); - } -} - -fn builtin_span(host: &Ident, fields: &SmallVec<[Field; 1]>) -> Span { - if fields.is_empty() { - host.span() - } else { - let span = fields[0].member.span(); - fields[1..] - .iter() - .fold(span, |span, f| span.join(f.member.span()).unwrap()) - } -} - -pub fn builtin_obj(src_name: &Ident, ty: &str, fields: SmallVec<[Field; 1]>) -> DeclareObj { - let span = builtin_span(src_name, &fields); - let name = builtin_var_name(src_name, span, ty); - let ty = Ident::new(ty, src_name.span()).into(); - DeclareObj::new(ty, name, fields) -} - -impl NamedObj { - pub fn name(&self) -> &Ident { - match self { - NamedObj::Host(obj) => &obj.name, - NamedObj::Builtin { obj, .. } => &obj.name, - } - } - - pub fn ty(&self) -> &Path { - match self { - NamedObj::Host(obj) => &obj.ty, - NamedObj::Builtin { obj, .. } => &obj.ty, - } - } -} diff --git a/macros/src/widget_macro/name_used_info.rs b/macros/src/widget_macro/name_used_info.rs deleted file mode 100644 index ba8fe9932..000000000 --- a/macros/src/widget_macro/name_used_info.rs +++ /dev/null @@ -1,236 +0,0 @@ -use super::desugar::NamedObjMap; -use crate::{error::CircleUsedPath, widget_macro::desugar::NamedObj}; -use proc_macro2::{Span, TokenStream}; -use quote::{quote, quote_spanned, ToTokens}; -use std::collections::HashMap; -use syn::Ident; - -#[derive(Clone, Debug)] -pub struct NameUsedInfo { - pub used_type: UsedType, - pub spans: Vec, -} - -bitflags::bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct UsedType: u16 { - /// subscribe the named object modifies. - const SUBSCRIBE = 0x0001; - /// directly use it. - const REF = 0x0010; - /// named object be used and subscribe, the default behavior. - const USED = Self::SUBSCRIBE.bits() | Self::REF.bits(); - /// named object used in inner scope with needn't provide `state_ref` by outside. - const SCOPE_CAPTURE = 0x1000; - } -} - -#[derive(Clone, Debug)] -pub struct UsedPart<'a> { - pub scope_label: Option<&'a Ident>, - pub used_info: &'a HashMap, -} - -#[derive(Clone, Debug)] -pub struct ObjectUsed<'a>(pub Vec>); - -#[derive(Debug, Default, Clone)] -pub struct ScopeUsedInfo(Option>); - -impl<'a, IntoIter> From for ObjectUsed<'a> -where - IntoIter: IntoIterator>, -{ - #[inline] - fn from(iter: IntoIter) -> Self { Self(iter.into_iter().collect()) } -} - -#[derive(Clone, Debug)] -pub struct ObjectUsedPath<'a> { - pub obj: &'a Ident, - pub scope_label: Option<&'a Ident>, - pub used_obj: &'a Ident, - pub used_info: &'a NameUsedInfo, -} - -impl<'a> ObjectUsed<'a> { - // return the iterator of tuple, the tuple compose by a field and a widget name, - // the widget name is what the field follow on - pub fn used_full_path_iter<'r>( - &'r self, - self_name: &'r Ident, - ) -> impl Iterator> + 'r { - self.iter().flat_map(move |p| { - let &UsedPart { scope_label, used_info } = p; - used_info - .iter() - .map(move |(used_obj, used_info)| ObjectUsedPath { - obj: self_name, - scope_label, - used_obj, - used_info, - }) - }) - } -} - -impl<'a> std::ops::Deref for ObjectUsed<'a> { - type Target = [UsedPart<'a>]; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl<'a> std::ops::DerefMut for ObjectUsed<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } -} - -impl<'a> FromIterator> for ObjectUsed<'a> { - #[inline] - fn from_iter>>(iter: T) -> Self { - Self(iter.into_iter().collect()) - } -} - -impl NameUsedInfo { - pub fn merge(&mut self, other: &NameUsedInfo) { - self.used_type |= other.used_type; - self.spans.extend(&other.spans) - } -} - -impl ScopeUsedInfo { - pub fn take(&mut self) -> Self { Self(self.0.take()) } - - pub fn add_used(&mut self, name: Ident, used_type: UsedType) { - let span = name.span(); - self - .0 - .get_or_insert_with(<_>::default) - .entry(name) - .and_modify(|info| { - info.used_type |= used_type; - info.spans.push(span) - }) - .or_insert_with(|| NameUsedInfo { used_type, spans: vec![span] }); - } - - pub fn merge(&mut self, other: &Self) { - match (self.0.as_mut(), other.0.as_ref()) { - (Some(a), Some(b)) => b.iter().for_each(|(name, info)| { - a.entry(name.clone()) - .and_modify(|i| i.merge(info)) - .or_insert_with(|| info.clone()); - }), - (None, b @ Some(_)) => self.0 = b.cloned(), - _ => {} - } - } - - pub fn upstream_modifies_tokens(&self, raw: bool) -> Option { - self.subscribe_widget().map(|directly_used| { - let modifies = if raw { - quote! { raw_modifies } - } else { - quote! {modifies} - }; - let upstream = directly_used.clone().map(|w| { - quote_spanned! { w.span() => #w.#modifies() } - }); - if directly_used.count() > 1 { - quote! { observable::from_iter([#(#upstream),*]).merge_all(usize::MAX) } - } else { - quote! { #(#upstream)* } - } - }) - } - - pub fn subscribe_widget(&self) -> Option + Clone + '_> { - self.filter_widget(|info| info.used_type.contains(UsedType::SUBSCRIBE)) - } - - pub fn ref_widgets(&self) -> Option + Clone + '_> { - self.filter_widget(|info| info.used_type.contains(UsedType::REF)) - } - - pub fn state_refs_tokens(&self, tokens: &mut TokenStream) { - if let Some(names) = self.ref_widgets() { - let c_names = names.clone(); - - c_names.for_each(|n| { - quote_spanned! {n.span() => let mut #n = #n.state_ref();}.to_tokens(tokens); - }); - } - } - - pub fn iter_mut(&mut self) -> impl Iterator { - self.0.iter_mut().flat_map(|m| m.iter_mut()) - } - - pub fn all_used(&self) -> Option + Clone + '_> { - let used = self.0.as_ref()?; - (!used.is_empty()).then(|| used.keys()) - } - - pub fn used_part<'a>(&'a self, scope_label: Option<&'a Ident>) -> Option { - self - .0 - .as_ref() - .map(|used_info| UsedPart { scope_label, used_info }) - } - - pub fn filter_item( - &self, - filter: impl Fn(&NameUsedInfo) -> bool + Clone, - ) -> Option + Clone> { - let widgets = self - .0 - .as_ref()? - .iter() - .filter(move |(_, info)| filter(info)); - - widgets.clone().next().is_some().then_some(widgets) - } - - fn filter_widget( - &self, - filter: impl Fn(&NameUsedInfo) -> bool + Clone, - ) -> Option + Clone> { - self.filter_item(filter).map(|iter| iter.map(|(w, _)| w)) - } -} - -impl<'a> ObjectUsedPath<'a> { - pub fn to_used_path(&self, declare_objs: &NamedObjMap) -> CircleUsedPath { - fn src_name<'a>(name: &Ident, declare_objs: &'a NamedObjMap) -> Option<&'a Ident> { - declare_objs.get(name).map(|obj| match obj { - NamedObj::Host(obj) => &obj.name, - NamedObj::Builtin { src_name, .. } => src_name, - }) - } - let obj = src_name(self.obj, declare_objs) - .unwrap_or_else(|| { - if self.scope_label.is_none() { - self.obj - } else { - // same id, but use the one which at the define place to provide more friendly - // compile error. - declare_objs - .get_name_obj(self.obj) - .expect("Some named object not collect.") - .0 - } - }) - .clone(); - - let used_obj = src_name(self.used_obj, declare_objs).map_or_else( - || self.used_obj.clone(), - |user| Ident::new(&user.to_string(), self.used_obj.span()), - ); - CircleUsedPath { - obj, - member: self.scope_label.cloned(), - used_widget: used_obj, - used_info: self.used_info.clone(), - } - } -} diff --git a/macros/src/widget_macro/parser.rs b/macros/src/widget_macro/parser.rs deleted file mode 100644 index 42194a47d..000000000 --- a/macros/src/widget_macro/parser.rs +++ /dev/null @@ -1,479 +0,0 @@ -//! mod parse the `widget!` macro. -use proc_macro2::TokenStream; -use quote::ToTokens; -use smallvec::{smallvec, SmallVec}; -use std::collections::HashSet; -use syn::{ - braced, parenthesized, - parse::{Parse, ParseStream}, - parse_quote, - punctuated::Punctuated, - spanned::Spanned, - token::{Bang, Brace, Colon, Colon2, Comma, Dot, FatArrow, Paren}, - Block, Expr, ExprMacro, Ident, Path, Result, -}; - -use super::TrackExpr; - -pub mod kw { - syn::custom_keyword!(states); - syn::custom_keyword!(init); - syn::custom_keyword!(finally); - syn::custom_keyword!(DynWidget); - syn::custom_keyword!(id); - syn::custom_keyword!(Animate); - syn::custom_keyword!(Transition); - syn::custom_keyword!(transition); - syn::custom_punctuation!(AssignColon, :=); - syn::custom_keyword!(widget); -} - -pub struct MacroSyntax { - pub init: Option, - pub states: Option, - pub widget: DeclareWidget, - pub items: Vec, - pub finally: Option, -} - -pub struct States { - _states_token: kw::states, - _brace: Brace, - pub states: Vec, -} - -pub struct Init { - _init_token: kw::init, - pub ctx_name: Option, - _fat_arrow: Option, - pub block: Block, -} - -pub struct Finally { - _finally_token: kw::finally, - pub ctx_name: Option, - _fat_arrow: Option, - pub block: Block, -} - -#[derive(Clone, Debug)] -pub enum FieldColon { - Colon(Colon), - AssignColon(kw::AssignColon), -} -#[derive(Debug)] -pub struct StateField { - pub(crate) member: Ident, - pub(crate) colon_token: Option, - pub(crate) expr: Expr, -} - -#[derive(Debug)] -pub enum DeclareWidget { - /// Declare widget as struct literal. - Literal { - ty: Path, - brace: Brace, - fields: Punctuated, - children: Vec, - }, - /// Declare a widget use a path. - Path(Path), - /// Declare a widget across widget construct call, only as a leaf declare. - /// `X::new(...)` - Call(ConstructCall), - /// Declare a child widget across widget! macro - EmbedWidget(ExprMacro), -} - -#[derive(Debug)] -pub struct ConstructCall { - path: Path, - paren: Paren, - args: Punctuated, -} -#[derive(Debug)] -pub struct Id { - pub id: kw::id, - pub colon: Colon, - pub name: Ident, - pub tail_comma: Option, -} - -#[derive(Clone, Debug)] -pub struct DeclareField { - pub member: Ident, - pub colon: Option, - pub expr: Expr, -} - -pub struct DeclareSingle { - pub ty: Path, - pub brace: Brace, - pub fields: Punctuated, -} -pub enum Item { - TransProps(TransProps), - Transition(DeclareSingle), - Animate(DeclareSingle), -} - -pub struct TransProps { - pub transition: kw::transition, - pub props: SmallVec<[Expr; 1]>, - pub brace: Brace, - pub fields: Punctuated, -} - -pub enum Property { - Name(Ident), - Member { - target: Ident, - dot: Dot, - member: Ident, - }, -} -pub struct PropMacro { - pub prop: Property, - pub comma: Option, - pub lerp_fn: Option, -} - -impl Parse for PropMacro { - fn parse(input: ParseStream) -> Result { - let prop = if input.peek2(Dot) { - Property::Member { - target: input.parse()?, - dot: input.parse()?, - member: input.parse()?, - } - } else { - Property::Name(input.parse()?) - }; - let comma = input.parse()?; - let lerp_fn = if input.is_empty() { - None - } else { - Some(input.parse::()?.into()) - }; - Ok(Self { prop, comma, lerp_fn }) - } -} - -impl Parse for MacroSyntax { - fn parse(input: ParseStream) -> Result { - let mut widget: Option = None; - let mut items = vec![]; - let mut init: Option = None; - let mut finally: Option = None; - let mut states: Option = None; - loop { - if input.is_empty() { - break; - } - let lk = input.lookahead1(); - if lk.peek(kw::Animate) { - items.push(Item::Animate(input.parse()?)); - } else if lk.peek(kw::Transition) { - items.push(Item::Transition(input.parse()?)); - } else if lk.peek(kw::transition) { - items.push(Item::TransProps(input.parse()?)); - } else if lk.peek(kw::states) { - let mut t = input.parse::()?; - if let Some(ot) = states.take() { - t.states.extend(ot.states); - } - states = Some(t); - } else if lk.peek(kw::init) { - let e: Init = input.parse::()?; - if let Some(init) = init.as_mut() { - init.block.stmts.extend(e.block.stmts); - } else { - init = Some(e) - } - } else if lk.peek(kw::finally) { - let e: Finally = input.parse::()?; - if let Some(finally) = finally.as_mut() { - finally.block.stmts.extend(e.block.stmts); - } else { - finally = Some(e) - } - } else if peek_widget(input) { - let w: DeclareWidget = input.parse()?; - if let Some(first) = widget.as_ref() { - let err = syn::Error::new( - w.span(), - format!( - "Only one root widget can declare, but `{}` already declared.", - first.ty_path().to_token_stream() - ), - ); - return Err(err); - } - widget = Some(w); - } else { - return Err(lk.error()); - } - } - let widget = widget - .ok_or_else(|| syn::Error::new(input.span(), "must declare a root widget in `widget!`"))?; - Ok(Self { init, widget, items, states, finally }) - } -} - -impl Parse for States { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let content; - - let states = States { - _states_token: input.parse()?, - _brace: braced!(content in input), - states: { - let fields: Punctuated = content.parse_terminated(StateField::parse)?; - fields.into_iter().collect() - }, - }; - Ok(states) - } -} - -fn ctx_block_parse( - input: ParseStream, -) -> Result<(Kw, Option, Option, Block)> { - let kw = input.parse::()?; - let ctx: Option = input.parse()?; - let fat_arrow: Option = if ctx.is_some() { input.parse()? } else { None }; - - Ok((kw, ctx, fat_arrow, input.parse()?)) -} - -impl Parse for Init { - fn parse(input: ParseStream) -> Result { - ctx_block_parse(input).map(|(_init_token, ctx_name, _fat_arrow, block)| Self { - _init_token, - ctx_name, - _fat_arrow, - block, - }) - } -} - -impl Parse for Finally { - fn parse(input: ParseStream) -> Result { - ctx_block_parse(input).map(|(_finally_token, ctx_name, _fat_arrow, block)| Self { - _finally_token, - ctx_name, - _fat_arrow, - block, - }) - } -} - -impl Parse for FieldColon { - fn parse(input: ParseStream) -> Result { - if input.peek(kw::AssignColon) { - Ok(FieldColon::AssignColon(input.parse()?)) - } else { - Ok(FieldColon::Colon(input.parse::()?)) - } - } -} - -impl Parse for StateField { - fn parse(input: ParseStream) -> syn::Result { - let member = input.parse::()?; - let (colon_token, expr) = if input.peek(Colon) { - (Some(input.parse()?), input.parse()?) - } else { - (None, parse_quote!(#member)) - }; - Ok(StateField { member, colon_token, expr }) - } -} - -impl Parse for Id { - fn parse(input: ParseStream) -> syn::Result { - Ok(Self { - id: input.parse()?, - colon: input.parse()?, - name: input.parse()?, - tail_comma: input.parse()?, - }) - } -} - -impl Parse for DeclareWidget { - fn parse(input: ParseStream) -> syn::Result { - let path: Path = input.parse()?; - - // we not allow an ident as a widget, ambiguous with shorthand field init. - if input.peek(Paren) { - let content; - Ok(DeclareWidget::Call(ConstructCall { - path, - paren: syn::parenthesized!(content in input), - args: content.parse_terminated(Expr::parse)?, - })) - } else if input.peek(Brace) { - let content; - let brace = syn::braced!(content in input); - let mut fields = Punctuated::default(); - let mut children = vec![]; - loop { - if content.is_empty() { - break; - } - if content.peek(kw::widget) && content.peek2(Bang) { - children.push(DeclareWidget::EmbedWidget(content.parse()?)); - } else if peek_widget(&content) { - children.push(content.parse()?); - } else { - let f: DeclareField = content.parse()?; - if !children.is_empty() { - return Err(syn::Error::new( - f.span(), - "Field should always declare before children.", - )); - } - fields.push(f); - if !content.is_empty() { - content.parse::()?; - } - } - } - check_duplicate_field(&fields)?; - - Ok(DeclareWidget::Literal { ty: path, brace, fields, children }) - } else { - Ok(DeclareWidget::Path(path)) - } - } -} - -fn peek_widget(input: ParseStream) -> bool { - (input.peek(Ident) && (input.peek2(Brace) || input.peek2(Colon2) || input.peek2(Paren))) - || input.peek(Colon2) - || input.peek2(Colon2) -} - -impl Parse for DeclareField { - fn parse(input: ParseStream) -> syn::Result { - let member: Ident = input.parse()?; - let mut colon_token = None; - if input.peek(Colon) { - colon_token = Some(input.parse()?); - } - let expr = if colon_token.is_some() { - input.parse()? - } else { - parse_quote!(#member) - }; - - Ok(DeclareField { member, colon: colon_token, expr }) - } -} - -impl Parse for TransProps { - fn parse(input: ParseStream) -> Result { - let transition = input.parse()?; - let props = if input.peek(Paren) { - let content; - parenthesized!(content in input ); - content - .parse_terminated::<_, Comma>(Expr::parse)? - .into_iter() - .collect() - } else { - smallvec![input.parse()?] - }; - let content; - let brace = braced!(content in input); - let fields = content.parse_terminated(DeclareField::parse)?; - Ok(Self { transition, props, brace, fields }) - } -} - -impl Parse for DeclareSingle { - fn parse(input: ParseStream) -> Result { - let content; - let res = Self { - ty: input.parse()?, - brace: braced!( content in input), - fields: content.parse_terminated(DeclareField::parse)?, - }; - check_duplicate_field(&res.fields)?; - Ok(res) - } -} - -impl Spanned for DeclareWidget { - fn span(&self) -> proc_macro2::Span { - match self { - DeclareWidget::Literal { ty, brace, .. } => ty.span().join(brace.span).unwrap(), - DeclareWidget::Path(path) => path.span(), - DeclareWidget::Call(ConstructCall { path: ty, paren, .. }) => { - ty.span().join(paren.span).unwrap() - } - DeclareWidget::EmbedWidget(expr) => expr.mac.path.span(), - } - } -} - -impl DeclareWidget { - pub(crate) fn ty_path(&self) -> &Path { - match self { - DeclareWidget::Literal { ty, .. } => ty, - DeclareWidget::Call(call) => &call.path, - DeclareWidget::Path(path) => path, - DeclareWidget::EmbedWidget(expr) => &expr.mac.path, - } - } -} - -impl ToTokens for FieldColon { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - FieldColon::Colon(c) => c.to_tokens(tokens), - FieldColon::AssignColon(a) => a.to_tokens(tokens), - } - } -} - -impl ToTokens for DeclareField { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.member.to_tokens(tokens); - if self.colon.is_some() { - self.colon.to_tokens(tokens); - self.expr.to_tokens(tokens); - } - } -} - -impl ToTokens for ConstructCall { - fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { path, paren, args } = self; - path.to_tokens(tokens); - paren.surround(tokens, |tokens| args.to_tokens(tokens)); - } -} - -impl ToTokens for Id { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - self.id.to_tokens(tokens); - self.colon.to_tokens(tokens); - self.name.to_tokens(tokens); - } -} - -pub fn check_duplicate_field(fields: &Punctuated) -> syn::Result<()> { - let mut sets = HashSet::<&Ident, ahash::RandomState>::default(); - for f in fields { - if !sets.insert(&f.member) { - return Err(syn::Error::new( - f.member.span(), - format!("`{}` declare more than once", f.member).as_str(), - )); - } - } - Ok(()) -} diff --git a/macros/src/widget_macro/visit_mut.rs b/macros/src/widget_macro/visit_mut.rs deleted file mode 100644 index 062031e85..000000000 --- a/macros/src/widget_macro/visit_mut.rs +++ /dev/null @@ -1,790 +0,0 @@ -use crate::{ - declare_derive::declare_field_name, - error::DeclareError, - widget_macro::{desugar::WatchField, guard_ident, guard_vec_ident}, - ASSIGN_WATCH_MACRO_NAME, LET_WATCH_MACRO_NAME, MOVE_TO_WIDGET_MACRO_NAME, NO_WATCH_MACRO_NAME, - PROP_MACRO_NAME, WATCH_MACRO_NAME, WIDGET_MACRO_NAME, -}; - -use super::{ - builtin_var_name, capture_widget, - code_gen::{ - gen_assign_watch, gen_move_to_widget_macro, gen_no_watch_macro, gen_prop_macro, gen_watch_macro, - }, - ctx_ident, - desugar::{ - ComposeItem, DeclareObj, FieldValue, FinallyBlock, FinallyStmt, InitStmts, NamedObj, WidgetNode, - }, - gen_widget_macro, is_dyn_widget, ribir_suffix_variable, Desugared, ScopeUsedInfo, TrackExpr, - UsedType, WIDGET_OF_BUILTIN_FIELD, WIDGET_OF_BUILTIN_METHOD, -}; - -use proc_macro::Span; -use proc_macro2::TokenStream; -use quote::{quote, quote_spanned, ToTokens}; -use std::{ - collections::{HashMap, HashSet}, - hash::Hash, -}; -use syn::{ - parse_quote, parse_quote_spanned, - spanned::Spanned, - token::{Brace, Semi}, - visit_mut, - visit_mut::VisitMut, - Expr, ExprClosure, ExprMethodCall, ExprPath, Ident, ItemMacro, Member, Path, Stmt, -}; - -bitflags::bitflags! { - - pub struct IdType: u16 { - /// Declared by `id: name`, - const DECLARE = 0x001; - /// name provide in `states { ... }` - const USER_SPECIFY = 0x010; - /// name pass by outside `widget!` macro. - const FROM_ANCESTOR = 0x100; - } -} - -pub struct VisitCtx { - /// All declared object. - pub declare_objs: HashMap, - pub states: HashSet, - pub current_used_info: ScopeUsedInfo, - /// name object has be used and its source name. - pub used_objs: HashMap, - pub analyze_stack: Vec>, - pub has_guards_data: bool, - pub visit_error_occur: bool, - pub replace_ident: Option<(Ident, Ident)>, -} - -#[derive(Debug, Clone)] -pub struct LocalVariable { - name: Ident, - alias_of_name: Option, -} - -impl LocalVariable { - pub fn new(name: Ident, alias: Ident) -> Self { Self { name, alias_of_name: Some(alias) } } - pub fn local(name: Ident) -> Self { Self { name, alias_of_name: None } } -} - -#[derive(Debug, Clone)] -pub struct UsedInfo { - pub builtin: Option, - pub spans: Vec, -} - -#[derive(Debug, Clone)] -pub struct BuiltinUsed { - pub src_name: Ident, - pub builtin_ty: &'static str, -} - -impl Default for VisitCtx { - fn default() -> Self { - Self { - declare_objs: <_>::default(), - states: <_>::default(), - current_used_info: Default::default(), - used_objs: Default::default(), - analyze_stack: vec![vec![]], - has_guards_data: false, - visit_error_occur: false, - replace_ident: None, - } - } -} - -impl VisitMut for VisitCtx { - fn visit_expr_mut(&mut self, expr: &mut Expr) { - match expr { - Expr::Macro(m) => { - let mac = &m.mac; - if mac.path.is_ident(WIDGET_MACRO_NAME) { - *expr = Expr::Verbatim(gen_widget_macro(mac.tokens.clone().into(), Some(self)).into()); - } else if mac.path.is_ident(WATCH_MACRO_NAME) { - *expr = Expr::Verbatim(gen_watch_macro(mac.tokens.clone(), self).into()); - } else if mac.path.is_ident(PROP_MACRO_NAME) { - *expr = Expr::Verbatim(gen_prop_macro(mac.tokens.clone().into(), self).into()); - } else if mac.path.is_ident(MOVE_TO_WIDGET_MACRO_NAME) { - *expr = Expr::Verbatim(gen_move_to_widget_macro(&mac.tokens, self)); - } else if mac.path.is_ident(LET_WATCH_MACRO_NAME) { - let mut tokens = quote! {}; - DeclareError::LetWatchWrongPlace(mac.span().unwrap()).to_compile_error(&mut tokens); - *expr = Expr::Verbatim(tokens); - self.visit_error_occur = true; - } else if mac.path.is_ident(ASSIGN_WATCH_MACRO_NAME) { - *expr = Expr::Verbatim(gen_assign_watch(mac.tokens.clone(), self).into()); - } else if mac.path.is_ident(NO_WATCH_MACRO_NAME) { - *expr = Expr::Verbatim(gen_no_watch_macro(mac.tokens.clone(), self).into()); - } else { - visit_mut::visit_expr_macro_mut(self, m); - } - } - Expr::Path(p) => { - visit_mut::visit_expr_path_mut(self, p); - if let Some(name) = p.path.get_ident() { - if let Some(name) = self.find_named_obj(name).cloned() { - self.add_used_widget(name, None, UsedType::USED) - } else if let Some((from, to)) = self.replace_ident.as_ref() { - if name == from && self.get_local_var(name).is_none() { - let mut instead = to.clone(); - instead.set_span(name.span()); - p.path = instead.into(); - } - } - } - } - Expr::Closure(c) => { - let mut new_closure = None; - let is_capture = c.capture.is_some(); - let mut ctx = self.stack_push(); - ctx.new_scope_visit( - |ctx| { - visit_mut::visit_expr_closure_mut(ctx, c); - new_closure = closure_surround_refs(&ctx.current_used_info, c); - }, - |scope| { - scope.iter_mut().for_each(|(_, info)| { - info.used_type |= UsedType::SCOPE_CAPTURE; - // move capture closure will generate state reference by itself. - if is_capture { - info.used_type.remove(UsedType::REF); - } - }); - }, - ); - if let Some(new) = new_closure { - *expr = parse_quote!(#new); - } - } - _ => { - visit_mut::visit_expr_mut(self, expr); - } - } - } - - fn visit_stmt_mut(&mut self, stmt: &mut Stmt) { - match stmt { - Stmt::Item(syn::Item::Macro(ItemMacro { ident: None, mac, .. })) => { - if mac.path.is_ident(WIDGET_MACRO_NAME) { - let expr: TokenStream = gen_widget_macro(mac.tokens.clone().into(), Some(self)).into(); - *stmt = Stmt::Expr(Expr::Verbatim(expr)); - } else if mac.path.is_ident(WATCH_MACRO_NAME) { - let t = gen_watch_macro(mac.tokens.clone(), self); - *stmt = Stmt::Expr(Expr::Verbatim(t.into())); - } else if mac.path.is_ident(PROP_MACRO_NAME) { - *stmt = Stmt::Expr(Expr::Verbatim( - gen_prop_macro(mac.tokens.clone().into(), self).into(), - )); - } else if mac.path.is_ident(MOVE_TO_WIDGET_MACRO_NAME) { - *stmt = Stmt::Expr(Expr::Verbatim(gen_move_to_widget_macro(&mac.tokens, self))); - } else if mac.path.is_ident(LET_WATCH_MACRO_NAME) { - let mut tokens = quote! {}; - DeclareError::LetWatchWrongPlace(mac.span().unwrap()).to_compile_error(&mut tokens); - self.visit_error_occur = true; - *stmt = Stmt::Expr(Expr::Verbatim(tokens)); - } else if mac.path.is_ident(ASSIGN_WATCH_MACRO_NAME) { - *stmt = Stmt::Expr(Expr::Verbatim( - gen_assign_watch(mac.tokens.clone(), self).into(), - )); - } else if mac.path.is_ident(NO_WATCH_MACRO_NAME) { - *stmt = Stmt::Expr(Expr::Verbatim( - gen_no_watch_macro(mac.tokens.clone(), self).into(), - )); - } - } - Stmt::Expr(expr) => { - if let Some(new_stmt) = self.let_watch_desugar(expr, None) { - *stmt = new_stmt; - } - } - Stmt::Semi(expr, semi) => { - if let Some(new_stmt) = self.let_watch_desugar(expr, Some(*semi)) { - *stmt = new_stmt; - } - } - _ => {} - } - visit_mut::visit_stmt_mut(self, stmt); - } - - fn visit_expr_field_mut(&mut self, f_expr: &mut syn::ExprField) { - if let Member::Named(member) = &f_expr.member { - if let Some(builtin_ty) = WIDGET_OF_BUILTIN_FIELD.get(member.to_string().as_str()) { - let span = f_expr.span(); - if self.visit_builtin_in_expr(&mut f_expr.base, span, builtin_ty) { - return; - } - } - } - - visit_mut::visit_expr_field_mut(self, f_expr); - } - - fn visit_expr_method_call_mut(&mut self, i: &mut ExprMethodCall) { - if let Some(builtin_ty) = WIDGET_OF_BUILTIN_METHOD.get(i.method.to_string().as_str()) { - let span = i.span(); - if self.visit_builtin_in_expr(&mut i.receiver, span, builtin_ty) { - return; - } - } - - visit_mut::visit_expr_method_call_mut(self, i); - } - - fn visit_expr_assign_mut(&mut self, assign: &mut syn::ExprAssign) { - self.recursive_visit_assign_mut(&mut assign.left, &mut assign.right); - } - - fn visit_block_mut(&mut self, i: &mut syn::Block) { - let mut ctx = self.stack_push(); - visit_mut::visit_block_mut(&mut *ctx, i); - } - - fn visit_item_const_mut(&mut self, i: &mut syn::ItemConst) { - visit_mut::visit_item_const_mut(self, i); - - self.analyze_stack.last_mut().unwrap().push(LocalVariable { - name: i.ident.clone(), - alias_of_name: None, - }); - } - - fn visit_local_mut(&mut self, local: &mut syn::Local) { - self.recursive_visit_local_mut( - &mut local.pat, - local.init.as_mut().map(|(_, init)| &mut **init), - ); - } - - fn visit_expr_block_mut(&mut self, i: &mut syn::ExprBlock) { - let mut ctx = self.stack_push(); - visit_mut::visit_expr_block_mut(&mut *ctx, i); - } - - fn visit_expr_for_loop_mut(&mut self, i: &mut syn::ExprForLoop) { - let mut ctx = self.stack_push(); - visit_mut::visit_expr_for_loop_mut(&mut *ctx, i); - } - - fn visit_expr_loop_mut(&mut self, i: &mut syn::ExprLoop) { - let mut ctx = self.stack_push(); - visit_mut::visit_expr_loop_mut(&mut *ctx, i); - } - - fn visit_expr_if_mut(&mut self, i: &mut syn::ExprIf) { - let mut ctx = self.stack_push(); - visit_mut::visit_expr_if_mut(&mut *ctx, i); - } - - fn visit_arm_mut(&mut self, i: &mut syn::Arm) { - let mut ctx = self.stack_push(); - visit_mut::visit_arm_mut(&mut *ctx, i); - } - - fn visit_expr_unsafe_mut(&mut self, i: &mut syn::ExprUnsafe) { - let mut ctx = self.stack_push(); - visit_mut::visit_expr_unsafe_mut(&mut *ctx, i); - } - - fn visit_expr_while_mut(&mut self, i: &mut syn::ExprWhile) { - let mut ctx = self.stack_push(); - visit_mut::visit_expr_while_mut(&mut *ctx, i); - } - - fn visit_pat_ident_mut(&mut self, i: &mut syn::PatIdent) { - visit_mut::visit_pat_ident_mut(self, i); - - self - .analyze_stack - .last_mut() - .unwrap_or_else(|| { - panic!( - "Crash when visit `{}`, stack should not be empty, at {}:{}:{}", - quote! { #i }, - file!(), - line!(), - column!() - ) - }) - .push(LocalVariable::local(i.ident.clone())); - } -} - -impl VisitCtx { - fn let_watch_desugar(&mut self, expr: &mut Expr, semi: Option) -> Option { - fn let_watch_as_watch(expr: &mut Expr) -> bool { - let Expr::MethodCall(method_call) = expr else {return false;}; - if let Expr::Macro(mac) = &mut *method_call.receiver { - let path = &mut mac.mac.path; - if path.is_ident(LET_WATCH_MACRO_NAME) { - let watch = Ident::new(WATCH_MACRO_NAME, path.span()); - *path = watch.into(); - return true; - } - } - let_watch_as_watch(&mut method_call.receiver) - } - - let_watch_as_watch(expr).then(|| { - let guard = guard_ident(expr.span()); - - let move_to_widget = Ident::new(MOVE_TO_WIDGET_MACRO_NAME, expr.span()); - let res = syn::parse2::(quote_spanned! { expr.span() =>{ - let #guard = #expr #semi - #move_to_widget!(#guard.unsubscribe_when_dropped()); - }}); - - res.unwrap_or_else(|err| { - self.visit_error_occur = true; - let tokens = err.into_compile_error(); - Stmt::Expr(Expr::Verbatim(tokens)) - }) - }) - } - - pub fn visit_desugared_syntax_mut(&mut self, desugar: &mut Desugared) { - desugar.named_objs.objs_mut().for_each(|obj| match obj { - NamedObj::Host(obj) | NamedObj::Builtin { obj, .. } => self.visit_declare_obj_mut(obj, false), - }); - - self.take_current_used_info(); - - self.visit_widget_node_mut(desugar.widget.as_mut().unwrap()); - if let Some(finally) = desugar.finally.as_mut() { - self.visit_finally_mut(finally); - } - } - - pub fn visit_init_stmts_mut(&mut self, init: &mut InitStmts) { - if let Some(ctx_name) = init.ctx_name.as_ref() { - self.replace_ident = Some((ctx_name.clone(), ctx_ident())); - } - init - .stmts - .iter_mut() - .for_each(|stmt| self.visit_stmt_mut(stmt)); - init.used_name_info = self.take_current_used_info(); - self.replace_ident.take(); - } - - pub fn visit_finally_mut(&mut self, finally: &mut FinallyBlock) { - finally.stmts.iter_mut().for_each(|stmt| match stmt { - FinallyStmt::Stmt(s) => self.visit_stmt_mut(s), - FinallyStmt::Obj(o) => self.visit_declare_obj_mut(o, false), - }); - finally.used_name_info = self.take_current_used_info(); - } - - pub fn visit_declare_obj_mut(&mut self, obj: &mut DeclareObj, value_obj: bool) { - let DeclareObj { ty, name, fields, watch_stmts, .. } = obj; - self.new_scope_visit( - |ctx| { - ctx.visit_path_mut(ty); - fields.iter_mut().for_each(|f| { - ctx.new_scope_visit( - |ctx| match &mut f.value { - FieldValue::Expr(expr) => { - ctx.visit_expr_as_value_mut(expr); - - if expr.used_name_info.subscribe_widget().is_some() { - ctx.take_current_used_info(); - - let field_value = ribir_suffix_variable(&f.member, "value"); - let mut field_fn: ExprClosure = - parse_quote_spanned! { expr.span() => move || #expr }; - let body_used = expr.used_name_info.clone(); - let field_fn = closure_surround_refs(&body_used, &mut field_fn); - let pre_def = quote_spanned! { expr.span() => - let mut #field_value = DeclareFieldValue::new(#field_fn); - }; - - expr.expr = parse_quote_spanned! {expr.span() => #field_value.value()}; - ctx.has_guards_data = true; - // DynWidget is a special object, it's both require data and framework change to - // update its children. When user call `.silent()` means no - // need relayout and redraw the widget. `DynWidget` as the directly subscriber - // also needn't to change. - let upstream = if is_dyn_widget(ty) && f.member == "dyns" { - let mut upstream = expr.used_name_info.upstream_modifies_tokens(true).unwrap(); - upstream.extend(quote_spanned! { - f.member.span() => .filter(|s| s.contains(ModifyScope::FRAMEWORK)) - }); - upstream - } else { - expr.used_name_info.upstream_modifies_tokens(false).unwrap() - }; - let guards = guard_vec_ident(); - let declare_set = declare_field_name(&f.member); - let watch_update = parse_quote_spanned! { expr.span() => - #guards.push(AnonymousData::new(Box::new( - #upstream - .subscribe({ - let #name = #name.clone_stateful(); - move |_| #name.state_ref().#declare_set(#field_value.value()) - }) - .unsubscribe_when_dropped() - ))); - }; - watch_stmts.push(WatchField { pre_def, watch_update }); - } - } - FieldValue::Observable(expr) => { - ctx.visit_track_expr_mut(expr); - ctx.take_current_used_info(); - - let field_subject = ribir_suffix_variable(&f.member, "subject"); - let field_value = ribir_suffix_variable(&f.member, "init"); - let pre_def = quote_spanned! { expr.span() => - let (#field_value, #field_subject) = Pipe::unzip(#expr); - }; - expr.expr = parse_quote_spanned! { expr.span() => #field_value }; - let guards = guard_vec_ident(); - let declare_set = declare_field_name(&f.member); - let watch_update = parse_quote_spanned! { expr.span() => - #guards.push(AnonymousData::new(Box::new( - #field_subject - .subscribe({ - let #name = #name.clone_stateful(); - move |v| #name.state_ref().#declare_set(v) - }) - .unsubscribe_when_dropped() - ))); - }; - watch_stmts.push(WatchField { pre_def, watch_update }); - ctx.has_guards_data = true; - } - FieldValue::Obj(obj) => { - ctx.visit_declare_obj_mut(obj, true); - } - }, - |_| {}, - ) - }); - - if !value_obj { - obj.used_name_info = ctx.take_current_used_info(); - } - }, - |_| {}, - ); - } - - pub fn visit_track_expr_mut(&mut self, expr: &mut TrackExpr) { - self.new_scope_visit( - |ctx| { - ctx.visit_expr_mut(&mut expr.expr); - expr.used_name_info = ctx.current_used_info.clone(); - }, - |_| {}, - ); - } - - pub fn visit_expr_as_value_mut(&mut self, expr: &mut TrackExpr) { - let closure_as_value = is_pure_closure(&expr.expr); - if closure_as_value { - self.new_scope_visit( - |ctx| { - ctx.visit_expr_mut(&mut expr.expr); - // when a closure directly as value, needn't subscribe it, because no - // matter how many states the closure body used, they are only effects the logic - // of the closure. - ctx - .current_used_info - .iter_mut() - .for_each(|(_, info)| info.used_type.remove(UsedType::SUBSCRIBE)); - - expr.used_name_info = ctx.current_used_info.clone(); - }, - |_| {}, - ); - } else { - self.visit_track_expr_mut(expr); - } - } - - pub fn visit_widget_node_mut(&mut self, widget: &mut WidgetNode) { - let WidgetNode { node: parent, children } = widget; - self.visit_compose_item_mut(parent); - children - .iter_mut() - .for_each(|node| self.visit_widget_node_mut(node)); - } - - pub fn visit_compose_item_mut(&mut self, widget: &mut ComposeItem) { - match widget { - ComposeItem::ChainObjs(objs) => objs - .iter_mut() - .for_each(|obj| self.visit_declare_obj_mut(obj, false)), - ComposeItem::Id(_) => {} - } - } - - pub fn take_current_used_info(&mut self) -> ScopeUsedInfo { self.current_used_info.take() } - - pub fn stack_push(&mut self) -> StackGuard<'_> { StackGuard::new(self) } - - // return the name of widget that `ident` point to if it's have. - pub fn find_named_obj<'a>(&'a self, ident: &'a Ident) -> Option<&'a Ident> { - self - .get_local_var(ident) - .map(|v| v.alias_of_name.as_ref()) - .unwrap_or_else(|| { - (self.declare_objs.contains_key(ident) || self.states.contains(ident)).then_some(ident) - }) - } - - fn get_local_var(&self, name: &Ident) -> Option<&LocalVariable> { - self - .analyze_stack - .iter() - .rev() - .flat_map(|local| local.iter().rev()) - .find(|v| &v.name == name) - } - - fn path_as_named_obj(&self, expr: &ExprPath) -> Option { - expr - .path - .get_ident() - .and_then(|name| self.find_named_obj(name)) - .cloned() - } - - fn recursive_visit_assign_mut(&mut self, left: &mut Expr, right: &mut Expr) { - match (left, right) { - (Expr::Path(l), Expr::Path(r)) => { - if let (Some(l), Some(r)) = (self.path_as_named_obj(l), r.path.get_ident()) { - let local_var = self - .analyze_stack - .iter_mut() - .rev() - .flat_map(|locals| locals.iter_mut().rev()) - .find(|v| v.name == l); - if let Some(local_var) = local_var { - local_var.alias_of_name = Some(r.clone()); - } - } - } - (Expr::Tuple(l), Expr::Tuple(r)) => { - l.elems - .iter_mut() - .zip(r.elems.iter_mut()) - .for_each(|(l, r)| self.recursive_visit_assign_mut(l, r)); - } - (left, right) => { - self.visit_expr_mut(left); - self.visit_expr_mut(right); - } - } - } - - fn recursive_visit_local_mut(&mut self, left: &mut syn::Pat, right: Option<&mut Expr>) { - match (left, right) { - (syn::Pat::Ident(i), Some(Expr::Path(path))) => { - let name = i.ident.clone(); - let var = if let Some(right) = self.path_as_named_obj(path) { - LocalVariable { name, alias_of_name: Some(right) } - } else { - LocalVariable::local(name) - }; - self.analyze_stack.last_mut().unwrap().push(var); - } - (syn::Pat::Tuple(left), Some(Expr::Tuple(right))) => { - left - .elems - .iter_mut() - .zip(right.elems.iter_mut()) - .for_each(|(l, r)| self.recursive_visit_local_mut(l, Some(r))); - } - (left, right) => { - if let Some(right) = right { - self.visit_expr_mut(right); - } - self.visit_pat_mut(left); - } - } - } - - pub fn add_used_widget( - &mut self, - name: Ident, - builtin: Option, - used_type: UsedType, - ) { - self.inner_add_used_obj(name.clone(), builtin); - self.current_used_info.add_used(name, used_type); - } - - fn inner_add_used_obj(&mut self, name: Ident, builtin: Option) { - let span = name.span().unwrap(); - self - .used_objs - .entry(name) - .and_modify(|info| { - info.spans.push(span); - }) - .or_insert_with(|| UsedInfo { builtin, spans: vec![span] }); - } - - pub fn visit_builtin_in_expr( - &mut self, - expr: &mut syn::Expr, - span: proc_macro2::Span, - builtin_ty: &'static str, - ) -> bool { - let path = match expr { - Expr::Path(syn::ExprPath { path, .. }) => path, - Expr::MethodCall(ExprMethodCall { receiver, method, args, .. }) - if args.is_empty() && (method == "shallow" || method == "silent") => - { - if let Expr::Path(syn::ExprPath { path, .. }) = &mut **receiver { - path - } else { - return false; - } - } - _ => return true, - }; - let Some(name) = path.get_ident() else { return false }; - - if let Some(builtin_name) = self.visit_builtin_name_mut(name, span, builtin_ty) { - *path = parse_quote! { #builtin_name }; - true - } else { - false - } - } - - pub fn visit_builtin_name_mut( - &mut self, - host: &Ident, - span: proc_macro2::Span, - builtin_ty: &'static str, - ) -> Option { - let name = self.find_named_obj(host)?; - - let ty = self.declare_objs.get(name)?; - - if !ty.is_ident(builtin_ty) { - let builtin_name = builtin_var_name(name, span, builtin_ty); - let src_name = name.clone(); - self.add_used_widget( - builtin_name.clone(), - Some(BuiltinUsed { src_name, builtin_ty }), - UsedType::USED, - ); - Some(builtin_name) - } else { - None - } - } - - pub(crate) fn new_scope_visit( - &mut self, - visiter: impl FnOnce(&mut Self), - update_used_type: impl Fn(&mut ScopeUsedInfo), - ) { - let mut outside_used = self.current_used_info.take(); - visiter(self); - update_used_type(&mut self.current_used_info); - outside_used.merge(&self.current_used_info); - self.current_used_info = outside_used; - } -} - -#[must_use] -pub(crate) fn closure_surround_refs( - body_used: &ScopeUsedInfo, - c: &mut ExprClosure, -) -> Option { - c.capture?; - let all_capture = body_used.all_used()?; - - let mut tokens = quote!(); - Brace(c.span()).surround(&mut tokens, |tokens| { - all_capture.for_each(|obj| capture_widget(obj).to_tokens(tokens)); - if body_used.ref_widgets().is_some() { - let mut refs = quote! {}; - body_used.state_refs_tokens(&mut refs); - let body = &mut *c.body; - if let Expr::Block(block) = body { - block - .block - .stmts - .insert(0, Stmt::Expr(Expr::Verbatim(refs))); - } else { - *body = parse_quote_spanned!(body.span() => { #refs #body }); - } - } - c.to_tokens(tokens); - }); - Some(tokens) -} -pub struct StackGuard<'a> { - ctx: &'a mut VisitCtx, -} - -pub struct CaptureScopeGuard<'a> { - ctx: &'a mut VisitCtx, -} - -impl<'a> StackGuard<'a> { - pub fn new(ctx: &'a mut VisitCtx) -> Self { - ctx.analyze_stack.push(vec![]); - StackGuard { ctx } - } -} - -impl<'a> Drop for StackGuard<'a> { - fn drop(&mut self) { self.ctx.analyze_stack.pop(); } -} - -impl<'a> std::ops::Deref for StackGuard<'a> { - type Target = VisitCtx; - - fn deref(&self) -> &Self::Target { self.ctx } -} - -impl<'a> std::ops::DerefMut for StackGuard<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { self.ctx } -} - -impl<'a> std::ops::Deref for CaptureScopeGuard<'a> { - type Target = VisitCtx; - - fn deref(&self) -> &Self::Target { self.ctx } -} - -impl<'a> std::ops::DerefMut for CaptureScopeGuard<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { self.ctx } -} - -fn is_pure_closure(expr: &Expr) -> bool { - match expr { - Expr::Closure(_) => true, - Expr::Block(b) if b.block.stmts.len() == 1 => { - let stmt = &b.block.stmts[0]; - matches!(stmt, Stmt::Expr(expr) if is_pure_closure(expr)) - } - _ => false, - } -} - -#[test] -fn local_var() { - let mut ctx = VisitCtx::default(); - let v = Ident::new("v", proc_macro2::Span::call_site()); - ctx - .analyze_stack - .last_mut() - .unwrap() - .push(LocalVariable::local(v.clone())); - ctx.declare_objs.insert(v.clone(), v.clone().into()); - - assert!(ctx.find_named_obj(&v).is_none()); -} diff --git a/ribir/tests/timer_test.rs b/ribir/tests/timer_test.rs index befee4648..f56455d04 100644 --- a/ribir/tests/timer_test.rs +++ b/ribir/tests/timer_test.rs @@ -6,6 +6,7 @@ mod test_single_thread { use ribir_core::reset_test_env; use ribir_core::test_helper::TestWindow; use ribir_dev_helper::*; + use std::cell::Cell; use std::{cell::RefCell, rc::Rc}; use std::{thread::sleep, time::Duration}; use winit::event::{DeviceId, ElementState, ModifiersState, MouseButton, WindowEvent}; @@ -13,16 +14,12 @@ mod test_single_thread { use ribir_core::{prelude::*, test_helper::MockBox}; pub fn test_widget_with_timer() { - let w = widget! { - MockBox { - id: c, - size: Size::new(20., 20.) - } - finally { - observable::of(Size::new(10., 10.)) - .delay(Duration::from_millis(10), AppCtx::scheduler()) - .subscribe(move |v| c.size = v); - } + let w = fn_widget! { + let c = @MockBox { size: Size::new(20., 20.) }; + observable::of(Size::new(10., 10.)) + .delay(Duration::from_millis(10), AppCtx::scheduler()) + .subscribe(move |v| $c.write().size = v); + c }; let mut wnd = TestWindow::new(w); @@ -44,14 +41,14 @@ mod test_single_thread { assert_layout_result_by_path!(wnd, {path = [0], width == 10., height == 10.,}); } - fn env(times: usize) -> (TestWindow, Rc>) { + fn env(times: usize) -> (TestWindow, Rc>) { let size = Size::new(400., 400.); - let count = Rc::new(RefCell::new(0)); + let count = Rc::new(Cell::new(0)); let c_count = count.clone(); - let w = widget! { - MockBox { + let w = fn_widget! { + @MockBox { size, - on_x_times_tap: (times, move |_| *c_count.borrow_mut() += 1) + on_x_times_tap: (times, move |_| c_count.set(c_count.get() + 1)) } }; let mut wnd = TestWindow::new_with_size(w, size); @@ -101,7 +98,7 @@ mod test_single_thread { }); run_until(&wnd, || *is_complete2.borrow()); - assert_eq!(*count.borrow(), 2); + assert_eq!(count.get(), 2); let (wnd, count) = env(2); let c_wnd = wnd.clone(); @@ -128,7 +125,7 @@ mod test_single_thread { }); run_until(&wnd, || *is_complete2.borrow()); - assert_eq!(*count.borrow(), 0); + assert_eq!(count.get(), 0); } pub fn test_tripe_tap() { @@ -160,7 +157,7 @@ mod test_single_thread { run_until(&wnd, || *is_complete2.borrow()); - assert_eq!(*count.borrow(), 2); + assert_eq!(count.get(), 2); } } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index fd37439f8..94f0d3010 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -18,12 +18,11 @@ paste.workspace = true ribir = {path = "../ribir", features = ["material", "widgets"]} ribir_dev_helper = {path = "../dev-helper"} ribir_geom = {path = "../geom"} -trybuild.workspace = true winit.workspace = true [[test]] -name = "compile_message" -path = "compile_message.rs" +name = "include_svg" +path = "include_svg_test.rs" [[test]] name = "rdl_macro_test" diff --git a/tests/child_template_derive_test.rs b/tests/child_template_derive_test.rs index 50b244a7f..cdca82135 100644 --- a/tests/child_template_derive_test.rs +++ b/tests/child_template_derive_test.rs @@ -1,6 +1,6 @@ use ribir::prelude::*; -#[derive(Declare, Declare2)] +#[derive(Declare2)] struct P; struct ChildA; @@ -20,7 +20,7 @@ impl ComposeChild for P { fn compose_child(_: State, _: Self::Child) -> Widget { Void.into() } } -#[derive(Declare, Declare2)] +#[derive(Declare2)] struct P2; #[derive(Template)] @@ -32,7 +32,7 @@ impl ComposeChild for P2 { fn compose_child(_: State, _: Self::Child) -> Widget { Void.into() } } -#[derive(Declare, Declare2)] +#[derive(Declare2)] struct P3; #[derive(Template)] diff --git a/tests/compile_msg/declare/circular_dependency_fail.rs b/tests/compile_msg/declare/circular_dependency_fail.rs deleted file mode 100644 index 4154f1292..000000000 --- a/tests/compile_msg/declare/circular_dependency_fail.rs +++ /dev/null @@ -1,33 +0,0 @@ -use ribir::prelude::*; - -fn main() { - let _circular_dependency_err = widget! { - Flex { - SizedBox { - id: id1, - size: id2.size, - } - SizedBox { - id: id2, - size: id3.size, - } - SizedBox { - id: id3, - size: id1.size, - } - } - }; - - let _wrap_widget_circular_err = widget! { - SizedBox { - id: parent, - size: Size::zero(), - margin: child.margin.clone(), - SizedBox{ - id: child, - size: Size::zero(), - margin: parent.margin.clone(), - } - } - }; -} diff --git a/tests/compile_msg/declare/circular_dependency_fail.stderr b/tests/compile_msg/declare/circular_dependency_fail.stderr deleted file mode 100644 index 3527cf434..000000000 --- a/tests/compile_msg/declare/circular_dependency_fail.stderr +++ /dev/null @@ -1,78 +0,0 @@ -error: There is a directly circle depends exist, this will cause infinite loop: id3.size ~> id1 , id2.size ~> id3 , id1.size ~> id2 - --> compile_msg/declare/circular_dependency_fail.rs:7:13 - | -7 | id: id1, - | ^^^ -8 | size: id2.size, - | ^^^^ ^^^ -... -11 | id: id2, - | ^^^ -12 | size: id3.size, - | ^^^^ ^^^ -... -15 | id: id3, - | ^^^ -16 | size: id1.size, - | ^^^^ ^^^ - | -note: You should manual watch expression and add operator to break the circular, then debounce subscribe it avoid to mut borrow panic. For example - - ``` - let_watch!(...) - .distinct_until_changed() - .debounce(Duration::ZERO, ctx.wnd_ctx().frame_scheduler()) - .subscribe(...) - ``` - --> compile_msg/declare/circular_dependency_fail.rs:8:9 - | -8 | size: id2.size, - | ^^^^ -... -12 | size: id3.size, - | ^^^^ -... -16 | size: id1.size, - | ^^^^ - -error: There is a directly circle depends exist, this will cause infinite loop: parent.margin ~> child , child.margin ~> parent - --> compile_msg/declare/circular_dependency_fail.rs:23:11 - | -23 | id: parent, - | ^^^^^^ -24 | size: Size::zero(), -25 | margin: child.margin.clone(), - | ^^^^^^ ^^^^^^^^^^^^ -26 | SizedBox{ -27 | id: child, - | ^^^^^ -28 | size: Size::zero(), -29 | margin: parent.margin.clone(), - | ^^^^^^ ^^^^^^^^^^^^^ - | -note: You should manual watch expression and add operator to break the circular, then debounce subscribe it avoid to mut borrow panic. For example - - ``` - let_watch!(...) - .distinct_until_changed() - .debounce(Duration::ZERO, ctx.wnd_ctx().frame_scheduler()) - .subscribe(...) - ``` - --> compile_msg/declare/circular_dependency_fail.rs:25:7 - | -25 | margin: child.margin.clone(), - | ^^^^^^ -... -29 | margin: parent.margin.clone(), - | ^^^^^^ - -error[E0282]: type annotations needed - --> compile_msg/declare/circular_dependency_fail.rs:4:7 - | -4 | let _circular_dependency_err = widget! { - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider giving `_circular_dependency_err` an explicit type - | -4 | let _circular_dependency_err: /* Type */ = widget! { - | ++++++++++++ diff --git a/tests/compile_msg/declare/declare_syntax_pass.rs b/tests/compile_msg/declare/declare_syntax_pass.rs deleted file mode 100644 index 788f604ba..000000000 --- a/tests/compile_msg/declare/declare_syntax_pass.rs +++ /dev/null @@ -1,67 +0,0 @@ -use ribir::prelude::*; - -fn main() { - let _ref_parent = widget! { - SizedBox { - id: size_box, - size: Size::new(50., 50.), - SizedBox { - size: size_box.size, - } - } - }; - - let _ref_child = widget! { - SizedBox { - size: child_box.size, - SizedBox { - id: child_box, - size: Size::new(50., 50.), - } - } - }; - - let _ref_sibling = widget! { - Flex { - SizedBox { - size: size2.size, - } - SizedBox { - id: size2, - size: size3.size, - } - SizedBox { - id: size3, - size: Size::new(1., 1.), - } - } - }; - - let _temp_var_name_not_conflict = widget! { - Flex { - SizedBox { - id: c0, - size: w.size, - } - SizedBox { - id: w, - size: Size::new(500., 500.), - } - SizedBox { - size: c0.size, - } - } - }; - - let _wrap_widget_effect_order = widget! { - SizedBox { - size: Size::zero(), - margin: child.margin.clone(), - SizedBox{ - id: child, - size: Size::zero(), - margin: EdgeInsets::all(1.), - } - } - }; -} diff --git a/tests/compile_msg/declare/dollar_position_fail.rs b/tests/compile_msg/declare/dollar_position_fail.rs deleted file mode 100644 index de662418b..000000000 --- a/tests/compile_msg/declare/dollar_position_fail.rs +++ /dev/null @@ -1,11 +0,0 @@ -use ribir::prelude::*; - -fn main() { - let not_identify_after_dollar = fn_widget! { - rdl! { Row { x: $1 } } - }; - - let field_name_not_support_dollar = fn_widget! { - rdl! { Row { $x: 1} } - }; -} diff --git a/tests/compile_msg/declare/dollar_position_fail.stderr b/tests/compile_msg/declare/dollar_position_fail.stderr deleted file mode 100644 index 24b0e8a28..000000000 --- a/tests/compile_msg/declare/dollar_position_fail.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: Syntax error: expected an identifier after `$` - --> compile_msg/declare/dollar_position_fail.rs:5:22 - | -5 | rdl! { Row { x: $1 } } - | ^ - -error: expected `,` - --> compile_msg/declare/dollar_position_fail.rs:8:39 - | -8 | let field_name_not_support_dollar = fn_widget! { - | _______________________________________^ -9 | | rdl! { Row { $x: 1} } -10 | | }; - | |___^ - | - = note: this error originates in the macro `fn_widget` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/compile_msg/declare/fix_attr_indirect_follow_host_fail.rs b/tests/compile_msg/declare/fix_attr_indirect_follow_host_fail.rs deleted file mode 100644 index f506c2e16..000000000 --- a/tests/compile_msg/declare/fix_attr_indirect_follow_host_fail.rs +++ /dev/null @@ -1,33 +0,0 @@ -use ribir::prelude::*; - -fn main() { - let _fix_builtin_indirect_follow_host_widget_pass = widget! { - SizedBox { - id: a, - size: Size::zero(), - cursor: b.cursor, - SizedBox { - id: b, - size: Size::zero(), - cursor: if a.size.area() > 0. { - CursorIcon::Hand - } else { - CursorIcon::Arrow - } , - } - } - }; - - let _fix_attr_indirect_follow_host_attr_fail = widget! { - SizedBox { - id: a, - size: Size::zero(), - cursor: b.cursor, - SizedBox { - id: b, - size: Size::zero(), - cursor: a.cursor - } - } - }; -} diff --git a/tests/compile_msg/declare/fix_attr_indirect_follow_host_fail.stderr b/tests/compile_msg/declare/fix_attr_indirect_follow_host_fail.stderr deleted file mode 100644 index 60bb4f97b..000000000 --- a/tests/compile_msg/declare/fix_attr_indirect_follow_host_fail.stderr +++ /dev/null @@ -1,41 +0,0 @@ -error: There is a directly circle depends exist, this will cause infinite loop: b.cursor ~> a , a.cursor ~> b - --> compile_msg/declare/fix_attr_indirect_follow_host_fail.rs:23:11 - | -23 | id: a, - | ^ -24 | size: Size::zero(), -25 | cursor: b.cursor, - | ^^^^^^ ^^^^^^^^ -26 | SizedBox { -27 | id: b, - | ^ -28 | size: Size::zero(), -29 | cursor: a.cursor - | ^^^^^^ ^^^^^^^^ - | -note: You should manual watch expression and add operator to break the circular, then debounce subscribe it avoid to mut borrow panic. For example - - ``` - let_watch!(...) - .distinct_until_changed() - .debounce(Duration::ZERO, ctx.wnd_ctx().frame_scheduler()) - .subscribe(...) - ``` - --> compile_msg/declare/fix_attr_indirect_follow_host_fail.rs:25:7 - | -25 | cursor: b.cursor, - | ^^^^^^ -... -29 | cursor: a.cursor - | ^^^^^^ - -error[E0282]: type annotations needed - --> compile_msg/declare/fix_attr_indirect_follow_host_fail.rs:21:7 - | -21 | let _fix_attr_indirect_follow_host_attr_fail = widget! { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider giving `_fix_attr_indirect_follow_host_attr_fail` an explicit type - | -21 | let _fix_attr_indirect_follow_host_attr_fail: /* Type */ = widget! { - | ++++++++++++ diff --git a/tests/compile_msg/declare/fix_children_of_nested_named_widget_miss_pass.rs b/tests/compile_msg/declare/fix_children_of_nested_named_widget_miss_pass.rs deleted file mode 100644 index 3e90a4796..000000000 --- a/tests/compile_msg/declare/fix_children_of_nested_named_widget_miss_pass.rs +++ /dev/null @@ -1,14 +0,0 @@ -use ribir::prelude::*; - -fn main() { - widget! { - SizedBox { - size: Size::zero(), - SizedBox { - id: _id1, - size: Size::zero(), - SizedBox { size: Size::zero() } - } - } - }; -} diff --git a/tests/compile_msg/declare/fix_decoration_ref_pass.rs b/tests/compile_msg/declare/fix_decoration_ref_pass.rs deleted file mode 100644 index dff73e9df..000000000 --- a/tests/compile_msg/declare/fix_decoration_ref_pass.rs +++ /dev/null @@ -1,16 +0,0 @@ -use ribir::prelude::*; - -fn main() { - let size = Size::zero(); - let _ = widget! { - SizedBox { - id: a, - size, - background: Color::RED, - SizedBox{ - size, - background: a.background.clone() - } - } - }; -} diff --git a/tests/compile_msg/declare/fix_follow_wrap_widget_pass.rs b/tests/compile_msg/declare/fix_follow_wrap_widget_pass.rs deleted file mode 100644 index 760293e4f..000000000 --- a/tests/compile_msg/declare/fix_follow_wrap_widget_pass.rs +++ /dev/null @@ -1,17 +0,0 @@ -use ribir::prelude::*; - -fn main() { - let _ = widget! { - Row { - id: _parent, - background: son.background.clone(), - margin: son.margin.clone(), - SizedBox { - id: son, - size: Size::new(100., 100.), - margin: EdgeInsets::all(1.), - background: Color::RED, - } - } - }; -} diff --git a/tests/compile_msg/declare/let_watch_desugar_syntax_fail.rs b/tests/compile_msg/declare/let_watch_desugar_syntax_fail.rs deleted file mode 100644 index bafe3ce64..000000000 --- a/tests/compile_msg/declare/let_watch_desugar_syntax_fail.rs +++ /dev/null @@ -1,10 +0,0 @@ -use ribir::prelude::*; - -fn main() { - let _ = widget!{ - SizedBox { id: sized_box, size: ZERO_SIZE } - finally { - let_watch!(sized_box.size).subscribe(|_| {}) - } - }; -} \ No newline at end of file diff --git a/tests/compile_msg/declare/let_watch_desugar_syntax_fail.stderr b/tests/compile_msg/declare/let_watch_desugar_syntax_fail.stderr deleted file mode 100644 index 19979ff94..000000000 --- a/tests/compile_msg/declare/let_watch_desugar_syntax_fail.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expected `;` - --> compile_msg/declare/let_watch_desugar_syntax_fail.rs:7:7 - | -7 | let_watch!(sized_box.size).subscribe(|_| {}) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/compile_msg/declare/multi_track_allow_pass.rs b/tests/compile_msg/declare/multi_track_allow_pass.rs deleted file mode 100644 index 3b6191c73..000000000 --- a/tests/compile_msg/declare/multi_track_allow_pass.rs +++ /dev/null @@ -1,15 +0,0 @@ -use ribir::prelude::*; - -fn main() { - let x = 1.; - let y = 1.; - - let _multi_track = widget! { - states { x: Stateful::new(x) } - states { y: Stateful::new(y) } - Void { - left_anchor: x.clone(), - top_anchor: y.clone(), - } - }; -} diff --git a/tests/compile_msg/declare/ref_attach_before_self_widget_pass.rs b/tests/compile_msg/declare/ref_attach_before_self_widget_pass.rs deleted file mode 100644 index e968789db..000000000 --- a/tests/compile_msg/declare/ref_attach_before_self_widget_pass.rs +++ /dev/null @@ -1,14 +0,0 @@ -use ribir::prelude::*; - -fn main() { - let size = Size::zero(); - let _use_id_declare_later = widget! { - Flex { - SizedBox { - size, - SizedBox{ id: grandson, size } - } - SizedBox { size: grandson.size } - } - }; -} diff --git a/tests/compile_msg/declare/widget_before_field_fail.rs b/tests/compile_msg/declare/widget_before_field_fail.rs deleted file mode 100644 index b28c561dc..000000000 --- a/tests/compile_msg/declare/widget_before_field_fail.rs +++ /dev/null @@ -1,11 +0,0 @@ -use ribir::prelude::*; - -fn main() { - let size = Size::zero(); - let _child_always_declare_behind_field = widget! { - SizedBox { - SizedBox { size } - size - } - }; -} diff --git a/tests/compile_msg/declare/widget_before_field_fail.stderr b/tests/compile_msg/declare/widget_before_field_fail.stderr deleted file mode 100644 index 66a227a65..000000000 --- a/tests/compile_msg/declare/widget_before_field_fail.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: Field should always declare before children. - --> compile_msg/declare/widget_before_field_fail.rs:8:7 - | -8 | size - | ^^^^ diff --git a/tests/compile_msg/declare_builder_fail.rs b/tests/compile_msg/declare_builder_fail.rs deleted file mode 100644 index 3241f5775..000000000 --- a/tests/compile_msg/declare_builder_fail.rs +++ /dev/null @@ -1,20 +0,0 @@ -use ribir::prelude::*; - -#[derive(Declare)] -struct ReservedNames { - margin: i32, -} - -#[derive(Declare)] -struct RenameReservedNames { - #[declare(rename = margin_data)] - margin: i32, -} - -#[derive(Declare)] -struct Converter { - #[declare(convert=into)] - x: Option, -} - -fn main() {} diff --git a/tests/compile_msg/declare_builder_fail.stderr b/tests/compile_msg/declare_builder_fail.stderr deleted file mode 100644 index de9f93a11..000000000 --- a/tests/compile_msg/declare_builder_fail.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: the identify `margin` is reserved to expand space around widget wrapped. - --> compile_msg/declare_builder_fail.rs:5:3 - | -5 | margin: i32, - | ^^^^^^ - | - = help: use `rename` meta to avoid the name conflict in `widget!` macro. - - #[declare(rename = xxx)] - margin : i32 diff --git a/tests/declare_builder_test.rs b/tests/declare_builder_test.rs index f55d3d45e..113a288d3 100644 --- a/tests/declare_builder_test.rs +++ b/tests/declare_builder_test.rs @@ -5,57 +5,58 @@ fn dummy_ctx() -> &'static BuildCtx<'static> { unsafe { std::mem::transmute(&0) #[test] fn declare_builder_smoke() { // empty struct - #[derive(Declare)] + #[derive(Declare2)] struct A; - let _: A = ADeclarer {}.build_declare(dummy_ctx()); - #[derive(Declare)] + let _: A = A::declare2_builder().build_declare(dummy_ctx()); + + #[derive(Declare2)] struct B { a: f32, b: i32, } - let b = ::declare_builder() + let b = ::declare2_builder() .a(1.) .b(1) .build_declare(dummy_ctx()); - assert_eq!(b.a, 1.); - assert_eq!(b.b, 1); + assert_eq!(b.read().a, 1.); + assert_eq!(b.read().b, 1); } #[test] #[should_panic = "Required field `T::_a` not set"] fn panic_if_miss_require_field() { - #[derive(Declare)] + #[derive(Declare2)] struct T { _a: f32, } - let _ = ::declare_builder().build_declare(dummy_ctx()); + let _ = ::declare2_builder().build_declare(dummy_ctx()); } #[test] fn default_field() { - #[derive(Declare)] + #[derive(Declare2)] struct DefaultDeclare { #[declare(default)] a: f32, } - let t = ::declare_builder().build_declare(dummy_ctx()); - assert_eq!(t.a, 0.); + let t = ::declare2_builder().build_declare(dummy_ctx()); + assert_eq!(t.read().a, 0.); } #[test] fn default_field_with_value() { - #[derive(Declare)] + #[derive(Declare2)] struct DefaultWithValue { #[declare(default = "hi!")] text: &'static str, } - let t = ::declare_builder().build_declare(dummy_ctx()); - assert_eq!(t.text, "hi!"); + let t = ::declare2_builder().build_declare(dummy_ctx()); + assert_eq!(t.read().text, "hi!"); } diff --git a/tests/compile_message.rs b/tests/include_svg_test.rs similarity index 50% rename from tests/compile_message.rs rename to tests/include_svg_test.rs index f88d8555b..e63a91416 100644 --- a/tests/compile_message.rs +++ b/tests/include_svg_test.rs @@ -1,12 +1,5 @@ use ribir::prelude::{include_svg, Svg}; -#[test] -fn compile_msg() { - let t = trybuild::TestCases::new(); - t.compile_fail("./compile_msg/**/*fail.rs"); - t.pass("./compile_msg/**/*pass.rs"); -} - #[test] fn include_svg() { let svg: Svg = include_svg!("./assets/test1.svg"); diff --git a/tests/rdl_macro_test.rs b/tests/rdl_macro_test.rs index c706d133f..3759ebef6 100644 --- a/tests/rdl_macro_test.rs +++ b/tests/rdl_macro_test.rs @@ -389,8 +389,8 @@ widget_layout_test!(at_embed_in_expression, width == 300., height == 100.,); fn declare_smoke() { reset_test_env!(); - let _ = widget! { - SizedBox { + let _ = fn_widget! { + @SizedBox { size: Size::new(500.,500.), background: Color::RED, } @@ -402,13 +402,14 @@ fn simple_ref_bind_work() { reset_test_env!(); let size = Size::new(100., 100.); - let w = widget! { - Flex { - SizedBox { - size: size2.size, - on_tap: move |_| size2.size *= 2., + let w = fn_widget! { + let size2 = @SizedBox { size }; + @Flex { + @SizedBox { + size: pipe!($size2.size), + on_tap: move |_| $size2.write().size *= 2., } - SizedBox { id: size2, size, } + @ { size2 } } }; @@ -428,13 +429,12 @@ fn event_attr_sugar_work() { reset_test_env!(); const BEFORE_SIZE: Size = Size::new(50., 50.); const AFTER_TAP_SIZE: Size = Size::new(100., 100.); - let w = widget! { - SizedBox { - id: sized_box, - size: BEFORE_SIZE, - SizedBox { - size: sized_box.size, - on_tap: move |_| sized_box.size = AFTER_TAP_SIZE, + let w = fn_widget! { + let sized_box = @SizedBox { size: BEFORE_SIZE }; + @$sized_box { + @SizedBox { + size: pipe!($sized_box.size), + on_tap: move |_| $sized_box.write().size = AFTER_TAP_SIZE, } } }; @@ -456,18 +456,18 @@ fn event_attr_sugar_work() { fn widget_wrap_bind_work() { reset_test_env!(); - let w = widget! { - Flex { - SizedBox { - id: sibling, - margin: EdgeInsets::all(1.0), - size: Size::new(50., 50.), - } - SizedBox { - margin: sibling.margin.clone(), - size: if sibling.margin.left > 1. { Size::zero() } else { sibling.size }, - on_tap: move |_| sibling.margin = EdgeInsets::all(5.), - } + let w = fn_widget! { + let mut sibling = @SizedBox { + margin: EdgeInsets::all(1.0), + size: Size::new(50., 50.), + }; + let next_box = @SizedBox { + margin: pipe!($sibling.margin), + size: pipe!(if $sibling.margin.left > 1. { Size::zero() } else { $sibling.size }), + on_tap: move |_| $sibling.write().margin = EdgeInsets::all(5.), + }; + @Flex { + @ { [sibling, next_box ] } } }; @@ -576,17 +576,15 @@ fn local_var_not_bind() -> Widget { const EXPECT_SIZE: Size = Size::new(5., 5.); const BE_CLIPPED_SIZE: Size = Size::new(500., 500.); - widget! { - SizedBox { + fn_widget! { + let _size_box = @SizedBox { size: BE_CLIPPED_SIZE }; + @SizedBox { size: { let _size_box = EXPECT_SIZE; let _size_box_def = EXPECT_SIZE; _size_box + _size_box_def }, - SizedBox { - id: _size_box, - size: BE_CLIPPED_SIZE, - } + @{ _size_box } } } .into() @@ -605,16 +603,17 @@ fn builtin_ref() { let icon_track = Rc::new(Cell::new(CursorIcon::default())); let c_icon_track = icon_track.clone(); - let w = widget! { - Flex { - cursor: tap_box.cursor.clone(), - SizedBox { - id: tap_box, - size: Size::new(5., 5.), - cursor: CursorIcon::Hand, + let w = fn_widget! { + let mut tap_box = @SizedBox { + size: Size::new(5., 5.), + cursor: CursorIcon::Hand, + }; + @Flex { + cursor: pipe!($tap_box.cursor), + @$tap_box { on_tap: move |_| { - tap_box.cursor = CursorIcon::AllScroll; - c_icon_track.set(tap_box.cursor); + $tap_box.write().cursor = CursorIcon::AllScroll; + c_icon_track.set($tap_box.cursor); } } } @@ -634,12 +633,11 @@ fn builtin_bind_to_self() { let icon_track = Rc::new(Cell::new(CursorIcon::default())); let c_icon_track = icon_track.clone(); - let w = widget! { - SizedBox { - id: sized_box, - size: Size::new(5., 5.), - cursor: { - let icon = if sized_box.size.area() < 100. { + let w = fn_widget! { + let sized_box = @SizedBox { size: Size::new(5., 5.) }; + @$sized_box { + cursor: pipe!{ + let icon = if $sized_box.size.area() < 100. { CursorIcon::Hand } else { CursorIcon::Help @@ -647,7 +645,7 @@ fn builtin_bind_to_self() { c_icon_track.set(icon); icon }, - on_tap: move |_| sized_box.size = Size::new(20.,20.), + on_tap: move |_| $sized_box.write().size = Size::new(20.,20.), } }; @@ -787,18 +785,17 @@ fn fix_subscribe_cancel_after_widget_drop() { } fn fix_local_assign_tuple() -> Widget { - widget! { - Row { - SizedBox { - id: _sized, - size: Size::new(1., 1.,), - } - SizedBox { - size: { - let (x, _) = (_sized, 2); - x.size - } + fn_widget! { + let _sized = @SizedBox { size: Size::new(1., 1.) }; + let sized_box2 = @SizedBox { + size: { + let (x, _) = ($_sized, 2); + x.size } + }; + @Row { + @ { _sized } + @ { sized_box2 } } } .into() @@ -840,9 +837,9 @@ fn no_watch() { reset_test_env!(); let size = Stateful::new(ZERO_SIZE); - let w = widget! { - states { size: size.clone_stateful() } - SizedBox { size: no_watch!(*size) } + let c_size = size.clone_reader(); + let w = fn_widget! { + @SizedBox { size: *$c_size } }; let mut wnd = TestWindow::new(w); diff --git a/themes/material/src/ripple.rs b/themes/material/src/ripple.rs index 36d881fcb..d0cc4a5ce 100644 --- a/themes/material/src/ripple.rs +++ b/themes/material/src/ripple.rs @@ -4,14 +4,14 @@ use ribir_widgets::prelude::*; /// Widget use to do ripple animate as a visual feedback to user interactive. /// Usually for touch and mouse. -#[derive(Declare, Debug, Declare2)] +#[derive(Debug, Declare2)] pub struct Ripple { /// The color of ripples. pub color: Color, /// The radius in pixels of foreground ripples when fully expanded. The /// default radius will be the distance from the center of the ripple to the /// furthest corner of the host bounding rectangle. - #[declare(default, convert=strip_option)] + #[declare(default)] pub radius: Option, /// Whether the ripple always originates from the center of the host bound. #[declare(default)] diff --git a/themes/material/src/state_layer.rs b/themes/material/src/state_layer.rs index e3059835f..ced155d79 100644 --- a/themes/material/src/state_layer.rs +++ b/themes/material/src/state_layer.rs @@ -4,7 +4,7 @@ use ribir_widgets::{layout::Stack, path::PathPaintKit}; /// Widget that as an visual indicator of material design used to present the /// interactive status of its child. -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct StateLayer { pub color: Color, pub path: Path, @@ -13,7 +13,7 @@ pub struct StateLayer { /// Widget that as visual indicator of material design used to communicate the /// status of interactive widget, its visual state will reactive to its child /// interactive state. -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct InteractiveLayer { /// the color of the state layer, will apply a fixed opacity in different /// state. diff --git a/widgets/src/avatar.rs b/widgets/src/avatar.rs index fa82d1afb..e5c2266a0 100644 --- a/widgets/src/avatar.rs +++ b/widgets/src/avatar.rs @@ -22,7 +22,7 @@ use ribir_core::prelude::*; /// } /// }; /// ``` -#[derive(Declare, Declare2, Default, Clone)] +#[derive(Declare2, Default, Clone)] pub struct Avatar { #[declare(default=Palette::of(ctx).primary())] pub color: Color, diff --git a/widgets/src/buttons.rs b/widgets/src/buttons.rs index 07c1f7ff1..a0535a3b8 100644 --- a/widgets/src/buttons.rs +++ b/widgets/src/buttons.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use ribir_core::prelude::*; -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct ButtonImpl { #[declare(default = 48.)] pub min_width: f32, @@ -12,13 +12,9 @@ pub struct ButtonImpl { pub icon_pos: IconPosition, pub label_style: CowArc, pub foreground_color: Brush, - #[declare(convert=strip_option)] pub background_color: Option, - #[declare(convert=strip_option)] pub radius: Option, - #[declare(convert=strip_option)] pub border_style: Option, - #[declare(convert=strip_option)] pub padding_style: Option, } diff --git a/widgets/src/buttons/button.rs b/widgets/src/buttons/button.rs index a3538fee7..35d10ed47 100644 --- a/widgets/src/buttons/button.rs +++ b/widgets/src/buttons/button.rs @@ -24,7 +24,7 @@ impl CustomStyle for ButtonStyle { } } -#[derive(Clone, Declare, Declare2)] +#[derive(Clone, Declare2)] pub struct ButtonDecorator { #[allow(unused)] pub button_type: ButtonType, @@ -62,7 +62,7 @@ impl ComposeDecorator for ButtonDecorator { /// } /// }; /// ``` -#[derive(Declare, Default, Declare2)] +#[derive(Default, Declare2)] pub struct Button { #[declare(default=Palette::of(ctx).primary())] color: Color, diff --git a/widgets/src/buttons/fab_button.rs b/widgets/src/buttons/fab_button.rs index 35e43133e..f8f789a87 100644 --- a/widgets/src/buttons/fab_button.rs +++ b/widgets/src/buttons/fab_button.rs @@ -26,7 +26,7 @@ impl CustomStyle for FabButtonStyle { } } -#[derive(Clone, Declare, Declare2)] +#[derive(Clone, Declare2)] pub struct FabButtonDecorator { #[allow(unused)] pub button_type: ButtonType, @@ -73,7 +73,7 @@ impl ComposeDecorator for FabButtonDecorator { /// } /// }; /// ``` -#[derive(Declare, Default, Declare2)] +#[derive(Default, Declare2)] pub struct FabButton { #[declare(default=Palette::of(ctx).primary())] color: Color, diff --git a/widgets/src/buttons/filled_button.rs b/widgets/src/buttons/filled_button.rs index 16eb31bda..83e42c4bd 100644 --- a/widgets/src/buttons/filled_button.rs +++ b/widgets/src/buttons/filled_button.rs @@ -26,7 +26,7 @@ impl CustomStyle for FilledButtonStyle { } } -#[derive(Clone, Declare, Declare2)] +#[derive(Clone, Declare2)] pub struct FilledButtonDecorator { #[allow(unused)] pub button_type: ButtonType, @@ -73,7 +73,7 @@ impl ComposeDecorator for FilledButtonDecorator { /// } /// }; /// ``` -#[derive(Declare, Declare2, Default)] +#[derive(Declare2, Default)] pub struct FilledButton { #[declare(default=Palette::of(ctx).primary())] color: Color, diff --git a/widgets/src/buttons/outlined_button.rs b/widgets/src/buttons/outlined_button.rs index 5d03c6e60..c55e39f5a 100644 --- a/widgets/src/buttons/outlined_button.rs +++ b/widgets/src/buttons/outlined_button.rs @@ -28,7 +28,7 @@ impl CustomStyle for OutlinedButtonStyle { } } -#[derive(Clone, Declare, Declare2)] +#[derive(Clone, Declare2)] pub struct OutlinedButtonDecorator { #[allow(unused)] pub button_type: ButtonType, @@ -75,7 +75,7 @@ impl ComposeDecorator for OutlinedButtonDecorator { /// } /// }; /// ``` -#[derive(Declare, Default, Declare2)] +#[derive(Default, Declare2)] pub struct OutlinedButton { #[declare(default=Palette::of(ctx).primary())] color: Color, diff --git a/widgets/src/checkbox.rs b/widgets/src/checkbox.rs index 04b102222..0af1adea9 100644 --- a/widgets/src/checkbox.rs +++ b/widgets/src/checkbox.rs @@ -5,7 +5,7 @@ use crate::{ use ribir_core::prelude::*; /// Represents a control that a user can select and clear. -#[derive(Clone, Declare, Declare2)] +#[derive(Clone, Declare2)] pub struct Checkbox { #[declare(default)] pub checked: bool, @@ -25,7 +25,7 @@ pub struct CheckBoxStyle { pub label_color: Brush, } -#[derive(Clone, Declare, Declare2)] +#[derive(Clone, Declare2)] pub struct CheckBoxDecorator { #[declare(default=Palette::of(ctx).primary())] pub color: Color, @@ -134,7 +134,7 @@ mod tests { extern crate test; use test::Bencher; - fn checked() -> Widget { widget! { Checkbox { checked: true } }.into() } + fn checked() -> Widget { fn_widget! { @Checkbox { checked: true } }.into() } widget_test_suit!( checked, wnd_size = Size::new(48., 48.), @@ -142,7 +142,7 @@ mod tests { height == 24., ); - fn unchecked() -> Widget { widget! { Checkbox { } }.into() } + fn unchecked() -> Widget { fn_widget! { @Checkbox {} }.into() } widget_test_suit!( unchecked, wnd_size = Size::new(48., 48.), @@ -151,8 +151,8 @@ mod tests { ); fn indeterminate() -> Widget { - widget! { - Checkbox { + fn_widget! { + @Checkbox { checked: true, indeterminate: true, } diff --git a/widgets/src/common_widget.rs b/widgets/src/common_widget.rs index 87516c9e8..734c0b392 100644 --- a/widgets/src/common_widget.rs +++ b/widgets/src/common_widget.rs @@ -1,9 +1,9 @@ use ribir_core::prelude::*; -#[derive(Default, Declare, Declare2, SingleChild)] +#[derive(Default, Declare2, SingleChild)] pub struct Leading; -#[derive(Default, Declare, Declare2, SingleChild)] +#[derive(Default, Declare2, SingleChild)] pub struct Trailing; pub type TrailingText = SinglePair>; diff --git a/widgets/src/divider.rs b/widgets/src/divider.rs index 3c9fcb45d..525da2fc1 100644 --- a/widgets/src/divider.rs +++ b/widgets/src/divider.rs @@ -9,19 +9,19 @@ use ribir_core::{impl_query_self_only, prelude::*}; /// # use ribir_widgets::prelude::{Divider, SizedBox, Direction, Column}; /// /// // use default Divider default settings -/// let widget = widget! { -/// Column { -/// SizedBox { size: Size::new(10., 0.) } -/// Divider { extent: 20. } -/// SizedBox { size: Size::new(10., 0.) } +/// let widget = fn_widget! { +/// @Column { +/// @SizedBox { size: Size::new(10., 0.) } +/// @Divider { extent: 20. } +/// @SizedBox { size: Size::new(10., 0.) } /// } /// }; /// /// // use custom settings -/// let widget = widget! { -/// Column { -/// SizedBox { size: Size::new(10., 0.) } -/// Divider { +/// let widget = fn_widget! { +/// @Column { +/// @SizedBox { size: Size::new(10., 0.) } +/// @Divider { /// extent: 20., /// color: Color::RED, /// direction: Direction::Horizontal, @@ -32,17 +32,17 @@ use ribir_core::{impl_query_self_only, prelude::*}; /// // behind indentation distance /// end_indent: 10., /// } -/// SizedBox { size: Size::new(10., 0.) } +/// @SizedBox { size: Size::new(10., 0.) } /// } /// }; /// ``` -#[derive(Default, Declare, Declare2)] +#[derive(Default, Declare2)] pub struct Divider { #[declare(default = 1.)] // Extent of divider pub extent: f32, // Color of divider - #[declare(default=Palette::of(ctx).outline_variant(), convert=into)] + #[declare(default=Palette::of(ctx).outline_variant())] pub color: Brush, // Direction of divider #[declare(default=Direction::Horizontal)] diff --git a/widgets/src/grid_view.rs b/widgets/src/grid_view.rs index 141424e71..b7567e3f5 100644 --- a/widgets/src/grid_view.rs +++ b/widgets/src/grid_view.rs @@ -1,7 +1,7 @@ use crate::layout::Direction; use ribir_core::{impl_query_self_only, prelude::*}; -#[derive(Declare, MultiChild)] +#[derive(MultiChild)] pub struct GridView { axis_dir: Direction, cross_axis_cnt: u32, diff --git a/widgets/src/icon.rs b/widgets/src/icon.rs index 7f49ee380..4d6334dd7 100644 --- a/widgets/src/icon.rs +++ b/widgets/src/icon.rs @@ -5,7 +5,7 @@ use ribir_core::prelude::*; /// classic frameworks, it's not draw anything and not require you to /// provide image or font fot it to draw, it just center align and fit size of /// its child. So you can declare any widget as its child to display as a icon. -#[derive(Declare, Declare2, Default, Clone, Copy)] +#[derive(Declare2, Default, Clone, Copy)] pub struct Icon { #[declare(default = IconSize::of(ctx).small)] pub size: Size, diff --git a/widgets/src/input.rs b/widgets/src/input.rs index 67e1555e2..fd3134de4 100644 --- a/widgets/src/input.rs +++ b/widgets/src/input.rs @@ -33,7 +33,7 @@ impl CustomStyle for InputStyle { fn default_style(_: &BuildCtx) -> Self { InputStyle { size: Some(20.) } } } -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct Input { #[declare(default = TypographyTheme::of(ctx).body_large.text.clone())] pub style: CowArc, @@ -45,7 +45,7 @@ pub struct Input { size: Option, } -#[derive(Declare)] +#[derive(Declare2)] pub struct TextArea { #[declare(default = TypographyTheme::of(ctx).body_large.text.clone())] pub style: CowArc, diff --git a/widgets/src/input/caret.rs b/widgets/src/input/caret.rs index 424697e5d..ddcfdf9a3 100644 --- a/widgets/src/input/caret.rs +++ b/widgets/src/input/caret.rs @@ -1,7 +1,7 @@ use crate::layout::SizedBox; use ribir_core::prelude::*; use std::time::Duration; -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct Caret { pub focused: bool, pub height: f32, diff --git a/widgets/src/input/editarea.rs b/widgets/src/input/editarea.rs index f50866ceb..22ac91114 100644 --- a/widgets/src/input/editarea.rs +++ b/widgets/src/input/editarea.rs @@ -6,7 +6,7 @@ use ribir_core::prelude::*; use ribir_core::ticker::FrameMsg; use std::time::Duration; -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub(crate) struct TextEditorArea { pub(crate) style: CowArc, pub(crate) text: CowArc, diff --git a/widgets/src/input/selected_text.rs b/widgets/src/input/selected_text.rs index 797b5ac2b..06806cfd7 100644 --- a/widgets/src/input/selected_text.rs +++ b/widgets/src/input/selected_text.rs @@ -1,7 +1,7 @@ use crate::layout::{Container, Stack}; use ribir_core::prelude::*; -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub(crate) struct SelectedText { pub(crate) rects: Vec, } diff --git a/widgets/src/input/text_selectable.rs b/widgets/src/input/text_selectable.rs index 05c4a787d..03887f30b 100644 --- a/widgets/src/input/text_selectable.rs +++ b/widgets/src/input/text_selectable.rs @@ -5,7 +5,7 @@ use crate::layout::{Stack, StackFit}; use crate::prelude::Text; use ribir_core::prelude::*; -#[derive(Declare, Declare2, Default)] +#[derive(Declare2, Default)] pub struct TextSelectable { #[declare(default)] pub caret: CaretState, diff --git a/widgets/src/layout/constrained_box.rs b/widgets/src/layout/constrained_box.rs index 858d29ff1..1b3110b6f 100644 --- a/widgets/src/layout/constrained_box.rs +++ b/widgets/src/layout/constrained_box.rs @@ -1,7 +1,7 @@ use ribir_core::{impl_query_self_only, prelude::*}; /// a widget that imposes additional constraints clamp on its child. -#[derive(SingleChild, Declare, Declare2, Clone)] +#[derive(SingleChild, Declare2, Clone)] pub struct ConstrainedBox { pub clamp: BoxClamp, } @@ -30,12 +30,12 @@ mod tests { use ribir_dev_helper::*; fn outside_fixed_clamp() -> Widget { - widget! { - SizedBox { + fn_widget! { + @SizedBox { size: Size::new(50., 50.), - ConstrainedBox { + @ConstrainedBox { clamp: BoxClamp::fixed_size(Size::new(40., 40.)), - Void {} + @Void {} } } } @@ -47,12 +47,12 @@ mod tests { ); fn expand_one_axis() -> Widget { - widget! { - Container { + fn_widget! { + @Container { size: Size::new(256., 50.), - ConstrainedBox { + @ConstrainedBox { clamp: BoxClamp::EXPAND_X, - Container { + @Container { size: Size::new(128., 20.), } } @@ -66,12 +66,12 @@ mod tests { ); fn expand_both() -> Widget { - widget! { - Container { + fn_widget! { + @Container { size: Size::new(256., 50.), - ConstrainedBox { + @ConstrainedBox { clamp: BoxClamp::EXPAND_BOTH, - Container { + @Container { size: Size::new(128., 20.), } } diff --git a/widgets/src/layout/container.rs b/widgets/src/layout/container.rs index fd82a9846..c1c069e12 100644 --- a/widgets/src/layout/container.rs +++ b/widgets/src/layout/container.rs @@ -1,7 +1,7 @@ use ribir_core::prelude::*; /// Widget with fixed size as a container for its child. -#[derive(Declare, Declare2, SingleChild)] +#[derive(Declare2, SingleChild)] pub struct Container { pub size: Size, } @@ -33,6 +33,6 @@ mod tests { use ribir_geom::Size; const SIZE: Size = Size::new(100., 100.); - fn smoke() -> Widget { widget! { Container { size: SIZE }}.into() } + fn smoke() -> Widget { fn_widget! { @Container { size: SIZE }}.into() } widget_layout_test!(smoke, size == SIZE,); } diff --git a/widgets/src/layout/expanded.rs b/widgets/src/layout/expanded.rs index 40c8bdb4f..eaab4fef8 100644 --- a/widgets/src/layout/expanded.rs +++ b/widgets/src/layout/expanded.rs @@ -5,7 +5,7 @@ use super::ConstrainedBox; /// A widget that expanded a child of `Flex`, so that the child fills the /// available space. If multiple children are expanded, the available space is /// divided among them according to the flex factor. -#[derive(Clone, PartialEq, Declare, Declare2)] +#[derive(Clone, PartialEq, Declare2)] pub struct Expanded { #[declare(default = 1.)] pub flex: f32, @@ -39,16 +39,16 @@ mod tests { fn expand_child_size_zero() -> Widget { let size = Size::new(100., 50.); - widget! { - Row { - Expanded { + fn_widget! { + @Row { + @Expanded { flex: 1., - SizedBox { size } + @SizedBox { size } } - SizedBox { size } - Expanded { + @SizedBox { size } + @Expanded { flex: 2., - SizedBox { size: Size::new(0., 50.) } + @SizedBox { size: Size::new(0., 50.) } } } } @@ -63,17 +63,17 @@ mod tests { fn one_line_expanded() -> Widget { let size = Size::new(100., 50.); - widget! { - Row { - Expanded { + fn_widget! { + @Row { + @Expanded { flex: 1., - SizedBox { size } + @SizedBox { size } } - SizedBox { size } - SizedBox { size } - Expanded { + @SizedBox { size } + @SizedBox { size } + @Expanded { flex: 2., - SizedBox { size } + @SizedBox { size } } } } @@ -91,24 +91,24 @@ mod tests { fn wrap_expanded() -> Widget { let size = Size::new(100., 50.); - widget! { - Row { + fn_widget! { + @Row { wrap: true, - Expanded { + @Expanded { flex: 1. , - SizedBox { size } + @SizedBox { size } } - SizedBox { size } - SizedBox { size } - SizedBox { size } - SizedBox { size } - Expanded { + @SizedBox { size } + @SizedBox { size } + @SizedBox { size } + @SizedBox { size } + @Expanded { flex: 1. , - SizedBox { size, } + @SizedBox { size, } } - Expanded { + @Expanded { flex: 4., - SizedBox { size, } + @SizedBox { size, } } } } diff --git a/widgets/src/layout/flex.rs b/widgets/src/layout/flex.rs index 904a109e0..62c403f4d 100644 --- a/widgets/src/layout/flex.rs +++ b/widgets/src/layout/flex.rs @@ -31,7 +31,7 @@ pub enum JustifyContent { SpaceEvenly, } -#[derive(Default, MultiChild, Declare, Declare2, Clone, PartialEq)] +#[derive(Default, MultiChild, Declare2, Clone, PartialEq)] pub struct Flex { /// Reverse the main axis. #[declare(default)] @@ -64,23 +64,6 @@ pub struct Row; /// A type help to declare flex widget as Vertical. pub struct Column; -impl Declare for Row { - type Builder = FlexDeclarer; - fn declare_builder() -> Self::Builder { Flex::declare_builder().direction(Direction::Horizontal) } -} - -impl FlexDeclarer { - pub fn item_gap(mut self, gap: f32) -> Self { - self.main_axis_gap = Some(gap); - self - } - - pub fn line_gap(mut self, gap: f32) -> Self { - self.cross_axis_gap = Some(gap); - self - } -} - impl Declare2 for Row { type Builder = FlexDeclarer2; fn declare2_builder() -> Self::Builder { @@ -106,11 +89,6 @@ impl FlexDeclarer2 { } } -impl Declare for Column { - type Builder = FlexDeclarer; - fn declare_builder() -> Self::Builder { Flex::declare_builder().direction(Direction::Vertical) } -} - impl Declare2 for Column { type Builder = FlexDeclarer2; fn declare2_builder() -> Self::Builder { Flex::declare2_builder().direction(Direction::Vertical) } @@ -486,12 +464,12 @@ mod tests { ); fn main_axis_gap() -> Widget { - widget! { - Row { + fn_widget! { + @Row { item_gap: 15., - SizedBox { size: Size::new(120., 20.) } - SizedBox { size: Size::new(80., 20.) } - SizedBox { size: Size::new(30., 20.) } + @SizedBox { size: Size::new(120., 20.) } + @SizedBox { size: Size::new(80., 20.) } + @SizedBox { size: Size::new(30., 20.) } } } .into() @@ -505,13 +483,13 @@ mod tests { ); fn main_axis_reverse_gap() -> Widget { - widget! { - Row { + fn_widget! { + @Row { item_gap: 15., reverse: true, - SizedBox { size: Size::new(120., 20.) } - SizedBox { size: Size::new(80., 20.) } - SizedBox { size: Size::new(30., 20.) } + @SizedBox { size: Size::new(120., 20.) } + @SizedBox { size: Size::new(80., 20.) } + @SizedBox { size: Size::new(30., 20.) } } } .into() @@ -525,20 +503,20 @@ mod tests { ); fn main_axis_expand() -> Widget { - widget! { - Row { + fn_widget! { + @Row { item_gap: 15., - SizedBox { size: Size::new(120., 20.) } - Expanded { + @SizedBox { size: Size::new(120., 20.) } + @Expanded { flex: 1., - SizedBox { size: Size::new(10., 20.) } + @SizedBox { size: Size::new(10., 20.) } } - SizedBox { size: Size::new(80., 20.) } - Expanded { + @SizedBox { size: Size::new(80., 20.) } + @Expanded { flex: 2., - SizedBox { size: Size::new(10., 20.) } + @SizedBox { size: Size::new(10., 20.) } } - SizedBox { size: Size::new(30., 20.) } + @SizedBox { size: Size::new(30., 20.) } } } .into() @@ -575,12 +553,12 @@ mod tests { ); fn cross_align(align: Align) -> Widget { - widget! { - Row { + fn_widget! { + @Row { align_items: align, - SizedBox { size: Size::new(100., 20.) } - SizedBox { size: Size::new(100., 30.) } - SizedBox { size: Size::new(100., 40.) } + @SizedBox { size: Size::new(100., 20.) } + @SizedBox { size: Size::new(100., 30.) } + @SizedBox { size: Size::new(100., 40.) } } } .into() @@ -625,15 +603,15 @@ mod tests { fn main_align(justify_content: JustifyContent) -> Widget { let item_size = Size::new(100., 20.); - widget! { - SizedBox { + fn_widget! { + @SizedBox { size: Size::new(500., 500.), - Row { + @Row { justify_content, align_items: Align::Start, - SizedBox { size: item_size } - SizedBox { size: item_size } - SizedBox { size: item_size } + @SizedBox { size: item_size } + @SizedBox { size: item_size } + @SizedBox { size: item_size } } } } @@ -702,19 +680,19 @@ mod tests { ); fn flex_expand() -> Widget { - widget! { - SizedBox { + fn_widget! { + @SizedBox { size: Size::new(500., 25.), - Flex { + @Flex { direction: Direction::Horizontal, - Expanded { + @Expanded { flex: 1., - SizedBox { size: INFINITY_SIZE,} + @SizedBox { size: INFINITY_SIZE,} } - SizedBox { size: Size::new(100., 20.) } - Expanded { + @SizedBox { size: Size::new(100., 20.) } + @Expanded { flex: 3., - SizedBox { size: INFINITY_SIZE, } + @SizedBox { size: INFINITY_SIZE, } } } } diff --git a/widgets/src/layout/sized_box.rs b/widgets/src/layout/sized_box.rs index ffa93153c..9f30539fc 100644 --- a/widgets/src/layout/sized_box.rs +++ b/widgets/src/layout/sized_box.rs @@ -4,7 +4,7 @@ use ribir_core::{impl_query_self_only, prelude::*}; /// /// This widget forces its child to have a specific width and/or height /// (assuming values are permitted by the parent of this widget). -#[derive(SingleChild, Declare, Declare2, Clone)] +#[derive(SingleChild, Declare2, Clone)] pub struct SizedBox { pub size: Size, } @@ -33,10 +33,10 @@ mod tests { fn fix_size() -> Widget { let size: Size = Size::new(100., 100.); - widget! { - SizedBox { + fn_widget! { + @SizedBox { size, - Text { text: "" } + @Text { text: "" } } } .into() @@ -44,10 +44,10 @@ mod tests { widget_layout_test!(fix_size, width == 100., height == 100.,); fn shrink_size() -> Widget { - widget! { - SizedBox { + fn_widget! { + @SizedBox { size: ZERO_SIZE, - Text { text: "" } + @Text { text: "" } } } .into() @@ -59,10 +59,10 @@ mod tests { ); fn expanded_size() -> Widget { - widget! { - SizedBox { + fn_widget! { + @SizedBox { size: INFINITY_SIZE, - Text { text: "" } + @Text { text: "" } } } .into() diff --git a/widgets/src/layout/stack.rs b/widgets/src/layout/stack.rs index 8438d82ea..ec808e701 100644 --- a/widgets/src/layout/stack.rs +++ b/widgets/src/layout/stack.rs @@ -1,7 +1,7 @@ use ribir_core::{impl_query_self_only, prelude::*}; /// A widget that overlap children align with left top. -#[derive(MultiChild, Declare, Declare2)] +#[derive(MultiChild, Declare2)] pub struct Stack { #[declare(default)] fit: StackFit, @@ -78,10 +78,10 @@ mod tests { fn smoke() -> Widget { let one = Size::new(1., 1.); let five = Size::new(5., 5.); - widget! { - Stack { - SizedBox { size: one} - SizedBox { size: five} + fn_widget! { + @Stack { + @SizedBox { size: one} + @SizedBox { size: five} } } .into() diff --git a/widgets/src/link.rs b/widgets/src/link.rs index 0c363833d..67313b612 100644 --- a/widgets/src/link.rs +++ b/widgets/src/link.rs @@ -2,10 +2,9 @@ use log::warn; use ribir_core::prelude::*; use webbrowser::{open_browser as open, Browser}; -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct Link { /// Want to open url - #[declare(convert=into)] url: CowArc, /// Select the browser software you expect to open #[declare(default=Browser::Default)] diff --git a/widgets/src/lists.rs b/widgets/src/lists.rs index 77cff6007..70fba1049 100644 --- a/widgets/src/lists.rs +++ b/widgets/src/lists.rs @@ -121,10 +121,10 @@ use ribir_core::prelude::*; /// } /// }; /// ``` -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct Lists; -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct ListsDecorator {} impl ComposeDecorator for ListsDecorator { type Host = Widget; @@ -339,7 +339,7 @@ impl ComposeChild for ListItem { } } -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct ListItem { #[declare(default = 1usize)] pub line_number: usize, @@ -437,7 +437,7 @@ impl CustomStyle for ListItemStyle { } } -#[derive(Clone, Declare, Declare2)] +#[derive(Clone, Declare2)] pub struct ListItemDecorator { pub color: Color, pub is_active: bool, diff --git a/widgets/src/path.rs b/widgets/src/path.rs index 0344284bb..26287f0fe 100644 --- a/widgets/src/path.rs +++ b/widgets/src/path.rs @@ -2,10 +2,9 @@ use ribir_core::{impl_query_self_only, prelude::*}; /// Widget just use as a paint kit for a path and not care about its size. Use /// `[PathWidget]!` instead of. -#[derive(Declare, Declare2, Clone)] +#[derive(Declare2, Clone)] pub struct PathPaintKit { pub path: Path, - #[declare(convert=into)] pub brush: Brush, #[declare(default)] pub style: PathPaintStyle, @@ -42,12 +41,11 @@ impl Render for PathPaintKit { impl_query_self_only!(PathPaintKit); -#[derive(Declare)] +#[derive(Declare2)] /// A path widget which size careful and can process events only if user hit at /// the path self, not its size cover area. pub struct PathWidget { pub path: Path, - #[declare(convert=into)] pub brush: Brush, #[declare(default)] pub style: PathPaintStyle, diff --git a/widgets/src/scrollbar.rs b/widgets/src/scrollbar.rs index c544796f9..504a1d2f9 100644 --- a/widgets/src/scrollbar.rs +++ b/widgets/src/scrollbar.rs @@ -3,7 +3,7 @@ use ribir_core::prelude::*; /// A control widget that enables the user to access horizontal parts child that /// is larger than the box rect. -#[derive(Declare, Declare2, Clone)] +#[derive(Declare2, Clone)] pub struct HScrollBar { /// Scrolled pixels of child content. #[declare(default)] @@ -22,7 +22,7 @@ pub struct ScrollBarStyle { /// Compose style that use to decoration the thumb of horizontal scrollbar, /// overwrite it when init theme. -#[derive(Debug, Declare, Declare2)] +#[derive(Debug, Declare2)] pub struct HScrollBarThumbDecorator { pub offset: f32, } @@ -37,7 +37,7 @@ impl ComposeDecorator for HScrollBarThumbDecorator { /// Compose style that use to decoration the thumb of vertical scrollbar, /// overwrite it when init theme. -#[derive(Debug, Declare, Declare2)] +#[derive(Debug, Declare2)] pub struct VScrollBarThumbDecorator { pub offset: f32, } @@ -86,7 +86,7 @@ impl ComposeChild for HScrollBar { /// A control widget that enables the user to access vertical parts child that /// is larger than the box rect. -#[derive(Declare, Declare2, Clone)] +#[derive(Declare2, Clone)] pub struct VScrollBar { /// Scrolled pixels of child content. #[declare(default)] @@ -129,7 +129,7 @@ impl ComposeChild for VScrollBar { } /// A control widget that enables the user to access horizontal parts child that /// is larger than the box rect. -#[derive(Declare, Declare2, Clone)] +#[derive(Declare2, Clone)] pub struct BothScrollbar { /// Scrolled pixels of child content. #[declare(default)] @@ -175,7 +175,7 @@ impl ComposeChild for BothScrollbar { /// A widget that display the horizontal scrolling information of the /// `scrolling` widget. -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct HRawScrollbar { scrolling: Reader, } @@ -237,7 +237,7 @@ impl Compose for HRawScrollbar { /// A widget that display the vertical scrolling information of the /// `scrolling` widget. -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct VRawScrollbar { scrolling: Reader, } @@ -316,19 +316,19 @@ mod test { use ribir_dev_helper::*; fn content_expand_so_all_view_can_scroll() -> Widget { - widget! { - ConstrainedBox { + fn_widget! { + @ConstrainedBox { clamp: BoxClamp::EXPAND_BOTH, - Stack { + @Stack { fit: StackFit::Passthrough, - HScrollBar { - Container { size: Size::new(100., 100.) } + @HScrollBar { + @Container { size: Size::new(100., 100.) } } - VScrollBar { - Container { size: Size::new(100., 100.) } + @VScrollBar { + @Container { size: Size::new(100., 100.) } } - BothScrollbar { - Container { size: Size::new(100., 100.) } + @BothScrollbar { + @Container { size: Size::new(100., 100.) } } } } diff --git a/widgets/src/tabs.rs b/widgets/src/tabs.rs index 43f7ac3be..10f0476a8 100644 --- a/widgets/src/tabs.rs +++ b/widgets/src/tabs.rs @@ -56,7 +56,7 @@ use ribir_core::prelude::*; /// } /// }; /// ``` -#[derive(Declare, Declare2, Clone)] +#[derive(Declare2, Clone)] pub struct Tabs { #[declare(default = Position::Top)] pub pos: Position, @@ -99,7 +99,7 @@ impl CustomStyle for TabsStyle { } } } -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct TabsDecorator {} impl ComposeDecorator for TabsDecorator { @@ -120,10 +120,10 @@ pub struct TabItem { text: Option>, } -#[derive(Declare, Declare2, SingleChild)] +#[derive(Declare2, SingleChild)] pub struct TabPane; -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct TabDecorator {} impl ComposeDecorator for TabDecorator { @@ -132,7 +132,7 @@ impl ComposeDecorator for TabDecorator { fn compose_decorator(_: State, host: Self::Host) -> Widget { host } } -#[derive(Declare, Declare2)] +#[derive(Declare2)] pub struct IndicatorDecorator { pub pos: Position, pub rect: Rect, diff --git a/widgets/src/text.rs b/widgets/src/text.rs index 38c7dfdad..e7a65e624 100644 --- a/widgets/src/text.rs +++ b/widgets/src/text.rs @@ -7,11 +7,10 @@ use ribir_core::{ }; /// The text widget display text with a single style. -#[derive(Debug, Declare, Declare2, Clone, PartialEq)] +#[derive(Debug, Declare2, Clone, PartialEq)] pub struct Text { - #[declare(convert=into)] pub text: CowArc, - #[declare(default = Palette::of(ctx).on_surface_variant(), convert=into)] + #[declare(default = Palette::of(ctx).on_surface_variant())] pub foreground: Brush, #[declare(default = TypographyTheme::of(ctx).body_medium.text.clone())] pub text_style: CowArc, @@ -130,11 +129,10 @@ impl_query_self_only!(Text); macro_rules! define_text_with_theme_style { ($name: ident, $style: ident) => { - #[derive(Declare, Declare2)] + #[derive(Declare2)] pub struct $name { - #[declare(convert=into)] pub text: CowArc, - #[declare(default = Palette::of(ctx).on_surface_variant(), convert = into)] + #[declare(default = Palette::of(ctx).on_surface_variant())] pub foreground: Brush, #[declare(default)] pub overflow: Overflow, @@ -175,10 +173,10 @@ mod tests { fn text_clip() { let _guard = unsafe { AppCtx::new_lock_scope() }; - let w = widget! { - SizedBox { + let w = fn_widget! { + @SizedBox { size: Size::new(50., 45.), - Text { + @Text { text: "hello world,\rnice to meet you.", } } diff --git a/widgets/src/text_field.rs b/widgets/src/text_field.rs index 04df03a2c..f39ff915b 100644 --- a/widgets/src/text_field.rs +++ b/widgets/src/text_field.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use ribir_core::prelude::*; use std::{collections::HashMap, hash::Hash, ops::Deref}; -#[derive(Declare, Default)] +#[derive(Declare2, Default)] pub struct TextField { /// textfield's input value #[declare(skip)] @@ -89,7 +89,7 @@ where fn get(&self, state: S) -> Option<&T> { self.themes.get(&state) } } -#[derive(Declare, Declare2)] +#[derive(Declare2)] struct ThemeSuitProxy where S: Hash + Eq, @@ -385,7 +385,7 @@ fn build_input_area( .into() } -#[derive(Declare, Declare2)] +#[derive(Declare2)] struct TextFieldLabel { text: CowArc, style: CowArc, diff --git a/widgets/src/transform_box.rs b/widgets/src/transform_box.rs index 083e7a343..8c9140804 100644 --- a/widgets/src/transform_box.rs +++ b/widgets/src/transform_box.rs @@ -1,6 +1,6 @@ use ribir_core::{impl_query_self_only, prelude::*}; -#[derive(SingleChild, Declare, Clone)] +#[derive(SingleChild, Declare2, Clone)] pub struct TransformBox { pub matrix: Transform, } @@ -43,10 +43,10 @@ mod tests { use crate::prelude::*; fn smoke() -> Widget { - widget! { - TransformBox { + fn_widget! { + @TransformBox { matrix: Transform::new(2., 0., 0., 2., 0., 0.), - SizedBox { size: Size::new(100., 100.) } + @SizedBox { size: Size::new(100., 100.) } } } .into()