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 crate::backend;
use crate::direction;
use crate::event::{Event, EventResult};
use crate::printer::Printer;
use crate::theme;
use crate::view::{self, Finder, IntoBoxedView, Position, View};
use crate::views::{self, LayerPosition};
use crate::Vec2;
use crate::{
backend, direction,
event::{Event, EventResult},
printer::Printer,
theme,
view::{self, Finder, IntoBoxedView, Position, View},
views::{self, LayerPosition},
Dump, Vec2,
};
static DEBUG_VIEW_NAME: &str = "_cursive_debug_view";
@ -1035,6 +1036,49 @@ impl Cursive {
pub fn backend_name(&self) -> &str {
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 {

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 {
fn default() -> Self {
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]
@ -44,6 +52,7 @@ pub mod traits;
pub mod vec;
mod cursive;
mod dump;
mod printer;
mod rect;
mod with;
@ -52,6 +61,7 @@ mod xy;
mod div;
pub use self::cursive::{CbSink, Cursive, ScreenId};
pub use self::dump::Dump;
pub use self::printer::Printer;
pub use self::rect::Rect;
pub use self::vec::Vec2;

View File

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

View File

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

View File

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

View File

@ -8,12 +8,14 @@ use crate::Printer;
///
/// [`StackView`]: crate::views::StackView
#[derive(Debug)]
pub struct Layer<T: View> {
pub struct Layer<T> {
view: T,
color: ColorStyle,
}
impl<T: View> Layer<T> {
new_default!(Layer<T: Default>);
impl<T> Layer<T> {
/// Wraps the given view.
pub fn new(view: T) -> Self {
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.
///
/// 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>>,
id: String,
}
@ -22,7 +22,7 @@ pub struct NamedView<V: View> {
/// [`RefMut`]: std::cell::RefMut
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`.
pub fn new<S: Into<String>>(id: S, view: V) -> Self {
NamedView {

View File

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

View File

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

View File

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

View File

@ -23,7 +23,7 @@ use crate::XY;
/// ```
///
/// 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
size: XY<SizeConstraint>,
@ -34,7 +34,7 @@ pub struct ResizedView<T: View> {
view: T,
}
impl<T: View> ResizedView<T> {
impl<T> ResizedView<T> {
/// Creates a new `ResizedView` with the given width and height requirements.
///
/// `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>,
}
new_default!(ScrollView<V: Default>);
impl_scroller!(ScrollView<V>::core);
impl<V> ScrollView<V>
where
V: View,
{
impl<V> ScrollView<V> {
/// Creates a new ScrollView around `view`.
pub fn new(inner: V) -> Self {
ScrollView {
@ -189,7 +188,10 @@ where
}
/// 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());
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)
where
F: FnMut(&mut Self, Rect) -> EventResult + 'static,
V: 'static,
{
self.set_on_scroll_inner(Self::skip_unchanged(on_scroll, || {
EventResult::Ignored
@ -266,6 +269,7 @@ where
pub fn set_on_scroll_change<F>(&mut self, on_scroll: F)
where
F: FnMut(&mut Cursive, Rect) + 'static,
V: 'static,
{
self.set_on_scroll(Self::skip_unchanged(on_scroll, || ()));
}

View File

@ -8,14 +8,16 @@ use crate::Vec2;
/// Wrapper view that adds a shadow.
///
/// It reserves a 1 pixel border on each side.
pub struct ShadowView<T: View> {
pub struct ShadowView<T> {
view: T,
top_padding: bool,
left_padding: bool,
// 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.
pub fn new(view: T) -> Self {
ShadowView {

View File

@ -1,18 +1,19 @@
use crate::view::{View, ViewWrapper};
use crate::views::NamedView;
use crate::Printer;
use crate::Vec2;
use std::cell::Cell;
/// Wrapper around a view that remembers its position.
pub struct TrackedView<T: View> {
pub struct TrackedView<T> {
/// Wrapped view.
pub view: T,
/// Last position the view was located.
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.
pub fn offset(&self) -> Vec2 {
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);
}