Files
2026-01-13 14:06:34 +00:00

133 lines
4.4 KiB
Python

"""
Common dialog screens.
Reusable dialog components for user interactions: confirmation, path input,
and path selection.
"""
from pathlib import Path
from typing import Optional
from textual.app import ComposeResult
from textual.containers import Container, Horizontal
from textual.widgets import Button, Input, Label, Static
from textual.screen import ModalScreen
class ConfirmDialog(ModalScreen):
"""Modal dialog for confirmation"""
CSS_PATH = str(Path(__file__).parent.parent / "styles" / "dialogs.tcss")
def __init__(self, title: str, message: str, confirm_variant: str = "error"):
"""
Args:
title: Dialog title
message: Confirmation message
confirm_variant: Variant for confirm button (default: "error" for destructive actions)
"""
super().__init__()
self.title = title
self.message = message
self.confirm_variant = confirm_variant
def compose(self) -> ComposeResult:
with Container(id="confirm-dialog"):
yield Label(f"[bold]{self.title}[/bold]")
yield Static(self.message, id="confirm-message")
with Horizontal(id="confirm-buttons"):
yield Button("Confirm", id="btn-confirm", variant=self.confirm_variant)
yield Button("Cancel", id="btn-cancel", variant="primary")
def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "btn-confirm":
self.dismiss(True)
elif event.button.id == "btn-cancel":
self.dismiss(False)
class PathInputScreen(ModalScreen):
"""Modal screen for inputting a path"""
CSS_PATH = str(Path(__file__).parent.parent / "styles" / "dialogs.tcss")
def __init__(self, title: str = "Enter Path", default_value: str = ""):
super().__init__()
self.title = title
self.default_value = default_value
def compose(self) -> ComposeResult:
with Container(id="path-input-dialog"):
yield Label(f"[bold cyan]{self.title}[/bold cyan]")
yield Label("")
yield Label("[dim]Enter absolute path to model directory[/dim]")
yield Input(placeholder="/path/to/models", value=self.default_value, id="path-input")
with Horizontal(id="path-input-buttons"):
yield Button("OK", id="btn-ok", variant="primary")
yield Button("Cancel", id="btn-cancel")
def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "btn-ok":
self._submit()
else:
self.dismiss(None)
def on_input_submitted(self, event: Input.Submitted) -> None:
"""Handle Enter key in input"""
self._submit()
def _submit(self) -> None:
"""Submit the path"""
input_widget = self.query_one("#path-input", Input)
path = input_widget.value.strip()
if not path:
self.notify("Path cannot be empty", severity="warning")
return
self.dismiss(path)
def on_mount(self) -> None:
"""Focus input when screen mounts"""
self.query_one("#path-input", Input).focus()
def on_key(self, event) -> None:
"""Handle key presses"""
if event.key == "escape":
self.dismiss(None)
class PathSelectScreen(ModalScreen):
"""Modal screen for selecting a path from a list"""
CSS_PATH = str(Path(__file__).parent.parent / "styles" / "dialogs.tcss")
def __init__(self, title: str, paths: list):
super().__init__()
self.title = title
self.paths = paths
def compose(self) -> ComposeResult:
with Container(id="path-select-dialog"):
yield Label(f"[bold cyan]{self.title}[/bold cyan]")
yield Label("")
with Container(id="path-list"):
for i, path in enumerate(self.paths):
yield Button(f"{i+1}. {path}", id=f"path-{i}", classes="path-item")
with Horizontal(id="path-select-buttons"):
yield Button("Cancel", id="btn-cancel", variant="primary")
def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "btn-cancel":
self.dismiss(None)
elif event.button.id.startswith("path-"):
index = int(event.button.id.split("-")[1])
self.dismiss(index)
def on_key(self, event) -> None:
"""Handle key presses"""
if event.key == "escape":
self.dismiss(None)