from textual.screen import ModalScreen from textual.containers import Container from context_menu import ButtonStatic from textual.widgets import TextArea, OptionList from textual.widgets.option_list import Option from textual.widget import Widget from textual.geometry import Region, Spacing, Offset class CompletionsMenu(Widget): DEFAULT_CSS = """ CompletionsMenu { width: auto; height: auto; overlay: screen; position: absolute; background: $surface; min-width: 30; OptionList { padding: 0 1; background: $surface; border: $panel tall; width: 30; } } """ def __init__(self, options: list[dict], text_area: TextArea): super().__init__() self.options = options self.text_area = text_area self.options_list = None def compose(self): self.options_list = OptionList(*[self.render_option(option) for option in self.options]) yield self.options_list def update(self, new_choices: list[dict]): self.options = new_choices self.options_list.set_options([self.render_option(option) for option in self.options]) def hide(self): self.display = "none" def show(self): self.display = "block" def render_option(self, option_data: dict): icon = "" match option_data["type"]: case 1: # text icon = "󰦨" case 2 | 3: # method / function icon = "[blueviolet]󰅩[/]" case 14: # keyword icon = "" case 7: # class icon = "[orange][/]" case 9: # module icon = "[cornflowerblue][/]" case _: icon = str(option_data["type"]) return icon + " " + option_data["text"] def align_to_cursor(self): x, y = self.text_area.cursor_screen_offset dropdown = self.options_list width, height = dropdown.outer_size # Constrain the dropdown within the screen. x, y, _width, _height = Region(x - 1, y + 1, width, height).constrain( "inside", "none", Spacing.all(0), self.screen.scrollable_content_region, ) self.absolute_offset = Offset(x, y) def on_mount(self): #self.options_list.styles.height = len(self.options_list.children) + 2 self.align_to_cursor()