diff --git a/diagrams/Screen.excalidraw b/diagrams/Screen.excalidraw new file mode 100644 index 0000000..55647ec --- /dev/null +++ b/diagrams/Screen.excalidraw @@ -0,0 +1,393 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "_w9oL6e5PZjiuiL0JdwcU", + "type": "rectangle", + "x": 2640, + "y": 540, + "width": 579.9999999999999, + "height": 340, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1T", + "roundness": { + "type": 3 + }, + "seed": 1525012351, + "version": 822, + "versionNonce": 1382038129, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Yw7udnk8EtAQiRupCmWtM" + }, + { + "id": "Rk5WnZvfw777rNZNF-ATc", + "type": "arrow" + } + ], + "updated": 1751303249549, + "link": null, + "locked": false + }, + { + "id": "Yw7udnk8EtAQiRupCmWtM", + "type": "text", + "x": 2751.800003051758, + "y": 687.5, + "width": 356.3999938964844, + "height": 45, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1U", + "roundness": null, + "seed": 878481311, + "version": 719, + "versionNonce": 312435039, + "isDeleted": false, + "boundElements": [], + "updated": 1751303238315, + "link": null, + "locked": false, + "text": "Elements / Widgets", + "fontSize": 36, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "_w9oL6e5PZjiuiL0JdwcU", + "originalText": "Elements / Widgets", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "B_iEV4KuaVndtiWAzZ0D6", + "type": "rectangle", + "x": 3059.506993, + "y": 200, + "width": 459.9999999999999, + "height": 220, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1Y", + "roundness": { + "type": 3 + }, + "seed": 196252767, + "version": 645, + "versionNonce": 782999839, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "ib1kyrF6uzt6a6zgosgzN" + }, + { + "id": "vKca-2Ct2_GVlvUOK5TMY", + "type": "arrow" + }, + { + "id": "Rk5WnZvfw777rNZNF-ATc", + "type": "arrow" + } + ], + "updated": 1751303249549, + "link": null, + "locked": false + }, + { + "id": "ib1kyrF6uzt6a6zgosgzN", + "type": "text", + "x": 3230.106991474121, + "y": 287.5, + "width": 118.80000305175781, + "height": 45, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1Z", + "roundness": null, + "seed": 1940822833, + "version": 565, + "versionNonce": 473023569, + "isDeleted": false, + "boundElements": [], + "updated": 1751303249549, + "link": null, + "locked": false, + "text": "Screen", + "fontSize": 36, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "B_iEV4KuaVndtiWAzZ0D6", + "originalText": "Screen", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "tvzYmIFcn2mgh_7Q1PCZm", + "type": "rectangle", + "x": 3380, + "y": 540, + "width": 579.9999999999999, + "height": 340, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1c", + "roundness": { + "type": 3 + }, + "seed": 1132545585, + "version": 831, + "versionNonce": 1274553663, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "ufeniH0yJLXSDsdZgr-s_" + }, + { + "id": "vKca-2Ct2_GVlvUOK5TMY", + "type": "arrow" + } + ], + "updated": 1751303249549, + "link": null, + "locked": false + }, + { + "id": "ufeniH0yJLXSDsdZgr-s_", + "type": "text", + "x": 3571, + "y": 687.5, + "width": 198, + "height": 45, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1d", + "roundness": null, + "seed": 616257553, + "version": 747, + "versionNonce": 720835121, + "isDeleted": false, + "boundElements": [], + "updated": 1751303249549, + "link": null, + "locked": false, + "text": "Extensions", + "fontSize": 36, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "tvzYmIFcn2mgh_7Q1PCZm", + "originalText": "Extensions", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "Rk5WnZvfw777rNZNF-ATc", + "type": "arrow", + "x": 3289.406993, + "y": 425.00000000000006, + "width": 359.50699299999997, + "height": 109.99999999999994, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1e", + "roundness": { + "type": 2 + }, + "seed": 2108467185, + "version": 52, + "versionNonce": 1902060895, + "isDeleted": false, + "boundElements": [], + "updated": 1751303249549, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 54.99999999999994 + ], + [ + -359.50699299999997, + 54.99999999999994 + ], + [ + -359.50699299999997, + 109.99999999999994 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "B_iEV4KuaVndtiWAzZ0D6", + "focus": 0.000434782608694893, + "gap": 5.000000000000057, + "fixedPoint": [ + 0.4997826086956525, + 1.022727272727273 + ] + }, + "endBinding": { + "elementId": "_w9oL6e5PZjiuiL0JdwcU", + "focus": -0.0003448275862063412, + "gap": 5, + "fixedPoint": [ + 0.4998275862068968, + -0.014705882352941176 + ] + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": true, + "fixedSegments": null, + "startIsSpecial": null, + "endIsSpecial": null + }, + { + "id": "vKca-2Ct2_GVlvUOK5TMY", + "type": "arrow", + "x": 3289.406993, + "y": 425.00000000000006, + "width": 380.49300700000003, + "height": 109.99999999999994, + "angle": 0, + "strokeColor": "#ffffff", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1f", + "roundness": { + "type": 2 + }, + "seed": 1931152479, + "version": 97, + "versionNonce": 865575953, + "isDeleted": false, + "boundElements": [], + "updated": 1751303249549, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 54.99999999999994 + ], + [ + 380.49300700000003, + 54.99999999999994 + ], + [ + 380.49300700000003, + 109.99999999999994 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "B_iEV4KuaVndtiWAzZ0D6", + "focus": 0.000434782608694893, + "gap": 5, + "fixedPoint": [ + 0.4997826086956525, + 1.022727272727273 + ] + }, + "endBinding": { + "elementId": "tvzYmIFcn2mgh_7Q1PCZm", + "focus": 1.0294117647058825, + "gap": 5, + "fixedPoint": [ + 0.4998275862068968, + -0.014705882352941176 + ] + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": true, + "fixedSegments": null, + "startIsSpecial": null, + "endIsSpecial": null + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#000", + "lockedMultiSelections": {} + }, + "files": {} +} \ No newline at end of file diff --git a/docs/a_index.md b/docs/a_index.md new file mode 100644 index 0000000..ec56237 --- /dev/null +++ b/docs/a_index.md @@ -0,0 +1,43 @@ +--- +title: Introduction +slug: / +--- + +OSUI is a powerful TUI library, what makes it special is it's design and features, it allows for both customization and easy of use. + +### Setup +First, you need to install osui to a already existing [rust cargo](https://doc.rust-lang.org/cargo/getting-started/index.html) project, then in your project directory run: +```bash +cargo add osui +``` + +## Hello World App +To make a hello world app in OSUI simply write this +```rust src/main.rs +use osui::Screen; + +fn main() -> std::io::Result<()> { + let mut screen = Screen::new(); + + screen.draw(format!("Hello, World!")); + + screen.run() +} +``` + +## Hello World App With Velocity +The text will move from left to right with a velocity of `100` +```rust src/main.rs +use osui::{Screen, extensions::velocity::{VelocityExtension, Velocity}}; + +fn main() -> std::io::Result<()> { + let mut screen = Screen::new(); + screen.extension(VelocityExtension); + + screen + .draw(format!("Hello, World!")) + .component(Velocity(100, 0)); // Velocity(x, y) + + screen.run() +} +``` diff --git a/docs/b_screen.md b/docs/b_screen.md new file mode 100644 index 0000000..ad23e8e --- /dev/null +++ b/docs/b_screen.md @@ -0,0 +1,27 @@ +--- +title: Screen +slug: /screen +--- +`Screen` is a OSUI structure that allows the program to use osui elements and it's extensions. +Diagram + +### `Screen::new()` +Creates a new `Screen` structure with empty widgets and extensions. + +### `draw(&mut self, element: E) -> &Arc` +Adds a element to the widgets array and returns the widget for extra parameters. + +### `extension(&mut self, ext: E)` +Adds a extension to the extensions array. + +### `run(&mut self) -> std::io::Result<()>` +Runs the app with the elements and extension provided. + +### `render(&self) -> std::io::Result<()>` +> :::warning +> Do not run this function if you don't know what you're doing, this function may cause a mutex deadlock. + + +### `render_extension(&self, wi: Arc) -> std::io::Result<()>` +> :::warning +> Do not run this function if you don't know what you're doing, this function may cause a mutex deadlock. \ No newline at end of file diff --git a/docs/c_extensions.md b/docs/c_extensions.md new file mode 100644 index 0000000..96fa9a9 --- /dev/null +++ b/docs/c_extensions.md @@ -0,0 +1,16 @@ +--- +title: Extensions +slug: /extensions +--- + +OSUI Extensions are a practical way to extend the possibilities with OSUI, Extensions allow for there to be a specific use or functionality that may otherwise be bloat or the opposite behavior of the use case. + +### `screen.extension(&mut self, ext: E)` +You likely noticed that we used this function in the [Hello World App With Velocity](/docs/next/#hello-world-app-with-velocity) example, this function simply allows for structures implementing [Extension](/docs/next/extensions#trait-extension) to be included in the program. + +## Trait `Extension` +### `init(&self, _widgets: &Vec>)` +Runs right after the Transform component is applied to every widget. + +### `render(&self, _widget: &Arc)` +Runs right before the `Element::render` is called. diff --git a/static/img/diagrams/screen.png b/static/img/diagrams/screen.png new file mode 100644 index 0000000..5c797a9 Binary files /dev/null and b/static/img/diagrams/screen.png differ diff --git a/versioned_docs/version-0.0.7/references/a_rsx.md b/versioned_docs/version-0.0.7/references/a_rsx.md index 0145c59..e681259 100644 --- a/versioned_docs/version-0.0.7/references/a_rsx.md +++ b/versioned_docs/version-0.0.7/references/a_rsx.md @@ -7,7 +7,7 @@ This macro is the recommended way to describe a ui, it is a div that holds multi fn app() -> Element { rsx! { button { "click me" } - text { "This is some text" } + text { "This is some text" }**** } } ``` diff --git a/docs/engine.md b/versioned_docs/version-0.0.8/engine.md similarity index 100% rename from docs/engine.md rename to versioned_docs/version-0.0.8/engine.md diff --git a/docs/index.md b/versioned_docs/version-0.0.8/index.md similarity index 100% rename from docs/index.md rename to versioned_docs/version-0.0.8/index.md diff --git a/versioned_docs/version-0.0.8/references/a_rsx.md b/versioned_docs/version-0.0.8/references/a_rsx.md new file mode 100644 index 0000000..0145c59 --- /dev/null +++ b/versioned_docs/version-0.0.8/references/a_rsx.md @@ -0,0 +1,96 @@ +--- +title: RSX +--- + +This macro is the recommended way to describe a ui, it is a div that holds multiple elements +```rust +fn app() -> Element { + rsx! { + button { "click me" } + text { "This is some text" } + } +} +``` + +> :::info `rsx!` is really just a div, so you can just do something like +> ```rust +> rsx! { +> id: "root", class: "root", +> +> button { "click me" } +> text { "This is some text" } +> } +> ``` + +## Child values +If you want to add more children to values then just simply express it like a `rsx!` +```rust +fn app() -> Element { + let x = 69; + rsx! { + div { + text { "the number is {x}, Nice!" } + } + } +} +``` + +## Formatted text +```rust +fn app() -> Element { + let x = 69; + rsx! { + text { "the number is {x}, Nice!" } + } +} +``` +> :::info You can also do expressions after the string +```rust +text { "the number is {}, Nice!", x } +``` + +## For loops +```rust +fn app() -> Element { + rsx! { + for (i in 0..5) { // () are required + text { "{i}" } + } + } +} +``` + +## Attribute +You can change the element's struct fields by just providing some attributes before the children +```rust +fn app() -> Element { + rsx! { + text { class: "my_text" } + } +} +``` + +## Type assignment +In special elements like `data_holder` You can specify what `type` the element holds. It would act like `my_fn::()` +```rust +fn app() -> Element { + rsx! { + // assign the type u32 + data_holder as u32 { id: "count" } + } +} +``` + +## Handler functions +Instead of closures which are tricky to work with on structs, We use `Handler`, It's basically a closure wrapped with a `Arc>` +```rust +fn app() -> Element { + rsx! { + button { + on_click: fn(btn: &mut Button, event, document) { + // do something + } + } + } +} +``` \ No newline at end of file diff --git a/versioned_docs/version-0.0.8/references/b_handler.md b/versioned_docs/version-0.0.8/references/b_handler.md new file mode 100644 index 0000000..3524d63 --- /dev/null +++ b/versioned_docs/version-0.0.8/references/b_handler.md @@ -0,0 +1,24 @@ +--- +title: Handler +--- + +A `Handler` is a closure wrapper. Due to how closures work, they aren't the best to work with on structs (in this case: elements). So to fix this we use a `Handler` which is a wrapper for the closure, Then you can safely clone and use it without any problems (in which a normal closure would fail at) + +## Examples +```rust +Handler::new(|btn: &mut Button, event, document| { + // do something +}) +``` +A better way to do this is: +```rust +rsx! { + button { + // No need for Handler::new + on_click: fn(btn: &mut Button, event, document) { + // do something + } + } +} +``` +> :::info `fn() {}` is a part of our macro `rsx!`. In rust you would normally use `|| {}` \ No newline at end of file diff --git a/versioned_docs/version-0.0.8/references/css.md b/versioned_docs/version-0.0.8/references/css.md new file mode 100644 index 0000000..a12bb30 --- /dev/null +++ b/versioned_docs/version-0.0.8/references/css.md @@ -0,0 +1,66 @@ +--- +title: CSS +--- + +Along with the `Css` type, This macro provides styling for multiple elements by their class name or struct name. + +## Style by classname +```rust +fn app() -> Element { + rsx! { + styling: Some(styles()) // Set the css styling for the div. + + button { class: "my_btn", "Click me!" } + } +} + +fn styles() -> Css { + css! { + .my_btn { // style by classname + color: Red, + } + } +} +``` + +## Style by state +```rust +fn app() -> Element { + rsx! { + styling: Some(styles()) // Set the css styling for the div. + + button { class: "my_btn", "Click me!" } + } +} + +fn styles() -> Css { + css! { + .my_btn: clicked { // When the element state is clicked + color: Blue, + } + } +} +``` + +## Style by struct name +```rust +fn app() -> Element { + rsx! { + styling: Some(styles()) // Set the css styling for the div. + + button { "Click me!" } // No need for classes + } +} + +fn styles() -> Css { + css! { + Button { // style by the struct name + color: Red, + } + + Button: clicked { // When the element state is clicked + color: Blue, + } + } +} +``` \ No newline at end of file diff --git a/versioned_docs/version-0.0.8/references/d_style.md b/versioned_docs/version-0.0.8/references/d_style.md new file mode 100644 index 0000000..c0aed85 --- /dev/null +++ b/versioned_docs/version-0.0.8/references/d_style.md @@ -0,0 +1,20 @@ +--- +title: Style +--- + +Like `css!`, `style!` is a way to style a element. But unlike `css!`, `style!` is only meant to style a single element + +```rust +rsx! { + button { + style: style! { + color: Red, + clicked { // when the button state is 'clicked' + color: Blue, + } + }, + + "click me!" + } +} +``` \ No newline at end of file diff --git a/versioned_docs/version-0.0.8/references/e_document.md b/versioned_docs/version-0.0.8/references/e_document.md new file mode 100644 index 0000000..f98d72e --- /dev/null +++ b/versioned_docs/version-0.0.8/references/e_document.md @@ -0,0 +1,28 @@ +--- +title: Document +--- + +The `Document` holds the root element and it renders and passes events to the `Element`. It's important for updating the UI. + +## Example +```rust +fn(_, _, document) { + if let Some(my_text) = document.get_element_by_id::("my_text") { + my_text.children.set_text("Updated!"); + } +} +``` + +## Methods +### `exit()` +Exits the program +### `restart()` +Restarts the program +### `get_element_by_id(id: &str) -> Option<&mut Box>` +Retrieves a mutable reference to a boxed element of type T by its string identifier, returning None if no such element exists. +> :::info Please make sure that the type of the element is correctly matched to the element of that id +### `get_element_by_id_raw(id: &str) -> Option<&mut Element>` +> :::warning Not recommended to use this function, Use `get_element_by_id` instead +Retrieves a mutable reference to a boxed element by its string identifier, returning None if no such element exists +### `render()` +Renders/reloads the screen, useful when a element updates \ No newline at end of file diff --git a/versioned_docs/version-0.0.8/references/f_element.md b/versioned_docs/version-0.0.8/references/f_element.md new file mode 100644 index 0000000..7f28914 --- /dev/null +++ b/versioned_docs/version-0.0.8/references/f_element.md @@ -0,0 +1,5 @@ +--- +title: Element +--- + +`Element` is a type alias of `Box`. It makes the code look cleaner \ No newline at end of file diff --git a/versioned_docs/version-0.0.8/references/g_element_widget.md b/versioned_docs/version-0.0.8/references/g_element_widget.md new file mode 100644 index 0000000..adfa57c --- /dev/null +++ b/versioned_docs/version-0.0.8/references/g_element_widget.md @@ -0,0 +1,14 @@ +--- +title: ElementWidget +--- + +`ElementWidget` is a trait that defines the Elements and it's rendering / event updates. + +## Methods +### `render(&self, focused: bool) -> Option` +- **required** +- This function renders the element + +### `event(&mut self, event: Event, document: &Document)` +- **optional** +- This function updates the element when there is a event \ No newline at end of file diff --git a/versioned_docs/version-0.0.8/references/h_element_core.md b/versioned_docs/version-0.0.8/references/h_element_core.md new file mode 100644 index 0000000..b7e8b38 --- /dev/null +++ b/versioned_docs/version-0.0.8/references/h_element_core.md @@ -0,0 +1,32 @@ +--- +title: ElementCore +--- + +`ElementCore` is a trait that will automatically be implemented by the `osui_element::element` proc-macro. All of it's functions are used by OSUI. + +## `element` proc-macro +This macro adds the essential fields and functions for a `ElementCore` structure. +```rust +use osui_element::element; +#[element] +#[derive(Default, Debug)] +struct MyElement {} +``` + +## `elem_fn` proc-macro +This macro makes a function that returns a `Box` of which T is the default of `MyElement` +```rust +use osui_element::{element, elem_fn}; +#[element] +#[elem_fn] +#[derive(Default, Debug)] +struct MyElement {} +``` + +>:::info +>You can also define a function for the element manually: +>```rust +>pub fn my_element() -> Box { +> Box::new(MyElement::default()) +>} +>``` \ No newline at end of file diff --git a/versioned_sidebars/version-0.0.8-sidebars.json b/versioned_sidebars/version-0.0.8-sidebars.json new file mode 100644 index 0000000..39332bf --- /dev/null +++ b/versioned_sidebars/version-0.0.8-sidebars.json @@ -0,0 +1,8 @@ +{ + "docsSidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ] +}