Source code for genro_textual.textual_builder

# Copyright 2025 Softwell S.r.l. - SPDX-License-Identifier: Apache-2.0
"""TextualBuilder - Builder for Textual TUI widgets.

Elements and components are defined in mixins so that subclasses of
TextualBuilder inherit the full schema. This follows the genro-builders
mixin pattern: _pop_decorated_methods collects decorators from mixin
bases (non-BagBuilderBase) in the MRO.

Component mixins live in genro_textual.components — each file is
a mixin that can be included or excluded when composing a builder.

No rendering logic here — that belongs in TextualCompiler.
"""

from __future__ import annotations

from genro_builders.builder import BagBuilderBase, component, element

from genro_textual.components.foundation import FoundationMixin


[docs] class TextualWidgetsMixin: """All Textual widget @element and @component definitions. Defined as a mixin so that subclasses of TextualBuilder automatically inherit the full schema via MRO. """ # ------------------------------------------------------------------------- # Container elements (from textual.containers) # -------------------------------------------------------------------------
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "Container"} ) def container(self): """A generic container widget.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "Vertical"} ) def vertical(self): """A container that arranges children vertically.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "Horizontal"} ) def horizontal(self): """A container that arranges children horizontally.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "Center"} ) def center(self): """A container that centers its children horizontally.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "Middle"} ) def middle(self): """A container that centers its children vertically.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "CenterMiddle"}, ) def centermiddle(self): """A container that centers its children both horizontally and vertically.""" ...
[docs] @element(sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "Right"}) def right(self): """A container that aligns its children to the right.""" ...
[docs] @element(sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "Grid"}) def grid(self): """A container with grid layout.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "VerticalScroll"}, ) def verticalscroll(self): """A scrollable vertical container.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "HorizontalScroll"}, ) def horizontalscroll(self): """A scrollable horizontal container.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "ScrollableContainer"}, ) def scrollablecontainer(self): """A scrollable container.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "VerticalGroup"}, ) def verticalgroup(self): """A vertical group of widgets.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "HorizontalGroup"}, ) def horizontalgroup(self): """A horizontal group of widgets.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.containers", "compile_class": "ItemGrid"} ) def itemgrid(self, min_column_width: int = 20): """A grid container that arranges items in columns.""" ...
# ------------------------------------------------------------------------- # Widget elements (from textual.widgets) # -------------------------------------------------------------------------
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Button"}) def button( self, content: str = "", label: str | None = None, variant: str = "default", tooltip: str | None = None, action: str | None = None, ): """A simple clickable button.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Checkbox"}) def checkbox( self, content: str = "", label: str = "", value: bool = False, button_first: bool = True, tooltip: str | None = None, compact: bool = False, ): """A check box widget that represents a boolean value.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.widgets", "compile_class": "Collapsible"} ) def collapsible( self, title: str = "Toggle", collapsed: bool = True, collapsed_symbol: str = "▶", expanded_symbol: str = "▼", ): """A collapsible container.""" ...
[docs] @element( sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "CollapsibleTitle"}, ) def collapsibletitle( self, content: str = "", label: str | None = None, collapsed_symbol: str | None = None, expanded_symbol: str | None = None, collapsed: str | None = None, ): """Title and symbol for the Collapsible.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.widgets", "compile_class": "ContentSwitcher"}, ) def contentswitcher(self, initial: str | None = None): """A widget for switching between different children.""" ...
[docs] @element( sub_tags="column,row", _meta={"compile_module": "textual.widgets", "compile_class": "DataTable"}, ) def datatable( self, show_header: bool = True, show_row_labels: bool = True, fixed_rows: int = 0, fixed_columns: int = 0, zebra_stripes: bool = False, header_height: int = 1, show_cursor: bool = True, cursor_foreground_priority: str = "css", cursor_background_priority: str = "renderable", cursor_type: str = "cell", cell_padding: int = 1, ): """A tabular widget that contains data.""" ...
[docs] @element(sub_tags="", parent_tags="datatable") def column(self, label: str = "", key: str | None = None, width: int | None = None): """A column definition for DataTable.""" ...
[docs] @element(sub_tags="", parent_tags="datatable") def row(self, key: str | None = None, label: str | None = None, height: int = 1): """A row for DataTable. Value can be a list of cell values.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Digits"}) def digits(self, content: str = "", value: str = ""): """A widget to display numerical values using a 3x3 grid of unicode characters.""" ...
[docs] @element( sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "DirectoryTree"} ) def directorytree(self, content: str = "", path: str | None = None): """A Tree widget that presents files and directories.""" ...
[docs] @element(sub_tags="*", _meta={"compile_module": "textual.widgets", "compile_class": "Footer"}) def footer(self, show_command_palette: bool = True, compact: bool = False): """Textual Footer widget.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Header"}) def header( self, content: str = "", show_clock: bool = False, icon: str | None = None, time_format: str | None = None, ): """A header widget with icon and clock.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.widgets", "compile_class": "HelpPanel"} ) def helppanel(self, markup: bool = True): """Textual HelpPanel widget.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Input"}) def input( self, content: str = "", value: str | None = None, placeholder: str = "", password: bool = False, restrict: str | None = None, type: str = "text", max_length: int = 0, valid_empty: bool = False, select_on_focus: bool = True, tooltip: str | None = None, compact: bool = False, ): """A text input widget.""" ...
[docs] @element(sub_tags="*", _meta={"compile_module": "textual.widgets", "compile_class": "KeyPanel"}) def keypanel( self, can_focus: str | None = None, can_focus_children: str | None = None, can_maximize: str | None = None, ): """Textual KeyPanel widget.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Label"}) def label( self, content: str = "", variant: str | None = None, expand: bool = False, shrink: bool = False, markup: bool = True, ): """A simple label widget for displaying text-oriented renderables.""" ...
[docs] @element(sub_tags="*", _meta={"compile_module": "textual.widgets", "compile_class": "ListItem"}) def listitem(self, markup: bool = True): """A widget that is an item within a `ListView`.""" ...
[docs] @element( sub_tags="listitem", _meta={"compile_module": "textual.widgets", "compile_class": "ListView"}, ) def listview(self, initial_index: int = 0): """A vertical list view widget.""" ...
[docs] @element( sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "LoadingIndicator"}, ) def loadingindicator(self, content: str = ""): """Display an animated loading indicator.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Log"}) def log( self, content: str = "", highlight: bool = False, max_lines: str | None = None, auto_scroll: bool = True, ): """A widget to log text.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Markdown"}) def markdown( self, content: str = "", markdown: str | None = None, parser_factory: str | None = None, open_links: bool = True, ): """Textual Markdown widget.""" ...
[docs] @element( sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "MarkdownViewer"} ) def markdownviewer( self, content: str = "", markdown: str | None = None, show_table_of_contents: bool = True, parser_factory: str | None = None, open_links: bool = True, ): """A Markdown viewer widget.""" ...
[docs] @element( sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "MaskedInput"} ) def maskedinput( self, content: str = "", template: str | None = None, value: str | None = None, placeholder: str = "", valid_empty: bool = False, select_on_focus: bool = True, tooltip: str | None = None, compact: bool = False, ): """A masked text input widget.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.widgets", "compile_class": "OptionList"} ) def optionlist(self, markup: bool = True, compact: bool = False): """A navigable list of options.""" ...
[docs] @element( sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Placeholder"} ) def placeholder(self, content: str = "", label: str | None = None, variant: str = "default"): """A simple placeholder widget to use before you build your custom widgets.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Pretty"}) def pretty(self, content: str = "", object: str | None = None): """A pretty-printing widget.""" ...
[docs] @element( sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "ProgressBar"} ) def progressbar( self, content: str = "", total: float | int | None = None, show_bar: bool = True, show_percentage: bool = True, show_eta: bool = True, ): """A progress bar widget.""" ...
[docs] @element( sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "RadioButton"} ) def radiobutton( self, content: str = "", label: str = "", value: bool = False, button_first: bool = True, tooltip: str | None = None, compact: bool = False, ): """A radio button widget that represents a boolean value.""" ...
[docs] @element( sub_tags="radiobutton", _meta={"compile_module": "textual.widgets", "compile_class": "RadioSet"}, ) def radioset(self, tooltip: str | None = None, compact: bool = False): """Widget for grouping a collection of radio buttons into a set.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "RichLog"}) def richlog( self, content: str = "", max_lines: str | None = None, min_width: int = 78, wrap: bool = False, highlight: bool = False, markup: bool = False, auto_scroll: bool = True, ): """A widget for logging Rich renderables and text.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Rule"}) def rule(self, content: str = "", orientation: str = "horizontal", line_style: str = "solid"): """A rule widget to separate content, similar to a `<hr>` HTML tag.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Select"}) def select( self, content: str = "", options: str | None = None, prompt: str = "Select", allow_blank: bool = True, value: str = None, type_to_search: bool = True, tooltip: str | None = None, compact: bool = False, ): """Widget to select from a list of possible options.""" ...
[docs] @element( sub_tags="*", _meta={"compile_module": "textual.widgets", "compile_class": "SelectionList"} ) def selectionlist(self, compact: bool = False): """A vertical selection list that allows making multiple selections.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Sparkline"}) def sparkline( self, content: str = "", data: str | None = None, min_color: str | None = None, max_color: str | None = None, summary_function: str | None = None, ): """A sparkline widget to display numerical data.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Static"}) def static( self, content: str = "", expand: bool = False, shrink: bool = False, markup: bool = True ): """A widget to display simple static content.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Switch"}) def switch( self, content: str = "", value: bool = False, animate: bool = True, tooltip: str | None = None, ): """A switch widget that represents a boolean value.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Tab"}) def tab(self, content: str = "", label: str | None = None): """A Widget to manage a single tab within a Tabs widget.""" ...
[docs] @element( sub_tags="*", parent_tags="tabbedcontent", _meta={"compile_module": "textual.widgets", "compile_class": "TabPane"}, ) def tabpane(self, title: str | None = None): """A container for switchable content, with additional title.""" ...
[docs] @element( sub_tags="tabpane", _meta={"compile_module": "textual.widgets", "compile_class": "TabbedContent"}, ) def tabbedcontent(self, initial: str = ""): """A container with associated tabs to toggle content visibility.""" ...
[docs] @element(sub_tags="tab", _meta={"compile_module": "textual.widgets", "compile_class": "Tabs"}) def tabs(self, active: str | None = None): """A row of tabs.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "TextArea"}) def textarea( self, content: str = "", text: str = "", language: str | None = None, theme: str = "css", soft_wrap: bool = True, tab_behavior: str = "focus", read_only: bool = False, show_cursor: bool = True, show_line_numbers: bool = False, line_number_start: int = 1, max_checkpoints: int = 50, tooltip: str | None = None, compact: bool = False, highlight_cursor_line: bool = True, placeholder: str = "", ): """Textual TextArea widget.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Tooltip"}) def tooltip( self, content: str = "", expand: bool = False, shrink: bool = False, markup: bool = True ): """Textual Tooltip widget.""" ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Tree"}) def tree( self, content: str = "", label: str | None = None, data: str | None = None, store: object | None = None, ): """A widget for displaying and navigating data in a tree. Args: store: A Bag object. The tree is populated recursively from its structure. """ ...
[docs] @element(sub_tags="", _meta={"compile_module": "textual.widgets", "compile_class": "Welcome"}) def welcome( self, content: str = "", expand: bool = False, shrink: bool = False, markup: bool = True ): """A Textual welcome widget.""" ...
# ------------------------------------------------------------------------- # App configuration elements (not rendered as widgets) # -------------------------------------------------------------------------
[docs] @element(sub_tags="") def css(self, content: str = ""): """CSS stylesheet applied to the live app.""" ...
[docs] @element(sub_tags="") def binding(self, key: str = "", action: str = "", description: str = ""): """Key binding: maps a key press to an action method.""" ...
# ------------------------------------------------------------------------- # Components (composite elements expanded at compile time) # -------------------------------------------------------------------------
[docs] @component(sub_tags="") def fieldset(self, comp, title="", **kwargs): """A group of input fields with a title. Closed: returns parent for chaining.""" if title: comp.static(title)
[docs] @component(sub_tags="*") def form(self, comp, title="", **kwargs): """A form container. Open: returns internal bag for adding fields.""" if title: comp.static(title)
[docs] class TextualBuilder(FoundationMixin, TextualWidgetsMixin, BagBuilderBase): """Builder for Textual TUI elements. Includes FoundationMixin (app_shell) and TextualWidgetsMixin (all widgets). Subclass TextualBuilder freely — mixin schemas are inherited via MRO. To add custom components, define them in a mixin:: from genro_builders.builder import component class MyMixin: @component(sub_tags="") def login_form(self, comp, **kwargs): comp.input(placeholder="Username") comp.button("Login") class MyBuilder(MyMixin, TextualBuilder): pass To exclude FoundationMixin, compose your own builder:: class MinimalBuilder(TextualWidgetsMixin, BagBuilderBase): pass """