diff --git a/CHANGELOG.md b/CHANGELOG.md index a7f5d61d6..5d0d34c8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,12 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he ## [@Unreleased] - @ReleaseDate +### Changed + +- **core**: Consolidated all listener and `FocusNode` into a `MixBuiltin` widget (#534 @M-Adoo) + - The `MixBuiltin` widget reduces memory usage and allows users to utilize all `on_xxx` event handlers, not only during the build declaration but also after the widget has been built. + + ## [0.2.0-alpha.3] - 2024-02-20 ## [0.2.0-alpha.2] - 2024-02-13 diff --git a/core/src/animation/transition.rs b/core/src/animation/transition.rs index 0299f3b87..1cec0b7ec 100644 --- a/core/src/animation/transition.rs +++ b/core/src/animation/transition.rs @@ -32,7 +32,7 @@ pub trait TransitionState: Sized + 'static { Self: AnimateState, { let state = self.clone_setter(); - let animate: State> = Animate::declare_builder() + let animate = Animate::declare_builder() .transition(transition) .from(self.get()) .state(self) diff --git a/core/src/builtin_widgets.rs b/core/src/builtin_widgets.rs index 846b6427e..b8778692c 100644 --- a/core/src/builtin_widgets.rs +++ b/core/src/builtin_widgets.rs @@ -35,8 +35,6 @@ mod void; pub use void::Void; mod unconstrained_box; pub use unconstrained_box::*; -mod lifecycle; -pub use lifecycle::*; mod opacity; pub use opacity::*; mod anchor; @@ -63,6 +61,8 @@ pub mod focus_scope; pub use focus_scope::*; pub mod global_anchor; pub use global_anchor::*; +mod mix_builtin; +pub use mix_builtin::*; use crate::{ prelude::*, @@ -148,16 +148,9 @@ macro_rules! impl_builtin_obj { } impl_builtin_obj!( - PointerListener, - FocusNode, + MixBuiltin, RequestFocus, - FocusListener, - FocusBubbleListener, HasFocus, - KeyboardListener, - CharsListener, - ImePreEditListener, - WheelListener, MouseHover, PointerPressed, FittedBox, @@ -174,7 +167,6 @@ impl_builtin_obj!( GlobalAnchor, Visibility, Opacity, - LifecycleListener, DelayDrop ); diff --git a/core/src/builtin_widgets/focus_node.rs b/core/src/builtin_widgets/focus_node.rs index 3b7825165..8aa6c52a9 100644 --- a/core/src/builtin_widgets/focus_node.rs +++ b/core/src/builtin_widgets/focus_node.rs @@ -1,83 +1,4 @@ -use crate::{ - events::focus_mgr::{FocusHandle, FocusType}, - prelude::*, -}; - -#[derive(Default, Query, Declare)] -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. - /// - /// It accepts an integer as a value, with different results depending on the - /// integer's value: - /// - A negative value (usually -1) means that the widget is not reachable via - /// sequential keyboard navigation, but could be focused with API or - /// visually by clicking with the mouse. - /// - Zero means that the element should be focusable in sequential keyboard - /// navigation, after any positive tab_index values and its order is defined - /// by the tree's source order. - /// - A positive value means the element should be focusable in sequential - /// keyboard navigation, with its order defined by the value of the number. - /// That is, tab_index=4 is focused before tab_index=5 and tab_index=0, but - /// after tab_index=3. If multiple elements share the same positive - /// tab_index value, their order relative to each other follows their - /// position in the tree source. The maximum value for tab_index is 32767. - /// If not specified, it takes the default value 0. - #[declare(default, builtin)] - pub tab_index: i16, - /// Indicates whether the `widget` should automatically get focus when the - /// window loads. - /// - /// Only one widget should have this attribute specified. If there are - /// several, the widget nearest the root, get the initial - /// focus. - #[declare(default, builtin)] - pub auto_focus: bool, -} - -impl ComposeChild for FocusNode { - type Child = Widget; - fn compose_child( - this: impl StateWriter, - mut child: Self::Child, - ) -> impl WidgetBuilder { - fn_widget! { - let tree = ctx!().tree.borrow(); - let node = child.id().assert_get(&tree.arena); - let has_focus_node = node.contain_type::(); - if !has_focus_node { - let subject = node.query_most_outside(|l: &LifecycleListener| l.lifecycle_stream()); - drop(tree); - let subject = if let Some(subject) = subject { - subject - } else { - let listener = LifecycleListener::default(); - let subject = listener.lifecycle_stream(); - child = child.attach_data(listener, ctx!()); - subject - }; - - fn subscribe_fn(this: impl StateReader) - -> impl FnMut(&'_ mut AllLifecycle) + 'static - { - move |e| match e { - AllLifecycle::Mounted(e) => { - let auto_focus = this.read().auto_focus; - e.window().add_focus_node(e.id, auto_focus, FocusType::Node) - } - AllLifecycle::PerformedLayout(_) => {} - AllLifecycle::Disposed(e) => e.window().remove_focus_node(e.id, FocusType::Node), - } - } - let h = subject - .subscribe(subscribe_fn(this.clone_reader())) - .unsubscribe_when_dropped(); - child = child.attach_state_data(this, ctx!()).attach_anonymous_data(h, ctx!()); - } - child - } - } -} +use crate::{events::focus_mgr::FocusHandle, prelude::*}; #[derive(Declare, Query)] pub struct RequestFocus { @@ -124,11 +45,11 @@ mod tests { reset_test_env!(); let widget = fn_widget! { - @FocusNode { + @MixBuiltin { tab_index: 0i16, auto_focus: false, - @FocusNode { + @MixBuiltin { tab_index: 0i16, auto_focus: false, - @FocusNode { + @MixBuiltin { tab_index: 0i16, auto_focus: false, @MockBox { size: Size::default(), @@ -143,11 +64,13 @@ mod tests { let id = tree.root(); let node = id.get(&tree.arena).unwrap(); let mut cnt = 0; - node.query_type_inside_first(|_: &FocusNode| { - cnt += 1; + node.query_type_inside_first(|b: &MixBuiltin| { + if b.contain_flag(BuiltinFlags::Focus) { + cnt += 1; + } true }); - assert!(cnt == 1); + assert_eq!(cnt, 1); } } diff --git a/core/src/builtin_widgets/mix_builtin.rs b/core/src/builtin_widgets/mix_builtin.rs new file mode 100644 index 000000000..2e4857027 --- /dev/null +++ b/core/src/builtin_widgets/mix_builtin.rs @@ -0,0 +1,671 @@ +use crate::prelude::*; +use rxrust::prelude::*; +use std::{ + cell::Cell, + convert::Infallible, + time::{Duration, Instant}, +}; + +use self::focus_mgr::FocusType; + +const MULTI_TAP_DURATION: Duration = Duration::from_millis(250); + +bitflags! { + #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] + pub struct BuiltinFlags: u64 { + // Listener flags, the flags are used to indicate what + // kind of events the widget are listening to. + const Lifecycle = 1 << 0; + /// Pointer listener flag, hint the widget is listening to pointer events + const Pointer = 1 << 1; + /// Wheel listener flag, hint the widget is listening to wheel events + const Wheel = 1 << 2; + /// Keyboard listener flag, hint the widget is listening to keyboard events + const KeyBoard = 1 << 3 | Self::Focus.bits(); + /// Whether the widget is a focus node also hint the widget + /// is listening to focus/blur events + const Focus = 1 << 4; + /// Bubble focus event listener flag, hint the widget is listening to + /// FocusIn/FocusOut and their capture events + const FocusInOut = 1 << 5; + + const AllListeners = Self::Lifecycle.bits() + | Self::Pointer.bits() + | Self::Wheel.bits() + | Self::KeyBoard.bits() + | Self::Focus.bits() + | Self::FocusInOut.bits(); + // listener end + + const AutoFocus = 1 << 47; + // 16 bits keep for tab index + } +} + +pub type EventSubject = MutRefItemSubject<'static, Event, Infallible>; + +#[derive(Default, Query)] +pub struct MixBuiltin { + flags: Cell, + subject: EventSubject, +} + +/// The declarer of the `MixBuiltin` widget +pub struct MixBuiltinDeclarer(State); + +macro_rules! event_map_filter { + ($event_name: ident, $event_ty: ident) => { + (|e| match e { + Event::$event_name(e) => Some(e), + _ => None, + }) as fn(&mut Event) -> Option<&mut $event_ty> + }; +} + +macro_rules! impl_event_callback { + ($this: ident, $listen_type: ident, $event_name: ident, $event_ty: ident, $handler: ident) => {{ + $this.flag_mark(BuiltinFlags::$listen_type); + let _ = $this + .subject() + .filter_map(event_map_filter!($event_name, $event_ty)) + .subscribe($handler); + + $this + }}; +} + +impl MixBuiltin { + #[inline] + pub fn contain_flag(&self, t: BuiltinFlags) -> bool { self.flags.get().contains(t) } + + pub fn flag_mark(&self, t: BuiltinFlags) { + let t = self.flags.get() | t; + self.flags.set(t) + } + + pub fn dispatch(&self, event: &mut Event) { self.subject.clone().next(event) } + + pub fn subject(&self) -> EventSubject { self.subject.clone() } + + /// Listen to all events + pub fn on_event(&self, handler: impl FnMut(&mut Event) + 'static) -> &Self { + self.flag_mark(BuiltinFlags::AllListeners); + let _ = self.subject().subscribe(handler); + self + } + + pub fn on_mounted(&self, handler: impl FnMut(&mut LifecycleEvent) + 'static) -> &Self { + self.flag_mark(BuiltinFlags::Lifecycle); + let _ = self + .subject() + .filter_map(event_map_filter!(Mounted, LifecycleEvent)) + .take(1) + .subscribe(handler); + + self + } + + pub fn on_performed_layout(&self, handler: impl FnMut(&mut LifecycleEvent) + 'static) -> &Self { + impl_event_callback!(self, Lifecycle, PerformedLayout, LifecycleEvent, handler) + } + + pub fn on_disposed(&self, handler: impl FnMut(&mut LifecycleEvent) + 'static) -> &Self { + self.flag_mark(BuiltinFlags::Lifecycle); + let _ = self + .subject() + .filter_map(event_map_filter!(Disposed, LifecycleEvent)) + .take(1) + .subscribe(handler); + + self + } + + pub fn on_pointer_down(&self, handler: impl FnMut(&mut PointerEvent) + 'static) -> &Self { + impl_event_callback!(self, Pointer, PointerDown, PointerEvent, handler) + } + + pub fn on_pointer_down_capture(&self, handler: impl FnMut(&mut PointerEvent) + 'static) -> &Self { + impl_event_callback!(self, Pointer, PointerDownCapture, PointerEvent, handler) + } + + pub fn on_pointer_up(&self, handler: impl FnMut(&mut PointerEvent) + 'static) -> &Self { + impl_event_callback!(self, Pointer, PointerUp, PointerEvent, handler) + } + + pub fn on_pointer_up_capture(&self, handler: impl FnMut(&mut PointerEvent) + 'static) -> &Self { + impl_event_callback!(self, Pointer, PointerUpCapture, PointerEvent, handler) + } + + pub fn on_pointer_move(&self, handler: impl FnMut(&mut PointerEvent) + 'static) -> &Self { + impl_event_callback!(self, Pointer, PointerMove, PointerEvent, handler) + } + + pub fn on_pointer_move_capture(&self, handler: impl FnMut(&mut PointerEvent) + 'static) -> &Self { + impl_event_callback!(self, Pointer, PointerMoveCapture, PointerEvent, handler) + } + + pub fn on_pointer_cancel(&self, handler: impl FnMut(&mut PointerEvent) + 'static) -> &Self { + impl_event_callback!(self, Pointer, PointerCancel, PointerEvent, handler) + } + + pub fn on_pointer_enter(&self, handler: impl FnMut(&mut PointerEvent) + 'static) -> &Self { + impl_event_callback!(self, Pointer, PointerEnter, PointerEvent, handler) + } + + pub fn on_pointer_leave(&self, handler: impl FnMut(&mut PointerEvent) + 'static) -> &Self { + impl_event_callback!(self, Pointer, PointerLeave, PointerEvent, handler) + } + + pub fn on_tap(&self, handler: impl FnMut(&mut PointerEvent) + 'static) -> &Self { + impl_event_callback!(self, Pointer, Tap, PointerEvent, handler) + } + + pub fn on_tap_capture(&self, handler: impl FnMut(&mut PointerEvent) + 'static) -> &Self { + impl_event_callback!(self, Pointer, TapCapture, PointerEvent, handler) + } + + 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) + } + + pub fn on_wheel(&self, handler: impl FnMut(&mut WheelEvent) + 'static) -> &Self { + impl_event_callback!(self, Wheel, Wheel, WheelEvent, handler) + } + + pub fn on_wheel_capture(&self, handler: impl FnMut(&mut WheelEvent) + 'static) -> &Self { + impl_event_callback!(self, Wheel, WheelCapture, WheelEvent, handler) + } + + fn on_x_times_tap_impl( + &self, + times: usize, + dur: Duration, + capture: bool, + handler: impl FnMut(&mut PointerEvent) + 'static, + ) -> &Self { + self.flag_mark(BuiltinFlags::Pointer); + self + .subject() + .filter_map(x_times_tap_map_filter(times, dur, capture)) + .subscribe(handler); + self + } + + pub fn on_ime_pre_edit(&self, f: impl FnMut(&mut ImePreEditEvent) + 'static) -> &Self { + impl_event_callback!(self, KeyBoard, ImePreEdit, ImePreEditEvent, f) + } + + pub fn on_ime_pre_edit_capture(&self, f: impl FnMut(&mut ImePreEditEvent) + 'static) -> &Self { + impl_event_callback!(self, KeyBoard, ImePreEditCapture, ImePreEditEvent, f) + } + + pub fn on_chars(&self, f: impl FnMut(&mut CharsEvent) + 'static) -> &Self { + impl_event_callback!(self, KeyBoard, Chars, CharsEvent, f) + } + + pub fn on_chars_capture(&self, f: impl FnMut(&mut CharsEvent) + 'static) -> &Self { + impl_event_callback!(self, KeyBoard, CharsCapture, CharsEvent, f) + } + + pub fn on_key_down(&self, f: impl FnMut(&mut KeyboardEvent) + 'static) -> &Self { + impl_event_callback!(self, KeyBoard, KeyDown, KeyboardEvent, f) + } + + pub fn on_key_down_capture(&self, f: impl FnMut(&mut KeyboardEvent) + 'static) -> &Self { + impl_event_callback!(self, KeyBoard, KeyDownCapture, KeyboardEvent, f) + } + + pub fn on_key_up(&self, f: impl FnMut(&mut KeyboardEvent) + 'static) -> &Self { + impl_event_callback!(self, KeyBoard, KeyUp, KeyboardEvent, f) + } + + pub fn on_key_up_capture(&self, f: impl FnMut(&mut KeyboardEvent) + 'static) -> &Self { + impl_event_callback!(self, KeyBoard, KeyUpCapture, KeyboardEvent, f) + } + + pub fn on_focus(&self, f: impl FnMut(&mut FocusEvent) + 'static) -> &Self { + impl_event_callback!(self, Focus, Focus, FocusEvent, f) + } + + pub fn on_blur(&self, f: impl FnMut(&mut FocusEvent) + 'static) -> &Self { + impl_event_callback!(self, Focus, Blur, FocusEvent, f) + } + + pub fn on_focus_in(&self, f: impl FnMut(&mut FocusEvent) + 'static) -> &Self { + impl_event_callback!(self, FocusInOut, FocusIn, FocusEvent, f) + } + + pub fn on_focus_in_capture(&self, f: impl FnMut(&mut FocusEvent) + 'static) -> &Self { + impl_event_callback!(self, FocusInOut, FocusInCapture, FocusEvent, f) + } + + pub fn on_focus_out(&self, f: impl FnMut(&mut FocusEvent) + 'static) -> &Self { + impl_event_callback!(self, FocusInOut, FocusOut, FocusEvent, f) + } + + pub fn on_focus_out_capture(&self, f: impl FnMut(&mut FocusEvent) + 'static) -> &Self { + impl_event_callback!(self, FocusInOut, FocusOutCapture, FocusEvent, f) + } + + /// Indicates that `widget` can be focused, and where it participates in + /// sequential keyboard navigation (usually with the Tab key). + pub fn is_focus_node(&self) -> bool { self.flags.get().contains(BuiltinFlags::Focus) } + + /// It accepts an integer as a value, with different results depending on the + /// integer's value: + /// - A negative value (usually -1) means that the widget is not reachable via + /// sequential keyboard navigation, but could be focused with API or + /// visually by clicking with the mouse. + /// - Zero means that the element should be focusable in sequential keyboard + /// navigation, after any positive tab_index values and its order is defined + /// by the tree's source order. + /// - A positive value means the element should be focusable in sequential + /// keyboard navigation, with its order defined by the value of the number. + /// That is, tab_index=4 is focused before tab_index=5 and tab_index=0, but + /// after tab_index=3. If multiple elements share the same positive + /// tab_index value, their order relative to each other follows their + /// position in the tree source. The maximum value for tab_index is 32767. + /// If not specified, it takes the default value 0. + pub fn tab_index(&self) -> i16 { (self.flags.get().bits() >> 48) as i16 } + + /// Set the tab index of the focus node + pub fn set_tab_index(&self, tab_idx: i16) { + let flags = self.flags.get().bits() | ((tab_idx as u64) << 48); + self.flags.set(BuiltinFlags::from_bits_retain(flags)); + } + + /// Indicates whether the `widget` should automatically get focus when the + /// window loads. + /// + /// Only one widget should have this attribute specified. If there are + /// several, the widget nearest the root, get the initial + /// focus. + pub fn auto_focus(&self) -> bool { self.flags.get().contains(BuiltinFlags::AutoFocus) } + + pub fn set_auto_focus(&self, v: bool) { + if v { + self.flag_mark(BuiltinFlags::AutoFocus); + } else { + let mut flag = self.flags.get(); + flag.remove(BuiltinFlags::AutoFocus); + self.flags.set(flag) + } + } + + fn merge(&self, other: Self) { + let tab_index = self.tab_index(); + let other_tab_index = other.tab_index(); + self.flags.set(self.flags.get() | other.flags.get()); + if other_tab_index != 0 { + self.set_tab_index(other_tab_index); + } else if tab_index != 0 { + self.set_tab_index(tab_index); + } + + let other_subject = other.subject(); + fn subscribe_fn(subject: EventSubject) -> impl FnMut(&mut Event) { + move |e: &mut Event| { + subject.clone().next(e); + } + } + self.subject().subscribe(subscribe_fn(other_subject)); + } + + fn callbacks_for_focus_node(&self) { + self + .on_mounted(move |e| { + e.query_type(|mix: &MixBuiltin| { + let auto_focus = mix.auto_focus(); + e.window().add_focus_node(e.id, auto_focus, FocusType::Node) + }); + }) + .on_disposed(|e| e.window().remove_focus_node(e.id, FocusType::Node)); + } +} + +impl MixBuiltinDeclarer { + pub fn tab_index<_M, _V>(self, v: _V) -> Self + where + DeclareInit: DeclareFrom<_V, _M>, + { + let inner = self.0.read(); + self.0.read().flag_mark(BuiltinFlags::Focus); + let v = DeclareInit::::declare_from(v); + match v { + DeclareInit::Value(v) => inner.set_tab_index(v), + DeclareInit::Pipe(p) => { + let (v, p) = p.into_pipe().unzip(); + inner.set_tab_index(v); + let this = self.0.clone_reader(); + p.subscribe(move |(_, v)| this.read().set_tab_index(v)); + } + } + drop(inner); + self + } + + pub fn auto_focus<_M, _V>(self, v: _V) -> Self + where + DeclareInit: DeclareFrom<_V, _M>, + { + let inner = self.0.read(); + inner.flag_mark(BuiltinFlags::Focus); + let v = DeclareInit::::declare_from(v); + match v { + DeclareInit::Value(v) => inner.set_auto_focus(v), + DeclareInit::Pipe(p) => { + let (v, p) = p.into_pipe().unzip(); + inner.set_auto_focus(v); + let this = self.0.clone_reader(); + p.subscribe(move |(_, v)| this.read().set_auto_focus(v)); + } + } + drop(inner); + self + } + + pub fn on_event(self, handler: impl FnMut(&mut Event) + 'static) -> Self { + self.0.read().on_event(handler); + self + } + + pub fn on_mounted(self, handler: impl FnMut(&mut LifecycleEvent) + 'static) -> Self { + self.0.read().on_mounted(handler); + self + } + + pub fn on_performed_layout(self, handler: impl FnMut(&mut LifecycleEvent) + 'static) -> Self { + self.0.read().on_performed_layout(handler); + self + } + + pub fn on_disposed(self, handler: impl FnMut(&mut LifecycleEvent) + 'static) -> Self { + self.0.read().on_disposed(handler); + self + } + + pub fn on_pointer_down(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_pointer_down(handler); + self + } + + pub fn on_pointer_down_capture(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_pointer_down_capture(handler); + self + } + + pub fn on_pointer_up(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_pointer_up(handler); + self + } + + pub fn on_pointer_up_capture(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_pointer_up_capture(handler); + self + } + + pub fn on_pointer_move(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_pointer_move(handler); + self + } + + pub fn on_pointer_move_capture(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_pointer_move_capture(handler); + self + } + + pub fn on_pointer_cancel(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_pointer_cancel(handler); + self + } + + pub fn on_pointer_enter(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_pointer_enter(handler); + self + } + + pub fn on_pointer_leave(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_pointer_leave(handler); + self + } + + pub fn on_tap(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_tap(handler); + self + } + + pub fn on_tap_capture(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_tap_capture(handler); + self + } + + pub fn on_double_tap(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_double_tap(handler); + self + } + + pub fn on_double_tap_capture(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_double_tap_capture(handler); + self + } + + pub fn on_triple_tap(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_triple_tap(handler); + self + } + + pub fn on_triple_tap_capture(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self { + self.0.read().on_triple_tap_capture(handler); + self + } + + pub fn on_x_times_tap(self, handler: (usize, impl FnMut(&mut PointerEvent) + 'static)) -> Self { + self.0.read().on_x_times_tap(handler); + self + } + + pub fn on_x_times_tap_capture( + self, + handler: (usize, impl FnMut(&mut PointerEvent) + 'static), + ) -> Self { + self.0.read().on_x_times_tap_capture(handler); + self + } + + pub fn on_wheel(self, handler: impl FnMut(&mut WheelEvent) + 'static) -> Self { + self.0.read().on_wheel(handler); + self + } + + pub fn on_wheel_capture(self, handler: impl FnMut(&mut WheelEvent) + 'static) -> Self { + self.0.read().on_wheel_capture(handler); + self + } + + pub fn on_ime_pre_edit(self, f: impl FnMut(&mut ImePreEditEvent) + 'static) -> Self { + self.0.read().on_ime_pre_edit(f); + self + } + + pub fn on_ime_pre_edit_capture(self, f: impl FnMut(&mut ImePreEditEvent) + 'static) -> Self { + self.0.read().on_ime_pre_edit_capture(f); + self + } + + pub fn on_chars(self, f: impl FnMut(&mut CharsEvent) + 'static) -> Self { + self.0.read().on_chars(f); + self + } + + pub fn on_chars_capture(self, f: impl FnMut(&mut CharsEvent) + 'static) -> Self { + self.0.read().on_chars_capture(f); + self + } + + pub fn on_key_down(self, f: impl FnMut(&mut KeyboardEvent) + 'static) -> Self { + self.0.read().on_key_down(f); + self + } + + pub fn on_key_down_capture(self, f: impl FnMut(&mut KeyboardEvent) + 'static) -> Self { + self.0.read().on_key_down_capture(f); + self + } + + pub fn on_key_up(self, f: impl FnMut(&mut KeyboardEvent) + 'static) -> Self { + self.0.read().on_key_up(f); + self + } + + pub fn on_key_up_capture(self, f: impl FnMut(&mut KeyboardEvent) + 'static) -> Self { + self.0.read().on_key_up_capture(f); + self + } + + pub fn on_focus(self, f: impl FnMut(&mut FocusEvent) + 'static) -> Self { + self.0.read().on_focus(f); + self + } + + pub fn on_blur(self, f: impl FnMut(&mut FocusEvent) + 'static) -> Self { + self.0.read().on_blur(f); + self + } + + pub fn on_focus_in(self, f: impl FnMut(&mut FocusEvent) + 'static) -> Self { + self.0.read().on_focus_in(f); + self + } + + pub fn on_focus_in_capture(self, f: impl FnMut(&mut FocusEvent) + 'static) -> Self { + self.0.read().on_focus_in_capture(f); + self + } + + pub fn on_focus_out(self, f: impl FnMut(&mut FocusEvent) + 'static) -> Self { + self.0.read().on_focus_out(f); + self + } + + pub fn on_focus_out_capture(self, f: impl FnMut(&mut FocusEvent) + 'static) -> Self { + self.0.read().on_focus_out_capture(f); + self + } +} + +impl Declare for MixBuiltin { + type Builder = MixBuiltinDeclarer; + fn declare_builder() -> Self::Builder { MixBuiltinDeclarer(State::value(MixBuiltin::default())) } +} + +impl DeclareBuilder for MixBuiltinDeclarer { + type Target = State; + fn build_declare(self, _: &BuildCtx) -> Self::Target { self.0 } +} + +impl ComposeChild for MixBuiltin { + type Child = Widget; + #[inline] + fn compose_child(this: impl StateWriter, child: Self::Child) -> impl WidgetBuilder { + move |ctx: &BuildCtx| match this.try_into_value() { + Ok(this) => { + let mut this = Some(this); + child + .id() + .assert_get(&ctx.tree.borrow().arena) + .query_most_outside(|m: &MixBuiltin| { + let this = this.take().unwrap(); + if !m.contain_flag(BuiltinFlags::Focus) && this.contain_flag(BuiltinFlags::Focus) { + this.callbacks_for_focus_node(); + } + m.merge(this) + }); + if let Some(this) = this { + if this.contain_flag(BuiltinFlags::Focus) { + this.callbacks_for_focus_node(); + } + child.attach_data(this, ctx) + } else { + child + } + } + Err(this) => { + if this.read().contain_flag(BuiltinFlags::Focus) { + this.read().callbacks_for_focus_node(); + } + child.attach_data(this, ctx) + } + } + } +} + +fn x_times_tap_map_filter( + x: usize, + dur: Duration, + capture: bool, +) -> impl FnMut(&mut Event) -> Option<&mut PointerEvent> { + assert!(x > 0); + struct TapInfo { + pointer_id: PointerId, + stamps: Vec, + } + + let mut type_info: Option = None; + move |e: &mut Event| { + let e = match e { + Event::Tap(e) if !capture => e, + Event::TapCapture(e) if capture => e, + _ => return None, + }; + let now = Instant::now(); + match &mut type_info { + Some(info) if info.pointer_id == e.id => { + if info.stamps.len() + 1 == x { + if now.duration_since(info.stamps[0]) <= dur { + // emit x-tap event and reset the tap info + type_info = None; + Some(e) + } else { + // remove the expired tap + info.stamps.remove(0); + info.stamps.push(now); + None + } + } else { + info.stamps.push(now); + None + } + } + _ => { + type_info = Some(TapInfo { pointer_id: e.id, stamps: vec![now] }); + None + } + } + } +} diff --git a/core/src/context/widget_ctx.rs b/core/src/context/widget_ctx.rs index 187e55f12..52a193019 100644 --- a/core/src/context/widget_ctx.rs +++ b/core/src/context/widget_ctx.rs @@ -72,6 +72,7 @@ pub trait WidgetCtx { pub(crate) trait WidgetCtxImpl { fn id(&self) -> WidgetId; + // todo: return sc instead of rc fn current_wnd(&self) -> Rc; #[inline] diff --git a/core/src/events.rs b/core/src/events.rs index baf1271e3..f4bf6a928 100644 --- a/core/src/events.rs +++ b/core/src/events.rs @@ -1,5 +1,6 @@ use self::dispatcher::DispatchInfo; use crate::{ + builtin_widgets::BuiltinFlags, context::{define_widget_context, WidgetCtx, WidgetCtxImpl}, prelude::AppCtx, widget_tree::WidgetId, @@ -11,8 +12,6 @@ pub(crate) mod dispatcher; mod pointers; pub use pointers::*; use ribir_geom::Point; -mod focus; -pub use focus::*; mod keyboard; pub use keyboard::*; mod character; @@ -21,6 +20,8 @@ mod wheel; pub use wheel::*; mod ime_pre_edit; pub use ime_pre_edit::*; +mod lifecycle; +pub use lifecycle::*; pub(crate) mod focus_mgr; mod listener_impl_helper; @@ -32,6 +33,8 @@ define_widget_context!( prevent_default: bool ); +pub type FocusEvent = CommonEvent; +pub type FocusBubbleEvent = CommonEvent; impl CommonEvent { /// The target property of the Event interface is a reference to the object /// onto which the event was dispatched. It is different from @@ -104,9 +107,163 @@ impl CommonEvent { pub fn button_num(&self) -> u32 { self.mouse_buttons().bits().count_ones() } } -pub trait EventListener { - type Event; - fn dispatch(&self, event: &mut Self::Event); +pub enum Event { + /// Event fired when the widget is mounted. This event is fired only once. + Mounted(LifecycleEvent), + /// Event fired when the widget is performed layout. This event may fire + /// multiple times in same frame if a widget modified after performed layout. + PerformedLayout(LifecycleEvent), + /// Event fired when the widget is disposed. This event is fired only once. + Disposed(LifecycleEvent), + PointerDown(PointerEvent), + PointerDownCapture(PointerEvent), + PointerUp(PointerEvent), + PointerUpCapture(PointerEvent), + PointerMove(PointerEvent), + PointerMoveCapture(PointerEvent), + PointerCancel(PointerEvent), + PointerEnter(PointerEvent), + PointerLeave(PointerEvent), + Tap(PointerEvent), + TapCapture(PointerEvent), + ImePreEdit(ImePreEditEvent), + ImePreEditCapture(ImePreEditEvent), + /// Firing the wheel event when the user rotates a wheel button on a pointing + /// device (typically a mouse). + Wheel(WheelEvent), + /// Same as `Wheel` but emit in capture phase. + WheelCapture(WheelEvent), + Chars(CharsEvent), + CharsCapture(CharsEvent), + /// The `KeyDown` event is fired when a key is pressed. + KeyDown(KeyboardEvent), + /// The `KeyDownCapture` event is same as `KeyDown` but emit in capture phase. + KeyDownCapture(KeyboardEvent), + /// The `KeyUp` event is fired when a key is released. + KeyUp(KeyboardEvent), + /// The `KeyUpCapture` event is same as `KeyUp` but emit in capture phase. + KeyUpCapture(KeyboardEvent), + /// The focus event fires when an widget has received focus. The main + /// difference between this event and focusin is that focusin bubbles while + /// focus does not. + Focus(FocusEvent), + /// The blur event fires when an widget has lost focus. The main difference + /// between this event and focusout is that focusout bubbles while blur does + /// not. + Blur(FocusEvent), + /// The focusin event fires when an widget is about to receive focus. The main + /// difference between this event and focus is that focusin bubbles while + /// focus does not. + FocusIn(FocusEvent), + /// The focusin capture event fires when an widget is about to receive focus. + /// The main difference between this event and focusin is that focusin emit in + /// bubbles phase but this event emit in capture phase. + FocusInCapture(FocusEvent), + /// The focusout event fires when an widget is about to lose focus. The main + /// difference between this event and blur is that focusout bubbles while blur + /// does not. + FocusOut(FocusEvent), + /// The focusout capture event fires when an widget is about to lose focus. + /// The main difference between this event and focusout is that focusout emit + /// in bubbles phase but this event emit in capture phase. + FocusOutCapture(FocusEvent), +} + +impl std::ops::Deref for Event { + type Target = CommonEvent; + + fn deref(&self) -> &Self::Target { + match self { + Event::Mounted(e) + | Event::PerformedLayout(e) + | Event::Disposed(e) + | Event::Focus(e) + | Event::Blur(e) + | Event::FocusIn(e) + | Event::FocusInCapture(e) + | Event::FocusOut(e) + | Event::FocusOutCapture(e) => e, + Event::PointerDown(e) + | Event::PointerDownCapture(e) + | Event::PointerUp(e) + | Event::PointerUpCapture(e) + | Event::PointerMove(e) + | Event::PointerMoveCapture(e) + | Event::PointerCancel(e) + | Event::PointerEnter(e) + | Event::PointerLeave(e) + | Event::Tap(e) + | Event::TapCapture(e) => e, + Event::ImePreEdit(e) | Event::ImePreEditCapture(e) => e, + Event::Wheel(e) | Event::WheelCapture(e) => e, + Event::Chars(e) | Event::CharsCapture(e) => e, + Event::KeyDown(e) | Event::KeyDownCapture(e) | Event::KeyUp(e) | Event::KeyUpCapture(e) => e, + } + } +} + +impl std::ops::DerefMut for Event { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + Event::Mounted(e) + | Event::PerformedLayout(e) + | Event::Disposed(e) + | Event::Focus(e) + | Event::Blur(e) + | Event::FocusIn(e) + | Event::FocusInCapture(e) + | Event::FocusOut(e) + | Event::FocusOutCapture(e) => e, + Event::PointerDown(e) + | Event::PointerDownCapture(e) + | Event::PointerUp(e) + | Event::PointerUpCapture(e) + | Event::PointerMove(e) + | Event::PointerMoveCapture(e) + | Event::PointerCancel(e) + | Event::PointerEnter(e) + | Event::PointerLeave(e) + | Event::Tap(e) + | Event::TapCapture(e) => e, + Event::ImePreEdit(e) | Event::ImePreEditCapture(e) => e, + Event::Wheel(e) | Event::WheelCapture(e) => e, + Event::Chars(e) | Event::CharsCapture(e) => e, + Event::KeyDown(e) | Event::KeyDownCapture(e) | Event::KeyUp(e) | Event::KeyUpCapture(e) => e, + } + } +} + +impl Event { + pub fn flags(&self) -> BuiltinFlags { + match self { + Event::Mounted(_) | Event::PerformedLayout(_) | Event::Disposed(_) => BuiltinFlags::Lifecycle, + Event::PointerDown(_) + | Event::PointerDownCapture(_) + | Event::PointerUp(_) + | Event::PointerUpCapture(_) + | Event::PointerMove(_) + | Event::PointerMoveCapture(_) + | Event::PointerCancel(_) + | Event::PointerEnter(_) + | Event::PointerLeave(_) + | Event::Tap(_) + | Event::TapCapture(_) => BuiltinFlags::Pointer, + Event::Wheel(_) | Event::WheelCapture(_) => BuiltinFlags::Wheel, + Event::ImePreEdit(_) + | Event::ImePreEditCapture(_) + | Event::Chars(_) + | Event::CharsCapture(_) + | Event::KeyDown(_) + | Event::KeyDownCapture(_) + | Event::KeyUp(_) + | Event::KeyUpCapture(_) => BuiltinFlags::KeyBoard, + Event::Focus(_) | Event::Blur(_) => BuiltinFlags::Focus, + Event::FocusIn(_) + | Event::FocusInCapture(_) + | Event::FocusOut(_) + | Event::FocusOutCapture(_) => BuiltinFlags::FocusInOut, + } + } } impl std::fmt::Debug for CommonEvent { diff --git a/core/src/events/character.rs b/core/src/events/character.rs index 60e7fbb76..3cb11207b 100644 --- a/core/src/events/character.rs +++ b/core/src/events/character.rs @@ -1,9 +1,4 @@ -use crate::{ - impl_all_event, impl_common_event_deref, impl_compose_child_with_focus_for_listener, - impl_listener, impl_multi_event_listener, prelude::*, window::WindowId, -}; -use rxrust::prelude::*; -use std::convert::Infallible; +use crate::{impl_common_event_deref, prelude::*, window::WindowId}; #[derive(Debug)] pub struct CharsEvent { @@ -11,17 +6,6 @@ pub struct CharsEvent { pub common: CommonEvent, } -pub type CharsSubject = MutRefItemSubject<'static, AllChars, Infallible>; - -impl_multi_event_listener! { - "The listener use to fire and listen chars events.", - Chars, - "", Chars, - "", CharsCapture -} - -impl_compose_child_with_focus_for_listener!(CharsListener); - impl_common_event_deref!(CharsEvent); impl CharsEvent { diff --git a/core/src/events/dispatcher.rs b/core/src/events/dispatcher.rs index 55bca0e93..d649d8b60 100644 --- a/core/src/events/dispatcher.rs +++ b/core/src/events/dispatcher.rs @@ -69,9 +69,7 @@ impl Dispatcher { if let Some(focus_id) = wnd.focusing() { let event = KeyboardEvent::new(wnd.id(), focus_id, physical_key, key, is_repeat, location); match state { - ElementState::Pressed => { - wnd.add_delay_event(DelayEvent::KeyDown(event)); - } + ElementState::Pressed => wnd.add_delay_event(DelayEvent::KeyDown(event)), ElementState::Released => wnd.add_delay_event(DelayEvent::KeyUp(event)), }; } else if key == VirtualKey::Named(NamedKey::Tab) { @@ -173,8 +171,14 @@ impl Dispatcher { let nearest_focus = self.pointer_down_uid.and_then(|wid| { wid.ancestors(&tree.arena).find(|id| { - id.get(&tree.arena) - .map_or(false, |w| w.contain_type::()) + let mut is_focus_node = false; + if let Some(w) = id.get(&tree.arena) { + w.query_type_outside_first(|m: &MixBuiltin| { + is_focus_node |= m.contain_flag(BuiltinFlags::Focus); + !is_focus_node + }); + } + is_focus_node }) }); if let Some(focus_id) = nearest_focus { diff --git a/core/src/events/focus.rs b/core/src/events/focus.rs deleted file mode 100644 index 1cf3b60ea..000000000 --- a/core/src/events/focus.rs +++ /dev/null @@ -1,48 +0,0 @@ -use rxrust::prelude::*; -use std::convert::Infallible; - -use crate::{ - impl_all_event, impl_compose_child_for_listener, impl_compose_child_with_focus_for_listener, - impl_listener, impl_multi_event_listener, prelude::*, -}; - -pub type FocusEvent = CommonEvent; -pub type FocusSubject = MutRefItemSubject<'static, AllFocus, Infallible>; - -impl_multi_event_listener! { - "The listener use to fire and listen focus events.", - Focus, - "The focus event fires when an widget has received focus. The main \ - difference between this event and focusin is that focusin bubbles while\ - focus does not.", - Focus, - "The blur event fires when an widget has lost focus. The main difference \ - between this event and focusout is that focusout bubbles while blur does not.", - Blur -} -impl_compose_child_with_focus_for_listener!(FocusListener); - -pub type FocusBubbleEvent = CommonEvent; -pub type FocusBubbleSubject = MutRefItemSubject<'static, AllFocusBubble, Infallible>; - -impl_multi_event_listener! { - "The listener use to fire and listen focusin and focusout events.", - FocusBubble, - "The focusin event fires when an widget is about to receive focus. The main \ - difference between this event and focus is that focusin bubbles while \ - focus does not.", - FocusIn, - "The focusin capture event fires when an widget is about to receive focus. The main \ - difference between this event and focusin is that focusin emit in bubbles phase \ - but this event emit in capture phase.", - FocusInCapture, - "The focusout event fires when an widget is about to lose focus. The main \ - difference between this event and blur is that focusout bubbles while blur \ - does not.", - FocusOut, - "The focusout capture event fires when an widget is about to lose focus. The main \ - difference between this event and focusout is that focusout emit in bubbles phase \ - but this event emit in capture phase.", - FocusOutCapture -} -impl_compose_child_for_listener!(FocusBubbleListener); diff --git a/core/src/events/focus_mgr.rs b/core/src/events/focus_mgr.rs index 9d8140dc4..894b2cf2a 100644 --- a/core/src/events/focus_mgr.rs +++ b/core/src/events/focus_mgr.rs @@ -362,7 +362,7 @@ impl FocusManager { let wid = self.get(node_id)?.wid?; let tree = wnd.widget_tree.borrow(); let r = wid.get(&tree.arena)?; - r.query_most_outside(|s: &FocusNode| s.tab_index) + r.query_most_outside(|s: &MixBuiltin| s.tab_index()) }; get_index().unwrap_or(0) diff --git a/core/src/events/ime_pre_edit.rs b/core/src/events/ime_pre_edit.rs index c4ea340b2..d360d2f16 100644 --- a/core/src/events/ime_pre_edit.rs +++ b/core/src/events/ime_pre_edit.rs @@ -1,6 +1,4 @@ -use crate::{impl_all_event, impl_common_event_deref, impl_multi_event_listener, prelude::*}; -use crate::{impl_compose_child_for_listener, impl_listener}; -use std::convert::Infallible; +use crate::{impl_common_event_deref, prelude::*}; #[derive(Debug)] pub enum ImePreEdit { @@ -44,17 +42,4 @@ impl ImePreEditEvent { } } -pub type ImePreEditSubject = MutRefItemSubject<'static, AllImePreEdit, Infallible>; - -impl_multi_event_listener! { - "The listener use to listen ime pre edit events.", - ImePreEdit, - "", - ImePreEdit, - "", - ImePreEditCapture -} - impl_common_event_deref!(ImePreEditEvent); - -impl_compose_child_for_listener!(ImePreEditListener); diff --git a/core/src/events/keyboard.rs b/core/src/events/keyboard.rs index c8b525b18..fac390dc7 100644 --- a/core/src/events/keyboard.rs +++ b/core/src/events/keyboard.rs @@ -1,10 +1,4 @@ -use rxrust::prelude::*; -use std::convert::Infallible; - -use crate::{ - impl_all_event, impl_common_event_deref, impl_compose_child_with_focus_for_listener, - impl_listener, impl_multi_event_listener, prelude::*, window::WindowId, -}; +use crate::{impl_common_event_deref, prelude::*, window::WindowId}; pub use winit::keyboard::{ Key as VirtualKey, KeyCode, KeyLocation, ModifiersState, NamedKey, PhysicalKey, @@ -33,25 +27,8 @@ impl KeyboardEvent { pub fn location(&self) -> KeyLocation { self.location } } -pub type KeyboardSubject = MutRefItemSubject<'static, AllKeyboard, Infallible>; - -impl_multi_event_listener! { - "The listener use to fire and listen keyboard events.", - Keyboard, - "The `KeyDown` event is fired when a key is pressed.", - KeyDown, - "The `KeyDownCapture` event is same as `KeyDown` but emit in capture phase.", - KeyDownCapture, - "The `KeyUp` event is fired when a key is released.", - KeyUp, - "The `KeyUpCapture` event is same as `KeyUp` but emit in capture phase.", - KeyUpCapture -} - impl_common_event_deref!(KeyboardEvent); -impl_compose_child_with_focus_for_listener!(KeyboardListener); - impl KeyboardEvent { #[inline] pub fn new( diff --git a/core/src/builtin_widgets/lifecycle.rs b/core/src/events/lifecycle.rs similarity index 60% rename from core/src/builtin_widgets/lifecycle.rs rename to core/src/events/lifecycle.rs index d6f51f8ff..dcb1b6d2d 100644 --- a/core/src/builtin_widgets/lifecycle.rs +++ b/core/src/events/lifecycle.rs @@ -1,91 +1,7 @@ -use crate::{impl_all_event, impl_compose_child_for_listener, prelude::*, window::WindowId}; -use rxrust::prelude::*; -use std::{convert::Infallible, rc::Rc}; +use super::*; -define_widget_context!(LifecycleEvent); - -pub type LifecycleSubject = MutRefItemSubject<'static, AllLifecycle, Infallible>; - -#[derive(Default, Query)] -pub struct LifecycleListener { - lifecycle: LifecycleSubject, -} - -impl_all_event!( - Lifecycle, - "Event fired when the widget is mounted. This event is fired only once.", - Mounted, - "Event fired when the widget is performed layout. This event may fire multiple \ - times in same frame if a widget modified after performed layout.", - PerformedLayout, - "Event fired when the widget is disposed. This event is fired only once.", - Disposed -); - -impl_compose_child_for_listener!(LifecycleListener); - -impl LifecycleListener { - #[inline] - pub fn lifecycle_stream(&self) -> LifecycleSubject { self.lifecycle.clone() } -} - -macro_rules! match_closure { - ($event_ty: ident) => { - (|e| match e { - AllLifecycle::$event_ty(e) => Some(e), - _ => None, - }) as fn(&mut AllLifecycle) -> Option<&mut LifecycleEvent> - }; -} - -impl Declare for LifecycleListener { - type Builder = Self; - fn declare_builder() -> Self::Builder { Self::default() } -} - -impl DeclareBuilder for LifecycleListener { - type Target = State; - fn build_declare(self, _: &BuildCtx) -> Self::Target { State::value(self) } -} - -impl LifecycleListener { - 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.clone() } -} - -impl EventListener for LifecycleListener { - type Event = AllLifecycle; - #[inline] - fn dispatch(&self, event: &mut Self::Event) { self.lifecycle.clone().next(event) } -} +/// The event fired when the widget is mounted, performed layout or disposed. +pub type LifecycleEvent = CommonEvent; #[cfg(test)] mod tests { diff --git a/core/src/events/listener_impl_helper.rs b/core/src/events/listener_impl_helper.rs index ef4b62f65..2ee64a4f2 100644 --- a/core/src/events/listener_impl_helper.rs +++ b/core/src/events/listener_impl_helper.rs @@ -1,161 +1,3 @@ -#[macro_export] -macro_rules! impl_all_event { - ($name: ident, $($on_doc: literal, $event_ty: ident),+) => { - paste::paste! { - #[doc="All `" $name:snake "` related events"] - pub enum [] { - $( - #[doc = $on_doc] - $event_ty([<$name Event>]), - )+ - } - - impl std::ops::Deref for [] { - type Target = [<$name Event>]; - fn deref(&self) -> &Self::Target { - match self { - $([]::$event_ty(e)) |+ => e - } - } - } - - impl std::ops::DerefMut for [] { - fn deref_mut(&mut self) -> &mut Self::Target { - match self { - $([]::$event_ty(e)) |+ => e - } - } - } - - impl [] { - pub fn into_inner(self) -> [<$name Event>] { - match self { - $([]::$event_ty(e)) |+ => e - } - } - } - } - }; -} - -#[macro_export] -macro_rules! impl_listener { - ($doc: literal, $name: ident, $event_ty: ident) => { - paste::paste! { - #[doc= $doc] - #[derive(Query)] - pub struct [<$name Listener>]{ - [<$name:snake _subject>]: [<$name Subject>] - } - - impl [<$name Listener>] { - fn subject(&mut self) -> [<$name Subject>] { - self - .[<$name:snake _subject>] - .clone() - } - } - - impl Declare for [<$name Listener>] { - type Builder = Self; - fn declare_builder() -> Self::Builder { - Self { [<$name:snake _subject>]: Default::default()} - } - } - - impl DeclareBuilder for [<$name Listener>] { - type Target = State; - fn build_declare(self, _ctx: &BuildCtx) -> Self::Target { State::value(self) } - } - - impl [<$name Listener>] { - /// Convert a observable stream of this event. - pub fn [<$name:snake _stream>](&self) -> [<$name Subject>] { - self.[<$name:snake _subject>].clone() - } - } - - impl EventListener for [<$name Listener>] { - type Event = $event_ty; - #[inline] - fn dispatch(&self, event: &mut Self::Event) { - self.[<$name:snake _subject>].clone().next(event) - } - } - } - }; -} - -#[macro_export] -macro_rules! impl_multi_event_listener { - ( - $doc: literal, $name: ident, - $($on_doc: literal, $event_ty: ident),+ - ) => { - paste::paste! { - impl_all_event!($name, $($on_doc, $event_ty),+); - impl_listener!($doc, $name, []); - - impl [<$name Listener>] { - $( - #[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 - } - )+ - } - } - }; -} - -#[macro_export] -macro_rules! impl_single_event_listener { - ($doc: literal, $name: ident) => { - paste::paste! { - impl_listener!($doc, $name); - - impl [<$name ListenerDeclarer2>] { - #[doc = "Sets up a function that will be called whenever the `" [<$name Event>] "` is delivered"] - pub fn []( - self, - handler: impl FnMut(&'_ mut [<$name Event>]<'_>) + 'static - ) -> Self { - self - .subject() - .subscribe(handler); - self - } - } - - impl [<$name ListenerDeclarer>] { - #[doc = "Sets up a function that will be called whenever the `" [<$name Event>] "` is delivered"] - pub fn []( - self, - handler: impl FnMut(&'_ mut [<$name Event>]<'_>) + 'static - ) -> Self { - self - .subject() - .subscribe(handler); - self - } - } - } - }; -} - #[macro_export] macro_rules! impl_common_event_deref { ($event_name: ident) => { @@ -182,39 +24,3 @@ macro_rules! impl_common_event_deref { } }; } - -#[macro_export] -macro_rules! impl_compose_child_for_listener { - ($listener: ident) => { - impl ComposeChild for $listener { - type Child = Widget; - #[inline] - fn compose_child( - this: impl StateWriter, - child: Self::Child, - ) -> impl WidgetBuilder { - move |ctx: &BuildCtx| child.attach_state_data(this, ctx) - } - } - }; -} - -#[macro_export] -macro_rules! impl_compose_child_with_focus_for_listener { - ($listener: ident) => { - impl ComposeChild for $listener { - type Child = Widget; - fn compose_child( - this: impl StateWriter, - child: Self::Child, - ) -> impl WidgetBuilder { - fn_widget! { - @FocusNode { - tab_index: 0i16, auto_focus: false, - @ { child.attach_state_data(this, ctx!()) } - } - } - } - } - }; -} diff --git a/core/src/events/pointers.rs b/core/src/events/pointers.rs index 1accc1fcd..7a5786ec8 100644 --- a/core/src/events/pointers.rs +++ b/core/src/events/pointers.rs @@ -1,15 +1,6 @@ use super::CommonEvent; -use crate::{ - impl_all_event, impl_common_event_deref, impl_compose_child_for_listener, impl_listener, - impl_multi_event_listener, prelude::*, -}; -use rxrust::prelude::*; -use std::{ - convert::Infallible, - time::{Duration, Instant}, -}; +use crate::impl_common_event_deref; mod from_mouse; -const MULTI_TAP_DURATION: Duration = Duration::from_millis(250); #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct PointerId(usize); @@ -83,134 +74,11 @@ pub enum PointerType { Touch, } -impl_multi_event_listener!( - "The listener use to fire and listen pointer events.", - Pointer, - "", - PointerDown, - "", - PointerDownCapture, - "", - PointerUp, - "", - PointerUpCapture, - "", - PointerMove, - "", - PointerMoveCapture, - "", - PointerCancel, - "", - PointerEnter, - "", - PointerLeave, - "", - Tap, - "", - TapCapture -); - impl_common_event_deref!(PointerEvent); - -pub type PointerSubject = MutRefItemSubject<'static, AllPointer, Infallible>; - -impl_compose_child_for_listener!(PointerListener); - -fn x_times_tap_map_filter( - x: usize, - dur: Duration, - capture: bool, -) -> impl FnMut(&mut AllPointer) -> Option<&mut PointerEvent> { - assert!(x > 0); - struct TapInfo { - pointer_id: PointerId, - stamps: Vec, - } - - let mut type_info: Option = None; - move |e: &mut AllPointer| { - let e = match e { - AllPointer::Tap(e) if !capture => e, - AllPointer::TapCapture(e) if capture => e, - _ => return None, - }; - let now = Instant::now(); - match &mut type_info { - Some(info) if info.pointer_id == e.id => { - if info.stamps.len() + 1 == x { - if now.duration_since(info.stamps[0]) <= dur { - // emit x-tap event and reset the tap info - type_info = None; - Some(e) - } else { - // remove the expired tap - info.stamps.remove(0); - info.stamps.push(now); - None - } - } else { - info.stamps.push(now); - None - } - } - _ => { - type_info = Some(TapInfo { pointer_id: e.id, stamps: vec![now] }); - None - } - } - } -} - -impl PointerListener { - 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 - } -} - #[cfg(test)] mod tests { - use super::*; use crate::{ + prelude::*, reset_test_env, test_helper::{MockBox, MockMulti, TestWindow}, }; diff --git a/core/src/events/wheel.rs b/core/src/events/wheel.rs index 533bd8c5d..f513f3baa 100644 --- a/core/src/events/wheel.rs +++ b/core/src/events/wheel.rs @@ -1,9 +1,4 @@ -use crate::{ - impl_all_event, impl_common_event_deref, impl_compose_child_for_listener, impl_listener, - impl_multi_event_listener, prelude::*, window::WindowId, -}; -use rxrust::prelude::*; -use std::convert::Infallible; +use crate::{impl_common_event_deref, prelude::*, window::WindowId}; #[derive(Debug)] pub struct WheelEvent { @@ -12,18 +7,6 @@ pub struct WheelEvent { pub common: CommonEvent, } -pub type WheelSubject = MutRefItemSubject<'static, AllWheel, Infallible>; - -impl_multi_event_listener! { - "The listener use to fire and listen wheel events.", - Wheel, - "Firing the wheel event when the user rotates a wheel button on a pointing \ - device (typically a mouse).", - Wheel, - "Same as `Wheel` but emit in capture phase.", - WheelCapture -} -impl_compose_child_for_listener!(WheelListener); impl_common_event_deref!(WheelEvent); impl WheelEvent { diff --git a/core/src/window.rs b/core/src/window.rs index 6339b9057..91129870d 100644 --- a/core/src/window.rs +++ b/core/src/window.rs @@ -16,11 +16,9 @@ use futures::{ use ribir_geom::Point; use rxrust::{scheduler::FuturesLocalScheduler, subject::Subject}; use std::{ - borrow::BorrowMut, cell::{Cell, RefCell}, collections::VecDeque, convert::Infallible, - ops::{Deref, DerefMut}, rc::Rc, time::Instant, }; @@ -400,12 +398,12 @@ impl Window { match e { DelayEvent::Mounted(id) => { - let e = AllLifecycle::Mounted(LifecycleEvent { id, wnd_id: self.id() }); - self.emit::(id, e); + let mut e = Event::Mounted(LifecycleEvent::new(id, self.id())); + self.emit(id, &mut e); } DelayEvent::PerformedLayout(id) => { - let e = AllLifecycle::PerformedLayout(LifecycleEvent { id, wnd_id: self.id() }); - self.emit::(id, e); + let mut e = Event::PerformedLayout(LifecycleEvent::new(id, self.id())); + self.emit(id, &mut e); } DelayEvent::Disposed { id, parent } => { id.descendants(&self.widget_tree.borrow().arena) @@ -416,8 +414,8 @@ impl Window { if Some(id) == self.focusing() { self.focus_mgr.borrow_mut().blur_on_dispose(); } - let e = AllLifecycle::Disposed(LifecycleEvent { id, wnd_id: self.id() }); - self.emit::(id, e); + let mut e = Event::Disposed(LifecycleEvent::new(id, self.id())); + self.emit(id, &mut e); }); let delay_drop = id @@ -434,33 +432,36 @@ impl Window { self.widget_tree.borrow_mut().remove_subtree(id); } DelayEvent::Focus(id) => { - let e = AllFocus::Focus(FocusEvent::new(id, self.id())); - self.emit::(id, e); + let mut e = Event::Focus(FocusEvent::new(id, self.id())); + self.emit(id, &mut e); } DelayEvent::FocusIn { bottom, up } => { - let mut e = AllFocusBubble::FocusInCapture(FocusEvent::new(bottom, self.id())); - self.top_down_emit::(&mut e, bottom, up); - let mut e = AllFocusBubble::FocusIn(e.into_inner()); - self.bottom_up_emit::(&mut e, bottom, up); + let mut e = Event::FocusInCapture(FocusEvent::new(bottom, self.id())); + self.top_down_emit(&mut e, bottom, up); + let mut e = Event::FocusIn(FocusEvent::new(bottom, self.id())); + self.bottom_up_emit(&mut e, bottom, up); } DelayEvent::Blur(id) => { - let e = AllFocus::Blur(FocusEvent::new(id, self.id())); - self.emit::(id, e); + let mut e = Event::Blur(FocusEvent::new(id, self.id())); + self.emit(id, &mut e); } DelayEvent::FocusOut { bottom, up } => { - let mut e = AllFocusBubble::FocusOutCapture(FocusEvent::new(bottom, self.id())); - self.top_down_emit::(&mut e, bottom, up); - let mut e = AllFocusBubble::FocusOut(e.into_inner()); - self.bottom_up_emit::(&mut e, bottom, up); + let mut e = Event::FocusOutCapture(FocusEvent::new(bottom, self.id())); + self.top_down_emit(&mut e, bottom, up); + let mut e = Event::FocusOut(FocusEvent::new(bottom, self.id())); + self.bottom_up_emit(&mut e, bottom, up); } DelayEvent::KeyDown(event) => { let id = event.id(); - let mut e = AllKeyboard::KeyDownCapture(event); - self.top_down_emit::(&mut e, id, None); - let mut e = AllKeyboard::KeyDown(e.into_inner()); - self.bottom_up_emit::(&mut e, id, None); - + let mut e = Event::KeyDownCapture(event); + self.top_down_emit(&mut e, id, None); + let Event::KeyDownCapture(e) = e else { + unreachable!() + }; + let mut e = Event::KeyDown(e); + self.bottom_up_emit(&mut e, id, None); + let Event::KeyDown(e) = e else { unreachable!() }; if !e.is_prevent_default() && *e.key() == VirtualKey::Named(NamedKey::Tab) { self.add_delay_event(DelayEvent::TabFocusMove); } @@ -480,95 +481,96 @@ impl Window { } DelayEvent::KeyUp(event) => { let id = event.id(); - let mut e = AllKeyboard::KeyUpCapture(event); - self.top_down_emit::(&mut e, id, None); - let mut e = AllKeyboard::KeyUp(e.into_inner()); - self.bottom_up_emit::(&mut e, id, None); + let mut e = Event::KeyUpCapture(event); + self.top_down_emit(&mut e, id, None); + let Event::KeyUpCapture(e) = e else { + unreachable!() + }; + let mut e = Event::KeyUp(e); + self.bottom_up_emit(&mut e, id, None); } DelayEvent::Chars { id, chars } => { - let mut e = AllChars::CharsCapture(CharsEvent::new(chars, id, self.id())); - self.top_down_emit::(&mut e, id, None); - let mut e = AllChars::Chars(e.into_inner()); - self.bottom_up_emit::(&mut e, id, None); + let mut e = Event::CharsCapture(CharsEvent::new(chars, id, self.id())); + self.top_down_emit(&mut e, id, None); + let Event::CharsCapture(e) = e else { + unreachable!() + }; + let mut e = Event::Chars(e); + self.bottom_up_emit(&mut e, id, None); } DelayEvent::Wheel { id, delta_x, delta_y } => { - let mut e = AllWheel::WheelCapture(WheelEvent::new(delta_x, delta_y, id, self.id())); - self.top_down_emit::(&mut e, id, None); - let mut e = AllWheel::Wheel(e.into_inner()); - self.bottom_up_emit::(&mut e, id, None); + let mut e = Event::WheelCapture(WheelEvent::new(delta_x, delta_y, id, self.id())); + self.top_down_emit(&mut e, id, None); + let mut e = Event::Wheel(WheelEvent::new(delta_x, delta_y, id, self.id())); + self.bottom_up_emit(&mut e, id, None); } DelayEvent::PointerDown(id) => { - let mut e = AllPointer::PointerDownCapture(PointerEvent::from_mouse(id, self)); - self.top_down_emit::(&mut e, id, None); - let mut e = AllPointer::PointerDown(e.into_inner()); - self.bottom_up_emit::(&mut e, id, None); + let mut e = Event::PointerDownCapture(PointerEvent::from_mouse(id, self)); + self.top_down_emit(&mut e, id, None); + let mut e = Event::PointerDown(PointerEvent::from_mouse(id, self)); + self.bottom_up_emit(&mut e, id, None); self .focus_mgr .borrow_mut() .refresh_focus(&self.widget_tree.borrow().arena); } DelayEvent::PointerMove(id) => { - let mut e = AllPointer::PointerMoveCapture(PointerEvent::from_mouse(id, self)); - self.top_down_emit::(&mut e, id, None); - let mut e = AllPointer::PointerMove(e.into_inner()); - self.bottom_up_emit::(&mut e, id, None); + let mut e = Event::PointerMoveCapture(PointerEvent::from_mouse(id, self)); + self.top_down_emit(&mut e, id, None); + let mut e = Event::PointerMove(PointerEvent::from_mouse(id, self)); + self.bottom_up_emit(&mut e, id, None); } DelayEvent::PointerUp(id) => { - let mut e = AllPointer::PointerUpCapture(PointerEvent::from_mouse(id, self)); - self.top_down_emit::(&mut e, id, None); - let mut e = AllPointer::PointerUp(e.into_inner()); - self.bottom_up_emit::(&mut e, id, None); + let mut e = Event::PointerUpCapture(PointerEvent::from_mouse(id, self)); + self.top_down_emit(&mut e, id, None); + let mut e = Event::PointerUp(PointerEvent::from_mouse(id, self)); + self.bottom_up_emit(&mut e, id, None); } DelayEvent::_PointerCancel(id) => { - let mut e = AllPointer::PointerCancel(PointerEvent::from_mouse(id, self)); - self.bottom_up_emit::(&mut e, id, None); + let mut e = Event::PointerCancel(PointerEvent::from_mouse(id, self)); + self.bottom_up_emit(&mut e, id, None); } DelayEvent::PointerEnter { bottom, up } => { - let mut e = AllPointer::PointerEnter(PointerEvent::from_mouse(bottom, self)); - self.top_down_emit::(&mut e, bottom, up); + let mut e = Event::PointerEnter(PointerEvent::from_mouse(bottom, self)); + self.top_down_emit(&mut e, bottom, up); } DelayEvent::PointerLeave { bottom, up } => { - let mut e = AllPointer::PointerLeave(PointerEvent::from_mouse(bottom, self)); - self.bottom_up_emit::(&mut e, bottom, up); + let mut e = Event::PointerLeave(PointerEvent::from_mouse(bottom, self)); + self.bottom_up_emit(&mut e, bottom, up); } DelayEvent::Tap(wid) => { - let mut e = AllPointer::TapCapture(PointerEvent::from_mouse(wid, self)); - self.top_down_emit::(&mut e, wid, None); - let mut e = AllPointer::Tap(e.into_inner()); - self.bottom_up_emit::(&mut e, wid, None); + let mut e = Event::TapCapture(PointerEvent::from_mouse(wid, self)); + self.top_down_emit(&mut e, wid, None); + let mut e = Event::Tap(PointerEvent::from_mouse(wid, self)); + self.bottom_up_emit(&mut e, wid, None); } DelayEvent::ImePreEdit { wid, pre_edit } => { - let mut e = AllImePreEdit::ImePreEditCapture(ImePreEditEvent::new(pre_edit, wid, self)); - self.top_down_emit::(&mut e, wid, None); - let mut e = AllImePreEdit::ImePreEdit(e.into_inner()); - self.bottom_up_emit::(&mut e, wid, None); + let mut e = Event::ImePreEditCapture(ImePreEditEvent::new(pre_edit, wid, self)); + self.top_down_emit(&mut e, wid, None); + let Event::ImePreEditCapture(e) = e else { + unreachable!() + }; + self.bottom_up_emit(&mut Event::ImePreEdit(e), wid, None); } } } } - fn emit(&self, id: WidgetId, mut e: L::Event) - where - L: EventListener + 'static, - { + fn emit(&self, id: WidgetId, e: &mut Event) { // Safety: we only use tree to query the inner data of a node and dispatch a // event by it, and never read or write the node. And in the callback, there is // no way to mut access the inner data of node or destroy the node. let tree = unsafe { &*(&*self.widget_tree.borrow() as *const WidgetTree) }; - id.assert_get(&tree.arena).query_type_inside_first(|m: &L| { - m.dispatch(&mut e); - true - }); + id.assert_get(&tree.arena) + .query_type_inside_first(|m: &MixBuiltin| { + if m.contain_flag(e.flags()) { + m.dispatch(e); + } + true + }); } - fn top_down_emit(&self, e: &mut L::Event, bottom: WidgetId, up: Option) - where - L: EventListener + 'static, - L::Event: DerefMut, - ::Target: std::borrow::BorrowMut, - { - use std::borrow::Borrow; - + fn top_down_emit(&self, e: &mut Event, bottom: WidgetId, up: Option) { let tree = self.widget_tree.borrow(); let path = bottom .ancestors(&tree.arena) @@ -577,23 +579,18 @@ impl Window { path.iter().rev().all(|id| { id.assert_get(&tree.arena) - .query_type_outside_first(|m: &L| { - (**e).borrow_mut().set_current_target(*id); - m.dispatch(e); - (**e).borrow_mut().is_propagation() - }); - (**e).borrow().is_propagation() + .query_type_outside_first(|m: &MixBuiltin| { + if m.contain_flag(e.flags()) { + e.set_current_target(*id); + m.dispatch(e); + } + e.is_propagation() + }) }); } - fn bottom_up_emit(&self, e: &mut L::Event, bottom: WidgetId, up: Option) - where - L: EventListener + 'static, - L::Event: DerefMut, - ::Target: std::borrow::BorrowMut, - { - use std::borrow::Borrow; - if !(**e).borrow().is_propagation() { + fn bottom_up_emit(&self, e: &mut Event, bottom: WidgetId, up: Option) { + if !e.is_propagation() { return; } @@ -602,12 +599,14 @@ impl Window { .ancestors(&tree.arena) .take_while(|id| Some(*id) != up) .all(|id| { - id.assert_get(&tree.arena).query_type_inside_first(|m: &L| { - (**e).borrow_mut().set_current_target(id); - m.dispatch(e); - (**e).borrow_mut().is_propagation() - }); - (**e).borrow().is_propagation() + id.assert_get(&tree.arena) + .query_type_inside_first(|m: &MixBuiltin| { + if m.contain_flag(e.flags()) { + e.set_current_target(id); + m.dispatch(e); + } + e.is_propagation() + }) }); } diff --git a/docs/builtin_widget/declare_builtin_fields.md b/docs/builtin_widget/declare_builtin_fields.md index e61991f69..8c839cee5 100644 --- a/docs/builtin_widget/declare_builtin_fields.md +++ b/docs/builtin_widget/declare_builtin_fields.md @@ -1,5 +1,19 @@ # Full builtin fields list +- auto_focus : [`bool`] + - Indicates whether the widget should automatically get focus when the window loads. +- tab_index : [`i16`] + - indicates that widget can be focused, and where it participates in sequential keyboard navigation (usually with the Tab key, hence the name. +- on_event : [`impl FnMut(& mut Event)`] + - action perform after any event received. +- on_mounted : [`Box < dyn for < 'r > FnMut(LifeCycleCtx < 'r >, MountedType) >`] + - action perform after widget be added to the widget tree. +- on_disposed : [`Box < dyn for < 'r > FnMut(LifeCycleCtx < 'r >, DisposedType) >`] + - action perform after widget remove from widget tree. +- on_performed_layout : [`impl FnMut(& mut PointerEvent)`] + - specify the event handler for the pointer down event in bubble phase. +- lifecycle_stream : [`LifecycleSubject`] + - return the stream of lifecycle. - on_pointer_down : [`impl FnMut(& mut PointerEvent)`] - specify the event handler for the pointer down event in bubble phase. - on_pointer_down_capture : [`impl FnMut(& mut PointerEvent)`] @@ -34,10 +48,26 @@ - specify the event handler for the pointer `x` times tap event in bubble phase. - on_x_times_tap_capture : [`(usize, Box < dyn for < 'r > FnMut(& 'r mut PointerEvent) >)`] - specify the event handler for the pointer `x` times tap event in capture phase. -- auto_focus : [`bool`] - - Indicates whether the widget should automatically get focus when the window loads. -- tab_index : [`i16`] - - indicates that widget can be focused, and where it participates in sequential keyboard navigation (usually with the Tab key, hence the name. +- on_ime_pre_edit : [`impl FnMut(& mut ImePreEditEvent)`] + - specify the event handler when received unicode characters in ime pre edit bubble phase. +- on_ime_pre_edit_capture : [`impl FnMut(& mut ImePreEditEvent)`] + - specify the event handler when received unicode characters in ime pre edit capture phase. +- on_wheel : [`impl FnMut(& mut WheelEvent)`] + - specify the event handler when user moving a mouse wheel or similar input device in bubble phase. +- on_wheel_capture : [`impl FnMut(& mut WheelEvent)`] + - specify the event handler when user moving a mouse wheel or similar input device in capture phase. +- on_chars : [`impl FnMut(& mut CharsEvent)`] + - specify the event handler when received unicode characters in bubble phase. +- on_chars_capture : [`impl FnMut(& mut CharsEvent)`] + - specify the event handler when received unicode characters in capture phase. +- on_key_down_capture : [`impl FnMut(& mut KeyboardEvent)`] + - specify the event handler when keyboard press down in capture phase. +- on_key_down : [`impl FnMut(& mut KeyboardEvent)`] + - specify the event handler when keyboard press down in bubble phase. +- on_key_up : [`impl FnMut(& mut KeyboardEvent)`] + - specify the event handler when a key is released in bubble phase. +- on_key_up_capture : [`impl FnMut(& mut KeyboardEvent)`] + - specify the event handler when a key is released in capture phase. - on_focus : [`impl FnMut(& mut FocusEvent)`] - specify the event handler to process focus event. - on_blur : [`impl FnMut(& mut FocusEvent)`] @@ -50,26 +80,6 @@ - specify the event handler to process focusout event in bubble phase. - on_focus_out_capture : [`impl FnMut(& mut FocusEvent)`] - specify the event handler to process focusout event in capture phase. -- on_key_down_capture : [`impl FnMut(& mut KeyboardEvent)`] - - specify the event handler when keyboard press down in capture phase. -- on_key_down : [`impl FnMut(& mut KeyboardEvent)`] - - specify the event handler when keyboard press down in bubble phase. -- on_key_up : [`impl FnMut(& mut KeyboardEvent)`] - - specify the event handler when a key is released in bubble phase. -- on_key_up_capture : [`impl FnMut(& mut KeyboardEvent)`] - - specify the event handler when a key is released in capture phase. -- on_chars : [`impl FnMut(& mut CharsEvent)`] - - specify the event handler when received unicode characters in bubble phase. -- on_chars_capture : [`impl FnMut(& mut CharsEvent)`] - - specify the event handler when received unicode characters in capture phase. -- on_ime_pre_edit : [`impl FnMut(& mut ImePreEditEvent)`] - - specify the event handler when received unicode characters in ime pre edit bubble phase. -- on_ime_pre_edit_capture : [`impl FnMut(& mut ImePreEditEvent)`] - - specify the event handler when received unicode characters in ime pre edit capture phase. -- on_wheel : [`impl FnMut(& mut WheelEvent)`] - - specify the event handler when user moving a mouse wheel or similar input device in bubble phase. -- on_wheel_capture : [`impl FnMut(& mut WheelEvent)`] - - specify the event handler when user moving a mouse wheel or similar input device in capture phase. - box_fit : [`BoxFit`] - set how its child should be resized to its box. - background : [`Brush`] @@ -102,14 +112,6 @@ - Whether to show or hide a child - opacity : [`f32`] - Opacity is the degree to which content behind an element is hidden, and is the opposite of transparency. -- on_mounted : [`Box < dyn for < 'r > FnMut(LifeCycleCtx < 'r >, MountedType) >`] - - action perform after widget be added to the widget tree. -- on_disposed : [`Box < dyn for < 'r > FnMut(LifeCycleCtx < 'r >, DisposedType) >`] - - action perform after widget remove from widget tree. -- on_performed_layout : [`impl FnMut(& mut PointerEvent)`] - - specify the event handler for the pointer down event in bubble phase. -- lifecycle_stream : [`LifecycleSubject`] - - return the stream of lifecycle. - delay_drop_until : [`bool`] - The widget delay the drop of its child until the field delay_drop_until is false, but not affect its dispose event emit time. It's useful to ensure the disappear-animate display fully. @@ -119,44 +121,8 @@ - `fn lazy_id(& self) -> LazyWidgetId` - Return the LazyWidgetId of the external widget (wrapped with the built-in host), through which you can access the WidgetId after building. - - `fn double_tap_stream(& self,) -> FilterMapOp < MutRefItemSubject < 'static, -PointerEvent, () >, impl FnMut(& mut PointerEvent) -> Option < & mut -PointerEvent >, & mut PointerEvent, >` - - Return an observable stream of double tap event in bubble phase. - - - `fn triple_tap_stream(& self,) -> FilterMapOp < MutRefItemSubject < 'static, -PointerEvent, () >, impl FnMut(& mut PointerEvent) -> Option < & mut -PointerEvent >, & mut PointerEvent, >` - - Return an observable stream of tripe tap event in bubble phase. - - - `fn x_times_tap_stream(& self, x : usize, dur : Duration,) -> FilterMapOp < -MutRefItemSubject < 'static, PointerEvent, () >, impl -FnMut(& mut PointerEvent) -> Option < & mut PointerEvent >, & mut -PointerEvent, >` - - Return an observable stream of x-tap event that user tapped 'x' times in the specify duration `dur` in bubble phase. - - - `fn tap_capture_stream(& self) -> MutRefItemSubject < 'static, PointerEvent, () ->` - - return an observable stream of the pointer tap event in capture phase. - - - `fn double_tap_capture_stream(& self,) -> FilterMapOp < MutRefItemSubject < -'static, PointerEvent, () >, impl FnMut(& mut PointerEvent) -> Option < & mut -PointerEvent >, & mut PointerEvent, >` - - return an observable stream of double tap event in capture phase. - - - `fn triple_tap_capture_stream(& self,) -> FilterMapOp < MutRefItemSubject < -'static, PointerEvent, () >, impl FnMut(& mut PointerEvent) -> Option < & mut -PointerEvent >, & mut PointerEvent, >` - - Return an observable stream of tripe tap event in capture phase. - - - `fn x_times_tap_capture_stream(& self, x : usize, dur : Duration,) -> -FilterMapOp < MutRefItemSubject < 'static, PointerEvent, () >, impl -FnMut(& mut PointerEvent) -> Option < & mut PointerEvent >, & mut -PointerEvent, >` - - Return an observable stream of x-tap event that user tapped 'x' times in the specify duration `dur` in capture phase. - - - `fn pointer_stream(& self) -> PointerSubject` - - return the stream include all pointer events. + - `fn subject(& self) -> EventSubject` + - return the stream of the event. - `fn request_focus(& self)` - request the this node to be focused. @@ -164,25 +130,9 @@ PointerEvent, >` - `fn unfocus(& self)` - removes the focus from this node. - - `fn focus_stream(& self) -> MutRefItemSubject < 'static, FocusEvent, () >` - - Return the stream include all focus and blur events. - - - `fn focus_bubble_stream(& self) -> MutRefItemSubject < 'static, FocusEvent, () ->` - - Return the stream include all focus in/out related events. - - `fn has_focus(& self) -> bool` - return if the widget has focus. - - `fn keyboard_stream(& self) -> KeyboardSubject` - - return the stream include all keyboard events. - - - `fn chars_stream(& self) -> CharsSubject` - - return the stream include all chars events. - - - `fn wheel_stream(& self) -> MutRefItemSubject < 'static, WheelEvent, () >` - - return the stream include all wheel events. - - `fn mouse_hover(& self) -> bool` - return if the pointer is hover on the widget diff --git a/examples/wordle_game/src/ui.rs b/examples/wordle_game/src/ui.rs index 34e0d6a08..d71ee19e7 100644 --- a/examples/wordle_game/src/ui.rs +++ b/examples/wordle_game/src/ui.rs @@ -29,23 +29,23 @@ trait WordleExtraWidgets: StateWriter + Sized { fn_widget! { let palette = Palette::of(ctx!()); @Column { - main_axis_gap: 5., + item_gap: 5., align_items: Align::Center, justify_content: JustifyContent::Center, @Row { - main_axis_gap: 5., + item_gap: 5., align_items: Align::Center, justify_content: JustifyContent::Center, @ { self.chars_key(['Q', 'W', 'E', 'R','T', 'Y', 'U', 'I','O', 'P']) } } @Row { - main_axis_gap: 5., + item_gap: 5., align_items: Align::Center, justify_content: JustifyContent::Center, @ { self.chars_key(['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L' ]) } } @Row { - main_axis_gap: 5., + item_gap: 5., align_items: Align::Center, justify_content: JustifyContent::Center, @FilledButton { @@ -72,13 +72,13 @@ trait WordleExtraWidgets: StateWriter + Sized { let this = self.clone_writer(); fn_widget! { @Column { - main_axis_gap: 5., + item_gap: 5., align_items: Align::Center, justify_content: JustifyContent::Center, @ { (0..$this.max_rounds()).map(move |row| { @Row { - main_axis_gap: 5., + item_gap: 5., align_items: Align::Center, justify_content: JustifyContent::Center, @ { @@ -192,7 +192,7 @@ impl Compose for Wordle { h_align: HAlign::Center, align_items: Align::Center, justify_content: JustifyContent::Center, - main_axis_gap: 5., + item_gap: 5., @H1 { text: "Wordle" } @Divider { extent: 20. } @ {this.chars_grid()} @@ -200,7 +200,7 @@ impl Compose for Wordle { @ { keyboard } @Row { margin: EdgeInsets::only_top(10.), - main_axis_gap: 15., + item_gap: 15., @ { give_up } @ { new_game } } diff --git a/macros/src/builtin_fields_list.rs b/macros/src/builtin_fields_list.rs index bf87339d5..193551d78 100644 --- a/macros/src/builtin_fields_list.rs +++ b/macros/src/builtin_fields_list.rs @@ -5,7 +5,22 @@ builtin! { #[doc = "Return the LazyWidgetId of the external widget (wrapped with the built-in host), through which you can access the WidgetId after building."] fn lazy_id(&self) -> LazyWidgetId, } - PointerListener { + MixBuiltin { + #[doc="Indicates whether the widget should automatically get focus when the window loads."] + auto_focus: bool, + #[doc="indicates that widget can be focused, and where it participates in \ + sequential keyboard navigation (usually with the Tab key, hence the name."] + tab_index: i16, + #[doc = "action perform after any event received."] + on_event: impl FnMut(&mut Event), + #[doc="action perform after widget be added to the widget tree."] + on_mounted: Box FnMut(LifeCycleCtx<'r>, MountedType)>, + #[doc="action perform after widget remove from widget tree."] + on_disposed: Box FnMut(LifeCycleCtx<'r>, DisposedType)>, + #[doc="specify the event handler for the pointer down event in bubble phase."] + on_performed_layout: impl FnMut(&mut PointerEvent), + #[doc="return the stream of lifecycle."] + lifecycle_stream: LifecycleSubject, #[doc="specify the event handler for the pointer down event in bubble phase."] on_pointer_down: impl FnMut(&mut PointerEvent), #[doc="specify the event handler for the pointer down event in capture phase."] @@ -40,98 +55,30 @@ builtin! { on_x_times_tap: (usize, Box FnMut(&'r mut PointerEvent)>), #[doc="specify the event handler for the pointer `x` times tap event in capture phase."] on_x_times_tap_capture: (usize, Box FnMut(&'r mut PointerEvent)>), - - #[doc=" Return an observable stream of double tap event in bubble phase."] - fn double_tap_stream( - &self, - ) -> FilterMapOp< - MutRefItemSubject<'static, PointerEvent, ()>, - impl FnMut(&mut PointerEvent) -> Option<&mut PointerEvent>, - &mut PointerEvent, - >, - - #[doc="Return an observable stream of tripe tap event in bubble phase."] - fn triple_tap_stream( - &self, - ) -> FilterMapOp< - MutRefItemSubject<'static, PointerEvent, ()>, - impl FnMut(&mut PointerEvent) -> Option<&mut PointerEvent>, - &mut PointerEvent, - >, - - #[doc=" Return an observable stream of x-tap event that user tapped 'x' \ - times in the specify duration `dur` in bubble phase."] - fn x_times_tap_stream( - &self, - x: usize, - dur: Duration, - ) -> FilterMapOp< - MutRefItemSubject<'static, PointerEvent, ()>, - impl FnMut(&mut PointerEvent) -> Option<&mut PointerEvent>, - &mut PointerEvent, - >, - - #[doc= "return an observable stream of the pointer tap event in capture phase."] - fn tap_capture_stream(&self) -> MutRefItemSubject<'static, PointerEvent, ()>, - - #[doc="return an observable stream of double tap event in capture phase."] - fn double_tap_capture_stream( - &self, - ) -> FilterMapOp< - MutRefItemSubject<'static, PointerEvent, ()>, - impl FnMut(&mut PointerEvent) -> Option<&mut PointerEvent>, - &mut PointerEvent, - >, - - #[doc="Return an observable stream of tripe tap event in capture phase."] - fn triple_tap_capture_stream( - &self, - ) -> FilterMapOp< - MutRefItemSubject<'static, PointerEvent, ()>, - impl FnMut(&mut PointerEvent) -> Option<&mut PointerEvent>, - &mut PointerEvent, - >, - - #[doc=" Return an observable stream of x-tap event that user tapped 'x' \ - times in the specify duration `dur` in capture phase."] - fn x_times_tap_capture_stream( - &self, - x: usize, - dur: Duration, - ) -> FilterMapOp< - MutRefItemSubject<'static, PointerEvent, ()>, - impl FnMut(&mut PointerEvent) -> Option<&mut PointerEvent>, - &mut PointerEvent, - >, - #[doc="return the stream include all pointer events."] - fn pointer_stream(&self) -> PointerSubject, - } - - FocusNode { - #[doc="Indicates whether the widget should automatically get focus when the window loads."] - auto_focus: bool, - #[doc="indicates that widget can be focused, and where it participates in \ - sequential keyboard navigation (usually with the Tab key, hence the name."] - tab_index: i16, - } - - RequestFocus{ - #[doc="request the this node to be focused."] - fn request_focus(&self), - #[doc="removes the focus from this node."] - fn unfocus(&self), - } - - FocusListener { + #[doc="specify the event handler when received unicode characters in ime pre edit bubble phase."] + on_ime_pre_edit: impl FnMut(&mut ImePreEditEvent), + #[doc="specify the event handler when received unicode characters in ime pre edit capture phase."] + on_ime_pre_edit_capture: impl FnMut(&mut ImePreEditEvent), + #[doc="specify the event handler when user moving a mouse wheel or similar input device in bubble phase."] + on_wheel: impl FnMut(&mut WheelEvent), + #[doc="specify the event handler when user moving a mouse wheel or similar input device in capture phase."] + on_wheel_capture: impl FnMut(&mut WheelEvent), + #[doc="specify the event handler when received unicode characters in bubble phase."] + on_chars: impl FnMut(&mut CharsEvent), + #[doc="specify the event handler when received unicode characters in capture phase."] + on_chars_capture: impl FnMut(&mut CharsEvent), + #[doc="specify the event handler when keyboard press down in capture phase."] + on_key_down_capture: impl FnMut(&mut KeyboardEvent), + #[doc="specify the event handler when keyboard press down in bubble phase."] + on_key_down: impl FnMut(&mut KeyboardEvent), + #[doc="specify the event handler when a key is released in bubble phase."] + on_key_up: impl FnMut(&mut KeyboardEvent), + #[doc="specify the event handler when a key is released in capture phase."] + on_key_up_capture: impl FnMut(&mut KeyboardEvent), #[doc="specify the event handler to process focus event."] on_focus: impl FnMut(&mut FocusEvent), #[doc="specify the event handler to process blur event."] on_blur: impl FnMut(&mut FocusEvent), - #[doc= "Return the stream include all focus and blur events."] - fn focus_stream(&self) -> MutRefItemSubject<'static, FocusEvent, ()>, - } - - FocusBubbleListener { #[doc="specify the event handler to process focusin event in bubble phase."] on_focus_in: impl FnMut(&mut FocusEvent), #[doc="specify the event handler to process focusin event in capture phase."] @@ -140,8 +87,15 @@ builtin! { on_focus_out: impl FnMut(&mut FocusEvent), #[doc="specify the event handler to process focusout event in capture phase."] on_focus_out_capture: impl FnMut(&mut FocusEvent), - #[doc= "Return the stream include all focus in/out related events."] - fn focus_bubble_stream(&self) -> MutRefItemSubject<'static, FocusEvent, ()>, + #[doc="return the stream of the event."] + fn subject(&self) -> EventSubject, + } + + RequestFocus{ + #[doc="request the this node to be focused."] + fn request_focus(&self), + #[doc="removes the focus from this node."] + fn unfocus(&self), } HasFocus { @@ -149,44 +103,6 @@ builtin! { fn has_focus(&self) -> bool, } - KeyboardListener { - #[doc="specify the event handler when keyboard press down in capture phase."] - on_key_down_capture: impl FnMut(&mut KeyboardEvent), - #[doc="specify the event handler when keyboard press down in bubble phase."] - on_key_down: impl FnMut(&mut KeyboardEvent), - #[doc="specify the event handler when a key is released in bubble phase."] - on_key_up: impl FnMut(&mut KeyboardEvent), - #[doc="specify the event handler when a key is released in capture phase."] - on_key_up_capture: impl FnMut(&mut KeyboardEvent), - #[doc="return the stream include all keyboard events."] - fn keyboard_stream(&self) -> KeyboardSubject, - } - - CharsListener { - #[doc="specify the event handler when received unicode characters in bubble phase."] - on_chars: impl FnMut(&mut CharsEvent), - #[doc="specify the event handler when received unicode characters in capture phase."] - on_chars_capture: impl FnMut(&mut CharsEvent), - #[doc="return the stream include all chars events."] - fn chars_stream(&self) -> CharsSubject, - } - - ImePreEditListener { - #[doc="specify the event handler when received unicode characters in ime pre edit bubble phase."] - on_ime_pre_edit: impl FnMut(&mut ImePreEditEvent), - #[doc="specify the event handler when received unicode characters in ime pre edit capture phase."] - on_ime_pre_edit_capture: impl FnMut(&mut ImePreEditEvent), - } - - WheelListener { - #[doc="specify the event handler when user moving a mouse wheel or similar input device in bubble phase."] - on_wheel: impl FnMut(&mut WheelEvent), - #[doc="specify the event handler when user moving a mouse wheel or similar input device in capture phase."] - on_wheel_capture: impl FnMut(&mut WheelEvent), - #[doc="return the stream include all wheel events."] - fn wheel_stream(&self) -> MutRefItemSubject<'static, WheelEvent, ()>, - } - MouseHover { #[doc="return if the pointer is hover on the widget"] fn mouse_hover(&self) -> bool, @@ -291,17 +207,6 @@ builtin! { opacity: f32 } - LifecycleListener { - #[doc="action perform after widget be added to the widget tree."] - on_mounted: Box FnMut(LifeCycleCtx<'r>, MountedType)>, - #[doc="action perform after widget remove from widget tree."] - on_disposed: Box FnMut(LifeCycleCtx<'r>, DisposedType)>, - #[doc="specify the event handler for the pointer down event in bubble phase."] - on_performed_layout: impl FnMut(&mut PointerEvent), - #[doc="return the stream of lifecycle."] - lifecycle_stream: LifecycleSubject - } - DelayDrop { #[doc= "The widget delay the drop of its child until the field delay_drop_until is false, but not affect its dispose event emit time. It's useful to ensure the disappear-animate display fully."] delay_drop_until: bool, diff --git a/macros/src/declare_obj.rs b/macros/src/declare_obj.rs index 0ea7c156c..6bf914ab5 100644 --- a/macros/src/declare_obj.rs +++ b/macros/src/declare_obj.rs @@ -50,8 +50,9 @@ impl<'a> DeclareObj<'a> { Ok(()) } else { let mut err_tokens = quote! {}; + for f in fields { - quote_spanned! { f.member.span() => #err_msg }.to_tokens(&mut err_tokens) + quote_spanned! { f.member.span() => compile_error!(#err_msg) }.to_tokens(&mut err_tokens) } Err(err_tokens) } @@ -73,7 +74,7 @@ impl<'a> DeclareObj<'a> { RdlParent::Var(name) => { invalid_member_err( &self_fields, - "only allow to declare builtin fields in a variable parent.", + "not allow to declare builtin fields in a variable parent.", )?; let this = Some(ObjNode::Var(name)); Ok(Self { this, span, builtin, children }) @@ -179,9 +180,10 @@ impl<'a> ToTokens for ObjNode<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Self::Obj { ty, span, fields } => { - quote_spanned! { *span => #ty::declare_builder() }.to_tokens(tokens); - fields.iter().for_each(|f| f.to_tokens(tokens)); - tokens.extend(quote_spanned! { *span => .build_declare(ctx!()) }); + quote_spanned! { *span => + #ty::declare_builder() #(#fields)*.build_declare(ctx!()) + } + .to_tokens(tokens); } Self::Var(var) => var.to_tokens(tokens), } diff --git a/widgets/src/layout/flex.rs b/widgets/src/layout/flex.rs index eebb9c8e5..7deee2bc7 100644 --- a/widgets/src/layout/flex.rs +++ b/widgets/src/layout/flex.rs @@ -49,10 +49,10 @@ pub struct Flex { pub justify_content: JustifyContent, /// Define item between gap in main axis #[declare(default)] - pub main_axis_gap: f32, + pub item_gap: f32, /// Define item between gap in cross axis #[declare(default)] - pub cross_axis_gap: f32, + pub line_gap: f32, } /// A type help to declare flex widget as horizontal. @@ -66,24 +66,6 @@ impl Declare for Row { fn declare_builder() -> Self::Builder { Flex::declare_builder().direction(Direction::Horizontal) } } -impl FlexDeclarer { - pub fn item_gap(mut self, gap: T) -> Self - where - DeclareInit: DeclareFrom, - { - self.main_axis_gap = Some(DeclareInit::declare_from(gap)); - self - } - - pub fn line_gap(mut self, gap: T) -> Self - where - DeclareInit: DeclareFrom, - { - self.cross_axis_gap = Some(DeclareInit::declare_from(gap)); - self - } -} - impl Declare for Column { type Builder = FlexDeclarer; fn declare_builder() -> Self::Builder { Flex::declare_builder().direction(Direction::Vertical) } @@ -108,8 +90,8 @@ impl Render for Flex { align_items: self.align_items, justify_content: self.justify_content, wrap: self.wrap, - main_axis_gap: self.main_axis_gap, - cross_axis_gap: self.cross_axis_gap, + main_axis_gap: self.item_gap, + cross_axis_gap: self.line_gap, current_line: <_>::default(), lines: vec![], }; @@ -527,7 +509,7 @@ mod tests { fn_widget! { @Flex { wrap: true, - cross_axis_gap: 10., + line_gap: 10., align_items: Align::Center, @{ (0..3).map(|_| SizedBox { size }) } }