fltk-decl 0.2.0

Describe your fltk-rs app declaratively, supports hot-reloading!
Documentation
# 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 load_fn(path: &'static str) -> Option<Widget> {
    let s = std::fs::read_to_string(path).ok()?;
    // We want to see the serde error on the command line while we're developing
    serde_json::from_str(&s).map_err(|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", load_fn).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::widget_from_id("result").unwrap();
        self.count += val;
        result.set_label(&self.count.to_string());
    }
}

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

fn load_fn(path: &'static str) -> Option<Widget> {
    let s = std::fs::read_to_string(path).ok()?;
    serde_json::from_str(&s).map_err(|e| eprintln!("{e}")).ok()
}

fn main() {
    app::GlobalState::new(State { count: 0 });
    DeclarativeApp::new(200, 300, "MyApp", PATH, load_fn)
        .run(|_win| {
            app::set_scheme(app::Scheme::Oxy);
            if let Some(mut btn) = app::widget_from_id::<button::Button>("inc") {
                btn.set_callback(btn_cb);
            }
            if let Some(mut btn) = app::widget_from_id::<button::Button>("dec") {
                btn.set_callback(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:
- widget: (Required) The widget type (string)
- label: The widget label (string)
- fixed: Whether the widget is fixed inside a Flex (integer)
- id: The widget's id (string)
- labelcolor: The widget's label color (string, format #xxxxxx)
- color: The widget's color (string, format #xxxxxx)
- selectioncolor: The widget's selection color (string, format #xxxxxx)
- hide: Whether the widget is hidden (bool)
- visible: Whether the widget is visible (bool)
- deactivate: Whether the widget is deactivated (bool)
- resizable: Whether the widget is the resiable widget in a group (bool)
- tooltip: The widget's tooltip (string)
- image: A path to an image for the widget (string)
- deimage: A path to an image (deactivated) for the widget (string)
- labelfont: The label font (integer)
- labelsize: The label size (integer)
- align: The label's alignment (integer)
- when: The widget's callback trigger (integer)
- frame: The widget's frame type (integer)
- downframe: The widget's down_frame type, for buttons (integer)
- shortcut: The widget's shortcut, for buttons (string)
- pad: The Flex's padding (integer)
- minimun: The valuator's minimum value (floating point number)
- maximum: The valuator's maximum value (floating point number)
- slidersize: The valuator's slider size (floating point number)
- step: The valuator's step (floating point number)
- textcolor: The widget's text color (string)
- textsize: The widget's text size (integer)
- textfont: The widget's font (integer)
- children: an array of widgets representing the children of the widget (array of objects)

## Supported widgets:
- Column (Flex column)
- Row (Flex row)
- Button 
- CheckButton 
- RadioButton 
- ToggleButton 
- RadioRoundButton 
- ReturnButton 
- Frame 
- Group 
- Pack 
- Tile 
- Tabs 
- Scroll 
- ColorChooser 
- TextDisplay
- TextEditor
- Input 
- IntInput 
- FloatInput 
- SecretInput 
- FileInput 
- MultilineInput 
- Output 
- MultilineOutput 
- MenuBar 
- SysMenuBar 
- Choice 
- Slider 
- NiceSlider 
- FillSlider 
- ValueSlider 
- Dial 
- LineDial 
- FillDial 
- Counter 
- Scrollbar 
- Roller 
- Adjuster 
- ValueInput 
- ValueOutput 
- HorSlider 
- HorNiceSlider 
- HorFillSlider 
- HorValueSlider 
- Browser 
- SelectBrowser 
- HoldBrowser 
- FileBrowser 
- CheckBrowser 
- MultiBrowser 
- Table 
- TableRow 
- Tree 
- Spinner 
- Chart 
- Progress 
- InputChoice 
- HelpView