Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
line_box.py208 linesDownload Raw Back to widget
1from __future__ import annotations2 3import typing4 5from .columns import Columns6from .constants import BOX_SYMBOLS, Align, WHSettings7from .divider import Divider8from .pile import Pile9from .solid_fill import SolidFill10from .text import Text11from .widget_decoration import WidgetDecoration, delegate_to_widget_mixin12 13if typing.TYPE_CHECKING:14    from typing_extensions import Literal15 16    from .widget import Widget17 18WrappedWidget = typing.TypeVar("WrappedWidget")19 20 21class LineBox(WidgetDecoration[WrappedWidget], delegate_to_widget_mixin("_wrapped_widget")):22    Symbols = BOX_SYMBOLS23 24    def __init__(25        self,26        original_widget: WrappedWidget,27        title: str = "",28        title_align: Literal["left", "center", "right"] | Align = Align.CENTER,29        title_attr=None,30        tlcorner: str = BOX_SYMBOLS.LIGHT.TOP_LEFT,31        tline: str = BOX_SYMBOLS.LIGHT.HORIZONTAL,32        lline: str = BOX_SYMBOLS.LIGHT.VERTICAL,33        trcorner: str = BOX_SYMBOLS.LIGHT.TOP_RIGHT,34        blcorner: str = BOX_SYMBOLS.LIGHT.BOTTOM_LEFT,35        rline: str = BOX_SYMBOLS.LIGHT.VERTICAL,36        bline: str = BOX_SYMBOLS.LIGHT.HORIZONTAL,37        brcorner: str = BOX_SYMBOLS.LIGHT.BOTTOM_RIGHT,38    ) -> None:39        """40        Draw a line around original_widget.41 42        Use 'title' to set an initial title text with will be centered43        on top of the box.44 45        Use `title_attr` to apply a specific attribute to the title text.46 47        Use `title_align` to align the title to the 'left', 'right', or 'center'.48        The default is 'center'.49 50        You can also override the widgets used for the lines/corners:51            tline: top line52            bline: bottom line53            lline: left line54            rline: right line55            tlcorner: top left corner56            trcorner: top right corner57            blcorner: bottom left corner58            brcorner: bottom right corner59 60        If empty string is specified for one of the lines/corners, then no character will be output there.61        If no top/bottom/left/right lines - whole lines will be omitted.62        This allows for seamless use of adjoining LineBoxes.63 64        Class attribute `Symbols` can be used as source for standard lines:65 66        >>> print(LineBox(Text("Some text")).render(()))67        ┌─────────┐68        │Some text│69        └─────────┘70        >>> print(71        ...     LineBox(72        ...         Text("Some text"),73        ...         tlcorner=LineBox.Symbols.LIGHT.TOP_LEFT_ROUNDED,74        ...         trcorner=LineBox.Symbols.LIGHT.TOP_RIGHT_ROUNDED,75        ...         blcorner=LineBox.Symbols.LIGHT.BOTTOM_LEFT_ROUNDED,76        ...         brcorner=LineBox.Symbols.LIGHT.BOTTOM_RIGHT_ROUNDED,77        ...     ).render(())78        ... )79        ╭─────────╮80        │Some text│81        ╰─────────╯82        >>> print(83        ...     LineBox(84        ...         Text("Some text"),85        ...         tline=LineBox.Symbols.HEAVY.HORIZONTAL,86        ...         bline=LineBox.Symbols.HEAVY.HORIZONTAL,87        ...         lline=LineBox.Symbols.HEAVY.VERTICAL,88        ...         rline=LineBox.Symbols.HEAVY.VERTICAL,89        ...         tlcorner=LineBox.Symbols.HEAVY.TOP_LEFT,90        ...         trcorner=LineBox.Symbols.HEAVY.TOP_RIGHT,91        ...         blcorner=LineBox.Symbols.HEAVY.BOTTOM_LEFT,92        ...         brcorner=LineBox.Symbols.HEAVY.BOTTOM_RIGHT,93        ...     ).render(())94        ... )95        ┏━━━━━━━━━┓96        ┃Some text┃97        ┗━━━━━━━━━┛98 99        To make Table constructions, some lineboxes need to be drawn without sides100        and T or CROSS symbols used for corners of cells.101        """102 103        w_lline = SolidFill(lline)104        w_rline = SolidFill(rline)105 106        w_tlcorner, w_tline, w_trcorner = Text(tlcorner), Divider(tline), Text(trcorner)107        w_blcorner, w_bline, w_brcorner = Text(blcorner), Divider(bline), Text(brcorner)108 109        if not tline and title:110            raise ValueError("Cannot have a title when tline is empty string")111 112        if title_attr:113            self.title_widget = Text((title_attr, self.format_title(title)))114        else:115            self.title_widget = Text(self.format_title(title))116 117        if tline:118            if title_align not in {Align.LEFT, Align.CENTER, Align.RIGHT}:119                raise ValueError('title_align must be one of "left", "right", or "center"')120            if title_align == Align.LEFT:121                tline_widgets = [(WHSettings.PACK, self.title_widget), w_tline]122            else:123                tline_widgets = [w_tline, (WHSettings.PACK, self.title_widget)]124                if title_align == Align.CENTER:125                    tline_widgets.append(w_tline)126 127            self.tline_widget = Columns(tline_widgets)128            top = Columns(129                (130                    (int(bool(tlcorner and lline)), w_tlcorner),131                    self.tline_widget,132                    (int(bool(trcorner and rline)), w_trcorner),133                )134            )135 136        else:137            self.tline_widget = None138            top = None139 140        # Note: We need to define a fixed first widget (even if it's 0 width) so that the other141        # widgets have something to anchor onto142        middle = Columns(143            ((int(bool(lline)), w_lline), original_widget, (int(bool(rline)), w_rline)),144            box_columns=[0, 2],145            focus_column=original_widget,146        )147 148        if bline:149            bottom = Columns(150                (151                    (int(bool(blcorner and lline)), w_blcorner),152                    w_bline,153                    (int(bool(brcorner and rline)), w_brcorner),154                )155            )156        else:157            bottom = None158 159        pile_widgets = []160        if top:161            pile_widgets.append((WHSettings.PACK, top))162        pile_widgets.append(middle)163        if bottom:164            pile_widgets.append((WHSettings.PACK, bottom))165 166        self._wrapped_widget = Pile(pile_widgets, focus_item=middle)167 168        super().__init__(original_widget)169 170    @property171    def original_widget(self) -> WrappedWidget:172        return super().original_widget173 174    @original_widget.setter175    def original_widget(self, original_widget: WrappedWidget) -> None:176        v_index = int(bool(self.tline_widget))  # we care only about top177        h_index = 1  # constant178        middle: Columns = typing.cast("Columns", self._wrapped_widget[v_index])179        _old_widget, options = middle.contents[h_index]180        middle.contents[h_index] = (original_widget, options)181        WidgetDecoration.original_widget.fset(self, original_widget)  # pylint: disable=no-member182 183    @property184    def _w(self) -> Pile:185        return self._wrapped_widget186 187    def format_title(self, text: str) -> str:188        if text:189            return f" {text} "190 191        return ""192 193    def set_title(self, text: str) -> None:194        if not self.tline_widget:195            raise ValueError("Cannot set title when tline is unset")196        self.title_widget.set_text(self.format_title(text))197        self.tline_widget._invalidate()198 199    @property200    def focus(self) -> Widget | None:201        """LineBox is partially container.202 203        While focus position is a bit hacky204        (formally it's not container and only position 0 available),205        focus widget is always provided by original widget.206        """207        return self._original_widget.focus208 
codekingpro/portable-devtools · Team Ai