A SwiftUI-inspired Rust SDK for building native user interfaces on OblivionOS, a Debian-based Linux distribution inspired by macOS.
Oblivion UI provides a declarative, component-based framework for creating native applications with a SwiftUI-like API. It supports reactive state management, event handling, and rendering via SDL2.
- Declarative Components: Window, VStack, HStack, Grid, Panel, Button, Label, Toggle, Input
- Reactive State: @State and @Binding for automatic UI updates
- Event Handling: Click, hover, keyboard, drag events
- Theming: Customizable themes with colors and fonts
- Layout: Spacing, padding, borders for flexible layouts
- Rendering: SDL2-based native rendering with OpenGL/Vulkan support
- Cross-Platform: Designed for Desktop and ARM architectures
The SDK is structured as a Cargo workspace:
oblivion-sdk/
├── oblivion_ui/ # Core library crate
│ ├── src/
│ │ ├── lib.rs # Main library entry
│ │ ├── components.rs # UI components
│ │ ├── state.rs # State management
│ │ ├── rendering.rs # SDL2 rendering engine
│ │ └── themes.rs # Theme definitions
│ └── Cargo.toml
├── examples/ # Example applications
│ └── simple_app/
└── Cargo.toml # Workspace configuration
- Rust 1.70+
- SDL2 development libraries
- SDL2_ttf for text rendering
On Ubuntu/Debian:
sudo apt-get install libsdl2-dev libsdl2-ttf-devAdd to your Cargo.toml:
[dependencies]
oblivion_ui = { path = "../oblivion_ui" }use oblivion_ui::components::{Window, VStack, Button, Label};
use oblivion_ui::state::State;
use oblivion_ui::rendering::SDLEngine;
use oblivion_ui::themes::Theme;
fn main() -> Result<(), String> {
let counter = State::new("0".to_string());
let mut window = Window::new("My App".to_string(), 800, 600);
let mut vstack = VStack::new(10.0).padding(20.0);
let label = Label::new(counter.binding());
vstack.add_child(Box::new(label));
let button = Button::new("Increment".to_string())
.on_click(move || {
let current: i32 = counter.get().parse().unwrap_or(0);
counter.set((current + 1).to_string());
});
vstack.add_child(Box::new(button));
window.add_child(Box::new(vstack));
let theme = Theme::default();
let mut engine = SDLEngine::new("My App", 800, 600)?;
engine.run(Box::new(window), &theme)
}The root container for your application.
let window = Window::new("Title".to_string(), width, height);Vertical stack of components.
let mut vstack = VStack::new(spacing)
.padding(10.0)
.border(2.0);
vstack.add_child(Box::new(component));Horizontal stack of components.
let mut hstack = HStack::new(spacing)
.padding(10.0)
.border(2.0);
hstack.add_child(Box::new(component));2D grid layout.
let mut grid = Grid::new(rows, cols, spacing);
grid.set_child(row, col, Box::new(component));Container with optional border and padding.
let panel = Panel::new(border_width, padding)
.child(Box::new(component));Clickable button with label.
let button = Button::new("Click me".to_string())
.padding(5.0)
.border(1.0)
.on_click(|| println!("Clicked!"));Displays text, bound to reactive state.
let label = Label::new(state.binding())
.padding(5.0);On/off switch.
let toggle = Toggle::new(state.binding())
.on_toggle(|is_on| println!("Toggled: {}", is_on));Text input field.
let input = Input::new(text_binding, "Placeholder".to_string());Local reactive state.
let count = State::new(0);
count.set(count.get() + 1); // Triggers redrawShared state between components.
let binding = state.binding();
let value = binding.get();
binding.set(new_value);Customize appearance with themes.
let theme = Theme {
primary_color: (255, 0, 0),
background_color: (255, 255, 255),
text_color: (0, 0, 0),
font_size: 14,
};Components receive events through the handle_event method.
Supported events:
Click { x, y }: Mouse clickHover { x, y }: Mouse hoverKeyPress(char): Keyboard inputDrag { dx, dy }: Drag motion
The SDK uses SDL2 for rendering. The SDLEngine manages the event loop and canvas.
let mut engine = SDLEngine::new(title, width, height)?;
engine.run(root_component, &theme)?;To create a custom component:
- Implement the
Componenttrait:
use crate::components::{Component, Renderer, Event};
use crate::themes::Theme;
pub struct MyComponent {
// fields
}
impl Component for MyComponent {
fn render(&self, renderer: &mut dyn Renderer, theme: &Theme) {
// Draw your component
}
fn handle_event(&mut self, event: &Event) {
// Handle events
}
}- Add to your layout:
let my_component = Box::new(MyComponent::new());
vstack.add_child(my_component);Add new structs and implementations in components.rs.
Extend the Renderer trait for different backends.
Add services in a new services.rs module:
pub mod services {
pub struct Auth;
pub struct Store;
// etc.
}See examples/simple_app/ for a complete application.
# Build the SDK
cargo build
# Run an example
cargo run -p simple_app
# Build for ARM
cargo build --target aarch64-unknown-linux-gnupub trait Component {
fn render(&self, renderer: &mut dyn Renderer, theme: &Theme);
fn handle_event(&mut self, event: &Event);
}new(title: String, width: u32, height: u32) -> Selfadd_child(&mut self, child: Box<dyn Component>)
new(spacing: f32) -> Selfpadding(self, padding: f32) -> Selfborder(self, border: f32) -> Selfadd_child(&mut self, child: Box<dyn Component>)
- Similar to VStack
new(rows: usize, cols: usize, spacing: f32) -> Selfset_child(&mut self, row: usize, col: usize, child: Box<dyn Component>)
new(border_width: f32, padding: f32) -> Selfchild(self, child: Box<dyn Component>) -> Self
new(label: String) -> Selfon_click<F>(self, f: F) -> Self where F: FnMut() + 'staticpadding(self, padding: f32) -> Selfborder(self, border: f32) -> Self
new(text: Binding<String>) -> Selfpadding(self, padding: f32) -> Self
new(is_on: Binding<bool>) -> Selfon_toggle<F>(self, f: F) -> Self where F: FnMut(bool) + 'static
new(text: Binding<String>, placeholder: String) -> Self
new(initial: T) -> Selfget(&self) -> T where T: Cloneset(&self, new_value: T)binding(&self) -> Binding<T>
get(&self) -> T where T: Cloneset(&self, new_value: T)
draw_text(&mut self, text: &str, x: f32, y: f32)draw_rect(&mut self, x: f32, y: f32, w: f32, h: f32)
new(title: &str, width: u32, height: u32) -> Result<Self, String>run(&mut self, root_component: Box<dyn Component>, theme: &Theme) -> Result<(), String>
primary_color: (u8, u8, u8)secondary_color: (u8, u8, u8)background_color: (u8, u8, u8)text_color: (u8, u8, u8)font_size: u32
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Submit a pull request
This project is licensed under the MIT License.
- Animation system
- System services (Auth, Store, Settings, Notifications)
- WebView integration
- Vulkan renderer
- More components (List, ScrollView, etc.)
- Accessibility support
- Internationalization