diff --git a/RA3MP3Playback.py b/RA3MP3Playback.py index 9b96512..1491da9 100644 --- a/RA3MP3Playback.py +++ b/RA3MP3Playback.py @@ -22,7 +22,7 @@ import pygame shutdown_event = threading.Event() # ========================= CONFIG ========================= -DEBUG = False # Set True to enable debug overrides +DEBUG = True # Set True to enable debug overrides # Default paths (used if not in debug mode) DEFAULT_WIN_GAME_EXE = r"./qgame.dll" @@ -588,7 +588,7 @@ def main(): # bufsize=0, # unbuffered # universal_newlines=False) - items = ["Pee pee","Poo poo","Stinky caca peepee poopoo pants.","a","b","c","d"] + items = ["choice1","choice2","choice 3.","a","b","c","d"] menu = TextMenu( items, width=30, diff --git a/textmenu.py b/textmenu.py index 63e6e85..b7b029d 100644 --- a/textmenu.py +++ b/textmenu.py @@ -83,13 +83,52 @@ class TextMenu: self._timeout_alarm = None self.last_click_time = 0 - self.double_click_threshold = 0.5 # seconds + self.double_click_threshold = 0.3 # seconds - # Buttons with double-click support + # Custom button class that handles enter/space differently + class SelectableButton(urwid.Button): + signals = urwid.Button.signals + ['activate'] # Add our custom signal + + def keypress(self, size, key): + if key in ('enter', ' '): + # For enter/space, trigger activation without generating click signal + self._emit('activate') + return None + return super().keypress(size, key) + + # Buttons with proper click handling self.menu_widgets = [] - for c in choices: - btn = urwid.Button(c) - urwid.connect_signal(btn, 'click', self._double_click, c) + for i, c in enumerate(choices): + btn = SelectableButton(c) + + def make_click_handler(choice_text, index): + def click_handler(button): + # Handle mouse clicks with double-click detection + now = time.time() + + # Check if this is a double-click + if now - self.last_click_time < self.double_click_threshold: + # Double-click detected - return the selection + self._cancel_timeout() + self.selected = choice_text + raise urwid.ExitMainLoop() + else: + # Single click - just focus the item (like keyboard navigation) + self.list_walker.set_focus(index) + + self.last_click_time = now + return click_handler + + def make_activate_handler(choice_text): + def activate_handler(button): + # Handle enter/space activation + self._cancel_timeout() + self.selected = choice_text + raise urwid.ExitMainLoop() + return activate_handler + + urwid.connect_signal(btn, 'click', make_click_handler(c, i)) + urwid.connect_signal(btn, 'activate', make_activate_handler(c)) self.menu_widgets.append(urwid.AttrMap(btn, None, focus_map='selected')) # Scrollable ListBox @@ -132,27 +171,32 @@ class TextMenu: self._timeout_alarm = None self.linebox.set_title(self.title) # restore original title - # Mouse double-click - def _double_click(self, button, choice_text): - self._cancel_timeout() - now = time.time() - if now - self.last_click_time < self.double_click_threshold: - self.selected = choice_text - raise urwid.ExitMainLoop() - self.last_click_time = now - - # Keyboard or mouse input + # Keyboard or mouse input - handle ALL input types def _unhandled_input(self, key): - # Cancel timeout on any input + # Cancel timeout on ANY input self._cancel_timeout() - if key == 'enter': + # Handle Enter key immediately (like double-click behavior) + if key in ('enter', ' '): focus_widget, _ = self.listbox.get_focus() if focus_widget: - self.selected = focus_widget.base_widget.get_label() + # Get the button text from the nested widgets + btn_text = focus_widget.original_widget.base_widget.get_label() + self.selected = btn_text raise urwid.ExitMainLoop() - # Scroll events also count: 'up', 'down', 'page up', 'page down', 'mouse press', 'mouse drag', 'mouse wheel' - # urwid passes these to unhandled_input automatically, so we cancel timeout above + elif key == 'esc': + self.selected = None + raise urwid.ExitMainLoop() + # Handle navigation keys that should stop timer but not select + elif key in ('up', 'down', 'page up', 'page down'): + # Timer already cancelled above, just continue + # Navigation already changes focus automatically + pass + # Handle mouse events that should stop timer + elif isinstance(key, tuple) and len(key) >= 3 and key[0].startswith('mouse'): + # This catches mouse events like ('mouse press', 1, x, y) + # Timer already cancelled above, continue with normal processing + pass # Timeout exit def _timeout_exit(self, loop, user_data=None): @@ -185,4 +229,4 @@ class TextMenu: self._update_timer(self.loop) # show countdown immediately self.loop.run() - return self.selected + return self.selected \ No newline at end of file