Skip to content

Dropmenus' onchange/onreturn events are inconsistent with KB vs mouse actions #502

@maqp

Description

@maqp

One more bug I'm running into with some more interactive menus:

Environment information

  • OS: Ubuntu 25.10
  • python version: v3.13.7
  • pygame version: v2.6.1
  • pygame-menu version: v4.5.2

Describe the bug

With two inter-dependent drop menus I'm unable to have a satisfying solution that allows selecting an item from drop menu with both keyboard and mouse.

Stand-alone code to Reproduce The Bug

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import pygame
import pygame_menu

USE_ONRETURN = False  # initial mode

CARS = [
    ("Ford",   "Ford"),
    ("Toyota", "Toyota"),
    ("Tesla",  "Tesla"),
]

ENGINES_BY_CAR = {
    "Ford": [
        ("V6", "v6"),
        ("V8", "v8"),
        ("EcoBoost", "ecoboost"),
    ],
    "Toyota": [
        ("Inline-4", "i4"),
        ("Hybrid",   "hybrid"),
        ("V6",       "v6"),
    ],
    "Tesla": [
        ("Single Motor", "single"),
        ("Dual Motor",   "dual"),
        ("Plaid",        "plaid"),
    ],
}

def build_menu(surface, app_state):
    theme = pygame_menu.themes.THEME_DARK.copy()
    menu  = pygame_menu.Menu(
        title  = "Title",
        width  = surface.get_width(),
        height = surface.get_height(),
        theme  = theme
    )

    state = {"engine_ds": None}

    def on_car_change(selected_value, _widget):
        car = selected_value[0][1]
        engines = ENGINES_BY_CAR.get(car, [])
        if not engines:
            engines = [("— No engines —", None)]
        state["engine_ds"].update_items(engines)
        state["engine_ds"].show()
        state["engine_ds"].reset_value()
        menu.render()

    def on_engine_change(selected_value, _widget):
        pass

    def change_mode():
        app_state["use_onreturn"] = not app_state["use_onreturn"]
        app_state["rerender"]     = True
        menu.disable()  # return from mainloop so we can rebuild

    def quit_app():
        app_state["running"] = False
        app_state["rerender"] = False
        menu.disable()

    use_onreturn = app_state["use_onreturn"]

    menu.add.label("Select a car, then an engine.")
    menu.add.vertical_margin(10)

    if use_onreturn:
        menu.add.label("Car drop-select mode: onreturn (keyboard works; Mouse select won't update Engine drop-menu).")
    else:
        menu.add.label("Car drop-select mode: onchange (mouse works; Enter+arrows selects car immediately).")
    menu.add.vertical_margin(20)

    kwargs = dict(
        selection_box_height = 5,
        selection_infinite   = True,
        placeholder          = "Select…",
        default              = 0
    )

    if use_onreturn:
        menu.add.dropselect(
            title    = "Car: ",
            items    = CARS,
            onreturn = on_car_change,
            **kwargs
        )
    else:
        menu.add.dropselect(
            title    = "Car: ",
            items    = CARS,
            onchange = on_car_change,
            **kwargs
        )

    engine_ds = menu.add.dropselect(
        title    = "Engine: ",
        items    = [("Select a car first", None)],
        onchange = on_engine_change,
        **kwargs
    )
    engine_ds.hide()
    state["engine_ds"] = engine_ds

    menu.add.vertical_margin(20)
    menu.add.button("Change Mode", change_mode)
    menu.add.button("Quit", quit_app)

    return menu

def main() -> None:
    pygame.init()
    surface = pygame.display.set_mode((1600, 900))
    pygame.display.set_caption("DropSelect PoC: Car → Engine")

    app_state = {
        "use_onreturn": USE_ONRETURN,
        "rerender":     False,
        "running":      True,
    }

    while app_state["running"]:
        app_state["rerender"] = False
        menu = build_menu(surface, app_state)
        menu.mainloop(surface)
        if not app_state["rerender"]:
            break

    pygame.quit()
    sys.exit()


if __name__ == "__main__":
    try:
        main()
    except SystemExit:
        pygame.quit()
        sys.exit()

Expected behavior

Selecting with mouse and keyboard should feel intuitive. My hunch says the bug boils down to this:

Currently: Hovering cursor over opened drop menu items: ignored, clicking on item: onchange event
Should be: Hovering cursor over opened drop menu items: onchange event, clicking on item: onreturn event

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions