from typing import Optional, Tuple import urwid __all__ = ["EditWidget", "PasswordEditWidget"] class EditWidget(urwid.WidgetWrap): def __init__(self, prompt: str, caption: str = "", style: Optional[str] = None, ) -> None: self._prompt = urwid.Text(prompt, align=urwid.CENTER) self._edit = urwid.Edit(caption=caption, align=urwid.CENTER) if style is None: self._edit_wrap = self._edit else: self._edit_wrap = urwid.AttrMap(self._edit, style) self._pile = urwid.Pile([ (urwid.PACK, self._prompt), (urwid.PACK, self._edit_wrap), ]) super().__init__(self._pile) @property def width(self) -> int: prompt_width, _ = self._prompt.pack(None) edit_width, _ = self._edit.pack(None) return max(prompt_width, edit_width + 1) @property def text(self) -> str: return self._edit.edit_text @text.setter def text(self, text: str) -> None: self._edit.edit_text = text class PasswordEditWidget(urwid.WidgetWrap): def __init__(self, prompt: str, mask_char: str = "*", style: Optional[str] = None, ) -> None: self._mask_char = mask_char self._prompt = urwid.Text(prompt, align=urwid.CENTER) self._edit = urwid.Edit(align=urwid.CENTER) self._fake_edit = urwid.Edit(align=urwid.CENTER) if style is None: self._fake_edit_wrap = self._fake_edit else: self._fake_edit_wrap = urwid.AttrMap(self._fake_edit, style) self._pile = urwid.Pile([ (urwid.PACK, self._prompt), (urwid.PACK, self._fake_edit_wrap), ]) super().__init__(self._pile) @property def width(self) -> int: prompt_width, _ = self._prompt.pack(None) edit_width, _ = self._edit.pack(None) return max(prompt_width, edit_width + 1) @property def text(self) -> str: return self._edit.edit_text @text.setter def text(self, text: str) -> None: self._edit.edit_text = text def update_fake_edit(self) -> None: fake_text = self._mask_char * len(self._edit.edit_text) self._fake_edit.edit_text = fake_text self._fake_edit.edit_pos = self._edit.edit_pos def keypress(self, size: Tuple[int], key: str) -> Optional[str]: key = self._edit.keypress(size, key) self.update_fake_edit() return key