Controls

This chapter covers all input widgets available in the declarative API.

Text entry

Entry supports three types: "normal", "password", and "search":

"""Entry types — normal, password, and search entries."""

import libui
from libui.declarative import (
    App,
    Window,
    VBox,
    Form,
    Group,
    Label,
    Entry,
    MultilineEntry,
    State,
    stretchy,
)


async def main():
    app = App()
    status = State("Type in any field.")

    app.build(
        window=Window(
            "Entry Types",
            500,
            400,
            child=VBox(
                Label(text=status),
                Group(
                    "Single-line Entries",
                    Form(
                        (
                            "Normal:",
                            Entry(
                                on_changed=lambda t: status.set(f"Normal: {t}"),
                            ),
                        ),
                        (
                            "Password:",
                            Entry(
                                type="password",
                                on_changed=lambda t: status.set(
                                    f"Password length: {len(t)}"
                                ),
                            ),
                        ),
                        (
                            "Search:",
                            Entry(
                                type="search",
                                on_changed=lambda t: status.set(f"Search: {t}"),
                            ),
                        ),
                        padded=True,
                    ),
                ),
                Group(
                    "Multi-line Entry",
                    stretchy(
                        MultilineEntry(
                            text="Multi-line text with word wrapping enabled.",
                            wrapping=True,
                            on_changed=lambda t: status.set(
                                f"Lines: {t.count(chr(10)) + 1}"
                            ),
                        ),
                    ),
                ),
            ),
        )
    )

    app.show()
    await app.wait()


libui.run(main())
Entry types

MultilineEntry provides multi-line text editing with optional word wrapping.

Checkbox, Slider, and Spinbox

These controls support two-way binding with State:

"""Checkbox, slider, and spinbox — two-way binding with shared state."""

import libui
from libui.declarative import (
    App,
    Window,
    VBox,
    Form,
    Group,
    Separator,
    Label,
    Checkbox,
    Slider,
    Spinbox,
    ProgressBar,
    State,
)


async def main():
    app = App()
    status = State("Adjust the controls.")
    value = State(50)
    enabled = State(True)

    # Subscribe to print changes
    value.subscribe(lambda: status.set(f"Value: {value.value}"))

    app.build(
        window=Window(
            "Controls",
            450,
            350,
            child=VBox(
                Label(text=status),
                Separator(),
                Group(
                    "Shared Value",
                    Form(
                        ("Slider:", Slider(0, 100, value=value)),
                        ("Spinbox:", Spinbox(0, 100, value=value)),
                        ("Progress:", ProgressBar(value=value)),
                        padded=True,
                    ),
                ),
                Group(
                    "Checkbox",
                    VBox(
                        Checkbox(
                            "Enable feature",
                            checked=enabled,
                            on_toggled=lambda c: status.set(
                                f"Feature {'enabled' if c else 'disabled'}"
                            ),
                        ),
                        Label(
                            text=enabled.map(
                                lambda e: "Status: enabled" if e else "Status: disabled"
                            )
                        ),
                    ),
                ),
            ),
        )
    )

    app.show()
    await app.wait()


libui.run(main())
Checkbox, slider, and spinbox

When multiple widgets bind to the same State, they stay in sync automatically. In this example, the slider and spinbox share a State[int] — dragging the slider updates the spinbox and vice versa.

Combobox and RadioButtons

Combobox is a dropdown selector, RadioButtons shows all options at once. Both use an index-based selected state:

"""Combobox and radio buttons — selection-based controls."""

import libui
from libui.declarative import (
    App,
    Window,
    VBox,
    Form,
    Group,
    Separator,
    Label,
    Combobox,
    EditableCombobox,
    RadioButtons,
    State,
)


async def main():
    app = App()
    status = State("Make a selection.")

    colors = ["Red", "Green", "Blue", "Yellow"]
    quality = ["Low", "Medium", "High", "Ultra"]
    fruits = ["Apple", "Banana", "Cherry", "Date"]

    app.build(
        window=Window(
            "Selection Controls",
            450,
            400,
            child=VBox(
                Label(text=status),
                Separator(),
                Group(
                    "Combobox",
                    Form(
                        (
                            "Color:",
                            Combobox(
                                items=colors,
                                selected=0,
                                on_selected=lambda i: status.set(f"Color: {colors[i]}"),
                            ),
                        ),
                        (
                            "Fruit:",
                            EditableCombobox(
                                items=fruits,
                                on_changed=lambda t: status.set(f"Fruit: {t}"),
                            ),
                        ),
                        padded=True,
                    ),
                ),
                Group(
                    "Radio Buttons",
                    VBox(
                        RadioButtons(
                            items=quality,
                            on_selected=lambda i: status.set(
                                f"Quality: {quality[i]}" if i >= 0 else "Quality: none"
                            ),
                        ),
                    ),
                ),
            ),
        )
    )

    app.show()
    await app.wait()


libui.run(main())
Combobox and radio buttons

EditableCombobox combines a dropdown with a free-text entry. It binds to a State[str] via text instead of selected.

Pickers

Color, font, and date/time pickers provide native OS dialogs:

"""Pickers — color, font, and date/time selection."""

import libui
from libui.declarative import (
    App,
    Window,
    VBox,
    Form,
    Group,
    Label,
    ColorButton,
    FontButton,
    DateTimePicker,
    State,
)


async def main():
    app = App()
    status = State("Pick a value.")

    app.build(
        window=Window(
            "Pickers",
            450,
            350,
            child=VBox(
                Label(text=status),
                Group(
                    "Pickers",
                    Form(
                        (
                            "Color:",
                            ColorButton(
                                on_changed=lambda rgba: status.set(
                                    "Color: R={:.2f} G={:.2f} B={:.2f} A={:.2f}".format(
                                        *rgba
                                    )
                                ),
                            ),
                        ),
                        (
                            "Font:",
                            FontButton(
                                on_changed=lambda f: status.set(
                                    f"Font: {f['family']} {f['size']}pt"
                                ),
                            ),
                        ),
                        (
                            "Date & Time:",
                            DateTimePicker(
                                type="datetime",
                                on_changed=lambda t: status.set(
                                    "DateTime: {0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}".format(
                                        *t[:5]
                                    )
                                ),
                            ),
                        ),
                        ("Date only:", DateTimePicker(type="date")),
                        ("Time only:", DateTimePicker(type="time")),
                        padded=True,
                    ),
                ),
            ),
        )
    )

    app.show()
    await app.wait()


libui.run(main())
Pickers

DateTimePicker supports three types: "datetime", "date", and "time".

ProgressBar

ProgressBar displays a value from 0 to 100. It supports one-way binding only (no user input):

"""ProgressBar — one-way binding to display progress."""

import libui
from libui.declarative import (
    App,
    Window,
    VBox,
    HBox,
    Form,
    Label,
    Button,
    Slider,
    ProgressBar,
    State,
)


async def main():
    app = App()
    progress = State(0)

    app.build(
        window=Window(
            "Progress Bar",
            400,
            200,
            child=VBox(
                Form(
                    ("Adjust:", Slider(0, 100, value=progress)),
                    ("Progress:", ProgressBar(value=progress)),
                    padded=True,
                ),
                Label(text=progress.map(lambda v: f"{v}%")),
                HBox(
                    Button("0%", on_clicked=lambda: progress.set(0)),
                    Button("50%", on_clicked=lambda: progress.set(50)),
                    Button("100%", on_clicked=lambda: progress.set(100)),
                ),
            ),
        )
    )

    app.show()
    await app.wait()


libui.run(main())
Progress bar

Separator

Separator() draws a horizontal line. Use Separator(vertical=True) for a vertical line in an HBox.