From bb6dbc71869f3a53a1152e31986b98b99d18b0ed Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Mon, 7 Jul 2025 22:58:14 -0400 Subject: [PATCH] Fixed character navigation in table mode. --- .../commands/commands/review_curr_char.py | 15 ++++++ .../commands/commands/review_next_char.py | 49 +++++++++++++++++++ .../commands/commands/review_prev_char.py | 47 ++++++++++++++++++ src/fenrirscreenreader/core/tableManager.py | 21 ++++++++ 4 files changed, 132 insertions(+) diff --git a/src/fenrirscreenreader/commands/commands/review_curr_char.py b/src/fenrirscreenreader/commands/commands/review_curr_char.py index 637b6982..073dc952 100644 --- a/src/fenrirscreenreader/commands/commands/review_curr_char.py +++ b/src/fenrirscreenreader/commands/commands/review_curr_char.py @@ -26,6 +26,21 @@ class command: "CursorManager" ].enter_review_mode_curr_text_cursor() + # In table mode, sync to cell start if cursor is outside current cell + if self.env["runtime"]["TableManager"].is_table_mode(): + table_info = self.env["runtime"]["TableManager"].get_current_table_cell_info() + if table_info: + cursor_pos = self.env["screen"]["newCursorReview"] + line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_pos["y"]) + if line_text: + column_start = self.env["runtime"]["TableManager"].get_column_start_position(line_text, table_info["column_index"]) + cell_content = table_info["cell_content"] + cell_end = column_start + len(cell_content) + + # If cursor is outside the current cell, move to cell start + if cursor_pos["x"] < column_start or cursor_pos["x"] >= cell_end: + self.env["screen"]["newCursorReview"]["x"] = column_start + ( self.env["screen"]["newCursorReview"]["x"], self.env["screen"]["newCursorReview"]["y"], diff --git a/src/fenrirscreenreader/commands/commands/review_next_char.py b/src/fenrirscreenreader/commands/commands/review_next_char.py index b576f65f..6ae71a5f 100644 --- a/src/fenrirscreenreader/commands/commands/review_next_char.py +++ b/src/fenrirscreenreader/commands/commands/review_next_char.py @@ -25,6 +25,55 @@ class command: self.env["runtime"][ "CursorManager" ].enter_review_mode_curr_text_cursor() + + # Check if we're in table mode for bounded navigation + if self.env["runtime"]["TableManager"].is_table_mode(): + table_info = self.env["runtime"]["TableManager"].get_current_table_cell_info() + if table_info: + cursor_pos = self.env["screen"]["newCursorReview"] + line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_pos["y"]) + if line_text: + column_start = self.env["runtime"]["TableManager"].get_column_start_position(line_text, table_info["column_index"]) + cell_content = table_info["cell_content"] + cell_end = column_start + len(cell_content) + + # Check if we're already at the end of the cell + if cursor_pos["x"] >= cell_end - 1: + # At cell boundary - announce end and don't move + char_utils.present_char_for_review( + self.env, + cell_content[-1] if cell_content else "", + interrupt=True, + announce_capital=True, + flush=False, + ) + self.env["runtime"]["OutputManager"].present_text( + _("end of cell"), interrupt=False, sound_icon="EndOfLine" + ) + return + + # Move within cell bounds + relative_pos = cursor_pos["x"] - column_start + if relative_pos < len(cell_content) - 1: + new_relative_pos = relative_pos + 1 + self.env["screen"]["newCursorReview"]["x"] = column_start + new_relative_pos + + # Get character at new position + if new_relative_pos < len(cell_content): + next_char = cell_content[new_relative_pos] + else: + next_char = "" + + char_utils.present_char_for_review( + self.env, + next_char, + interrupt=True, + announce_capital=True, + flush=False, + ) + return + + # Regular navigation for non-table mode ( self.env["screen"]["newCursorReview"]["x"], self.env["screen"]["newCursorReview"]["y"], diff --git a/src/fenrirscreenreader/commands/commands/review_prev_char.py b/src/fenrirscreenreader/commands/commands/review_prev_char.py index 4c8c159e..88c3c246 100644 --- a/src/fenrirscreenreader/commands/commands/review_prev_char.py +++ b/src/fenrirscreenreader/commands/commands/review_prev_char.py @@ -30,6 +30,53 @@ class command: "new_cursor" ].copy() + # Check if we're in table mode for bounded navigation + if self.env["runtime"]["TableManager"].is_table_mode(): + table_info = self.env["runtime"]["TableManager"].get_current_table_cell_info() + if table_info: + cursor_pos = self.env["screen"]["newCursorReview"] + line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_pos["y"]) + if line_text: + column_start = self.env["runtime"]["TableManager"].get_column_start_position(line_text, table_info["column_index"]) + + # Check if we're already at the start of the cell + if cursor_pos["x"] <= column_start: + # At cell boundary - announce start and don't move + char_utils.present_char_for_review( + self.env, + table_info["cell_content"][0] if table_info["cell_content"] else "", + interrupt=True, + announce_capital=True, + flush=False, + ) + self.env["runtime"]["OutputManager"].present_text( + _("start of cell"), interrupt=False, sound_icon="StartOfLine" + ) + return + + # Move within cell bounds + cell_content = table_info["cell_content"] + relative_pos = cursor_pos["x"] - column_start + if relative_pos > 0: + new_relative_pos = relative_pos - 1 + self.env["screen"]["newCursorReview"]["x"] = column_start + new_relative_pos + + # Get character at new position + if new_relative_pos < len(cell_content): + prev_char = cell_content[new_relative_pos] + else: + prev_char = "" + + char_utils.present_char_for_review( + self.env, + prev_char, + interrupt=True, + announce_capital=True, + flush=False, + ) + return + + # Regular navigation for non-table mode ( self.env["screen"]["newCursorReview"]["x"], self.env["screen"]["newCursorReview"]["y"], diff --git a/src/fenrirscreenreader/core/tableManager.py b/src/fenrirscreenreader/core/tableManager.py index 7d0434fe..58063c64 100644 --- a/src/fenrirscreenreader/core/tableManager.py +++ b/src/fenrirscreenreader/core/tableManager.py @@ -361,6 +361,27 @@ class TableManager: column_pos = line_text.find(target_text, search_start) return column_pos if column_pos != -1 else search_start + def is_cursor_within_current_cell(self, cursor_x, cursor_y): + """Check if the given cursor position is within the current table cell""" + if not self.is_table_mode(): + return False + + line_text = self.env["runtime"]["ScreenManager"].get_line_text(cursor_y) + if not line_text: + return False + + columns = self.parse_line_into_columns(line_text) + if not columns or self.currentColumn < 0 or self.currentColumn >= len(columns): + return False + + # Get the bounds of the current column + column_start = self.get_column_start_position(line_text, self.currentColumn) + column_text = columns[self.currentColumn] + column_end = column_start + len(column_text) + + # Check if cursor is within the column bounds + return column_start <= cursor_x < column_end + def reset_table_mode(self): self.set_head_line()