Reverse View and AnyView

We now have `View: AnyView`
This commit is contained in:
Alexandre Bury 2018-03-14 14:59:41 -07:00
parent f27f7792df
commit 7db1ee7335
10 changed files with 114 additions and 108 deletions

View File

@ -9,7 +9,7 @@ use std::path::Path;
use std::sync::mpsc;
use theme;
use vec::Vec2;
use view::{self, AnyView, IntoBoxedView, Finder, Position, View};
use view::{self, Finder, IntoBoxedView, Position, View};
use views::{self, LayerPosition};
/// Identifies a screen in the cursive root.
@ -472,7 +472,7 @@ impl Cursive {
}
/// Convenient method to remove a layer from the current screen.
pub fn pop_layer(&mut self) -> Option<Box<AnyView>> {
pub fn pop_layer(&mut self) -> Option<Box<View>> {
self.screen_mut().pop_layer()
}

View File

@ -1,28 +1,22 @@
use view::{AnyView, View};
use view::{View};
/// Represents a type that can be made into a `Box<AnyView>`.
pub trait IntoBoxedView {
/// Returns a `Box<AnyView>`.
fn as_boxed_view(self) -> Box<AnyView>;
fn as_boxed_view(self) -> Box<View>;
}
impl<T> IntoBoxedView for T
where
T: View,
{
fn as_boxed_view(self) -> Box<AnyView> {
fn as_boxed_view(self) -> Box<View> {
Box::new(self)
}
}
impl IntoBoxedView for Box<AnyView> {
fn as_boxed_view(self) -> Box<AnyView> {
impl IntoBoxedView for Box<View> {
fn as_boxed_view(self) -> Box<View> {
self
}
}
impl IntoBoxedView for Box<View> {
fn as_boxed_view(self) -> Box<AnyView> {
self.to_any()
}
}

View File

@ -51,8 +51,6 @@ mod boxable;
mod into_boxed_view;
pub mod internal;
pub use self::into_boxed_view::IntoBoxedView;
pub use self::boxable::Boxable;
pub use self::identifiable::Identifiable;
@ -72,7 +70,7 @@ 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: View {
pub trait AnyView {
/// Downcast self to a `Any`.
fn as_any(&self) -> &Any;
@ -115,10 +113,7 @@ impl<T: View> AnyView for T {
/// Main trait defining a view behaviour.
///
/// This is what you should implement to define a custom View.
///
/// You can ignore the `Any` and `ToAny` trait bound;
/// they are implementation details, and will be implemented automatically.
pub trait View: Any + internal::ToAny {
pub trait View: Any + AnyView {
/// Called when a key was pressed.
///
/// Default implementation just ignores it.

View File

@ -1,58 +0,0 @@
use std::ops::{Deref, DerefMut};
use view::{AnyView, IntoBoxedView, ViewWrapper};
/// A boxed `AnyView`.
///
/// It derefs to the wrapped view.
pub struct AnyBox {
view: Box<AnyView>,
}
impl AnyBox {
/// Creates a new `AnyBox` around the given boxed view.
pub fn new(view: Box<AnyView>) -> Self {
AnyBox { view }
}
/// Box the given view
pub fn boxed<T: IntoBoxedView>(view: T) -> Self {
AnyBox::new(view.as_boxed_view())
}
/// Returns the inner boxed view.
pub fn unwrap(self) -> Box<AnyView> {
self.view
}
}
impl Deref for AnyBox {
type Target = AnyView;
fn deref(&self) -> &AnyView {
&*self.view
}
}
impl DerefMut for AnyBox {
fn deref_mut(&mut self) -> &mut AnyView {
&mut *self.view
}
}
impl ViewWrapper for AnyBox {
type V = AnyView;
fn with_view<F, R>(&self, f: F) -> Option<R>
where
F: FnOnce(&Self::V) -> R,
{
Some(f(&*self.view))
}
fn with_view_mut<F, R>(&mut self, f: F) -> Option<R>
where
F: FnOnce(&mut Self::V) -> R,
{
Some(f(&mut *self.view))
}
}

View File

@ -10,8 +10,8 @@ use std::cmp::max;
use theme::ColorStyle;
use unicode_width::UnicodeWidthStr;
use vec::{Vec2, Vec4};
use view::{AnyView, Selector, View};
use views::{AnyBox, Button, DummyView, SizedView, TextView};
use view::{View, Selector};
use views::{ViewBox, Button, DummyView, SizedView, TextView};
/// Identifies currently focused element in [`Dialog`].
///
@ -58,7 +58,7 @@ pub struct Dialog {
title_position: HAlign,
// The actual inner view.
content: SizedView<AnyBox>,
content: SizedView<ViewBox>,
// Optional list of buttons under the main view.
// Include the top-left corner.
@ -90,7 +90,7 @@ impl Dialog {
/// Creates a new `Dialog` with the given content.
pub fn around<V: View + 'static>(view: V) -> Self {
Dialog {
content: SizedView::new(AnyBox::boxed(view)),
content: SizedView::new(ViewBox::boxed(view)),
buttons: Vec::new(),
title: String::new(),
title_position: HAlign::Center,
@ -120,12 +120,12 @@ impl Dialog {
/// .unwrap();
/// assert_eq!(text_view.get_content().source(), "Hello!");
/// ```
pub fn get_content(&self) -> &AnyView {
pub fn get_content(&self) -> &View {
&*self.content.view
}
/// Gets mutable access to the content.
pub fn get_content_mut(&mut self) -> &mut AnyView {
pub fn get_content_mut(&mut self) -> &mut View {
&mut *self.content.view
}
@ -133,7 +133,7 @@ impl Dialog {
///
/// Previous content will be dropped.
pub fn set_content<V: View + 'static>(&mut self, view: V) {
self.content = SizedView::new(AnyBox::boxed(view));
self.content = SizedView::new(ViewBox::boxed(view));
}
/// Convenient method to create a dialog with a simple text content.

View File

@ -7,7 +7,7 @@ use std::any::Any;
use std::cmp::min;
use std::ops::Deref;
use vec::Vec2;
use view::{AnyView, Selector, SizeCache, View};
use view::{Selector, SizeCache, View};
/// Arranges its children linearly according to its orientation.
pub struct LinearLayout {
@ -19,7 +19,7 @@ pub struct LinearLayout {
}
struct Child {
view: Box<AnyView>,
view: Box<View>,
// The last result from the child's required_size
// Doesn't have to be what the child actually gets.
size: Vec2,
@ -33,7 +33,7 @@ impl Child {
self.size
}
fn as_view(&self) -> &AnyView {
fn as_view(&self) -> &View {
&*self.view
}
}
@ -166,12 +166,12 @@ impl LinearLayout {
}
/// Returns a reference to a child.
pub fn get_child(&self, i: usize) -> Option<&AnyView> {
pub fn get_child(&self, i: usize) -> Option<&View> {
self.children.get(i).map(|child| &*child.view)
}
/// Returns a mutable reference to a child.
pub fn get_child_mut(&mut self, i: usize) -> Option<&mut AnyView> {
pub fn get_child_mut(&mut self, i: usize) -> Option<&mut View> {
self.children.get_mut(i).map(|child| &mut *child.view)
}

View File

@ -7,14 +7,14 @@ use std::any::Any;
use std::rc::Rc;
use unicode_width::UnicodeWidthStr;
use vec::Vec2;
use view::{AnyView, ScrollBase, Selector, View};
use view::{ScrollBase, Selector, View};
/// Represents a child from a [`ListView`].
///
/// [`ListView`]: struct.ListView.html
pub enum ListChild {
/// A single row, with a label and a view.
Row(String, Box<AnyView>),
Row(String, Box<View>),
/// A delimiter between groups.
Delimiter,
}
@ -27,7 +27,7 @@ impl ListChild {
}
}
fn view(&mut self) -> Option<&mut AnyView> {
fn view(&mut self) -> Option<&mut View> {
match *self {
ListChild::Row(_, ref mut view) => Some(view.as_mut()),
_ => None,

View File

@ -35,7 +35,7 @@ macro_rules! impl_enabled {
}
}
mod any_box;
mod view_box;
mod box_view;
mod button;
mod canvas;
@ -62,7 +62,7 @@ mod text_area;
mod text_view;
mod tracked_view;
pub use self::any_box::AnyBox;
pub use self::view_box::ViewBox;
pub use self::box_view::BoxView;
pub use self::button::Button;
pub use self::canvas::Canvas;

View File

@ -7,9 +7,9 @@ use std::cell;
use std::ops::Deref;
use theme::ColorStyle;
use vec::Vec2;
use view::{AnyView, IntoBoxedView, Offset, Position, Selector, View,
ViewWrapper};
use views::{AnyBox, Layer, ShadowView};
use view::{View, Offset, Position, Selector,
ViewWrapper, IntoBoxedView};
use views::{ViewBox, Layer, ShadowView};
/// Simple stack of views.
/// Only the top-most view is active and can receive input.
@ -74,9 +74,9 @@ impl<T: View> ChildWrapper<T> {
}
}
impl<T: AnyView> ChildWrapper<T> {
impl<T: View> ChildWrapper<T> {
/// Returns a reference to the inner view
pub fn get_inner(&self) -> &AnyView {
pub fn get_inner(&self) -> &View {
match *self {
ChildWrapper::Shadow(ref shadow) => shadow.get_inner().get_inner(),
ChildWrapper::Plain(ref layer) => layer.get_inner(),
@ -84,7 +84,7 @@ impl<T: AnyView> ChildWrapper<T> {
}
/// Returns a mutable reference to the inner view
pub fn get_inner_mut(&mut self) -> &mut AnyView {
pub fn get_inner_mut(&mut self) -> &mut View {
match *self {
ChildWrapper::Shadow(ref mut shadow) => {
shadow.get_inner_mut().get_inner_mut()
@ -153,7 +153,7 @@ impl<T: View> View for ChildWrapper<T> {
}
struct Child {
view: ChildWrapper<AnyBox>,
view: ChildWrapper<ViewBox>,
size: Vec2,
placement: Placement,
@ -183,7 +183,7 @@ impl StackView {
where
T: IntoBoxedView,
{
let boxed = AnyBox::boxed(view);
let boxed = ViewBox::boxed(view);
self.layers.push(Child {
view: ChildWrapper::Plain(Layer::new(boxed)),
size: Vec2::zero(),
@ -211,13 +211,13 @@ impl StackView {
}
/// Returns a reference to the layer at the given position.
pub fn get(&self, pos: LayerPosition) -> Option<&AnyView> {
pub fn get(&self, pos: LayerPosition) -> Option<&View> {
let i = self.get_index(pos);
self.layers.get(i).map(|child| child.view.get_inner())
}
/// Returns a mutable reference to the layer at the given position.
pub fn get_mut(&mut self, pos: LayerPosition) -> Option<&mut AnyView> {
pub fn get_mut(&mut self, pos: LayerPosition) -> Option<&mut View> {
let i = self.get_index(pos);
self.layers
.get_mut(i)
@ -276,7 +276,7 @@ impl StackView {
where
T: IntoBoxedView,
{
let boxed = AnyBox::boxed(view);
let boxed = ViewBox::boxed(view);
self.layers.push(Child {
// Skip padding for absolute/parent-placed views
view: ChildWrapper::Shadow(
@ -301,13 +301,13 @@ impl StackView {
}
/// Remove the top-most layer.
pub fn pop_layer(&mut self) -> Option<Box<AnyView>> {
pub fn pop_layer(&mut self) -> Option<Box<View>> {
self.bg_dirty.set(true);
self.layers
.pop()
.map(|child| child.view)
.map(ChildWrapper::unwrap)
.map(AnyBox::unwrap)
.map(ViewBox::unwrap)
}
/// Computes the offset of the current top view.
@ -554,6 +554,22 @@ mod tests {
use super::*;
use views::TextView;
#[test]
fn pop_add() {
let mut stack = StackView::new();
stack.add_layer(TextView::new("1"));
for _ in 0..20 {
let layer = stack.pop_layer().unwrap();
stack.add_layer(layer);
}
let layer = stack.pop_layer().unwrap();
let text: Box<TextView> = layer.as_boxed_any().downcast().unwrap();
assert_eq!(text.get_content().source(), "1");
}
#[test]
fn move_layer_works() {
let mut stack = StackView::new()

59
src/views/view_box.rs Normal file
View File

@ -0,0 +1,59 @@
use std::ops::{Deref, DerefMut};
use view::{View, ViewWrapper, IntoBoxedView};
/// A boxed `View`.
///
/// It derefs to the wrapped view.
pub struct ViewBox {
view: Box<View>,
}
impl ViewBox {
/// Creates a new `ViewBox` around the given boxed view.
pub fn new(view: Box<View>) -> Self {
ViewBox { view }
}
/// Box the given view
pub fn boxed<T>(view: T) -> Self
where T: IntoBoxedView {
ViewBox::new(view.as_boxed_view())
}
/// Returns the inner boxed view.
pub fn unwrap(self) -> Box<View> {
self.view
}
}
impl Deref for ViewBox {
type Target = View;
fn deref(&self) -> &View {
&*self.view
}
}
impl DerefMut for ViewBox {
fn deref_mut(&mut self) -> &mut View {
&mut *self.view
}
}
impl ViewWrapper for ViewBox {
type V = View;
fn with_view<F, R>(&self, f: F) -> Option<R>
where
F: FnOnce(&Self::V) -> R,
{
Some(f(&*self.view))
}
fn with_view_mut<F, R>(&mut self, f: F) -> Option<R>
where
F: FnOnce(&mut Self::V) -> R,
{
Some(f(&mut *self.view))
}
}