11# ruff: noqa: D103
2+ import math
23import re
34import traceback
45from typing import Any
56
67import unrealsdk
7- from unrealsdk .hooks import Block
8+ from unrealsdk .hooks import Block , Type
89from unrealsdk .unreal import BoundFunction , UObject , WeakPointer , WrappedStruct
910
1011from mods_base import (
@@ -44,8 +45,9 @@ def open_lobby_mods_menu(frontend: WillowGFxMenuFrontend) -> None:
4445 block_search_delegate .enable ()
4546 init_content .enable ()
4647 play_sound .enable ()
47- menu_close .enable ()
4848 handle_input_key .enable ()
49+ menu_scroll .enable ()
50+ menu_close .enable ()
4951
5052 frontend .OpenMP ()
5153
@@ -131,6 +133,7 @@ def init_next_tick(*_: Any) -> None:
131133
132134 menu .SetVariableString ("lobby.tab.text" , "SDK Mod Manager" )
133135 menu .SetVariableString ("lobby.optionsHeader.text" , "Mods" )
136+ menu .SetVariableNumber ("mMenu.mList._y" , 0 )
134137
135138 # Most of the mod details could be updated in the initial hook, but there's a few parts which
136139 # need to be here
@@ -270,6 +273,39 @@ def handle_input_key(
270273 return Block , True
271274
272275
276+ @hook ("WillowGame.WillowGFxLobbyBase:inNext" , hook_type = Type .POST )
277+ @hook ("WillowGame.WillowGFxLobbyBase:inPrev" , hook_type = Type .POST )
278+ def menu_scroll (
279+ obj : UObject ,
280+ _args : WrappedStruct ,
281+ _ret : Any ,
282+ _func : BoundFunction ,
283+ ) -> None :
284+ # If we have more than a page of menu items (14), the scrolling slightly breaks, and the top
285+ # item gets displayed off screen. This is probably a vanilla bug.
286+ # Note this hook is not suitable for focus change, since it's not triggered on mouse movement.
287+
288+ if not math .isfinite (list_y := obj .GetVariableNumber ("mMenu.mList._y" )):
289+ return
290+ if list_y >= 0 :
291+ # Already scrolled to the top, no need to do anything
292+ return
293+
294+ if get_focused_mod (obj ) == drawn_mods [0 ]:
295+ # Special case: if we just scrolled to index 0 always scroll back to the top
296+ obj .SetVariableNumber ("mMenu.mList._y" , 0 )
297+ return
298+
299+ # You can still scroll off the top, as long as you're not selecting the very top item.
300+
301+ # Unfortunately, this seems non-trivial to fix.
302+ # For one, there isn't really a good way to detect if the focused item is off screen.
303+ # Then even if we come up with a heuristic, changing Y while not at the top has some weird
304+ # effects, it'll bring the header back and leave us with some empty menu items.
305+
306+ # Leaving it for now. You can always just scroll up an extra time.
307+
308+
273309@hook ("WillowGame.WillowGFxLobbyMultiplayer:OnClose" )
274310def menu_close (
275311 _obj : UObject ,
@@ -282,8 +318,9 @@ def menu_close(
282318 init_next_tick .disable ()
283319 play_sound .disable ()
284320 select_next_tick .disable ()
285- menu_close .disable ()
286321 handle_input_key .disable ()
322+ menu_scroll .disable ()
323+ menu_close .disable ()
287324
288325 global current_menu
289326 current_menu = WeakPointer ()
0 commit comments