build_tools.tui_common.controls.spinners

Integer spinner control widget.

This module provides the IntSpinner widget for integer parameter control with keyboard support and optional dynamic suffix.

Features:

  • Keyboard navigation: +/- or j/k or arrow keys to adjust

  • Configurable step size and min/max range

  • Optional suffix function for dynamic labels (e.g., “5 items”, “10 chars”)

  • Posts Changed message when value updates

  • Focusable with visual feedback

Example Usage:

from build_tools.tui_common.controls import IntSpinner
from textual import on

class MyApp(App):
    def compose(self) -> ComposeResult:
        # Simple spinner with static label
        yield IntSpinner(
            label="Count",
            value=5,
            min_val=1,
            max_val=100,
            id="count-spinner",
        )

        # Spinner with dynamic suffix based on value
        yield IntSpinner(
            label="Walk Steps",
            value=5,
            min_val=0,
            max_val=20,
            suffix_fn=lambda v: f"-> {v + 1} syllables",
            id="steps-spinner",
        )

    @on(IntSpinner.Changed)
    def on_spinner_changed(self, event: IntSpinner.Changed) -> None:
        print(f"Spinner {event.widget_id} = {event.value}")

Classes

IntSpinner

Integer spinner widget with increment/decrement support.

Module Contents

class build_tools.tui_common.controls.spinners.IntSpinner(label, value, min_val, max_val, step=1, suffix_fn=None, *args, **kwargs)[source]

Bases: textual.widgets.Static

Integer spinner widget with increment/decrement support.

A horizontal widget displaying a label, current value in brackets, and optional dynamic suffix. Users can adjust the value using keyboard shortcuts while the widget has focus.

label_text

Display label shown before the value

value

Current integer value (clamped to min/max range)

min_val

Minimum allowed value

max_val

Maximum allowed value

step

Amount to increment/decrement per keypress

suffix_fn

Optional callable that generates suffix text from current value

Keybindings:
  • + or = or j or down: Increment value by step

  • - or _ or k or up: Decrement value by step

Messages:
  • Changed: Posted when value changes, includes new value and widget ID

CSS Classes:
  • .spinner-label: Label element (width: 15, right-aligned)

  • .spinner-value: Value display (width: 6, centered, highlighted on focus)

  • .spinner-suffix: Suffix text (auto width, muted color)

Initialize integer spinner.

Parameters:
  • label (str) – Display label shown before the value

  • value (int) – Initial value (will be clamped to min/max range)

  • min_val (int) – Minimum allowed value

  • max_val (int) – Maximum allowed value

  • step (int) – Increment/decrement step size (default: 1)

  • suffix_fn (collections.abc.Callable[[int], str] | None) – Optional callback to generate suffix text from current value. Called with current value, returns string to display. Example: lambda v: f"-> {v + 1} items"

  • *args – Additional positional arguments passed to Static

  • **kwargs – Additional keyword arguments passed to Static

BINDINGS = [('+', 'increment', 'Increment'), ('=', 'increment', 'Increment'), ('j', 'increment',...

A list of key bindings.

class Changed(value, widget_id)[source]

Bases: textual.message.Message

Message posted when the spinner value changes.

value

The new integer value after the change

widget_id

The ID of the widget that posted this message, or None

Initialize the Changed message.

Parameters:
  • value (int) – The new spinner value

  • widget_id (str | None) – ID of the spinner widget that changed

value
widget_id
DEFAULT_CSS = Multiline-String
Show Value
"""
    IntSpinner {
        layout: horizontal;
        height: 1;
        width: 100%;
    }

    IntSpinner .spinner-label {
        width: 15;
        text-align: right;
        padding-right: 1;
    }

    IntSpinner .spinner-value {
        width: 6;
        text-align: center;
        background: $boost;
    }

    IntSpinner:focus .spinner-value {
        background: $accent;
        text-style: bold;
    }

    IntSpinner .spinner-suffix {
        width: auto;
        padding-left: 1;
        color: $text-muted;
    }
    """

Default TCSS.

label_text
value
min_val
max_val
step = 1
suffix_fn = None
compose()[source]

Create the spinner layout.

Layout: [Label:] [value] [suffix]

Yields:

Label widgets for label, value display, and optional suffix

on_mount()[source]

Configure widget after mounting.

Makes the widget focusable for keyboard navigation. Focus state is used to highlight the value display.

action_increment()[source]

Increment value by step, clamped to max.

Only posts Changed message if value actually changed. Updates both value display and suffix if present.

action_decrement()[source]

Decrement value by step, clamped to min.

Only posts Changed message if value actually changed. Updates both value display and suffix if present.

set_value(value)[source]

Set value programmatically, clamped to range.

Use this method to update the spinner value from code. Posts Changed message if value actually changed.

Parameters:

value (int) – New value (will be clamped to min/max range)