Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions Lib/curses/textpad.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,16 @@ def _insert_printable_char(self, ch):
self._update_max_yx()
(y, x) = self.win.getyx()
backyx = None
while y < self.maxy or x < self.maxx:
while True:
if self.insert_mode:
oldch = self.win.inch()
# The try-catch ignores the error we trigger from some curses
# versions by trying to write into the lowest-rightmost spot
# in the window.
try:
self.win.addch(ch)
except curses.error:
pass
if y >= self.maxy and x >= self.maxx:
# Use insch() in the lower-right cell: addch() there would move
# the cursor out of the window, raising an error and scrolling
# a scrollable window.
self.win.insch(ch)
break
self.win.addch(ch)
if not self.insert_mode or not curses.ascii.isprint(oldch):
break
ch = oldch
Expand All @@ -101,8 +101,7 @@ def do_command(self, ch):
(y, x) = self.win.getyx()
self.lastcmd = ch
if curses.ascii.isprint(ch):
if y < self.maxy or x < self.maxx:
self._insert_printable_char(ch)
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,
Expand Down
29 changes: 29 additions & 0 deletions Lib/test/test_curses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,35 @@ def test_textbox_insert_mode(self):
self._type(box, 'b')
self.assertEqual(box.gather(), 'abXc ')

def test_textbox_fill_last_cell(self):
# The lower-right cell can be written, even though addch() there
# cannot advance the cursor past the end of the window.
box, win = self._make_textbox(1, 4, stripspaces=0)
self._type(box, 'abcd')
self.assertEqual(box.gather(), 'abcd')

def test_textbox_fill_last_cell_multiline(self):
box, win = self._make_textbox(2, 3, stripspaces=0)
self._type(box, 'abc')
box.do_command(curses.ascii.NL) # ^j -> start of next line
self._type(box, 'def') # 'f' lands in the lower-right cell
self.assertEqual(box.gather(), 'abc\ndef\n')

def test_textbox_fill_last_cell_insert_mode(self):
box, win = self._make_textbox(1, 4, insert_mode=True, stripspaces=0)
self._type(box, 'abcd')
self.assertEqual(box.gather(), 'abcd')

def test_textbox_fill_last_cell_scrollok(self):
# Writing the lower-right cell must not scroll the window even if it
# has scrolling enabled.
box, win = self._make_textbox(2, 3, stripspaces=0)
win.scrollok(True)
self._type(box, 'abc')
box.do_command(curses.ascii.NL)
self._type(box, 'def')
self.assertEqual(box.gather(), 'abc\ndef\n')

def test_textbox_movement(self):
box, win = self._make_textbox(3, 10)
self._type(box, 'abc')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
:class:`curses.textpad.Textbox` now lets the lower-right cell of the window be
edited. Writing it with :meth:`~curses.window.addch` would move the cursor
past the end of the window, raising an error and scrolling a scrollable window,
so it is now written with :meth:`~curses.window.insch`, which keeps the cursor
in place.
Loading