mcdreforged.minecraft.rtext.style 源代码

from abc import ABC, abstractmethod
from typing import Union, Optional, ClassVar

from colorama import Fore, Style
from typing_extensions import override

from mcdreforged.minecraft.rtext._registry import RRegistry, NamedObject
from mcdreforged.utils import class_utils, string_utils


[文档] class RItem(NamedObject): """ A general Minecraft text style item """ @property @abstractmethod def name(self) -> str: """ Its value in Minecraft text component """ raise NotImplementedError()
[文档] class RItemClassic(RItem, ABC): """ A type of :class:`RItem` that can be represented with a classic "§"-prefixed formatting code, or a "\\\\033[31m" styled console ANSI escape code .. seealso:: Minecraft Wiki `Formatting codes <https://minecraft.wiki/w/Formatting_codes>`__ page """ def __init__(self, name: str, mc_code: str, console_code: str): self.__name: str = name assert len(mc_code) == 2 and mc_code[0] == '§' self.__mc_code: str = mc_code self.__console_code: str = console_code @property @override def name(self) -> str: return self.__name @property def mc_code(self) -> str: """ Its code in Minecraft, with "§" prefix """ return self.__mc_code @property def console_code(self) -> str: """ Its code in console, i.e. ANSI escape code It might be an empty str if there's no appropriate code """ return self.__console_code def __repr__(self) -> str: return class_utils.represent(self, { 'name': self.name, 'mc_code': self.mc_code, 'console_code': self.console_code, })
# ------------------------------------------------ # Text Color # ------------------------------------------------ class __RColorMeta(RRegistry['RColor']): pass
[文档] class RColor(RItem, ABC, metaclass=__RColorMeta): """ Minecraft text colors """ black: ClassVar['RColorClassic'] dark_blue: ClassVar['RColorClassic'] dark_green: ClassVar['RColorClassic'] dark_aqua: ClassVar['RColorClassic'] dark_red: ClassVar['RColorClassic'] dark_purple: ClassVar['RColorClassic'] gold: ClassVar['RColorClassic'] gray: ClassVar['RColorClassic'] dark_gray: ClassVar['RColorClassic'] blue: ClassVar['RColorClassic'] green: ClassVar['RColorClassic'] aqua: ClassVar['RColorClassic'] red: ClassVar['RColorClassic'] light_purple: ClassVar['RColorClassic'] yellow: ClassVar['RColorClassic'] white: ClassVar['RColorClassic'] reset: ClassVar['RColorClassic'] def __init__(self, rgb_code: int): class_utils.check_type(rgb_code, int) self._rgb_code: int = rgb_code # e.g. 0xRRGGBB
[文档] @classmethod def from_mc_value(cls, value: str) -> 'RColor': """ A factory function to create a :class:`RColor` object from a valid Minecraft color value :param value: The value of the ``color`` field in Minecraft text component. e.g. ``"red"``, ``"blue"``, ``"#00AAFF"`` :return: A corresponding :class:`RColor` object :raise ValueError: If the given value is not a valid Minecraft color """ if value in cls._registry: return cls._registry[value] else: return RColorRGB.from_code(value)
# RGB attributes @property def r(self) -> int: """ The red portion of the color in [0, 255] """ return (self._rgb_code >> 16) & 0xFF @property def g(self) -> int: """ The green portion of the color in [0, 255] """ return (self._rgb_code >> 8) & 0xFF @property def b(self) -> int: """ The blue portion of the color in [0, 255] """ return (self._rgb_code >> 0) & 0xFF
def __register_classic_rcolor(): def register(name: str, rgb_hex: int, mc_code: str, console_code: str): # noinspection PyProtectedMember RColor._register_item(name, RColorClassic(name, rgb_hex, mc_code, console_code)) register('black', 0x000000, '§0', Fore.BLACK) register('dark_blue', 0x0000AA, '§1', Fore.BLUE) register('dark_green', 0x00AA00, '§2', Fore.GREEN) register('dark_aqua', 0x00AAAA, '§3', Fore.CYAN) register('dark_red', 0xAA0000, '§4', Fore.RED) register('dark_purple', 0xAA00AA, '§5', Fore.MAGENTA) register('gold', 0xFFAA00, '§6', Fore.YELLOW) register('gray', 0xAAAAAA, '§7', Fore.WHITE + Style.DIM) register('dark_gray', 0x555555, '§8', Fore.WHITE + Style.DIM) register('blue', 0x5555FF, '§9', Fore.LIGHTBLUE_EX) register('green', 0x55FF55, '§a', Fore.LIGHTGREEN_EX) register('aqua', 0x55FFFF, '§b', Fore.LIGHTCYAN_EX) register('red', 0xFF5555, '§c', Fore.LIGHTRED_EX) register('light_purple', 0xFF55FF, '§d', Fore.LIGHTMAGENTA_EX) register('yellow', 0xFFFF55, '§e', Fore.LIGHTYELLOW_EX) register('white', 0xFFFFFF, '§f', Fore.WHITE) # default text color is white, so use 0xFFFFFF register('reset', 0xFFFFFF, '§r', Style.RESET_ALL) # noinspection PyProtectedMember RColor._ensure_registration_done()
[文档] class RColorClassic(RItemClassic, RColor): """ Classic Minecraft text color defined with color name .. attention:: Do not construct the class yourself. Use class fields in :class:`RColor` if you want to use classic colors """ def __init__(self, name: str, rgb_code: int, mc_code: str, console_code: str): super().__init__(name, mc_code, console_code) super(RItemClassic, self).__init__(rgb_code) self.__rgb_cache: Optional['RColorRGB'] = None
[文档] def to_rgb(self) -> 'RColorRGB': """ Converts to the corresponding :class:`RColorRGB` object with the exact same color """ if self.__rgb_cache is None: # concurrency invocation is fine self.__rgb_cache = RColorRGB(self._rgb_code) return self.__rgb_cache
[文档] class RColorRGB(RColor): """ Minecraft text color type in hexadecimal color format, with which you can specify the red / green / blue values of the color precisely .. note:: Available in Minecraft 1.16+ """
[文档] def __init__(self, rgb_code: int): """ :param rgb_code: An int in hex 0xRRGGBB format, or a str like ``"RRGGBB"``, ``"0xRRGGBB"`` or ``"#RRGGBB"`` """ class_utils.check_type(rgb_code, (str, int)) super().__init__(rgb_code) self.__classic_cache: Optional['RColorClassic'] = None
[文档] @classmethod def from_code(cls, rgb_code: Union[str, int]) -> 'RColorRGB': """ A factory function to create a :class:`RColorRGB` object using a single RGB code :param rgb_code: An int in hex 0xRRGGBB format, or a str like ``"RRGGBB"``, ``"0xRRGGBB"`` or ``"#RRGGBB"`` """ class_utils.check_type(rgb_code, (str, int)) if isinstance(rgb_code, str): hex_code = string_utils.remove_prefix(string_utils.remove_prefix(rgb_code, '0x'), '#') try: rgb_code = int(hex_code, 16) except ValueError: raise ValueError('Invalid rgb code {}'.format(repr(rgb_code))) from None return RColorRGB(rgb_code)
[文档] @classmethod def from_rgb(cls, red: int, green: int, blue: int) -> 'RColorRGB': """ A factory function to create a :class:`RColorRGB` object using 3 RGB values :param red: The red portion of the color :param green: The green portion of the color :param blue: The blue portion of the color """ return cls.from_code((red << 16) | (green << 8) | (blue << 0))
def __to_classic(self) -> 'RColorClassic': def calc_distance(x: RColorRGB, y: RColorRGB) -> int: return (x.r - y.r) ** 2 + (x.g - y.g) ** 2 + (x.b - y.b) ** 2 result: Optional[RColorClassic] = None min_distance: int = 0 for color in RColor: if isinstance(color, RColorClassic) and color != RColor.reset: # ignore reset distance = calc_distance(self, color.to_rgb()) if result is None or distance < min_distance: result = color min_distance = distance if result is None: raise AssertionError() return result
[文档] def to_classic(self) -> 'RColorClassic': """ Converts to the :class:`RColorClassic` object with the closest Euclidean distance in the RGB color space """ if self.__classic_cache is None: # concurrency invocation is fine self.__classic_cache = self.__to_classic() return self.__classic_cache
def __repr__(self) -> str: return class_utils.represent(self, {'rgb': '0x{:06X}'.format(self._rgb_code)}) # Interface implementation @property @override def name(self) -> str: return '#{:06X}'.format(self._rgb_code)
__register_classic_rcolor() # ------------------------------------------------ # Text Style # ------------------------------------------------ class __RStyleMeta(RRegistry['RStyle']): pass
[文档] class RStyle(RItem, ABC, metaclass=__RStyleMeta): """ Minecraft text styles """ bold: ClassVar['RStyleClassic'] italic: ClassVar['RStyleClassic'] underlined: ClassVar['RStyleClassic'] strikethrough: ClassVar['RStyleClassic'] obfuscated: ClassVar['RStyleClassic']
[文档] class RStyleClassic(RItemClassic, RStyle): """ Classic Minecraft text style with corresponding "§"-prefixed formatting code """
def __register_rstyle(): def register(name: str, mc_code: str, console_code: str): # noinspection PyProtectedMember RStyle._register_item(name, RStyleClassic(name, mc_code, console_code)) register('bold', '§l', Style.BRIGHT) register('italic', '§o', '') register('underlined', '§n', '') register('strikethrough', '§m', '') register('obfuscated', '§k', '') # noinspection PyProtectedMember RStyle._ensure_registration_done() __register_rstyle()