# gui

Windows with labels, buttons, text boxes, check boxes, lists and progress bars. They're real Win32 controls, so
they look and behave like every other Windows program (themed, Tab moves between them, crisp on high-DPI screens).
Windows only.

```
basher install gui
```

Basher has no callbacks, so a GUI program is a loop that asks what happened:

```python
import gui

w = gui.window("Counter", 320, 160)
label = gui.label(w, "0", 20, 20, 280, 50)
gui.style(label, size=24, bold=True)
go = gui.button(w, "Count", 20, 100, 120, 32)
n = 0
while gui.running(w):          # until the X is clicked
    gui.wait(w)                # sleep until something happens
    if gui.clicked(go):
        n += 1
        gui.set_text(label, str(n))
```

Busy in a long loop? Call `gui.poll(w)` now and then (it never blocks) so the window keeps up, and check
`gui.running(w)` to stop when it's closed. `share/tests/hi_gui.bsh` does exactly that while counting to a million.

Build a GUI program with `basher build app.bsh --windowed` so it doesn't open a console window too.

## Windows and controls

Every window and control is an int. Positions and sizes are in pixels at 100% scale and are scaled to the screen.

| function | makes |
|---|---|
| `window(title, width=400, height=300, bg="") -> int` | a window; width/height are the inside. `bg` is a color like `"#1e1e1e"` |
| `label(window, text, x, y, width, height)` | text |
| `button(window, text, x, y, width, height)` | a button |
| `checkbox(window, text, x, y, width, height)` | a check box |
| `textbox(window, text, x, y, width, height, multiline=False)` | a text box (multiline: Enter makes new lines, scroll bar) |
| `listbox(window, x, y, width, height)` | a list to pick from |
| `progress(window, x, y, width, height)` | a progress bar, 0-100 |

## Using them

| function | does |
|---|---|
| `style(handle, size=0, bold=False, font="", color="", bg="")` | font size (points), bold, font name (`"Consolas"`), text color and background (`"#rrggbb"`). On a window: its colors |
| `set_text(handle, text)`, `get_text(handle) -> str` | a control's text (a window's title) |
| `append(textbox, text)` | add to the end of a text box and scroll there (a log) |
| `set_title(window, title)` | the title bar |
| `clicked(handle) -> bool` | was it clicked since you last asked? (a list: was the selection changed?) |
| `checked(checkbox) -> bool`, `set_checked(checkbox, on=True)` | a check box |
| `list_add(listbox, text)`, `list_clear(listbox)`, `selected(listbox) -> int`, `select(listbox, index)` | a list (`selected` is -1 when nothing is) |
| `set_progress(progress, percent)` | 0 to 100 |
| `enable(handle, on=True)`, `show(handle, on=True)` | grey it out / hide it |
| `press(button)` | click it from code |
| `timer(window, ms)`, `timer_fired(window) -> bool` | a tick every `ms` (0 stops it) |
| `wait(window)`, `poll(window)` | handle what happened: `wait` sleeps until something does, `poll` never blocks |
| `running(window) -> bool`, `close(window)` | is it still open? / close it |

After a window closes, its handles stay "dead": `clicked` says False, `get_text` says "", nothing raises.

## Testing a GUI program

Headless, with the `horseman` library (Headless Horseman): it starts the program on a hidden desktop, clicks its
buttons and reads its labels from code, and nothing ever shows on the screen. See `../horseman/README.md`. Or call
`horseman.headless()` at the top of the program itself and drive it with `gui.press`.

## Examples

`examples/counter.bsh`. Bigger: `share/tests/hi_gui.bsh` (live counter, clock, progress bar, a log in a text box).
