cocoanut 0.2.3

A minimal, declarative macOS GUI framework for Rust
docs.rs failed to build cocoanut-0.2.3
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

🥥 Cocoanut

A minimal, declarative macOS GUI framework for Rust.

Why Cocoanut?

Crate Level Safety Learning Curve Best For
objc Low Unsafe Steep Raw FFI
cocoa Low Unsafe Steep Raw AppKit
cacao Mid Safe Medium Cross-platform
cocoanut High Safe Gentle macOS native

Quick Start

use cocoanut::prelude::*;

fn main() -> cocoanut::Result<()> {
    let count = state(0);
    let inc = count.clone();
    let dec = count.clone();

    app("Counter")
        .size(400.0, 300.0)
        .build()
        .root(
            View::vstack()
                .child(View::text("Counter").bold().font_size(20.0))
                .child(View::label(&format!("Count: {}", count.get())).font_size(32.0))
                .child(View::hstack()
                    .child(View::button("+").on_click_fn(move || inc.increment()))
                    .child(View::button("-").on_click_fn(move || dec.decrement()))
                )
        )
        .run()
}

What's New in v0.3.0

Direct Closures

No more manual event ID management:

// Before (v0.2.0)
event::register(1, || count.set(count.get() + 1));
View::button("+").on_click(1)

// After (v0.3.0)
View::button("+").on_click_fn(|| count.increment())

State Helpers

let count = state(0);
count.increment();  // +1
count.decrement();  // -1
count.reset();      // → 0

let text = state(String::new());
text.append("hello");
text.clear();

let flag = state(false);
flag.toggle();

Styling DSL

use cocoanut::prelude::*;

let btn_style = style()
    .width(100.0)
    .height(40.0)
    .padding(10.0)
    .background(color("blue"))
    .foreground("white")
    .corner_radius(8.0);

View::button("Click Me").styled(&btn_style)

Component Macros

component! {
    header,
    View::hstack()
        .child(View::text("My App").bold())
        .child(View::spacer())
        .child(View::button("Settings"))
}

stateful_component! {
    Counter {
        count: State<i64>,
        label: String,
    } => |this| {
        View::vstack()
            .child(View::text(&this.label))
            .child(View::button("+").on_click_fn(move || this.count.increment()))
    }
}

Async Support

View::button("Fetch Data")
    .on_click_async(|| async {
        let data = fetch_from_api().await;
        state.set(data);
    })

CLI Tool

# Install
cd cli && cargo install --path .

# Create new project
cocoanut new myapp
cd myapp && cargo run

# Development
cocoanut run --watch    # Hot reload
cocoanut build --release

Architecture

12 source files. ~4000 lines. 32 view types. Extensive unit tests. Type-safe APIs.

view.rs          → View + ViewKind enum (the ONE core type)
renderer.rs      → View tree → AppKit NSViews (single pass)
app.rs           → App lifecycle + Appearance (dark mode)
event.rs         → Callback registry + ObjC action handler
state.rs         → Reactive State<T> + bind
menu.rs          → Menu bar with action dispatch
layout.rs        → Auto Layout support (NSLayoutConstraint)
native.rs        → Native view properties & utilities
delegate.rs      → Delegate patterns for native controls
component.rs     → Component macros
style.rs         → CSS-like styling DSL
async_support.rs → Async/await support

32 View Types

  • Layout: VStack, HStack, ZStack, Spacer
  • Text: Text, Label
  • Controls: Button, TextField, SecureField, Checkbox, Radio, Slider, Toggle, Dropdown
  • Extended: TextArea, DatePicker, ColorPicker
  • Containers: ScrollView, TabView, SplitView, GroupBox, WebView, TableView
  • Data: ProgressBar, Image
  • Native Controls: SegmentedControl, ComboBox, SearchField, Stepper, LevelIndicator, PathControl
  • Extensible: Custom

Features

Core Features

  • Reactive State — State<T> with on_change listeners
  • Direct Closures — on_click_fn(|| ...) without manual IDs
  • Event System — event::register(id, closure) + ObjC target/action dispatch
  • Dark Mode — .dark() / .light() / set_appearance() at runtime
  • Accessibility — .accessibility("label") on any view
  • Style Modifiers — .width(), .bold(), .background(), .on_click()
  • CSS-like Styling — style().width(100).padding(10).background("blue")
  • JSON Serializable — ViewDesc for debugging/bridging

Native Cocoa Integration

  • Auto Layout — NSLayoutConstraint support with type-safe API
  • Native Properties — Direct access to shadows, layers, corner radius, opacity
  • Responder Chain — First responder management and key view navigation
  • Delegate Patterns — TableView data sources, TextField delegates
  • Animation Support — Native AppKit animations
  • Pasteboard — Clipboard operations (copy/paste)
  • Window Management — Window level, alpha, background color

See NATIVE_COCOA_IMPROVEMENTS.md for detailed documentation.

Examples

cargo run --example minimal_app      # basic view tree
cargo run --example counter_app      # reactive state + dark mode
cargo run --example ui_showcase      # all view types + new API
cargo run --example native_features  # native Cocoa integration

Developing

cargo test — full library tests (AppKit stubs only under cfg(test)). cargo build --workspace includes the optional CLI crate.

License

Apache-2.0