Add BoxableView and AnyBox

And remove `impl ViewWrapper for T: Deref<View>`
This means `Box<View>` doesn't implement `View` anymore.
This commit is contained in:
Alexandre Bury 2018-03-14 12:32:07 -07:00
parent dea226a095
commit e4bf9accc3
6 changed files with 96 additions and 35 deletions

View File

@ -65,6 +65,8 @@ use vec::Vec2;
use views::IdView; use views::IdView;
/// A view that can be downcasted to its concrete type. /// 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: View {
/// Downcast self to a `Any`. /// Downcast self to a `Any`.
fn as_any(&self) -> &Any; fn as_any(&self) -> &Any;
@ -105,7 +107,30 @@ impl<T: View> AnyView for T {
} }
} }
/// Represents a type that can be made into a `Box<AnyView>`.
pub trait BoxableView {
/// Returns a `Box<AnyView>`.
fn as_boxed_view(self) -> Box<AnyView>;
}
impl<T> BoxableView for T
where
T: View,
{
fn as_boxed_view(self) -> Box<AnyView> {
Box::new(self)
}
}
impl BoxableView for Box<AnyView> {
fn as_boxed_view(self) -> Box<AnyView> {
self
}
}
/// Main trait defining a view behaviour. /// Main trait defining a view behaviour.
///
/// This is what you should implement to define a custom View.
pub trait View: Any { pub trait View: Any {
/// Called when a key was pressed. /// Called when a key was pressed.
/// ///

View File

@ -95,32 +95,6 @@ pub trait ViewWrapper: 'static {
} }
} }
// Some types easily implement ViewWrapper.
// This includes Box<T: View>
use std::ops::{Deref, DerefMut};
impl<U, T> ViewWrapper for T
where
U: View + ?Sized,
T: Deref<Target = U> + DerefMut + 'static,
{
type V = U;
fn with_view<F, R>(&self, f: F) -> Option<R>
where
F: FnOnce(&Self::V) -> R,
{
Some(f(self.deref()))
}
fn with_view_mut<F, R>(&mut self, f: F) -> Option<R>
where
F: FnOnce(&mut Self::V) -> R,
{
Some(f(self.deref_mut()))
}
}
// The main point of implementing ViewWrapper is to have View for free. // The main point of implementing ViewWrapper is to have View for free.
impl<T: ViewWrapper> View for T { impl<T: ViewWrapper> View for T {
fn draw(&self, printer: &Printer) { fn draw(&self, printer: &Printer) {

56
src/views/any_box.rs Normal file
View File

@ -0,0 +1,56 @@
use std::ops::{Deref, DerefMut};
use view::{AnyView, View, ViewWrapper};
/// A boxed `AnyView`.
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: View>(view: T) -> Self {
AnyBox::new(Box::new(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

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

View File

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

View File

@ -8,7 +8,7 @@ use std::ops::Deref;
use theme::ColorStyle; use theme::ColorStyle;
use vec::Vec2; use vec::Vec2;
use view::{AnyView, Offset, Position, Selector, View, ViewWrapper}; use view::{AnyView, Offset, Position, Selector, View, ViewWrapper};
use views::{Layer, ShadowView}; use views::{AnyBox, Layer, ShadowView};
/// Simple stack of views. /// Simple stack of views.
/// Only the top-most view is active and can receive input. /// Only the top-most view is active and can receive input.
@ -152,7 +152,7 @@ impl<T: View> View for ChildWrapper<T> {
} }
struct Child { struct Child {
view: ChildWrapper<Box<AnyView>>, view: ChildWrapper<AnyBox>,
size: Vec2, size: Vec2,
placement: Placement, placement: Placement,
@ -182,7 +182,7 @@ impl StackView {
where where
T: 'static + View, T: 'static + View,
{ {
let boxed: Box<AnyView> = Box::new(view); let boxed = AnyBox::boxed(view);
self.layers.push(Child { self.layers.push(Child {
view: ChildWrapper::Plain(Layer::new(boxed)), view: ChildWrapper::Plain(Layer::new(boxed)),
size: Vec2::zero(), size: Vec2::zero(),
@ -275,7 +275,7 @@ impl StackView {
where where
T: 'static + View, T: 'static + View,
{ {
let boxed: Box<AnyView> = Box::new(view); let boxed = AnyBox::boxed(view);
self.layers.push(Child { self.layers.push(Child {
// Skip padding for absolute/parent-placed views // Skip padding for absolute/parent-placed views
view: ChildWrapper::Shadow( view: ChildWrapper::Shadow(
@ -302,7 +302,11 @@ impl StackView {
/// Remove the top-most layer. /// Remove the top-most layer.
pub fn pop_layer(&mut self) -> Option<Box<AnyView>> { pub fn pop_layer(&mut self) -> Option<Box<AnyView>> {
self.bg_dirty.set(true); self.bg_dirty.set(true);
self.layers.pop().map(|child| child.view.unwrap()) self.layers
.pop()
.map(|child| child.view)
.map(ChildWrapper::unwrap)
.map(AnyBox::unwrap)
} }
/// Computes the offset of the current top view. /// Computes the offset of the current top view.