build_tools.tui_common.controls.browsers ======================================== .. py:module:: build_tools.tui_common.controls.browsers .. autoapi-nested-parse:: Directory browser modal widget. This module provides the DirectoryBrowserScreen modal for selecting directories with customizable validation. **Features:** - Textual DirectoryTree widget for file system navigation - Vim-style keybindings (h/j/k/l) for navigation - Customizable validation callback - Visual feedback for valid/invalid selections - Select/Cancel buttons with state management **Example Usage:** .. code-block:: python from pathlib import Path from build_tools.tui_common.controls import DirectoryBrowserScreen # Custom validator for source directories def validate_source_dir(path: Path) -> tuple[bool, str, str]: txt_files = list(path.glob("*.txt")) if txt_files: return (True, "source", f"Found {len(txt_files)} text files") return (False, "", "No .txt files found") # In your App async def select_directory(self) -> None: result = await self.push_screen_wait( DirectoryBrowserScreen( title="Select Source Directory", validator=validate_source_dir, initial_dir=Path.cwd(), ) ) if result: print(f"Selected: {result}") Attributes ---------- .. autoapisummary:: build_tools.tui_common.controls.browsers.DirectoryValidator Classes ------- .. autoapisummary:: build_tools.tui_common.controls.browsers.FilterableDirectoryTree build_tools.tui_common.controls.browsers.DirectoryBrowserScreen Functions --------- .. autoapisummary:: build_tools.tui_common.controls.browsers.default_validator Module Contents --------------- .. py:data:: DirectoryValidator .. py:class:: FilterableDirectoryTree(path, show_hidden = False, **kwargs) Bases: :py:obj:`textual.widgets.DirectoryTree` DirectoryTree subclass that supports filtering hidden files. By default, hidden files (those starting with '.') are not shown. Toggle visibility with the ``show_hidden`` attribute. Initialize filterable directory tree. :param path: Root path for the tree :param show_hidden: If True, show hidden files/directories :param \*\*kwargs: Additional arguments passed to DirectoryTree .. py:property:: show_hidden :type: bool Whether hidden files are shown. .. py:method:: filter_paths(paths) Filter paths to optionally exclude hidden files. :param paths: Iterable of paths to filter :returns: Filtered iterable of paths .. py:function:: default_validator(path) Default validator that accepts any directory. :param path: Directory path to validate :returns: Tuple of (True, "directory", path name) for any directory .. py:class:: DirectoryBrowserScreen(title = 'Select Directory', validator = None, initial_dir = None, help_text = None, root_dir = None) Bases: :py:obj:`textual.screen.ModalScreen`\ [\ :py:obj:`pathlib.Path | None`\ ] Modal screen for browsing and selecting a directory. A modal dialog displaying a directory tree for navigation with customizable validation. Returns the selected directory path when user confirms, or None if cancelled. **Validation:** Provide a ``validator`` callable that takes a Path and returns a tuple of ``(is_valid, type_label, message)``: - ``is_valid``: True if the directory can be selected - ``type_label``: Short label for valid directories (e.g., "corpus") - ``message``: Error message if invalid, or description if valid **Navigation:** - ``j`` / ``down``: Move cursor down - ``k`` / ``up``: Move cursor up - ``h`` / ``left``: Collapse directory - ``l`` / ``right``: Expand directory - ``>`` (Shift+.): Toggle hidden files .. attribute:: browser_title Header text displayed at top of modal .. attribute:: validator Callback function to validate selected directories .. attribute:: initial_dir Starting directory for the browser .. attribute:: selected_path Currently selected path (or None) :returns: Selected Path when "Select" is pressed, or None if cancelled .. admonition:: Example .. code-block:: python result = await self.app.push_screen_wait( DirectoryBrowserScreen( title="Select Corpus Directory", validator=validate_corpus_directory, initial_dir=Path.home() / "corpora", ) ) if result: self.load_corpus(result) Initialize directory browser. :param title: Header text displayed at top of modal :param validator: Callback function to validate directories. Signature: ``(Path) -> (is_valid, type_label, message)`` If None, uses default_validator which accepts any directory. :param initial_dir: Starting directory for browser (defaults to home directory) :param help_text: Custom help text to display. If None, uses default help text. :param root_dir: Root directory for the tree. If None, uses home directory. Set this higher than initial_dir to allow navigating up. .. py:attribute:: BINDINGS :value: [('j', 'cursor_down', 'Down'), ('k', 'cursor_up', 'Up'), ('h', 'cursor_left', 'Collapse'), ('l',... A list of key bindings. .. py:attribute:: CSS :value: Multiline-String .. raw:: html
Show Value .. code-block:: python """ DirectoryBrowserScreen { align: center middle; } #browser-container { width: 80; height: 30; background: $panel; border: thick $primary; padding: 1; } #browser-header { text-align: center; text-style: bold; color: $accent; margin-bottom: 1; } #directory-tree { width: 100%; height: 1fr; border: solid $primary; margin-bottom: 1; } #help-text { height: 2; width: 100%; color: $text-muted; text-align: center; margin-bottom: 1; } #validation-status { height: 3; width: 100%; border: solid $primary; padding: 0 1; margin-bottom: 1; } .status-valid { color: $success; } .status-invalid { color: $error; } .status-none { color: $text-muted; } #button-bar { width: 100%; height: auto; align: center middle; } #button-bar Button { margin: 0 1; } """ .. raw:: html
Inline CSS, useful for quick scripts. Rules here take priority over CSS_PATH. .. note:: This CSS applies to the whole app. .. py:attribute:: browser_title :value: 'Select Directory' .. py:attribute:: validator .. py:attribute:: initial_dir .. py:attribute:: root_dir .. py:attribute:: help_text :value: 'Expand a directory to validate it. Click Select when valid.' .. py:attribute:: selected_path :type: pathlib.Path | None :value: None .. py:attribute:: show_hidden :value: False .. py:method:: compose() Create browser UI layout. Layout: - Header with title - Help text - Directory tree (expandable/collapsible) - Validation status display - Select/Cancel buttons :Yields: Composed widget tree for the modal .. py:method:: on_mount() :async: Handle screen mount event. If initial_dir differs from root_dir, attempt to expand the tree to show and select initial_dir after a brief delay to let the tree load its initial content. .. py:method:: directory_selected(event) Handle directory selection in tree. Validates the selected directory using the configured validator and updates the UI accordingly. Triggered when user clicks on a directory NAME (not the expand arrow). :param event: Directory selection event from DirectoryTree .. py:method:: node_expanded(event) Handle directory expansion in tree. When a user expands a directory (via arrow click or 'l' key), validate it and allow selection if valid. This improves UX by letting users select a directory after navigating into it, without needing to click on the directory name again. Note: We use Tree.NodeExpanded because DirectoryTree inherits from Tree and doesn't define its own NodeExpanded message class. :param event: Node expanded event from Tree (parent class of DirectoryTree) .. py:method:: file_selected(event) Handle file selection in tree. When a file is clicked, we don't clear the current selection since the user may have already validated the parent directory by expanding into it. Instead, we provide a gentle reminder that the parent directory is what will be selected. :param event: File selection event from DirectoryTree .. py:method:: select_pressed() Handle Select button press. Dismisses the modal with the selected path. .. py:method:: cancel_pressed() Handle Cancel button press. Dismisses the modal with None. .. py:method:: action_cursor_down() Move cursor down in directory tree (j key). .. py:method:: action_cursor_up() Move cursor up in directory tree (k key). .. py:method:: action_cursor_left() Collapse directory in tree (h key). Collapses the current directory node if expanded. .. py:method:: action_cursor_right() Expand directory in tree (l key). Expands the current directory node if collapsed. .. py:method:: action_toggle_node() Toggle expand/collapse of current node (space key). Expands collapsed nodes, collapses expanded nodes. .. py:method:: action_select_node() Select the current node (enter key). If the current node is a directory, validates it. If already valid, confirms the selection. .. py:method:: action_cancel() Cancel and close the dialog (escape key). .. py:method:: action_toggle_hidden() Toggle visibility of hidden files (> key).