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;
/// A view that can be downcasted to its concrete type.
///
/// This trait is automatically implemented for any `T: View`.
pub trait AnyView: View {
/// Downcast self to a `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.
///
/// This is what you should implement to define a custom View.
pub trait View: Any {
/// 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.
impl<T: ViewWrapper> View for T {
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 vec::{Vec2, Vec4};
use view::{AnyView, Selector, View};
use views::{Button, DummyView, SizedView, TextView};
use views::{AnyBox, 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<Box<AnyView>>,
content: SizedView<AnyBox>,
// 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(Box::new(view)),
content: SizedView::new(AnyBox::boxed(view)),
buttons: Vec::new(),
title: String::new(),
title_position: HAlign::Center,
@ -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(Box::new(view));
self.content = SizedView::new(AnyBox::boxed(view));
}
/// 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 button;
mod canvas;
@ -61,6 +62,7 @@ mod text_area;
mod text_view;
mod tracked_view;
pub use self::any_box::AnyBox;
pub use self::box_view::BoxView;
pub use self::button::Button;
pub use self::canvas::Canvas;

View File

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