diff --git a/CHANGELOG.md b/CHANGELOG.md index b215cae8..5784d15b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he ### Changed +- **core**: Merged boolean status widgets into a single widget `MixFlags`, including `HasFocus`, `MouseHover` and `PointerPressed`. (#627 @M-Adoo) - **core**: Reimplemented `HAlignWidget`, `VAlignWidget`, `RelativeAnchor`, `BoxDecoration`, `ConstrainedBox`, `IgnorePoint`, `Opacity`, `Padding`, `TransformWidget`, and `VisibilityRender` as `WrapRender`. (#626 @M-Adoo) diff --git a/core/src/builtin_widgets.rs b/core/src/builtin_widgets.rs index 6d1ffc27..1f955d25 100644 --- a/core/src/builtin_widgets.rs +++ b/core/src/builtin_widgets.rs @@ -46,14 +46,8 @@ pub mod fitted_box; pub use fitted_box::*; pub mod svg; -pub mod has_focus; -pub use has_focus::*; -pub mod mouse_hover; -pub use mouse_hover::*; pub mod clip; pub use clip::*; -pub mod pointer_pressed; -pub use pointer_pressed::*; pub mod focus_node; pub use focus_node::*; pub mod focus_scope; @@ -108,11 +102,8 @@ pub struct FatObj { host: T, host_id: LazyWidgetId, id: LazyWidgetId, - mix_builtin: Option>, + mix_builtin: Option, request_focus: Option>, - has_focus: Option>, - mouse_hover: Option>, - pointer_pressed: Option>, fitted_box: Option>, box_decoration: Option>, padding: Option>, @@ -176,9 +167,6 @@ impl FatObj { id: self.id, mix_builtin: self.mix_builtin, request_focus: self.request_focus, - has_focus: self.has_focus, - mouse_hover: self.mouse_hover, - pointer_pressed: self.pointer_pressed, fitted_box: self.fitted_box, box_decoration: self.box_decoration, padding: self.padding, @@ -206,9 +194,6 @@ impl FatObj { && self.id.ref_count() == 1 && self.mix_builtin.is_none() && self.request_focus.is_none() - && self.has_focus.is_none() - && self.mouse_hover.is_none() - && self.pointer_pressed.is_none() && self.fitted_box.is_none() && self.box_decoration.is_none() && self.padding.is_none() @@ -249,53 +234,55 @@ impl FatObj { impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't exist, a /// new one is created. - pub fn get_class_widget(&mut self) -> &mut State { + pub fn get_class_widget(&mut self) -> &State { self .class .get_or_insert_with(|| State::value(<_>::default())) } - pub fn get_mix_builtin_widget(&mut self) -> &mut State { + pub fn get_mix_builtin_widget(&mut self) -> &MixBuiltin { self .mix_builtin - .get_or_insert_with(|| State::value(<_>::default())) + .get_or_insert_with(MixBuiltin::default) } /// Returns the `State` widget from the FatObj. If it doesn't /// exist, a new one is created. - pub fn get_request_focus_widget(&mut self) -> &mut State { + pub fn get_request_focus_widget(&mut self) -> &State { self .request_focus .get_or_insert_with(|| State::value(<_>::default())) } - /// Returns the `State` widget from the FatObj. If it doesn't exist, - /// a new one is created. - pub fn get_has_focus_widget(&mut self) -> &mut State { + /// Return the `State` from the `MixBuiltin`. If the `MixBuiltin` + /// does not exist in the `FatObj`, a new one is created. + pub fn get_mix_flags_widget(&mut self) -> &State { + self.get_mix_builtin_widget().mix_flags() + } + + /// Begin tracing the focus status of this widget. + pub fn trace_focus(&mut self) -> &mut Self { + self.get_mix_builtin_widget().trace_focus(); self - .has_focus - .get_or_insert_with(|| State::value(<_>::default())) } - /// Returns the `State` widget from the FatObj. If it doesn't - /// exist, a new one is created. - pub fn get_mouse_hover_widget(&mut self) -> &mut State { + /// Begin tracing the hover status of this widget. + pub fn trace_hover(&mut self) -> &mut Self { + self.get_mix_builtin_widget().trace_hover(); self - .mouse_hover - .get_or_insert_with(|| State::value(<_>::default())) } - /// Returns the `State` widget from the FatObj. If it doesn't - /// exist, a new one is created. - pub fn get_pointer_pressed_widget(&mut self) -> &mut State { + /// Begin tracing if the pointer pressed on this widget + pub fn trace_pointer_pressed(&mut self) -> &mut Self { + self + .get_mix_builtin_widget() + .trace_pointer_pressed(); self - .pointer_pressed - .get_or_insert_with(|| State::value(<_>::default())) } /// Returns the `State` widget from the FatObj. If it doesn't /// exist, a new one is created. - pub fn get_fitted_box_widget(&mut self) -> &mut State { + pub fn get_fitted_box_widget(&mut self) -> &State { self .fitted_box .get_or_insert_with(|| State::value(<_>::default())) @@ -303,7 +290,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't /// exist, a new one is created. - pub fn get_box_decoration_widget(&mut self) -> &mut State { + pub fn get_box_decoration_widget(&mut self) -> &State { self .box_decoration .get_or_insert_with(|| State::value(<_>::default())) @@ -311,7 +298,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't exist, /// a new one is created. - pub fn get_padding_widget(&mut self) -> &mut State { + pub fn get_padding_widget(&mut self) -> &State { self .padding .get_or_insert_with(|| State::value(<_>::default())) @@ -319,7 +306,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't /// exist, a new one is created. - pub fn get_layout_box_widget(&mut self) -> &mut State { + pub fn get_layout_box_widget(&mut self) -> &State { self .layout_box .get_or_insert_with(|| State::value(<_>::default())) @@ -327,7 +314,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't exist, a /// new one is created. - pub fn get_cursor_widget(&mut self) -> &mut State { + pub fn get_cursor_widget(&mut self) -> &State { self .cursor .get_or_insert_with(|| State::value(<_>::default())) @@ -335,13 +322,13 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't exist, a /// new one is created. - pub fn get_margin_widget(&mut self) -> &mut State { + pub fn get_margin_widget(&mut self) -> &State { self .margin .get_or_insert_with(|| State::value(<_>::default())) } - pub fn get_constrained_box_widget(&mut self) -> &mut State { + pub fn get_constrained_box_widget(&mut self) -> &State { self .constrained_box .get_or_insert_with(|| State::value(<_>::default())) @@ -349,7 +336,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it /// doesn't exist, a new one is created. - pub fn get_scrollable_widget(&mut self) -> &mut State { + pub fn get_scrollable_widget(&mut self) -> &State { self .scrollable .get_or_insert_with(|| State::value(<_>::default())) @@ -357,7 +344,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't /// exist, a new one is created. - pub fn get_transform_widget(&mut self) -> &mut State { + pub fn get_transform_widget(&mut self) -> &State { self .transform .get_or_insert_with(|| State::value(<_>::default())) @@ -365,7 +352,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't /// exist, a new one is created. - pub fn get_h_align_widget(&mut self) -> &mut State { + pub fn get_h_align_widget(&mut self) -> &State { self .h_align .get_or_insert_with(|| State::value(<_>::default())) @@ -373,7 +360,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't /// exist, a new one is created. - pub fn get_v_align_widget(&mut self) -> &mut State { + pub fn get_v_align_widget(&mut self) -> &State { self .v_align .get_or_insert_with(|| State::value(<_>::default())) @@ -381,7 +368,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't /// exist, a new one is created. - pub fn get_relative_anchor_widget(&mut self) -> &mut State { + pub fn get_relative_anchor_widget(&mut self) -> &State { self .relative_anchor .get_or_insert_with(|| State::value(<_>::default())) @@ -389,7 +376,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't /// exist, a new one is created. - pub fn get_global_anchor_widget(&mut self) -> &mut State { + pub fn get_global_anchor_widget(&mut self) -> &State { self .global_anchor .get_or_insert_with(|| State::value(<_>::default())) @@ -397,7 +384,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't /// exist, a new one is created. - pub fn get_visibility_widget(&mut self) -> &mut State { + pub fn get_visibility_widget(&mut self) -> &State { self .visibility .get_or_insert_with(|| State::value(<_>::default())) @@ -405,7 +392,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't exist, /// a new one is created. - pub fn get_opacity_widget(&mut self) -> &mut State { + pub fn get_opacity_widget(&mut self) -> &State { self .opacity .get_or_insert_with(|| State::value(<_>::default())) @@ -413,7 +400,7 @@ impl FatObj { /// Returns the `State` widget from the FatObj. If it doesn't /// exist, a new one is created. - pub fn get_keep_alive_widget(&mut self) -> &mut State { + pub fn get_keep_alive_widget(&mut self) -> &State { self .keep_alive .get_or_insert_with(|| State::value(<_>::default())) @@ -422,10 +409,7 @@ impl FatObj { macro_rules! on_mixin { ($this:ident, $on_method:ident, $f:ident) => {{ - $this - .get_mix_builtin_widget() - .read() - .$on_method($f); + $this.get_mix_builtin_widget().$on_method($f); $this }}; } @@ -565,7 +549,6 @@ impl FatObj { ) -> Self { self .get_mix_builtin_widget() - .read() .on_x_times_tap((times, f)); self } @@ -579,7 +562,6 @@ impl FatObj { ) -> Self { self .get_mix_builtin_widget() - .read() .on_x_times_tap_capture((times, f)); self } @@ -715,9 +697,11 @@ impl FatObj { /// 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, tab_idx: impl DeclareInto) -> Self { - self.declare_builtin_init(tab_idx, Self::get_mix_builtin_widget, |mixin, v| { - mixin.set_tab_index(v); - }) + self.declare_builtin_init( + tab_idx, + |this| this.get_mix_builtin_widget().mix_flags(), + |m, v| m.set_tab_index(v), + ) } /// Initializes the `Class` that should be applied to the widget. @@ -731,9 +715,11 @@ impl FatObj { /// 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, v: impl DeclareInto) -> Self { - self.declare_builtin_init(v, Self::get_mix_builtin_widget, |m, v| { - m.set_auto_focus(v); - }) + self.declare_builtin_init( + v, + |this| this.get_mix_builtin_widget().mix_flags(), + |m, v| m.set_auto_focus(v), + ) } /// Initializes how its child should be scale to fit its box. @@ -839,7 +825,7 @@ impl FatObj { } fn declare_builtin_init( - mut self, init: impl DeclareInto, get_builtin: impl FnOnce(&mut Self) -> &mut State, + mut self, init: impl DeclareInto, get_builtin: impl FnOnce(&mut Self) -> &State, set_value: fn(&mut B, V), ) -> Self { let builtin = get_builtin(&mut self); @@ -847,9 +833,7 @@ impl FatObj { set_value(&mut *builtin.silent(), v); if let Some(o) = o { let c_builtin = builtin.clone_writer(); - let u = o.subscribe(move |(_, v)| { - set_value(&mut *c_builtin.write(), v); - }); + let u = o.subscribe(move |(_, v)| set_value(&mut *c_builtin.write(), v)); self.on_disposed(move |_| u.unsubscribe()) } else { self @@ -896,9 +880,6 @@ impl<'a> FatObj> { layout_box, mix_builtin, request_focus, - has_focus, - mouse_hover, - pointer_pressed, cursor, margin, transform, diff --git a/core/src/builtin_widgets/focus_node.rs b/core/src/builtin_widgets/focus_node.rs index 0216c7c7..3e4528f2 100644 --- a/core/src/builtin_widgets/focus_node.rs +++ b/core/src/builtin_widgets/focus_node.rs @@ -72,7 +72,7 @@ mod tests { let mut cnt = 0; id.query_all_iter::(tree) .for_each(|b| { - if b.contain_flag(BuiltinFlags::Focus) { + if b.contain_flag(MixFlags::Focus) { cnt += 1; } }); diff --git a/core/src/builtin_widgets/has_focus.rs b/core/src/builtin_widgets/has_focus.rs deleted file mode 100644 index a973be62..00000000 --- a/core/src/builtin_widgets/has_focus.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::prelude::*; -#[derive(PartialEq, Clone, Default)] -pub struct HasFocus { - focused: bool, -} - -impl HasFocus { - pub fn has_focus(&self) -> bool { self.focused } -} - -impl Declare for HasFocus { - type Builder = FatObj<()>; - #[inline] - fn declarer() -> Self::Builder { FatObj::new(()) } -} - -impl<'c> ComposeChild<'c> for HasFocus { - type Child = Widget<'c>; - fn compose_child(this: impl StateWriter, child: Self::Child) -> Widget<'c> { - fn_widget! { - @ $child { - on_focus_in: move|_| $this.write().focused = true, - on_focus_out: move |_| $this.write().focused = false, - } - } - .into_widget() - } -} diff --git a/core/src/builtin_widgets/mix_builtin.rs b/core/src/builtin_widgets/mix_builtin.rs index b5bd163c..e7435479 100644 --- a/core/src/builtin_widgets/mix_builtin.rs +++ b/core/src/builtin_widgets/mix_builtin.rs @@ -1,4 +1,4 @@ -use std::{cell::Cell, convert::Infallible}; +use std::convert::Infallible; use rxrust::prelude::*; @@ -9,21 +9,21 @@ const MULTI_TAP_DURATION: Duration = Duration::from_millis(250); bitflags! { #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] - pub struct BuiltinFlags: u64 { + pub struct MixFlags: 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 + #[doc="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 + #[doc="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 + #[doc="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 + #[doc="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 + #[doc="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() @@ -34,16 +34,29 @@ bitflags! { | Self::FocusInOut.bits(); // listener end + #[doc="Indicates whether this widget is tracing its focus status."] + const TraceFocus = 1 << 16; + #[doc="Indicates whether the focus is on this widget (including its descendants)."] + const Focused = 1 << 17; + #[doc="Indicates whether this widget is tracing the hover status."] + const TraceHover = 1 << 18; + #[doc="Indicates whether the mouse is hover on this widget (including its descendants)."] + const Hovered = 1 << 19; + #[doc="Indicates whether this widget is tracing the pressed status of pointer."] + const TracePointerPressed = 1 << 20; + #[doc="Indicates whether the pointer is pressed on this widget."] + const PointerPressed = 1 << 21; + + #[doc="Indicates whether this widget has auto-focus functionality."] const AutoFocus = 1 << 47; - // 16 bits keep for tab index + // The last 16 bits keep for tab index } } pub type EventSubject = MutRefItemSubject<'static, Event, Infallible>; -#[derive(Default)] pub struct MixBuiltin { - flags: Cell, + flags: State, subject: EventSubject, } @@ -64,7 +77,7 @@ macro_rules! event_map_filter { macro_rules! impl_event_callback { ($this:ident, $listen_type:ident, $event_name:ident, $event_ty:ident, $handler:ident) => {{ - $this.flag_mark(BuiltinFlags::$listen_type); + $this.silent_mark(MixFlags::$listen_type); let _ = $this .subject() .filter_map(event_map_filter!($event_name, $event_ty)) @@ -74,28 +87,68 @@ macro_rules! impl_event_callback { }}; } -impl MixBuiltin { - #[inline] - pub fn contain_flag(&self, t: BuiltinFlags) -> bool { self.flags.get().contains(t) } +impl MixFlags { + /// Indicates whether the focus is on this widget (including its children). + /// + /// By default, the focus status is not traced. You need to call + /// `MixBuiltin::trace_focus` to start recording the focus status of + /// this widget. If you do not call `MixBuiltin::trace_focus` when + /// this widget is created, this method will always return `false`, even if it + /// has focus. + pub fn has_focus(&self) -> bool { self.contains(MixFlags::Focused) } + + /// Indicates whether the mouse is hovering over this widget (including its + /// children). + /// + /// By default, the hover status is not traced. You need to call + /// `MixBuiltin::trace_hover` to start tracking the focus status of + /// this widget. If you do not call `MixBuiltin::trace_hover` when this + /// widget is created, this method will always return false, even if the mouse + /// is hovering over it. + pub fn is_hover(&self) -> bool { self.contains(MixFlags::Hovered) } + + /// Indicates whether the the pointer is pressed on this widget. + /// + /// By default, the pressed status is not traced. You need to call + /// `MixBuiltin::trace_pointer_pressed` to start tracking the focus status of + /// this widget. If you do not call `MixBuiltin::trace_pointer_pressed` when + /// this widget is created, this method will always return false, even if + /// the mouse is hovering over it. + pub fn is_pointer_pressed(&self) -> bool { self.contains(MixFlags::PointerPressed) } + + pub fn is_auto_focus(&self) -> bool { self.contains(MixFlags::AutoFocus) } + + pub fn set_auto_focus(&mut self, v: bool) { + if v { + self.insert(MixFlags::AutoFocus | MixFlags::Focus); + } else { + self.remove(MixFlags::AutoFocus); + } + } + + pub fn tab_index(&self) -> i16 { (self.bits() >> 48) as i16 } - pub fn flag_mark(&self, t: BuiltinFlags) { - let t = self.flags.get() | t; - self.flags.set(t) + pub fn set_tab_index(&mut self, tab_idx: i16) { + self.insert(MixFlags::Focus); + let flags = self.bits() | ((tab_idx as u64) << 48); + *self = MixFlags::from_bits_retain(flags); } +} - pub fn dispatch(&self, event: &mut Event) { self.subject.clone().next(event) } +impl MixBuiltin { + pub fn mix_flags(&self) -> &State { &self.flags } - pub fn subject(&self) -> EventSubject { self.subject.clone() } + pub fn dispatch(&self, event: &mut Event) { self.subject.clone().next(event) } /// Listen to all events pub fn on_event(&self, handler: impl FnMut(&mut Event) + 'static) -> &Self { - self.flag_mark(BuiltinFlags::AllListeners); + self.silent_mark(MixFlags::AllListeners); let _ = self.subject().subscribe(handler); self } pub fn on_mounted(&self, handler: impl FnOnce(&mut LifecycleEvent) + 'static) -> &Self { - self.flag_mark(BuiltinFlags::Lifecycle); + self.silent_mark(MixFlags::Lifecycle); let _ = self .subject() .filter_map(event_map_filter!(Mounted, LifecycleEvent)) @@ -110,7 +163,7 @@ impl MixBuiltin { } pub fn on_disposed(&self, handler: impl FnOnce(&mut LifecycleEvent) + 'static) -> &Self { - self.flag_mark(BuiltinFlags::Lifecycle); + self.silent_mark(MixFlags::Lifecycle); let _ = self .subject() .filter_map(event_map_filter!(Disposed, LifecycleEvent)) @@ -204,7 +257,7 @@ impl MixBuiltin { &self, times: usize, dur: Duration, capture: bool, handler: impl FnMut(&mut PointerEvent) + 'static, ) -> &Self { - self.flag_mark(BuiltinFlags::Pointer); + self.silent_mark(MixFlags::Pointer); self .subject() .filter_map(x_times_tap_map_filter(times, dur, capture)) @@ -268,60 +321,78 @@ impl MixBuiltin { 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) } - - pub fn get_tab_index(&self) -> i16 { (self.flags.get().bits() >> 48) as i16 } - - pub fn set_tab_index(&self, tab_idx: i16) -> &Self { - self.flag_mark(BuiltinFlags::Focus); - let flags = self.flags.get().bits() | ((tab_idx as u64) << 48); - self - .flags - .set(BuiltinFlags::from_bits_retain(flags)); - self + /// Begin tracing the focus status of this widget. + pub fn trace_focus(&self) { + if !self.contain_flag(MixFlags::TraceFocus) { + self.silent_mark(MixFlags::TraceFocus); + let flags = self.flags.clone_writer(); + self.on_focus_in(move |_| flags.write().insert(MixFlags::Focused)); + let flags = self.flags.clone_writer(); + self.on_focus_out(move |_| flags.write().remove(MixFlags::Focused)); + } } - pub fn is_auto_focus(&self) -> bool { self.flags.get().contains(BuiltinFlags::AutoFocus) } + /// Begin tracing the hover status of this widget. + pub fn trace_hover(&self) { + if !self.contain_flag(MixFlags::TraceHover) { + self.silent_mark(MixFlags::TraceHover); + let flags = self.flags.clone_writer(); + self.on_pointer_enter(move |_| flags.write().insert(MixFlags::Hovered)); + let flags = self.flags.clone_writer(); + self.on_pointer_leave(move |_| flags.write().remove(MixFlags::Hovered)); + } + } - pub fn set_auto_focus(&self, v: bool) -> &Self { - if v { - self.flag_mark(BuiltinFlags::AutoFocus | BuiltinFlags::Focus); - } else { - let mut flag = self.flags.get(); - flag.remove(BuiltinFlags::AutoFocus); - self.flags.set(flag); + /// Begin tracing if the pointer pressed on this widget + pub fn trace_pointer_pressed(&self) { + if !self.contain_flag(MixFlags::TracePointerPressed) { + self.silent_mark(MixFlags::TracePointerPressed); + let flags = self.flags.clone_writer(); + self.on_pointer_down(move |_| flags.write().insert(MixFlags::PointerPressed)); + let flags = self.flags.clone_writer(); + self.on_pointer_up(move |_| flags.write().remove(MixFlags::PointerPressed)); } - self } fn merge(&self, other: Self) { - let tab_index = self.get_tab_index(); - let other_tab_index = other.get_tab_index(); - self - .flags - .set(self.flags.get() | other.flags.get()); + let tab_index = self.flags.read().tab_index(); + let other_tab_index = other.flags.read().tab_index(); + let mut this_flags = self.flags.write(); + this_flags.insert(*other.flags.read()); if other_tab_index != 0 { - self.set_tab_index(other_tab_index); + this_flags.set_tab_index(other_tab_index); } else if tab_index != 0 { - self.set_tab_index(tab_index); + this_flags.set_tab_index(tab_index); + } + drop(this_flags); + let Self { flags, subject } = other; + + // if the other `MixFlags` is a writer, we need to subscribe it. + if let Err(other) = flags.into_reader() { + let this_flags = self.flags.clone_writer(); + let u = other + .modifies() + .distinct_until_changed() + .subscribe(move |_| { + let flags = *other.read(); + let mut this = this_flags.write(); + this.insert(flags); + this.set_tab_index(flags.tab_index()); + }); + self.on_disposed(|_| u.unsubscribe()); } - 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)); + self.subject().subscribe(subscribe_fn(subject)); } fn callbacks_for_focus_node(&self) { self .on_mounted(move |e| { if let Some(mix) = e.query::() { - let auto_focus = mix.is_auto_focus(); + let auto_focus = mix.flags.read().is_auto_focus(); e.window() .add_focus_node(e.id, auto_focus, FocusType::Node) } @@ -331,6 +402,16 @@ impl MixBuiltin { .remove_focus_node(e.id, FocusType::Node) }); } + + fn subject(&self) -> EventSubject { self.subject.clone() } + + pub(crate) fn contain_flag(&self, t: MixFlags) -> bool { self.flags.read().contains(t) } + + fn silent_mark(&self, t: MixFlags) { + let mut w = self.flags.write(); + w.insert(t); + w.forget_modifies(); + } } fn life_fn_once_to_fn_mut( @@ -347,31 +428,25 @@ fn life_fn_once_to_fn_mut( impl<'c> ComposeChild<'c> for MixBuiltin { type Child = Widget<'c>; fn compose_child(this: impl StateWriter, child: Self::Child) -> Widget<'c> { - child.on_build(move |id, ctx| { - match this.try_into_value() { - Ok(this) => { - let mut this = Some(this); - if let Some(m) = id.query_ref::(ctx.tree()) { - let this = unsafe { this.take().unwrap_unchecked() }; - if !m.contain_flag(BuiltinFlags::Focus) && this.contain_flag(BuiltinFlags::Focus) { - this.callbacks_for_focus_node(); - } - m.merge(this); + child.on_build(move |id, ctx| match this.try_into_value() { + Ok(this) => { + if let Some(m) = id.query_ref::(ctx.tree()) { + if !m.contain_flag(MixFlags::Focus) && this.contain_flag(MixFlags::Focus) { + this.callbacks_for_focus_node(); } - // We do not use an else branch here, due to the borrow conflict of the `ctx`. - if let Some(this) = this { - if this.contain_flag(BuiltinFlags::Focus) { - this.callbacks_for_focus_node(); - } - id.attach_data(Box::new(Queryable(this)), ctx.tree_mut()); + m.merge(this); + } else { + if this.contain_flag(MixFlags::Focus) { + this.callbacks_for_focus_node(); } + id.attach_data(Box::new(Queryable(this)), ctx.tree_mut()); } - Err(this) => { - if this.read().contain_flag(BuiltinFlags::Focus) { - this.read().callbacks_for_focus_node(); - } - id.attach_data(Box::new(this), ctx.tree_mut()) + } + Err(this) => { + if this.read().contain_flag(MixFlags::Focus) { + this.read().callbacks_for_focus_node(); } + id.attach_data(Box::new(this), ctx.tree_mut()) } }) } @@ -419,3 +494,16 @@ fn x_times_tap_map_filter( } } } + +impl Default for MixBuiltin { + fn default() -> Self { + Self { flags: State::value(MixFlags::default()), subject: Default::default() } + } +} + +impl Clone for MixBuiltin { + fn clone(&self) -> Self { + let flags = State::stateful(self.flags.clone_writer()); + Self { flags, subject: self.subject.clone() } + } +} diff --git a/core/src/builtin_widgets/mouse_hover.rs b/core/src/builtin_widgets/mouse_hover.rs deleted file mode 100644 index 894df739..00000000 --- a/core/src/builtin_widgets/mouse_hover.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::prelude::*; - -#[derive(PartialEq, Clone, Default)] -pub struct MouseHover { - hover: bool, -} - -impl MouseHover { - pub fn mouse_hover(&self) -> bool { self.hover } -} - -impl Declare for MouseHover { - type Builder = FatObj<()>; - #[inline] - fn declarer() -> Self::Builder { FatObj::new(()) } -} - -impl<'c> ComposeChild<'c> for MouseHover { - type Child = Widget<'c>; - fn compose_child(this: impl StateWriter, child: Self::Child) -> Widget<'c> { - fn_widget! { - @ $child { - on_pointer_enter: move |_| $this.write().hover = true, - on_pointer_leave: move |_| $this.write().hover = false, - } - } - .into_widget() - } -} diff --git a/core/src/builtin_widgets/pointer_pressed.rs b/core/src/builtin_widgets/pointer_pressed.rs deleted file mode 100644 index 9d8f73f9..00000000 --- a/core/src/builtin_widgets/pointer_pressed.rs +++ /dev/null @@ -1,33 +0,0 @@ -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(Default)] -pub struct PointerPressed { - pointer_pressed: bool, -} - -impl Declare for PointerPressed { - type Builder = FatObj<()>; - #[inline] - fn declarer() -> Self::Builder { FatObj::new(()) } -} - -impl PointerPressed { - // return if its child widget is pressed. - #[inline] - pub fn pointer_pressed(&self) -> bool { self.pointer_pressed } -} - -impl<'c> ComposeChild<'c> for PointerPressed { - type Child = Widget<'c>; - fn compose_child(this: impl StateWriter, child: Self::Child) -> Widget<'c> { - fn_widget! { - @ $child { - on_pointer_down: move|_| $this.write().pointer_pressed = true, - on_pointer_up: move |_| $this.write().pointer_pressed = false, - } - } - .into_widget() - } -} diff --git a/core/src/events.rs b/core/src/events.rs index 98e3f34a..d0da184a 100644 --- a/core/src/events.rs +++ b/core/src/events.rs @@ -2,7 +2,7 @@ use std::ptr::NonNull; use self::dispatcher::DispatchInfo; use crate::{ - builtin_widgets::BuiltinFlags, + builtin_widgets::MixFlags, context::{define_widget_context, WidgetCtx, WidgetCtxImpl}, widget_tree::{WidgetId, WidgetTree}, }; @@ -233,9 +233,9 @@ impl std::ops::DerefMut for Event { } impl Event { - pub fn flags(&self) -> BuiltinFlags { + pub(crate) fn flags(&self) -> MixFlags { match self { - Event::Mounted(_) | Event::PerformedLayout(_) | Event::Disposed(_) => BuiltinFlags::Lifecycle, + Event::Mounted(_) | Event::PerformedLayout(_) | Event::Disposed(_) => MixFlags::Lifecycle, Event::PointerDown(_) | Event::PointerDownCapture(_) | Event::PointerUp(_) @@ -246,8 +246,8 @@ impl Event { | Event::PointerEnter(_) | Event::PointerLeave(_) | Event::Tap(_) - | Event::TapCapture(_) => BuiltinFlags::Pointer, - Event::Wheel(_) | Event::WheelCapture(_) => BuiltinFlags::Wheel, + | Event::TapCapture(_) => MixFlags::Pointer, + Event::Wheel(_) | Event::WheelCapture(_) => MixFlags::Wheel, Event::ImePreEdit(_) | Event::ImePreEditCapture(_) | Event::Chars(_) @@ -255,12 +255,12 @@ impl Event { | Event::KeyDown(_) | Event::KeyDownCapture(_) | Event::KeyUp(_) - | Event::KeyUpCapture(_) => BuiltinFlags::KeyBoard, - Event::Focus(_) | Event::Blur(_) => BuiltinFlags::Focus, + | Event::KeyUpCapture(_) => MixFlags::KeyBoard, + Event::Focus(_) | Event::Blur(_) => MixFlags::Focus, Event::FocusIn(_) | Event::FocusInCapture(_) | Event::FocusOut(_) - | Event::FocusOutCapture(_) => BuiltinFlags::FocusInOut, + | Event::FocusOutCapture(_) => MixFlags::FocusInOut, } } } diff --git a/core/src/events/dispatcher.rs b/core/src/events/dispatcher.rs index 34415db0..f1c0630f 100644 --- a/core/src/events/dispatcher.rs +++ b/core/src/events/dispatcher.rs @@ -160,7 +160,7 @@ impl Dispatcher { let nearest_focus = self.pointer_down_uid.and_then(|wid| { wid.ancestors(tree).find(|id| { id.query_ref::(tree) - .map_or(false, |m| m.contain_flag(BuiltinFlags::Focus)) + .map_or(false, |m| m.contain_flag(MixFlags::Focus)) }) }); if let Some(focus_id) = nearest_focus { diff --git a/core/src/events/focus_mgr.rs b/core/src/events/focus_mgr.rs index 5170a5d3..348d3561 100644 --- a/core/src/events/focus_mgr.rs +++ b/core/src/events/focus_mgr.rs @@ -370,7 +370,7 @@ impl FocusManager { .and_then(|wid| { wid .query_ref::(tree) - .map(|m| m.get_tab_index()) + .map(|m| m.mix_flags().read().tab_index()) }) .unwrap_or_default() } diff --git a/macros/src/symbol_process.rs b/macros/src/symbol_process.rs index 762f5cbf..ff7e5301 100644 --- a/macros/src/symbol_process.rs +++ b/macros/src/symbol_process.rs @@ -1,3 +1,5 @@ +use std::cmp::Ordering; + use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; use smallvec::{smallvec, SmallVec}; @@ -13,7 +15,7 @@ use syn::{ use crate::{ fn_widget_macro, rdl_macro::RdlMacro, - variable_names::{ribir_suffix_variable, BuiltinMemberType, BUILTIN_INFOS}, + variable_names::{ribir_suffix_variable, BuiltinMember, BuiltinMemberType, BUILTIN_INFOS}, }; pub const KW_DOLLAR_STR: &str = "_dollar_ಠ_ಠ"; @@ -34,7 +36,8 @@ pub mod kw { #[derive(Hash, PartialEq, Eq, Debug, Clone)] pub struct BuiltinInfo { pub(crate) host: Ident, - pub(crate) member: Ident, + pub(crate) get_builtin: Ident, + pub(crate) run_before_clone: SmallVec<[Ident; 1]>, } #[derive(Hash, PartialEq, Eq, Debug, Clone, PartialOrd, Ord)] @@ -268,7 +271,7 @@ impl Fold for DollarRefsCtx { let dollar = BUILTIN_INFOS .get(member.to_string().as_str()) .filter(|info| info.mem_ty == BuiltinMemberType::Field) - .and_then(|info| self.replace_builtin_ident(&mut *base, info.var_name)); + .and_then(|info| self.replace_builtin_ident(&mut *base, info)); if dollar.is_some() { return i; } @@ -282,7 +285,7 @@ impl Fold for DollarRefsCtx { if BUILTIN_INFOS .get(&i.method.to_string()) .filter(|info| info.mem_ty == BuiltinMemberType::Method) - .and_then(|info| self.replace_builtin_ident(&mut i.receiver, info.var_name)) + .and_then(|info| self.replace_builtin_ident(&mut i.receiver, info)) .is_some() { return i; @@ -381,8 +384,10 @@ fn mark_macro_expanded(mac: &mut Macro) { impl ToTokens for DollarRefsScope { fn to_tokens(&self, tokens: &mut TokenStream) { for DollarRef { name, builtin, used } in &self.refs { - if let Some(BuiltinInfo { host, member }) = builtin { - quote_spanned! { name.span() => let #name = #host.#member() } + if let Some(BuiltinInfo { host, get_builtin: member, run_before_clone }) = builtin { + quote_spanned! { name.span() => + let #name = #host #(.#run_before_clone())* .#member() + } } else { quote_spanned! { name.span() => let #name = #name } } @@ -448,15 +453,12 @@ impl DollarRefsCtx { } let mut scope = self.scopes.pop().unwrap(); - // sort and remove duplicate - scope.refs.sort_by(|a, b| { - a.builtin - .is_none() - .cmp(&b.builtin.is_none()) - .then_with(|| a.name.cmp(&b.name)) - .then_with(|| b.used.cmp(&a.used)) - }); - scope.refs.dedup_by(|a, b| a.name == b.name); + // To maintain the order, ensure that the builtin widget precedes its host. + // Otherwise, the host might only clone a reader that cannot create a + // builtin widget. + scope + .refs + .sort_by(|a, b| a.builtin.is_none().cmp(&b.builtin.is_none())); if !self.scopes.is_empty() { self.current_dollar_scope_mut().used_ctx |= scope.used_ctx(); @@ -467,7 +469,8 @@ impl DollarRefsCtx { if watch_scope && c_r.used == DollarUsedInfo::Reader { c_r.used = DollarUsedInfo::Watcher; } - self.current_dollar_scope_mut().refs.push(c_r); + self.add_dollar_ref(c_r); + // if ref variable is not a local variable of parent capture level, should // remove its builtin info as a normal variable, because parent will capture the // builtin object individually. @@ -477,6 +480,7 @@ impl DollarRefsCtx { } } } + scope } @@ -487,7 +491,7 @@ impl DollarRefsCtx { pub fn builtin_host_tokens(&self, dollar_ref: &DollarRef) -> TokenStream { let DollarRef { name, builtin, .. } = dollar_ref; - let BuiltinInfo { host, member } = builtin.as_ref().unwrap(); + let BuiltinInfo { host, get_builtin: member, run_before_clone } = builtin.as_ref().unwrap(); // if used in embedded closure, we directly use the builtin variable, the // variable that capture by the closure is already a separate builtin variable. @@ -495,14 +499,14 @@ impl DollarRefsCtx { if !self.is_local_var(host) && self.capture_level_heads.len() > 1 { name.to_token_stream() } else { - quote_spanned! { host.span() => #host.#member() } + quote_spanned! { host.span() => #host #(.#run_before_clone())*.#member() } } } fn mark_used_ctx(&mut self) { self.current_dollar_scope_mut().used_ctx = true; } fn replace_builtin_ident( - &mut self, caller: &mut Expr, builtin_member: &str, + &mut self, caller: &mut Expr, info: &BuiltinMember, ) -> Option<&DollarRef> { let mut used = DollarUsedInfo::Reader; let e = if let Expr::MethodCall(m) = caller { @@ -522,9 +526,14 @@ impl DollarRefsCtx { // When a builtin widget captured by a `move |_| {...}` closure, we need split // the builtin widget from the `FatObj` so we only capture the builtin part that // we used. - let name = ribir_suffix_variable(&host, builtin_member); - let get_builtin_method = Ident::new(&format!("get_{builtin_member}_widget"), host.span()); - let builtin = Some(BuiltinInfo { host, member: get_builtin_method }); + let name = ribir_suffix_variable(&host, info.var_name); + let get_builtin = Ident::new(&format!("get_{}_widget", info.var_name), host.span()); + let mut run_before_clone = SmallVec::default(); + if let Some(method) = info.run_before_clone { + run_before_clone.push(Ident::new(method, host.span())); + } + Ident::new(&format!("get_{}_widget", info.var_name), host.span()); + let builtin = Some(BuiltinInfo { host, get_builtin, run_before_clone }); let dollar_ref = DollarRef { name, builtin, used }; let state = self.builtin_host_tokens(&dollar_ref); @@ -550,11 +559,25 @@ impl DollarRefsCtx { fn add_dollar_ref(&mut self, dollar_ref: DollarRef) { // local variable is not a outside reference. if !self.is_local_var(dollar_ref.host()) { - let scope = self - .scopes - .last_mut() - .expect("no dollar refs scope"); - scope.refs.push(dollar_ref); + let scope = self.current_dollar_scope_mut(); + let r = scope + .refs + .iter_mut() + .find(|v| v.name == dollar_ref.name); + if let Some(r) = r { + if r.used.cmp(&dollar_ref.used) == Ordering::Less { + r.used = dollar_ref.used + } + if let (Some(this), Some(other)) = (r.builtin.as_mut(), dollar_ref.builtin) { + for e in other.run_before_clone { + if this.run_before_clone.iter().any(|e2| &e == e2) { + this.run_before_clone.push(e); + } + } + } + } else { + scope.refs.push(dollar_ref); + } } } @@ -608,7 +631,7 @@ impl DollarRef { pub fn upstream_tokens(&self) -> TokenStream { let DollarRef { name, builtin, .. } = self; - if let Some(BuiltinInfo { host, member }) = builtin { + if let Some(BuiltinInfo { host, get_builtin: member, .. }) = builtin { quote_spanned! { name.span() => #host.#member().modifies() } } else { quote_spanned! { name.span() => #name.modifies() } diff --git a/macros/src/variable_names.rs b/macros/src/variable_names.rs index d779df10..da8f5d46 100644 --- a/macros/src/variable_names.rs +++ b/macros/src/variable_names.rs @@ -24,110 +24,130 @@ pub struct BuiltinMember { pub host_ty: &'static str, pub mem_ty: BuiltinMemberType, pub var_name: &'static str, + pub run_before_clone: Option<&'static str>, } use phf::phf_map; use self::BuiltinMemberType::*; +macro_rules! builtin_member { + ($host_ty:literal, $mem_ty:ident, $var_name:literal, $run_before_clone:literal) => { + BuiltinMember { + host_ty: $host_ty, + mem_ty: $mem_ty, + var_name: $var_name, + run_before_clone: Some($run_before_clone), + } + }; + ($host_ty:literal, $mem_ty:ident, $var_name:literal) => { + BuiltinMember { + host_ty: $host_ty, + mem_ty: $mem_ty, + var_name: $var_name, + run_before_clone: None, + } + }; +} pub static BUILTIN_INFOS: phf::Map<&'static str, BuiltinMember> = phf_map! { // BuiltinObj - "lazy_host_id" => BuiltinMember { host_ty: "BuiltinObj", mem_ty: Method, var_name: "lazy"}, - "lazy_id" => BuiltinMember { host_ty: "BuiltinObj", mem_ty: Method, var_name: "lazy"}, + "lazy_host_id" => builtin_member!{"BuiltinObj", Method, "lazy"}, + "lazy_id" => builtin_member!{"BuiltinObj", Method, "lazy"}, // Class - "class" => BuiltinMember { host_ty: "Class", mem_ty: Field, var_name: "class" }, + "class" => builtin_member!{"Class", Field, "class"}, + // MixFlags + "has_focus" => builtin_member!{"MixFlags", Method, "mix_flags", "trace_focus" }, + "is_hover" => builtin_member!{"MixFlags", Method, "mix_flags", "trace_hover" }, + "is_pointer_pressed" => builtin_member!{"MixFlags", Method, "mix_flags", "trace_pointer_pressed" }, + "is_auto_focus" => builtin_member!{"MixFlags", Method, "mix_flags"}, + "set_auto_focus" => builtin_member!{"MixFlags", Method, "mix_flags"}, + "tab_index" => builtin_member!{"MixFlags", Method, "mix_flags"}, + "set_tab_index" => builtin_member!{"MixFlags", Method, "mix_flags"}, // MixBuiltin - "auto_focus" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Field, var_name: "mix_builtin" }, - "tab_index" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Field, var_name: "mix_builtin" }, - "on_event" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_mounted" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_disposed" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_performed_layout" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_pointer_down" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_pointer_down_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_pointer_up" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_pointer_up_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_pointer_move" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_pointer_move_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_pointer_cancel" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_pointer_enter" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_pointer_leave" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_tap" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_tap_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_double_tap" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_double_tap_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_triple_tap" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_triple_tap_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_x_times_tap" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_x_times_tap_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_ime_pre_edit" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_ime_pre_edit_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_wheel" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_wheel_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_chars" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_chars_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_key_down" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_key_down_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_key_up" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_key_up_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_focus" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_blur" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_focus_in" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_focus_in_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_focus_out" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "on_focus_out_capture" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "mix_builtin" }, - "events_stream" => BuiltinMember { host_ty: "MixBuiltin", mem_ty: Method, var_name: "request_focus" }, + "on_event" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_mounted" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_disposed" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_performed_layout" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_pointer_down" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_pointer_down_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_pointer_up" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_pointer_up_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_pointer_move" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_pointer_move_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_pointer_cancel" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_pointer_enter" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_pointer_leave" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_tap" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_tap_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_double_tap" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_double_tap_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_triple_tap" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_triple_tap_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_x_times_tap" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_x_times_tap_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_ime_pre_edit" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_ime_pre_edit_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_wheel" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_wheel_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_chars" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_chars_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_key_down" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_key_down_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_key_up" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_key_up_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_focus" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_blur" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_focus_in" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_focus_in_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_focus_out" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "on_focus_out_capture" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + "events_stream" => builtin_member!{"MixBuiltin", Method, "mix_builtin"}, + // RequestFocus - "request_focus" => BuiltinMember { host_ty: "RequestFocus", mem_ty: Method, var_name: "request_focus" }, - "unfocus" => BuiltinMember { host_ty: "RequestFocus", mem_ty: Method, var_name: "request_focus" }, - // HasFocus - "has_focus" => BuiltinMember { host_ty: "HasFocus", mem_ty: Method, var_name: "has_focus" }, - // MouseHover - "mouse_hover" => BuiltinMember { host_ty: "MouseHover", mem_ty: Method, var_name: "mouse_hover" }, - // PointerPressed - "pointer_pressed" => BuiltinMember { host_ty: "PointerPressed", mem_ty: Method, var_name: "pointer_pressed" }, + "request_focus" => builtin_member!{"RequestFocus", Method, "request_focus"}, + "unfocus" => builtin_member!{"RequestFocus", Method, "request_focus"}, // FittedBox - "box_fit" => BuiltinMember { host_ty: "FittedBox", mem_ty: Field, var_name: "fitted_box" }, + "box_fit" => builtin_member!{"FittedBox", Field, "fitted_box"}, // BoxDecoration - "background" => BuiltinMember { host_ty: "BoxDecoration", mem_ty: Field, var_name: "box_decoration" }, - "border" => BuiltinMember { host_ty: "BoxDecoration", mem_ty: Field, var_name: "box_decoration" }, - "border_radius" => BuiltinMember { host_ty: "BoxDecoration", mem_ty: Field, var_name: "box_decoration" }, + "background" => builtin_member!{"BoxDecoration", Field, "box_decoration"}, + "border" => builtin_member!{"BoxDecoration", Field, "box_decoration"}, + "border_radius" => builtin_member!{"BoxDecoration", Field, "box_decoration"}, // Padding - "padding" => BuiltinMember { host_ty: "Padding", mem_ty: Field, var_name: "padding" }, + "padding" => builtin_member!{"Padding", Field, "padding"}, // LayoutBox - "layout_rect" => BuiltinMember { host_ty: "LayoutBox", mem_ty: Method, var_name: "layout_box"}, - "layout_pos" => BuiltinMember { host_ty: "LayoutBox", mem_ty: Method, var_name: "layout_box"}, - "layout_size" => BuiltinMember { host_ty: "LayoutBox", mem_ty: Method, var_name: "layout_box"}, - "layout_left" => BuiltinMember { host_ty: "LayoutBox", mem_ty: Method, var_name: "layout_box"}, - "layout_top" => BuiltinMember { host_ty: "LayoutBox", mem_ty: Method, var_name: "layout_box"}, - "layout_width" => BuiltinMember { host_ty: "LayoutBox", mem_ty: Method, var_name: "layout_box"}, - "layout_height" => BuiltinMember { host_ty: "LayoutBox", mem_ty: Method, var_name: "layout_box"}, + "layout_rect" => builtin_member!{"LayoutBox", Method, "layout_box"}, + "layout_pos" => builtin_member!{"LayoutBox", Method, "layout_box"}, + "layout_size" => builtin_member!{"LayoutBox", Method, "layout_box"}, + "layout_left" => builtin_member!{"LayoutBox", Method, "layout_box"}, + "layout_top" => builtin_member!{"LayoutBox", Method, "layout_box"}, + "layout_width" => builtin_member!{"LayoutBox", Method, "layout_box"}, + "layout_height" => builtin_member!{"LayoutBox", Method, "layout_box"}, // GlobalAnchor - "global_anchor" => BuiltinMember { host_ty: "GlobalAnchor", mem_ty: Field, var_name: "global_anchor" }, + "global_anchor" => builtin_member!{"GlobalAnchor", Field, "global_anchor"}, // Cursor - "cursor" => BuiltinMember { host_ty: "Cursor", mem_ty: Field, var_name: "cursor" }, + "cursor" => builtin_member!{"Cursor", Field, "cursor"}, // Margin - "margin" => BuiltinMember { host_ty: "Margin", mem_ty: Field, var_name: "margin" }, + "margin" => builtin_member!{"Margin", Field, "margin"}, // ScrollableWidget - "scrollable" => BuiltinMember { host_ty: "ScrollableWidget", mem_ty: Field, var_name: "scrollable"}, - "get_scroll_pos" => BuiltinMember { host_ty: "ScrollableWidget", mem_ty: Method, var_name: "scrollable"}, - "scroll_view_size" => BuiltinMember { host_ty: "ScrollableWidget", mem_ty: Method, var_name: "scrollable"}, - "scroll_content_size" => BuiltinMember { host_ty: "ScrollableWidget", mem_ty: Method, var_name: "scrollable"}, - "jump_to" => BuiltinMember { host_ty: "ScrollableWidget", mem_ty: Method, var_name: "scrollable"}, + "scrollable" => builtin_member!{"ScrollableWidget", Field, "scrollable"}, + "get_scroll_pos" => builtin_member!{"ScrollableWidget", Method, "scrollable"}, + "scroll_view_size" => builtin_member!{"ScrollableWidget", Method, "scrollable"}, + "scroll_content_size" => builtin_member!{"ScrollableWidget", Method, "scrollable"}, + "jump_to" => builtin_member!{"ScrollableWidget", Method, "scrollable"}, // ConstrainedBox - "clamp" => BuiltinMember { host_ty: "ConstrainedBox", mem_ty: Field, var_name: "constrained_box"}, + "clamp" => builtin_member!{"ConstrainedBox", Field, "constrained_box"}, // TransformWidget - "transform" => BuiltinMember { host_ty: "TransformWidget", mem_ty: Field, var_name: "transform" }, + "transform" => builtin_member!{"TransformWidget", Field, "transform"}, // HAlignWidget - "h_align" => BuiltinMember { host_ty: "HAlignWidget", mem_ty: Field, var_name: "h_align" }, + "h_align" => builtin_member!{"HAlignWidget", Field, "h_align"}, // VAlignWidget - "v_align" => BuiltinMember { host_ty: "VAlignWidget", mem_ty: Field, var_name: "v_align" }, + "v_align" => builtin_member!{"VAlignWidget", Field, "v_align"}, // RelativeAnchor - "anchor" => BuiltinMember { host_ty: "RelativeAnchor", mem_ty: Field, var_name: "relative_anchor" }, + "anchor" => builtin_member!{"RelativeAnchor", Field, "relative_anchor"}, // Visibility - "visible" => BuiltinMember { host_ty: "Visibility", mem_ty: Field, var_name: "visibility" }, + "visible" => builtin_member!{"Visibility", Field, "visibility"}, // Opacity - "opacity" => BuiltinMember { host_ty: "Opacity", mem_ty: Field, var_name: "opacity" }, + "opacity" => builtin_member!{"Opacity", Field, "opacity"}, // KeepAlive - "keep_alive" => BuiltinMember { host_ty: "KeepAlive", mem_ty: Field, var_name: "keep_alive" }, + "keep_alive" => builtin_member!{"KeepAlive", Field, "keep_alive"}, }; diff --git a/themes/material/src/classes/scrollbar_cls.rs b/themes/material/src/classes/scrollbar_cls.rs index e8c3c03e..09048263 100644 --- a/themes/material/src/classes/scrollbar_cls.rs +++ b/themes/material/src/classes/scrollbar_cls.rs @@ -61,7 +61,7 @@ fn base_track(w: Widget) -> Widget { f.unsubscribe(); } let u = observable::timer((), Duration::from_secs(3), AppCtx::scheduler()) - .filter(move |_| !$w.mouse_hover()) + .filter(move |_| !$w.is_hover()) .subscribe(move |_| $w.write().opacity = 0.); fade = Some(u); }); @@ -78,7 +78,7 @@ fn base_track(w: Widget) -> Widget { let mut w = @ $w { background: { let color = Palette::of(ctx!()).primary_container(); - pipe!(if $w.mouse_hover() { color } else { color.with_alpha(0.)}) + pipe!(if $w.is_hover() { color } else { color.with_alpha(0.)}) }, on_disposed: move |_| u.unsubscribe(), }; diff --git a/themes/material/src/ripple.rs b/themes/material/src/ripple.rs index 9f2e943e..057bf03f 100644 --- a/themes/material/src/ripple.rs +++ b/themes/material/src/ripple.rs @@ -71,7 +71,7 @@ impl<'c> ComposeChild<'c> for Ripple { from: Path::circle(Point::zero(), 0.) }; - watch!(!$container.pointer_pressed() && !$ripper_enter.is_running()) + watch!(!$container.is_pointer_pressed() && !$ripper_enter.is_running()) .filter(|b| *b) // the ripple used only once, so we unsubscribe it after the animate finished. .take(1) diff --git a/themes/material/src/state_layer.rs b/themes/material/src/state_layer.rs index fe7b81ed..f59465e1 100644 --- a/themes/material/src/state_layer.rs +++ b/themes/material/src/state_layer.rs @@ -45,11 +45,11 @@ impl<'c> ComposeChild<'c> for InteractiveLayer { @StateLayer { color: pipe!($this.color), path: pipe!(Path::rect_round(&$host.layout_rect(), &$this.border_radii)), - role: pipe!(if $host.pointer_pressed() { + role: pipe!(if $host.is_pointer_pressed() { StateRole::pressed() } else if $host.has_focus() { StateRole::focus() - } else if $host.mouse_hover() { + } else if $host.is_hover() { StateRole::hover() } else { // todo: not support drag & drop now