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 {
|
pub fn examples() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
styling: Some(styling()),
|
styling: Some(styling()),
|
||||||
button { class: "btn", on_click: Handler::new(|btn: &mut Button, _, document| {
|
button { class: "btn", "Hello, World!" }
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn styling() -> Css {
|
pub fn styling() -> Css {
|
||||||
css! {
|
css! {
|
||||||
.btn {
|
.btn {
|
||||||
x: 30%,
|
x: Center,
|
||||||
y: Auto,
|
y: Auto,
|
||||||
color: Red,
|
color: Red,
|
||||||
}
|
}
|
||||||
|
|||||||
+68
-25
@@ -26,9 +26,6 @@ use std::{
|
|||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crossterm::event::Event;
|
|
||||||
|
|
||||||
pub mod app;
|
|
||||||
pub mod css;
|
pub mod css;
|
||||||
pub mod examples;
|
pub mod examples;
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
@@ -36,13 +33,11 @@ pub mod rsx;
|
|||||||
pub mod ui;
|
pub mod ui;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
pub use app::run;
|
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::ui::Color::*;
|
pub use crate::ui::Color::*;
|
||||||
pub use crate::ui::Number::*;
|
pub use crate::ui::Number::*;
|
||||||
pub use crate::{self as osui, css, rsx, rsx_elem, ui::*, Handler};
|
pub use crate::{self as osui, css, ersx, rsx, ui::*, Handler, launch};
|
||||||
pub use crate::{call, Children, ElementCore, ElementWidget, Element};
|
pub use crate::{call, Children, Element, ElementCore, ElementWidget};
|
||||||
pub use crate::{style, Command, Document, RenderResult, RenderWriter, Value};
|
pub use crate::{style, Command, Document, RenderResult, RenderWriter, Value};
|
||||||
pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
|
pub use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
|
||||||
pub fn sleep(ms: u64) {
|
pub fn sleep(ms: u64) {
|
||||||
@@ -68,7 +63,7 @@ pub trait ElementCore {
|
|||||||
|
|
||||||
pub trait ElementWidget: ElementCore + std::fmt::Debug + Send + Sync {
|
pub trait ElementWidget: ElementCore + std::fmt::Debug + Send + Sync {
|
||||||
fn render(&self, focused: bool) -> Option<RenderResult>;
|
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)
|
_ = (event, document)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,11 +72,13 @@ pub trait ElementWidget: ElementCore + std::fmt::Debug + Send + Sync {
|
|||||||
/// Structs
|
/// 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 {
|
pub struct Document {
|
||||||
cmd_sender: std::sync::mpsc::Sender<Command>,
|
element: *mut Element,
|
||||||
cmd_recv: *const std::ffi::c_void,
|
running: *mut Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
@@ -169,28 +166,48 @@ impl<T: Copy + PartialEq + Default> Default for Value<T> {
|
|||||||
|
|
||||||
impl Document {
|
impl Document {
|
||||||
pub fn exit(&self) {
|
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>> {
|
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) {
|
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 {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_element_by_id_raw(&self, id: &str) -> Option<*mut Element> {
|
pub fn get_element_by_id_raw(&self, id: &str) -> Option<&mut Element> {
|
||||||
self.cmd_sender
|
if !self.element.is_null() {
|
||||||
.send(Command::GetElementById(id.to_string()))
|
unsafe {
|
||||||
.unwrap();
|
if (*self.element).get_id() == id {
|
||||||
let rx = unsafe { &*(self.cmd_recv as *const std::sync::mpsc::Receiver<CommandResult>) };
|
Some(&mut *self.element)
|
||||||
if let Ok(CommandResult::Element(e)) = rx.recv() {
|
} else {
|
||||||
Some(e)
|
(*self.element).get_element_by_id(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn render(&self) {
|
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> {
|
impl<T> Handler<T> {
|
||||||
pub fn new<F>(handler_fn: F) -> Handler<T>
|
pub fn new<F>(handler_fn: F) -> Handler<T>
|
||||||
where
|
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)))
|
Handler(Arc::new(Mutex::new(handler_fn)))
|
||||||
}
|
}
|
||||||
@@ -280,7 +297,9 @@ impl RenderWriter {
|
|||||||
|
|
||||||
impl<T> Default for Handler<T> {
|
impl<T> Default for Handler<T> {
|
||||||
fn default() -> Self {
|
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> {
|
pub fn run(element: &mut Element) -> bool {
|
||||||
unsafe { &mut *(widget as *mut _ as *mut Box<T>) }
|
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;
|
&$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::*;
|
use osui::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
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)*) => {
|
($elem:expr, $elem_path:path { $($inner:tt)* } $($rest:tt)*) => {
|
||||||
if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
|
if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
|
||||||
if let $crate::Children::Children(children, _) = &mut $elem.children {
|
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)*)
|
osui::parse_rsx_param!($elem, $($rest)*)
|
||||||
};
|
};
|
||||||
@@ -66,7 +66,7 @@ macro_rules! parse_rsx_param {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! rsx_elem {
|
macro_rules! ersx {
|
||||||
($elem:path { $($inner:tt)* }) => {{
|
($elem:path { $($inner:tt)* }) => {{
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut elem = $elem();
|
let mut elem = $elem();
|
||||||
@@ -89,7 +89,7 @@ macro_rules! rsx_elem {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! rsx {
|
macro_rules! rsx {
|
||||||
($($inner:tt)*) => {
|
($($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 crate::prelude::*;
|
||||||
use osui_element::{elem_fn, element};
|
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]
|
#[element]
|
||||||
#[elem_fn]
|
#[elem_fn]
|
||||||
#[derive(Default, Debug)]
|
#[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]
|
#[element]
|
||||||
#[elem_fn]
|
#[elem_fn]
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
@@ -128,6 +93,7 @@ impl ElementWidget for Button<'_> {
|
|||||||
document.render();
|
document.render();
|
||||||
sleep(100);
|
sleep(100);
|
||||||
self.style.set_state("");
|
self.style.set_state("");
|
||||||
|
document.rebuild();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|||||||
Reference in New Issue
Block a user