From 6bb3e88c2e7b98a398aace185cc39d025b7ed667 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Wed, 23 Aug 2017 16:43:17 -0700 Subject: [PATCH] feat: Add pre-events and inner events to OnEventView --- src/event.rs | 15 +++- src/views/on_event_view.rs | 176 ++++++++++++++++++++++++++++++++----- 2 files changed, 168 insertions(+), 23 deletions(-) diff --git a/src/event.rs b/src/event.rs index 9e77d68..9d32b77 100644 --- a/src/event.rs +++ b/src/event.rs @@ -87,10 +87,21 @@ impl EventResult { cb(s); } } + + /// Returns `self` if it is not `Event::Ignored`, otherwise returns `f()`. + pub fn or_else(self, f: F) -> Self + where + F: FnOnce() -> EventResult, + { + match self { + EventResult::Ignored => f(), + other => other, + } + } } /// A non-character key on the keyboard -#[derive(PartialEq,Eq,Clone,Copy,Hash,Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] pub enum Key { /// Both Enter (or Return) and numpad Enter Enter, @@ -186,7 +197,7 @@ impl Key { } /// Represents an event as seen by the application. -#[derive(PartialEq,Eq,Clone,Hash,Debug)] +#[derive(PartialEq, Eq, Clone, Hash, Debug)] pub enum Event { /// Event fired when the window is resized. WindowResize, diff --git a/src/views/on_event_view.rs b/src/views/on_event_view.rs index 402de06..7750b69 100644 --- a/src/views/on_event_view.rs +++ b/src/views/on_event_view.rs @@ -1,10 +1,10 @@ - - use Cursive; +use With; use event::{Callback, Event, EventResult}; use std::collections::HashMap; + +use std::rc::Rc; use view::{View, ViewWrapper}; -use With; /// 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()); /// ``` pub struct OnEventView { - content: T, - callbacks: HashMap, + inner: T, + callbacks: HashMap>, +} + +type InnerCallback = Rc Option>>; + +struct Action { + phase: TriggerPhase, + callback: InnerCallback, +} + +impl Clone for Action { + fn clone(&self) -> Self { + Action { + phase: self.phase.clone(), + callback: self.callback.clone(), + } + } +} + +#[derive(PartialEq, Clone)] +enum TriggerPhase { + BeforeChild, + AfterChild, } impl OnEventView { /// Wraps the given view in a new OnEventView. pub fn new(view: T) -> Self { OnEventView { - content: view, + inner: view, callbacks: HashMap::new(), } } @@ -36,32 +58,144 @@ impl OnEventView { /// Registers a callback when the given event is ignored by the child. /// /// Chainable variant. - pub fn on_event>(self, event: E, cb: F) -> Self - where F: Fn(&mut Cursive) + 'static + pub fn on_event(self, event: E, cb: F) -> Self + where + E: Into, + F: Fn(&mut Cursive) + 'static, { self.with(|s| s.set_on_event(event, cb)) } - /// Registers a callback when the given event is ignored by the child. - pub fn set_on_event>(&mut self, event: E, cb: F) - where F: Fn(&mut Cursive) + 'static + /// Registers a callback when the given event is received. + /// + /// The child will never receive this event. + /// + /// Chainable variant. + pub fn on_pre_event(self, event: E, cb: F) -> Self + where + E: Into, + 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(self, event: E, cb: F) -> Self + where + E: Into, + F: Fn(&mut T) -> Option + '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(self, event: E, cb: F) -> Self + where + E: Into, + F: Fn(&mut T) -> Option + '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(&mut self, event: E, cb: F) + where + E: Into, + 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(&mut self, event: E, cb: F) + where + E: Into, + 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(&mut self, event: E, cb: F) + where + E: Into, + F: Fn(&mut T) -> Option + '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(&mut self, event: E, cb: F) + where + E: Into, + F: Fn(&mut T) -> Option + 'static, + { + self.callbacks.insert( + event.into(), + Action { + phase: TriggerPhase::AfterChild, + callback: Rc::new(Box::new(cb)), + }, + ); } } impl ViewWrapper for OnEventView { - wrap_impl!(self.content: T); + wrap_impl!(self.inner: T); fn wrap_on_event(&mut self, event: Event) -> EventResult { - match self.content.on_event(event.clone()) { - EventResult::Ignored => { - match self.callbacks.get(&event) { - None => EventResult::Ignored, - Some(cb) => EventResult::Consumed(Some(cb.clone())), - } - } - res => res, + let action = self.callbacks.get(&event).cloned(); + let pre_child = action + .as_ref() + .map(|a| a.phase == TriggerPhase::BeforeChild) + .unwrap_or(false); + + if pre_child { + 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) + }) } } }