diff --git a/docs/changelog.rst b/docs/changelog.rst index b762031fc..c6bc018b5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -88,6 +88,12 @@ To update |kitty|, :doc:`follow the instructions `. - Wayland: Fix a crash when drag and dropping into kitty (:iss:`2432`) +- diff kitten: Fix images lingering as blank rectangles after the kitten quits + (:iss:`2449`) + +- diff kitten: Fix images losing position when scrolling using mouse + wheel/touchpad + 0.16.0 [2020-01-28] -------------------- diff --git a/kittens/diff/main.py b/kittens/diff/main.py index 8f3587b57..399b57d6c 100644 --- a/kittens/diff/main.py +++ b/kittens/diff/main.py @@ -36,7 +36,7 @@ from .render import ( ) from .search import BadRegex, Search from ..tui.handler import Handler -from ..tui.images import ImageManager +from ..tui.images import ImageManager, Placement from ..tui.line_edit import LineEdit from ..tui.loop import Loop from ..tui.operations import styled @@ -322,7 +322,43 @@ class DiffHandler(Handler): if image_involved: self.place_images() + def update_image_placement_for_resend(self, image_id: int, pl: Placement) -> bool: + offset = self.scroll_pos + limit = len(self.diff_lines) + in_image = False + + def adjust(row: int, candidate: ImagePlacement, is_left: bool) -> bool: + if candidate.image.image_id == image_id: + q = self.xpos_for_image(row, candidate, is_left) + if q is not None: + pl['x'] = q[0] + pl['y'] = row + return True + return False + + for row in range(self.num_lines): + lpos = offset + row + if lpos >= limit: + break + line = self.diff_lines[lpos] + if in_image: + if line.image_data is None: + in_image = False + continue + if line.image_data is not None: + left_placement, right_placement = line.image_data + if left_placement is not None: + if adjust(row, left_placement, True): + return True + in_image = True + if right_placement is not None: + if adjust(row, right_placement, False): + return True + in_image = True + return False + def place_images(self) -> None: + self.image_manager.update_image_placement_for_resend = self.update_image_placement_for_resend self.cmd.clear_images_on_screen() offset = self.scroll_pos limit = len(self.diff_lines) @@ -345,13 +381,20 @@ class DiffHandler(Handler): self.place_image(row, right_placement, False) in_image = True - def place_image(self, row: int, placement: ImagePlacement, is_left: bool) -> None: + def xpos_for_image(self, row: int, placement: ImagePlacement, is_left: bool) -> Optional[Tuple[int, float]]: xpos = (0 if is_left else (self.screen_size.cols // 2)) + placement.image.margin_size image_height_in_rows = placement.image.rows topmost_visible_row = placement.row num_visible_rows = image_height_in_rows - topmost_visible_row visible_frac = min(num_visible_rows / image_height_in_rows, 1) - if visible_frac > 0: + if visible_frac <= 0: + return None + return xpos, visible_frac + + def place_image(self, row: int, placement: ImagePlacement, is_left: bool) -> None: + q = self.xpos_for_image(row, placement, is_left) + if q is not None: + xpos, visible_frac = q height = int(visible_frac * placement.image.height) top = placement.image.height - height self.image_manager.show_image(placement.image.image_id, xpos, row, src_rect=( diff --git a/kittens/tui/images.py b/kittens/tui/images.py index 8e71900ca..f4c99d4df 100644 --- a/kittens/tui/images.py +++ b/kittens/tui/images.py @@ -10,7 +10,8 @@ from collections import defaultdict, deque from contextlib import suppress from itertools import count from typing import ( - Any, DefaultDict, Deque, Dict, List, Optional, Sequence, Tuple, Union + Any, Callable, DefaultDict, Deque, Dict, List, Optional, Sequence, Tuple, + Union ) from kitty.typing import ( @@ -191,6 +192,7 @@ class ImageManager: self.image_id_to_converted_data: Dict[int, ImageKey] = {} self.transmission_status: Dict[int, Union[str, int]] = {} self.placements_in_flight: DefaultDict[int, Deque[Placement]] = defaultdict(deque) + self.update_image_placement_for_resend: Optional[Callable[[int, Placement], bool]] @property def next_image_id(self) -> int: @@ -254,6 +256,8 @@ class ImageManager: self.placements_in_flight.pop(image_id, None) def resend_image(self, image_id: int, pl: Placement) -> None: + if self.update_image_placement_for_resend is not None and not self.update_image_placement_for_resend(image_id, pl): + return image_data = self.image_id_to_image_data[image_id] skey = self.image_id_to_converted_data[image_id] self.transmit_image(image_data, image_id, *skey)