import re import textual.widgets class MultiSelect(textual.widgets.Select): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.__new_options__ = [] self.__seltion_states__ = {} self.__default_value__ = True self.__selection_regex__ = "" def __sorted_selection_keys__(self): rv = list(self.__seltion_states__.keys()) rv.sort() return rv def SetSelectionRegEx(self, regex: str) -> None: self.__selection_regex__ = regex self.set_options(self.__get_select_list__()) def AddEntry(self, entry) -> None: if entry not in self.__new_options__ and entry not in self.__seltion_states__: self.__seltion_states__[entry] = self.__default_value__ self.__new_options__.append(entry) def __toggle__(self, entry_num): if entry_num < 0: if not self.__selection_regex__: self.__default_value__ = entry_num == -1 for key in self.__seltion_states__: if self.__match__(key): self.__seltion_states__[key] = entry_num == -1 elif entry_num > len(self.__seltion_states__): raise ValueError(f"The Entry '{entry} is not in the list") else: entry = self.__sorted_selection_keys__()[entry_num] self.__seltion_states__[entry] = not self.__seltion_states__[entry] def __match__(self, key): try: match = len(re.findall(self.__selection_regex__, key)) > 0 except re.error: match = True # No valid regular expression return match def __get_select_list__(self): rv = [] if len(self.__seltion_states__) > 2: rv.append(('All', -1)) rv.append(('None', -2)) for index, key in enumerate(self.__sorted_selection_keys__()): if self.__match__(key): prefix = "\\[X] " if self.__seltion_states__[key] else "\\[-] " rv.append((prefix + key, index)) return rv def IsSelected(self, entry): return self.__seltion_states__.get(entry, self.__default_value__) def _on_blur(self, event): for entry in self.__new_options__: self.__seltion_states__[entry] = self.__default_value__ self.__new_options__ = [] self.set_options(self.__get_select_list__()) return super()._on_blur(event) def _on_message(self, message): if type(message) == self.Changed: if type(message.value) is int: self.__toggle__(message.value) self.clear() self.set_options(self.__get_select_list__()) return super()._on_message(message)