Compare commits

..
2 Commits
Author SHA1 Message Date
pdxlocations f375d788f1 Bump version to 1.7.4 in pyproject.toml 2026-10-27 22:01:14 -07:00
pdxlocations 4c4b8a42c5 tryfix f5 resize crash 2026-10-27 22:00:53 -07:00
3 changed files with 49 additions and 18 deletions

No files matched your search

+24 -17
View File
@@ -2065,34 +2065,41 @@ def refresh_pad(window: int, commit: bool = True) -> None:
# Clamp start_index within the pad's height
pad_h, pad_w = pad.getmaxyx()
if pad_h <= 0:
if pad_h <= 0 or pad_w <= 0:
return
start_index = max(0, min(start_index, max(0, pad_h - 1)))
top = box_y + 1
left = box_x + 1
bottom = box_y + min(inner_h, lines) # inclusive
right = box_x + min(inner_w, box_w - 2)
# A resize can leave the frame larger than its pad or the screen.
# noutrefresh requires both source and destination rectangles to fit.
bottom = min(top + lines - 1, top + pad_h - start_index - 1, curses.LINES - 1)
right = min(left + inner_w - 1, left + pad_w - 1, curses.COLS - 1)
if bottom < top or right < left:
return
draw_frame_title(box, get_window_title(window))
box.noutrefresh()
try:
box.noutrefresh()
# The frame overlaps the pad's viewport. Recompose even unchanged pad
# rows before committing, so its text cannot flash blank between draws.
pad.touchwin()
pad.noutrefresh(
start_index,
0,
top,
left,
bottom,
right,
)
if commit:
curses.doupdate()
# The frame overlaps the pad's viewport. Recompose even unchanged pad
# rows before committing, so its text cannot flash blank between draws.
pad.touchwin()
pad.noutrefresh(
start_index,
0,
top,
left,
bottom,
right,
)
if commit:
curses.doupdate()
except curses.error:
# The terminal can resize again between checking bounds and drawing.
# Let the next resize/redraw rebuild the windows instead of exiting.
ui_state.redraw_full_ui = True
def add_notification(channel_number: int) -> None:
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "contact"
version = "1.7.3"
version = "1.7.4"
description = "This Python curses client for Meshtastic is a terminal-based client designed to manage device settings, enable mesh chat communication, and handle configuration backups and restores."
authors = [
{name = "Ben Lipsey",email = "ben@pdxlocations.com"}
+24
View File
@@ -12,6 +12,28 @@ from tests.test_support import reset_shared_state, restore_config, snapshot_conf
class ContactUiTests(unittest.TestCase):
def test_refresh_pad_clips_to_pad_and_resized_screen(self):
for pad_size, screen_size, expected in (
((2, 5), (24, 80), (1, 0, 1, 1, 1, 5)),
((100, 100), (6, 8), (1, 0, 1, 1, 5, 7)),
):
with self.subTest(pad_size=pad_size, screen_size=screen_size), ExitStack() as stack:
box = mock.Mock()
box.getmaxyx.return_value = (40, 100)
box.getbegyx.return_value = (0, 0)
pad = mock.Mock()
pad.getmaxyx.return_value = pad_size
ui_state.selected_channel = 38
stack.enter_context(mock.patch.object(contact_ui, "channel_win", box, create=True))
stack.enter_context(mock.patch.object(contact_ui, "channel_pad", pad, create=True))
stack.enter_context(mock.patch.object(contact_ui.curses, "LINES", screen_size[0], create=True))
stack.enter_context(mock.patch.object(contact_ui.curses, "COLS", screen_size[1], create=True))
contact_ui.refresh_pad(0, commit=False)
pad.noutrefresh.assert_called_once_with(*expected)
pad.noutrefresh.side_effect = contact_ui.curses.error("resize during refresh")
contact_ui.refresh_pad(0, commit=False)
self.assertTrue(ui_state.redraw_full_ui)
def test_f5_rebuilds_background_before_staging_after_terminal_resize(self):
for size in ((24, 80), (50, 140)):
with self.subTest(size=size), ExitStack() as stack:
@@ -594,6 +616,8 @@ class ContactUiTests(unittest.TestCase):
def test_refresh_pad_draws_selected_channel_title_on_message_frame(self) -> None:
self.addCleanup(mock.patch.stopall)
mock.patch.object(contact_ui.curses, "LINES", 24, create=True).start()
mock.patch.object(contact_ui.curses, "COLS", 80, create=True).start()
mock.patch.object(contact_ui, "packetlog_win", mock.Mock(), create=True).start()
ui_state.single_pane_mode = True
ui_state.current_window = 1