From b354e954016963184d6dcaff4f5fa515ba7fd81b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 Oct 2016 20:15:07 +0530 Subject: [PATCH] Implement cursor movement APIs --- kitty/screen.py | 41 +++++++++++++++++++++++++---------------- kitty_tests/screen.py | 25 ++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/kitty/screen.py b/kitty/screen.py index f3049d46b..49949c728 100644 --- a/kitty/screen.py +++ b/kitty/screen.py @@ -66,6 +66,11 @@ class Screen(QObject): def notify_cursor_position(self, x, y): if self._notify_cursor_position: + if x >= self.columns: + if y < self.lines - 1: + x, y = 0, y + 1 + else: + x, y = x - 1, y self.cursor_position_changed(self.cursor, x, y) @property @@ -707,59 +712,63 @@ class Screen(QObject): self.cursor.x = max(0, min(self.cursor.x, self.columns - 1)) self.cursor.y = max(top, min(self.cursor.y, bottom)) - def cursor_up(self, count=None): + def cursor_up(self, count=1, do_carriage_return=False, move_direction=-1): """Moves cursor up the indicated # of lines in same column. Cursor stops at top margin. :param int count: number of lines to skip. """ - self.cursor.y -= count or 1 + x, y = self.cursor.x, self.cursor.y + self.cursor.y += move_direction * (count or 1) self.ensure_bounds(use_margins=True) + if do_carriage_return: + self.cursor.x = 0 + if y != self.cursor.y or x != self.cursor.x: + self.notify_cursor_position(x, y) - def cursor_up1(self, count=None): + def cursor_up1(self, count=1): """Moves cursor up the indicated # of lines to column 1. Cursor stops at bottom margin. :param int count: number of lines to skip. """ - self.cursor_up(count) - self.carriage_return() + self.cursor_up(count, do_carriage_return=True) - def cursor_down(self, count=None): + def cursor_down(self, count=1): """Moves cursor down the indicated # of lines in same column. Cursor stops at bottom margin. :param int count: number of lines to skip. """ - self.cursor.y += count or 1 - self.ensure_bounds(use_margins=True) + self.cursor_up(count, move_direction=1) - def cursor_down1(self, count=None): + def cursor_down1(self, count=1): """Moves cursor down the indicated # of lines to column 1. Cursor stops at bottom margin. :param int count: number of lines to skip. """ - self.cursor_down(count) - self.carriage_return() + self.cursor_up(count, do_carriage_return=True, move_direction=1) - def cursor_back(self, count=None): + def cursor_back(self, count=1, move_direction=-1): """Moves cursor left the indicated # of columns. Cursor stops at left margin. :param int count: number of columns to skip. """ - self.cursor.x -= count or 1 + x = self.cursor.x + self.cursor.x += move_direction * (count or 1) self.ensure_bounds() + if x != self.cursor.x: + self.notify_cursor_position(x, self.cursor.y) - def cursor_forward(self, count=None): + def cursor_forward(self, count=1): """Moves cursor right the indicated # of columns. Cursor stops at right margin. :param int count: number of columns to skip. """ - self.cursor.x += count or 1 - self.ensure_bounds() + self.cursor_back(count, move_direction=1) def cursor_position(self, line=None, column=None): """Set the cursor to a specific `line` and `column`. diff --git a/kitty_tests/screen.py b/kitty_tests/screen.py index 04001ec8c..412b046c8 100644 --- a/kitty_tests/screen.py +++ b/kitty_tests/screen.py @@ -64,7 +64,7 @@ class TestScreen(BaseTest): self.ae(str(s.linebuf[0]), 'ココx') self.ae(str(s.linebuf[1]), 'ニチ ') self.ae(str(s.linebuf[2]), 'ハ ') - self.assertChanges(t, ignore='cursor', cells={0: ((5, 5),), 1: ((0, 3),), 2: ((0, 1),)}) + self.assertChanges(t, ignore='cursor', cells={1: ((0, 3),), 2: ((0, 1),)}) self.ae(s.cursor.x, 2), self.ae(s.cursor.y, 2) s.draw('Ƶ̧\u0308'.encode('utf-8')) self.ae(str(s.linebuf[2]), 'ハƵ̧\u0308 ') @@ -192,3 +192,26 @@ class TestScreen(BaseTest): self.ae(s.display, (' ', ' ', ' ', ' ', ' ')) self.assertChanges(t, lines=set(range(5))) self.assertFalse(s.linebuf[0].cursor_from(1).bold) + + def test_cursor_movement(self): + s, t = self.create_screen() + s.draw(b'12345' * 5) + t.reset() + s.cursor_up(2) + self.ae((s.cursor.x, s.cursor.y), (4, 2)) + self.assertChanges(t, ignore='cursor', cells={4: ((4, 4),)}) + s.cursor_up1() + self.ae((s.cursor.x, s.cursor.y), (0, 1)) + self.assertChanges(t, ignore='cursor', cells={2: ((4, 4),)}) + s.cursor_forward(3) + self.ae((s.cursor.x, s.cursor.y), (3, 1)) + self.assertChanges(t, ignore='cursor', cells={1: ((0, 0),)}) + s.cursor_back() + self.ae((s.cursor.x, s.cursor.y), (2, 1)) + self.assertChanges(t, ignore='cursor', cells={1: ((3, 3),)}) + s.cursor_down() + self.ae((s.cursor.x, s.cursor.y), (2, 2)) + self.assertChanges(t, ignore='cursor', cells={1: ((2, 2),)}) + s.cursor_down1(5) + self.ae((s.cursor.x, s.cursor.y), (0, 4)) + self.assertChanges(t, ignore='cursor', cells={2: ((2, 2),)})