89 lines
2.6 KiB
Python
89 lines
2.6 KiB
Python
|
|
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()
|