feat: Add pre-events and inner events to OnEventView

This commit is contained in:
Alexandre Bury 2017-08-23 16:43:17 -07:00
parent 2ac5dcb559
commit 6bb3e88c2e
2 changed files with 168 additions and 23 deletions

View File

@ -87,10 +87,21 @@ impl EventResult {
cb(s); cb(s);
} }
} }
/// Returns `self` if it is not `Event::Ignored`, otherwise returns `f()`.
pub fn or_else<F>(self, f: F) -> Self
where
F: FnOnce() -> EventResult,
{
match self {
EventResult::Ignored => f(),
other => other,
}
}
} }
/// A non-character key on the keyboard /// A non-character key on the keyboard
#[derive(PartialEq,Eq,Clone,Copy,Hash,Debug)] #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
pub enum Key { pub enum Key {
/// Both Enter (or Return) and numpad Enter /// Both Enter (or Return) and numpad Enter
Enter, Enter,
@ -186,7 +197,7 @@ impl Key {
} }
/// Represents an event as seen by the application. /// Represents an event as seen by the application.
#[derive(PartialEq,Eq,Clone,Hash,Debug)] #[derive(PartialEq, Eq, Clone, Hash, Debug)]
pub enum Event { pub enum Event {
/// Event fired when the window is resized. /// Event fired when the window is resized.
WindowResize, WindowResize,

View File

@ -1,10 +1,10 @@
use Cursive; use Cursive;
use With;
use event::{Callback, Event, EventResult}; use event::{Callback, Event, EventResult};
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc;
use view::{View, ViewWrapper}; use view::{View, ViewWrapper};
use With;
/// A simple wrapper view that catches some ignored event from its child. /// A simple wrapper view that catches some ignored event from its child.
/// ///
@ -20,15 +20,37 @@ use With;
/// .on_event(event::Key::Esc, |s| s.quit()); /// .on_event(event::Key::Esc, |s| s.quit());
/// ``` /// ```
pub struct OnEventView<T: View> { pub struct OnEventView<T: View> {
content: T, inner: T,
callbacks: HashMap<Event, Callback>, callbacks: HashMap<Event, Action<T>>,
}
type InnerCallback<T> = Rc<Box<Fn(&mut T) -> Option<EventResult>>>;
struct Action<T> {
phase: TriggerPhase,
callback: InnerCallback<T>,
}
impl<T> Clone for Action<T> {
fn clone(&self) -> Self {
Action {
phase: self.phase.clone(),
callback: self.callback.clone(),
}
}
}
#[derive(PartialEq, Clone)]
enum TriggerPhase {
BeforeChild,
AfterChild,
} }
impl<T: View> OnEventView<T> { impl<T: View> OnEventView<T> {
/// Wraps the given view in a new OnEventView. /// Wraps the given view in a new OnEventView.
pub fn new(view: T) -> Self { pub fn new(view: T) -> Self {
OnEventView { OnEventView {
content: view, inner: view,
callbacks: HashMap::new(), callbacks: HashMap::new(),
} }
} }
@ -36,32 +58,144 @@ impl<T: View> OnEventView<T> {
/// Registers a callback when the given event is ignored by the child. /// Registers a callback when the given event is ignored by the child.
/// ///
/// Chainable variant. /// Chainable variant.
pub fn on_event<F, E: Into<Event>>(self, event: E, cb: F) -> Self pub fn on_event<F, E>(self, event: E, cb: F) -> Self
where F: Fn(&mut Cursive) + 'static where
E: Into<Event>,
F: Fn(&mut Cursive) + 'static,
{ {
self.with(|s| s.set_on_event(event, cb)) self.with(|s| s.set_on_event(event, cb))
} }
/// Registers a callback when the given event is ignored by the child. /// Registers a callback when the given event is received.
pub fn set_on_event<F, E: Into<Event>>(&mut self, event: E, cb: F) ///
where F: Fn(&mut Cursive) + 'static /// The child will never receive this event.
///
/// Chainable variant.
pub fn on_pre_event<F, E>(self, event: E, cb: F) -> Self
where
E: Into<Event>,
F: Fn(&mut Cursive) + 'static,
{ {
self.callbacks.insert(event.into(), Callback::from_fn(cb)); self.with(|s| s.set_on_pre_event(event, cb))
}
/// Registers a callback when the given event is received.
///
/// The given callback will be run before the child view sees the event.
/// If the result is `None`, then the child view is given the event as usual.
/// Otherwise, it bypasses the child view and directly processes the result.
///
/// Chainable variant.
pub fn on_pre_event_inner<F, E>(self, event: E, cb: F) -> Self
where
E: Into<Event>,
F: Fn(&mut T) -> Option<EventResult> + 'static,
{
self.with(|s| s.set_on_pre_event_inner(event, cb))
}
/// Registers a callback when the given event is ignored by the child.
///
/// If the child view ignores the event, `cb` will be called with the
/// child view as argument.
/// If the result is not `None`, it will be processed as well.
///
/// Chainable variant.
pub fn on_event_inner<F, E>(self, event: E, cb: F) -> Self
where
E: Into<Event>,
F: Fn(&mut T) -> Option<EventResult> + 'static,
{
self.with(|s| s.set_on_event_inner(event, cb))
}
/// Registers a callback when the given event is ignored by the child.
pub fn set_on_event<F, E>(&mut self, event: E, cb: F)
where
E: Into<Event>,
F: Fn(&mut Cursive) + 'static,
{
let cb = Callback::from_fn(cb);
let action =
move |_: &mut T| Some(EventResult::Consumed(Some(cb.clone())));
self.set_on_event_inner(event, action);
}
/// Registers a callback when the given event is received.
///
/// The child will never receive this event.
pub fn set_on_pre_event<F, E>(&mut self, event: E, cb: F)
where
E: Into<Event>,
F: Fn(&mut Cursive) + 'static,
{
let cb = Callback::from_fn(cb);
let action =
move |_: &mut T| Some(EventResult::Consumed(Some(cb.clone())));
self.set_on_pre_event_inner(event, action);
}
/// Registers a callback when the given event is received.
///
/// The given callback will be run before the child view sees the event.
/// If the result is `None`, then the child view is given the event as usual.
/// Otherwise, it bypasses the child view and directly processes the result.
pub fn set_on_pre_event_inner<F, E>(&mut self, event: E, cb: F)
where
E: Into<Event>,
F: Fn(&mut T) -> Option<EventResult> + 'static,
{
self.callbacks.insert(
event.into(),
Action {
phase: TriggerPhase::BeforeChild,
callback: Rc::new(Box::new(cb)),
},
);
}
/// Registers a callback when the given event is ignored by the child.
///
/// If the child view ignores the event, `cb` will be called with the
/// child view as argument.
/// If the result is not `None`, it will be processed as well.
pub fn set_on_event_inner<F, E>(&mut self, event: E, cb: F)
where
E: Into<Event>,
F: Fn(&mut T) -> Option<EventResult> + 'static,
{
self.callbacks.insert(
event.into(),
Action {
phase: TriggerPhase::AfterChild,
callback: Rc::new(Box::new(cb)),
},
);
} }
} }
impl<T: View> ViewWrapper for OnEventView<T> { impl<T: View> ViewWrapper for OnEventView<T> {
wrap_impl!(self.content: T); wrap_impl!(self.inner: T);
fn wrap_on_event(&mut self, event: Event) -> EventResult { fn wrap_on_event(&mut self, event: Event) -> EventResult {
match self.content.on_event(event.clone()) { let action = self.callbacks.get(&event).cloned();
EventResult::Ignored => { let pre_child = action
match self.callbacks.get(&event) { .as_ref()
None => EventResult::Ignored, .map(|a| a.phase == TriggerPhase::BeforeChild)
Some(cb) => EventResult::Consumed(Some(cb.clone())), .unwrap_or(false);
}
} if pre_child {
res => res, action
.and_then(|a| (*a.callback)(&mut self.inner))
.unwrap_or_else(|| self.inner.on_event(event))
} else {
self.inner.on_event(event).or_else(|| {
action
.and_then(|a| (*a.callback)(&mut self.inner))
.unwrap_or(EventResult::Ignored)
})
} }
} }
} }