zsui 0.3.0-alpha.2

Rust-first feature-gated native UI framework for Win32, AppKit, and Linux.
Documentation
#![cfg_attr(all(windows, not(debug_assertions)), windows_subsystem = "windows")]

use std::{env, fs};

use zsui::{
    button, column, native_window, row, styled_text, textbox, toggle, AppCx, Command, CommandId,
    Dp, NativeWindowRuntimeDriver, NativeWindowSmokeRunOptions, SemanticTextStyle, UiCommand,
    ViewNode, WidgetId,
};

const SAVE: WidgetId = WidgetId::new(1);
const NAME: WidgetId = WidgetId::new(2);
const DARK_MODE: WidgetId = WidgetId::new(3);

#[derive(Debug, Clone, PartialEq, Eq)]
enum Msg {
    SaveClicked,
    NameChanged(String),
    DarkModeChanged(bool),
}

struct AppState {
    name: String,
    dark_mode: bool,
    save_count: u32,
}

fn view(state: &AppState) -> ViewNode<Msg> {
    column([
        styled_text(format!("Hello, {}", state.name), SemanticTextStyle::body()),
        textbox(&state.name).id(NAME).on_change(Msg::NameChanged),
        row([
            styled_text("Dark mode", SemanticTextStyle::body()),
            toggle(state.dark_mode)
                .id(DARK_MODE)
                .on_toggle(Msg::DarkModeChanged),
        ]),
        button("Save").id(SAVE).on_click(Msg::SaveClicked),
        styled_text(
            format!("Saved {} time(s)", state.save_count),
            SemanticTextStyle::body(),
        ),
    ])
    .gap(Dp::new(12.0))
    .padding(Dp::new(20.0))
}

fn update(state: &mut AppState, msg: Msg, cx: &mut AppCx) {
    match msg {
        Msg::SaveClicked => {
            state.save_count += 1;
            cx.command(Command::custom("settings.save"));
            cx.ui_command(UiCommand::app(CommandId("settings.persist")));
        }
        Msg::NameChanged(name) => state.name = name,
        Msg::DarkModeChanged(dark_mode) => state.dark_mode = dark_mode,
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args = env::args().skip(1).collect::<Vec<_>>();
    let builder = native_window("ZSUI Stateful View")
        .size(520, 360)
        .stateful_view(
            AppState {
                name: "ZSUI".to_string(),
                dark_mode: false,
                save_count: 0,
            },
            view,
            update,
        )
        .app_command_executor(NativeWindowRuntimeDriver::new())
        .ui_command_executor(NativeWindowRuntimeDriver::new());

    if args.iter().any(|arg| arg == "--smoke") {
        let artifact_dir = "target/zsui-stateful-view";
        fs::create_dir_all(artifact_dir)?;
        let report = builder.run_smoke(
            NativeWindowSmokeRunOptions::new(1200)
                .screenshot_file(format!("{artifact_dir}/window.png"))
                .require_screenshot(true)
                .native_view_click_widget(NAME)
                .native_view_text_input(" Native")
                .native_view_click_widget(DARK_MODE)
                .native_view_click_widget(SAVE),
        )?;
        assert!(report.native_view_text_input_count > 0);
        assert_eq!(report.native_view_toggle_count, 1);
        assert!(report.native_view_message_count >= 3);
        assert_eq!(report.native_view_unhandled_click_count, 0);
        println!("{}", serde_json::to_string_pretty(&report)?);
        return Ok(());
    }

    if args.iter().any(|arg| arg == "--manifest") {
        println!(
            "{}",
            serde_json::to_string_pretty(&serde_json::json!({
                "draw_command_count": builder.native_draw_plan().map(|plan| plan.command_count()),
                "hit_target_count": builder.native_view_interaction_plan().map(|plan| plan.hit_target_count()),
                "live_runtime": builder.native_live_view_runtime().is_some(),
                "app_command_executor": builder.native_app_command_executor().is_some(),
                "ui_command_executor": builder.native_ui_command_executor().is_some(),
                "revision": builder.native_live_view_runtime().map(|runtime| runtime.revision()),
            }))?
        );
        return Ok(());
    }

    builder.run()?;
    Ok(())
}