Refactor UI redraw handling and improve message drawing logic

This commit is contained in:
pdxlocations
2026-03-19 14:06:01 -07:00
parent 8f376edabe
commit ecc5308dad
9 changed files with 338 additions and 152 deletions
+57
View File
@@ -36,3 +36,60 @@ class ContactUiTests(unittest.TestCase):
self.assertEqual(curs_set.call_args_list[0].args, (0,))
self.assertEqual(curs_set.call_args_list[-1].args, (1,))
self.assertEqual(ui_state.current_window, 1)
def test_process_pending_ui_updates_draws_requested_windows(self) -> None:
stdscr = mock.Mock()
ui_state.redraw_channels = True
ui_state.redraw_messages = True
ui_state.redraw_nodes = True
ui_state.redraw_packetlog = True
ui_state.scroll_messages_to_bottom = True
with mock.patch.object(contact_ui, "draw_channel_list") as draw_channel_list:
with mock.patch.object(contact_ui, "draw_messages_window") as draw_messages_window:
with mock.patch.object(contact_ui, "draw_node_list") as draw_node_list:
with mock.patch.object(contact_ui, "draw_packetlog_win") as draw_packetlog_win:
contact_ui.process_pending_ui_updates(stdscr)
draw_channel_list.assert_called_once_with()
draw_messages_window.assert_called_once_with(True)
draw_node_list.assert_called_once_with()
draw_packetlog_win.assert_called_once_with()
def test_process_pending_ui_updates_full_redraw_uses_handle_resize(self) -> None:
stdscr = mock.Mock()
ui_state.redraw_full_ui = True
ui_state.redraw_channels = True
ui_state.redraw_messages = True
with mock.patch.object(contact_ui, "handle_resize") as handle_resize:
contact_ui.process_pending_ui_updates(stdscr)
handle_resize.assert_called_once_with(stdscr, False)
self.assertFalse(ui_state.redraw_channels)
self.assertFalse(ui_state.redraw_messages)
def test_refresh_node_selection_highlights_full_row_width(self) -> None:
ui_state.node_list = [101, 202]
ui_state.selected_node = 1
ui_state.start_index = [0, 0, 0]
contact_ui.nodes_pad = mock.Mock()
contact_ui.nodes_pad.getmaxyx.return_value = (4, 20)
contact_ui.nodes_win = mock.Mock()
contact_ui.nodes_win.getmaxyx.return_value = (10, 20)
interface = mock.Mock()
interface.nodesByNum = {101: {}, 202: {}}
with mock.patch.object(contact_ui, "refresh_pad") as refresh_pad:
with mock.patch.object(contact_ui, "draw_window_arrows") as draw_window_arrows:
with mock.patch.object(contact_ui, "get_node_row_color", side_effect=[11, 22]):
with mock.patch("contact.ui.contact_ui.interface_state.interface", interface):
contact_ui.refresh_node_selection(old_index=0, highlight=True)
self.assertEqual(
contact_ui.nodes_pad.chgat.call_args_list,
[mock.call(0, 1, 18, 11), mock.call(1, 1, 18, 22)],
)
refresh_pad.assert_called_once_with(2)
draw_window_arrows.assert_called_once_with(2)
+36
View File
@@ -0,0 +1,36 @@
import unittest
from unittest import mock
from contact.ui import nav_utils
from contact.ui.nav_utils import truncate_with_ellipsis, wrap_text
from contact.utilities.singleton import ui_state
class NavUtilsTests(unittest.TestCase):
def setUp(self) -> None:
ui_state.current_window = 0
ui_state.node_list = []
ui_state.start_index = [0, 0, 0]
def test_wrap_text_splits_wide_characters_by_display_width(self) -> None:
self.assertEqual(wrap_text("🔐🔐🔐", 4), ["🔐", "🔐", "🔐"])
def test_truncate_with_ellipsis_respects_display_width(self) -> None:
self.assertEqual(truncate_with_ellipsis("🔐Alpha", 5), "🔐Al…")
def test_highlight_line_uses_full_node_row_width(self) -> None:
ui_state.current_window = 2
ui_state.start_index = [0, 0, 0]
menu_win = mock.Mock()
menu_win.getbegyx.return_value = (0, 0)
menu_win.getmaxyx.return_value = (8, 20)
menu_pad = mock.Mock()
menu_pad.getmaxyx.return_value = (4, 20)
with mock.patch.object(nav_utils, "get_node_color", side_effect=[11, 22]):
nav_utils.highlight_line(menu_win, menu_pad, 0, 1, 5)
self.assertEqual(
menu_pad.chgat.call_args_list,
[mock.call(0, 1, 18, 11), mock.call(1, 1, 18, 22)],
)
+15 -21
View File
@@ -34,17 +34,13 @@ class RxHandlerTests(unittest.TestCase):
}
with mock.patch.object(rx_handler, "refresh_node_list", return_value=True):
with mock.patch.object(rx_handler, "draw_node_list") as draw_node_list:
with mock.patch.object(rx_handler, "draw_messages_window") as draw_messages_window:
with mock.patch.object(rx_handler, "draw_channel_list") as draw_channel_list:
with mock.patch.object(rx_handler, "add_notification") as add_notification:
with mock.patch.object(rx_handler, "save_message_to_db") as save_message_to_db:
with mock.patch.object(rx_handler, "get_name_from_database", return_value="SAT2"):
rx_handler.on_receive(packet, interface=None)
with mock.patch.object(rx_handler, "request_ui_redraw") as request_ui_redraw:
with mock.patch.object(rx_handler, "add_notification") as add_notification:
with mock.patch.object(rx_handler, "save_message_to_db") as save_message_to_db:
with mock.patch.object(rx_handler, "get_name_from_database", return_value="SAT2"):
rx_handler.on_receive(packet, interface=None)
draw_node_list.assert_called_once_with()
draw_messages_window.assert_called_once_with(True)
draw_channel_list.assert_not_called()
self.assertEqual(request_ui_redraw.call_args_list, [mock.call(nodes=True), mock.call(messages=True, scroll_messages_to_bottom=True)])
add_notification.assert_not_called()
save_message_to_db.assert_called_once_with("Primary", 222, "hello")
self.assertEqual(ui_state.all_messages["Primary"][-1][1], "hello")
@@ -66,18 +62,16 @@ class RxHandlerTests(unittest.TestCase):
}
with mock.patch.object(rx_handler, "refresh_node_list", return_value=False):
with mock.patch.object(rx_handler, "draw_messages_window") as draw_messages_window:
with mock.patch.object(rx_handler, "draw_channel_list") as draw_channel_list:
with mock.patch.object(rx_handler, "add_notification") as add_notification:
with mock.patch.object(rx_handler, "update_node_info_in_db") as update_node_info_in_db:
with mock.patch.object(rx_handler, "save_message_to_db") as save_message_to_db:
with mock.patch.object(rx_handler, "get_name_from_database", return_value="SAT2"):
rx_handler.on_receive(packet, interface=None)
with mock.patch.object(rx_handler, "request_ui_redraw") as request_ui_redraw:
with mock.patch.object(rx_handler, "add_notification") as add_notification:
with mock.patch.object(rx_handler, "update_node_info_in_db") as update_node_info_in_db:
with mock.patch.object(rx_handler, "save_message_to_db") as save_message_to_db:
with mock.patch.object(rx_handler, "get_name_from_database", return_value="SAT2"):
rx_handler.on_receive(packet, interface=None)
self.assertIn(222, ui_state.channel_list)
self.assertIn(222, ui_state.all_messages)
draw_messages_window.assert_not_called()
draw_channel_list.assert_called_once_with()
request_ui_redraw.assert_called_once_with(channels=True)
add_notification.assert_called_once_with(1)
update_node_info_in_db.assert_called_once_with(222, chat_archived=False)
save_message_to_db.assert_called_once_with(222, 222, "dm")
@@ -87,10 +81,10 @@ class RxHandlerTests(unittest.TestCase):
ui_state.display_log = True
ui_state.current_window = 4
with mock.patch.object(rx_handler, "draw_packetlog_win") as draw_packetlog_win:
with mock.patch.object(rx_handler, "request_ui_redraw") as request_ui_redraw:
rx_handler.on_receive({"id": "new"}, interface=None)
draw_packetlog_win.assert_called_once_with()
request_ui_redraw.assert_called_once_with(packetlog=True)
self.assertEqual(len(ui_state.packet_buffer), 20)
self.assertEqual(ui_state.packet_buffer[-1], {"id": "new"})
self.assertTrue(menu_state.need_redraw)
+3 -3
View File
@@ -81,11 +81,11 @@ class TxHandlerTests(unittest.TestCase):
with mock.patch.object(tx_handler, "update_ack_nak") as update_ack_nak:
with mock.patch("contact.message_handlers.tx_handler.time.strftime", return_value="[01:02:03] "):
with mock.patch("contact.ui.contact_ui.draw_messages_window") as draw_messages_window:
with mock.patch("contact.ui.contact_ui.request_ui_redraw") as request_ui_redraw:
tx_handler.onAckNak(packet)
update_ack_nak.assert_called_once_with("Primary", 55, "hello", "Ack")
draw_messages_window.assert_called_once_with()
request_ui_redraw.assert_called_once_with(messages=True)
self.assertIn(config.sent_message_prefix, ui_state.all_messages["Primary"][0][0])
self.assertIn(config.ack_str, ui_state.all_messages["Primary"][0][0])
@@ -100,7 +100,7 @@ class TxHandlerTests(unittest.TestCase):
with mock.patch.object(tx_handler, "update_ack_nak") as update_ack_nak:
with mock.patch("contact.message_handlers.tx_handler.time.strftime", return_value="[01:02:03] "):
with mock.patch("contact.ui.contact_ui.draw_messages_window"):
with mock.patch("contact.ui.contact_ui.request_ui_redraw"):
tx_handler.onAckNak(packet)
update_ack_nak.assert_called_once_with("Primary", 55, "hello", "Implicit")