mirror of https://github.com/Textualize/rich.git
scrollbars
This commit is contained in:
parent
068cb86555
commit
6aac8da698
|
@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [10.3.0] - Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- Added Console.size setter
|
||||
- Added Console.width setter
|
||||
- Added Console.height setter
|
||||
|
||||
## [10.2.2] - 2021-05-19
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -962,8 +962,16 @@ class Console:
|
|||
Returns:
|
||||
int: The width (in characters) of the console.
|
||||
"""
|
||||
width, _ = self.size
|
||||
return width
|
||||
return self.size.width
|
||||
|
||||
@width.setter
|
||||
def width(self, width: int) -> None:
|
||||
"""Set width.
|
||||
|
||||
Args:
|
||||
width (int): New width.
|
||||
"""
|
||||
self._width = width
|
||||
|
||||
@property
|
||||
def height(self) -> int:
|
||||
|
@ -972,8 +980,16 @@ class Console:
|
|||
Returns:
|
||||
int: The height (in lines) of the console.
|
||||
"""
|
||||
_, height = self.size
|
||||
return height
|
||||
return self.size.height
|
||||
|
||||
@height.setter
|
||||
def height(self, height: int) -> None:
|
||||
"""Set height.
|
||||
|
||||
Args:
|
||||
height (int): new height.
|
||||
"""
|
||||
self._height = height
|
||||
|
||||
def bell(self) -> None:
|
||||
"""Play a 'bell' sound (if supported by the terminal)."""
|
||||
|
|
|
@ -24,6 +24,8 @@ class Screen:
|
|||
style (StyleType, optional): Optional background style. Defaults to None.
|
||||
"""
|
||||
|
||||
renderable: "RenderableType"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*renderables: "RenderableType",
|
||||
|
|
|
@ -7,7 +7,11 @@ from .style import Style
|
|||
|
||||
from itertools import filterfalse
|
||||
from operator import attrgetter
|
||||
from typing import Iterable, List, Sequence, Union, Tuple
|
||||
from typing import Iterable, List, Sequence, Union, Tuple, TYPE_CHECKING
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .console import Console, ConsoleOptions, RenderResult
|
||||
|
||||
|
||||
class ControlType(IntEnum):
|
||||
|
@ -404,6 +408,30 @@ class Segment(NamedTuple):
|
|||
yield cls(text, None, control)
|
||||
|
||||
|
||||
class Segments:
|
||||
"""A simple renderable to render an iterable of segments.
|
||||
|
||||
Args:
|
||||
segments (Iterable[Segment]): An iterable of segments.
|
||||
new_lines (bool, optional): Add new lines between segments. Defaults to False.
|
||||
"""
|
||||
|
||||
def __init__(self, segments: Iterable[Segment], new_lines: bool = False) -> None:
|
||||
self.segments = list(segments)
|
||||
self.new_lines = new_lines
|
||||
|
||||
def __rich_console__(
|
||||
self, console: "Console", options: "ConsoleOptions"
|
||||
) -> "RenderResult":
|
||||
if self.new_lines:
|
||||
line = Segment.line()
|
||||
for segment in self.segments:
|
||||
yield segment
|
||||
yield line
|
||||
else:
|
||||
yield from self.segments
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
from rich.syntax import Syntax
|
||||
from rich.text import Text
|
||||
|
|
|
@ -4,8 +4,8 @@ import logging
|
|||
import signal
|
||||
from typing import Any, Dict, Set
|
||||
|
||||
from rich.live import Live
|
||||
from rich.control import Control
|
||||
from rich.repr import rich_repr, RichReprResult
|
||||
from rich.screen import Screen
|
||||
|
||||
from . import events
|
||||
|
@ -22,23 +22,27 @@ log = logging.getLogger("rich")
|
|||
LayoutDefinition = Dict[str, Any]
|
||||
|
||||
|
||||
@rich_repr
|
||||
class App(MessagePump):
|
||||
view: View
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
console: Console = None,
|
||||
view: View = None,
|
||||
screen: bool = True,
|
||||
auto_refresh=4,
|
||||
title: str = "Megasoma Application",
|
||||
):
|
||||
super().__init__()
|
||||
self.console = console or get_console()
|
||||
self._screen = screen
|
||||
self._auto_refresh = auto_refresh
|
||||
self.title = title
|
||||
self.view = view or LayoutView()
|
||||
self.children: Set[MessagePump] = set()
|
||||
|
||||
def __rich_repr__(self) -> RichReprResult:
|
||||
yield "title", self.title
|
||||
|
||||
@classmethod
|
||||
def run(cls, console: Console = None, screen: bool = True):
|
||||
async def run_app() -> None:
|
||||
|
@ -99,8 +103,6 @@ class App(MessagePump):
|
|||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
from logging import FileHandler
|
||||
from rich.layout import Layout
|
||||
from rich.panel import Panel
|
||||
|
||||
from .widgets.header import Header
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ class Timer(Event, type=EventType.TIMER, priority=10):
|
|||
self.callback = callback
|
||||
|
||||
def __rich_repr__(self) -> RichReprResult:
|
||||
yield "timer", self.timer
|
||||
yield self.timer.name
|
||||
|
||||
|
||||
class Focus(Event, type=EventType.FOCUS):
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
from rich.layout import Layout
|
||||
|
||||
|
||||
class LayoutManager:
|
||||
def __init__(self) -> None:
|
||||
self.layout = Layout()
|
|
@ -105,7 +105,7 @@ class MessagePump:
|
|||
except Exception:
|
||||
log.exception("error getting message")
|
||||
break
|
||||
log.debug(repr(message))
|
||||
log.debug("%r -> %r", message, self)
|
||||
await self.dispatch_message(message, priority)
|
||||
|
||||
async def dispatch_message(self, message: Message, priority: int) -> Optional[bool]:
|
||||
|
@ -150,16 +150,3 @@ class MessagePump:
|
|||
async def on_timer(self, event: events.Timer) -> None:
|
||||
if event.callback is not None:
|
||||
await event.callback()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
class Widget(MessagePump):
|
||||
pass
|
||||
|
||||
widget1 = Widget()
|
||||
widget2 = Widget()
|
||||
|
||||
import asyncio
|
||||
|
||||
asyncio.get_event_loop().run_until_complete(widget1.run_message_loop())
|
|
@ -0,0 +1,85 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from rich.segment import Segment
|
||||
from rich.style import Style
|
||||
|
||||
|
||||
def render_bar(
|
||||
height: int = 25,
|
||||
size: float = 100,
|
||||
window_size: float = 25,
|
||||
position: float = 0,
|
||||
bar_style: Optional[Style] = None,
|
||||
back_style: Optional[Style] = None,
|
||||
ascii_only: bool = False,
|
||||
vertical: bool = True,
|
||||
) -> List[Segment]:
|
||||
if vertical:
|
||||
if ascii_only:
|
||||
solid = "|"
|
||||
half_start = "|"
|
||||
half_end = "|"
|
||||
else:
|
||||
solid = "┃"
|
||||
half_start = "╻"
|
||||
half_end = "╹"
|
||||
else:
|
||||
if ascii_only:
|
||||
solid = "-"
|
||||
half_start = "-"
|
||||
half_end = "-"
|
||||
else:
|
||||
solid = "━"
|
||||
half_start = "╺"
|
||||
half_end = "╸"
|
||||
|
||||
_bar_style = bar_style or Style.parse("bright_magenta")
|
||||
_back_style = back_style or Style.parse("#555555")
|
||||
|
||||
_Segment = Segment
|
||||
|
||||
start_bar_segment = _Segment(half_start, _bar_style)
|
||||
end_bar_segment = _Segment(half_end, _bar_style)
|
||||
bar_segment = _Segment(solid, _bar_style)
|
||||
|
||||
start_back_segment = _Segment(half_end, _back_style)
|
||||
end_back_segment = _Segment(half_end, _back_style)
|
||||
back_segment = _Segment(solid, _back_style)
|
||||
|
||||
segments = [back_segment] * height
|
||||
|
||||
step_size = size / height
|
||||
|
||||
start = position / step_size
|
||||
end = (position + window_size) / step_size
|
||||
|
||||
start_index = int(start)
|
||||
end_index = int(end)
|
||||
bar_height = (end_index - start_index) + 1
|
||||
|
||||
segments[start_index:end_index] = [bar_segment] * bar_height
|
||||
|
||||
sub_position = start % 1.0
|
||||
if sub_position >= 0.5:
|
||||
segments[start_index] = start_bar_segment
|
||||
elif start_index:
|
||||
segments[start_index - 1] = end_back_segment
|
||||
|
||||
sub_position = end % 1.0
|
||||
if sub_position < 0.5:
|
||||
segments[end_index] = end_bar_segment
|
||||
elif end_index + 1 < len(segments):
|
||||
segments[end_index + 1] = start_back_segment
|
||||
|
||||
return segments
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from rich.console import Console
|
||||
from rich.segment import Segments
|
||||
|
||||
console = Console()
|
||||
|
||||
bar = render_bar(height=20, position=10, vertical=False, ascii_only=False)
|
||||
|
||||
console.print(Segments(bar, new_lines=False))
|
|
@ -1,8 +1,9 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from rich.console import Console, RenderableType
|
||||
from rich.console import Console, ConsoleOptions, RenderResult, RenderableType
|
||||
from rich.layout import Layout
|
||||
from rich.live import Live
|
||||
from rich.repr import rich_repr, RichReprResult
|
||||
|
||||
from . import events
|
||||
from ._context import active_app
|
||||
|
@ -14,7 +15,8 @@ if TYPE_CHECKING:
|
|||
from .app import App
|
||||
|
||||
|
||||
class View(MessagePump):
|
||||
@rich_repr
|
||||
class View(ABC, MessagePump):
|
||||
@property
|
||||
def app(self) -> "App":
|
||||
return active_app.get()
|
||||
|
@ -23,16 +25,34 @@ class View(MessagePump):
|
|||
def console(self) -> Console:
|
||||
return active_app.get().console
|
||||
|
||||
def __rich_console__(
|
||||
self, console: Console, options: ConsoleOptions
|
||||
) -> RenderResult:
|
||||
return
|
||||
yield
|
||||
|
||||
def __rich_repr__(self) -> RichReprResult:
|
||||
return
|
||||
yield
|
||||
|
||||
async def on_resize(self, event: events.Resize) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def mount(self, widget: Widget, *, slot: str = "main") -> None:
|
||||
...
|
||||
|
||||
|
||||
class LayoutView(View):
|
||||
layout: Layout
|
||||
|
||||
def __init__(
|
||||
self, layout: Layout = None, title: str = "Layout Application"
|
||||
self,
|
||||
layout: Layout = None,
|
||||
name: str = "default",
|
||||
title: str = "Layout Application",
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.title = title
|
||||
if layout is None:
|
||||
layout = Layout()
|
||||
|
@ -49,6 +69,9 @@ class LayoutView(View):
|
|||
self.layout = layout
|
||||
super().__init__()
|
||||
|
||||
def __rich_repr__(self) -> RichReprResult:
|
||||
yield "name", self.name
|
||||
|
||||
def __rich__(self) -> RenderableType:
|
||||
return self.layout
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
from typing import Optional
|
||||
|
||||
from rich.console import RenderableType
|
||||
from ..widget import Widget
|
||||
|
||||
|
||||
class Window(Widget):
|
||||
renderable: Optional[RenderableType]
|
||||
|
||||
def __init__(self, renderable: RenderableType):
|
||||
self.renderable = renderable
|
||||
|
||||
def update(self, renderable: RenderableType) -> None:
|
||||
self.renderable = renderable
|
Loading…
Reference in New Issue