huge improvedments

This commit is contained in:
2024-11-25 22:15:32 +01:00
parent 0266c26d34
commit 6d86e8a42b
11 changed files with 82 additions and 217 deletions
Vendored
BIN
View File
Binary file not shown.
-4
View File
@@ -1,4 +0,0 @@
fn main() {
cc::Build::new()
.file("src/app.c").compile("app");
}
-4
View File
@@ -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
-21
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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() }
}
+7
View File
@@ -16,3 +16,10 @@ macro_rules! call {
&$self.$handler;
}};
}
#[macro_export]
macro_rules! launch {
($expr:expr) => {
while $crate::run(&mut $expr) {}
};
}
+1 -1
View File
@@ -2,5 +2,5 @@ use osui::examples;
use osui::prelude::*;
fn main() {
while osui::run(&mut examples::examples()) {}
launch!(examples::examples());
}
+3 -3
View File
@@ -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
View File
@@ -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();
}
}
_ => {}