From 28cd51c2651a9479c55956fccbf3d535a0d21c4e Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Thu, 2 Jul 2020 23:58:54 -0700 Subject: [PATCH] Add Cursive::dump --- cursive-core/src/cursive.rs | 60 ++++++++++++++++++++--- cursive-core/src/dump.rs | 22 +++++++++ cursive-core/src/lib.rs | 14 +++++- cursive-core/src/views/enableable_view.rs | 2 + cursive-core/src/views/hideable_view.rs | 2 + cursive-core/src/views/last_size_view.rs | 2 + cursive-core/src/views/layer.rs | 6 ++- cursive-core/src/views/named_view.rs | 4 +- cursive-core/src/views/on_event_view.rs | 7 +-- cursive-core/src/views/padded_view.rs | 2 +- cursive-core/src/views/panel.rs | 6 ++- cursive-core/src/views/resized_view.rs | 4 +- cursive-core/src/views/scroll_view.rs | 14 ++++-- cursive-core/src/views/shadow_view.rs | 6 ++- cursive-core/src/views/tracked_view.rs | 18 ++----- 15 files changed, 126 insertions(+), 43 deletions(-) create mode 100644 cursive-core/src/dump.rs diff --git a/cursive-core/src/cursive.rs b/cursive-core/src/cursive.rs index 9d05b4f..4e221df 100644 --- a/cursive-core/src/cursive.rs +++ b/cursive-core/src/cursive.rs @@ -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 { diff --git a/cursive-core/src/dump.rs b/cursive-core/src/dump.rs new file mode 100644 index 0000000..6fa3512 --- /dev/null +++ b/cursive-core/src/dump.rs @@ -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>, + pub(crate) cb_source: Receiver>, + + pub(crate) fps: Option, + + pub(crate) menubar: views::Menubar, + pub(crate) root_view: + views::OnEventView>, + + pub(crate) theme: Theme, + + pub(crate) user_data: Box, +} diff --git a/cursive-core/src/lib.rs b/cursive-core/src/lib.rs index 48c7d69..11c1c80 100644 --- a/cursive-core/src/lib.rs +++ b/cursive-core/src/lib.rs @@ -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; diff --git a/cursive-core/src/views/enableable_view.rs b/cursive-core/src/views/enableable_view.rs index 991f953..dbf6cd8 100644 --- a/cursive-core/src/views/enableable_view.rs +++ b/cursive-core/src/views/enableable_view.rs @@ -31,6 +31,8 @@ pub struct EnableableView { enabled: bool, } +new_default!(EnableableView); + impl EnableableView { /// Creates a new `EnableableView` around `view`. /// diff --git a/cursive-core/src/views/hideable_view.rs b/cursive-core/src/views/hideable_view.rs index 59907cb..ce6ce4a 100644 --- a/cursive-core/src/views/hideable_view.rs +++ b/cursive-core/src/views/hideable_view.rs @@ -17,6 +17,8 @@ pub struct HideableView { invalidated: bool, } +new_default!(HideableView); + impl HideableView { /// Creates a new HideableView around `view`. /// diff --git a/cursive-core/src/views/last_size_view.rs b/cursive-core/src/views/last_size_view.rs index 1694fc9..730fcbe 100644 --- a/cursive-core/src/views/last_size_view.rs +++ b/cursive-core/src/views/last_size_view.rs @@ -10,6 +10,8 @@ pub struct LastSizeView { pub size: Vec2, } +new_default!(LastSizeView); + impl LastSizeView { /// Wraps the given view. pub fn new(view: T) -> Self { diff --git a/cursive-core/src/views/layer.rs b/cursive-core/src/views/layer.rs index 0aa037a..22bc5f0 100644 --- a/cursive-core/src/views/layer.rs +++ b/cursive-core/src/views/layer.rs @@ -8,12 +8,14 @@ use crate::Printer; /// /// [`StackView`]: crate::views::StackView #[derive(Debug)] -pub struct Layer { +pub struct Layer { view: T, color: ColorStyle, } -impl Layer { +new_default!(Layer); + +impl Layer { /// Wraps the given view. pub fn new(view: T) -> Self { Self::with_color(view, ColorStyle::primary()) diff --git a/cursive-core/src/views/named_view.rs b/cursive-core/src/views/named_view.rs index 6a99ee8..a7c2ca7 100644 --- a/cursive-core/src/views/named_view.rs +++ b/cursive-core/src/views/named_view.rs @@ -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 { +pub struct NamedView { view: Rc>, id: String, } @@ -22,7 +22,7 @@ pub struct NamedView { /// [`RefMut`]: std::cell::RefMut pub type ViewRef = OwningHandle>, RefMut<'static, V>>; -impl NamedView { +impl NamedView { /// Wraps `view` in a new `NamedView`. pub fn new>(id: S, view: V) -> Self { NamedView { diff --git a/cursive-core/src/views/on_event_view.rs b/cursive-core/src/views/on_event_view.rs index 64fcdbe..2a2ab86 100644 --- a/cursive-core/src/views/on_event_view.rs +++ b/cursive-core/src/views/on_event_view.rs @@ -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 { +pub struct OnEventView { view: T, callbacks: Vec<(EventTrigger, Action)>, } +new_default!(OnEventView); + type InnerCallback = Rc Option>>; struct Action { @@ -65,7 +67,7 @@ enum TriggerPhase { AfterChild, } -impl OnEventView { +impl OnEventView { /// Wraps the given view in a new OnEventView. pub fn new(view: T) -> Self { OnEventView { @@ -83,7 +85,6 @@ impl OnEventView { self.callbacks .retain(move |&(ref trigger, _)| !trigger.has_tag(&event)); } - /// Registers a callback when the given event is ignored by the child. /// /// Chainable variant. diff --git a/cursive-core/src/views/padded_view.rs b/cursive-core/src/views/padded_view.rs index dddbe13..c508ef9 100644 --- a/cursive-core/src/views/padded_view.rs +++ b/cursive-core/src/views/padded_view.rs @@ -25,7 +25,7 @@ pub struct PaddedView { margins: Margins, } -impl PaddedView { +impl PaddedView { /// Wraps `view` in a new `PaddedView` with the given margins. pub fn new(margins: Margins, view: V) -> Self { PaddedView { view, margins } diff --git a/cursive-core/src/views/panel.rs b/cursive-core/src/views/panel.rs index a381a98..3a33ecc 100644 --- a/cursive-core/src/views/panel.rs +++ b/cursive-core/src/views/panel.rs @@ -10,7 +10,7 @@ use unicode_width::UnicodeWidthStr; /// Draws a border around a wrapped view. #[derive(Debug)] -pub struct Panel { +pub struct Panel { // Inner view view: V, @@ -24,7 +24,9 @@ pub struct Panel { invalidated: bool, } -impl Panel { +new_default!(Panel); + +impl Panel { /// Creates a new panel around the given view. pub fn new(view: V) -> Self { Panel { diff --git a/cursive-core/src/views/resized_view.rs b/cursive-core/src/views/resized_view.rs index f382bc7..4e7746c 100644 --- a/cursive-core/src/views/resized_view.rs +++ b/cursive-core/src/views/resized_view.rs @@ -23,7 +23,7 @@ use crate::XY; /// ``` /// /// See also [`Boxable`](crate::view::Boxable) for an easy way to wrap any view. -pub struct ResizedView { +pub struct ResizedView { /// Constraint on each axis size: XY, @@ -34,7 +34,7 @@ pub struct ResizedView { view: T, } -impl ResizedView { +impl ResizedView { /// Creates a new `ResizedView` with the given width and height requirements. /// /// `None` values will use the wrapped view's preferences. diff --git a/cursive-core/src/views/scroll_view.rs b/cursive-core/src/views/scroll_view.rs index 077121e..a4279a8 100644 --- a/cursive-core/src/views/scroll_view.rs +++ b/cursive-core/src/views/scroll_view.rs @@ -17,12 +17,11 @@ pub struct ScrollView { on_scroll: Rc EventResult>, } +new_default!(ScrollView); + impl_scroller!(ScrollView::core); -impl ScrollView -where - V: View, -{ +impl ScrollView { /// 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(&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(&mut self, on_scroll: F) where F: FnMut(&mut Cursive, Rect) + 'static, + V: 'static, { self.set_on_scroll(Self::skip_unchanged(on_scroll, || ())); } diff --git a/cursive-core/src/views/shadow_view.rs b/cursive-core/src/views/shadow_view.rs index c5cb569..5965af5 100644 --- a/cursive-core/src/views/shadow_view.rs +++ b/cursive-core/src/views/shadow_view.rs @@ -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 { +pub struct ShadowView { view: T, top_padding: bool, left_padding: bool, // TODO: invalidate if we change the padding? wrap_needs_relayout? } -impl ShadowView { +new_default!(ShadowView); + +impl ShadowView { /// Wraps the given view. pub fn new(view: T) -> Self { ShadowView { diff --git a/cursive-core/src/views/tracked_view.rs b/cursive-core/src/views/tracked_view.rs index bbc0394..554046f 100644 --- a/cursive-core/src/views/tracked_view.rs +++ b/cursive-core/src/views/tracked_view.rs @@ -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 { +pub struct TrackedView { /// Wrapped view. pub view: T, /// Last position the view was located. offset: Cell, } -impl TrackedView { +new_default!(TrackedView); + +impl TrackedView { /// Return the last offset at which the view was drawn. pub fn offset(&self) -> Vec2 { self.offset.get() @@ -26,17 +27,6 @@ impl TrackedView { } } - /// 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.with_name(id) - } - - /// Wraps itself in a `NamedView` for easy retrieval. - pub fn with_name(self, name: &str) -> NamedView { - NamedView::new(name, self) - } - inner_getters!(self.view: T); }