Skip to content

xnano.components.button

xnano.components.button

xnano.components.button


Display a focusable button and handle activation with normal keyboard or click hooks.

Classes:

  • Button

    Focusable styled text button.

Button dataclass

Button(
    label: str | Any = "",
    disabled: bool = False,
    focusable: bool = True,
    foreground: ColorLike | None = None,
    background: ColorLike | None = None,
    focused_color: ColorLike | None = "black",
    focused_background: ColorLike | None = "white",
    disabled_color: ColorLike | None = "gray",
    disabled_background: ColorLike | None = None,
    left: str = "[ ",
    right: str = " ]",
    activation_keys: Sequence[str] = ("enter", "space"),
    *,
    visible: bool = True,
    z: int = 0,
    fit_content: bool = True
)

Bases: Component

Focusable styled text button.

Buttons use the same hooks as every other field. Assign a group and handle clicks or activation keys on the grid:

class Form(BaseGrid):
    submit: Button = Field(
        default=Button("Submit"),
        group="submit",
    )

    @on_click(group="submit")
@on_keyboard("enter", group="submit")
def submit_form(self, ctx: Context) -> None: ...
Example

Button(label="Save", focused_background="blue")

Attributes:

Methods:

  • get_label_text

    Return the plain-text form of label.

  • handle_keyboard

    Leave keyboard activation to the grid's event hooks.

  • compose

    Compose a styled label, wrapped in a panel when focused.

  • component_post_init

    Initialize subclass state after dataclass fields are assigned.

  • get_frame

    Optional frame/panel chrome around composed content.

  • get_size

    Return the preferred cell size of this component.

  • before_render

    Called before rendering; returns the effective render area.

  • after_render

    Called after rendering for optional post-paint work.

  • compose_extra_small

    Compose content when the viewport is extra small (< 40 cols).

  • compose_small

    Compose content when the viewport is small (40–79 cols).

  • compose_medium

    Compose content when the viewport is medium (80–119 cols).

  • compose_large

    Compose content when the viewport is large (120–159 cols).

  • compose_extra_large

    Compose content when the viewport is extra large (>= 160 cols).

  • handle_paste

    Optional paste handler while focused.

label class-attribute instance-attribute

label: str | Any = ''

Button caption (plain string or styled Text).

disabled class-attribute instance-attribute

disabled: bool = False

When True, paints disabled colors and ignores activation.

focusable class-attribute instance-attribute

focusable: bool = True

Whether this button participates in field focus (tab order).

foreground class-attribute instance-attribute

foreground: ColorLike | None = None

Idle foreground color (deprecated alias: color).

background class-attribute instance-attribute

background: ColorLike | None = None

Idle background color.

focused_color class-attribute instance-attribute

focused_color: ColorLike | None = 'black'

Foreground while focused.

focused_background class-attribute instance-attribute

focused_background: ColorLike | None = 'white'

Background while focused.

disabled_color class-attribute instance-attribute

disabled_color: ColorLike | None = 'gray'

Foreground while disabled.

disabled_background class-attribute instance-attribute

disabled_background: ColorLike | None = None

Background while disabled.

left class-attribute instance-attribute

left: str = '[ '

Prefix chrome around the label.

right class-attribute instance-attribute

right: str = ' ]'

Suffix chrome around the label.

activation_keys class-attribute instance-attribute

activation_keys: Sequence[str] = ('enter', 'space')

Bindings that bubble to hooks (never consumed here).

visible class-attribute instance-attribute

visible: bool = dataclasses.field(
    default=True, kw_only=True
)

Whether this component paints at all.

z class-attribute instance-attribute

z: int = dataclasses.field(default=0, kw_only=True)

Stacking order among sibling content.

fit_content class-attribute instance-attribute

fit_content: bool = dataclasses.field(
    default=True, kw_only=True
)

When True, paint at natural size inside a larger slot.

focused property

focused: bool

Whether this component currently holds field focus.

get_label_text

get_label_text() -> str

Return the plain-text form of label.

Source code in xnano/components/button.py
def get_label_text(self) -> str:
    """Return the plain-text form of ``label``."""
    if isinstance(self.label, str):
        return self.label
    value = getattr(self.label, "value", None)
    if isinstance(value, str):
        return value
    content = getattr(self.label, "content", None)
    if isinstance(content, str):
        return content
    return str(self.label)

handle_keyboard

handle_keyboard(keyboard: 'KeyboardEventData') -> bool

Leave keyboard activation to the grid's event hooks.

Parameters:

  • keyboard ('KeyboardEventData') –

    The keyboard event payload.

Returns:

  • bool

    False so the event continues to hooks.

Source code in xnano/components/button.py
def handle_keyboard(self, keyboard: "KeyboardEventData") -> bool:
    """Leave keyboard activation to the grid's event hooks.

    Args:
        keyboard: The keyboard event payload.

    Returns:
        ``False`` so the event continues to hooks.
    """
    del keyboard
    return False

compose

compose(ctx: 'ComponentRenderContext') -> Any

Compose a styled label, wrapped in a panel when focused.

Parameters:

  • ctx ('ComponentRenderContext') –

    Render-time scope for this paint.

Returns:

  • Any

    A TextBlock, or a Panel around it while focused and

  • Any

    not disabled.

Source code in xnano/components/button.py
def compose(self, ctx: "ComponentRenderContext") -> Any:
    """Compose a styled label, wrapped in a panel when focused.

    Args:
        ctx: Render-time scope for this paint.

    Returns:
        A ``TextBlock``, or a ``Panel`` around it while focused and
        not disabled.
    """
    from xnano.core.content import Panel, Run, TextBlock

    del ctx
    foreground, background = self._resolved_colors()
    caption = f"{self.left}{self.get_label_text()}{self.right}"
    block = TextBlock(
        lines=(
            (
                Run(
                    text=caption,
                    foreground=foreground,
                    background=background,
                ),
            ),
        ),
        foreground=foreground,
        background=background,
        z=self.z,
        visible=self.visible,
    )
    if self.focused and not self.disabled:
        return Panel(
            child=block,
            background=background,
            border_color=foreground,
            z=self.z,
            visible=self.visible,
        )
    return block

component_post_init

component_post_init() -> None

Initialize subclass state after dataclass fields are assigned.

Override this method instead of __post_init__.

Source code in xnano/components/component.py
def component_post_init(self) -> None:
    """Initialize subclass state after dataclass fields are assigned.

    Override this method instead of ``__post_init__``.
    """
    return None

get_frame

get_frame() -> Any | None

Optional frame/panel chrome around composed content.

Source code in xnano/components/component.py
def get_frame(self) -> Any | None:
    """Optional frame/panel chrome around composed content."""
    return None

get_size

get_size(ctx: ComponentRenderContext[StateT]) -> Size

Return the preferred cell size of this component.

Source code in xnano/components/component.py
def get_size(self, ctx: ComponentRenderContext[StateT]) -> Size:
    """Return the preferred cell size of this component."""
    return Size(width=0, height=0)

before_render

before_render(
    ctx: ComponentRenderContext[StateT], area: "Area"
) -> "Area"

Called before rendering; returns the effective render area.

Source code in xnano/components/component.py
def before_render(
    self,
    ctx: ComponentRenderContext[StateT],
    area: "Area",
) -> "Area":
    """Called before rendering; returns the effective render area."""
    return area

after_render

after_render(
    ctx: ComponentRenderContext[StateT], area: "Area"
) -> None

Called after rendering for optional post-paint work.

Source code in xnano/components/component.py
def after_render(
    self,
    ctx: ComponentRenderContext[StateT],
    area: "Area",
) -> None:
    """Called after rendering for optional post-paint work."""
    return None

compose_extra_small

compose_extra_small(
    ctx: ComponentRenderContext[StateT],
) -> Content | None

Compose content when the viewport is extra small (< 40 cols).

Optional responsive counterpart to :meth:compose. When overridden, it is used instead of compose while the window is in this size tier. Overriding any compose_* variant opts the component into breakpoint dispatch; a component that overrides none pays no per-frame cost.

Source code in xnano/components/component.py
@responsive_noop
def compose_extra_small(
    self, ctx: ComponentRenderContext[StateT]
) -> Content | None:
    """Compose content when the viewport is extra small (< 40 cols).

    Optional responsive counterpart to :meth:`compose`. When
    overridden, it is used *instead of* ``compose`` while the window
    is in this size tier. Overriding any ``compose_*`` variant opts the
    component into breakpoint dispatch; a component that overrides none
    pays no per-frame cost.
    """
    return None

compose_small

compose_small(
    ctx: ComponentRenderContext[StateT],
) -> Content | None

Compose content when the viewport is small (40–79 cols).

See :meth:compose_extra_small.

Source code in xnano/components/component.py
@responsive_noop
def compose_small(
    self, ctx: ComponentRenderContext[StateT]
) -> Content | None:
    """Compose content when the viewport is small (40–79 cols).

    See :meth:`compose_extra_small`.
    """
    return None

compose_medium

compose_medium(
    ctx: ComponentRenderContext[StateT],
) -> Content | None

Compose content when the viewport is medium (80–119 cols).

See :meth:compose_extra_small.

Source code in xnano/components/component.py
@responsive_noop
def compose_medium(
    self, ctx: ComponentRenderContext[StateT]
) -> Content | None:
    """Compose content when the viewport is medium (80–119 cols).

    See :meth:`compose_extra_small`.
    """
    return None

compose_large

compose_large(
    ctx: ComponentRenderContext[StateT],
) -> Content | None

Compose content when the viewport is large (120–159 cols).

See :meth:compose_extra_small.

Source code in xnano/components/component.py
@responsive_noop
def compose_large(
    self, ctx: ComponentRenderContext[StateT]
) -> Content | None:
    """Compose content when the viewport is large (120–159 cols).

    See :meth:`compose_extra_small`.
    """
    return None

compose_extra_large

compose_extra_large(
    ctx: ComponentRenderContext[StateT],
) -> Content | None

Compose content when the viewport is extra large (>= 160 cols).

See :meth:compose_extra_small.

Source code in xnano/components/component.py
@responsive_noop
def compose_extra_large(
    self, ctx: ComponentRenderContext[StateT]
) -> Content | None:
    """Compose content when the viewport is extra large (>= 160 cols).

    See :meth:`compose_extra_small`.
    """
    return None

handle_paste

handle_paste(text: str) -> bool

Optional paste handler while focused.

Returns:

  • bool

    True when the paste was consumed.

Source code in xnano/components/component.py
def handle_paste(self, text: str) -> bool:
    """Optional paste handler while focused.

    Returns:
        ``True`` when the paste was consumed.
    """
    return False