Make AnyCb take a &mut dyn View and add Cursive::debug_name

This commit is contained in:
Alexandre Bury 2020-03-03 13:08:55 -08:00
parent 5423a9a003
commit 4c5e4abe49
9 changed files with 98 additions and 22 deletions

View File

@ -545,7 +545,7 @@ impl Cursive {
callback: F,
) -> Option<R>
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<R>
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<V, F, R>(&mut self, id: &str, callback: F) -> Option<R>
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<V>(&mut self, id: &str) -> Option<views::ViewRef<V>>
where
V: View + Any,
V: View,
{
self.call_on_name(id, views::NamedView::<V>::get_mut)
}
@ -649,7 +649,7 @@ impl Cursive {
#[deprecated(note = "`find_id` is being renamed to `find_name`")]
pub fn find_id<V>(&mut self, id: &str) -> Option<views::ViewRef<V>>
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<F, E>(&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<E, F>(&mut self, trigger: E, cb: F)
where
E: Into<crate::event::EventTrigger>,
F: Fn(&Event) -> Option<EventResult> + '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<E, F>(&mut self, trigger: E, cb: F)
where
E: Into<crate::event::EventTrigger>,
F: Fn(&Event) -> Option<EventResult> + '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

View File

@ -26,10 +26,10 @@ use std::rc::Rc;
pub struct Callback(Rc<Box<dyn Fn(&mut Cursive)>>);
// TODO: remove the Box when Box<T: Sized> -> Rc<T> 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.
///

View File

@ -63,4 +63,9 @@ impl dyn AnyView {
Err(self)
}
}
/// Checks if this view is of type `T`.
pub fn is<T: Any>(&mut self) -> bool {
self.as_any().is::<T>()
}
}

View File

@ -1,6 +1,5 @@
use crate::view::{View, ViewPath, ViewWrapper};
use crate::views::{NamedView, ViewRef};
use std::any::Any;
/// Provides `call_on<V: View>` to views.
///
@ -20,13 +19,13 @@ pub trait Finder {
callback: F,
) -> Option<R>
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<V, F, R>(&mut self, name: &str, callback: F) -> Option<R>
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<V, F, R>(&mut self, id: &str, callback: F) -> Option<R>
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<V>(&mut self, name: &str) -> Option<ViewRef<V>>
where
V: View + Any,
V: View,
{
self.call_on_name(name, NamedView::<V>::get_mut)
}
@ -54,7 +53,7 @@ pub trait Finder {
#[deprecated(note = "`find_id` is being renamed to `find_name`")]
fn find_id<V>(&mut self, id: &str) -> Option<ViewRef<V>>
where
V: View + Any,
V: View,
{
self.find_name(id)
}
@ -67,7 +66,7 @@ impl<T: View> Finder for T {
callback: F,
) -> Option<R>
where
V: View + Any,
V: View,
F: FnOnce(&mut V) -> R,
{
let mut result = None;
@ -75,12 +74,13 @@ impl<T: View> 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::<V>() {
*result_ref =
v.downcast_mut::<V>().map(|v| callback(v));
} else if v.is::<NamedView<V>>() {
// Special case
*result_ref = v
.downcast_mut::<NamedView<V>>()
.and_then(|v| v.with_view_mut(callback));

View File

@ -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::<Self>()
}
}
impl dyn View {
/// Attempts to downcast `self` to a concrete type.
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
self.as_any().downcast_ref()
}
/// Attempts to downcast `self` to a concrete type.
pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
self.as_any_mut().downcast_mut()
}
/// Attempts to downcast `Box<Self>` to a concrete type.
pub fn downcast<T: Any>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
// Do the check here + unwrap, so the error
// value is `Self` and not `dyn Any`.
if self.as_any().is::<T>() {
Ok(self.as_boxed_any().downcast().unwrap())
} else {
Err(self)
}
}
/// Checks if this view is of type `T`.
pub fn is<T: Any>(&self) -> bool {
self.as_any().is::<T>()
}
}

View File

@ -127,7 +127,6 @@ impl Dialog {
/// let dialog = Dialog::around(TextView::new("Hello!"));
/// let text_view: &TextView = dialog
/// .get_content()
/// .as_any()
/// .downcast_ref::<TextView>()
/// .unwrap();
/// assert_eq!(text_view.get_content().source(), "Hello!");

View File

@ -76,7 +76,6 @@ impl<T: View + 'static> ViewWrapper for NamedView<T> {
}
}
// Some for<'b> weirdness here to please the borrow checker gods...
fn wrap_call_on_any<'a>(
&mut self,
selector: &Selector<'_>,

View File

@ -769,22 +769,18 @@ mod tests {
assert!(stack
.get(LayerPosition::FromFront(0))
.unwrap()
.as_any()
.is::<TextView>());
assert!(stack
.get(LayerPosition::FromBack(0))
.unwrap()
.as_any()
.is::<TextView>());
assert!(stack
.get_mut(LayerPosition::FromFront(0))
.unwrap()
.as_any_mut()
.is::<TextView>());
assert!(stack
.get_mut(LayerPosition::FromBack(0))
.unwrap()
.as_any_mut()
.is::<TextView>());
}
}

View File

@ -201,7 +201,7 @@ impl TextView {
Self::new_with_content(TextContent::new(content))
}
/// Creates a new TextView using the given `Arc<Mutex<String>>`.
/// 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.