diff --git a/src/backend/curses/mod.rs b/src/backend/curses/mod.rs index d97e4a9..8a8034e 100644 --- a/src/backend/curses/mod.rs +++ b/src/backend/curses/mod.rs @@ -1,6 +1,6 @@ -use theme::{BaseColor, Color}; use event::{Event, Key}; use std::collections::HashMap; +use theme::{BaseColor, Color}; #[cfg(feature = "ncurses")] mod n; @@ -58,7 +58,6 @@ where }; target.insert(code, event); } - } fn find_closest(color: &Color) -> i16 { diff --git a/src/view/any.rs b/src/view/any.rs new file mode 100644 index 0000000..a6be913 --- /dev/null +++ b/src/view/any.rs @@ -0,0 +1,46 @@ +use std::any::Any; +use view::View; + +/// A view that can be downcasted to its concrete type. +/// +/// This trait is automatically implemented for any `T: View`. +pub trait AnyView { + /// Downcast self to a `Any`. + fn as_any(&self) -> &Any; + + /// Downcast self to a mutable `Any`. + fn as_any_mut(&mut self) -> &mut Any; + + /// Returns a boxed any from a boxed self. + /// + /// Can be used before `Box::downcast()`. + /// + /// # Examples + /// + /// ```rust + /// # use cursive::views::TextView; + /// # use cursive::view::View; + /// # fn main() { + /// let boxed: Box = Box::new(TextView::new("text")); + /// let text: Box = boxed.as_boxed_any().downcast().unwrap(); + /// # } + /// ``` + fn as_boxed_any(self: Box) -> Box; +} + +impl AnyView for T { + /// Downcast self to a `Any`. + fn as_any(&self) -> &Any { + self + } + + /// Downcast self to a mutable `Any`. + fn as_any_mut(&mut self) -> &mut Any { + self + } + + fn as_boxed_any(self: Box) -> Box { + self + } +} + diff --git a/src/view/finder.rs b/src/view/finder.rs new file mode 100644 index 0000000..2e81423 --- /dev/null +++ b/src/view/finder.rs @@ -0,0 +1,66 @@ +use std::any::Any; +use view::{View, ViewPath, ViewWrapper}; +use views::IdView; + +/// Provides `call_on` to views. +/// +/// This trait is mostly a wrapper around [`View::call_on_any`]. +/// +/// It provides a nicer interface to find a view when you know its type. +/// +/// [`View::call_on_any`]: ./trait.View.html#method.call_on_any +pub trait Finder { + /// Tries to find the view pointed to by the given selector. + /// + /// If the view is not found, or if it is not of the asked type, + /// it returns None. + fn call_on(&mut self, sel: &Selector, callback: F) -> Option + where + V: View + Any, + F: FnOnce(&mut V) -> R; + + /// Convenient method to use `call_on` with a `view::Selector::Id`. + fn find_id(&mut self, id: &str, callback: F) -> Option + where + V: View + Any, + F: FnOnce(&mut V) -> R, + { + self.call_on(&Selector::Id(id), callback) + } +} + +impl Finder for T { + fn call_on(&mut self, sel: &Selector, callback: F) -> Option + where + V: View + Any, + F: FnOnce(&mut V) -> R, + { + let mut result = None; + { + let result_ref = &mut result; + + let mut callback = Some(callback); + let callback = |v: &mut Any| { + if let Some(callback) = callback.take() { + if v.is::() { + *result_ref = + v.downcast_mut::().map(|v| callback(v)); + } else if v.is::>() { + *result_ref = v.downcast_mut::>() + .and_then(|v| v.with_view_mut(callback)); + } + } + }; + self.call_on_any(sel, Box::new(callback)); + } + result + } +} + +/// Selects a single view (if any) in the tree. +pub enum Selector<'a> { + /// Selects a view from its ID. + Id(&'a str), + /// Selects a view from its path. + Path(&'a ViewPath), +} diff --git a/src/view/internal.rs b/src/view/internal.rs deleted file mode 100644 index e2ce83c..0000000 --- a/src/view/internal.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Internal module for some implementation details. -//! -//! You probable are not interested in anything here. These elements are -//! required to be public, but you should never need to use them directly. - -use view::{View, AnyView}; - -/// A trait for elements that can be converted to `AnyView`. -/// -/// You should never need to implement this yourself; it is automatically -/// implemented for any `T: View`. -/// -/// Just ignore this trait entirely. -pub trait ToAny { - /// Converts a boxed `Self` to a boxed `AnyView`. - fn to_any(self: Box) -> Box; -} - -impl ToAny for T { - fn to_any(self: Box) -> Box { - self - } -} diff --git a/src/view/into_boxed_view.rs b/src/view/into_boxed_view.rs index 9db8510..d4f2079 100644 --- a/src/view/into_boxed_view.rs +++ b/src/view/into_boxed_view.rs @@ -1,8 +1,8 @@ -use view::{View}; +use view::View; -/// Represents a type that can be made into a `Box`. +/// Represents a type that can be made into a `Box`. pub trait IntoBoxedView { - /// Returns a `Box`. + /// Returns a `Box`. fn as_boxed_view(self) -> Box; } diff --git a/src/view/mod.rs b/src/view/mod.rs index f8934e4..75d167e 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -39,10 +39,13 @@ mod view_wrapper; // Essentials components +mod any; +mod finder; mod position; mod size_cache; mod size_constraint; mod view_path; +mod view; // Helper bases mod scroll; @@ -51,205 +54,15 @@ mod boxable; mod into_boxed_view; -pub use self::into_boxed_view::IntoBoxedView; +pub use self::any::AnyView; pub use self::boxable::Boxable; +pub use self::finder::{Finder, Selector}; pub use self::identifiable::Identifiable; +pub use self::into_boxed_view::IntoBoxedView; pub use self::position::{Offset, Position}; pub use self::scroll::{ScrollBase, ScrollStrategy}; pub use self::size_cache::SizeCache; pub use self::size_constraint::SizeConstraint; pub use self::view_path::ViewPath; pub use self::view_wrapper::ViewWrapper; -use Printer; -use direction::Direction; -use event::{Event, EventResult}; -use std::any::Any; -use vec::Vec2; -use views::IdView; - -/// A view that can be downcasted to its concrete type. -/// -/// This trait is automatically implemented for any `T: View`. -pub trait AnyView { - /// Downcast self to a `Any`. - fn as_any(&self) -> &Any; - - /// Downcast self to a mutable `Any`. - fn as_any_mut(&mut self) -> &mut Any; - - /// Returns a boxed any from a boxed self. - /// - /// Can be used before `Box::downcast()`. - /// - /// # Examples - /// - /// ```rust - /// # use cursive::views::TextView; - /// # use cursive::view::AnyView; - /// # fn main() { - /// let boxed: Box = Box::new(TextView::new("text")); - /// let text: Box = boxed.as_boxed_any().downcast().unwrap(); - /// # } - /// ``` - fn as_boxed_any(self: Box) -> Box; -} - -impl AnyView for T { - /// Downcast self to a `Any`. - fn as_any(&self) -> &Any { - self - } - - /// Downcast self to a mutable `Any`. - fn as_any_mut(&mut self) -> &mut Any { - self - } - - fn as_boxed_any(self: Box) -> Box { - self - } -} - -/// Main trait defining a view behaviour. -/// -/// This is what you should implement to define a custom View. -pub trait View: Any + AnyView { - /// Called when a key was pressed. - /// - /// Default implementation just ignores it. - fn on_event(&mut self, Event) -> EventResult { - EventResult::Ignored - } - - /// Returns the minimum size the view requires with the given restrictions. - /// - /// If the view is flexible (it has multiple size options), it can try - /// to return one that fits the given `constraint`. - /// It's also fine to ignore it and return a fixed value. - /// - /// Default implementation always return `(1,1)`. - fn required_size(&mut self, constraint: Vec2) -> Vec2 { - let _ = constraint; - Vec2::new(1, 1) - } - - /// Returns `true` if the view content changed since last layout phase. - /// - /// This is mostly an optimisation for views where the layout phase is - /// expensive. - /// - /// * Views can ignore it and always return true (default implementation). - /// They will always be assumed to have changed. - /// * View Groups can ignore it and always re-layout their children. - /// * If they call `required_size` or `layout` with stable parameters, - /// the children may cache the result themselves and speed up the - /// process anyway. - fn needs_relayout(&self) -> bool { - true - } - - /// Called once the size for this view has been decided, - /// - /// View groups should propagate the information to their children. - fn layout(&mut self, Vec2) {} - - /// Draws the view with the given printer (includes bounds) and focus. - fn draw(&self, printer: &Printer); - - /// Runs a closure on the view identified by the given selector. - /// - /// See [`Finder::call_on`] for a nicer interface, implemented for all - /// views. - /// - /// [`Finder::call_on`]: trait.Finder.html#method.call_on - /// - /// If the selector doesn't find a match, the closure will not be run. - /// - /// Default implementation is a no-op. - fn call_on_any<'a>(&mut self, _: &Selector, _: Box) { - // TODO: FnMut -> FnOnce once it works - } - - /// Moves the focus to the view identified by the given selector. - /// - /// Returns `Ok(())` if the view was found and selected. - /// - /// Default implementation simply returns `Err(())`. - fn focus_view(&mut self, &Selector) -> Result<(), ()> { - Err(()) - } - - /// This view is offered focus. Will it take it? - /// - /// `source` indicates where the focus comes from. - /// When the source is unclear, `Front` is usually used. - /// - /// Default implementation always return `false`. - fn take_focus(&mut self, source: Direction) -> bool { - let _ = source; - false - } -} - -/// Provides `call_on` to views. -/// -/// This trait is mostly a wrapper around [`View::call_on_any`]. -/// -/// It provides a nicer interface to find a view when you know its type. -/// -/// [`View::call_on_any`]: ./trait.View.html#method.call_on_any -pub trait Finder { - /// Tries to find the view pointed to by the given selector. - /// - /// If the view is not found, or if it is not of the asked type, - /// it returns None. - fn call_on(&mut self, sel: &Selector, callback: F) -> Option - where - V: View + Any, - F: FnOnce(&mut V) -> R; - - /// Convenient method to use `call_on` with a `view::Selector::Id`. - fn find_id(&mut self, id: &str, callback: F) -> Option - where - V: View + Any, - F: FnOnce(&mut V) -> R, - { - self.call_on(&Selector::Id(id), callback) - } -} - -impl Finder for T { - fn call_on(&mut self, sel: &Selector, callback: F) -> Option - where - V: View + Any, - F: FnOnce(&mut V) -> R, - { - let mut result = None; - { - let result_ref = &mut result; - - let mut callback = Some(callback); - let callback = |v: &mut Any| { - if let Some(callback) = callback.take() { - if v.is::() { - *result_ref = - v.downcast_mut::().map(|v| callback(v)); - } else if v.is::>() { - *result_ref = v.downcast_mut::>() - .and_then(|v| v.with_view_mut(callback)); - } - } - }; - self.call_on_any(sel, Box::new(callback)); - } - result - } -} - -/// Selects a single view (if any) in the tree. -pub enum Selector<'a> { - /// Selects a view from its ID. - Id(&'a str), - /// Selects a view from its path. - Path(&'a ViewPath), -} +pub use self::view::View; diff --git a/src/view/view.rs b/src/view/view.rs new file mode 100644 index 0000000..c72e825 --- /dev/null +++ b/src/view/view.rs @@ -0,0 +1,89 @@ +use Printer; +use direction::Direction; +use event::{Event, EventResult}; +use std::any::Any; +use vec::Vec2; +use view::{AnyView, Selector}; + + +/// Main trait defining a view behaviour. +/// +/// This is what you should implement to define a custom View. +pub trait View: Any + AnyView { + /// Called when a key was pressed. + /// + /// Default implementation just ignores it. + fn on_event(&mut self, Event) -> EventResult { + EventResult::Ignored + } + + /// Returns the minimum size the view requires with the given restrictions. + /// + /// If the view is flexible (it has multiple size options), it can try + /// to return one that fits the given `constraint`. + /// It's also fine to ignore it and return a fixed value. + /// + /// Default implementation always return `(1,1)`. + fn required_size(&mut self, constraint: Vec2) -> Vec2 { + let _ = constraint; + Vec2::new(1, 1) + } + + /// Returns `true` if the view content changed since last layout phase. + /// + /// This is mostly an optimisation for views where the layout phase is + /// expensive. + /// + /// * Views can ignore it and always return true (default implementation). + /// They will always be assumed to have changed. + /// * View Groups can ignore it and always re-layout their children. + /// * If they call `required_size` or `layout` with stable parameters, + /// the children may cache the result themselves and speed up the + /// process anyway. + fn needs_relayout(&self) -> bool { + true + } + + /// Called once the size for this view has been decided, + /// + /// View groups should propagate the information to their children. + fn layout(&mut self, Vec2) {} + + /// Draws the view with the given printer (includes bounds) and focus. + fn draw(&self, printer: &Printer); + + /// Runs a closure on the view identified by the given selector. + /// + /// See [`Finder::call_on`] for a nicer interface, implemented for all + /// views. + /// + /// [`Finder::call_on`]: trait.Finder.html#method.call_on + /// + /// If the selector doesn't find a match, the closure will not be run. + /// + /// Default implementation is a no-op. + fn call_on_any<'a>(&mut self, _: &Selector, _: Box) { + // TODO: FnMut -> FnOnce once it works + } + + /// Moves the focus to the view identified by the given selector. + /// + /// Returns `Ok(())` if the view was found and selected. + /// + /// Default implementation simply returns `Err(())`. + fn focus_view(&mut self, &Selector) -> Result<(), ()> { + Err(()) + } + + /// This view is offered focus. Will it take it? + /// + /// `source` indicates where the focus comes from. + /// When the source is unclear, `Front` is usually used. + /// + /// Default implementation always return `false`. + fn take_focus(&mut self, source: Direction) -> bool { + let _ = source; + false + } +} + diff --git a/src/view/view_wrapper.rs b/src/view/view_wrapper.rs index a314180..2ce8925 100644 --- a/src/view/view_wrapper.rs +++ b/src/view/view_wrapper.rs @@ -176,7 +176,8 @@ macro_rules! wrap_impl { }; } -/// Convenient macro to implement the getters for inner [`View`] in [`ViewWrapper`]. +/// Convenient macro to implement the getters for inner [`View`] in +/// [`ViewWrapper`]. /// /// It defines the `get_inner` and `get_inner_mut` implementations. /// diff --git a/src/views/dialog.rs b/src/views/dialog.rs index 4d06877..529004e 100644 --- a/src/views/dialog.rs +++ b/src/views/dialog.rs @@ -10,8 +10,8 @@ use std::cmp::max; use theme::ColorStyle; use unicode_width::UnicodeWidthStr; use vec::{Vec2, Vec4}; -use view::{View, Selector}; -use views::{ViewBox, Button, DummyView, SizedView, TextView}; +use view::{Selector, View}; +use views::{Button, DummyView, SizedView, TextView, ViewBox}; /// Identifies currently focused element in [`Dialog`]. /// diff --git a/src/views/mod.rs b/src/views/mod.rs index 21c6967..b26217a 100644 --- a/src/views/mod.rs +++ b/src/views/mod.rs @@ -62,7 +62,6 @@ mod text_area; mod text_view; mod tracked_view; -pub use self::view_box::ViewBox; pub use self::box_view::BoxView; pub use self::button::Button; pub use self::canvas::Canvas; @@ -88,3 +87,4 @@ pub use self::stack_view::{LayerPosition, StackView}; pub use self::text_area::TextArea; pub use self::text_view::{TextContent, TextContentRef, TextView}; pub use self::tracked_view::TrackedView; +pub use self::view_box::ViewBox; diff --git a/src/views/stack_view.rs b/src/views/stack_view.rs index 7c0cf19..cc949c9 100644 --- a/src/views/stack_view.rs +++ b/src/views/stack_view.rs @@ -7,9 +7,8 @@ use std::cell; use std::ops::Deref; use theme::ColorStyle; use vec::Vec2; -use view::{View, Offset, Position, Selector, - ViewWrapper, IntoBoxedView}; -use views::{ViewBox, Layer, ShadowView}; +use view::{IntoBoxedView, Offset, Position, Selector, View, ViewWrapper}; +use views::{Layer, ShadowView, ViewBox}; /// Simple stack of views. /// Only the top-most view is active and can receive input. diff --git a/src/views/view_box.rs b/src/views/view_box.rs index 72d7536..c116de3 100644 --- a/src/views/view_box.rs +++ b/src/views/view_box.rs @@ -1,5 +1,5 @@ use std::ops::{Deref, DerefMut}; -use view::{View, ViewWrapper, IntoBoxedView}; +use view::{IntoBoxedView, View, ViewWrapper}; /// A boxed `View`. /// @@ -15,8 +15,10 @@ impl ViewBox { } /// Box the given view - pub fn boxed(view: T) -> Self - where T: IntoBoxedView { + pub fn boxed(view: T) -> Self + where + T: IntoBoxedView, + { ViewBox::new(view.as_boxed_view()) }