Render Signal
App now has a renderer object that can be retrieved to send a render signal from outside the application. This is useful for rendering based on operations taking place in other threads.
This commit is contained in:
parent
ba7703e4c2
commit
82e044a34a
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "arkham"
|
name = "arkham"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
|
@ -37,6 +37,6 @@ path = "examples/paragraph.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.71"
|
anyhow = "1.0.71"
|
||||||
crossterm = "0.26.1"
|
crossterm = "0.27"
|
||||||
ctrlc = "3.3.1"
|
ctrlc = "3.3.1"
|
||||||
textwrap = "0.16.0"
|
textwrap = "0.16.0"
|
||||||
|
|
69
src/app.rs
69
src/app.rs
|
@ -1,4 +1,12 @@
|
||||||
use std::{any::Any, cell::RefCell, io::Write, marker::PhantomData, rc::Rc};
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
cell::RefCell,
|
||||||
|
io::Write,
|
||||||
|
marker::PhantomData,
|
||||||
|
rc::Rc,
|
||||||
|
sync::mpsc::{channel, Receiver, Sender},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor,
|
cursor,
|
||||||
|
@ -14,6 +22,17 @@ use crate::{
|
||||||
|
|
||||||
use super::input::Keyboard;
|
use super::input::Keyboard;
|
||||||
|
|
||||||
|
/// A renderer that can signal a render needs to take place.
|
||||||
|
pub struct Renderer {
|
||||||
|
tx: Sender<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Renderer {
|
||||||
|
pub fn render(&self) {
|
||||||
|
let _ = self.tx.send(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The app is the core container for the application logic, resources,
|
/// The app is the core container for the application logic, resources,
|
||||||
/// state, and run loop.
|
/// state, and run loop.
|
||||||
pub struct App<F, Args>
|
pub struct App<F, Args>
|
||||||
|
@ -23,6 +42,8 @@ where
|
||||||
{
|
{
|
||||||
container: Rc<RefCell<Container>>,
|
container: Rc<RefCell<Container>>,
|
||||||
main_view: View,
|
main_view: View,
|
||||||
|
render_signal: Receiver<()>,
|
||||||
|
render_tx: Sender<()>,
|
||||||
root: F,
|
root: F,
|
||||||
args: PhantomData<Args>,
|
args: PhantomData<Args>,
|
||||||
}
|
}
|
||||||
|
@ -39,14 +60,25 @@ where
|
||||||
let container = Rc::new(RefCell::new(Container::default()));
|
let container = Rc::new(RefCell::new(Container::default()));
|
||||||
let size = terminal::size().unwrap();
|
let size = terminal::size().unwrap();
|
||||||
let main_view = View::new(size);
|
let main_view = View::new(size);
|
||||||
|
let (render_tx, render_signal) = channel();
|
||||||
App {
|
App {
|
||||||
container,
|
container,
|
||||||
root,
|
root,
|
||||||
main_view,
|
main_view,
|
||||||
|
render_tx,
|
||||||
|
render_signal,
|
||||||
args: PhantomData,
|
args: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a renderer that can signal the application to rerender. This
|
||||||
|
/// renderer can be cloned and passed between threads.
|
||||||
|
pub fn get_renderer(&self) -> Renderer {
|
||||||
|
Renderer {
|
||||||
|
tx: self.render_tx.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Insert a resource which can be injected into component functions.
|
/// Insert a resource which can be injected into component functions.
|
||||||
///
|
///
|
||||||
/// This resource can only be accessed immutably by reference.
|
/// This resource can only be accessed immutably by reference.
|
||||||
|
@ -146,24 +178,29 @@ where
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.reset();
|
.reset();
|
||||||
|
|
||||||
if let Ok(event) = crossterm::event::read() {
|
if crossterm::event::poll(Duration::from_millis(100)).unwrap_or(false) {
|
||||||
match event {
|
if let Ok(event) = crossterm::event::read() {
|
||||||
Event::FocusGained => todo!(),
|
match event {
|
||||||
Event::FocusLost => todo!(),
|
Event::FocusGained => self.render()?,
|
||||||
Event::Key(key_event) if key_event.code == KeyCode::Char('q') => {
|
Event::FocusLost => {}
|
||||||
break;
|
Event::Key(key_event) if key_event.code == KeyCode::Char('q') => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
|
||||||
|
let container = self.container.borrow();
|
||||||
|
let kb = container.get::<Res<Keyboard>>().unwrap();
|
||||||
|
kb.set_key(key_event.code);
|
||||||
|
}
|
||||||
|
Event::Mouse(_) => todo!(),
|
||||||
|
Event::Paste(_) => todo!(),
|
||||||
|
Event::Resize(_, _) => self.render()?,
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
|
|
||||||
let container = self.container.borrow();
|
|
||||||
let kb = container.get::<Res<Keyboard>>().unwrap();
|
|
||||||
kb.set_key(key_event.code);
|
|
||||||
}
|
|
||||||
Event::Mouse(_) => todo!(),
|
|
||||||
Event::Paste(_) => todo!(),
|
|
||||||
Event::Resize(_, _) => todo!(),
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.render_signal.try_recv().is_ok() {
|
||||||
|
self.render()?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.teardown();
|
self.teardown();
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use crossterm::style::Color;
|
|
||||||
|
|
||||||
use crate::prelude::{Callable, ToRuneExt};
|
use crate::prelude::{Callable, ToRuneExt};
|
||||||
|
use crossterm::style::Color;
|
||||||
|
use std::ops::Deref;
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Paragraph {
|
pub struct Paragraph {
|
||||||
content: String,
|
content: String,
|
||||||
|
@ -29,7 +27,7 @@ impl Callable<()> for Paragraph {
|
||||||
let lines = textwrap::wrap(&self.content, view.width());
|
let lines = textwrap::wrap(&self.content, view.width());
|
||||||
let mut stack = view.vertical_stack(view.size());
|
let mut stack = view.vertical_stack(view.size());
|
||||||
for line in lines.iter() {
|
for line in lines.iter() {
|
||||||
let l = line.deref().to_runes();
|
let _ = line.deref().to_runes();
|
||||||
|
|
||||||
stack.insert(line);
|
stack.insert(line);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ mod view;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::{
|
pub use super::{
|
||||||
app::{App, Terminal},
|
app::{App, Renderer, Terminal},
|
||||||
container::{Callable, FromContainer, Res, State},
|
container::{Callable, FromContainer, Res, State},
|
||||||
context::ViewContext,
|
context::ViewContext,
|
||||||
geometry::{Pos, Rect, Size},
|
geometry::{Pos, Rect, Size},
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
#[allow(dead_code)]
|
||||||
pub struct KeybindPlugin;
|
pub struct KeybindPlugin;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
#[cfg(any(not(windows), all(windows)))]
|
|
||||||
mod universal {
|
mod universal {
|
||||||
pub const TICK: char = '✔';
|
pub const TICK: char = '✔';
|
||||||
pub const CROSS: char = '✖';
|
pub const CROSS: char = '✖';
|
||||||
|
@ -61,11 +60,9 @@ mod universal {
|
||||||
pub const SEVEN_EIGHTHS: char = '⅞';
|
pub const SEVEN_EIGHTHS: char = '⅞';
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(not(windows), all(windows)))]
|
|
||||||
pub use universal::*;
|
pub use universal::*;
|
||||||
|
|
||||||
#[cfg(all(windows))]
|
pub mod win {
|
||||||
mod win {
|
|
||||||
pub const TICK: char = '√';
|
pub const TICK: char = '√';
|
||||||
pub const CROSS: char = '×';
|
pub const CROSS: char = '×';
|
||||||
pub const STAR: char = '*';
|
pub const STAR: char = '*';
|
||||||
|
@ -89,6 +86,3 @@ mod win {
|
||||||
pub const QUESTION_MARK_PREFIX: char = '?';
|
pub const QUESTION_MARK_PREFIX: char = '?';
|
||||||
pub const ONE_HALF: char = ' ';
|
pub const ONE_HALF: char = ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(windows))]
|
|
||||||
pub use win::*;
|
|
||||||
|
|
Loading…
Reference in New Issue