Testing
This commit is contained in:
+10
-29
@@ -1,34 +1,15 @@
|
|||||||
use osui::{state::use_state, Context, Event, Node};
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct ClickEvent;
|
use osui::prelude::*;
|
||||||
|
|
||||||
impl Event for ClickEvent {
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn app(cx: &mut Context) -> Node {
|
|
||||||
// Hooks
|
|
||||||
let count = use_state(0);
|
|
||||||
cx.event({
|
|
||||||
let count = count.clone();
|
|
||||||
move |_: &ClickEvent| {
|
|
||||||
**count.get() += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Box::new(move |renderer| {
|
|
||||||
renderer
|
|
||||||
.draw_text(&format!("Count: {count}"), 0, 0)
|
|
||||||
.unwrap();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut osui = osui::Osui::new(Box::new(app));
|
let mut cx = Context::new(app);
|
||||||
loop {
|
cx.refresh();
|
||||||
osui.render().unwrap();
|
// println!("{:?}", cx.get_nodes());
|
||||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn app(cx: &mut Context) -> Vec<Node> {
|
||||||
|
let count = use_state(0);
|
||||||
|
|
||||||
|
vec![Node::String(Arc::new(move || format!("{count}")))]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::Node;
|
||||||
|
|
||||||
|
pub type Component = Arc<dyn Fn(&mut Context) -> Vec<Node>>;
|
||||||
|
|
||||||
|
pub struct Context {
|
||||||
|
component: Component,
|
||||||
|
nodes: Vec<Node>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new<F: Fn(&mut Context) -> Vec<Node> + 'static>(component: F) -> Self {
|
||||||
|
Self {
|
||||||
|
component: Arc::new(component),
|
||||||
|
nodes: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh(&mut self) {
|
||||||
|
self.nodes = (self.component.clone())(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_nodes(&self) -> Vec<Node> {
|
||||||
|
self.nodes.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
pub type Result<T> = std::result::Result<T, OsuiError>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum OsuiError {
|
|
||||||
Io(std::io::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for OsuiError {
|
|
||||||
fn from(e: std::io::Error) -> Self {
|
|
||||||
OsuiError::Io(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+10
-68
@@ -1,74 +1,16 @@
|
|||||||
use std::any::Any;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::renderer::{OsuiRenderer, Renderer};
|
pub mod component;
|
||||||
|
|
||||||
pub mod error;
|
|
||||||
pub mod renderer;
|
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
|
||||||
pub use error::Result;
|
pub mod prelude {
|
||||||
|
pub use crate::component::*;
|
||||||
pub type Node = Box<dyn Fn(&mut dyn Renderer)>;
|
pub use crate::state::*;
|
||||||
pub type Component = Box<dyn Fn(&mut Context) -> Node>;
|
pub use crate::Node;
|
||||||
|
|
||||||
pub trait Event {
|
|
||||||
fn as_any(&self) -> &dyn Any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Context {
|
#[derive(Clone)]
|
||||||
event_handlers: Vec<Box<dyn Fn(&dyn Event)>>,
|
pub enum Node {
|
||||||
}
|
String(Arc<dyn Fn() -> String>),
|
||||||
|
Component(component::Component),
|
||||||
pub struct Osui {
|
|
||||||
node: Node,
|
|
||||||
renderer: Option<Box<dyn Renderer>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Osui {
|
|
||||||
pub fn new(component: Component) -> Self {
|
|
||||||
let mut cx = Context {
|
|
||||||
event_handlers: Vec::new(),
|
|
||||||
};
|
|
||||||
Osui {
|
|
||||||
node: component(&mut cx),
|
|
||||||
renderer: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn with_renderer(component: Component, renderer: Box<dyn Renderer>) -> Self {
|
|
||||||
let mut cx = Context {
|
|
||||||
event_handlers: Vec::new(),
|
|
||||||
};
|
|
||||||
Osui {
|
|
||||||
node: component(&mut cx),
|
|
||||||
renderer: Some(renderer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(&mut self) -> Result<()> {
|
|
||||||
renderer::utils::clear()?;
|
|
||||||
if let Some(renderer) = &mut self.renderer {
|
|
||||||
(self.node)(renderer.as_mut());
|
|
||||||
} else {
|
|
||||||
(self.node)(&mut OsuiRenderer);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
pub fn event<E: Event + 'static, F: Fn(&E) + 'static>(&mut self, handler: F) {
|
|
||||||
self.event_handlers.push(Box::new(move |e| {
|
|
||||||
if let Some(e) = e.as_any().downcast_ref() {
|
|
||||||
(handler)(e)
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn global_event<E: Event + 'static, F: Fn(&E) + 'static>(&mut self, handler: F) {
|
|
||||||
self.event_handlers.push(Box::new(move |e| {
|
|
||||||
if let Some(e) = e.as_any().downcast_ref() {
|
|
||||||
(handler)(e)
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
pub(crate) mod utils {
|
|
||||||
//! The `utils` module provides utility functions for terminal manipulation
|
|
||||||
//! and string operations, designed to enhance terminal-based applications.
|
|
||||||
|
|
||||||
use std::io::{self, Write};
|
|
||||||
|
|
||||||
/// Clears the terminal screen and moves the cursor to the top-left corner.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// A `io::Result<()>` indicating whether the operation succeeded.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// utils::clear().unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn clear() -> io::Result<()> {
|
|
||||||
print!("\x1B[2J\x1B[H");
|
|
||||||
std::io::stdout().flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hides the terminal cursor.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// A `io::Result<()>` indicating whether the operation succeeded.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// utils::hide_cursor().unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn hide_cursor() -> io::Result<()> {
|
|
||||||
print!("\x1b[?25l");
|
|
||||||
std::io::stdout().flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shows the terminal cursor.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// A `io::Result<()>` indicating whether the operation succeeded.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// utils::show_cursor().unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn show_cursor() -> io::Result<()> {
|
|
||||||
print!("\x1B[?25h");
|
|
||||||
std::io::stdout().flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Flushes the terminal's stdout buffer.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// A `io::Result<()>` indicating whether the operation succeeded.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// utils::flush().unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn flush() -> io::Result<()> {
|
|
||||||
std::io::stdout().flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hex_ansi(hex: u32) -> String {
|
|
||||||
let r = ((hex >> 16) & 0xFF) as u8;
|
|
||||||
let g = ((hex >> 8) & 0xFF) as u8;
|
|
||||||
let b = (hex & 0xFF) as u8;
|
|
||||||
format!("\x1b[38;2;{r};{g};{b}m")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hex_ansi_bg(hex: u32) -> String {
|
|
||||||
let r = ((hex >> 16) & 0xFF) as u8;
|
|
||||||
let g = ((hex >> 8) & 0xFF) as u8;
|
|
||||||
let b = (hex & 0xFF) as u8;
|
|
||||||
format!("\x1b[48;2;{r};{g};{b}m")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Renderer {
|
|
||||||
fn draw_text(&mut self, text: &str, x: u32, y: u32) -> crate::Result<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OsuiRenderer;
|
|
||||||
|
|
||||||
impl Renderer for OsuiRenderer {
|
|
||||||
fn draw_text(&mut self, text: &str, x: u32, y: u32) -> crate::Result<()> {
|
|
||||||
for line in text.lines() {
|
|
||||||
print!("\x1b[{};{}H{line}\x1b[0m", y + 1, x + 1);
|
|
||||||
}
|
|
||||||
utils::flush()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+62
-59
@@ -1,100 +1,103 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fmt::{Debug, Display, Formatter, Result as FmtResult},
|
fmt::{Debug, Display, Formatter, Result as FmtResult},
|
||||||
ops::{Deref, DerefMut},
|
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
|
||||||
sync::{Arc, Mutex, MutexGuard},
|
sync::{Arc, Mutex, MutexGuard},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Trait for tracking dependencies and reactivity.
|
|
||||||
pub trait DependencyHandler: std::fmt::Debug + Send + Sync {
|
|
||||||
/// Called when a dependent is registered.
|
|
||||||
fn add(&self);
|
|
||||||
/// Returns `true` if the state has changed since the last check.
|
|
||||||
fn check(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct State<T> {
|
pub struct State<T> {
|
||||||
inner: Arc<Mutex<Inner<T>>>,
|
inner: Arc<Mutex<T>>,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Inner<T> {
|
|
||||||
value: T,
|
|
||||||
dependencies: usize,
|
|
||||||
changed: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_state<T>(v: T) -> State<T> {
|
pub fn use_state<T>(v: T) -> State<T> {
|
||||||
State {
|
State {
|
||||||
inner: Arc::new(Mutex::new(Inner {
|
inner: Arc::new(Mutex::new(v)),
|
||||||
value: v,
|
|
||||||
dependencies: 0,
|
|
||||||
changed: 0,
|
|
||||||
})),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> State<T> {
|
impl<T: Clone> State<T> {
|
||||||
/// Gets the cloned value, recommended for preventing deadlocks
|
/// Gets the cloned value, recommended for preventing deadlocks
|
||||||
pub fn get_dl(&self) -> T {
|
pub fn get_dl(&self) -> T {
|
||||||
self.inner.lock().unwrap().value.clone()
|
self.inner.lock().unwrap().clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> State<T> {
|
impl<T> State<T> {
|
||||||
/// Gets a lock on the state for read/write access.
|
/// Gets a lock on the state for read/write access.
|
||||||
pub fn get(&self) -> MutexGuard<'_, Inner<T>> {
|
pub fn get(&self) -> MutexGuard<'_, T> {
|
||||||
self.inner.lock().unwrap()
|
self.inner.lock().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the value and marks it as changed.
|
/// Sets the value and marks it as changed.
|
||||||
pub fn set(&self, v: T) {
|
pub fn set(&self, v: T) {
|
||||||
let mut inner = self.inner.lock().unwrap();
|
*self.inner.lock().unwrap() = v;
|
||||||
inner.value = v;
|
|
||||||
inner.changed = inner.dependencies;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Marks the state as updated.
|
|
||||||
pub fn update(&self) {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
inner.changed = inner.dependencies;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Debug + Send + Sync> DependencyHandler for State<T> {
|
|
||||||
fn check(&self) -> bool {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
let i = inner.changed > 0;
|
|
||||||
if i {
|
|
||||||
inner.changed -= 1;
|
|
||||||
}
|
|
||||||
i
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(&self) {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
inner.dependencies += 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Display> Display for State<T> {
|
impl<T: Display> Display for State<T> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
let inner = self.inner.lock().unwrap();
|
write!(f, "{}", self.inner.lock().unwrap())
|
||||||
write!(f, "{}", inner.value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Deref for Inner<T> {
|
impl<T: Add<Output = T> + Clone> Add<T> for State<T> {
|
||||||
type Target = T;
|
type Output = T;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn add(self, rhs: T) -> Self::Output {
|
||||||
&self.value
|
let v = self.inner.lock().unwrap().clone();
|
||||||
|
v.add(rhs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> DerefMut for Inner<T> {
|
impl<T: Sub<Output = T> + Clone> Sub<T> for State<T> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
type Output = T;
|
||||||
self.changed = self.dependencies;
|
|
||||||
&mut self.value
|
fn sub(self, rhs: T) -> Self::Output {
|
||||||
|
let v = self.inner.lock().unwrap().clone();
|
||||||
|
v.sub(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Mul<Output = T> + Clone> Mul<T> for State<T> {
|
||||||
|
type Output = T;
|
||||||
|
|
||||||
|
fn mul(self, rhs: T) -> Self::Output {
|
||||||
|
let v = self.inner.lock().unwrap().clone();
|
||||||
|
v.mul(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Div<Output = T> + Clone> Div<T> for State<T> {
|
||||||
|
type Output = T;
|
||||||
|
|
||||||
|
fn div(self, rhs: T) -> Self::Output {
|
||||||
|
let v = self.inner.lock().unwrap().clone();
|
||||||
|
v.div(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign
|
||||||
|
|
||||||
|
impl<T: AddAssign> AddAssign<T> for State<T> {
|
||||||
|
fn add_assign(&mut self, rhs: T) {
|
||||||
|
self.get().add_assign(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SubAssign> SubAssign<T> for State<T> {
|
||||||
|
fn sub_assign(&mut self, rhs: T) {
|
||||||
|
self.get().sub_assign(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: MulAssign> MulAssign<T> for State<T> {
|
||||||
|
fn mul_assign(&mut self, rhs: T) {
|
||||||
|
self.get().mul_assign(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: DivAssign> DivAssign<T> for State<T> {
|
||||||
|
fn div_assign(&mut self, rhs: T) {
|
||||||
|
self.get().div_assign(rhs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user