codekingpro/portable-devtools
115k
1from typing import TYPE_CHECKING, List, Optional, Tuple, Union2 3if TYPE_CHECKING:4 from .console import (5 Console,6 ConsoleOptions,7 RenderableType,8 RenderResult,9 )10 11from .jupyter import JupyterMixin12from .measure import Measurement13from .segment import Segment14from .style import Style15 16PaddingDimensions = Union[int, Tuple[int], Tuple[int, int], Tuple[int, int, int, int]]17 18 19class Padding(JupyterMixin):20 """Draw space around content.21 22 Example:23 >>> print(Padding("Hello", (2, 4), style="on blue"))24 25 Args:26 renderable (RenderableType): String or other renderable.27 pad (Union[int, Tuple[int]]): Padding for top, right, bottom, and left borders.28 May be specified with 1, 2, or 4 integers (CSS style).29 style (Union[str, Style], optional): Style for padding characters. Defaults to "none".30 expand (bool, optional): Expand padding to fit available width. Defaults to True.31 """32 33 def __init__(34 self,35 renderable: "RenderableType",36 pad: "PaddingDimensions" = (0, 0, 0, 0),37 *,38 style: Union[str, Style] = "none",39 expand: bool = True,40 ):41 self.renderable = renderable42 self.top, self.right, self.bottom, self.left = self.unpack(pad)43 self.style = style44 self.expand = expand45 46 @classmethod47 def indent(cls, renderable: "RenderableType", level: int) -> "Padding":48 """Make padding instance to render an indent.49 50 Args:51 renderable (RenderableType): String or other renderable.52 level (int): Number of characters to indent.53 54 Returns:55 Padding: A Padding instance.56 """57 58 return Padding(renderable, pad=(0, 0, 0, level), expand=False)59 60 @staticmethod61 def unpack(pad: "PaddingDimensions") -> Tuple[int, int, int, int]:62 """Unpack padding specified in CSS style."""63 if isinstance(pad, int):64 return (pad, pad, pad, pad)65 if len(pad) == 1:66 _pad = pad[0]67 return (_pad, _pad, _pad, _pad)68 if len(pad) == 2:69 pad_top, pad_right = pad70 return (pad_top, pad_right, pad_top, pad_right)71 if len(pad) == 4:72 top, right, bottom, left = pad73 return (top, right, bottom, left)74 raise ValueError(f"1, 2 or 4 integers required for padding; {len(pad)} given")75 76 def __repr__(self) -> str:77 return f"Padding({self.renderable!r}, ({self.top},{self.right},{self.bottom},{self.left}))"78 79 def __rich_console__(80 self, console: "Console", options: "ConsoleOptions"81 ) -> "RenderResult":82 style = console.get_style(self.style)83 if self.expand:84 width = options.max_width85 else:86 width = min(87 Measurement.get(console, options, self.renderable).maximum88 + self.left89 + self.right,90 options.max_width,91 )92 render_options = options.update_width(width - self.left - self.right)93 if render_options.height is not None:94 render_options = render_options.update_height(95 height=render_options.height - self.top - self.bottom96 )97 lines = console.render_lines(98 self.renderable, render_options, style=style, pad=True99 )100 _Segment = Segment101 102 left = _Segment(" " * self.left, style) if self.left else None103 right = (104 [_Segment(f'{" " * self.right}', style), _Segment.line()]105 if self.right106 else [_Segment.line()]107 )108 blank_line: Optional[List[Segment]] = None109 if self.top:110 blank_line = [_Segment(f'{" " * width}\n', style)]111 yield from blank_line * self.top112 if left:113 for line in lines:114 yield left115 yield from line116 yield from right117 else:118 for line in lines:119 yield from line120 yield from right121 if self.bottom:122 blank_line = blank_line or [_Segment(f'{" " * width}\n', style)]123 yield from blank_line * self.bottom124 125 def __rich_measure__(126 self, console: "Console", options: "ConsoleOptions"127 ) -> "Measurement":128 max_width = options.max_width129 extra_width = self.left + self.right130 if max_width - extra_width < 1:131 return Measurement(max_width, max_width)132 measure_min, measure_max = Measurement.get(console, options, self.renderable)133 measurement = Measurement(measure_min + extra_width, measure_max + extra_width)134 measurement = measurement.with_maximum(max_width)135 return measurement136 137 138if __name__ == "__main__": # pragma: no cover139 from rich import print140 141 print(Padding("Hello, World", (2, 4), style="on blue"))142 