from rich.text import Text from textual import on from textual.app import ComposeResult from textual.binding import Binding from textual.containers import Vertical from textual.screen import ModalScreen from textual.widgets import Input, Label, OptionList from textual.widgets.option_list import Option import static_images class ImagePicker(ModalScreen[str | None]): BINDINGS = [ Binding("escape", "cancel", "Cancel"), Binding("down", "focus_list", "List", show=False), ] def __init__(self, current: str = "", start_filter: str = ""): super().__init__() self.current = current self.start_filter = start_filter self.images: list[dict] = [] def compose(self) -> ComposeResult: with Vertical(classes="dialog picker"): yield Label("Pick an image from static/", classes="dialog-title") yield Input(value=self.start_filter, placeholder="Type to filter, Enter to pick", id="image-filter") yield OptionList(id="image-list") yield Label("", id="image-count", classes="field-label") def on_mount(self): self.images = static_images.list_images() self.show_images(self.start_filter) def show_images(self, text: str): query = text.strip().lower() matches = [image for image in self.images if query in image["path"].lower()] option_list = self.query_one(OptionList) option_list.clear_options() option_list.add_options( Option(Text.assemble(image["path"], (" " + static_images.format_size(image["size"]), "dim")), id=image["path"]) for image in matches ) current_index = next((index for index, image in enumerate(matches) if image["path"] == self.current), 0) if matches: option_list.highlighted = current_index self.query_one("#image-count", Label).update(f"{len(matches)} of {len(self.images)} images") @on(Input.Changed, "#image-filter") def filter_changed(self, event: Input.Changed): self.show_images(event.value) @on(Input.Submitted, "#image-filter") def pick_highlighted(self): option_list = self.query_one(OptionList) if option_list.highlighted is not None and option_list.option_count: self.dismiss(option_list.get_option_at_index(option_list.highlighted).id) @on(OptionList.OptionSelected) def pick_option(self, event: OptionList.OptionSelected): self.dismiss(event.option.id) def action_focus_list(self): self.query_one(OptionList).focus() def action_cancel(self): self.dismiss(None)