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::sync::Arc;

use zsui::{
    button, column, image_preview, native_window, row, text, AppCx, Dp,
    NativeWindowSmokeRunOptions, ThemeColorToken, ViewNode, WidgetId, ZsIcon, ZsImageFit,
    ZsImageFrameId, ZsImagePreviewConfig, ZsImagePreviewState, ZsuiError, ZsuiResult,
};

const PREVIEW: WidgetId = WidgetId::new(1);

struct State {
    preview: ZsImagePreviewState,
    alternate: bool,
}

impl State {
    fn new() -> ZsuiResult<Self> {
        let mut preview = ZsImagePreviewState::new(ZsImagePreviewConfig::default())?;
        preview.set_png(ZsImageFrameId::new(1), icon_png(ZsIcon::Image)?);
        Ok(Self {
            preview,
            alternate: false,
        })
    }
}

#[derive(Debug, Clone, Copy)]
enum Msg {
    SwitchImage,
}

fn view(state: &State) -> ViewNode<Msg> {
    let snapshot = state.preview.snapshot();
    let status = if snapshot.loading {
        "Decoding image"
    } else if snapshot.last_error.is_some() {
        "Image decode failed"
    } else {
        "Image ready"
    };
    let preview = image_preview(&snapshot)
        .id(PREVIEW)
        .image_fit(ZsImageFit::Contain)
        .height(Dp::new(360.0))
        .bg(ThemeColorToken::SurfaceRaised);
    column([
        row([
            text(status).flex(1.0),
            button("Switch image").on_click(Msg::SwitchImage),
        ])
        .height(Dp::new(40.0))
        .gap(Dp::new(8.0)),
        preview,
    ])
    .padding(Dp::new(16.0))
    .gap(Dp::new(12.0))
    .bg(ThemeColorToken::Surface)
}

fn update(state: &mut State, message: Msg, _cx: &mut AppCx) {
    match message {
        Msg::SwitchImage => {
            state.alternate = !state.alternate;
            let icon = if state.alternate {
                ZsIcon::Search
            } else {
                ZsIcon::Image
            };
            if let Ok(bytes) = icon_png(icon) {
                state.preview.set_png(
                    ZsImageFrameId::new(if state.alternate { 2 } else { 1 }),
                    bytes,
                );
            }
        }
    }
}

fn icon_png(icon: ZsIcon) -> ZsuiResult<Arc<[u8]>> {
    icon.png_24_bytes().map(Arc::from).ok_or_else(|| {
        ZsuiError::invalid_spec("image_preview.icon", "icon has no PNG fallback asset")
    })
}

fn main() -> ZsuiResult<()> {
    let builder = native_window("ZSUI Retained Image Preview")
        .size(720, 500)
        .min_size(480, 360)
        .stateful_view(State::new()?, view, update);
    if std::env::args().any(|argument| argument == "--smoke") {
        let report = builder.run_smoke(NativeWindowSmokeRunOptions::new(700))?;
        if !report.visible_window_was_created() {
            return Err(ZsuiError::host(
                "image_preview_smoke",
                "the preview window was not visible",
            ));
        }
    } else {
        builder.run()?;
    }
    Ok(())
}