mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-24 01:46:31 +00:00
230 lines
6.3 KiB
Rust
230 lines
6.3 KiB
Rust
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]: CursiveRunner::run()
|
|
/// [2]: CursiveRunner::step()
|
|
/// [3]: CursiveRunner::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]: CursiveRunner::run()
|
|
/// [2]: CursiveRunner::step()
|
|
/// [3]: CursiveRunner::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();
|
|
}
|
|
}
|
|
}
|