Add Cursive::dump

This commit is contained in:
Alexandre Bury 2020-07-02 23:58:54 -07:00
parent 9bc1cd04c3
commit 28cd51c265
15 changed files with 126 additions and 43 deletions

View File

@ -6,14 +6,15 @@ use std::time::Duration;
use crossbeam_channel::{self, Receiver, Sender}; use crossbeam_channel::{self, Receiver, Sender};
use crate::backend; use crate::{
use crate::direction; backend, direction,
use crate::event::{Event, EventResult}; event::{Event, EventResult},
use crate::printer::Printer; printer::Printer,
use crate::theme; theme,
use crate::view::{self, Finder, IntoBoxedView, Position, View}; view::{self, Finder, IntoBoxedView, Position, View},
use crate::views::{self, LayerPosition}; views::{self, LayerPosition},
use crate::Vec2; Dump, Vec2,
};
static DEBUG_VIEW_NAME: &str = "_cursive_debug_view"; static DEBUG_VIEW_NAME: &str = "_cursive_debug_view";
@ -1035,6 +1036,49 @@ impl Cursive {
pub fn backend_name(&self) -> &str { pub fn backend_name(&self) -> &str {
self.backend.name() self.backend.name()
} }
/// Dump the current state of the Cursive root.
///
/// This will stop the backend and clean up the terminal.
///
/// It will save everything, including:
/// * The view tree
/// * Callbacks
/// * Menubar
/// * User data
/// * Callback sink
pub fn dump(mut self) -> crate::Dump {
Dump {
cb_sink: self.cb_sink.clone(),
cb_source: self.cb_source.clone(),
fps: self.fps,
menubar: std::mem::take(&mut self.menubar),
root_view: std::mem::take(&mut self.root),
theme: self.theme.clone(),
user_data: std::mem::replace(&mut self.user_data, Box::new(())),
}
}
/// Restores the state from a previous dump.
///
/// This will discard everything from this `Cursive` instance.
/// In particular:
/// * All current views will be dropped, replaced by the dump.
/// * All callbacks will be replaced.
/// * Menubar will be replaced.
/// * User Data will be replaced.
/// * The callback channel will be replaced - any previous call to
/// `cb_sink` on this instance will be disconnected.
pub fn restore(&mut self, dump: Dump) {
self.cb_sink = dump.cb_sink;
self.cb_source = dump.cb_source;
self.fps = dump.fps;
self.menubar = dump.menubar;
self.root = dump.root_view;
self.theme = dump.theme;
self.user_data = dump.user_data;
self.clear();
}
} }
impl Drop for Cursive { impl Drop for Cursive {

22
cursive-core/src/dump.rs Normal file
View File

@ -0,0 +1,22 @@
use crate::{theme::Theme, views, Cursive};
use crossbeam_channel::{Receiver, Sender};
use std::any::Any;
use std::num::NonZeroU32;
/// Represents a dump of everything from a `Cursive` instance.
///
/// See [`Cursive::dump()`](../cursive.html#method.dump)
pub struct Dump {
pub(crate) cb_sink: Sender<Box<dyn FnOnce(&mut Cursive) + Send>>,
pub(crate) cb_source: Receiver<Box<dyn FnOnce(&mut Cursive) + Send>>,
pub(crate) fps: Option<NonZeroU32>,
pub(crate) menubar: views::Menubar,
pub(crate) root_view:
views::OnEventView<views::ScreensView<views::StackView>>,
pub(crate) theme: Theme,
pub(crate) user_data: Box<dyn Any>,
}

View File

@ -17,13 +17,21 @@ macro_rules! new_default(
} }
} }
}; };
($c:ty) => { ($c:ident) => {
impl Default for $c { impl Default for $c {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
};
($c:ident<$t:ident: Default>) => {
impl <$t> Default for $c<$t>
where $t: Default {
fn default() -> Self {
Self::new($t::default())
} }
}
};
); );
#[macro_use] #[macro_use]
@ -44,6 +52,7 @@ pub mod traits;
pub mod vec; pub mod vec;
mod cursive; mod cursive;
mod dump;
mod printer; mod printer;
mod rect; mod rect;
mod with; mod with;
@ -52,6 +61,7 @@ mod xy;
mod div; mod div;
pub use self::cursive::{CbSink, Cursive, ScreenId}; pub use self::cursive::{CbSink, Cursive, ScreenId};
pub use self::dump::Dump;
pub use self::printer::Printer; pub use self::printer::Printer;
pub use self::rect::Rect; pub use self::rect::Rect;
pub use self::vec::Vec2; pub use self::vec::Vec2;

View File

@ -31,6 +31,8 @@ pub struct EnableableView<V> {
enabled: bool, enabled: bool,
} }
new_default!(EnableableView<V: Default>);
impl<V> EnableableView<V> { impl<V> EnableableView<V> {
/// Creates a new `EnableableView` around `view`. /// Creates a new `EnableableView` around `view`.
/// ///

View File

@ -17,6 +17,8 @@ pub struct HideableView<V> {
invalidated: bool, invalidated: bool,
} }
new_default!(HideableView<V: Default>);
impl<V> HideableView<V> { impl<V> HideableView<V> {
/// Creates a new HideableView around `view`. /// Creates a new HideableView around `view`.
/// ///

View File

@ -10,6 +10,8 @@ pub struct LastSizeView<T> {
pub size: Vec2, pub size: Vec2,
} }
new_default!(LastSizeView<V: Default>);
impl<T> LastSizeView<T> { impl<T> LastSizeView<T> {
/// Wraps the given view. /// Wraps the given view.
pub fn new(view: T) -> Self { pub fn new(view: T) -> Self {

View File

@ -8,12 +8,14 @@ use crate::Printer;
/// ///
/// [`StackView`]: crate::views::StackView /// [`StackView`]: crate::views::StackView
#[derive(Debug)] #[derive(Debug)]
pub struct Layer<T: View> { pub struct Layer<T> {
view: T, view: T,
color: ColorStyle, color: ColorStyle,
} }
impl<T: View> Layer<T> { new_default!(Layer<T: Default>);
impl<T> Layer<T> {
/// Wraps the given view. /// Wraps the given view.
pub fn new(view: T) -> Self { pub fn new(view: T) -> Self {
Self::with_color(view, ColorStyle::primary()) Self::with_color(view, ColorStyle::primary())

View File

@ -10,7 +10,7 @@ use std::rc::Rc;
/// This lets other views refer to this one using a string identifier. /// This lets other views refer to this one using a string identifier.
/// ///
/// See [`Identifiable`](crate::view::Identifiable) for an easy way to wrap any view with it. /// See [`Identifiable`](crate::view::Identifiable) for an easy way to wrap any view with it.
pub struct NamedView<V: View> { pub struct NamedView<V> {
view: Rc<RefCell<V>>, view: Rc<RefCell<V>>,
id: String, id: String,
} }
@ -22,7 +22,7 @@ pub struct NamedView<V: View> {
/// [`RefMut`]: std::cell::RefMut /// [`RefMut`]: std::cell::RefMut
pub type ViewRef<V> = OwningHandle<RcRef<RefCell<V>>, RefMut<'static, V>>; pub type ViewRef<V> = OwningHandle<RcRef<RefCell<V>>, RefMut<'static, V>>;
impl<V: View> NamedView<V> { impl<V> NamedView<V> {
/// Wraps `view` in a new `NamedView`. /// Wraps `view` in a new `NamedView`.
pub fn new<S: Into<String>>(id: S, view: V) -> Self { pub fn new<S: Into<String>>(id: S, view: V) -> Self {
NamedView { NamedView {

View File

@ -38,11 +38,13 @@ use std::rc::Rc;
/// .on_event('q', |s| s.quit()) /// .on_event('q', |s| s.quit())
/// .on_event(event::Key::Esc, |s| s.quit()); /// .on_event(event::Key::Esc, |s| s.quit());
/// ``` /// ```
pub struct OnEventView<T: View> { pub struct OnEventView<T> {
view: T, view: T,
callbacks: Vec<(EventTrigger, Action<T>)>, callbacks: Vec<(EventTrigger, Action<T>)>,
} }
new_default!(OnEventView<T: Default>);
type InnerCallback<T> = Rc<Box<dyn Fn(&mut T, &Event) -> Option<EventResult>>>; type InnerCallback<T> = Rc<Box<dyn Fn(&mut T, &Event) -> Option<EventResult>>>;
struct Action<T> { struct Action<T> {
@ -65,7 +67,7 @@ enum TriggerPhase {
AfterChild, AfterChild,
} }
impl<T: View> OnEventView<T> { impl<T> OnEventView<T> {
/// Wraps the given view in a new OnEventView. /// Wraps the given view in a new OnEventView.
pub fn new(view: T) -> Self { pub fn new(view: T) -> Self {
OnEventView { OnEventView {
@ -83,7 +85,6 @@ impl<T: View> OnEventView<T> {
self.callbacks self.callbacks
.retain(move |&(ref trigger, _)| !trigger.has_tag(&event)); .retain(move |&(ref trigger, _)| !trigger.has_tag(&event));
} }
/// Registers a callback when the given event is ignored by the child. /// Registers a callback when the given event is ignored by the child.
/// ///
/// Chainable variant. /// Chainable variant.

View File

@ -25,7 +25,7 @@ pub struct PaddedView<V> {
margins: Margins, margins: Margins,
} }
impl<V: View> PaddedView<V> { impl<V> PaddedView<V> {
/// Wraps `view` in a new `PaddedView` with the given margins. /// Wraps `view` in a new `PaddedView` with the given margins.
pub fn new(margins: Margins, view: V) -> Self { pub fn new(margins: Margins, view: V) -> Self {
PaddedView { view, margins } PaddedView { view, margins }

View File

@ -10,7 +10,7 @@ use unicode_width::UnicodeWidthStr;
/// Draws a border around a wrapped view. /// Draws a border around a wrapped view.
#[derive(Debug)] #[derive(Debug)]
pub struct Panel<V: View> { pub struct Panel<V> {
// Inner view // Inner view
view: V, view: V,
@ -24,7 +24,9 @@ pub struct Panel<V: View> {
invalidated: bool, invalidated: bool,
} }
impl<V: View> Panel<V> { new_default!(Panel<V: Default>);
impl<V> Panel<V> {
/// Creates a new panel around the given view. /// Creates a new panel around the given view.
pub fn new(view: V) -> Self { pub fn new(view: V) -> Self {
Panel { Panel {

View File

@ -23,7 +23,7 @@ use crate::XY;
/// ``` /// ```
/// ///
/// See also [`Boxable`](crate::view::Boxable) for an easy way to wrap any view. /// See also [`Boxable`](crate::view::Boxable) for an easy way to wrap any view.
pub struct ResizedView<T: View> { pub struct ResizedView<T> {
/// Constraint on each axis /// Constraint on each axis
size: XY<SizeConstraint>, size: XY<SizeConstraint>,
@ -34,7 +34,7 @@ pub struct ResizedView<T: View> {
view: T, view: T,
} }
impl<T: View> ResizedView<T> { impl<T> ResizedView<T> {
/// Creates a new `ResizedView` with the given width and height requirements. /// Creates a new `ResizedView` with the given width and height requirements.
/// ///
/// `None` values will use the wrapped view's preferences. /// `None` values will use the wrapped view's preferences.

View File

@ -17,12 +17,11 @@ pub struct ScrollView<V> {
on_scroll: Rc<dyn Fn(&mut Self, Rect) -> EventResult>, on_scroll: Rc<dyn Fn(&mut Self, Rect) -> EventResult>,
} }
new_default!(ScrollView<V: Default>);
impl_scroller!(ScrollView<V>::core); impl_scroller!(ScrollView<V>::core);
impl<V> ScrollView<V> impl<V> ScrollView<V> {
where
V: View,
{
/// Creates a new ScrollView around `view`. /// Creates a new ScrollView around `view`.
pub fn new(inner: V) -> Self { pub fn new(inner: V) -> Self {
ScrollView { ScrollView {
@ -189,7 +188,10 @@ where
} }
/// Programmatically scroll until the child's important area is in view. /// Programmatically scroll until the child's important area is in view.
pub fn scroll_to_important_area(&mut self) -> EventResult { pub fn scroll_to_important_area(&mut self) -> EventResult
where
V: View,
{
let important_area = self.inner.important_area(self.core.last_size()); let important_area = self.inner.important_area(self.core.last_size());
self.core.scroll_to_rect(important_area); self.core.scroll_to_rect(important_area);
@ -256,6 +258,7 @@ where
pub fn set_on_scroll_change_inner<F>(&mut self, on_scroll: F) pub fn set_on_scroll_change_inner<F>(&mut self, on_scroll: F)
where where
F: FnMut(&mut Self, Rect) -> EventResult + 'static, F: FnMut(&mut Self, Rect) -> EventResult + 'static,
V: 'static,
{ {
self.set_on_scroll_inner(Self::skip_unchanged(on_scroll, || { self.set_on_scroll_inner(Self::skip_unchanged(on_scroll, || {
EventResult::Ignored EventResult::Ignored
@ -266,6 +269,7 @@ where
pub fn set_on_scroll_change<F>(&mut self, on_scroll: F) pub fn set_on_scroll_change<F>(&mut self, on_scroll: F)
where where
F: FnMut(&mut Cursive, Rect) + 'static, F: FnMut(&mut Cursive, Rect) + 'static,
V: 'static,
{ {
self.set_on_scroll(Self::skip_unchanged(on_scroll, || ())); self.set_on_scroll(Self::skip_unchanged(on_scroll, || ()));
} }

View File

@ -8,14 +8,16 @@ use crate::Vec2;
/// Wrapper view that adds a shadow. /// Wrapper view that adds a shadow.
/// ///
/// It reserves a 1 pixel border on each side. /// It reserves a 1 pixel border on each side.
pub struct ShadowView<T: View> { pub struct ShadowView<T> {
view: T, view: T,
top_padding: bool, top_padding: bool,
left_padding: bool, left_padding: bool,
// TODO: invalidate if we change the padding? wrap_needs_relayout? // TODO: invalidate if we change the padding? wrap_needs_relayout?
} }
impl<T: View> ShadowView<T> { new_default!(ShadowView<T: Default>);
impl<T> ShadowView<T> {
/// Wraps the given view. /// Wraps the given view.
pub fn new(view: T) -> Self { pub fn new(view: T) -> Self {
ShadowView { ShadowView {

View File

@ -1,18 +1,19 @@
use crate::view::{View, ViewWrapper}; use crate::view::{View, ViewWrapper};
use crate::views::NamedView;
use crate::Printer; use crate::Printer;
use crate::Vec2; use crate::Vec2;
use std::cell::Cell; use std::cell::Cell;
/// Wrapper around a view that remembers its position. /// Wrapper around a view that remembers its position.
pub struct TrackedView<T: View> { pub struct TrackedView<T> {
/// Wrapped view. /// Wrapped view.
pub view: T, pub view: T,
/// Last position the view was located. /// Last position the view was located.
offset: Cell<Vec2>, offset: Cell<Vec2>,
} }
impl<T: View> TrackedView<T> { new_default!(TrackedView<T: Default>);
impl<T> TrackedView<T> {
/// Return the last offset at which the view was drawn. /// Return the last offset at which the view was drawn.
pub fn offset(&self) -> Vec2 { pub fn offset(&self) -> Vec2 {
self.offset.get() self.offset.get()
@ -26,17 +27,6 @@ impl<T: View> TrackedView<T> {
} }
} }
/// Same as [`with_name`](TrackedView::with_name)
#[deprecated(note = "`with_id` is being renamed to `with_name`")]
pub fn with_id(self, id: &str) -> NamedView<Self> {
self.with_name(id)
}
/// Wraps itself in a `NamedView` for easy retrieval.
pub fn with_name(self, name: &str) -> NamedView<Self> {
NamedView::new(name, self)
}
inner_getters!(self.view: T); inner_getters!(self.view: T);
} }