_rich_components.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. from rich.cells import cell_len
  2. from rich.console import Console, ConsoleOptions, RenderResult, Style
  3. from rich.padding import Padding
  4. from rich.panel import Panel as RichPanel
  5. from rich.segment import Segment
  6. from rich.text import Text
  7. # this is a custom version of Rich's panel, where we override
  8. # the __rich_console__ magic method to just render a basic panel
  9. class Panel(RichPanel):
  10. def __rich_console__(
  11. self, console: "Console", options: "ConsoleOptions"
  12. ) -> "RenderResult":
  13. # copied from Panel.__rich_console__
  14. _padding = Padding.unpack(self.padding)
  15. renderable = (
  16. Padding(self.renderable, _padding) if any(_padding) else self.renderable
  17. )
  18. style = console.get_style(self.style)
  19. partial_border_style = console.get_style(self.border_style)
  20. border_style = style + partial_border_style
  21. width = (
  22. options.max_width
  23. if self.width is None
  24. else min(options.max_width, self.width)
  25. )
  26. safe_box: bool = console.safe_box if self.safe_box is None else self.safe_box
  27. box = self.box.substitute(options, safe=safe_box)
  28. def align_text(
  29. text: Text, width: int, align: str, character: str, style: Style
  30. ) -> Text:
  31. """Gets new aligned text.
  32. Args:
  33. text (Text): Title or subtitle text.
  34. width (int): Desired width.
  35. align (str): Alignment.
  36. character (str): Character for alignment.
  37. style (Style): Border style
  38. Returns:
  39. Text: New text instance
  40. """
  41. text = text.copy()
  42. text.truncate(width)
  43. excess_space = width - cell_len(text.plain)
  44. if text.style:
  45. text.stylize(console.get_style(text.style))
  46. if excess_space:
  47. if align == "left":
  48. return Text.assemble(
  49. text,
  50. (character * excess_space, style),
  51. no_wrap=True,
  52. end="",
  53. )
  54. elif align == "center":
  55. left = excess_space // 2
  56. return Text.assemble(
  57. (character * left, style),
  58. text,
  59. (character * (excess_space - left), style),
  60. no_wrap=True,
  61. end="",
  62. )
  63. else:
  64. return Text.assemble(
  65. (character * excess_space, style),
  66. text,
  67. no_wrap=True,
  68. end="",
  69. )
  70. return text
  71. title_text = self._title
  72. if title_text is not None:
  73. title_text.stylize_before(partial_border_style)
  74. child_width = (
  75. width - 2
  76. if self.expand
  77. else console.measure(
  78. renderable, options=options.update_width(width - 2)
  79. ).maximum
  80. )
  81. child_height = self.height or options.height or None
  82. if child_height:
  83. child_height -= 2
  84. if title_text is not None:
  85. child_width = min(
  86. options.max_width - 2, max(child_width, title_text.cell_len + 2)
  87. )
  88. width = child_width + 2
  89. child_options = options.update(
  90. width=child_width, height=child_height, highlight=self.highlight
  91. )
  92. lines = console.render_lines(renderable, child_options, style=style)
  93. line_start = Segment(box.mid_left, border_style)
  94. line_end = Segment(f"{box.mid_right}", border_style)
  95. new_line = Segment.line()
  96. if title_text is None or width <= 4:
  97. yield Segment(box.get_top([width - 2]), border_style)
  98. else:
  99. title_text = align_text(
  100. title_text,
  101. width - 4,
  102. self.title_align,
  103. box.top,
  104. border_style,
  105. )
  106. # changed from `box.top_left + box.top` to just `box.top_left``
  107. yield Segment(box.top_left, border_style)
  108. yield from console.render(title_text, child_options.update_width(width - 4))
  109. # changed from `box.top + box.top_right` to `box.top * 2 + box.top_right``
  110. yield Segment(box.top * 2 + box.top_right, border_style)
  111. yield new_line
  112. for line in lines:
  113. yield line_start
  114. yield from line
  115. yield line_end
  116. yield new_line
  117. subtitle_text = self._subtitle
  118. if subtitle_text is not None:
  119. subtitle_text.stylize_before(partial_border_style)
  120. if subtitle_text is None or width <= 4:
  121. yield Segment(box.get_bottom([width - 2]), border_style)
  122. else:
  123. subtitle_text = align_text(
  124. subtitle_text,
  125. width - 4,
  126. self.subtitle_align,
  127. box.bottom,
  128. border_style,
  129. )
  130. yield Segment(box.bottom_left + box.bottom, border_style)
  131. yield from console.render(
  132. subtitle_text, child_options.update_width(width - 4)
  133. )
  134. yield Segment(box.bottom + box.bottom_right, border_style)
  135. yield new_line