From 6f265f448d921b299c9643c06fc44deb34971496 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 28 Jul 2021 15:12:59 +0530 Subject: [PATCH] DRYer --- docs/graphics-protocol.rst | 2 +- kittens/tui/images.py | 355 ++++++------------------------------- 2 files changed, 60 insertions(+), 297 deletions(-) diff --git a/docs/graphics-protocol.rst b/docs/graphics-protocol.rst index 1ad7b4f23..482d05f4a 100644 --- a/docs/graphics-protocol.rst +++ b/docs/graphics-protocol.rst @@ -757,7 +757,7 @@ Key Value Default Description **Keys for animation frame composition** ----------------------------------------------------------- -``c`` Positive integer ``0`` The 1-based frame number of the frame whose image data serves as the base data +``c`` Positive integer ``0`` The 1-based frame number of the frame whose image data serves as the overlaid data ``r`` Positive integer ``0`` The 1-based frame number of the frame that is being edited. ``x`` Positive integer ``0`` The left edge (in pixels) of the destination rectangle ``y`` Positive integer ``0`` The top edge (in pixels) of the destination rectangle diff --git a/kittens/tui/images.py b/kittens/tui/images.py index c50eab234..7f8cafe5a 100644 --- a/kittens/tui/images.py +++ b/kittens/tui/images.py @@ -11,8 +11,8 @@ from contextlib import suppress from enum import IntEnum from itertools import count from typing import ( - Any, Callable, DefaultDict, Deque, Dict, Iterator, List, Optional, - Sequence, Tuple, Union + Any, Callable, ClassVar, DefaultDict, Deque, Dict, Generic, Iterator, List, + Optional, Sequence, Tuple, Type, TypeVar, Union, cast ) from kitty.conf.utils import positive_float, positive_int @@ -299,310 +299,75 @@ def can_display_images() -> bool: ImageKey = Tuple[str, int, int] SentImageKey = Tuple[int, int, int] +T = TypeVar('T') + + +class Alias(Generic[T]): + + currently_processing: ClassVar[str] = '' + + def __init__(self, defval: T) -> None: + self.name = '' + self.defval = defval + + def __get__(self, instance: Optional['GraphicsCommand'], cls: Optional[Type['GraphicsCommand']] = None) -> T: + if instance is None: + return self.defval + return cast(T, instance._actual_values.get(self.name, self.defval)) + + def __set__(self, instance: 'GraphicsCommand', val: T) -> None: + if val == self.defval: + instance._actual_values.pop(self.name, None) + else: + instance._actual_values[self.name] = val + + def __set_name__(self, owner: Type['GraphicsCommand'], name: str) -> None: + if len(name) == 1: + Alias.currently_processing = name + self.name = Alias.currently_processing class GraphicsCommand: - a: GRT_a = 't' # action - q: int = 0 # suppress responses - f: GRT_f = 32 # image data format - t: GRT_t = 'd' # transmission medium - s: int = 0 # sent image width - v: int = 0 # sent image height - S: int = 0 # size of data to read from file - O: int = 0 # offset of data to read from file - i: int = 0 # image id - I: int = 0 # image number - p: int = 0 # placement id - o: Optional[GRT_o] = None # type of compression - m: GRT_m = 0 # 0 or 1 whether there is more chunked data - x: int = 0 # left edge of image area to display - y: int = 0 # top edge of image area to display - w: int = 0 # image width to display - h: int = 0 # image height to display - X: int = 0 # X-offset within cell - Y: int = 0 # Y-offset within cell - c: int = 0 # number of cols to display image over - r: int = 0 # number of rows to display image over - z: int = 0 # z-index - C: GRT_C = 0 # cursor movement policy/composition mode - d: GRT_d = 'a' # what to delete - - @property - def action(self) -> GRT_a: - return self.a - - @action.setter - def action(self, val: GRT_a) -> None: - self.a = val - - @property - def transmission_type(self) -> GRT_t: - return self.t - - @transmission_type.setter - def transmission_type(self, val: GRT_t) -> None: - self.t = val - - @property - def compressed(self) -> Optional[GRT_o]: - return self.o - - @compressed.setter - def compressed(self, val: Optional[GRT_o]) -> None: - self.o = val - - @property - def delete_action(self) -> Optional[GRT_d]: - return self.d - - @delete_action.setter - def delete_action(self, val: GRT_d) -> None: - self.d = val - - @delete_action.setter - def delete_action(self, val: GRT_d) -> None: - self.d = val - - @property - def format(self) -> GRT_f: - return self.f - - @format.setter - def format(self, val: GRT_f) -> None: - self.f = val - - @property - def more(self) -> GRT_m: - return self.m - - @more.setter - def more(self, val: GRT_m) -> None: - self.m = val - - @property - def image_id(self) -> int: - return self.i - - @image_id.setter - def image_id(self, val: int) -> None: - self.i = val - - @property - def image_number(self) -> int: - return self.I - - @image_number.setter - def image_number(self, val: int) -> None: - self.I = val # noqa - - @property - def placement_id(self) -> int: - return self.p - - @placement_id.setter - def placement_id(self, val: int) -> None: - self.p = val - - @property - def data_size(self) -> int: - return self.S - - @data_size.setter - def data_size(self, val: int) -> None: - self.S = val - - @property - def data_offset(self) -> int: - return self.O - - @data_offset.setter - def data_offset(self, val: int) -> None: - self.O = val # noqa - - @property - def quiet(self) -> int: - return self.q - - @quiet.setter - def quiet(self, val: int) -> None: - self.q = val - - @property - def width(self) -> int: - return self.w - - @width.setter - def width(self, val: int) -> None: - self.w = val - - @property - def height(self) -> int: - return self.h - - @height.setter - def height(self, val: int) -> None: - self.h = val - - @property - def left_edge(self) -> int: - return self.x - - @left_edge.setter - def left_edge(self, val: int) -> None: - self.x = val - - @property - def top_edge(self) -> int: - return self.y - - @top_edge.setter - def top_edge(self, val: int) -> None: - self.y = val - - @property - def cursor_movement(self) -> GRT_C: - return self.C - - @cursor_movement.setter - def cursor_movement(self, val: GRT_C) -> None: - self.C = val - - @property - def compose_mode(self) -> GRT_C: - return self.C - - @compose_mode.setter - def compose_mode(self, val: GRT_C) -> None: - self.C = val - - @property - def cell_x_offset(self) -> int: - return self.X - - @cell_x_offset.setter - def cell_x_offset(self, val: int) -> None: - self.X = val - - @property - def blend_mode(self) -> int: - return self.X - - @blend_mode.setter - def blend_mode(self, val: int) -> None: - self.X = val - - @property - def cell_y_offset(self) -> int: - return self.Y - - @cell_y_offset.setter - def cell_y_offset(self, val: int) -> None: - self.Y = val - - @property - def bgcolor(self) -> int: - return self.Y - - @bgcolor.setter - def bgcolor(self, val: int) -> None: - self.Y = val - - @property - def data_width(self) -> int: - return self.s - - @data_width.setter - def data_width(self, val: int) -> None: - self.s = val - - @property - def animation_state(self) -> int: - return self.s - - @animation_state.setter - def animation_state(self, val: int) -> None: - self.s = val - - @property - def data_height(self) -> int: - return self.v - - @data_height.setter - def data_height(self, val: int) -> None: - self.v = val - - @property - def loop_count(self) -> int: - return self.v - - @loop_count.setter - def loop_count(self, val: int) -> None: - self.v = val - - @property - def num_lines(self) -> int: - return self.r - - @num_lines.setter - def num_lines(self, val: int) -> None: - self.r = val - - @property - def frame_number(self) -> int: - return self.r - - @frame_number.setter - def frame_number(self, val: int) -> None: - self.r = val - - @property - def num_cells(self) -> int: - return self.c - - @num_cells.setter - def num_cells(self, val: int) -> None: - self.c = val - - @property - def other_frame_number(self) -> int: - return self.c - - @other_frame_number.setter - def other_frame_number(self, val: int) -> None: - self.c = val - - @property - def z_index(self) -> int: - return self.z - - @z_index.setter - def z_index(self, val: int) -> None: - self.z = val - - @property - def gap(self) -> int: - return self.z - - @gap.setter - def gap(self, val: int) -> None: - self.z = val + a = action = Alias(cast(GRT_a, 't')) + q = quiet = Alias(0) + f = format = Alias(32) + t = transmission_type = Alias(cast(GRT_t, 'd')) + s = data_width = animation_state = Alias(0) + v = data_height = loop_count = Alias(0) + S = data_size = Alias(0) + O = data_offset = Alias(0) # noqa + i = image_id = Alias(0) + I = image_number = Alias(0) # noqa + p = placement_id = Alias(0) + o = compression = Alias(cast(Optional[GRT_o], None)) + m = more = Alias(cast(GRT_m, 0)) + x = left_edge = Alias(0) + y = top_edge = Alias(0) + w = width = Alias(0) + h = height = Alias(0) + X = cell_x_offset = blend_mode = Alias(0) + Y = cell_y_offset = bgcolor = Alias(0) + c = columns = other_frame_number = Alias(0) + r = rows = frame_number = Alias(0) + z = z_index = gap = Alias(0) + C = cursor_movement = compose_mode = Alias(cast(GRT_C, 0)) + d = delete_action = Alias(cast(GRT_d, 'a')) + + def __init__(self) -> None: + self._actual_values: Dict[str, Any] = {} def __repr__(self) -> str: return self.serialize().decode('ascii').replace('\033', '^]') def clone(self) -> 'GraphicsCommand': ans = GraphicsCommand() - for k in GraphicsCommand.__annotations__: - setattr(ans, k, getattr(self, k)) + ans._actual_values = self._actual_values.copy() return ans def serialize(self, payload: Union[bytes, str] = b'') -> bytes: items = [] - for k in GraphicsCommand.__annotations__: - val: Union[str, None, int] = getattr(self, k) - defval: Union[str, None, int] = getattr(GraphicsCommand, k) - if val != defval and val is not None: - items.append('{}={}'.format(k, val)) + for k, val in self._actual_values.items(): + items.append('{}={}'.format(k, val)) ans: List[bytes] = [] w = ans.append @@ -617,9 +382,7 @@ class GraphicsCommand: return b''.join(ans) def clear(self) -> None: - for k in GraphicsCommand.__annotations__: - defval: Union[str, None, int] = getattr(GraphicsCommand, k) - setattr(self, k, defval) + self._actual_values = {} def iter_transmission_chunks(self, data: Optional[bytes] = None, level: int = -1, compression_threshold: int = 1024) -> Iterator[bytes]: if data is None: