From 83cb330d34fc8e16baedfecdbc92864f75d1ce10 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Tue, 10 Jun 2025 18:10:08 -0400 Subject: [PATCH] Potential fixes to progress bar. Better handling of punctuation while reading. --- .../commands/commands/progress_bar_monitor.py | 24 +++++++++---------- .../commands/commands/review_curr_char.py | 2 +- .../commands/review_line_first_char.py | 2 +- .../commands/review_line_last_char.py | 2 +- .../commands/commands/review_next_char.py | 2 +- .../commands/commands/review_prev_char.py | 2 +- .../commands/review_screen_first_char.py | 2 +- .../commands/review_screen_last_char.py | 2 +- ...resent_char_if_cursor_change_horizontal.py | 9 ++++++- .../onScreenUpdate/65000-progress_detector.py | 11 ++++++++- src/fenrirscreenreader/fenrirVersion.py | 2 +- src/fenrirscreenreader/utils/char_utils.py | 11 +++++++++ 12 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/fenrirscreenreader/commands/commands/progress_bar_monitor.py b/src/fenrirscreenreader/commands/commands/progress_bar_monitor.py index 43b828d6..28808ec4 100644 --- a/src/fenrirscreenreader/commands/commands/progress_bar_monitor.py +++ b/src/fenrirscreenreader/commands/commands/progress_bar_monitor.py @@ -57,7 +57,7 @@ class command(): # Don't control speech - progress monitor is beep-only def detectProgress(self, text): - if not self.env['runtime']['progressMonitoring']: + if not self.env['commandBuffer']['progressMonitoring']: return # Skip progress detection if current screen looks like a prompt @@ -70,10 +70,10 @@ class command(): percentMatch = re.search(r'(\d+(?:\.\d+)?)\s*%', text) if percentMatch: percentage = float(percentMatch.group(1)) - if percentage != self.env['runtime']['lastProgressValue']: + if percentage != self.env['commandBuffer']['lastProgressValue']: self.playProgressTone(percentage) - self.env['runtime']['lastProgressValue'] = percentage - self.env['runtime']['lastProgressTime'] = currentTime + self.env['commandBuffer']['lastProgressValue'] = percentage + self.env['commandBuffer']['lastProgressTime'] = currentTime return # Pattern 2: Fraction (15/100, 3 of 10, etc.) @@ -83,10 +83,10 @@ class command(): total = int(fractionMatch.group(2)) if total > 0: percentage = (current / total) * 100 - if percentage != self.env['runtime']['lastProgressValue']: + if percentage != self.env['commandBuffer']['lastProgressValue']: self.playProgressTone(percentage) - self.env['runtime']['lastProgressValue'] = percentage - self.env['runtime']['lastProgressTime'] = currentTime + self.env['commandBuffer']['lastProgressValue'] = percentage + self.env['commandBuffer']['lastProgressTime'] = currentTime return # Pattern 3: Progress bars ([#### ], [====> ], etc.) @@ -99,19 +99,19 @@ class command(): # Require at least 2 progress chars total and unfilled portion must be spaces/dots if total >= 2 and (not barMatch.group(2) or re.match(r'^[\s\.]*$', barMatch.group(2))): percentage = (filled / total) * 100 - if percentage != self.env['runtime']['lastProgressValue']: + if percentage != self.env['commandBuffer']['lastProgressValue']: self.playProgressTone(percentage) - self.env['runtime']['lastProgressValue'] = percentage - self.env['runtime']['lastProgressTime'] = currentTime + self.env['commandBuffer']['lastProgressValue'] = percentage + self.env['commandBuffer']['lastProgressTime'] = currentTime return # Pattern 4: Generic activity indicators (Loading..., Working..., etc.) activityPattern = re.search(r'(loading|processing|working|installing|downloading|compiling|building).*\.{2,}', text, re.IGNORECASE) if activityPattern: # Play a steady beep every 2 seconds for ongoing activity - if currentTime - self.env['runtime']['lastProgressTime'] >= 2.0: + if currentTime - self.env['commandBuffer']['lastProgressTime'] >= 2.0: self.playActivityBeep() - self.env['runtime']['lastProgressTime'] = currentTime + self.env['commandBuffer']['lastProgressTime'] = currentTime def playProgressTone(self, percentage): # Map 0-100% to 400-1200Hz frequency range diff --git a/src/fenrirscreenreader/commands/commands/review_curr_char.py b/src/fenrirscreenreader/commands/commands/review_curr_char.py index a8c5e404..6ce99926 100644 --- a/src/fenrirscreenreader/commands/commands/review_curr_char.py +++ b/src/fenrirscreenreader/commands/commands/review_curr_char.py @@ -22,7 +22,7 @@ class command(): self.env['screen']['newCursorReview']['x'], self.env['screen']['newCursorReview']['y'], currChar = \ char_utils.getCurrentChar(self.env['screen']['newCursorReview']['x'], self.env['screen']['newCursorReview']['y'], self.env['screen']['newContentText']) - self.env['runtime']['outputManager'].presentText(currChar ,interrupt=True, ignorePunctuation=True, announceCapital=True, flush=False) + char_utils.presentCharForReview(self.env, currChar, interrupt=True, announceCapital=True, flush=False) # is has attribute it enabled? if self.env['runtime']['settingsManager'].getSettingAsBool('general', 'hasAttributes'): cursorPos = self.env['screen']['newCursorReview'] diff --git a/src/fenrirscreenreader/commands/commands/review_line_first_char.py b/src/fenrirscreenreader/commands/commands/review_line_first_char.py index 35cc4ae9..84de0a72 100644 --- a/src/fenrirscreenreader/commands/commands/review_line_first_char.py +++ b/src/fenrirscreenreader/commands/commands/review_line_first_char.py @@ -28,7 +28,7 @@ class command(): self.env['screen']['newCursorReview']['x'], self.env['screen']['newCursorReview']['y'], currChar = \ char_utils.getCurrentChar(self.env['screen']['newCursorReview']['x'], self.env['screen']['newCursorReview']['y'], self.env['screen']['newContentText']) - self.env['runtime']['outputManager'].presentText(currChar ,interrupt=True, ignorePunctuation=True, announceCapital=True, flush=False) + char_utils.presentCharForReview(self.env, currChar, interrupt=True, announceCapital=True, flush=False) self.env['runtime']['outputManager'].presentText(_("first character in line indent {0}").format(str(len(currLine) - len(currLine.lstrip()))), interrupt=False) def setCallback(self, callback): diff --git a/src/fenrirscreenreader/commands/commands/review_line_last_char.py b/src/fenrirscreenreader/commands/commands/review_line_last_char.py index 4586e35f..82bcf101 100644 --- a/src/fenrirscreenreader/commands/commands/review_line_last_char.py +++ b/src/fenrirscreenreader/commands/commands/review_line_last_char.py @@ -22,7 +22,7 @@ class command(): self.env['screen']['newCursorReview']['x'], self.env['screen']['newCursorReview']['y'], lastChar = \ char_utils.getLastCharInLine(self.env['screen']['newCursorReview']['y'], self.env['screen']['newContentText']) - self.env['runtime']['outputManager'].presentText(lastChar ,interrupt=True, ignorePunctuation=True, announceCapital=True, flush=False) + char_utils.presentCharForReview(self.env, lastChar, interrupt=True, announceCapital=True, flush=False) self.env['runtime']['outputManager'].presentText(_("last character in line"), interrupt=False) def setCallback(self, callback): diff --git a/src/fenrirscreenreader/commands/commands/review_next_char.py b/src/fenrirscreenreader/commands/commands/review_next_char.py index 02a91cab..496e2f83 100644 --- a/src/fenrirscreenreader/commands/commands/review_next_char.py +++ b/src/fenrirscreenreader/commands/commands/review_next_char.py @@ -21,7 +21,7 @@ class command(): self.env['screen']['newCursorReview']['x'], self.env['screen']['newCursorReview']['y'], nextChar, endOfScreen, lineBreak = \ char_utils.getNextChar(self.env['screen']['newCursorReview']['x'], self.env['screen']['newCursorReview']['y'], self.env['screen']['newContentText']) - self.env['runtime']['outputManager'].presentText(nextChar, interrupt=True, ignorePunctuation=True, announceCapital=True, flush=False) + char_utils.presentCharForReview(self.env, nextChar, interrupt=True, announceCapital=True, flush=False) if endOfScreen: if self.env['runtime']['settingsManager'].getSettingAsBool('review', 'endOfScreen'): self.env['runtime']['outputManager'].presentText(_('end of screen'), interrupt=True, soundIcon='EndOfScreen') diff --git a/src/fenrirscreenreader/commands/commands/review_prev_char.py b/src/fenrirscreenreader/commands/commands/review_prev_char.py index bdfe7cff..5c4587b8 100644 --- a/src/fenrirscreenreader/commands/commands/review_prev_char.py +++ b/src/fenrirscreenreader/commands/commands/review_prev_char.py @@ -24,7 +24,7 @@ class command(): self.env['screen']['newCursorReview']['x'], self.env['screen']['newCursorReview']['y'], prevChar, endOfScreen, lineBreak = \ char_utils.getPrevChar(self.env['screen']['newCursorReview']['x'], self.env['screen']['newCursorReview']['y'], self.env['screen']['newContentText']) - self.env['runtime']['outputManager'].presentText(prevChar, interrupt=True, ignorePunctuation=True, announceCapital=True, flush=False) + char_utils.presentCharForReview(self.env, prevChar, interrupt=True, announceCapital=True, flush=False) if endOfScreen: if self.env['runtime']['settingsManager'].getSettingAsBool('review', 'endOfScreen'): self.env['runtime']['outputManager'].presentText(_('end of screen'), interrupt=True, soundIcon='EndOfScreen') diff --git a/src/fenrirscreenreader/commands/commands/review_screen_first_char.py b/src/fenrirscreenreader/commands/commands/review_screen_first_char.py index 80247705..4bbc4554 100644 --- a/src/fenrirscreenreader/commands/commands/review_screen_first_char.py +++ b/src/fenrirscreenreader/commands/commands/review_screen_first_char.py @@ -22,7 +22,7 @@ class command(): self.env['screen']['newCursorReview']['x'], self.env['screen']['newCursorReview']['y'], lastChar = \ char_utils.getLastCharInLine(self.env['screen']['newCursorReview']['y'], self.env['screen']['newContentText']) - self.env['runtime']['outputManager'].presentText(lastChar ,interrupt=True, ignorePunctuation=True, announceCapital=True, flush=False) + char_utils.presentCharForReview(self.env, lastChar, interrupt=True, announceCapital=True, flush=False) self.env['runtime']['outputManager'].presentText(_("first character in screen"), interrupt=False) def setCallback(self, callback): diff --git a/src/fenrirscreenreader/commands/commands/review_screen_last_char.py b/src/fenrirscreenreader/commands/commands/review_screen_last_char.py index 7d8f870e..afec3477 100644 --- a/src/fenrirscreenreader/commands/commands/review_screen_last_char.py +++ b/src/fenrirscreenreader/commands/commands/review_screen_last_char.py @@ -22,7 +22,7 @@ class command(): self.env['screen']['newCursorReview']['x'], self.env['screen']['newCursorReview']['y'], lastChar = \ char_utils.getLastCharInLine(self.env['screen']['newCursorReview']['y'], self.env['screen']['newContentText']) - self.env['runtime']['outputManager'].presentText(lastChar ,interrupt=True, ignorePunctuation=True, announceCapital=True, flush=False) + char_utils.presentCharForReview(self.env, lastChar, interrupt=True, announceCapital=True, flush=False) self.env['runtime']['outputManager'].presentText(_("last character in screen"), interrupt=False) def setCallback(self, callback): diff --git a/src/fenrirscreenreader/commands/onCursorChange/50000-present_char_if_cursor_change_horizontal.py b/src/fenrirscreenreader/commands/onCursorChange/50000-present_char_if_cursor_change_horizontal.py index 2a2f23a9..a6609ea5 100644 --- a/src/fenrirscreenreader/commands/onCursorChange/50000-present_char_if_cursor_change_horizontal.py +++ b/src/fenrirscreenreader/commands/onCursorChange/50000-present_char_if_cursor_change_horizontal.py @@ -5,6 +5,7 @@ # By Chrys, Storm Dragon, and contributers. from fenrirscreenreader.utils import char_utils +from fenrirscreenreader.utils import word_utils class command(): def __init__(self): @@ -43,7 +44,13 @@ class command(): if self.env['screen']['newCursor']['x'] == x: return x, y, currChar = char_utils.getCurrentChar(self.env['screen']['newCursor']['x'], self.env['screen']['newCursor']['y'], self.env['screen']['newContentText']) - if not currChar.isspace(): + if currChar.isspace(): + # Only announce spaces during pure navigation (arrow keys) + # Check if this is really navigation by looking at input history + if (self.env['runtime']['inputManager'].getShortcutType() in ['KEY'] and + self.env['runtime']['inputManager'].getLastDeepestInput()[0] in ['KEY_LEFT', 'KEY_RIGHT', 'KEY_UP', 'KEY_DOWN']): + char_utils.presentCharForReview(self.env, currChar, interrupt=True, announceCapital=True, flush=False) + else: self.env['runtime']['outputManager'].presentText(currChar, interrupt=True, ignorePunctuation=True, announceCapital=True, flush=False) def setCallback(self, callback): pass diff --git a/src/fenrirscreenreader/commands/onScreenUpdate/65000-progress_detector.py b/src/fenrirscreenreader/commands/onScreenUpdate/65000-progress_detector.py index 108ca002..c4bb7481 100644 --- a/src/fenrirscreenreader/commands/onScreenUpdate/65000-progress_detector.py +++ b/src/fenrirscreenreader/commands/onScreenUpdate/65000-progress_detector.py @@ -23,8 +23,11 @@ class command(): # Only run if progress monitoring is enabled try: if 'progressMonitoring' in self.env['commandBuffer'] and self.env['commandBuffer']['progressMonitoring']: + # Check if current line is a prompt - if so, reset progress state + if self.isCurrentLinePrompt(): + self.resetProgressState() # Only check new incoming text (newDelta), but filter out screen changes - if self.env['screen']['newDelta'] and self.isRealProgressUpdate(): + elif self.env['screen']['newDelta'] and self.isRealProgressUpdate(): self.detectProgress(self.env['screen']['newDelta']) except Exception as e: # Silently ignore errors to avoid disrupting normal operation @@ -54,6 +57,12 @@ class command(): return False return True + + def resetProgressState(self): + """Reset progress state when a prompt is detected, allowing new progress operations to start fresh""" + self.env['runtime']['debug'].writeDebugOut("Resetting progress state due to prompt detection", debug.debugLevel.INFO) + self.env['commandBuffer']['lastProgressValue'] = -1 + self.env['commandBuffer']['lastProgressTime'] = 0 def detectProgress(self, text): import re diff --git a/src/fenrirscreenreader/fenrirVersion.py b/src/fenrirscreenreader/fenrirVersion.py index d60f9cce..493a8616 100644 --- a/src/fenrirscreenreader/fenrirVersion.py +++ b/src/fenrirscreenreader/fenrirVersion.py @@ -4,5 +4,5 @@ # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributers. -version = "2025.06.09" +version = "2025.06.10" codeName = "testing" diff --git a/src/fenrirscreenreader/utils/char_utils.py b/src/fenrirscreenreader/utils/char_utils.py index 1ae9a8e0..425bad83 100644 --- a/src/fenrirscreenreader/utils/char_utils.py +++ b/src/fenrirscreenreader/utils/char_utils.py @@ -119,4 +119,15 @@ def getPhonetic(currChar): return phonChar except: return currChar + +def presentCharForReview(env, char, interrupt=True, announceCapital=True, flush=False): + """Present a character for explicit review commands only""" + if char == ' ': + if ' ' in env['punctuation']['PUNCTDICT']: + announceChar = env['punctuation']['PUNCTDICT'][' '] + else: + announceChar = 'space' + env['runtime']['outputManager'].presentText(announceChar, interrupt=interrupt, flush=flush) + else: + env['runtime']['outputManager'].presentText(char, interrupt=interrupt, ignorePunctuation=True, announceCapital=announceCapital, flush=flush)