]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-71880: Allow editing the last cell in curses.textpad.Textbox (GH-152363)
authorSerhiy Storchaka <storchaka@gmail.com>
Sat, 27 Jun 2026 07:08:04 +0000 (10:08 +0300)
committerGitHub <noreply@github.com>
Sat, 27 Jun 2026 07:08:04 +0000 (10:08 +0300)
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).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Lib/curses/textpad.py
Lib/test/test_curses.py
Misc/NEWS.d/next/Library/2026-06-26-23-56-40.gh-issue-71880.782D31.rst [new file with mode: 0644]

index c8dbf9fb614fcbeaafa58d407502ad6b643f788a..57b2f4a523c95bc6f3900b6acb68d3dccf7a89e5 100644 (file)
@@ -74,16 +74,16 @@ class Textbox:
         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
@@ -101,8 +101,7 @@ class Textbox:
         (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,
index a2f33abc658ffe34aaf6a3d5eff0623013b1eeb3..b63fb63476f4255b8533ca0b15c6501bd4c3a01e 100644 (file)
@@ -1961,6 +1961,35 @@ class TestCurses(unittest.TestCase):
         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')
diff --git a/Misc/NEWS.d/next/Library/2026-06-26-23-56-40.gh-issue-71880.782D31.rst b/Misc/NEWS.d/next/Library/2026-06-26-23-56-40.gh-issue-71880.782D31.rst
new file mode 100644 (file)
index 0000000..5a12f42
--- /dev/null
@@ -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.