diff --git a/src/cursive.rs b/src/cursive.rs index 27aaa98..38afb46 100644 --- a/src/cursive.rs +++ b/src/cursive.rs @@ -545,7 +545,7 @@ impl Cursive { callback: F, ) -> Option where - V: View + Any, + V: View, F: FnOnce(&mut V) -> R, { self.root.call_on(sel, callback) @@ -577,7 +577,7 @@ impl Cursive { callback: F, ) -> Option where - V: View + Any, + V: View, F: FnOnce(&mut V) -> R, { self.call_on(&view::Selector::Name(name), callback) @@ -587,7 +587,7 @@ impl Cursive { #[deprecated(note = "`call_on_id` is being renamed to `call_on_name`")] pub fn call_on_id(&mut self, id: &str, callback: F) -> Option where - V: View + Any, + V: View, F: FnOnce(&mut V) -> R, { self.call_on_name(id, callback) @@ -640,7 +640,7 @@ impl Cursive { /// [`ViewRef`]: views::ViewRef pub fn find_name(&mut self, id: &str) -> Option> where - V: View + Any, + V: View, { self.call_on_name(id, views::NamedView::::get_mut) } @@ -649,7 +649,7 @@ impl Cursive { #[deprecated(note = "`find_id` is being renamed to `find_name`")] pub fn find_id(&mut self, id: &str) -> Option> where - V: View + Any, + V: View, { self.find_name(id) } @@ -706,6 +706,9 @@ impl Cursive { /// /// If an event matches the given trigger, it will not be sent to the view /// tree and will go to the given callback instead. + /// + /// Note that regular "post-event" callbacks will also be skipped for + /// these events. pub fn set_on_pre_event(&mut self, trigger: E, cb: F) where F: FnMut(&mut Cursive) + 'static, @@ -714,6 +717,34 @@ impl Cursive { self.root.set_on_pre_event(trigger, crate::immut1!(cb)); } + /// Registers an inner priority callback. + /// + /// See [`OnEventView`] for more information. + /// + /// [`OnEventView`]: crate::views::OnEventView::set_on_pre_event_inner() + pub fn set_on_pre_event_inner(&mut self, trigger: E, cb: F) + where + E: Into, + F: Fn(&Event) -> Option + 'static, + { + self.root + .set_on_pre_event_inner(trigger, move |_, event| cb(event)); + } + + /// Registers an inner callback. + /// + /// See [`OnEventView`] for more information. + /// + /// [`OnEventView`]: crate::views::OnEventView::set_on_event_inner() + pub fn set_on_event_inner(&mut self, trigger: E, cb: F) + where + E: Into, + F: Fn(&Event) -> Option + 'static, + { + self.root + .set_on_event_inner(trigger, move |_, event| cb(event)); + } + /// Sets the only global callback for the given event. /// /// Any other callback for this event will be removed. @@ -728,6 +759,17 @@ impl Cursive { self.add_global_callback(event, cb); } + /// Fetches the type name of a view in the tree. + pub fn debug_name(&mut self, name: &str) -> Option<&'static str> { + let mut result = None; + + self.root.call_on_any( + &view::Selector::Name(name), + &mut |v: &mut dyn crate::View| result = Some(v.type_name()), + ); + result + } + /// Removes any callback tied to the given event. /// /// # Examples diff --git a/src/event.rs b/src/event.rs index 8a89797..3559c9d 100644 --- a/src/event.rs +++ b/src/event.rs @@ -26,10 +26,10 @@ use std::rc::Rc; pub struct Callback(Rc>); // TODO: remove the Box when Box -> Rc is possible -/// A callback that can be run on `&mut Any`. +/// A callback that can be run on `&mut dyn View`. /// /// It is meant to be used as parameter in `View::call_on_any`, and not much else. -pub type AnyCb<'a> = &'a mut dyn FnMut(&mut dyn Any); +pub type AnyCb<'a> = &'a mut dyn FnMut(&mut dyn crate::view::View); /// A trigger that only selects some types of events. /// diff --git a/src/view/any.rs b/src/view/any.rs index 4193023..816d855 100644 --- a/src/view/any.rs +++ b/src/view/any.rs @@ -63,4 +63,9 @@ impl dyn AnyView { Err(self) } } + + /// Checks if this view is of type `T`. + pub fn is(&mut self) -> bool { + self.as_any().is::() + } } diff --git a/src/view/finder.rs b/src/view/finder.rs index 37f6bac..dd5833c 100644 --- a/src/view/finder.rs +++ b/src/view/finder.rs @@ -1,6 +1,5 @@ use crate::view::{View, ViewPath, ViewWrapper}; use crate::views::{NamedView, ViewRef}; -use std::any::Any; /// Provides `call_on` to views. /// @@ -20,13 +19,13 @@ pub trait Finder { callback: F, ) -> Option where - V: View + Any, + V: View, F: FnOnce(&mut V) -> R; /// Convenient method to use `call_on` with a `view::Selector::Name`. fn call_on_name(&mut self, name: &str, callback: F) -> Option where - V: View + Any, + V: View, F: FnOnce(&mut V) -> R, { self.call_on(&Selector::Name(name), callback) @@ -36,7 +35,7 @@ pub trait Finder { #[deprecated(note = "`call_on_id` is being renamed to `call_on_name`")] fn call_on_id(&mut self, id: &str, callback: F) -> Option where - V: View + Any, + V: View, F: FnOnce(&mut V) -> R, { self.call_on_name(id, callback) @@ -45,7 +44,7 @@ pub trait Finder { /// Convenient method to find a view wrapped in an [`NamedView`]. fn find_name(&mut self, name: &str) -> Option> where - V: View + Any, + V: View, { self.call_on_name(name, NamedView::::get_mut) } @@ -54,7 +53,7 @@ pub trait Finder { #[deprecated(note = "`find_id` is being renamed to `find_name`")] fn find_id(&mut self, id: &str) -> Option> where - V: View + Any, + V: View, { self.find_name(id) } @@ -67,7 +66,7 @@ impl Finder for T { callback: F, ) -> Option where - V: View + Any, + V: View, F: FnOnce(&mut V) -> R, { let mut result = None; @@ -75,12 +74,13 @@ impl Finder for T { let result_ref = &mut result; let mut callback = Some(callback); - let mut callback = |v: &mut dyn Any| { + let mut callback = |v: &mut dyn View| { if let Some(callback) = callback.take() { if v.is::() { *result_ref = v.downcast_mut::().map(|v| callback(v)); } else if v.is::>() { + // Special case *result_ref = v .downcast_mut::>() .and_then(|v| v.with_view_mut(callback)); diff --git a/src/view/view_trait.rs b/src/view/view_trait.rs index 066c0cd..7183d0f 100644 --- a/src/view/view_trait.rs +++ b/src/view/view_trait.rs @@ -118,4 +118,39 @@ pub trait View: Any + AnyView { fn important_area(&self, view_size: Vec2) -> Rect { Rect::from_size((0, 0), view_size) } + + /// Returns the type of this view. + /// + /// Useful when you have a `&dyn View`. + fn type_name(&self) -> &'static str { + std::any::type_name::() + } +} + +impl dyn View { + /// Attempts to downcast `self` to a concrete type. + pub fn downcast_ref(&self) -> Option<&T> { + self.as_any().downcast_ref() + } + + /// Attempts to downcast `self` to a concrete type. + pub fn downcast_mut(&mut self) -> Option<&mut T> { + self.as_any_mut().downcast_mut() + } + + /// Attempts to downcast `Box` to a concrete type. + pub fn downcast(self: Box) -> Result, Box> { + // Do the check here + unwrap, so the error + // value is `Self` and not `dyn Any`. + if self.as_any().is::() { + Ok(self.as_boxed_any().downcast().unwrap()) + } else { + Err(self) + } + } + + /// Checks if this view is of type `T`. + pub fn is(&self) -> bool { + self.as_any().is::() + } } diff --git a/src/views/dialog.rs b/src/views/dialog.rs index 9440fe3..bc2f3bc 100644 --- a/src/views/dialog.rs +++ b/src/views/dialog.rs @@ -127,7 +127,6 @@ impl Dialog { /// let dialog = Dialog::around(TextView::new("Hello!")); /// let text_view: &TextView = dialog /// .get_content() - /// .as_any() /// .downcast_ref::() /// .unwrap(); /// assert_eq!(text_view.get_content().source(), "Hello!"); diff --git a/src/views/named_view.rs b/src/views/named_view.rs index 4f1608b..6a99ee8 100644 --- a/src/views/named_view.rs +++ b/src/views/named_view.rs @@ -76,7 +76,6 @@ impl ViewWrapper for NamedView { } } - // Some for<'b> weirdness here to please the borrow checker gods... fn wrap_call_on_any<'a>( &mut self, selector: &Selector<'_>, diff --git a/src/views/stack_view.rs b/src/views/stack_view.rs index 23db237..206e900 100644 --- a/src/views/stack_view.rs +++ b/src/views/stack_view.rs @@ -769,22 +769,18 @@ mod tests { assert!(stack .get(LayerPosition::FromFront(0)) .unwrap() - .as_any() .is::()); assert!(stack .get(LayerPosition::FromBack(0)) .unwrap() - .as_any() .is::()); assert!(stack .get_mut(LayerPosition::FromFront(0)) .unwrap() - .as_any_mut() .is::()); assert!(stack .get_mut(LayerPosition::FromBack(0)) .unwrap() - .as_any_mut() .is::()); } } diff --git a/src/views/text_view.rs b/src/views/text_view.rs index 478bf08..0901e5c 100644 --- a/src/views/text_view.rs +++ b/src/views/text_view.rs @@ -201,7 +201,7 @@ impl TextView { Self::new_with_content(TextContent::new(content)) } - /// Creates a new TextView using the given `Arc>`. + /// Creates a new TextView using the given `TextContent`. /// /// If you kept a clone of the given content, you'll be able to update it /// remotely.