This commit is contained in:
edschuy95 2026-01-21 23:07:16 -05:00
parent aa782f3080
commit d32b83f790
2 changed files with 67 additions and 23 deletions

View file

@ -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,

View file

@ -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):