diff --git a/input.py b/input.py index b0960cc..d3a7320 100644 --- a/input.py +++ b/input.py @@ -10,11 +10,18 @@ Provides functionality for: import pygame import time -import wx from .speech import speak def get_input(prompt="Enter text:", text=""): - """Display a dialog box for text input. + """Display an accessible text input dialog using pygame. + + Features: + - Speaks each character as typed + - Left/Right arrows navigate and speak characters + - Up/Down arrows read full text content + - Backspace announces deletions + - Enter submits, Escape cancels + - Fully accessible without screen reader dependency Args: prompt (str): Prompt text to display (default: "Enter text:") @@ -23,15 +30,178 @@ def get_input(prompt="Enter text:", text=""): Returns: str: User input text, or None if cancelled """ - app = wx.App(False) - dialog = wx.TextEntryDialog(None, prompt, "Input", text) - dialog.SetValue(text) - if dialog.ShowModal() == wx.ID_OK: - userInput = dialog.GetValue() + + # Initialize text buffer and cursor + text_buffer = list(text) # Use list for easier character manipulation + cursor_pos = len(text_buffer) # Start at end of initial text + + # Announce the prompt and initial text + speak(prompt) + if text: + speak(f"Default text: {text}") else: - userInput = None - dialog.Destroy() - return userInput + speak("Empty text field") + + # Speak initial cursor position + if cursor_pos == 0: + speak("Beginning of text") + elif cursor_pos == len(text_buffer): + speak("End of text") + else: + speak(f"At character: {text_buffer[cursor_pos]}") + + # Main input loop + while True: + event = pygame.event.wait() + + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_RETURN: + # Submit the input + result = ''.join(text_buffer) + speak(f"Submitted: {result if result else 'empty'}") + return result + + elif event.key == pygame.K_ESCAPE: + # Cancel input + speak("Cancelled") + return None + + elif event.key == pygame.K_BACKSPACE: + # Delete character before cursor + if cursor_pos > 0: + deleted_char = text_buffer.pop(cursor_pos - 1) + cursor_pos -= 1 + speak(f"{deleted_char} deleted") + else: + speak("Nothing to delete") + + elif event.key == pygame.K_DELETE: + # Delete character at cursor + if cursor_pos < len(text_buffer): + deleted_char = text_buffer.pop(cursor_pos) + speak(f"{deleted_char} deleted") + else: + speak("Nothing to delete") + + elif event.key == pygame.K_LEFT: + # Move cursor left and speak character + if cursor_pos > 0: + cursor_pos -= 1 + if cursor_pos == 0: + speak("Beginning of text") + else: + speak(text_buffer[cursor_pos]) + else: + speak("Beginning of text") + + elif event.key == pygame.K_RIGHT: + # Move cursor right and speak character + if cursor_pos < len(text_buffer): + speak(text_buffer[cursor_pos]) + cursor_pos += 1 + if cursor_pos == len(text_buffer): + speak("End of text") + else: + speak("End of text") + + elif event.key == pygame.K_UP or event.key == pygame.K_DOWN: + # Read entire text content + if text_buffer: + speak(''.join(text_buffer)) + else: + speak("Empty text field") + + elif event.key == pygame.K_HOME: + # Move to beginning + cursor_pos = 0 + speak("Beginning of text") + + elif event.key == pygame.K_END: + # Move to end + cursor_pos = len(text_buffer) + speak("End of text") + + else: + # Handle regular character input + if event.unicode and event.unicode.isprintable(): + char = event.unicode + # Insert character at cursor position + text_buffer.insert(cursor_pos, char) + cursor_pos += 1 + + # Speak the character name + if char == ' ': + speak("space") + elif char == '\\': + speak("backslash") + elif char == '/': + speak("slash") + elif char == '!': + speak("exclamation mark") + elif char == '"': + speak("quotation mark") + elif char == '#': + speak("hash") + elif char == '$': + speak("dollar sign") + elif char == '%': + speak("percent") + elif char == '&': + speak("ampersand") + elif char == "'": + speak("apostrophe") + elif char == '(': + speak("left parenthesis") + elif char == ')': + speak("right parenthesis") + elif char == '*': + speak("asterisk") + elif char == '+': + speak("plus") + elif char == ',': + speak("comma") + elif char == '-': + speak("minus") + elif char == '.': + speak("period") + elif char == ':': + speak("colon") + elif char == ';': + speak("semicolon") + elif char == '<': + speak("less than") + elif char == '=': + speak("equals") + elif char == '>': + speak("greater than") + elif char == '?': + speak("question mark") + elif char == '@': + speak("at sign") + elif char == '[': + speak("left bracket") + elif char == ']': + speak("right bracket") + elif char == '^': + speak("caret") + elif char == '_': + speak("underscore") + elif char == '`': + speak("grave accent") + elif char == '{': + speak("left brace") + elif char == '|': + speak("pipe") + elif char == '}': + speak("right brace") + elif char == '~': + speak("tilde") + else: + # For regular letters, numbers, and other characters + speak(char) + + # Allow other events to be processed + pygame.event.pump() def pause_game(): """Pauses the game until user presses backspace.""" diff --git a/menu.py b/menu.py index 31355db..8b18259 100644 --- a/menu.py +++ b/menu.py @@ -485,6 +485,12 @@ def exit_game(fade=0): except Exception as e: print(f"Warning: Could not handle music during exit: {e}") + # Clean up pygame mixer first + try: + pygame.mixer.quit() + except Exception as e: + print(f"Warning: Error during pygame.mixer.quit(): {e}") + # Clean up pygame try: pygame.quit() diff --git a/requirements.txt b/requirements.txt index e74c05f..f3dd51e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,3 @@ pyxdg>=0.27 setproctitle>=1.2.0 numpy>=1.19.0 accessible-output2>=0.14 -wxpython