Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
popup.py174 linesDownload Raw Back to widget
1# Urwid Window-Icon-Menu-Pointer-style widget classes2#    Copyright (C) 2004-2011  Ian Ward3#4#    This library is free software; you can redistribute it and/or5#    modify it under the terms of the GNU Lesser General Public6#    License as published by the Free Software Foundation; either7#    version 2.1 of the License, or (at your option) any later version.8#9#    This library is distributed in the hope that it will be useful,10#    but WITHOUT ANY WARRANTY; without even the implied warranty of11#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU12#    Lesser General Public License for more details.13#14#    You should have received a copy of the GNU Lesser General Public15#    License along with this library; if not, write to the Free Software16#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA17#18# Urwid web site: https://urwid.org/19 20 21from __future__ import annotations22 23import typing24 25from urwid.canvas import CompositeCanvas26 27from .constants import Align, Sizing, VAlign28from .overlay import Overlay29from .widget import delegate_to_widget_mixin30from .widget_decoration import WidgetDecoration31 32if typing.TYPE_CHECKING:33    from typing_extensions import TypedDict34 35    from urwid.canvas import Canvas36 37    from .widget import Widget38 39    class PopUpParametersModel(TypedDict):40        left: int41        top: int42        overlay_width: int43        overlay_height: int44 45 46WrappedWidget = typing.TypeVar("WrappedWidget")47 48 49class PopUpLauncher(delegate_to_widget_mixin("_original_widget"), WidgetDecoration[WrappedWidget]):50    def __init__(self, original_widget: [WrappedWidget]) -> None:51        super().__init__(original_widget)52        self._pop_up_widget = None53 54    def create_pop_up(self) -> Widget:55        """56        Subclass must override this method and return a widget57        to be used for the pop-up.  This method is called once each time58        the pop-up is opened.59        """60        raise NotImplementedError("Subclass must override this method")61 62    def get_pop_up_parameters(self) -> PopUpParametersModel:63        """64        Subclass must override this method and have it return a dict, eg:65 66        {'left':0, 'top':1, 'overlay_width':30, 'overlay_height':4}67 68        This method is called each time this widget is rendered.69        """70        raise NotImplementedError("Subclass must override this method")71 72    def open_pop_up(self) -> None:73        self._pop_up_widget = self.create_pop_up()74        self._invalidate()75 76    def close_pop_up(self) -> None:77        self._pop_up_widget = None78        self._invalidate()79 80    def render(self, size, focus: bool = False) -> CompositeCanvas | Canvas:81        canv = super().render(size, focus)82        if self._pop_up_widget:83            canv = CompositeCanvas(canv)84            canv.set_pop_up(self._pop_up_widget, **self.get_pop_up_parameters())85        return canv86 87 88class PopUpTarget(WidgetDecoration[WrappedWidget]):89    # FIXME: this whole class is a terrible hack and must be fixed when layout and rendering are separated90    _sizing = frozenset((Sizing.BOX,))91    _selectable = True92 93    def __init__(self, original_widget: WrappedWidget) -> None:94        super().__init__(original_widget)95        self._pop_up = None96        self._current_widget = self._original_widget97 98    def _update_overlay(self, size: tuple[int, int], focus: bool) -> None:99        canv = self._original_widget.render(size, focus=focus)100        self._cache_original_canvas = canv  # imperfect performance hack101 102        if pop_up := canv.get_pop_up():103            left, top, (w, overlay_width, overlay_height) = pop_up104            if self._pop_up != w:105                self._pop_up = w106                self._current_widget = Overlay(107                    top_w=w,108                    bottom_w=self._original_widget,109                    align=Align.LEFT,110                    width=overlay_width,111                    valign=VAlign.TOP,112                    height=overlay_height,113                    left=left,114                    top=top,115                )116            else:117                self._current_widget.set_overlay_parameters(118                    align=Align.LEFT,119                    width=overlay_width,120                    valign=VAlign.TOP,121                    height=overlay_height,122                    left=left,123                    top=top,124                )125        else:126            self._pop_up = None127            self._current_widget = self._original_widget128 129    def render(self, size: tuple[int, int], focus: bool = False) -> Canvas:130        self._update_overlay(size, focus)131        return self._current_widget.render(size, focus=focus)132 133    def get_cursor_coords(self, size: tuple[int, int]) -> tuple[int, int] | None:134        self._update_overlay(size, True)135        return self._current_widget.get_cursor_coords(size)136 137    def get_pref_col(self, size: tuple[int, int]) -> int:138        self._update_overlay(size, True)139        return self._current_widget.get_pref_col(size)140 141    def keypress(self, size: tuple[int, int], key: str) -> str | None:142        self._update_overlay(size, True)143        return self._current_widget.keypress(size, key)144 145    def move_cursor_to_coords(self, size: tuple[int, int], x: int, y: int):146        self._update_overlay(size, True)147        return self._current_widget.move_cursor_to_coords(size, x, y)148 149    def mouse_event(150        self,151        size: tuple[int, int],152        event: str,153        button: int,154        col: int,155        row: int,156        focus: bool,157    ) -> bool | None:158        self._update_overlay(size, focus)159        return self._current_widget.mouse_event(size, event, button, col, row, focus)160 161    def pack(self, size: tuple[int, int] | None = None, focus: bool = False) -> tuple[int, int]:162        self._update_overlay(size, focus)163        return self._current_widget.pack(size)164 165 166def _test():167    import doctest168 169    doctest.testmod()170 171 172if __name__ == "__main__":173    _test()174 
codekingpro/portable-devtools · Team Ai