Working UI engine
This commit is contained in:
+25
-77
@@ -1,100 +1,48 @@
|
||||
use std::{any::Any, sync::Arc};
|
||||
use std::any::Any;
|
||||
|
||||
use tokio::sync::{Mutex, MutexGuard, mpsc};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
pub mod layout;
|
||||
pub mod render;
|
||||
pub mod state;
|
||||
pub mod unit;
|
||||
pub mod widget;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Context {
|
||||
tx: mpsc::Sender<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
pub struct Refresh;
|
||||
|
||||
#[allow(async_fn_in_trait)]
|
||||
pub trait App {
|
||||
// State
|
||||
#[allow(async_fn_in_trait)]
|
||||
async fn init(&mut self, ctx: Context);
|
||||
#[allow(async_fn_in_trait)]
|
||||
async fn init(&mut self, ctx: state::Context);
|
||||
async fn update(&mut self, event: Box<dyn Any>);
|
||||
|
||||
// Rendering
|
||||
fn layout(&self) -> layout::Allocation;
|
||||
fn render(&mut self, layout: layout::Allocation);
|
||||
async fn layout(&self) -> layout::LayoutItem;
|
||||
async fn render(&mut self, allocation: layout::Allocation);
|
||||
}
|
||||
|
||||
pub struct AppHandler<A: App> {
|
||||
pub app: A,
|
||||
}
|
||||
pub async fn run<A: App>(create_app: impl FnOnce(&state::Context) -> A) {
|
||||
let (tx, mut rx) = mpsc::channel(100);
|
||||
|
||||
impl<A: App> AppHandler<A> {
|
||||
pub async fn run(&mut self) {
|
||||
let (tx, mut rx) = mpsc::channel(100);
|
||||
let ctx = state::Context { tx };
|
||||
|
||||
self.app.init(Context { tx }).await;
|
||||
let mut app = create_app(&ctx);
|
||||
|
||||
self.app.render(self.app.layout());
|
||||
app.init(ctx).await;
|
||||
|
||||
while let Some(event) = rx.recv().await {
|
||||
self.app.update(event).await;
|
||||
self.app.render(self.app.layout());
|
||||
}
|
||||
app.render(get_screen().allocate(&app.layout().await)).await;
|
||||
|
||||
while let Some(event) = rx.recv().await {
|
||||
app.update(event).await;
|
||||
app.render(get_screen().allocate(&app.layout().await)).await;
|
||||
}
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn use_state<T>(&self, v: T) -> State<T> {
|
||||
State {
|
||||
value: Arc::new(Mutex::new(v)),
|
||||
tx: self.tx.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State<T> {
|
||||
pub value: Arc<Mutex<T>>,
|
||||
tx: mpsc::Sender<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
pub struct StateGuard<'a, T> {
|
||||
is_mutated: bool,
|
||||
value: MutexGuard<'a, T>,
|
||||
tx: &'a mpsc::Sender<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
impl<T> State<T> {
|
||||
pub async fn lock<'a>(&'a self) -> StateGuard<'a, T> {
|
||||
StateGuard {
|
||||
value: self.value.lock().await,
|
||||
is_mutated: false,
|
||||
tx: &self.tx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> std::ops::Deref for StateGuard<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> std::ops::DerefMut for StateGuard<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.is_mutated = true;
|
||||
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for StateGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
if self.is_mutated {
|
||||
self.tx.blocking_send(Box::new(Refresh)).unwrap()
|
||||
}
|
||||
pub fn get_screen() -> unit::Rect {
|
||||
let (width, height) = crossterm::terminal::size().unwrap();
|
||||
|
||||
unit::Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
use std::{any::Any, sync::Arc};
|
||||
|
||||
use tokio::sync::{Mutex, MutexGuard, mpsc::Sender};
|
||||
|
||||
pub struct Refresh;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Context {
|
||||
pub(crate) tx: Sender<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
pub struct State<T> {
|
||||
pub value: Arc<Mutex<T>>,
|
||||
tx: Sender<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
pub struct StateGuard<'a, T> {
|
||||
is_mutated: bool,
|
||||
value: MutexGuard<'a, T>,
|
||||
tx: &'a Sender<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn use_state<T>(&self, v: T) -> State<T> {
|
||||
State {
|
||||
value: Arc::new(Mutex::new(v)),
|
||||
tx: self.tx.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> State<T> {
|
||||
pub async fn lock<'a>(&'a self) -> StateGuard<'a, T> {
|
||||
StateGuard {
|
||||
value: self.value.lock().await,
|
||||
is_mutated: false,
|
||||
tx: &self.tx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> std::ops::Deref for StateGuard<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> std::ops::DerefMut for StateGuard<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.is_mutated = true;
|
||||
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for StateGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
if self.is_mutated {
|
||||
self.tx.blocking_send(Box::new(Refresh)).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user