//! # OSUI //! //! A terminal user interface (TUI) library providing customizable components //! to build command-line interfaces in Rust. OSUI enables users to create //! interactive CLI applications with various UI elements and handle keyboard //! input for real-time events. //! //! ## Example Usage //! //! ```rust //! use osui::prelude::*; //! //! launch(rsx! { //! text { "Hello, World!" } //! }); //! ``` //! //! ## Modules //! - `ui` - Contains all user interface components, enabling rich CLI experiences. //! - `utils` - Utility functions for common TUI tasks such as clearing the screen. use std::{ collections::HashMap, fmt::Debug, sync::{Arc, Mutex}, }; use ui::Style; pub mod css; pub mod examples; pub mod macros; pub mod rsx; pub mod ui; pub mod utils; pub mod prelude { pub use crate::ui::Color::*; pub use crate::ui::Font::*; pub use crate::ui::Number::*; pub use crate::{self as osui, css, ersx, launch, rsx, ui::*, Handler}; pub use crate::{call, Children, Element, ElementCore, ElementWidget}; pub use crate::{style, Document}; pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; pub fn sleep(ms: u64) { std::thread::sleep(std::time::Duration::from_millis(ms)); } } ////////////////////////////////////////////////////////////////////////////////////////////////// // Type aliases ////////////////////////////////////////////////////////////////////////////////////////////////// pub type Element = Box; ////////////////////////////////////////////////////////////////////////////////////////////////// /// Traits ////////////////////////////////////////////////////////////////////////////////////////////////// pub trait ElementCore { fn get_element_by_id(&mut self, id: &str) -> Option<&mut Element>; fn set_styling(&mut self, styling: &HashMap); fn get_id(&self) -> String; fn get_style(&self) -> &Style; } pub trait ElementWidget: ElementCore + std::fmt::Debug { fn render(&self, writer: &mut Writer); fn event(&mut self, event: crossterm::event::Event, document: &Document) { _ = (event, document) } } ////////////////////////////////////////////////////////////////////////////////////////////////// /// Structs ////////////////////////////////////////////////////////////////////////////////////////////////// pub struct Handler(pub Arc>); pub struct Document { element: *mut Element, running: *mut Option, } pub struct Writer { text: String, style: Style, focused: bool, size: (u16, u16), } ////////////////////////////////////////////////////////////////////////////////////////////////// /// Enums ////////////////////////////////////////////////////////////////////////////////////////////////// #[derive(Debug)] pub enum Children { None, Text(String), Children(Vec, usize), } ////////////////////////////////////////////////////////////////////////////////////////////////// /// Implementations ////////////////////////////////////////////////////////////////////////////////////////////////// impl Document { pub fn exit(&self) { if !self.running.is_null() { unsafe { *self.running = Some(false); } } } pub fn restart(&self) { if !self.running.is_null() { unsafe { *self.running = Some(true); } } } pub fn get_element_by_id(&self, id: &str) -> Option<&mut Box> { if let Some(e) = self.get_element_by_id_raw(id) { Some(unsafe { &mut *(e as *mut Element as *mut Box) }) } else { None } } pub fn get_element_by_id_raw(&self, id: &str) -> Option<&mut Element> { if !self.element.is_null() { unsafe { if (*self.element).get_id() == id { Some(&mut *self.element) } else { (*self.element).get_element_by_id(id) } } } else { None } } pub fn render(&self) { if !self.running.is_null() { let (width, height) = crossterm::terminal::size().unwrap(); let mut frame = utils::Frame::new(width, height); frame.render(true, unsafe { &*self.element }); utils::clear(); print!("{}", frame.output_nnl()); utils::flush(); } } pub fn draw(&mut self, element: &mut Element) { self.element = element; } pub fn use_state(&self, name: &str) -> &mut T { &mut self .get_element_by_id::>(name) .unwrap() .data } } impl Default for Children { fn default() -> Self { Self::None } } impl Children { pub fn is_none(&self) -> bool { match self { Children::None => true, _ => false, } } pub fn get_text(&self) -> String { match self { Children::Text(text) => text.clone(), _ => String::new(), } } pub fn get_text_mut(&mut self) -> Option<&mut String> { match self { Children::Text(text) => Some(text), _ => None, } } pub fn set_text(&mut self, text: &str) { match self { Children::Text(t) => { *t = text.to_string(); } _ => {} } } pub fn set_text_force(&mut self, text: &str) { match self { Children::Text(t) => { *t = text.to_string(); } _ => { *self = Children::Text(text.to_string()); } } } pub fn add_child(&mut self, element: Element) { if let Children::Children(children, _) = self { children.push(element); } } pub fn get_child(&mut self) -> Option<&mut Element> { if let Children::Children(children, child) = self { children.get_mut(*child) } else { None } } } impl Handler { pub fn new(handler_fn: F) -> Handler where F: FnMut(&mut T, crossterm::event::Event, &Document) + 'static, { Handler(Arc::new(Mutex::new(handler_fn))) } } impl Writer { pub fn new(focused: bool, style: ui::Style, size: (u16, u16)) -> Writer { Writer { text: String::new(), style, focused, size, } } pub fn write(&mut self, s: &str) { self.text += self.style.get(self.focused).write(s).as_str(); } pub fn get_size(&self) -> (u16, u16) { self.size } pub fn get_focused(&self) -> bool { self.focused } } impl Default for Handler { fn default() -> Self { Handler(Arc::new(Mutex::new( |_: &mut T, _: crossterm::event::Event, _: &Document| {}, ))) } } impl std::fmt::Debug for Handler { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("Handler()") } } pub fn run(element: &mut Element) -> bool { let document = Document { element: element, running: &mut None, }; // Send initial event element.event(crossterm::event::Event::FocusGained, &document); utils::hide_cursor(); crossterm::terminal::enable_raw_mode().unwrap(); utils::clear(); while unsafe { *document.running } == None { document.render(); element.event(crossterm::event::read().unwrap(), &document); } utils::show_cursor(); crossterm::terminal::disable_raw_mode().unwrap(); utils::clear(); unsafe { (*document.running).unwrap() } }