diff --git a/message_handlers/rx_handler.py b/message_handlers/rx_handler.py index 4e7c1e6..6eabe17 100644 --- a/message_handlers/rx_handler.py +++ b/message_handlers/rx_handler.py @@ -70,7 +70,7 @@ def on_receive(packet, interface): if(refresh_channels): draw_channel_list() if(refresh_messages): - draw_messages_window() + draw_messages_window(True) save_message_to_db(globals.channel_list[channel_number], message_from_id, message_string) diff --git a/message_handlers/tx_handler.py b/message_handlers/tx_handler.py index 4b46b4c..5b697dd 100644 --- a/message_handlers/tx_handler.py +++ b/message_handlers/tx_handler.py @@ -109,7 +109,7 @@ def on_response_traceroute(packet): if(refresh_channels): draw_channel_list() if(refresh_messages): - draw_messages_window() + draw_messages_window(True) save_message_to_db(globals.channel_list[channel_number], packet['from'], msg_str) def send_message(message, destination=BROADCAST_NUM, channel=0): diff --git a/ui/curses_ui.py b/ui/curses_ui.py index 1645fa7..cce46dd 100644 --- a/ui/curses_ui.py +++ b/ui/curses_ui.py @@ -80,70 +80,46 @@ def draw_channel_list(): channel_win.box() channel_win.refresh() - -def draw_messages_window(): +def draw_messages_window(scroll_to_bottom = False): """Update the messages window based on the selected channel and scroll position.""" - messages_win.clear() + messages_pad.clear() channel = globals.channel_list[globals.selected_channel] if channel in globals.all_messages: messages = globals.all_messages[channel] - num_messages = len(messages) - max_messages = messages_win.getmaxyx()[0] - 2 # Max messages that fit in the window - # Adjust for packetlog height if log is visible - if globals.display_log: - packetlog_height = packetlog_win.getmaxyx()[0] - max_messages -= packetlog_height - 1 - if max_messages < 1: - max_messages = 1 + msg_line_count = 0 - # Set the initial scroll position to the bottom of the list - max_scroll_position = max(0, num_messages - max_messages) - - # if globals.selected_message is None or globals.selected_message == 0 or globals.selected_message >= num_messages: - # globals.selected_message = num_messages - 1 # Default to the last message - - start_index = max(0, min(globals.selected_message, max_scroll_position)) - - # Dynamically calculate max_messages based on visible messages and wraps - row = 1 - visible_message_count = 0 - for index, (prefix, message) in enumerate(messages[start_index:], start=start_index): + row = 0 + for (prefix, message) in messages: full_message = f"{prefix}{message}" - wrapped_lines = textwrap.wrap(full_message, messages_win.getmaxyx()[1] - 2) - - if row + len(wrapped_lines) - 1 > messages_win.getmaxyx()[0] - 2: # Check if it fits in the window - break - - visible_message_count += 1 - row += len(wrapped_lines) - - # Adjust max_messages to match visible messages - max_messages = visible_message_count - - # Re-render the visible messages - row = 1 - for index, (prefix, message) in enumerate(messages[start_index:start_index + max_messages], start=start_index): - full_message = f"{prefix}{message}" - wrapped_lines = textwrap.wrap(full_message, messages_win.getmaxyx()[1] - 2) + wrapped_lines = textwrap.wrap(full_message, messages_box.getmaxyx()[1] - 2) + msg_line_count += len(wrapped_lines) + messages_pad.resize(msg_line_count, messages_box.getmaxyx()[1]) for line in wrapped_lines: - # Highlight the row if it's the selected message - if index == globals.selected_message and globals.current_window == 1: - color = curses.A_REVERSE # Highlighted row color - else: - color = curses.color_pair(4) if prefix.startswith(globals.sent_message_prefix) else curses.color_pair(3) - messages_win.addstr(row, 1, line, color) + color = curses.color_pair(1) if prefix.startswith(globals.sent_message_prefix) else curses.color_pair(2) + messages_pad.addstr(row, 1, line, color) row += 1 - messages_win.box() - messages_win.refresh() + messages_box.box() + messages_box.refresh() + + if(scroll_to_bottom): + globals.selected_message = max(msg_line_count - get_msg_window_lines(), 0) + else: + globals.selected_message = max(min(globals.selected_message, msg_line_count - get_msg_window_lines()), 0) + + # Adjust for packetlog height if log is visible + packetlog_height = packetlog_win.getmaxyx()[0] if globals.display_log else 0 + messages_pad.refresh(globals.selected_message, 0, + messages_box.getbegyx()[0] + 1, messages_box.getbegyx()[1] + 1, + messages_box.getbegyx()[0] + get_msg_window_lines(), messages_box.getbegyx()[1] + messages_box.getmaxyx()[1] - 2) + draw_packetlog_win() def draw_node_list(): - nodes_win.clear() win_height = nodes_win.getmaxyx()[0] start_index = max(0, globals.selected_node - (win_height - 3)) # Calculate starting index based on selected node and window height @@ -158,7 +134,6 @@ def draw_node_list(): nodes_win.box() nodes_win.refresh() - def select_channels(direction): channel_list_length = len(globals.channel_list) globals.selected_channel += direction @@ -168,22 +143,18 @@ def select_channels(direction): elif globals.selected_channel >= channel_list_length: globals.selected_channel = 0 - globals.selected_message = len(globals.all_messages[globals.channel_list[globals.selected_channel]]) - 1 - draw_channel_list() - draw_messages_window() + draw_messages_window(True) def select_messages(direction): - messages_length = len(globals.all_messages[globals.channel_list[globals.selected_channel]]) - globals.selected_message += direction - if globals.selected_message < 0: - globals.selected_message = messages_length - 1 - elif globals.selected_message >= messages_length: - globals.selected_message = 0 + msg_line_count = messages_pad.getmaxyx()[0] + globals.selected_message = max(0, min(globals.selected_message, msg_line_count - get_msg_window_lines())) - draw_messages_window() + messages_pad.refresh(globals.selected_message, 0, + messages_box.getbegyx()[0] + 1, messages_box.getbegyx()[1] + 1, + messages_box.getbegyx()[0] + get_msg_window_lines(), messages_box.getbegyx()[1] + messages_box.getmaxyx()[1] - 2) def select_nodes(direction): node_list_length = len(globals.node_list) @@ -242,7 +213,7 @@ def draw_packetlog_win(): def main_ui(stdscr): - global messages_win, nodes_win, channel_win, function_win, packetlog_win + global messages_pad, messages_box, nodes_win, channel_win, function_win, packetlog_win stdscr.keypad(True) get_channels() @@ -256,41 +227,39 @@ def main_ui(stdscr): messages_width = width - channel_width - nodes_width channel_win = curses.newwin(height - 6, channel_width, 3, 0) - messages_win = curses.newwin(height - 6, messages_width, 3, channel_width) + messages_box = curses.newwin(height - 6, messages_width, 3, channel_width) + + # Will be resized to what we need when drawn + messages_pad = curses.newpad(1, 1) packetlog_win = curses.newwin(int(height / 3), messages_width, height - int(height / 3) - 3, channel_width) nodes_win = curses.newwin(height - 6, nodes_width, 3, channel_width + messages_width) function_win = curses.newwin(3, width, height - 3, 0) draw_centered_text_field(function_win, f"↑→↓← = Select ENTER = Send ` = Settings ^P = Packet Log ESC = Quit") - # Enable scrolling for messages and nodes windows - messages_win.scrollok(True) - nodes_win.scrollok(True) - channel_win.scrollok(True) - - draw_channel_list() - draw_node_list() - draw_messages_window() - # Draw boxes around windows channel_win.box() entry_win.box() - messages_win.box() nodes_win.box() + messages_box.box() function_win.box() # Refresh all windows entry_win.refresh() - messages_win.refresh() + nodes_win.refresh() channel_win.refresh() function_win.refresh() - + messages_box.refresh() input_text = "" entry_win.keypad(True) curses.curs_set(1) + draw_channel_list() + draw_node_list() + draw_messages_window(True) + while True: draw_text_field(entry_win, f"Input: {input_text[-(width - 10):]}") @@ -302,7 +271,6 @@ def main_ui(stdscr): if char == curses.KEY_UP: if globals.current_window == 0: select_channels(-1) - globals.selected_message = len(globals.all_messages[globals.channel_list[globals.selected_channel]]) - 1 elif globals.current_window == 1: select_messages(-1) elif globals.current_window == 2: @@ -311,7 +279,6 @@ def main_ui(stdscr): elif char == curses.KEY_DOWN: if globals.current_window == 0: select_channels(1) - globals.selected_message = len(globals.all_messages[globals.channel_list[globals.selected_channel]]) - 1 elif globals.current_window == 1: select_messages(1) elif globals.current_window == 2: @@ -354,12 +321,12 @@ def main_ui(stdscr): draw_node_list() draw_channel_list() - draw_messages_window() + draw_messages_window(True) else: # Enter key pressed, send user input as message send_message(input_text, channel=globals.selected_channel) - draw_messages_window() + draw_messages_window(True) # Clear entry window and reset input text input_text = "" @@ -384,14 +351,18 @@ def main_ui(stdscr): # Display packet log if globals.display_log is False: globals.display_log = True - draw_messages_window() + draw_messages_window(True) else: globals.display_log = False packetlog_win.clear() - draw_messages_window() + draw_messages_window(True) else: # Append typed character to input text if(isinstance(char, str)): input_text += char else: input_text += chr(char) + +def get_msg_window_lines(): + packetlog_height = packetlog_win.getmaxyx()[0] if globals.display_log else 0 + return messages_box.getmaxyx()[0] - 2 - packetlog_height