diff --git a/contact/ui/contact_ui.py b/contact/ui/contact_ui.py index ca7fc81..4bdbec2 100644 --- a/contact/ui/contact_ui.py +++ b/contact/ui/contact_ui.py @@ -143,7 +143,7 @@ def refresh_node_selection(old_index: int = -1, highlight: bool = False) -> None if nodes_pad is None or not ui_state.node_list: return - width = max(0, nodes_pad.getmaxyx()[1] - 2) + width = max(0, nodes_pad.getmaxyx()[1] - 4) if 0 <= old_index < len(ui_state.node_list): try: @@ -175,7 +175,7 @@ def refresh_main_window(window_id: int, selected: bool) -> None: elif window_id == 2: paint_frame(nodes_win, selected=selected) if ui_state.node_list and nodes_pad is not None: - width = max(0, nodes_pad.getmaxyx()[1] - 2) + width = max(0, nodes_pad.getmaxyx()[1] - 4) nodes_pad.chgat(ui_state.selected_node, 1, width, get_node_row_color(ui_state.selected_node)) refresh_pad(2) @@ -1089,7 +1089,7 @@ def draw_channel_list() -> None: notification = " " + config.notification_symbol if idx in ui_state.notifications else "" # Truncate the channel name if it's too long to fit in the window - truncated_channel = truncate_with_ellipsis(f"{channel}{notification}", win_width - 3) + truncated_channel = truncate_with_ellipsis(f"{channel}{notification}", win_width - 4) color = get_color("channel_list") if idx == ui_state.selected_channel: @@ -1184,7 +1184,7 @@ def draw_node_list() -> None: node_name = get_node_display_name(node_num, node) # Future node name custom formatting possible - node_str = pad_to_width(f"{status_icon} {node_name}", box_width - 2) + node_str = truncate_with_ellipsis(f"{status_icon} {node_name}", box_width - 4) nodes_pad.addstr(i, 1, node_str, get_node_row_color(i)) paint_frame(nodes_win, selected=(ui_state.current_window == 2)) diff --git a/contact/ui/nav_utils.py b/contact/ui/nav_utils.py index 7ffb1d7..f8ff9d8 100644 --- a/contact/ui/nav_utils.py +++ b/contact/ui/nav_utils.py @@ -457,8 +457,8 @@ def highlight_line( menu_pad.chgat(new_idx, 1, menu_pad.getmaxyx()[1] - 4, color_new) elif ui_state.current_window == 2: - menu_pad.chgat(old_idx, 1, menu_pad.getmaxyx()[1] - 2, get_node_color(old_idx)) - menu_pad.chgat(new_idx, 1, menu_pad.getmaxyx()[1] - 2, get_node_color(new_idx, reverse=True)) + menu_pad.chgat(old_idx, 1, menu_pad.getmaxyx()[1] - 4, get_node_color(old_idx)) + menu_pad.chgat(new_idx, 1, menu_pad.getmaxyx()[1] - 4, get_node_color(new_idx, reverse=True)) menu_win.refresh() diff --git a/tests/test_contact_ui.py b/tests/test_contact_ui.py index 2003cb4..63ae4e7 100644 --- a/tests/test_contact_ui.py +++ b/tests/test_contact_ui.py @@ -3,6 +3,7 @@ from unittest import mock import contact.ui.default_config as config from contact.ui import contact_ui +from contact.ui.nav_utils import text_width from contact.utilities.singleton import ui_state from tests.test_support import reset_singletons, restore_config, snapshot_config @@ -69,7 +70,7 @@ class ContactUiTests(unittest.TestCase): self.assertFalse(ui_state.redraw_channels) self.assertFalse(ui_state.redraw_messages) - def test_refresh_node_selection_highlights_full_row_width(self) -> None: + def test_refresh_node_selection_reserves_scroll_arrow_column(self) -> None: ui_state.node_list = [101, 202] ui_state.selected_node = 1 ui_state.start_index = [0, 0, 0] @@ -89,11 +90,52 @@ class ContactUiTests(unittest.TestCase): self.assertEqual( contact_ui.nodes_pad.chgat.call_args_list, - [mock.call(0, 1, 18, 11), mock.call(1, 1, 18, 22)], + [mock.call(0, 1, 16, 11), mock.call(1, 1, 16, 22)], ) refresh_pad.assert_called_once_with(2) draw_window_arrows.assert_called_once_with(2) + def test_draw_channel_list_reserves_scroll_arrow_column(self) -> None: + ui_state.channel_list = ["VeryLongChannelName"] + ui_state.notifications = [] + ui_state.selected_channel = 0 + ui_state.current_window = 0 + contact_ui.channel_pad = mock.Mock() + contact_ui.channel_win = mock.Mock() + contact_ui.channel_win.getmaxyx.return_value = (10, 20) + + with mock.patch.object(contact_ui, "get_color", return_value=1): + with mock.patch.object(contact_ui, "paint_frame"): + with mock.patch.object(contact_ui, "refresh_pad"): + with mock.patch.object(contact_ui, "draw_window_arrows"): + with mock.patch.object(contact_ui, "remove_notification"): + contact_ui.draw_channel_list() + + text = contact_ui.channel_pad.addstr.call_args.args[2] + self.assertEqual(len(text), 16) + + def test_draw_node_list_reserves_scroll_arrow_column(self) -> None: + ui_state.node_list = [101] + ui_state.current_window = 2 + contact_ui.nodes_pad = mock.Mock() + contact_ui.nodes_win = mock.Mock() + contact_ui.nodes_win.getmaxyx.return_value = (10, 20) + contact_ui.entry_win = mock.Mock() + interface = mock.Mock() + interface.nodesByNum = {101: {"user": {"longName": "VeryLongNodeName", "publicKey": ""}}} + + with mock.patch("contact.ui.contact_ui.interface_state.interface", interface): + with mock.patch.object(contact_ui, "get_node_row_color", return_value=1): + with mock.patch.object(contact_ui.curses, "curs_set"): + with mock.patch.object(contact_ui, "paint_frame"): + with mock.patch.object(contact_ui, "refresh_pad"): + with mock.patch.object(contact_ui, "draw_window_arrows"): + contact_ui.draw_node_list() + + text = contact_ui.nodes_pad.addstr.call_args.args[2] + self.assertEqual(text_width(text), 16) + self.assertIn("…", text) + def test_handle_resize_single_pane_keeps_full_width_windows(self) -> None: stdscr = mock.Mock() stdscr.getmaxyx.return_value = (24, 80) diff --git a/tests/test_nav_utils.py b/tests/test_nav_utils.py index 5085a0d..c47234b 100644 --- a/tests/test_nav_utils.py +++ b/tests/test_nav_utils.py @@ -18,7 +18,7 @@ class NavUtilsTests(unittest.TestCase): 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: + def test_highlight_line_reserves_scroll_arrow_column_for_nodes(self) -> None: ui_state.current_window = 2 ui_state.start_index = [0, 0, 0] menu_win = mock.Mock() @@ -32,5 +32,5 @@ class NavUtilsTests(unittest.TestCase): self.assertEqual( menu_pad.chgat.call_args_list, - [mock.call(0, 1, 18, 11), mock.call(1, 1, 18, 22)], + [mock.call(0, 1, 16, 11), mock.call(1, 1, 16, 22)], )