std1.0.0-beta-7 SafeC docs

Source gui/gui_app.h

View in the repository

#pragma once
// SafeC Standard Library — GUI: the application object.
//
// A GuiApp owns any number of windows and runs them from one event loop:
// it pumps the platform's events, routes each to the window it belongs to
// (to that window's widget tree and/or your event callback), fires timers
// and per-frame animation callbacks, repaints the windows that changed and
// presents them, and closes a window when its close button is clicked
// (unless the GUI_EVENT_CLOSE is prevent_default()ed).
//
//   struct GuiApp app = GuiApp::new();
//   ?&GuiWindow ow = app.open_window("Hello", 480, 320);
//   match (ow) {
//       case some(win): { app.set_root(win, build_ui()); }
//       case null: { return 1; }       // no display
//   }
//   int code = app.run();             // until the last window closes or quit()
//   app.free();
//
// Driving the loop yourself (a game loop, an embedding host, or a
// WebAssembly page that can't block): call app.pump(0) once per frame —
// it does one iteration without waiting and returns 0 once the app should
// stop. app.pump(timeoutMs) with a positive timeout waits up to that long
// for the next event, timer or frame.
//
// Rendering: a window with a widget root is laid out and painted by its
// GuiUi; a window with an on_draw callback gets a GuiCanvas over its
// buffer (after the widgets, if it has both); a window with neither shows
// whatever you put in win.pixels yourself. Windows are repainted only
// when something changed: an input event, a resize, a timer or animation
// callback, or app.redraw(win). on_frame callbacks run once per frame
// (frame_interval(), 60 Hz by default) while they return 1, with the
// frame time in milliseconds — the animation API.
//
// Timers: set_timeout(ms, cb, data) runs cb once, set_interval(ms, ...)
// every ms until cancel_timer(id) (ids are never 0). They run on the loop
// (never concurrently with event handling), as close to their due time as
// the loop allows.

#include <std/gui/gui.h>
#include <std/gui/gui_canvas.h>
#include <std/gui/gui_widget.h>
#include <std/collections/vec.h>

namespace std {

// One window and what the app does with it (internal; see the methods).
struct GuiAppWindow {
    &GuiWindow win;
    ?&GuiUi ui;
    // The callbacks with their contexts, erased (see std/erased.h).
    ?fn int(&struct GuiApp, &GuiWindow, &GuiEvent, ?&Erased)  onEvent; ?&Erased onEventData;
    ?fn void(&struct GuiApp, &GuiWindow, &GuiCanvas, ?&Erased) onDraw; ?&Erased onDrawData;
    ?fn int(&struct GuiApp, &GuiWindow, double, ?&Erased)     onFrame; ?&Erased onFrameData;
    struct GuiCanvas canvas;
    int canvasReady;
    int dirty;
    int closing;
};

struct GuiTimer {
    unsigned int id;
    double due;
    double interval;      // 0: one-shot
    ?fn void(&struct GuiApp, unsigned int, ?&Erased) cb;   // erased (see std/erased.h)
    ?&Erased data;
    int dead;
};

struct GuiApp {
    impl !Send + !Sync;   // lives on the thread that created it (the UI thread)
    struct Vec<&GuiAppWindow> windows;
    struct Vec<struct GuiTimer> timers;
    unsigned int nextTimer;
    int quitting;
    int exitCode;
    double frameMs;
    double lastFrame;

    static struct GuiApp new();

    // ── windows ──────────────────────────────────────────────────────────
    // Opens a window (hiDpi). null if there's no display / it failed.
    ?&GuiWindow open_window(const &char title, int width, int height);
    ?&GuiWindow open(const &GuiWindowOptions opts);
    // Destroys the window (and its widget tree, if any) now.
    void close_window(&GuiWindow win);
    unsigned long window_count() const;
    ?&GuiWindow window_at(unsigned long i) const;
    ?&GuiWindow find_window(unsigned int id) const;
    // Shows 'root' in the window (the window owns the tree from now on:
    // closing the window frees it). Returns the window's GuiUi.
    &GuiUi set_root(&GuiWindow win, &Widget root);
    ?&GuiUi ui_of(const &GuiWindow win) const;
    // Repaint the window on the next iteration.
    void redraw(&GuiWindow win);

    // ── callbacks ────────────────────────────────────────────────────────
    // Every event for the window (after its widgets saw it): return 1 if
    // you handled it (the window is repainted).
    generic<T> void on_event(&GuiWindow win, fn int(&GuiApp, &GuiWindow, &GuiEvent, ?&T) cb, ?&T data);
    // Custom drawing, each repaint.
    generic<T> void on_draw(&GuiWindow win, fn void(&GuiApp, &GuiWindow, &GuiCanvas, ?&T) cb, ?&T data);
    // Animation: called every frame with the time in ms while it returns 1.
    generic<T> void on_frame(&GuiWindow win, fn int(&GuiApp, &GuiWindow, double, ?&T) cb, ?&T data);
    void on_event_raw_(&GuiWindow win, fn int(&GuiApp, &GuiWindow, &GuiEvent, ?&Erased) cb, ?&Erased data);
    void on_draw_raw_(&GuiWindow win, fn void(&GuiApp, &GuiWindow, &GuiCanvas, ?&Erased) cb, ?&Erased data);
    void on_frame_raw_(&GuiWindow win, fn int(&GuiApp, &GuiWindow, double, ?&Erased) cb, ?&Erased data);

    // ── timers ───────────────────────────────────────────────────────────
    generic<T> unsigned int set_timeout(double ms, fn void(&GuiApp, unsigned int, ?&T) cb, ?&T data);
    generic<T> unsigned int set_interval(double ms, fn void(&GuiApp, unsigned int, ?&T) cb, ?&T data);
    unsigned int add_timer_raw_(double ms, int repeat, fn void(&GuiApp, unsigned int, ?&Erased) cb, ?&Erased data);
    void cancel_timer(unsigned int id);

    // ── the loop ─────────────────────────────────────────────────────────
    // One iteration: wait up to 'timeoutMs' (0 = don't wait, negative = as
    // long as nothing is due) for events, route them, run due timers and
    // frame callbacks, repaint and present what changed. Returns 0 once the
    // app should stop (quit() was called or no window is left).
    int  pump(int timeoutMs);
    // pump() until it returns 0; returns the exit code given to quit()
    // (0 when the last window closed).
    int  run();
    void quit(int exitCode);
    // gui_time_ms()
    double now_ms() const;
    void set_frame_interval(double ms);
    double frame_interval() const;
    // Closes every window and frees the app's state.
    void drop();
    void free() consume;   // = drop()
};

generic<T>
void GuiApp::on_event(&GuiWindow win, fn int(&GuiApp, &GuiWindow, &GuiEvent, ?&T) cb, ?&T data) {
    unsafe { self.on_event_raw_(win, (fn int(&GuiApp, &GuiWindow, &GuiEvent, ?&Erased))cb, (?&Erased)data); }
}

generic<T>
void GuiApp::on_draw(&GuiWindow win, fn void(&GuiApp, &GuiWindow, &GuiCanvas, ?&T) cb, ?&T data) {
    unsafe { self.on_draw_raw_(win, (fn void(&GuiApp, &GuiWindow, &GuiCanvas, ?&Erased))cb, (?&Erased)data); }
}

generic<T>
void GuiApp::on_frame(&GuiWindow win, fn int(&GuiApp, &GuiWindow, double, ?&T) cb, ?&T data) {
    unsafe { self.on_frame_raw_(win, (fn int(&GuiApp, &GuiWindow, double, ?&Erased))cb, (?&Erased)data); }
}

generic<T>
unsigned int GuiApp::set_timeout(double ms, fn void(&GuiApp, unsigned int, ?&T) cb, ?&T data) {
    unsafe { return self.add_timer_raw_(ms, 0, (fn void(&GuiApp, unsigned int, ?&Erased))cb, (?&Erased)data); }
}

generic<T>
unsigned int GuiApp::set_interval(double ms, fn void(&GuiApp, unsigned int, ?&T) cb, ?&T data) {
    unsafe { return self.add_timer_raw_(ms, 1, (fn void(&GuiApp, unsigned int, ?&Erased))cb, (?&Erased)data); }
}

} // namespace std