Skip to content

Commit 07dbde6

Browse files
serhiy-storchakaclaude
authored andcommitted
gh-71880: Allow editing the last cell in curses.textpad.Textbox (GH-152363)
Textbox.edit() ignored typing in the lower-right cell of the window. It is now written with insch(), which fills the cell without moving the cursor out of the window (addch() there raises an error and scrolls a scrollable window). (cherry picked from commit 11b3943) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 069c9c0 commit 07dbde6

3 files changed

Lines changed: 43 additions & 10 deletions

File tree

Lib/curses/textpad.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,16 @@ def _insert_printable_char(self, ch):
7474
self._update_max_yx()
7575
(y, x) = self.win.getyx()
7676
backyx = None
77-
while y < self.maxy or x < self.maxx:
77+
while True:
7878
if self.insert_mode:
7979
oldch = self.win.inch()
80-
# The try-catch ignores the error we trigger from some curses
81-
# versions by trying to write into the lowest-rightmost spot
82-
# in the window.
83-
try:
84-
self.win.addch(ch)
85-
except curses.error:
86-
pass
80+
if y >= self.maxy and x >= self.maxx:
81+
# Use insch() in the lower-right cell: addch() there would move
82+
# the cursor out of the window, raising an error and scrolling
83+
# a scrollable window.
84+
self.win.insch(ch)
85+
break
86+
self.win.addch(ch)
8787
if not self.insert_mode or not curses.ascii.isprint(oldch):
8888
break
8989
ch = oldch
@@ -101,8 +101,7 @@ def do_command(self, ch):
101101
(y, x) = self.win.getyx()
102102
self.lastcmd = ch
103103
if curses.ascii.isprint(ch):
104-
if y < self.maxy or x < self.maxx:
105-
self._insert_printable_char(ch)
104+
self._insert_printable_char(ch)
106105
elif ch == curses.ascii.SOH: # ^a
107106
self.win.move(y, 0)
108107
elif ch in (curses.ascii.STX,curses.KEY_LEFT,

Lib/test/test_curses.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,35 @@ def test_textbox_insert_mode(self):
12871287
self._type(box, 'b')
12881288
self.assertEqual(box.gather(), 'abXc ')
12891289

1290+
def test_textbox_fill_last_cell(self):
1291+
# The lower-right cell can be written, even though addch() there
1292+
# cannot advance the cursor past the end of the window.
1293+
box, win = self._make_textbox(1, 4, stripspaces=0)
1294+
self._type(box, 'abcd')
1295+
self.assertEqual(box.gather(), 'abcd')
1296+
1297+
def test_textbox_fill_last_cell_multiline(self):
1298+
box, win = self._make_textbox(2, 3, stripspaces=0)
1299+
self._type(box, 'abc')
1300+
box.do_command(curses.ascii.NL) # ^j -> start of next line
1301+
self._type(box, 'def') # 'f' lands in the lower-right cell
1302+
self.assertEqual(box.gather(), 'abc\ndef\n')
1303+
1304+
def test_textbox_fill_last_cell_insert_mode(self):
1305+
box, win = self._make_textbox(1, 4, insert_mode=True, stripspaces=0)
1306+
self._type(box, 'abcd')
1307+
self.assertEqual(box.gather(), 'abcd')
1308+
1309+
def test_textbox_fill_last_cell_scrollok(self):
1310+
# Writing the lower-right cell must not scroll the window even if it
1311+
# has scrolling enabled.
1312+
box, win = self._make_textbox(2, 3, stripspaces=0)
1313+
win.scrollok(True)
1314+
self._type(box, 'abc')
1315+
box.do_command(curses.ascii.NL)
1316+
self._type(box, 'def')
1317+
self.assertEqual(box.gather(), 'abc\ndef\n')
1318+
12901319
def test_textbox_movement(self):
12911320
box, win = self._make_textbox(3, 10)
12921321
self._type(box, 'abc')
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
:class:`curses.textpad.Textbox` now lets the lower-right cell of the window be
2+
edited. Writing it with :meth:`~curses.window.addch` would move the cursor
3+
past the end of the window, raising an error and scrolling a scrollable window,
4+
so it is now written with :meth:`~curses.window.insch`, which keeps the cursor
5+
in place.

0 commit comments

Comments
 (0)