PicoUI
PicoUI is a declarative UI framework inspired by Apple's SwiftUI, designed to simplify UI development for Picotron. It provides a reactive state management system and a set of view components for constructing interfaces in a straightforward and maintainable way.
By using PicoUI, you can build complex user interfaces without dealing with the complexities of custom UI code based on raw offsets. PicoUI makes it easier to refactor and modify your codebase by automatically updating the UI when the underlying state changes.
Views
PicoUI provides several view components, each designed for a specific purpose:
TextView
Displays text with a specified color.
Arguments:
text
(string or Observable): The text to display.color
(number or Observable): The color of the text (default:7
).
SprView
Displays a sprite image.
Arguments:
sprite
(number or Observable): The index of the sprite to display.
RectView
Displays a filled rectangle with specified dimensions and color.
Arguments:
w
(number or Observable): The width of the rectangle.h
(number or Observable): The height of the rectangle.color
(number or Observable): The color of the rectangle (default:7
).
VStack
Arranges child views vertically with optional spacing.
Arguments:
spacing
(number or Observable): The vertical spacing between child views (default:0
).
Use the append(child)
method to add child views.
HStack
Arranges child views horizontally with optional spacing.
Arguments:
spacing
(number or Observable): The horizontal spacing between child views (default:0
).
Use the append(child)
method to add child views.
PaddingView
Adds padding around a single child view.
Arguments:
padding_x
(number or Observable): The horizontal padding (default:0
).padding_y
(number or Observable): The vertical padding (default:0
).
Usage:
local padding_view = PaddingView:new(padding_x, padding_y) padding_view:set_child(child_view) |
BackgroundView
Draws a background color behind a single child view.
Arguments:
color
(number or Observable): The background color (default:7
).
Usage:
local background_view = BackgroundView:new(color) background_view:set_child(child_view) |
BorderView
Draws a border around a single child view with optional padding.
Arguments:
border_color
(number or Observable): The color of the border (default:7
).padding_x
(number or Observable): The horizontal padding inside the border (default:0
).padding_y
(number or Observable): The vertical padding inside the border (default:0
).
Usage:
local border_view = BorderView:new(border_color, padding_x, padding_y) border_view:set_child(child_view) |
Usage
State Management
Any view argument can be either a static value or an Observable
value. Observables should be stored in a state
table accessible to the _update()
function. For example:
state = { mem_text = Observable:new("0 KiB"), fps_text = Observable:new("0 FPS"), fps_color = Observable:new(perf_colors.low), cpu_text = Observable:new("0.0 CPU"), } |
Setting Up the Interface
Include PicoUI in your main script:
include "PicoUI.lua" |
Build your interface by creating the innermost views first and then composing them into outer views. For example:
-- Create text views for performance counters local fps_counter = TextView:new(state.fps_text, state.fps_color) local cpu_counter = TextView:new(state.cpu_text, 7) local mem_counter = TextView:new(state.mem_text, 12) -- Arrange the counters vertically with spacing local perf_container = VStack:new(2) perf_container:append(fps_counter) perf_container:append(cpu_counter) perf_container:append(mem_counter) -- Add padding around the container local perf_container_padding = PaddingView:new(4, 4) perf_container_padding:set_child(perf_container) -- Add a background to the container local perf_bg = BackgroundView:new(32) perf_bg:set_child(perf_container_padding) -- Add a border around the entire view root = BorderView:new(17, 1, 1) root:set_child(perf_bg) |
Use the append(child)
method for views that can contain multiple children (like VStack
, HStack
), and set_child(child)
for views that hold a single child (like PaddingView
, BackgroundView
, BorderView
). Concrete views like TextView
or SprView
cannot hold children.
Initialize the view hierarchy in the _init()
function, and ensure the root view is accessible globally.
Updating and Drawing
In the _update()
function, update your state values and then call root:update()
to refresh the views:
function _update() -- Update state values state.fps_text:set(string.format("%.4f FPS", stat(7))) state.cpu_text:set(string.format("%.5f CPU", stat(1))) state.mem_text:set(string.format("%7i KiB", stat(0))) -- Update the root view root:update() end |
In the _draw()
function, clear the screen and draw the root view at the desired position:
function _draw() cls(0) root:draw()(12, 12) end |
Full Example
Here's a complete example demonstrating the usage of PicoUI. Ensure that you have a file named PicoUI.lua
in the same directory with the PicoUI code.
PicoUI Code
The full PicoUI framework code is provided below.
I plan to add more views and make things less complex (eg needing to nest 3 views to add background, padding, and border). I'd love any feedback or suggestions!
PicoUI Update 1.1
New features
- More declarative interface structuring
- Properties!!! (No more background or padding views)
Example code
I would love to add usage, but I am somewhat busy right now. Take a look at the example for reference.
PicoUI code
I plan to add more features soon:
- Easier assignments to reactive state
- Reactive state predicates (eg. if v >= 100 then 8)
- More layout like
- Defined dimensions
[Please log in to post a comment]