mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-27 11:16:03 +00:00
Make backends pausable
This commit is contained in:
parent
01bb32d640
commit
2cd6d7a80c
@ -38,14 +38,6 @@ pub trait Backend {
|
|||||||
/// * `Some(event)` for each event to process.
|
/// * `Some(event)` for each event to process.
|
||||||
fn poll_event(&mut self) -> Option<Event>;
|
fn poll_event(&mut self) -> Option<Event>;
|
||||||
|
|
||||||
// TODO: take `self` by value?
|
|
||||||
// Or implement Drop?
|
|
||||||
// Will change when implementing resumable backends
|
|
||||||
/// Prepares to close the backend.
|
|
||||||
///
|
|
||||||
/// This should clear any state in the terminal.
|
|
||||||
fn finish(&mut self);
|
|
||||||
|
|
||||||
/// Refresh the screen.
|
/// Refresh the screen.
|
||||||
///
|
///
|
||||||
/// This will be called each frame after drawing has been done.
|
/// This will be called each frame after drawing has been done.
|
||||||
@ -130,8 +122,6 @@ impl Backend for Dummy {
|
|||||||
"dummy"
|
"dummy"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(&mut self) {}
|
|
||||||
|
|
||||||
fn refresh(&mut self) {}
|
fn refresh(&mut self) {}
|
||||||
|
|
||||||
fn has_colors(&self) -> bool {
|
fn has_colors(&self) -> bool {
|
||||||
|
@ -2,12 +2,13 @@ use std::any::Any;
|
|||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use crossbeam_channel::{self, Receiver, Sender};
|
use crossbeam_channel::{self, Receiver, Sender};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend, direction,
|
backend,
|
||||||
|
cursive_run::CursiveRunner,
|
||||||
|
direction,
|
||||||
event::{Event, EventResult},
|
event::{Event, EventResult},
|
||||||
printer::Printer,
|
printer::Printer,
|
||||||
theme,
|
theme,
|
||||||
@ -18,9 +19,6 @@ use crate::{
|
|||||||
|
|
||||||
static DEBUG_VIEW_NAME: &str = "_cursive_debug_view";
|
static DEBUG_VIEW_NAME: &str = "_cursive_debug_view";
|
||||||
|
|
||||||
// How long we wait between two empty input polls
|
|
||||||
const INPUT_POLL_DELAY_MS: u64 = 30;
|
|
||||||
|
|
||||||
/// Central part of the cursive library.
|
/// Central part of the cursive library.
|
||||||
///
|
///
|
||||||
/// It initializes ncurses on creation and cleans up on drop.
|
/// It initializes ncurses on creation and cleans up on drop.
|
||||||
@ -36,14 +34,10 @@ pub struct Cursive {
|
|||||||
|
|
||||||
menubar: views::Menubar,
|
menubar: views::Menubar,
|
||||||
|
|
||||||
// Last layer sizes of the stack view.
|
pub(crate) needs_clear: bool,
|
||||||
// If it changed, clear the screen.
|
|
||||||
last_sizes: Vec<Vec2>,
|
|
||||||
|
|
||||||
running: bool,
|
running: bool,
|
||||||
|
|
||||||
backend: Box<dyn backend::Backend>,
|
|
||||||
|
|
||||||
// Handle asynchronous callbacks
|
// Handle asynchronous callbacks
|
||||||
cb_source: Receiver<Box<dyn FnOnce(&mut Cursive) + Send>>,
|
cb_source: Receiver<Box<dyn FnOnce(&mut Cursive) + Send>>,
|
||||||
cb_sink: Sender<Box<dyn FnOnce(&mut Cursive) + Send>>,
|
cb_sink: Sender<Box<dyn FnOnce(&mut Cursive) + Send>>,
|
||||||
@ -53,7 +47,6 @@ pub struct Cursive {
|
|||||||
|
|
||||||
// Handle auto-refresh when no event is received.
|
// Handle auto-refresh when no event is received.
|
||||||
fps: Option<NonZeroU32>,
|
fps: Option<NonZeroU32>,
|
||||||
boring_frame_count: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Identifies a screen in the cursive root.
|
/// Identifies a screen in the cursive root.
|
||||||
@ -71,24 +64,6 @@ pub type ScreenId = usize;
|
|||||||
pub type CbSink = Sender<Box<dyn FnOnce(&mut Cursive) + Send>>;
|
pub type CbSink = Sender<Box<dyn FnOnce(&mut Cursive) + Send>>;
|
||||||
|
|
||||||
impl Cursive {
|
impl Cursive {
|
||||||
/// Shortcut for `Cursive::try_new` with non-failible init function.
|
|
||||||
///
|
|
||||||
/// You probably don't want to use this function directly, unless you're
|
|
||||||
/// using a non-standard backend. Built-in backends have dedicated functions.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # use cursive_core::{Cursive, backend};
|
|
||||||
/// let siv = Cursive::new(backend::Dummy::init);
|
|
||||||
/// ```
|
|
||||||
pub fn new<F>(backend_init: F) -> Self
|
|
||||||
where
|
|
||||||
F: FnOnce() -> Box<dyn backend::Backend>,
|
|
||||||
{
|
|
||||||
Self::try_new::<_, ()>(|| Ok(backend_init())).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new Cursive root, and initialize the back-end.
|
/// Creates a new Cursive root, and initialize the back-end.
|
||||||
///
|
///
|
||||||
/// You probably don't want to use this function directly, unless you're
|
/// You probably don't want to use this function directly, unless you're
|
||||||
@ -96,42 +71,60 @@ impl Cursive {
|
|||||||
/// [`CursiveExt`] trait.
|
/// [`CursiveExt`] trait.
|
||||||
///
|
///
|
||||||
/// [`CursiveExt`]: https://docs.rs/cursive/0/cursive/trait.CursiveExt.html
|
/// [`CursiveExt`]: https://docs.rs/cursive/0/cursive/trait.CursiveExt.html
|
||||||
pub fn try_new<F, E>(backend_init: F) -> Result<Self, E>
|
pub fn new() -> Self {
|
||||||
where
|
|
||||||
F: FnOnce() -> Result<Box<dyn backend::Backend>, E>,
|
|
||||||
{
|
|
||||||
let theme = theme::load_default();
|
let theme = theme::load_default();
|
||||||
|
|
||||||
let (cb_sink, cb_source) = crossbeam_channel::unbounded();
|
let (cb_sink, cb_source) = crossbeam_channel::unbounded();
|
||||||
|
|
||||||
let backend = backend_init()?;
|
|
||||||
let mut cursive = Cursive {
|
let mut cursive = Cursive {
|
||||||
theme,
|
theme,
|
||||||
root: views::OnEventView::new(views::ScreensView::single_screen(
|
root: views::OnEventView::new(views::ScreensView::single_screen(
|
||||||
views::StackView::new(),
|
views::StackView::new(),
|
||||||
)),
|
)),
|
||||||
last_sizes: Vec::new(),
|
|
||||||
menubar: views::Menubar::new(),
|
menubar: views::Menubar::new(),
|
||||||
|
needs_clear: true,
|
||||||
running: true,
|
running: true,
|
||||||
cb_source,
|
cb_source,
|
||||||
cb_sink,
|
cb_sink,
|
||||||
backend,
|
|
||||||
fps: None,
|
fps: None,
|
||||||
boring_frame_count: 0,
|
|
||||||
user_data: Box::new(()),
|
user_data: Box::new(()),
|
||||||
};
|
};
|
||||||
cursive.reset_default_callbacks();
|
cursive.reset_default_callbacks();
|
||||||
|
|
||||||
Ok(cursive)
|
cursive
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a [dummy backend].
|
pub(crate) fn layout(&mut self, size: Vec2) {
|
||||||
///
|
let offset = if self.menubar.autohide { 0 } else { 1 };
|
||||||
/// Nothing will be output. This is mostly here for tests.
|
let size = size.saturating_sub((0, offset));
|
||||||
///
|
self.root.layout(size);
|
||||||
/// [dummy backend]: backend::Dummy
|
}
|
||||||
pub fn dummy() -> Self {
|
|
||||||
Self::new(backend::Dummy::init)
|
pub(crate) fn draw(&mut self, size: Vec2, backend: &dyn backend::Backend) {
|
||||||
|
let printer = Printer::new(size, &self.theme, backend);
|
||||||
|
|
||||||
|
let selected = self.menubar.receive_events();
|
||||||
|
|
||||||
|
// Print the stackview background before the menubar
|
||||||
|
let offset = if self.menubar.autohide { 0 } else { 1 };
|
||||||
|
|
||||||
|
// The printer for the stackview
|
||||||
|
let sv_printer = printer.offset((0, offset)).focused(!selected);
|
||||||
|
self.root.draw(&sv_printer);
|
||||||
|
|
||||||
|
self.root.get_inner().draw_bg(&sv_printer);
|
||||||
|
|
||||||
|
// Draw the currently active screen
|
||||||
|
// If the menubar is active, nothing else can be.
|
||||||
|
// Draw the menubar?
|
||||||
|
if self.menubar.visible() {
|
||||||
|
let printer = printer.focused(self.menubar.receive_events());
|
||||||
|
self.menubar.draw(&printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally draw stackview layers
|
||||||
|
// using variables from above
|
||||||
|
self.root.get_inner().draw_fg(&sv_printer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets some data to be stored in Cursive.
|
/// Sets some data to be stored in Cursive.
|
||||||
@ -162,7 +155,7 @@ impl Cursive {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// let mut siv = cursive_core::Cursive::dummy();
|
/// let mut siv = cursive_core::Cursive::new();
|
||||||
///
|
///
|
||||||
/// // Start with a simple `Vec<i32>` as user data.
|
/// // Start with a simple `Vec<i32>` as user data.
|
||||||
/// siv.set_user_data(vec![1i32, 2, 3]);
|
/// siv.set_user_data(vec![1i32, 2, 3]);
|
||||||
@ -231,7 +224,7 @@ impl Cursive {
|
|||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use cursive_core::Cursive;
|
/// # use cursive_core::Cursive;
|
||||||
/// # let mut siv = Cursive::dummy();
|
/// # let mut siv = Cursive::new();
|
||||||
/// siv.add_global_callback('~', Cursive::toggle_debug_console);
|
/// siv.add_global_callback('~', Cursive::toggle_debug_console);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn toggle_debug_console(&mut self) {
|
pub fn toggle_debug_console(&mut self) {
|
||||||
@ -264,7 +257,7 @@ impl Cursive {
|
|||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use cursive_core::*;
|
/// # use cursive_core::*;
|
||||||
/// let mut siv = Cursive::dummy();
|
/// let mut siv = Cursive::new();
|
||||||
///
|
///
|
||||||
/// // quit() will be called during the next event cycle
|
/// // quit() will be called during the next event cycle
|
||||||
/// siv.cb_sink().send(Box::new(|s| s.quit())).unwrap();
|
/// siv.cb_sink().send(Box::new(|s| s.quit())).unwrap();
|
||||||
@ -298,7 +291,7 @@ impl Cursive {
|
|||||||
/// # use cursive_core::traits::*;
|
/// # use cursive_core::traits::*;
|
||||||
/// # use cursive_core::menu::*;
|
/// # use cursive_core::menu::*;
|
||||||
/// #
|
/// #
|
||||||
/// let mut siv = Cursive::dummy();
|
/// let mut siv = Cursive::new();
|
||||||
///
|
///
|
||||||
/// siv.menubar()
|
/// siv.menubar()
|
||||||
/// .add_subtree("File",
|
/// .add_subtree("File",
|
||||||
@ -361,8 +354,7 @@ impl Cursive {
|
|||||||
///
|
///
|
||||||
/// Users rarely have to call this directly.
|
/// Users rarely have to call this directly.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.backend
|
self.needs_clear = true;
|
||||||
.clear(self.theme.palette[theme::PaletteColor::Background]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a theme from the given file.
|
/// Loads a theme from the given file.
|
||||||
@ -405,6 +397,14 @@ impl Cursive {
|
|||||||
self.set_fps(if autorefresh { 30 } else { 0 });
|
self.set_fps(if autorefresh { 30 } else { 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the current refresh rate, if any.
|
||||||
|
///
|
||||||
|
/// Returns `None` if no auto-refresh is set. Otherwise, returns the rate
|
||||||
|
/// in frames per second.
|
||||||
|
pub fn fps(&self) -> Option<NonZeroU32> {
|
||||||
|
self.fps
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a reference to the currently active screen.
|
/// Returns a reference to the currently active screen.
|
||||||
pub fn screen(&self) -> &views::StackView {
|
pub fn screen(&self) -> &views::StackView {
|
||||||
self.root.get_inner().screen().unwrap()
|
self.root.get_inner().screen().unwrap()
|
||||||
@ -452,13 +452,13 @@ impl Cursive {
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use cursive_core::{Cursive, views, view};
|
/// # use cursive_core::{Cursive, views, view};
|
||||||
/// # use cursive_core::traits::*;
|
/// # use cursive_core::traits::*;
|
||||||
/// let mut siv = Cursive::dummy();
|
/// let mut siv = Cursive::new();
|
||||||
///
|
///
|
||||||
/// siv.add_layer(views::TextView::new("Text #1").with_name("text"));
|
/// siv.add_layer(views::TextView::new("Text #1").with_name("text"));
|
||||||
///
|
///
|
||||||
/// siv.add_global_callback('p', |s| {
|
/// siv.add_global_callback('p', |s| {
|
||||||
/// s.call_on(
|
/// s.call_on(
|
||||||
/// &view::Selector::Id("text"),
|
/// &view::Selector::Name("text"),
|
||||||
/// |view: &mut views::TextView| {
|
/// |view: &mut views::TextView| {
|
||||||
/// view.set_content("Text #2");
|
/// view.set_content("Text #2");
|
||||||
/// },
|
/// },
|
||||||
@ -486,7 +486,7 @@ impl Cursive {
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use cursive_core::{Cursive, views};
|
/// # use cursive_core::{Cursive, views};
|
||||||
/// # use cursive_core::traits::*;
|
/// # use cursive_core::traits::*;
|
||||||
/// let mut siv = Cursive::dummy();
|
/// let mut siv = Cursive::new();
|
||||||
///
|
///
|
||||||
/// siv.add_layer(views::TextView::new("Text #1")
|
/// siv.add_layer(views::TextView::new("Text #1")
|
||||||
/// .with_name("text"));
|
/// .with_name("text"));
|
||||||
@ -530,7 +530,7 @@ impl Cursive {
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use cursive_core::Cursive;
|
/// # use cursive_core::Cursive;
|
||||||
/// # use cursive_core::views::{TextView, ViewRef};
|
/// # use cursive_core::views::{TextView, ViewRef};
|
||||||
/// # let mut siv = Cursive::dummy();
|
/// # let mut siv = Cursive::new();
|
||||||
/// use cursive_core::traits::Identifiable;
|
/// use cursive_core::traits::Identifiable;
|
||||||
///
|
///
|
||||||
/// siv.add_layer(TextView::new("foo").with_name("id"));
|
/// siv.add_layer(TextView::new("foo").with_name("id"));
|
||||||
@ -546,7 +546,7 @@ impl Cursive {
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use cursive_core::Cursive;
|
/// # use cursive_core::Cursive;
|
||||||
/// # use cursive_core::views::{SelectView};
|
/// # use cursive_core::views::{SelectView};
|
||||||
/// # let mut siv = Cursive::dummy();
|
/// # let mut siv = Cursive::new();
|
||||||
/// use cursive_core::traits::Identifiable;
|
/// use cursive_core::traits::Identifiable;
|
||||||
///
|
///
|
||||||
/// let select = SelectView::new().item("zero", 0u32).item("one", 1u32);
|
/// let select = SelectView::new().item("zero", 0u32).item("one", 1u32);
|
||||||
@ -606,7 +606,7 @@ impl Cursive {
|
|||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use cursive_core::*;
|
/// # use cursive_core::*;
|
||||||
/// let mut siv = Cursive::dummy();
|
/// let mut siv = Cursive::new();
|
||||||
///
|
///
|
||||||
/// siv.add_global_callback('q', |s| s.quit());
|
/// siv.add_global_callback('q', |s| s.quit());
|
||||||
/// ```
|
/// ```
|
||||||
@ -702,7 +702,7 @@ impl Cursive {
|
|||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use cursive_core::Cursive;
|
/// use cursive_core::Cursive;
|
||||||
/// let mut siv = Cursive::dummy();
|
/// let mut siv = Cursive::new();
|
||||||
///
|
///
|
||||||
/// siv.add_global_callback('q', |s| s.quit());
|
/// siv.add_global_callback('q', |s| s.quit());
|
||||||
/// siv.clear_global_callbacks('q');
|
/// siv.clear_global_callbacks('q');
|
||||||
@ -731,7 +731,7 @@ impl Cursive {
|
|||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use cursive_core::{Cursive, views};
|
/// use cursive_core::{Cursive, views};
|
||||||
/// let mut siv = Cursive::dummy();
|
/// let mut siv = Cursive::new();
|
||||||
///
|
///
|
||||||
/// siv.add_layer(views::TextView::new("Hello world!"));
|
/// siv.add_layer(views::TextView::new("Hello world!"));
|
||||||
/// ```
|
/// ```
|
||||||
@ -799,52 +799,18 @@ impl Cursive {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the size of the screen, in characters.
|
/// Try to process a single callback.
|
||||||
pub fn screen_size(&self) -> Vec2 {
|
///
|
||||||
self.backend.screen_size()
|
/// Returns `true` if a callback was processed, `false` if there was
|
||||||
}
|
/// nothing to process.
|
||||||
|
pub(crate) fn process_callback(&mut self) -> bool {
|
||||||
fn layout(&mut self) {
|
match self.cb_source.try_recv() {
|
||||||
let size = self.screen_size();
|
Ok(cb) => {
|
||||||
let offset = if self.menubar.autohide { 0 } else { 1 };
|
cb(self);
|
||||||
let size = size.saturating_sub((0, offset));
|
true
|
||||||
self.root.layout(size);
|
}
|
||||||
}
|
_ => false,
|
||||||
|
|
||||||
fn draw(&mut self) {
|
|
||||||
// TODO: do not allocate in the default, fast path?
|
|
||||||
let sizes = self.screen().layer_sizes();
|
|
||||||
if self.last_sizes != sizes {
|
|
||||||
// TODO: Maybe we only need to clear if the _max_ size differs?
|
|
||||||
// Or if the positions change?
|
|
||||||
self.clear();
|
|
||||||
self.last_sizes = sizes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let printer =
|
|
||||||
Printer::new(self.screen_size(), &self.theme, &*self.backend);
|
|
||||||
|
|
||||||
let selected = self.menubar.receive_events();
|
|
||||||
|
|
||||||
// Print the stackview background before the menubar
|
|
||||||
let offset = if self.menubar.autohide { 0 } else { 1 };
|
|
||||||
|
|
||||||
let sv_printer = printer.offset((0, offset)).focused(!selected);
|
|
||||||
self.root.draw(&sv_printer);
|
|
||||||
|
|
||||||
self.root.get_inner().draw_bg(&sv_printer);
|
|
||||||
|
|
||||||
// Draw the currently active screen
|
|
||||||
// If the menubar is active, nothing else can be.
|
|
||||||
// Draw the menubar?
|
|
||||||
if self.menubar.visible() {
|
|
||||||
let printer = printer.focused(self.menubar.receive_events());
|
|
||||||
self.menubar.draw(&printer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// finally draw stackview layers
|
|
||||||
// using variables from above
|
|
||||||
self.root.get_inner().draw_fg(&sv_printer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` until [`quit(&mut self)`] is called.
|
/// Returns `true` until [`quit(&mut self)`] is called.
|
||||||
@ -854,138 +820,65 @@ impl Cursive {
|
|||||||
self.running
|
self.running
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the event loop.
|
/// Runs a dummy event loop.
|
||||||
///
|
///
|
||||||
/// It will wait for user input (key presses)
|
/// Initializes a dummy backend for the event loop.
|
||||||
/// and trigger callbacks accordingly.
|
pub fn run_dummy(&mut self) {
|
||||||
|
self.run_with(|| backend::Dummy::init())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new runner on the given backend.
|
||||||
///
|
///
|
||||||
/// Internally, it calls [`step(&mut self)`] until [`quit(&mut self)`] is
|
/// Used to manually control the event loop. In most cases, running
|
||||||
/// called.
|
/// `Cursive::run_with` will be easier.
|
||||||
///
|
///
|
||||||
/// After this function returns, you can call it again and it will start a
|
/// The runner will borrow `self`; when dropped, it will clear out the
|
||||||
/// new loop.
|
/// terminal, and the cursive instance will be ready for another run if
|
||||||
|
/// needed.
|
||||||
|
pub fn runner(
|
||||||
|
&mut self,
|
||||||
|
backend: Box<dyn backend::Backend>,
|
||||||
|
) -> CursiveRunner<&mut Self> {
|
||||||
|
CursiveRunner::new(self, backend)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new runner on the given backend.
|
||||||
///
|
///
|
||||||
/// [`step(&mut self)`]: #method.step
|
/// Used to manually control the event loop. In most cases, running
|
||||||
/// [`quit(&mut self)`]: #method.quit
|
/// `Cursive::run_with` will be easier.
|
||||||
pub fn run(&mut self) {
|
///
|
||||||
|
/// The runner will embed `self`; when dropped, it will clear out the
|
||||||
|
/// terminal, and the cursive instance will be dropped as well.
|
||||||
|
pub fn into_runner(
|
||||||
|
self,
|
||||||
|
backend: Box<dyn backend::Backend>,
|
||||||
|
) -> CursiveRunner<Self> {
|
||||||
|
CursiveRunner::new(self, backend)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the backend and runs the event loop.
|
||||||
|
///
|
||||||
|
/// Used for infallible backend initializers.
|
||||||
|
pub fn run_with<F>(&mut self, backend_init: F)
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Box<dyn backend::Backend>,
|
||||||
|
{
|
||||||
|
self.try_run_with::<(), _>(|| Ok(backend_init())).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the backend and runs the event loop.
|
||||||
|
///
|
||||||
|
/// Returns an error if initializing the backend fails.
|
||||||
|
pub fn try_run_with<E, F>(&mut self, backend_init: F) -> Result<(), E>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<Box<dyn backend::Backend>, E>,
|
||||||
|
{
|
||||||
self.running = true;
|
self.running = true;
|
||||||
|
let mut runner = self.runner(backend_init()?);
|
||||||
|
|
||||||
self.refresh();
|
runner.run();
|
||||||
|
|
||||||
// And the big event loop begins!
|
Ok(())
|
||||||
while self.running {
|
|
||||||
self.step();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a single step from the event loop.
|
|
||||||
///
|
|
||||||
/// Useful if you need tighter control on the event loop.
|
|
||||||
/// Otherwise, [`run(&mut self)`] might be more convenient.
|
|
||||||
///
|
|
||||||
/// Returns `true` if an input event or callback was received
|
|
||||||
/// during this step, and `false` otherwise.
|
|
||||||
///
|
|
||||||
/// [`run(&mut self)`]: #method.run
|
|
||||||
pub fn step(&mut self) -> bool {
|
|
||||||
let received_something = self.process_events();
|
|
||||||
self.post_events(received_something);
|
|
||||||
received_something
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs the first half of `Self::step()`.
|
|
||||||
///
|
|
||||||
/// This is an advanced method for fine-tuned manual stepping;
|
|
||||||
/// you probably want [`run`][1] or [`step`][2].
|
|
||||||
///
|
|
||||||
/// This processes any pending event or callback. After calling this,
|
|
||||||
/// you will want to call [`post_events`][3] with the result from this
|
|
||||||
/// function.
|
|
||||||
///
|
|
||||||
/// Returns `true` if an event or callback was received,
|
|
||||||
/// and `false` otherwise.
|
|
||||||
///
|
|
||||||
/// [1]: Cursive::run()
|
|
||||||
/// [2]: Cursive::step()
|
|
||||||
/// [3]: Cursive::post_events()
|
|
||||||
pub fn process_events(&mut self) -> bool {
|
|
||||||
// Things are boring if nothing significant happened.
|
|
||||||
let mut boring = true;
|
|
||||||
|
|
||||||
// First, handle all available input
|
|
||||||
while let Some(event) = self.backend.poll_event() {
|
|
||||||
boring = false;
|
|
||||||
self.on_event(event);
|
|
||||||
|
|
||||||
if !self.running {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then, handle any available callback
|
|
||||||
while let Ok(cb) = self.cb_source.try_recv() {
|
|
||||||
boring = false;
|
|
||||||
cb(self);
|
|
||||||
|
|
||||||
if !self.running {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
!boring
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs the second half of `Self::step()`.
|
|
||||||
///
|
|
||||||
/// This is an advanced method for fine-tuned manual stepping;
|
|
||||||
/// you probably want [`run`][1] or [`step`][2].
|
|
||||||
///
|
|
||||||
/// You should call this after [`process_events`][3].
|
|
||||||
///
|
|
||||||
/// [1]: Cursive::run()
|
|
||||||
/// [2]: Cursive::step()
|
|
||||||
/// [3]: Cursive::process_events()
|
|
||||||
pub fn post_events(&mut self, received_something: bool) {
|
|
||||||
let boring = !received_something;
|
|
||||||
// How many times should we try if it's still boring?
|
|
||||||
// Total duration will be INPUT_POLL_DELAY_MS * repeats
|
|
||||||
// So effectively fps = 1000 / INPUT_POLL_DELAY_MS / repeats
|
|
||||||
if !boring
|
|
||||||
|| self
|
|
||||||
.fps
|
|
||||||
.map(|fps| 1000 / INPUT_POLL_DELAY_MS as u32 / fps.get())
|
|
||||||
.map(|repeats| self.boring_frame_count >= repeats)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
// We deserve to draw something!
|
|
||||||
|
|
||||||
if boring {
|
|
||||||
// We're only here because of a timeout.
|
|
||||||
self.on_event(Event::Refresh);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
if boring {
|
|
||||||
std::thread::sleep(Duration::from_millis(INPUT_POLL_DELAY_MS));
|
|
||||||
self.boring_frame_count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Refresh the screen with the current view tree state.
|
|
||||||
pub fn refresh(&mut self) {
|
|
||||||
self.boring_frame_count = 0;
|
|
||||||
|
|
||||||
// Do we need to redraw everytime?
|
|
||||||
// Probably, actually.
|
|
||||||
// TODO: Do we need to re-layout everytime?
|
|
||||||
self.layout();
|
|
||||||
|
|
||||||
// TODO: Do we need to redraw every view every time?
|
|
||||||
// (Is this getting repetitive? :p)
|
|
||||||
self.draw();
|
|
||||||
self.backend.refresh();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops the event loop.
|
/// Stops the event loop.
|
||||||
@ -998,13 +891,6 @@ impl Cursive {
|
|||||||
// foo
|
// foo
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the name of the backend used.
|
|
||||||
///
|
|
||||||
/// Mostly used for debugging.
|
|
||||||
pub fn backend_name(&self) -> &str {
|
|
||||||
self.backend.name()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dump the current state of the Cursive root.
|
/// Dump the current state of the Cursive root.
|
||||||
///
|
///
|
||||||
/// *It will clear out this `Cursive` instance* and save everything, including:
|
/// *It will clear out this `Cursive` instance* and save everything, including:
|
||||||
@ -1052,9 +938,3 @@ impl Cursive {
|
|||||||
self.clear();
|
self.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Cursive {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.backend.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
229
cursive-core/src/cursive_run.rs
Normal file
229
cursive-core/src/cursive_run.rs
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
use crate::{backend, event::Event, theme, Cursive, Vec2};
|
||||||
|
use std::borrow::{Borrow, BorrowMut};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
// How long we wait between two empty input polls
|
||||||
|
const INPUT_POLL_DELAY_MS: u64 = 30;
|
||||||
|
|
||||||
|
/// Event loop runner for a cursive instance.
|
||||||
|
///
|
||||||
|
/// You can get one from `Cursive::runner`, then either call `.run()`, or
|
||||||
|
/// manually `.step()`.
|
||||||
|
pub struct CursiveRunner<C> {
|
||||||
|
siv: C,
|
||||||
|
backend: Box<dyn backend::Backend>,
|
||||||
|
boring_frame_count: u32,
|
||||||
|
// Last layer sizes of the stack view.
|
||||||
|
// If it changed, clear the screen.
|
||||||
|
last_sizes: Vec<Vec2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> std::ops::Deref for CursiveRunner<C>
|
||||||
|
where
|
||||||
|
C: Borrow<Cursive>,
|
||||||
|
{
|
||||||
|
type Target = Cursive;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Cursive {
|
||||||
|
self.siv.borrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> std::ops::DerefMut for CursiveRunner<C>
|
||||||
|
where
|
||||||
|
C: BorrowMut<Cursive>,
|
||||||
|
{
|
||||||
|
fn deref_mut(&mut self) -> &mut Cursive {
|
||||||
|
self.siv.borrow_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> CursiveRunner<C> {
|
||||||
|
pub(crate) fn new(siv: C, backend: Box<dyn backend::Backend>) -> Self {
|
||||||
|
CursiveRunner {
|
||||||
|
siv,
|
||||||
|
backend,
|
||||||
|
boring_frame_count: 0,
|
||||||
|
last_sizes: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the size of the screen, in characters.
|
||||||
|
fn screen_size(&self) -> Vec2 {
|
||||||
|
self.backend.screen_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clean out the terminal and get back the wrapped object.
|
||||||
|
pub fn into_inner(self) -> C {
|
||||||
|
self.siv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> CursiveRunner<C>
|
||||||
|
where
|
||||||
|
C: BorrowMut<Cursive>,
|
||||||
|
{
|
||||||
|
fn layout(&mut self) {
|
||||||
|
let size = self.screen_size();
|
||||||
|
self.siv.borrow_mut().layout(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&mut self) {
|
||||||
|
let sizes = self.screen().layer_sizes();
|
||||||
|
if self.last_sizes != sizes {
|
||||||
|
// TODO: Maybe we only need to clear if the _max_ size differs?
|
||||||
|
// Or if the positions change?
|
||||||
|
self.clear();
|
||||||
|
self.last_sizes = sizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.needs_clear {
|
||||||
|
self.backend.clear(
|
||||||
|
self.current_theme().palette[theme::PaletteColor::Background],
|
||||||
|
);
|
||||||
|
self.needs_clear = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = self.screen_size();
|
||||||
|
|
||||||
|
self.siv.borrow_mut().draw(size, &*self.backend);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs the first half of `Self::step()`.
|
||||||
|
///
|
||||||
|
/// This is an advanced method for fine-tuned manual stepping;
|
||||||
|
/// you probably want [`run`][1] or [`step`][2].
|
||||||
|
///
|
||||||
|
/// This processes any pending event or callback. After calling this,
|
||||||
|
/// you will want to call [`post_events`][3] with the result from this
|
||||||
|
/// function.
|
||||||
|
///
|
||||||
|
/// Returns `true` if an event or callback was received,
|
||||||
|
/// and `false` otherwise.
|
||||||
|
///
|
||||||
|
/// [1]: CursiveRun::run()
|
||||||
|
/// [2]: CursiveRun::step()
|
||||||
|
/// [3]: CursiveRun::post_events()
|
||||||
|
pub fn process_events(&mut self) -> bool {
|
||||||
|
// Things are boring if nothing significant happened.
|
||||||
|
let mut boring = true;
|
||||||
|
|
||||||
|
// First, handle all available input
|
||||||
|
while let Some(event) = self.backend.poll_event() {
|
||||||
|
boring = false;
|
||||||
|
self.on_event(event);
|
||||||
|
|
||||||
|
if !self.is_running() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, handle any available callback
|
||||||
|
while self.process_callback() {
|
||||||
|
boring = false;
|
||||||
|
|
||||||
|
if !self.is_running() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
!boring
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs the second half of `Self::step()`.
|
||||||
|
///
|
||||||
|
/// This is an advanced method for fine-tuned manual stepping;
|
||||||
|
/// you probably want [`run`][1] or [`step`][2].
|
||||||
|
///
|
||||||
|
/// You should call this after [`process_events`][3].
|
||||||
|
///
|
||||||
|
/// [1]: Cursive::run()
|
||||||
|
/// [2]: Cursive::step()
|
||||||
|
/// [3]: Cursive::process_events()
|
||||||
|
pub fn post_events(&mut self, received_something: bool) {
|
||||||
|
let boring = !received_something;
|
||||||
|
// How many times should we try if it's still boring?
|
||||||
|
// Total duration will be INPUT_POLL_DELAY_MS * repeats
|
||||||
|
// So effectively fps = 1000 / INPUT_POLL_DELAY_MS / repeats
|
||||||
|
if !boring
|
||||||
|
|| self
|
||||||
|
.fps()
|
||||||
|
.map(|fps| 1000 / INPUT_POLL_DELAY_MS as u32 / fps.get())
|
||||||
|
.map(|repeats| self.boring_frame_count >= repeats)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
// We deserve to draw something!
|
||||||
|
|
||||||
|
if boring {
|
||||||
|
// We're only here because of a timeout.
|
||||||
|
self.on_event(Event::Refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if boring {
|
||||||
|
std::thread::sleep(Duration::from_millis(INPUT_POLL_DELAY_MS));
|
||||||
|
self.boring_frame_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Refresh the screen with the current view tree state.
|
||||||
|
pub fn refresh(&mut self) {
|
||||||
|
self.boring_frame_count = 0;
|
||||||
|
|
||||||
|
// Do we need to redraw everytime?
|
||||||
|
// Probably, actually.
|
||||||
|
// TODO: Do we need to re-layout everytime?
|
||||||
|
self.layout();
|
||||||
|
|
||||||
|
// TODO: Do we need to redraw every view every time?
|
||||||
|
// (Is this getting repetitive? :p)
|
||||||
|
self.draw();
|
||||||
|
self.backend.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the name of the backend used.
|
||||||
|
///
|
||||||
|
/// Mostly used for debugging.
|
||||||
|
pub fn backend_name(&self) -> &str {
|
||||||
|
self.backend.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs a single step from the event loop.
|
||||||
|
///
|
||||||
|
/// Useful if you need tighter control on the event loop.
|
||||||
|
/// Otherwise, [`run(&mut self)`] might be more convenient.
|
||||||
|
///
|
||||||
|
/// Returns `true` if an input event or callback was received
|
||||||
|
/// during this step, and `false` otherwise.
|
||||||
|
///
|
||||||
|
/// [`run(&mut self)`]: #method.run
|
||||||
|
pub fn step(&mut self) -> bool {
|
||||||
|
let received_something = self.process_events();
|
||||||
|
self.post_events(received_something);
|
||||||
|
received_something
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the event loop.
|
||||||
|
///
|
||||||
|
/// It will wait for user input (key presses)
|
||||||
|
/// and trigger callbacks accordingly.
|
||||||
|
///
|
||||||
|
/// Internally, it calls [`step(&mut self)`] until [`quit(&mut self)`] is
|
||||||
|
/// called.
|
||||||
|
///
|
||||||
|
/// After this function returns, you can call it again and it will start a
|
||||||
|
/// new loop.
|
||||||
|
///
|
||||||
|
/// [`step(&mut self)`]: #method.step
|
||||||
|
/// [`quit(&mut self)`]: #method.quit
|
||||||
|
pub fn run(&mut self) {
|
||||||
|
self.refresh();
|
||||||
|
|
||||||
|
// And the big event loop begins!
|
||||||
|
while self.is_running() {
|
||||||
|
self.step();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -52,6 +52,7 @@ pub mod traits;
|
|||||||
pub mod vec;
|
pub mod vec;
|
||||||
|
|
||||||
mod cursive;
|
mod cursive;
|
||||||
|
mod cursive_run;
|
||||||
mod dump;
|
mod dump;
|
||||||
mod printer;
|
mod printer;
|
||||||
mod rect;
|
mod rect;
|
||||||
@ -61,6 +62,7 @@ mod xy;
|
|||||||
mod div;
|
mod div;
|
||||||
|
|
||||||
pub use self::cursive::{CbSink, Cursive, ScreenId};
|
pub use self::cursive::{CbSink, Cursive, ScreenId};
|
||||||
|
pub use self::cursive_run::CursiveRunner;
|
||||||
pub use self::dump::Dump;
|
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;
|
||||||
|
@ -144,7 +144,7 @@
|
|||||||
//! method (or use [`theme::load_theme_file`] to aquire the theme object).
|
//! method (or use [`theme::load_theme_file`] to aquire the theme object).
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! ```rust,ignore
|
||||||
//! let mut siv = Cursive::dummy();
|
//! let mut siv = Cursive::new();
|
||||||
//! // Embed the theme with the binary.
|
//! // Embed the theme with the binary.
|
||||||
//! siv.load_toml(include_str!("<path_to_theme_file>.toml")).unwrap();
|
//! siv.load_toml(include_str!("<path_to_theme_file>.toml")).unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
|
@ -54,7 +54,7 @@ pub fn immutify<F: FnMut(&mut Cursive)>(
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use cursive_core::{Cursive, immut1};
|
/// # use cursive_core::{Cursive, immut1};
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
/// # let mut siv = Cursive::dummy();
|
/// # let mut siv = Cursive::new();
|
||||||
/// let mut i = 0;
|
/// let mut i = 0;
|
||||||
/// // `Cursive::add_global_callback` takes a `Fn(&mut Cursive)`
|
/// // `Cursive::add_global_callback` takes a `Fn(&mut Cursive)`
|
||||||
/// siv.add_global_callback('q', immut1!(move |s: &mut Cursive| {
|
/// siv.add_global_callback('q', immut1!(move |s: &mut Cursive| {
|
||||||
|
@ -19,7 +19,7 @@ pub trait Nameable: View + Sized {
|
|||||||
/// # use cursive_core::view::Resizable;
|
/// # use cursive_core::view::Resizable;
|
||||||
/// use cursive_core::view::Nameable;
|
/// use cursive_core::view::Nameable;
|
||||||
///
|
///
|
||||||
/// let mut siv = Cursive::dummy();
|
/// let mut siv = Cursive::new();
|
||||||
/// siv.add_layer(
|
/// siv.add_layer(
|
||||||
/// TextView::new("foo")
|
/// TextView::new("foo")
|
||||||
/// .with_name("text")
|
/// .with_name("text")
|
||||||
|
@ -34,7 +34,7 @@ pub type OnSubmit = dyn Fn(&mut Cursive, &str);
|
|||||||
/// # use cursive_core::Cursive;
|
/// # use cursive_core::Cursive;
|
||||||
/// # use cursive_core::traits::*;
|
/// # use cursive_core::traits::*;
|
||||||
/// # use cursive_core::views::{Dialog, EditView, TextView};
|
/// # use cursive_core::views::{Dialog, EditView, TextView};
|
||||||
/// let mut siv = Cursive::dummy();
|
/// let mut siv = Cursive::new();
|
||||||
///
|
///
|
||||||
/// // Create a dialog with an edit text and a button.
|
/// // Create a dialog with an edit text and a button.
|
||||||
/// // The user can either hit the <Ok> button,
|
/// // The user can either hit the <Ok> button,
|
||||||
|
@ -13,7 +13,7 @@ use crate::Printer;
|
|||||||
/// use cursive_core::views::{Button, EnableableView, Checkbox, LinearLayout};
|
/// use cursive_core::views::{Button, EnableableView, Checkbox, LinearLayout};
|
||||||
/// use cursive_core::traits::Identifiable;
|
/// use cursive_core::traits::Identifiable;
|
||||||
///
|
///
|
||||||
/// let mut siv = Cursive::dummy();
|
/// let mut siv = Cursive::new();
|
||||||
///
|
///
|
||||||
/// siv.add_layer(LinearLayout::vertical()
|
/// siv.add_layer(LinearLayout::vertical()
|
||||||
/// .child(EnableableView::new(Checkbox::new()).with_name("my_view"))
|
/// .child(EnableableView::new(Checkbox::new()).with_name("my_view"))
|
||||||
|
@ -40,7 +40,7 @@ use std::rc::Rc;
|
|||||||
/// .button("Quit", |s| s.quit()));
|
/// .button("Quit", |s| s.quit()));
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
/// let mut siv = Cursive::dummy();
|
/// let mut siv = Cursive::new();
|
||||||
/// siv.add_layer(Dialog::around(time_select)
|
/// siv.add_layer(Dialog::around(time_select)
|
||||||
/// .title("How long is your wait?"));
|
/// .title("How long is your wait?"));
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -176,7 +176,7 @@ impl TextContentInner {
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use cursive_core::Cursive;
|
/// # use cursive_core::Cursive;
|
||||||
/// # use cursive_core::views::TextView;
|
/// # use cursive_core::views::TextView;
|
||||||
/// let mut siv = Cursive::dummy();
|
/// let mut siv = Cursive::new();
|
||||||
///
|
///
|
||||||
/// siv.add_layer(TextView::new("Hello world!"));
|
/// siv.add_layer(TextView::new("Hello world!"));
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -236,15 +236,17 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for Backend {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
terminal::close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl backend::Backend for Backend {
|
impl backend::Backend for Backend {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"bear-lib-terminal"
|
"bear-lib-terminal"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(&mut self) {
|
|
||||||
terminal::close();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_color(&self, color: ColorPair) -> ColorPair {
|
fn set_color(&self, color: ColorPair) -> ColorPair {
|
||||||
let current = ColorPair {
|
let current = ColorPair {
|
||||||
front: blt_colour_to_colour(state::foreground()),
|
front: blt_colour_to_colour(state::foreground()),
|
||||||
|
@ -273,18 +273,8 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl backend::Backend for Backend {
|
impl Drop for Backend {
|
||||||
fn poll_event(&mut self) -> Option<Event> {
|
fn drop(&mut self) {
|
||||||
match poll(Duration::from_millis(1)) {
|
|
||||||
Ok(true) => match read() {
|
|
||||||
Ok(event) => Some(self.map_key(event)),
|
|
||||||
Err(e) => panic!("{:?}", e),
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(&mut self) {
|
|
||||||
// We have to execute the show cursor command at the `stdout`.
|
// We have to execute the show cursor command at the `stdout`.
|
||||||
execute!(
|
execute!(
|
||||||
io::stdout(),
|
io::stdout(),
|
||||||
@ -296,6 +286,18 @@ impl backend::Backend for Backend {
|
|||||||
|
|
||||||
disable_raw_mode().unwrap();
|
disable_raw_mode().unwrap();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl backend::Backend for Backend {
|
||||||
|
fn poll_event(&mut self) -> Option<Event> {
|
||||||
|
match poll(Duration::from_millis(1)) {
|
||||||
|
Ok(true) => match read() {
|
||||||
|
Ok(event) => Some(self.map_key(event)),
|
||||||
|
Err(e) => panic!("{:?}", e),
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn refresh(&mut self) {
|
fn refresh(&mut self) {
|
||||||
self.stdout_mut().flush().unwrap();
|
self.stdout_mut().flush().unwrap();
|
||||||
|
@ -14,7 +14,7 @@ use crate::theme::{Color, ColorPair, Effect};
|
|||||||
use crate::utf8;
|
use crate::utf8;
|
||||||
use crate::Vec2;
|
use crate::Vec2;
|
||||||
|
|
||||||
use self::super::split_i32;
|
use super::split_i32;
|
||||||
|
|
||||||
// Use AHash instead of the slower SipHash
|
// Use AHash instead of the slower SipHash
|
||||||
type HashMap<K, V> = std::collections::HashMap<K, V, ahash::RandomState>;
|
type HashMap<K, V> = std::collections::HashMap<K, V, ahash::RandomState>;
|
||||||
@ -321,6 +321,13 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for Backend {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
write_to_tty(b"\x1B[?1002l").unwrap();
|
||||||
|
ncurses::endwin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl backend::Backend for Backend {
|
impl backend::Backend for Backend {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"ncurses"
|
"ncurses"
|
||||||
@ -341,11 +348,6 @@ impl backend::Backend for Backend {
|
|||||||
self.parse_next()
|
self.parse_next()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(&mut self) {
|
|
||||||
write_to_tty(b"\x1B[?1002l").unwrap();
|
|
||||||
ncurses::endwin();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_color(&self, colors: ColorPair) -> ColorPair {
|
fn set_color(&self, colors: ColorPair) -> ColorPair {
|
||||||
// eprintln!("Color used: {:?}", colors);
|
// eprintln!("Color used: {:?}", colors);
|
||||||
let current = self.current_style.get();
|
let current = self.current_style.get();
|
||||||
|
@ -353,6 +353,14 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for Backend {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
print!("\x1B[?1002l");
|
||||||
|
stdout().flush().expect("could not flush stdout");
|
||||||
|
pancurses::endwin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl backend::Backend for Backend {
|
impl backend::Backend for Backend {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"pancurses"
|
"pancurses"
|
||||||
@ -368,12 +376,6 @@ impl backend::Backend for Backend {
|
|||||||
pancurses::has_colors()
|
pancurses::has_colors()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(&mut self) {
|
|
||||||
print!("\x1B[?1002l");
|
|
||||||
stdout().flush().expect("could not flush stdout");
|
|
||||||
pancurses::endwin();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_color(&self, colors: ColorPair) -> ColorPair {
|
fn set_color(&self, colors: ColorPair) -> ColorPair {
|
||||||
let current = self.current_style.get();
|
let current = self.current_style.get();
|
||||||
|
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
//! Dummy backend
|
|
||||||
|
|
||||||
use crate::backend;
|
|
||||||
use crate::event::Event;
|
|
||||||
use crate::theme;
|
|
||||||
use crate::Vec2;
|
|
||||||
|
|
||||||
/// Dummy backend that does nothing and immediately exits.
|
|
||||||
///
|
|
||||||
/// Mostly used for testing.
|
|
||||||
pub struct Backend;
|
|
||||||
|
|
||||||
impl Backend {
|
|
||||||
/// Creates a new dummy backend.
|
|
||||||
pub fn init() -> Box<dyn backend::Backend>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
Box::new(Backend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl backend::Backend for Backend {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"dummy"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(&mut self) {}
|
|
||||||
|
|
||||||
fn refresh(&mut self) {}
|
|
||||||
|
|
||||||
fn has_colors(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screen_size(&self) -> Vec2 {
|
|
||||||
(1, 1).into()
|
|
||||||
}
|
|
||||||
fn poll_event(&mut self) -> Option<Event> {
|
|
||||||
Some(Event::Exit)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_at(&self, _: Vec2, _: &str) {}
|
|
||||||
|
|
||||||
fn print_at_rep(&self, _pos: Vec2, _repetitions: usize, _text: &str) {}
|
|
||||||
|
|
||||||
fn clear(&self, _: theme::Color) {}
|
|
||||||
|
|
||||||
// This sets the Colours and returns the previous colours
|
|
||||||
// to allow you to set them back when you're done.
|
|
||||||
fn set_color(&self, colors: theme::ColorPair) -> theme::ColorPair {
|
|
||||||
// TODO: actually save a stack of colors?
|
|
||||||
colors
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_effect(&self, _: theme::Effect) {}
|
|
||||||
fn unset_effect(&self, _: theme::Effect) {}
|
|
||||||
}
|
|
@ -11,8 +11,6 @@
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
mod resize;
|
mod resize;
|
||||||
|
|
||||||
pub mod dummy;
|
|
||||||
|
|
||||||
pub mod blt;
|
pub mod blt;
|
||||||
pub mod crossterm;
|
pub mod crossterm;
|
||||||
pub mod curses;
|
pub mod curses;
|
||||||
|
@ -84,8 +84,6 @@ impl backend::Backend for Backend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(&mut self) {}
|
|
||||||
|
|
||||||
fn refresh(&mut self) {
|
fn refresh(&mut self) {
|
||||||
let size = self.size.get();
|
let size = self.size.get();
|
||||||
let current_frame =
|
let current_frame =
|
||||||
|
@ -203,12 +203,8 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl backend::Backend for Backend {
|
impl Drop for Backend {
|
||||||
fn name(&self) -> &str {
|
fn drop(&mut self) {
|
||||||
"termion"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(&mut self) {
|
|
||||||
write!(
|
write!(
|
||||||
self.terminal.get_mut(),
|
self.terminal.get_mut(),
|
||||||
"{}{}",
|
"{}{}",
|
||||||
@ -226,6 +222,12 @@ impl backend::Backend for Backend {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl backend::Backend for Backend {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"termion"
|
||||||
|
}
|
||||||
|
|
||||||
fn set_color(&self, color: theme::ColorPair) -> theme::ColorPair {
|
fn set_color(&self, color: theme::ColorPair) -> theme::ColorPair {
|
||||||
let current_style = self.current_style.get();
|
let current_style = self.current_style.get();
|
||||||
|
@ -7,26 +7,25 @@
|
|||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// use cursive::{Cursive, CursiveExt};
|
/// use cursive::{Cursive, CursiveExt};
|
||||||
///
|
///
|
||||||
/// // Use `Cursive::default()` to pick one of the enabled backends,
|
/// let mut siv = Cursive::new();
|
||||||
|
///
|
||||||
|
/// // Use `CursiveExt::run()` to pick one of the enabled backends,
|
||||||
/// // depending on cargo features.
|
/// // depending on cargo features.
|
||||||
/// let mut siv = Cursive::default();
|
/// siv.run();
|
||||||
///
|
///
|
||||||
/// // Or explicitly use a specific backend
|
/// // Or explicitly use a specific backend
|
||||||
/// #[cfg(feature = "ncurses-backend")]
|
/// #[cfg(feature = "ncurses-backend")]
|
||||||
/// let mut siv = Cursive::ncurses();
|
/// siv.run_ncurses().unwrap();
|
||||||
/// #[cfg(feature = "panncurses-backend")]
|
/// #[cfg(feature = "panncurses-backend")]
|
||||||
/// let mut siv = Cursive::pancurses();
|
/// siv.run_pancurses().unwrap();
|
||||||
/// #[cfg(feature = "termion-backend")]
|
/// #[cfg(feature = "termion-backend")]
|
||||||
/// let mut siv = Cursive::termion();
|
/// siv.run_termion().unwrap();
|
||||||
/// #[cfg(feature = "crossterm-backend")]
|
/// #[cfg(feature = "crossterm-backend")]
|
||||||
/// let mut siv = Cursive::crossterm();
|
/// siv.run_crossterm().unwrap();
|
||||||
/// #[cfg(feature = "blt-backend")]
|
/// #[cfg(feature = "blt-backend")]
|
||||||
/// let mut siv = Cursive::blt();
|
/// siv.run_blt();
|
||||||
/// ```
|
/// ```
|
||||||
pub trait CursiveExt {
|
pub trait CursiveExt {
|
||||||
/// Type of the returned cursive root.
|
|
||||||
type Cursive;
|
|
||||||
|
|
||||||
/// Tries to use one of the enabled backends.
|
/// Tries to use one of the enabled backends.
|
||||||
///
|
///
|
||||||
/// Will fallback to the dummy backend if no other backend feature is enabled.
|
/// Will fallback to the dummy backend if no other backend feature is enabled.
|
||||||
@ -34,73 +33,71 @@ pub trait CursiveExt {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If the backend initialization fails.
|
/// If the backend initialization fails.
|
||||||
fn default() -> Self::Cursive;
|
fn run(&mut self);
|
||||||
|
|
||||||
/// Creates a new Cursive root using a ncurses backend.
|
/// Creates a new Cursive root using a ncurses backend.
|
||||||
#[cfg(feature = "ncurses-backend")]
|
#[cfg(feature = "ncurses-backend")]
|
||||||
fn ncurses() -> std::io::Result<Self::Cursive>;
|
fn run_ncurses(&mut self) -> std::io::Result<()>;
|
||||||
|
|
||||||
/// Creates a new Cursive root using a pancurses backend.
|
/// Creates a new Cursive root using a pancurses backend.
|
||||||
#[cfg(feature = "pancurses-backend")]
|
#[cfg(feature = "pancurses-backend")]
|
||||||
fn pancurses() -> std::io::Result<Self::Cursive>;
|
fn run_pancurses(&mut self) -> std::io::Result<()>;
|
||||||
|
|
||||||
/// Creates a new Cursive root using a termion backend.
|
/// Creates a new Cursive root using a termion backend.
|
||||||
#[cfg(feature = "termion-backend")]
|
#[cfg(feature = "termion-backend")]
|
||||||
fn termion() -> std::io::Result<Self::Cursive>;
|
fn run_termion(&mut self) -> std::io::Result<()>;
|
||||||
|
|
||||||
/// Creates a new Cursive root using a crossterm backend.
|
/// Creates a new Cursive root using a crossterm backend.
|
||||||
#[cfg(feature = "crossterm-backend")]
|
#[cfg(feature = "crossterm-backend")]
|
||||||
fn crossterm() -> Result<Self::Cursive, crossterm::ErrorKind>;
|
fn run_crossterm(&mut self) -> Result<(), crossterm::ErrorKind>;
|
||||||
|
|
||||||
/// Creates a new Cursive root using a bear-lib-terminal backend.
|
/// Creates a new Cursive root using a bear-lib-terminal backend.
|
||||||
#[cfg(feature = "blt-backend")]
|
#[cfg(feature = "blt-backend")]
|
||||||
fn blt() -> Self::Cursive;
|
fn run_blt(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CursiveExt for cursive_core::Cursive {
|
impl CursiveExt for cursive_core::Cursive {
|
||||||
type Cursive = Self;
|
fn run(&mut self) {
|
||||||
|
|
||||||
fn default() -> Self::Cursive {
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(feature = "blt-backend")] {
|
if #[cfg(feature = "blt-backend")] {
|
||||||
Self::blt()
|
self.run_blt()
|
||||||
} else if #[cfg(feature = "termion-backend")] {
|
} else if #[cfg(feature = "termion-backend")] {
|
||||||
Self::termion().unwrap()
|
self.run_termion().unwrap()
|
||||||
} else if #[cfg(feature = "crossterm-backend")] {
|
} else if #[cfg(feature = "crossterm-backend")] {
|
||||||
Self::crossterm().unwrap()
|
self.run_crossterm().unwrap()
|
||||||
} else if #[cfg(feature = "pancurses-backend")] {
|
} else if #[cfg(feature = "pancurses-backend")] {
|
||||||
Self::pancurses().unwrap()
|
self.run_pancurses().unwrap()
|
||||||
} else if #[cfg(feature = "ncurses-backend")] {
|
} else if #[cfg(feature = "ncurses-backend")] {
|
||||||
Self::ncurses().unwrap()
|
self.run_ncurses().unwrap()
|
||||||
} else {
|
} else {
|
||||||
log::warn!("No built-it backend, falling back to Cursive::dummy().");
|
log::warn!("No built-it backend, falling back to Cursive::dummy().");
|
||||||
Self::dummy()
|
self.run_dummy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ncurses-backend")]
|
#[cfg(feature = "ncurses-backend")]
|
||||||
fn ncurses() -> std::io::Result<Self> {
|
fn run_ncurses(&mut self) -> std::io::Result<()> {
|
||||||
Self::try_new(crate::backends::curses::n::Backend::init)
|
self.try_run_with(crate::backends::curses::n::Backend::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "pancurses-backend")]
|
#[cfg(feature = "pancurses-backend")]
|
||||||
fn pancurses() -> std::io::Result<Self> {
|
fn run_pancurses(&mut self) -> std::io::Result<()> {
|
||||||
Self::try_new(crate::backends::curses::pan::Backend::init)
|
self.try_run_with(crate::backends::curses::pan::Backend::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "termion-backend")]
|
#[cfg(feature = "termion-backend")]
|
||||||
fn termion() -> std::io::Result<Self> {
|
fn run_termion(&mut self) -> std::io::Result<()> {
|
||||||
Self::try_new(crate::backends::termion::Backend::init)
|
self.try_run_with(crate::backends::termion::Backend::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "crossterm-backend")]
|
#[cfg(feature = "crossterm-backend")]
|
||||||
fn crossterm() -> Result<Self, crossterm::ErrorKind> {
|
fn run_crossterm(&mut self) -> Result<(), crossterm::ErrorKind> {
|
||||||
Self::try_new(crate::backends::crossterm::Backend::init)
|
self.try_run_with(crate::backends::crossterm::Backend::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "blt-backend")]
|
#[cfg(feature = "blt-backend")]
|
||||||
fn blt() -> Self {
|
fn run_blt(&mut self) {
|
||||||
Self::new(crate::backends::blt::Backend::init)
|
self.run_with(crate::backends::blt::Backend::init)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
143
cursive/src/cursive_runnable.rs
Normal file
143
cursive/src/cursive_runnable.rs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
use crate::{backend, backends, Cursive};
|
||||||
|
|
||||||
|
/// A runnable wrapper around `Cursive`, bundling the backend initializer.
|
||||||
|
///
|
||||||
|
/// This struct embeds both `Cursive` and a backend-initializer
|
||||||
|
/// (`FnMut() -> Result<dyn Backend>`), to provide a simple `.run()` method.
|
||||||
|
///
|
||||||
|
/// This lets you pick the backend when creating the Cursive root, rather than
|
||||||
|
/// when running it.
|
||||||
|
///
|
||||||
|
/// It implements `DerefMut<Target=Cursive>`, so you can use it just like a
|
||||||
|
/// regular `Cursive` object.
|
||||||
|
pub struct CursiveRunnable {
|
||||||
|
siv: Cursive,
|
||||||
|
backend_init: Box<
|
||||||
|
dyn FnMut() -> Result<
|
||||||
|
Box<dyn backend::Backend>,
|
||||||
|
Box<dyn std::error::Error>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for CursiveRunnable {
|
||||||
|
type Target = Cursive;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Cursive {
|
||||||
|
&self.siv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::DerefMut for CursiveRunnable {
|
||||||
|
fn deref_mut(&mut self) -> &mut Cursive {
|
||||||
|
&mut self.siv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn boxed(e: impl std::error::Error + 'static) -> Box<dyn std::error::Error> {
|
||||||
|
Box::new(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CursiveRunnable {
|
||||||
|
/// Creates a new Cursive wrapper, using the given backend.
|
||||||
|
pub fn new<E, F>(mut backend_init: F) -> Self
|
||||||
|
where
|
||||||
|
E: std::error::Error + 'static,
|
||||||
|
F: FnMut() -> Result<Box<dyn backend::Backend>, E> + 'static,
|
||||||
|
{
|
||||||
|
let siv = Cursive::new();
|
||||||
|
let backend_init = Box::new(move || backend_init().map_err(boxed));
|
||||||
|
Self { siv, backend_init }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the event loop with the registered backend initializer.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the backend initialization fails.
|
||||||
|
pub fn run(&mut self) {
|
||||||
|
self.try_run().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the event loop with the registered backend initializer.
|
||||||
|
pub fn try_run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
self.siv.try_run_with(&mut self.backend_init)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new Cursive wrapper using the dummy backend.
|
||||||
|
///
|
||||||
|
/// Nothing will actually be output when calling `.run()`.
|
||||||
|
pub fn dummy() -> Self {
|
||||||
|
Self::new::<std::convert::Infallible, _>(|| {
|
||||||
|
Ok(cursive_core::backend::Dummy::init())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new Cursive wrapper using the ncurses backend.
|
||||||
|
///
|
||||||
|
/// _Requires the `ncurses-backend` feature._
|
||||||
|
#[cfg(feature = "ncurses-backend")]
|
||||||
|
pub fn ncurses() -> Self {
|
||||||
|
Self::new(backends::curses::n::Backend::init)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new Cursive wrapper using the panncurses backend.
|
||||||
|
///
|
||||||
|
/// _Requires the `panncurses-backend` feature._
|
||||||
|
#[cfg(feature = "pancurses-backend")]
|
||||||
|
pub fn pancurses() -> Self {
|
||||||
|
Self::new(backends::curses::pan::Backend::init)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new Cursive wrapper using the termion backend.
|
||||||
|
///
|
||||||
|
/// _Requires the `termion-backend` feature._
|
||||||
|
#[cfg(feature = "termion-backend")]
|
||||||
|
pub fn termion() -> Self {
|
||||||
|
Self::new(backends::termion::Backend::init)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new Cursive wrapper using the crossterm backend.
|
||||||
|
///
|
||||||
|
/// _Requires the `crossterm-backend` feature._
|
||||||
|
#[cfg(feature = "crossterm-backend")]
|
||||||
|
pub fn crossterm() -> Self {
|
||||||
|
Self::new(backends::crossterm::Backend::init)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new Cursive wrapper using the bear-lib-terminal backend.
|
||||||
|
///
|
||||||
|
/// _Requires the `blt-backend` feature._
|
||||||
|
#[cfg(feature = "blt-backend")]
|
||||||
|
pub fn blt() -> Self {
|
||||||
|
Self::new(|| Ok(backends::blt::Backend::init()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new Cursive wrapper using one of the available backends.
|
||||||
|
///
|
||||||
|
/// Picks the first backend enabled from the list:
|
||||||
|
/// * BearLibTerminal
|
||||||
|
/// * Termion
|
||||||
|
/// * Crossterm
|
||||||
|
/// * Pancurses
|
||||||
|
/// * Ncurses
|
||||||
|
/// * Dummy
|
||||||
|
pub fn default() -> Self {
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "blt-backend")] {
|
||||||
|
Self::blt()
|
||||||
|
} else if #[cfg(feature = "termion-backend")] {
|
||||||
|
Self::termion()
|
||||||
|
} else if #[cfg(feature = "crossterm-backend")] {
|
||||||
|
Self::crossterm()
|
||||||
|
} else if #[cfg(feature = "pancurses-backend")] {
|
||||||
|
Self::pancurses()
|
||||||
|
} else if #[cfg(feature = "ncurses-backend")] {
|
||||||
|
Self::ncurses()
|
||||||
|
} else {
|
||||||
|
log::warn!("No built-it backend, falling back to Cursive::dummy().");
|
||||||
|
Self::dummy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,21 @@
|
|||||||
//! views and configuring their behaviours.
|
//! views and configuring their behaviours.
|
||||||
//! * Finally, the event loop is started by calling [`Cursive::run`].
|
//! * Finally, the event loop is started by calling [`Cursive::run`].
|
||||||
//!
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! use cursive::{Cursive, CursiveExt};
|
||||||
|
//! use cursive::views::TextView;
|
||||||
|
//!
|
||||||
|
//! let mut siv = Cursive::new();
|
||||||
|
//!
|
||||||
|
//! siv.add_layer(TextView::new("Hello World!\nPress q to quit."));
|
||||||
|
//!
|
||||||
|
//! siv.add_global_callback('q', |s| s.quit());
|
||||||
|
//!
|
||||||
|
//! siv.run();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
//! ## Views
|
//! ## Views
|
||||||
//!
|
//!
|
||||||
//! Views are the main components of a cursive interface.
|
//! Views are the main components of a cursive interface.
|
||||||
@ -29,21 +44,6 @@
|
|||||||
//! events. These functions usually take an `&mut Cursive` argument, allowing
|
//! events. These functions usually take an `&mut Cursive` argument, allowing
|
||||||
//! them to modify the view tree at will.
|
//! them to modify the view tree at will.
|
||||||
//!
|
//!
|
||||||
//! ## Examples
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! use cursive::Cursive;
|
|
||||||
//! use cursive::views::TextView;
|
|
||||||
//!
|
|
||||||
//! let mut siv = Cursive::dummy();
|
|
||||||
//!
|
|
||||||
//! siv.add_layer(TextView::new("Hello World!\nPress q to quit."));
|
|
||||||
//!
|
|
||||||
//! siv.add_global_callback('q', |s| s.quit());
|
|
||||||
//!
|
|
||||||
//! siv.run();
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ## Debugging
|
//! ## Debugging
|
||||||
//!
|
//!
|
||||||
//! The `Cursive` root initializes the terminal on creation, and does cleanups
|
//! The `Cursive` root initializes the terminal on creation, and does cleanups
|
||||||
@ -71,7 +71,10 @@ mod utf8;
|
|||||||
pub mod backends;
|
pub mod backends;
|
||||||
|
|
||||||
mod cursive_ext;
|
mod cursive_ext;
|
||||||
|
mod cursive_runnable;
|
||||||
|
|
||||||
pub use cursive_ext::CursiveExt;
|
pub use cursive_ext::CursiveExt;
|
||||||
|
pub use cursive_runnable::CursiveRunnable;
|
||||||
|
|
||||||
/// Creates a new Cursive root using one of the enabled backends.
|
/// Creates a new Cursive root using one of the enabled backends.
|
||||||
///
|
///
|
||||||
@ -87,43 +90,43 @@ pub use cursive_ext::CursiveExt;
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If the backend initialization fails.
|
/// If the backend initialization fails.
|
||||||
pub fn default() -> Cursive {
|
pub fn default() -> CursiveRunnable {
|
||||||
Cursive::default()
|
CursiveRunnable::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a ncurses backend.
|
/// Creates a new Cursive root using a ncurses backend.
|
||||||
#[cfg(feature = "ncurses-backend")]
|
#[cfg(feature = "ncurses-backend")]
|
||||||
pub fn ncurses() -> std::io::Result<Cursive> {
|
pub fn ncurses() -> CursiveRunnable {
|
||||||
Cursive::ncurses()
|
CursiveRunnable::ncurses()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a pancurses backend.
|
/// Creates a new Cursive root using a pancurses backend.
|
||||||
#[cfg(feature = "pancurses-backend")]
|
#[cfg(feature = "pancurses-backend")]
|
||||||
pub fn pancurses() -> std::io::Result<Cursive> {
|
pub fn pancurses() -> CursiveRunnable {
|
||||||
Cursive::pancurses()
|
CursiveRunnable::pancurses()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a termion backend.
|
/// Creates a new Cursive root using a termion backend.
|
||||||
#[cfg(feature = "termion-backend")]
|
#[cfg(feature = "termion-backend")]
|
||||||
pub fn termion() -> std::io::Result<Cursive> {
|
pub fn termion() -> CursiveRunnable {
|
||||||
Cursive::termion()
|
CursiveRunnable::termion()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a crossterm backend.
|
/// Creates a new Cursive root using a crossterm backend.
|
||||||
#[cfg(feature = "crossterm-backend")]
|
#[cfg(feature = "crossterm-backend")]
|
||||||
pub fn crossterm() -> Result<Cursive, crossterm::ErrorKind> {
|
pub fn crossterm() -> CursiveRunnable {
|
||||||
Cursive::crossterm()
|
CursiveRunnable::crossterm()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a bear-lib-terminal backend.
|
/// Creates a new Cursive root using a bear-lib-terminal backend.
|
||||||
#[cfg(feature = "blt-backend")]
|
#[cfg(feature = "blt-backend")]
|
||||||
pub fn blt() -> Cursive {
|
pub fn blt() -> CursiveRunnable {
|
||||||
Cursive::blt()
|
CursiveRunnable::blt()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a dummy backend.
|
/// Creates a new Cursive root using a dummy backend.
|
||||||
///
|
///
|
||||||
/// Nothing will be output. This is mostly here for tests.
|
/// Nothing will be output. This is mostly here for tests.
|
||||||
pub fn dummy() -> Cursive {
|
pub fn dummy() -> CursiveRunnable {
|
||||||
Cursive::dummy()
|
CursiveRunnable::dummy()
|
||||||
}
|
}
|
||||||
|
21
examples/src/bin/ctrl_c.rs
Normal file
21
examples/src/bin/ctrl_c.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use cursive::views;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut siv = cursive::default();
|
||||||
|
|
||||||
|
siv.clear_global_callbacks(cursive::event::Event::CtrlChar('c'));
|
||||||
|
|
||||||
|
siv.set_on_pre_event(cursive::event::Event::CtrlChar('c'), |s| {
|
||||||
|
s.add_layer(
|
||||||
|
views::Dialog::text("Do you want to quit?")
|
||||||
|
.button("Yes", |s| s.quit())
|
||||||
|
.button("No", |s| {
|
||||||
|
s.pop_layer();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
siv.add_layer(views::Dialog::text("Try pressing Ctrl-C!"));
|
||||||
|
|
||||||
|
siv.run();
|
||||||
|
}
|
@ -24,7 +24,7 @@ pub mod tests {
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
pub struct BasicSetup {
|
pub struct BasicSetup {
|
||||||
siv: Cursive,
|
siv: CursiveRunner<Cursive>,
|
||||||
screen_stream: crossbeam_channel::Receiver<ObservedScreen>,
|
screen_stream: crossbeam_channel::Receiver<ObservedScreen>,
|
||||||
input: crossbeam_channel::Sender<Option<Event>>,
|
input: crossbeam_channel::Sender<Option<Event>>,
|
||||||
last_screen: RefCell<Option<ObservedScreen>>,
|
last_screen: RefCell<Option<ObservedScreen>>,
|
||||||
@ -61,7 +61,7 @@ pub mod tests {
|
|||||||
let backend = backends::puppet::Backend::init(Some(size));
|
let backend = backends::puppet::Backend::init(Some(size));
|
||||||
let sink = backend.stream();
|
let sink = backend.stream();
|
||||||
let input = backend.input();
|
let input = backend.input();
|
||||||
let mut siv = Cursive::new(|| backend);
|
let mut siv = Cursive::new().into_runner(backend);
|
||||||
|
|
||||||
// Let's add a ResizedView to keep the list at a reasonable size
|
// Let's add a ResizedView to keep the list at a reasonable size
|
||||||
// (it can scroll anyway).
|
// (it can scroll anyway).
|
||||||
|
Loading…
Reference in New Issue
Block a user