codekingpro/portable-devtools
115k
1from typing import Literal, Optional, Tuple2 3from ._loop import loop_last4from .console import Console, ConsoleOptions, RenderableType, RenderResult5from .control import Control6from .segment import ControlType, Segment7from .style import StyleType8from .text import Text9 10VerticalOverflowMethod = Literal["crop", "ellipsis", "visible"]11 12 13class LiveRender:14 """Creates a renderable that may be updated.15 16 Args:17 renderable (RenderableType): Any renderable object.18 style (StyleType, optional): An optional style to apply to the renderable. Defaults to "".19 """20 21 def __init__(22 self,23 renderable: RenderableType,24 style: StyleType = "",25 vertical_overflow: VerticalOverflowMethod = "ellipsis",26 ) -> None:27 self.renderable = renderable28 self.style = style29 self.vertical_overflow = vertical_overflow30 self._shape: Optional[Tuple[int, int]] = None31 32 @property33 def last_render_height(self) -> int:34 """The number of lines in the last render (may be 0 if nothing was rendered).35 36 Returns:37 Height in lines38 """39 if self._shape is None:40 return 041 return self._shape[1]42 43 def set_renderable(self, renderable: RenderableType) -> None:44 """Set a new renderable.45 46 Args:47 renderable (RenderableType): Any renderable object, including str.48 """49 self.renderable = renderable50 51 def position_cursor(self) -> Control:52 """Get control codes to move cursor to beginning of live render.53 54 Returns:55 Control: A control instance that may be printed.56 """57 if self._shape is not None:58 _, height = self._shape59 return Control(60 ControlType.CARRIAGE_RETURN,61 (ControlType.ERASE_IN_LINE, 2),62 *(63 (64 (ControlType.CURSOR_UP, 1),65 (ControlType.ERASE_IN_LINE, 2),66 )67 * (height - 1)68 )69 )70 return Control()71 72 def restore_cursor(self) -> Control:73 """Get control codes to clear the render and restore the cursor to its previous position.74 75 Returns:76 Control: A Control instance that may be printed.77 """78 if self._shape is not None:79 _, height = self._shape80 return Control(81 ControlType.CARRIAGE_RETURN,82 *((ControlType.CURSOR_UP, 1), (ControlType.ERASE_IN_LINE, 2)) * height83 )84 return Control()85 86 def __rich_console__(87 self, console: Console, options: ConsoleOptions88 ) -> RenderResult:89 renderable = self.renderable90 style = console.get_style(self.style)91 lines = console.render_lines(renderable, options, style=style, pad=False)92 shape = Segment.get_shape(lines)93 94 _, height = shape95 if height > options.size.height:96 if self.vertical_overflow == "crop":97 lines = lines[: options.size.height]98 shape = Segment.get_shape(lines)99 elif self.vertical_overflow == "ellipsis":100 lines = lines[: (options.size.height - 1)]101 overflow_text = Text(102 "...",103 overflow="crop",104 justify="center",105 end="",106 style="live.ellipsis",107 )108 lines.append(list(console.render(overflow_text)))109 shape = Segment.get_shape(lines)110 self._shape = shape111 112 new_line = Segment.line()113 for last, line in loop_last(lines):114 yield from line115 if not last:116 yield new_line117 