fltk-decl

Use a declarative language (json5, json, yaml, xml, toml, s-exp) to describe your fltk-rs gui, with support for hot-reloading of your gui file. The crate is designed to be as permissive as possible. So wrong keys or values will be ignored. Normally only changing a widget's id at runtime would cause an error!

Usage

Assuming we're using json for our gui description, we'll pull fltk-decl, fltk and the deserialization library that we require, in this case it's serde_json: In your Cargo.toml: toml [dependencies] fltk-decl = "0.1" fltk = "1.3.32" serde_json = "1"

For the other data formats, you can pull the respective deserialization library: toml serde_json5 = "0.1" # for json5 serde-xml-rs = "0.6" # for xml serde_yaml = "0.9" # for yaml toml = "0.7" # for toml serde-lexpr = "0.1.2" # for an s-expression description

Since we're gonna use json, we'll create a json file and let's call it gui.json: ```json { "$schema": "https://raw.githubusercontent.com/MoAlyousef/fltk-decl/main/schemas/fltk-schema.json", "widget": "Column", "children": [ { "widget": "Button", "label": "Inc", "fixed": 60, "id": "inc", "labelcolor": "#0000ff"

    },
    {
        "widget": "Row",
        "children": [
            {
                "widget": "Frame",
                "fixed": 30
            },
            {
                "widget": "Frame",
                "label": "0",
                "id": "result",
                "labelcolor": "#ff0000"
            },
            {
                "widget": "Frame",
                "fixed": 30
            }
        ]
    },
    {
        "widget": "Button",
        "label": "Dec",
        "fixed": 60,
        "id": "dec"
    }
]

} ``` Notice we point to the schema to get auto-completion and hinting on vscode, otherwise it's optional.

Import it into your app: ```rust use fltk_decl::{DeclarativeApp, Widget};

// declare how you would like to deserialize fn loadfn(path: &'static str) -> Option { let s = std::fs::readtostring(path).ok()?; // We want to see the serde error on the command line while we're developing serdejson::fromstr(&s).maperr(|e| eprintln!("{e}")).ok() }

fn main() { // use the filetype and extension that you require. // run a callback that runs at least once, or whenever the gui file changes. DeclarativeApp::new(200, 300, "MyApp", "examples/gui.json", loadfn).run(|| {}).unwrap(); } ```

To handle callbacks: ```rust use fltk::{prelude::*, *}; use fltk_decl::{DeclarativeApp, Widget};

// use the extension you require! const PATH: &str = "examples/gui.json";

[derive(Clone, Copy)]

struct State { count: i32, }

impl State { pub fn increment(&mut self, val: i32) { let mut result: frame::Frame = app::widgetfromid("result").unwrap(); self.count += val; result.setlabel(&self.count.tostring()); } }

fn btn_cb(b: &mut button::Button) { let state = app::GlobalState::::get(); let val = if b.label() == "Inc" { 1 } else { -1 }; state.with(move |s| s.increment(val)); }

fn loadfn(path: &'static str) -> Option { let s = std::fs::readtostring(path).ok()?; serdejson::fromstr(&s).maperr(|e| eprintln!("{e}")).ok() }

fn main() { app::GlobalState::new(State { count: 0 }); DeclarativeApp::new(200, 300, "MyApp", PATH, loadfn) .run(|win| { app::setscheme(app::Scheme::Oxy); if let Some(mut btn) = app::widgetfromid::("inc") { btn.setcallback(btncb); } if let Some(mut btn) = app::widgetfromid::("dec") { btn.setcallback(btn_cb); } }) .unwrap(); } ```

Other data formats:

You can choose json5 (to benefit from comments, trailing commas and unquoted keys!): json5 { // main column widget: "Column", children: [ { // our button widget: "Button", label: "Click me", color: "#ff0000", id: "my_button", } ], } However, you lose vscode's auto-completion since json5 extensions in vscode don't support schemas.

You could also use yaml (optionally along with a schema for autocompletion and validation): ```yaml

yaml-language-server: $schema=https://raw.githubusercontent.com/MoAlyousef/fltk-decl/main/schemas/fltk-schema.yaml

widget: Column children: - widget: Button label: Inc fixed: 60 id: inc labelcolor: "#0000ff" - widget: Row children: - widget: Frame fixed: 30 - widget: Frame label: '0' id: result labelcolor: "#ff0000" - widget: Frame fixed: 30 - widget: Button label: Dec fixed: 60 id: dec ```

You could also use xml: gui.xml xml <?xml version="1.0" encoding="UTF-8"?> <root> <widget>Column</widget> <children> <widget>Button</widget> <label>Click Me</label> <id>my_button</id> <labelcolor>#0000ff</labelcolor> </children> </root>

or toml! ```toml widget = "Column"

[[children]] widget = "Button" label = "Click Me" id = "my_button" ```

or s-expression format: scheme ( (widget . "Column") (children ( ( (widget . "Button") (label "Inc") (id "inc") (fixed 60) (labelcolor "#0000ff") ) ( (widget . "Frame") (id "result") (label "0") ) ( (widget . "Button") (label "Dec") (id "dec") (fixed 60) (labelcolor "#ff0000") ) )) )

Supported properties:

Supported widgets: