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())
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())
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.
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())
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())
Separator¶
Separator() draws a horizontal line. Use Separator(vertical=True) for a vertical line in an HBox.