huge improvedments
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
fn main() {
|
||||
cc::Build::new()
|
||||
.file("src/app.c").compile("app");
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
rm -rf ./lib/
|
||||
mkdir lib
|
||||
gcc -c -o lib/app.o src/app.c
|
||||
ar rcs lib/libapp.a lib/app.o
|
||||
@@ -1,21 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <libc.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
extern void *event_loop(void* args);
|
||||
extern void *cmd_loop(void* args);
|
||||
|
||||
boolean_t c_run(void* args)
|
||||
{
|
||||
pthread_t event_thread;
|
||||
|
||||
if (pthread_create(&event_thread, NULL, event_loop, args) != 0)
|
||||
{
|
||||
perror("Failed to create thread");
|
||||
}
|
||||
|
||||
cmd_loop(args);
|
||||
return FALSE;
|
||||
}
|
||||
-112
@@ -1,112 +0,0 @@
|
||||
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::utils::{clear, flush, get_term_size, hide_cursor, show_cursor, Frame};
|
||||
use std::{ffi::c_void, ptr::null, sync::mpsc};
|
||||
|
||||
struct LArgs<'a> {
|
||||
pub element: &'a mut Element,
|
||||
|
||||
// for sending commands
|
||||
pub stx: mpsc::Sender<Command>,
|
||||
pub srx: mpsc::Receiver<Command>,
|
||||
|
||||
// for returning command output
|
||||
pub rtx: mpsc::Sender<crate::CommandResult>,
|
||||
pub rrx: mpsc::Receiver<crate::CommandResult>,
|
||||
pub running: bool,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn c_run(args: *mut c_void) -> bool;
|
||||
}
|
||||
|
||||
pub fn run(element: &mut Element) -> bool {
|
||||
hide_cursor();
|
||||
enable_raw_mode().unwrap();
|
||||
clear();
|
||||
unsafe {
|
||||
let (stx, srx) = mpsc::channel();
|
||||
let (rtx, rrx) = mpsc::channel();
|
||||
let c = c_run(&mut LArgs {
|
||||
running: true,
|
||||
element,
|
||||
stx,
|
||||
srx,
|
||||
rtx,
|
||||
rrx,
|
||||
} as *mut LArgs as *mut c_void);
|
||||
if !c {
|
||||
show_cursor();
|
||||
disable_raw_mode().unwrap();
|
||||
clear();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn event_loop(ptr: *mut c_void) -> *const c_void {
|
||||
if ptr.is_null() {
|
||||
return null() as *const c_void;
|
||||
}
|
||||
let args = unsafe { &mut *(ptr as *mut LArgs) };
|
||||
let document = Document {
|
||||
cmd_sender: args.stx.clone(),
|
||||
cmd_recv: &args.rrx as *const mpsc::Receiver<crate::CommandResult> as *const c_void,
|
||||
};
|
||||
|
||||
let (width, height) = get_term_size();
|
||||
args.element.event(
|
||||
crossterm::event::Event::Resize(width as u16, height as u16),
|
||||
&document,
|
||||
);
|
||||
args.element
|
||||
.event(crossterm::event::Event::FocusGained, &document);
|
||||
document.render();
|
||||
|
||||
while args.running || !ptr.is_null() {
|
||||
args.element
|
||||
.event(crossterm::event::read().unwrap(), &document);
|
||||
document.render();
|
||||
}
|
||||
null() as *const c_void
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn cmd_loop(ptr: *mut c_void) -> *const c_void {
|
||||
if ptr.is_null() {
|
||||
return null() as *const c_void;
|
||||
}
|
||||
|
||||
let args = unsafe { &mut *(ptr as *mut LArgs) };
|
||||
|
||||
loop {
|
||||
match args.srx.recv().unwrap() {
|
||||
Command::Exit => {
|
||||
args.running = false;
|
||||
break;
|
||||
}
|
||||
Command::GetElementById(id) => {
|
||||
args.rtx
|
||||
.send(if let Some(e) = args.element.get_element_by_id(&id) {
|
||||
crate::CommandResult::Element(e as *mut Element)
|
||||
} else if args.element.get_id() == id {
|
||||
crate::CommandResult::Element(args.element as *mut Element)
|
||||
} else {
|
||||
crate::CommandResult::None
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
Command::Render => {
|
||||
let (width, height) = get_term_size();
|
||||
let mut frame = Frame::new(width, height);
|
||||
frame.render(true, args.element);
|
||||
clear();
|
||||
print!("{}", frame.output_nnl());
|
||||
flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
null()
|
||||
}
|
||||
+2
-12
@@ -3,24 +3,14 @@ use crate::prelude::*;
|
||||
pub fn examples() -> Element {
|
||||
rsx! {
|
||||
styling: Some(styling()),
|
||||
button { class: "btn", on_click: Handler::new(|btn: &mut Button, _, document| {
|
||||
let data = document.get_element_by_id::<DataHolder<'_, String>>("my_data").unwrap();
|
||||
data.data += "!";
|
||||
btn.children.set_text(&data.data);
|
||||
}), "Hello, World!" }
|
||||
|
||||
{
|
||||
let mut data = dataholder::<String>();
|
||||
data.id = "my_data";
|
||||
data
|
||||
}
|
||||
button { class: "btn", "Hello, World!" }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn styling() -> Css {
|
||||
css! {
|
||||
.btn {
|
||||
x: 30%,
|
||||
x: Center,
|
||||
y: Auto,
|
||||
color: Red,
|
||||
}
|
||||
|
||||
+68
-25
@@ -26,9 +26,6 @@ use std::{
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use crossterm::event::Event;
|
||||
|
||||
pub mod app;
|
||||
pub mod css;
|
||||
pub mod examples;
|
||||
pub mod macros;
|
||||
@@ -36,13 +33,11 @@ pub mod rsx;
|
||||
pub mod ui;
|
||||
pub mod utils;
|
||||
|
||||
pub use app::run;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::ui::Color::*;
|
||||
pub use crate::ui::Number::*;
|
||||
pub use crate::{self as osui, css, rsx, rsx_elem, ui::*, Handler};
|
||||
pub use crate::{call, Children, ElementCore, ElementWidget, Element};
|
||||
pub use crate::{self as osui, css, ersx, rsx, ui::*, Handler, launch};
|
||||
pub use crate::{call, Children, Element, ElementCore, ElementWidget};
|
||||
pub use crate::{style, Command, Document, RenderResult, RenderWriter, Value};
|
||||
pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
|
||||
pub fn sleep(ms: u64) {
|
||||
@@ -68,7 +63,7 @@ pub trait ElementCore {
|
||||
|
||||
pub trait ElementWidget: ElementCore + std::fmt::Debug + Send + Sync {
|
||||
fn render(&self, focused: bool) -> Option<RenderResult>;
|
||||
fn event(&mut self, event: Event, document: &Document) {
|
||||
fn event(&mut self, event: crossterm::event::Event, document: &Document) {
|
||||
_ = (event, document)
|
||||
}
|
||||
}
|
||||
@@ -77,11 +72,13 @@ pub trait ElementWidget: ElementCore + std::fmt::Debug + Send + Sync {
|
||||
/// Structs
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct Handler<T>(pub Arc<Mutex<dyn FnMut(&mut T, Event, &Document) + Send + Sync>>);
|
||||
pub struct Handler<T>(
|
||||
pub Arc<Mutex<dyn FnMut(&mut T, crossterm::event::Event, &Document) + Send + Sync>>,
|
||||
);
|
||||
|
||||
pub struct Document {
|
||||
cmd_sender: std::sync::mpsc::Sender<Command>,
|
||||
cmd_recv: *const std::ffi::c_void,
|
||||
element: *mut Element,
|
||||
running: *mut Option<bool>,
|
||||
}
|
||||
|
||||
/// ```
|
||||
@@ -169,28 +166,48 @@ impl<T: Copy + PartialEq + Default> Default for Value<T> {
|
||||
|
||||
impl Document {
|
||||
pub fn exit(&self) {
|
||||
self.cmd_sender.send(Command::Exit).unwrap();
|
||||
if !self.running.is_null() {
|
||||
unsafe {
|
||||
*self.running = Some(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn rebuild(&self) {
|
||||
if !self.running.is_null() {
|
||||
unsafe {
|
||||
*self.running = Some(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn get_element_by_id<T>(&self, id: &str) -> Option<&mut Box<T>> {
|
||||
if let Some(e) = self.get_element_by_id_raw(id) {
|
||||
Some(convert(unsafe { &mut *e }))
|
||||
Some(unsafe { &mut *(e as *mut Element as *mut Box<T>) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn get_element_by_id_raw(&self, id: &str) -> Option<*mut Element> {
|
||||
self.cmd_sender
|
||||
.send(Command::GetElementById(id.to_string()))
|
||||
.unwrap();
|
||||
let rx = unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver<CommandResult>) };
|
||||
if let Ok(CommandResult::Element(e)) = rx.recv() {
|
||||
Some(e)
|
||||
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) {
|
||||
self.cmd_sender.send(Command::Render).unwrap();
|
||||
if !self.running.is_null() {
|
||||
let (width, height) = utils::get_term_size();
|
||||
let mut frame = utils::Frame::new(width, height);
|
||||
frame.render(true, unsafe { &*self.element });
|
||||
utils::clear();
|
||||
print!("{}", frame.output_nnl());
|
||||
utils::flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +271,7 @@ impl Children {
|
||||
impl<T> Handler<T> {
|
||||
pub fn new<F>(handler_fn: F) -> Handler<T>
|
||||
where
|
||||
F: FnMut(&mut T, Event, &Document) + 'static + Send + Sync,
|
||||
F: FnMut(&mut T, crossterm::event::Event, &Document) + 'static + Send + Sync,
|
||||
{
|
||||
Handler(Arc::new(Mutex::new(handler_fn)))
|
||||
}
|
||||
@@ -280,7 +297,9 @@ impl RenderWriter {
|
||||
|
||||
impl<T> Default for Handler<T> {
|
||||
fn default() -> Self {
|
||||
Handler(Arc::new(Mutex::new(|_: &mut T, _: Event, _: &Document| {})))
|
||||
Handler(Arc::new(Mutex::new(
|
||||
|_: &mut T, _: crossterm::event::Event, _: &Document| {},
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,6 +309,30 @@ impl<T> std::fmt::Debug for Handler<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert<T>(widget: &mut Box<dyn ElementWidget>) -> &mut Box<T> {
|
||||
unsafe { &mut *(widget as *mut _ as *mut Box<T>) }
|
||||
pub fn run(element: &mut Element) -> bool {
|
||||
let document = Document {
|
||||
element: element,
|
||||
running: &mut None,
|
||||
};
|
||||
let (width, height) = crate::utils::get_term_size();
|
||||
element.event(
|
||||
crossterm::event::Event::Resize(width as u16, height as u16),
|
||||
&document,
|
||||
);
|
||||
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() }
|
||||
}
|
||||
|
||||
@@ -16,3 +16,10 @@ macro_rules! call {
|
||||
&$self.$handler;
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! launch {
|
||||
($expr:expr) => {
|
||||
while $crate::run(&mut $expr) {}
|
||||
};
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,5 +2,5 @@ use osui::examples;
|
||||
use osui::prelude::*;
|
||||
|
||||
fn main() {
|
||||
while osui::run(&mut examples::examples()) {}
|
||||
launch!(examples::examples());
|
||||
}
|
||||
|
||||
+3
-3
@@ -50,7 +50,7 @@ macro_rules! parse_rsx_param {
|
||||
($elem:expr, $elem_path:path { $($inner:tt)* } $($rest:tt)*) => {
|
||||
if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
|
||||
if let $crate::Children::Children(children, _) = &mut $elem.children {
|
||||
children.push(osui::rsx_elem!($elem_path { $($inner)* }));
|
||||
children.push(osui::ersx!($elem_path { $($inner)* }));
|
||||
}
|
||||
osui::parse_rsx_param!($elem, $($rest)*)
|
||||
};
|
||||
@@ -66,7 +66,7 @@ macro_rules! parse_rsx_param {
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! rsx_elem {
|
||||
macro_rules! ersx {
|
||||
($elem:path { $($inner:tt)* }) => {{
|
||||
#[allow(unused_mut)]
|
||||
let mut elem = $elem();
|
||||
@@ -89,7 +89,7 @@ macro_rules! rsx_elem {
|
||||
#[macro_export]
|
||||
macro_rules! rsx {
|
||||
($($inner:tt)*) => {
|
||||
$crate::rsx_elem!{ $crate::ui::div { $($inner)* } }
|
||||
$crate::ersx!{ $crate::ui::div { $($inner)* } }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+1
-35
@@ -9,21 +9,6 @@ pub use styles::*;
|
||||
use crate::prelude::*;
|
||||
use osui_element::{elem_fn, element};
|
||||
|
||||
/// The `Text` element represents a piece of static text in the terminal UI.
|
||||
///
|
||||
/// It is used to render plain text within a UI component. The `Text` element does not interact
|
||||
/// with events and only outputs the text when rendered.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// let text = Text::default();
|
||||
/// let rendered_text = text.render(crate::State::Normal);
|
||||
/// println!("{}", rendered_text);
|
||||
/// ```
|
||||
///
|
||||
/// ## Methods
|
||||
/// - `render(state: crate::State) -> String`: Renders the text to a string, depending on the state.
|
||||
/// If the element contains text as a child, it returns that text. Otherwise, it returns an empty string.
|
||||
#[element]
|
||||
#[elem_fn]
|
||||
#[derive(Default, Debug)]
|
||||
@@ -50,26 +35,6 @@ impl ElementWidget for Text<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Div` element represents a container for other UI elements. It can hold child components
|
||||
/// and render them in a specific layout. The `Div` element also handles keyboard navigation through
|
||||
/// its children.
|
||||
///
|
||||
/// The `Div` element supports rendering a list of children and allows interactive navigation using
|
||||
/// arrow keys (Up, Down, Left, Right). It renders the selected child element differently based on the state.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// let div = Div::default();
|
||||
/// let rendered_div = div.render(crate::State::Normal);
|
||||
/// println!("{}", rendered_div);
|
||||
/// ```
|
||||
///
|
||||
/// ## Methods
|
||||
/// - `render(state: crate::State) -> String`: Renders the `Div` and its child elements to a string.
|
||||
/// The child elements are rendered according to the current selection (`child` index).
|
||||
/// - `event(event: Event, state: &crate::StateChanger)`: Handles keyboard events for
|
||||
/// navigation through child elements. Arrow keys (Up, Down, Left, Right) change the currently selected child.
|
||||
|
||||
#[element]
|
||||
#[elem_fn]
|
||||
#[derive(Default, Debug)]
|
||||
@@ -128,6 +93,7 @@ impl ElementWidget for Button<'_> {
|
||||
document.render();
|
||||
sleep(100);
|
||||
self.style.set_state("");
|
||||
document.rebuild();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
Reference in New Issue
Block a user