Higher abstraction level on addmsg, fixes #43
This commit is contained in:
@ -2,7 +2,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import curses
|
||||
from typing import Any, Optional, Union
|
||||
from typing import Any, Optional, Tuple, Union
|
||||
|
||||
from squirrelbattle.display.texturepack import TexturePack
|
||||
from squirrelbattle.game import Game
|
||||
@ -16,6 +16,9 @@ class Display:
|
||||
height: int
|
||||
pad: Any
|
||||
|
||||
_color_pairs = {(curses.COLOR_WHITE, curses.COLOR_BLACK): 0}
|
||||
_colors_rgb = {}
|
||||
|
||||
def __init__(self, screen: Any, pack: Optional[TexturePack] = None):
|
||||
self.screen = screen
|
||||
self.pack = pack or TexturePack.get_pack("ascii")
|
||||
@ -31,15 +34,84 @@ class Display:
|
||||
lines = [line[:width] for line in lines]
|
||||
return "\n".join(lines)
|
||||
|
||||
def addstr(self, pad: Any, y: int, x: int, msg: str, *options) -> None:
|
||||
def translate_color(self, color: Union[int, Tuple[int, int, int]]) -> int:
|
||||
"""
|
||||
Translate a tuple (R, G, B) into a curses color index.
|
||||
If we have already a color index, then nothing is processed.
|
||||
If this is a tuple, we construct a new color index if non-existing
|
||||
and we return this index.
|
||||
The values of R, G and B must be between 0 and 1000, and not
|
||||
between 0 and 255.
|
||||
"""
|
||||
if isinstance(color, tuple):
|
||||
# The color is a tuple (R, G, B), that is potentially unknown.
|
||||
# We translate it into a curses color number.
|
||||
if color not in self._colors_rgb:
|
||||
# The color does not exist, we create it.
|
||||
color_nb = len(self._colors_rgb) + 8
|
||||
self.init_color(color_nb, color[0], color[1], color[2])
|
||||
self._colors_rgb[color] = color_nb
|
||||
color = self._colors_rgb[color]
|
||||
return color
|
||||
|
||||
def addstr(self, pad: Any, y: int, x: int, msg: str,
|
||||
fg_color: Union[int, Tuple[int, int, int]] = curses.COLOR_WHITE,
|
||||
bg_color: Union[int, Tuple[int, int, int]] = curses.COLOR_BLACK,
|
||||
*, altcharset: bool = False, blink: bool = False,
|
||||
bold: bool = False, dim: bool = False, invis: bool = False,
|
||||
italic: bool = False, normal: bool = False,
|
||||
protect: bool = False, reverse: bool = False,
|
||||
standout: bool = False, underline: bool = False,
|
||||
horizontal: bool = False, left: bool = False,
|
||||
low: bool = False, right: bool = False, top: bool = False,
|
||||
vertical: bool = False, chartext: bool = False) -> None:
|
||||
"""
|
||||
Display a message onto the pad.
|
||||
If the message is too large, it is truncated vertically and horizontally
|
||||
The text can be bold, italic, blinking, ... if the good parameters are
|
||||
given. These parameters are translated into curses attributes.
|
||||
The foreground and background colors can be given as curses constants
|
||||
(curses.COLOR_*), or by giving a tuple (R, G, B) that corresponds to
|
||||
the color. R, G, B must be between 0 and 1000, and not 0 and 255.
|
||||
"""
|
||||
height, width = pad.getmaxyx()
|
||||
# Truncate message if it is too large
|
||||
msg = self.truncate(msg, height - y, width - x - 1)
|
||||
if msg.replace("\n", "") and x >= 0 and y >= 0:
|
||||
return pad.addstr(y, x, msg, *options)
|
||||
fg_color = self.translate_color(fg_color)
|
||||
bg_color = self.translate_color(bg_color)
|
||||
|
||||
# Get the pair number for the tuple (fg, bg)
|
||||
# If it does not exist, create it and give a new unique id.
|
||||
if (fg_color, bg_color) in self._color_pairs:
|
||||
pair_nb = self._color_pairs[(fg_color, bg_color)]
|
||||
else:
|
||||
pair_nb = len(self._color_pairs)
|
||||
self.init_pair(pair_nb, fg_color, bg_color)
|
||||
self._color_pairs[(fg_color, bg_color)] = pair_nb
|
||||
|
||||
# Compute curses attributes from the parameters
|
||||
attr = self.color_pair(pair_nb)
|
||||
attr |= curses.A_ALTCHARSET if altcharset else 0
|
||||
attr |= curses.A_BLINK if blink else 0
|
||||
attr |= curses.A_BOLD if bold else 0
|
||||
attr |= curses.A_DIM if dim else 0
|
||||
attr |= curses.A_INVIS if invis else 0
|
||||
attr |= curses.A_ITALIC if italic else 0
|
||||
attr |= curses.A_NORMAL if normal else 0
|
||||
attr |= curses.A_PROTECT if protect else 0
|
||||
attr |= curses.A_REVERSE if reverse else 0
|
||||
attr |= curses.A_STANDOUT if standout else 0
|
||||
attr |= curses.A_UNDERLINE if underline else 0
|
||||
attr |= curses.A_HORIZONTAL if horizontal else 0
|
||||
attr |= curses.A_LEFT if left else 0
|
||||
attr |= curses.A_LOW if low else 0
|
||||
attr |= curses.A_RIGHT if right else 0
|
||||
attr |= curses.A_TOP if top else 0
|
||||
attr |= curses.A_VERTICAL if vertical else 0
|
||||
attr |= curses.A_CHARTEXT if chartext else 0
|
||||
|
||||
return pad.addstr(y, x, msg, attr)
|
||||
|
||||
def init_pair(self, number: int, foreground: int, background: int) -> None:
|
||||
return curses.init_pair(number, foreground, background) \
|
||||
@ -156,17 +228,13 @@ class Box(Display):
|
||||
self.pad = self.newpad(self.rows, self.cols)
|
||||
self.fg_border_color = fg_border_color or curses.COLOR_WHITE
|
||||
|
||||
pair_number = 4 + self.fg_border_color
|
||||
self.init_pair(pair_number, self.fg_border_color, curses.COLOR_BLACK)
|
||||
self.pair = self.color_pair(pair_number)
|
||||
|
||||
def display(self) -> None:
|
||||
self.addstr(self.pad, 0, 0, "┏" + "━" * (self.width - 2) + "┓",
|
||||
self.pair)
|
||||
self.fg_border_color)
|
||||
for i in range(1, self.height - 1):
|
||||
self.addstr(self.pad, i, 0, "┃", self.pair)
|
||||
self.addstr(self.pad, i, self.width - 1, "┃", self.pair)
|
||||
self.addstr(self.pad, i, 0, "┃", self.fg_border_color)
|
||||
self.addstr(self.pad, i, self.width - 1, "┃", self.fg_border_color)
|
||||
self.addstr(self.pad, self.height - 1, 0,
|
||||
"┗" + "━" * (self.width - 2) + "┛", self.pair)
|
||||
"┗" + "━" * (self.width - 2) + "┛", self.fg_border_color)
|
||||
self.refresh_pad(self.pad, 0, 0, self.y, self.x,
|
||||
self.y + self.height - 1, self.x + self.width - 1)
|
||||
|
Reference in New Issue
Block a user