2022-01-12 13:45:52 -05:00
|
|
|
from typing import Iterable, List, Reversible, Tuple
|
2022-01-11 16:08:57 -05:00
|
|
|
import textwrap
|
|
|
|
|
|
|
|
import tcod
|
|
|
|
|
|
|
|
import color
|
|
|
|
|
|
|
|
|
|
|
|
class Message:
|
|
|
|
def __init__(self, text: str, fg: Tuple[int, int, int]):
|
|
|
|
self.plain_text = text
|
|
|
|
self.fg = fg
|
|
|
|
self.count = 1
|
|
|
|
|
|
|
|
@property
|
|
|
|
def full_text(self) -> str:
|
|
|
|
"""The full text of this message, including the count if necessary."""
|
|
|
|
if self.count > 1:
|
2022-01-12 16:53:09 -05:00
|
|
|
return f"{self.plain_text} (x{self.count})"
|
2022-01-11 16:08:57 -05:00
|
|
|
|
|
|
|
return self.plain_text
|
|
|
|
|
|
|
|
|
|
|
|
class MessageLog:
|
|
|
|
def __init__(self) -> None:
|
|
|
|
self.messages: List[Message] = []
|
|
|
|
|
|
|
|
def add_message(
|
|
|
|
self,
|
|
|
|
text: str,
|
|
|
|
fg: Tuple[int, int, int] = color.white,
|
|
|
|
*,
|
|
|
|
stack: bool = True
|
|
|
|
) -> None:
|
|
|
|
"""Add a message to this log.
|
|
|
|
`text` is the message text, `fg` is the text color.
|
|
|
|
If `stack is True then the message can stack with a previous message
|
|
|
|
of the same text.
|
|
|
|
"""
|
|
|
|
if stack and self.messages and text == self.messages[-1].plain_text:
|
|
|
|
self.messages[-1].count += 1
|
|
|
|
else:
|
|
|
|
self.messages.append(Message(text, fg))
|
|
|
|
|
|
|
|
def render(
|
|
|
|
self,
|
|
|
|
console: tcod.Console,
|
|
|
|
x: int,
|
|
|
|
y: int,
|
|
|
|
width: int,
|
|
|
|
height: int,
|
|
|
|
) -> None:
|
|
|
|
"""Render this log over the given area.
|
|
|
|
`x`, `y`, `width`, `height` is the rectangular region to render onto
|
|
|
|
the `console`.
|
|
|
|
"""
|
|
|
|
self.render_messages(console, x, y, width, height, self.messages)
|
|
|
|
|
|
|
|
@staticmethod
|
2022-01-12 13:45:52 -05:00
|
|
|
def wrap(string: str, width: int) -> Iterable[str]:
|
|
|
|
"""Return a wrapped text message."""
|
|
|
|
for line in string.splitlines(): # Handle newlines in messages.
|
|
|
|
yield from textwrap.wrap(
|
|
|
|
line,
|
|
|
|
width,
|
|
|
|
expand_tabs=True
|
|
|
|
)
|
|
|
|
|
|
|
|
@classmethod
|
2022-01-11 16:08:57 -05:00
|
|
|
def render_messages(
|
2022-01-12 13:45:52 -05:00
|
|
|
cls,
|
2022-01-11 16:08:57 -05:00
|
|
|
console: tcod.Console,
|
|
|
|
x: int,
|
|
|
|
y: int,
|
|
|
|
width: int,
|
|
|
|
height: int,
|
|
|
|
messages: Reversible[Message],
|
|
|
|
) -> None:
|
|
|
|
"""Render the messages provided.
|
|
|
|
The `messages` are rendered starting at the last message and working
|
|
|
|
backwards.
|
|
|
|
"""
|
|
|
|
y_offset = height - 1
|
|
|
|
|
|
|
|
for message in reversed(messages):
|
2022-01-12 13:45:52 -05:00
|
|
|
for line in reversed(list(cls.wrap(message.full_text, width))):
|
2022-01-11 16:08:57 -05:00
|
|
|
console.print(x=x, y=y + y_offset, string=line, fg=message.fg)
|
|
|
|
y_offset -= 1
|
|
|
|
if y_offset < 0:
|
|
|
|
return # No more space to print messages
|