swiftui-native 0.0.1

SwiftUI from pure Rust — no Swift bridge, no shim, only swift-runtime calls
Documentation
//! A dashboard — the third shape, after a form and a list.
//!
//! Grid layout, gauges, shapes drawn from data, and a value that animates when
//! Rust changes it. Where `settings.rs` is bound controls and `inbox.rs` is
//! navigation, this is the read-only screen that has to *move*.
//!
//! ```sh
//! cargo run -p swiftui-native --example dashboard
//! cargo run -p swiftui-native --example dashboard -- --render
//! ```

use std::sync::atomic::{AtomicU64, Ordering};
use swiftui_native::prelude::*;
use swiftui_native::{state, views};

/// The reading each tile shows, as a percentage. Rust owns it; the screen is a
/// function of it.
static LOAD: [AtomicU64; 3] = [AtomicU64::new(34), AtomicU64::new(71), AtomicU64::new(12)];

const LABELS: [&str; 3] = ["CPU", "Memory", "Disk"];
const TINTS: [SystemColor; 3] = [SystemColor::Blue, SystemColor::Orange, SystemColor::Green];

fn reading(i: usize) -> f64 {
    LOAD[i].load(Ordering::Relaxed) as f64 / 100.0
}

/// One tile: a ring showing the fraction, the number over it, and a caption.
fn tile(i: usize) -> View {
    let value = reading(i);
    vstack_spacing(
        &[
            zstack![
                circle_shape().stroke_system(SystemColor::Gray, 6.0).opacity(0.25),
                // The filled part: the same circle trimmed to the fraction, and
                // turned so the arc starts at the top rather than at three
                // o'clock.
                circle_shape()
                    .trim(0.0, value)
                    .stroke_system(TINTS[i], 6.0)
                    .rotate(-90.0),
                text!("{:.0}", value * 100.0).text_style(TextStyle::Title2).bold(),
            ]
            .frame(72.0, 72.0),
            text(LABELS[i]).font_size(12.0).fg_system(SystemColor::Gray),
        ],
        8.0,
    )
    .padding(12.0)
    .a11y_element(AccessibilityChildBehavior::Combine)
    .a11y_label(LABELS[i])
    .a11y_value(&format!("{:.0} percent", value * 100.0))
}

fn body() -> View {
    vstack_aligned_spacing(
        &[
            text("System").text_style(TextStyle::LargeTitle).bold(),
            grid(&[grid_row(&[tile(0), tile(1), tile(2)])]),
            divider(),
            // A gauge over the busiest reading, which is a different control
            // from the rings and reads the same state.
            vstack_aligned(
                &[
                    text("Busiest").font_size(12.0).fg_system(SystemColor::Gray),
                    gauge(reading(1), 0.0, 1.0, text("Memory"))
                        .gauge_style(views::GaugeStyle::AccessoryLinearCapacityGaugeStyle)
                        .tint_system(SystemColor::Orange),
                ],
                HorizontalAlignment::Leading,
            ),
            spacer(),
            button("Refresh", refresh)
                .button_style(views::ButtonStyle::BorderedProminent)
                .keyboard_shortcut(KeyboardShortcut::DefaultAction),
        ],
        HorizontalAlignment::Leading,
        Some(16.0),
    )
    .padding(20.0)
    // Animate whatever changed, rather than snapping to the new reading.
    .animation(LOAD[0].load(Ordering::Relaxed) as i64)
}

/// New readings, applied inside an animation so the rings sweep.
fn refresh() {
    static SEED: AtomicU64 = AtomicU64::new(0x9E37_79B9);
    for slot in &LOAD {
        let mut x = SEED.load(Ordering::Relaxed);
        x ^= x << 13;
        x ^= x >> 7;
        x ^= x << 17;
        SEED.store(x, Ordering::Relaxed);
        slot.store(x % 101, Ordering::Relaxed);
    }
    state::refresh_animated(state::ease_in_out(0.6));
}

fn content_view() -> swiftui_native::ViewHandle {
    state::root_view(|_| body().into_handle())
}

#[cfg(target_os = "macos")]
#[path = "common/simulator.rs"]
mod simulator;

#[cfg(target_os = "macos")]
fn main() {
    if std::env::args().any(|a| a == "--sim" || a == "device") {
        return simulator::main(&simulator::Bundle {
            example: "dashboard",
            bundle_id: "dev.rswift.Dashboard",
            executable: "Dashboard",
            display_name: "Dashboard",
            launch_args: &[],
        });
    }
    if std::env::args().any(|a| a == "--render") {
        let out = concat!(env!("CARGO_MANIFEST_DIR"), "/../../target/dashboard.png");
        swiftui_native::render::render(&content_view(), 420, 480).write_png(out);
        eprintln!("wrote {out}");
        return;
    }
    show_window(&content_view(), "Dashboard", 420.0, 480.0);
}

#[cfg(not(target_os = "macos"))]
fn main() {
    swiftui_native::app::run(content_view)
}