β οΈ Work In Progress: This project is actively being developed. APIs may change without notice.
A declarative, reactive UI framework for building native macOS applications with Rust.
- Declarative API: Build UIs using a fluent, chainable API similar to SwiftUI
- Reactive State: Automatic UI updates with
use_statehook - Type-Safe: Leverage Rust's type system for compile-time safety
- Native Performance: Direct bindings to AppKit via objc2
- 30+ Components: Rich set of pre-built components
- Chain-able Methods: Style and configure components with method chaining
- Event Handling: Simple callbacks for user interactions
Add to your Cargo.toml:
[dependencies]
ui-native = { path = "../ui-native" }use ui_native::prelude::*;
fn main() {
let mut count = use_state(|| 0);
let ui = rect()
.width(Size::fill())
.height(Size::fill())
.background((240, 240, 245))
.child(
Button::new()
.title("Click Me!")
.on_press(move |_| {
*count.write() += 1;
println!("Count: {}", count.read());
})
.width(Size::Fixed(200.0))
.height(Size::Fixed(44.0))
.builder()
);
let window = ui.build();
}Use use_state to create reactive values that automatically update the UI:
let count = use_state(|| 0);
// Read value
println!("Current: {}", count.read());
// Write value (triggers updates)
*count.write() += 1;
// Subscribe to changes
count.subscribe(|value| {
println!("Changed to: {}", value);
});Flexible sizing with Size enum:
rect()
.width(Size::Fixed(200.0)) // Fixed width
.height(Size::Percent(50.0)) // 50% of parent
.width(Size::Fill) // Fill available space
.height(Size::Auto) // Based on contentChain styling methods for any component:
rect()
.background((15, 163, 242)) // RGB color
.corner_radius(10.0) // Rounded corners
.shadow((0., 4., 20., 4., (0,0,0,80))) // Shadow effect
.alpha(0.9) // Opacity
.padding(EdgeInsets::all(16.0)) // PaddingSimple callback-based event handling:
Button::new()
.title("Save")
.on_press(|_| {
println!("Saving...");
})- rect() - Container view
- Label - Static text display
- Button - Clickable button
- TextField - Text input field
- TextView - Multi-line text editor
- Image - Image display
- Slider - Value slider
- Stepper - Increment/decrement control
- Checkbox - Checkbox toggle
- RadioButton - Radio button selection
- Toggle - On/off switch
- Picker - Dropdown picker
- ComboBox - Dropdown with text input
- SegmentedControl - Multi-segment selector
- DatePicker - Date selection
- ColorPicker - Color selection
- SearchField - Search input
- Stack (hstack/vstack) - Horizontal/vertical layout
- ScrollView - Scrollable container
- SplitView - Split panel container
- TabView - Tabbed interface
- BoxView - Grouped container with border/title
- VisualEffectView - Blur/vibrancy effects
- ListView - List of items
- TableView - Table data display
- CollectionView - Grid layout (image galleries, etc.)
- ProgressBar - Progress indicator
- Window - Application window
- Panel - Lightweight utility window
- OpenPanel - File open dialog
- SavePanel - File save dialog
- Alert - Alert dialog
- Toolbar - Window toolbar
- Menu - Context/menu bar menu
use ui_native::prelude::*;
fn counter_app() {
let count = use_state(|| 0);
let count_inc = count.clone();
let count_dec = count.clone();
let ui = rect()
.child(
// Display
Label::new().text(format!("Count: {}", count.read()))
.font_size(72.)
.font_weight(FontWeight::Bold)
.builder()
)
.child(
// Buttons
Stack::horizontal()
.spacing(12.0)
.child(
Button::new()
.title("β")
.on_press(move |_| *count_inc.write() += 1)
.builder()
)
.child(
Button::new()
.title("β")
.on_press(move |_| *count_dec.write() -= 1)
.builder()
)
.builder()
);
}use ui_native::prelude::*;
fn form_example() {
let name = use_state(|| String::new());
let email = use_state(|| String::new());
vstack()
.spacing(16.0)
.child(
TextField::new()
.placeholder("Name")
.width(Size::fill())
.builder()
)
.child(
TextField::new()
.placeholder("Email")
.width(Size::fill())
.builder()
)
.child(
Button::new()
.title("Submit")
.on_press(|_| {
println!("Form submitted!");
})
.builder()
)
.builder()
}use ui_native::prelude::*;
fn gallery() {
hstack()
.spacing(8.0)
.child(
Image::from_path("/path/to/image1.png")
.scale_mode(ImageScaleMode::AspectFit)
.width(Size::Fixed(200.0))
.height(Size::Fixed(200.0))
.corner_radius(8.0)
.builder()
)
.child(
Image::from_path("/path/to/image2.png")
.scale_mode(ImageScaleMode::AspectFit)
.width(Size::Fixed(200.0))
.height(Size::Fixed(200.0))
.corner_radius(8.0)
.builder()
)
.builder()
}use ui_native::prelude::*;
// Create a window
fn create_window() {
let window = Window::new()
.title("My App")
.size(800.0, 600.0)
.center()
.show();
}
// Open file dialog
fn open_file() {
if let Some(paths) = OpenPanel::new()
.title("Select Image")
.can_choose_files(true)
.allow_multiple_selection(false)
.allowed_file_types(vec!["png".into(), "jpg".into()])
.show()
{
println!("Selected: {:?}", paths);
}
}
// Save file dialog
fn save_file() {
if let Some(path) = SavePanel::new()
.title("Save Image")
.filename("untitled.png")
.allowed_file_types(vec!["png".into()])
.show()
{
println!("Save to: {}", path);
}
}use ui_native::prelude::*;
fn modern_panel() {
Panel::new()
.title("Settings")
.size(400.0, 300.0)
.center()
.show();
// Blur effect background
VisualEffectView::new()
.material(Material::Sidebar)
.blend_mode(BlendMode::BehindWindow)
.emphasized(true)
.width(Size::fill())
.height(Size::fill())
.builder();
}use ui_native::prelude::*;
fn advanced_inputs() {
vstack()
.spacing(16.0)
.child(
// Dropdown with text input
ComboBox::new()
.add_items(vec![
"Option 1".into(),
"Option 2".into(),
"Option 3".into(),
])
.placeholder("Select or type...")
.autocomplete(true)
.width(Size::fill())
.builder()
)
.child(
// Multi-line text editor
TextView::new()
.text("Enter your notes here...")
.word_wrap(true)
.font_size(14.0)
.width(400.0)
.height(200.0)
)
.builder()
}use ui_native::prelude::*;
fn image_grid() {
CollectionView::new()
.item_size(150.0, 150.0)
.item_spacing(10.0)
.line_spacing(10.0)
.columns(3)
.selectable(true)
.allow_multiple_selection(true)
.width(500.0)
.height(400.0);
}// RGB tuple
.background((255, 100, 50))
// RGBA tuple
.background((255, 100, 50, 128))
// Named colors
.background(Color::blue())
.background(Color::red())
.background(Color::gray(128))
// Custom color
.background(Color::rgb(15, 163, 242)).font_size(16.0)
.font_weight(FontWeight::Bold)
.font_weight(FontWeight::Medium)
.font_weight(FontWeight::Light)// (offset_x, offset_y, blur_radius, spread_radius, color)
.shadow((0., 4., 20., 4., (0, 0, 0, 80))).corner_radius(10.0) // Uniform
.corner_radius(CornerRadius::all(10.0)) // Uniform
.corner_radius(CornerRadius::new(
10.0, // top_left
10.0, // top_right
10.0, // bottom_right
10.0 // bottom_left
)).padding(EdgeInsets::all(16.0))
.padding(EdgeInsets::symmetric(12.0, 16.0)) // vertical, horizontal
.margin(EdgeInsets::new(8.0, 12.0, 8.0, 12.0))
.center() // Center in parent
.center_x() // Center horizontally
.center_y() // Center verticallycore- Core types, state, layout, style, event systemsbuilder- UIBuilder and chainable APIcomponents- All UI components
- Declarative: Describe what you want, not how to build it
- Composable: Combine small components into complex UIs
- Type-Safe: Compile-time guarantees
- Performance: Zero-cost abstractions over AppKit
- Professional: Production-ready code quality
Create your own components by wrapping UIBuilder:
pub struct MyButton {
builder: UIBuilder<NSButton>,
}
impl MyButton {
pub fn new() -> Self {
Self {
builder: Button::new()
.background((0, 122, 255))
.corner_radius(8.0)
.builder()
}
}
pub fn on_click<F>(self, callback: F) -> Self
where F: FnMut(&AnyObject) + 'static
{
// Implement event handling
self
}
}For complex apps, use multiple states:
struct AppState {
user: State<Option<User>>,
settings: State<Settings>,
theme: State<Theme>,
}
impl AppState {
fn new() -> Self {
Self {
user: use_state(|| None),
settings: use_state(Settings::default),
theme: use_state(|| Theme::Light),
}
}
}This project is designed to be open-sourced. Please add appropriate license before publishing.
Contributions welcome! Guidelines:
- Follow Rust API guidelines
- Add tests for new features
- Update documentation
- Maintain code quality standards
Current Status: Beta - Core functionality complete, API may evolve
Completed:
- β Core architecture
- β State management
- β Layout system
- β Style system
- β Event handling
- β 30+ components
- β Window management
- β File dialogs
- β Visual effects
- β Examples and documentation
Future Enhancements:
- Animations and transitions
- Gesture recognizers
- Accessibility support
- Custom drawing/Canvas
- Performance optimizations
- More complex layout algorithms