gh-60436: fix curses textbox backspace/del (#103783)

Co-authored-by: Łukasz Langa <lukasz@langa.pl>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: Michael Blahay <mblahay@gmail.com>
This commit is contained in:
Aidan Melen 2023-04-26 14:54:07 -06:00 committed by GitHub
parent 44010d0f12
commit a3a5b4bb23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 2 deletions

View File

@ -102,7 +102,10 @@ class Textbox:
self._insert_printable_char(ch)
elif ch == curses.ascii.SOH: # ^a
self.win.move(y, 0)
elif ch in (curses.ascii.STX,curses.KEY_LEFT, curses.ascii.BS,curses.KEY_BACKSPACE):
elif ch in (curses.ascii.STX,curses.KEY_LEFT,
curses.ascii.BS,
curses.KEY_BACKSPACE,
curses.ascii.DEL):
if x > 0:
self.win.move(y, x-1)
elif y == 0:
@ -111,7 +114,7 @@ class Textbox:
self.win.move(y-1, self._end_of_line(y-1))
else:
self.win.move(y-1, self.maxx)
if ch in (curses.ascii.BS, curses.KEY_BACKSPACE):
if ch in (curses.ascii.BS, curses.KEY_BACKSPACE, curses.ascii.DEL):
self.win.delch()
elif ch == curses.ascii.EOT: # ^d
self.win.delch()

View File

@ -5,6 +5,7 @@ import string
import sys
import tempfile
import unittest
from unittest.mock import MagicMock
from test.support import (requires, verbose, SaveSignals, cpython_only,
check_disallow_instantiation)
@ -1319,5 +1320,75 @@ def lorem_ipsum(win):
for y, line in enumerate(text[:maxy]):
win.addstr(y, 0, line[:maxx - (y == maxy - 1)])
class TextboxTest(unittest.TestCase):
def setUp(self):
self.mock_win = MagicMock(spec=curses.window)
self.mock_win.getyx.return_value = (1, 1)
self.mock_win.getmaxyx.return_value = (10, 20)
self.textbox = curses.textpad.Textbox(self.mock_win)
def test_init(self):
"""Test textbox initialization."""
self.mock_win.reset_mock()
tb = curses.textpad.Textbox(self.mock_win)
self.mock_win.getmaxyx.assert_called_once_with()
self.mock_win.keypad.assert_called_once_with(1)
self.assertEqual(tb.insert_mode, False)
self.assertEqual(tb.stripspaces, 1)
self.assertIsNone(tb.lastcmd)
self.mock_win.reset_mock()
def test_insert(self):
"""Test inserting a printable character."""
self.mock_win.reset_mock()
self.textbox.do_command(ord('a'))
self.mock_win.addch.assert_called_with(ord('a'))
self.textbox.do_command(ord('b'))
self.mock_win.addch.assert_called_with(ord('b'))
self.textbox.do_command(ord('c'))
self.mock_win.addch.assert_called_with(ord('c'))
self.mock_win.reset_mock()
def test_delete(self):
"""Test deleting a character."""
self.mock_win.reset_mock()
self.textbox.do_command(curses.ascii.BS)
self.textbox.do_command(curses.KEY_BACKSPACE)
self.textbox.do_command(curses.ascii.DEL)
assert self.mock_win.delch.call_count == 3
self.mock_win.reset_mock()
def test_move_left(self):
"""Test moving the cursor left."""
self.mock_win.reset_mock()
self.textbox.do_command(curses.KEY_LEFT)
self.mock_win.move.assert_called_with(1, 0)
self.textbox.do_command(curses.KEY_RIGHT)
self.mock_win.move.assert_called_with(1, 2)
self.mock_win.reset_mock()
def test_move_left(self):
"""Test moving the cursor left."""
self.mock_win.reset_mock()
self.textbox.do_command(curses.KEY_RIGHT)
self.mock_win.move.assert_called_with(1, 2)
self.mock_win.reset_mock()
def test_move_up(self):
"""Test moving the cursor left."""
self.mock_win.reset_mock()
self.textbox.do_command(curses.KEY_UP)
self.mock_win.move.assert_called_with(0, 1)
self.mock_win.reset_mock()
def test_move_down(self):
"""Test moving the cursor left."""
self.mock_win.reset_mock()
self.textbox.do_command(curses.KEY_DOWN)
self.mock_win.move.assert_called_with(2, 1)
self.mock_win.reset_mock()
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1 @@
update curses textbox to additionally handle backspace using the ``curses.ascii.DEL`` key press.