Skip to content

Add Physics for Panels mod suite - #4986

Open
Prashant-modder wants to merge 39 commits into
ramensoftware:mainfrom
Prashant-modder:main
Open

Add Physics for Panels mod suite#4986
Prashant-modder wants to merge 39 commits into
ramensoftware:mainfrom
Prashant-modder:main

Conversation

@Prashant-modder

Copy link
Copy Markdown

Changelog

If this pull request updates an existing mod, describe the changes below:

  • Changelog item 1...
  • Changelog item 2...

Mod authorship

If this pull request introduces a new mod, please complete the section below.

This mod was created by:

    • [ Yes ] Claude
    • [ Yes ] Gemini
    • Me too

Please select the options that best apply. Your selection does not affect the acceptance criteria, but it helps reviewers understand the context of the code and provide relevant feedback.

@windhawk-reviewer

Copy link
Copy Markdown

Thanks for the pull request! This repository uses a two-stage review: an AI review that you run yourself, followed by a human review.

To get started, comment /ai-review. Once you're happy with the result, comment /ready-for-reviewer to hand it over to a human reviewer.

See the pull request review process for the full details.

@windhawk-reviewer windhawk-reviewer Bot added the waiting-for-author The author's turn: request an AI review, or respond to one that was posted. label Aug 2, 2026
@Prashant-modder

Copy link
Copy Markdown
Author

/ai-review

@windhawk-reviewer

Copy link
Copy Markdown

@Prashant-modder /ai-review can't be applied here: the AI review only handles a pull request that adds or updates a single mod file under mods/, but it changes 2 files.

Comment /ready-for-reviewer to hand this pull request over to a human reviewer directly. See the pull request review process for details.

@Prashant-modder

Copy link
Copy Markdown
Author

/ready-for-reviewer

@windhawk-reviewer windhawk-reviewer Bot added waiting-for-reviewer Ready for a human reviewer, and in the queue for one. and removed waiting-for-author The author's turn: request an AI review, or respond to one that was posted. labels Aug 2, 2026
@m417z

m417z commented Aug 2, 2026

Copy link
Copy Markdown
Member

The submission is not ready, fix the CI errors.

@m417z m417z added waiting-for-author The author's turn: request an AI review, or respond to one that was posted. and removed waiting-for-reviewer Ready for a human reviewer, and in the queue for one. labels Aug 2, 2026

@Prashant-modder Prashant-modder left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...

@Prashant-modder

Copy link
Copy Markdown
Author

/ready-for-reviewer

@windhawk-reviewer windhawk-reviewer Bot added the waiting-for-reviewer Ready for a human reviewer, and in the queue for one. label Aug 2, 2026
@windhawk-reviewer windhawk-reviewer Bot removed the waiting-for-ai-review An AI review was requested and is being prepared. label Aug 3, 2026
@Prashant-modder

Copy link
Copy Markdown
Author

/ai-review

@windhawk-reviewer windhawk-reviewer Bot added waiting-for-ai-review An AI review was requested and is being prepared. and removed waiting-for-author The author's turn: request an AI review, or respond to one that was posted. labels Aug 3, 2026
@windhawk-reviewer

Copy link
Copy Markdown

Submission review

Note: This review was done by Claude. Due to the amount of submissions, doing a fully manual review for each pull request is no longer feasible. Thank you for understanding.

Remember: The AI reviewer can be wrong - it may misread code, flag correct code as broken, or suggest changes that make things worse. Treat its findings as suggestions to verify, not instructions to follow blindly. You're responsible for the code you submit, so if a finding doesn't hold up, say so instead of changing working code to satisfy it.

Please address the following issues. The items in the collapsed sections are optional, so it's your call whether to address them.


The main problems are in the flyout detection (wrong window class for most Windows 11 builds, and a visibility test that likely never goes false), and in the fact that a blocked _Hide is never re-armed, which can leave the taskbar stuck on screen. There's also a naming/identity issue that should be settled before merge.

1. The mod id has nothing to do with the mod. The id is physics-detector (and the file is physics-detector.wh.cpp), while the mod is "Taskbar Auto-Hide Fine Tuner for Flyouts". The id is the permanent identifier — it becomes the mod's URL and its on-disk identity — and it can't be changed after merge without breaking existing users. Please rename both the id and the file to something matching, e.g. taskbar-auto-hide-keep-shown-for-flyouts. (The PR title and body also still describe a "Physics for Panels mod suite" with an unfilled changelog template — worth cleaning up.)

2. The name collides with an existing mod, and the feature arguably belongs there. taskbar-auto-hide-keyboard-only is published as "Taskbar auto-hide fine tuning"; this one is "Taskbar Auto-Hide Fine Tuner for Flyouts". Two near-identically named auto-hide mods in the catalog will be confusing. That mod already hooks TrayUI::_Hide, CSecondaryTray::_AutoHide, the ViewCoordinator auto-hide path and the tray hide/unhide timers, i.e. it already owns all the plumbing this feature needs. The preferred outcome here is to propose "keep the taskbar shown while a shell flyout is open" as an option on that mod (issue/PR in the maintainer's repo) rather than shipping a second mod. If you'd rather keep it standalone, please at least pick a clearly distinct name.

3. ControlCenterWindow only exists on Windows 11 24H2+ — on earlier Windows 11 the flyout half of the mod does nothing. The Quick Settings / Notification Center flyout moved to ShellHost.exe with class ControlCenterWindow only in 24H2. Before that it lives in ShellExperienceHost.exe with class Windows.UI.Core.CoreWindow. See shell-flyout-positions.wh.cpp#L1755 and the @include list of windows-11-notification-center-styler.wh.cpp#L10, which both handle the two targets separately. FindWindowW(L"ControlCenterWindow", nullptr) returns nullptr on 21H2–23H2, so IsShellFlyoutOpen() is permanently false there and feature #2 in the README silently never works — with no error and nothing in the log. Either handle both classes (matching on the owning process, since Windows.UI.Core.CoreWindow is a very generic class) or state the real build requirement in the README.

4. FindWindowW + IsWindowVisible is not a reliable "flyout is open" test. Two separate problems:

  • FindWindowW returns only the first window of that class. ShellHost.exe hosts more than one ControlCenterWindow (Quick Settings and Notification Center are separate windows) — see how windows-11-notification-center-styler.wh.cpp#L9803 collects them into a std::vector rather than taking one. So the mod may test the closed window while the other one is open.

  • More importantly, these shell flyout windows are kept alive and cloaked rather than destroyed/hidden, so IsWindowVisible can stay TRUE while the flyout is closed. If that's the case on your build, _Hide is blocked forever and auto-hide stops working entirely until the mod is disabled — a much worse failure than the bug being fixed. The established test in this repo is DWMWA_CLOAKED; see IsStartMenuOpen in shell-flyout-positions.wh.cpp#L308 ("The start menu window stays cloaked while hidden and is uncloaked while shown").

    Roughly:

    static bool IsShellFlyoutOpen() {
        bool open = false;
        HWND hWnd = nullptr;
        while ((hWnd = FindWindowExW(nullptr, hWnd, L"ControlCenterWindow",
                                     nullptr)) != nullptr) {
            BOOL cloaked = FALSE;
            if (IsWindowVisible(hWnd) &&
                SUCCEEDED(DwmGetWindowAttribute(hWnd, DWMWA_CLOAKED, &cloaked,
                                                sizeof(cloaked))) &&
                !cloaked) {
                open = true;
                break;
            }
        }
        return open;
    }

    (needs -ldwmapi in @compilerOptions). Please verify with logging which of IsWindowVisible / cloaked actually tracks the flyout on your build before settling on a check.

5. A blocked _Hide is never re-armed, so the taskbar can stay visible indefinitely. The hook just swallows the call. The hide request came from a one-shot tray timer, so once it's dropped nothing schedules another one — when the blocking condition later clears, there is no new hide trigger. Repro: with the cursor away from the taskbar, press Win+A (taskbar unhides), the hide timer fires and gets blocked, then close the flyout with Esc — the taskbar stays on screen until you hover it and move away again. The same applies if the mod is disabled while a hide is being suppressed.

Record the taskbar window and re-request the hide once the condition clears, the way taskbar-auto-hide-keyboard-only.wh.cpp#L1109 does — it polls on a timer while the cursor is over the taskbar and then re-triggers the hide with SetTimer(hTaskbarWnd, kTrayUITimerHide, 0, nullptr) (L1213, with kTrayUITimerHide = 2 at L178). Also make sure any such timer is killed in Wh_ModUninit.

6. On current Windows 11 builds, hiding is driven by ViewCoordinator, not by TrayUI::_Hide. Recent builds ship a "new taskbar auto-hide animation" (feature id 41356296, default-on) where the collapse is decided in Taskbar.View.dll by winrt::Taskbar::implementation::ViewCoordinator::ShouldTaskbarBeExpanded / UpdateIsExpanded, off pointer-leave events — see taskbar-auto-hide-keyboard-only.wh.cpp#L1660 and the comment at L1104 ("The new Win11 auto-hide implementation only hides on pointer-leave events"). Blocking TrayUI::_Hide alone will likely have no visible effect there. Please test on 24H2/25H2 and, if so, hook the ViewCoordinator path too (in Taskbar.View.dll / ExplorerExtensions.dll, loaded late — see how that mod hooks LoadLibraryExW in kernelbase.dll at L2148 to catch the late load).

Optional improvements

Minor polish — none of this affects users, so it's your call.

  • The first symbol variant, public: virtual void __cdecl TrayUI::_Hide(void), doesn't exist — _Hide isn't virtual. All three existing mods that hook it use only public: void __cdecl TrayUI::_Hide(void) (example). Dropping the bogus variant avoids a wasted resolution attempt.
  • There are no Wh_Log calls at all, which makes the mod impossible to diagnose from the log. At minimum log the taskbar.dll load failure and the blocked-hide decision — Wh_Log is a cheap no-op when logging is off, so it costs nothing here.
  • Wh_ModUninit is empty and only carries a leftover development note ("since we dropped background thread allocations entirely"). All lifecycle callbacks are optional — just delete the function.
  • #include <windhawk_api.h> is redundant; windhawk_utils.h already includes it.
  • @description says "while the cursor is over the panel" — should be "taskbar".
  • The README could mention that the mod only does anything when taskbar auto-hide is enabled in Windows settings, and a short GIF showing the before/after would make the effect obvious (only i.imgur.com and raw.githubusercontent.com are allowed image hosts).

Functionality notes

Non-critical observations and ideas about the feature behavior itself.

  • Is condition I LOVE IT #1 actually needed? Windows already keeps an auto-hidden taskbar shown while the pointer is over it, so "don't hide while the cursor is over the taskbar" reads as redundant with stock behavior. If you hit a concrete case where it isn't, it'd be worth describing it in the README; otherwise the mod could be simplified to the flyout case only. (Since the code is AI-assisted, this is the kind of thing that's easy to end up with unintentionally.)
  • Secondary taskbars aren't covered. TrayUI::_Hide only governs the primary taskbar; taskbars on additional monitors hide via CSecondaryTray::_AutoHide (reference), and IsMouseOverTaskbar doesn't recognize Shell_SecondaryTrayWnd either. On a multi-monitor setup the behavior will be inconsistent between monitors.
  • WindowFromPoint misses things sitting on top of the taskbar. Taskbar tooltips, thumbnail previews, jump lists and context menus are their own top-level windows, so GetAncestor(..., GA_ROOT) returns those instead of Shell_TrayWnd and the mod concludes the cursor is not over the taskbar. Testing the cursor against the taskbar's GetWindowRect (PtInRect) would be more robust for this purpose.
  • A flyout open on one monitor blocks hiding everywhere. Not a big deal today given the primary-taskbar-only scope, but worth keeping in mind if you extend to secondary taskbars.


Next steps:

  • /ai-review - after pushing fixes, to get a review of the updated code. You can repeat this as many times as you need, but each review is thorough and usually there's no need for more than 2-3 iterations.
  • /ready-for-reviewer - once you're satisfied with the state of the pull request, to hand it over to a human reviewer. If some findings above are left unaddressed, add a short note explaining why.

See the review process for details.

@windhawk-reviewer windhawk-reviewer Bot added waiting-for-author The author's turn: request an AI review, or respond to one that was posted. and removed waiting-for-ai-review An AI review was requested and is being prepared. labels Aug 3, 2026
@Prashant-modder

Copy link
Copy Markdown
Author

/ready-for-reviewer

@windhawk-reviewer

Copy link
Copy Markdown

@Prashant-modder /ready-for-reviewer can't be applied here: the most recent AI review covers 00c9afd, but the current head of this pull request is 3691c9a. Comment /ai-review to get a review of the current code.

@Prashant-modder

Copy link
Copy Markdown
Author

/ai-review

@windhawk-reviewer windhawk-reviewer Bot added waiting-for-ai-review An AI review was requested and is being prepared. and removed waiting-for-author The author's turn: request an AI review, or respond to one that was posted. labels Aug 5, 2026
@windhawk-reviewer

Copy link
Copy Markdown

Submission review

Note: This review was done by Claude. Due to the amount of submissions, doing a fully manual review for each pull request is no longer feasible. Thank you for understanding.

Remember: The AI reviewer can be wrong - it may misread code, flag correct code as broken, or suggest changes that make things worse. Treat its findings as suggestions to verify, not instructions to follow blindly. You're responsible for the code you submit, so if a finding doesn't hold up, say so instead of changing working code to satisfy it.

Please address the following issues. The items in the collapsed sections are optional, so it's your call whether to address them.


The mod targets an area that is already well covered by existing mods in this repo, so there are good references to compare against — taskbar-auto-hide-when-maximized.wh.cpp and taskbar-auto-hide-custom-activation-area.wh.cpp both hook exactly these functions. Comparing the code against them surfaces several things that look like AI artifacts rather than working code.

1. The SetTimer callback can be invoked after the mod is unloaded → explorer.exe crash.

RearmTimerProc lives in the mod image and is installed as a TIMERPROC:

SetTimer(hTaskbar, TIMER_REARM_ID, TIMER_POLL_INTERVAL, RearmTimerProc);

KillTimer in Wh_ModUninit does not remove WM_TIMER messages already posted to the queue. With a TIMERPROC, DispatchMessage calls the proc through the message's lParam — so a WM_TIMER that was already queued when the mod was disabled will call into the mod image after Windhawk has FreeLibrary'd it. Windhawk unloads the mod with a single FreeLibrary right after Wh_ModUninit returns, so nothing from the mod image may still be scheduled to run at that point.

Also note Wh_ModUninit runs on an arbitrary thread, while SetTimer/KillTimer are thread-affine ("This window must be owned by the calling thread").

Safest fix: don't use a TIMERPROC at all. Either drop the polling timer (see item 3 — hooking ShouldTaskbarBeExpanded removes the need to poll), or use SetTimer(hWnd, id, interval, nullptr) and handle WM_TIMER in a subclass installed with WindhawkUtils::SetWindowSubclassFromAnyThread, which is removed deterministically on uninit.

2. The whole Taskbar.View.dll / ViewCoordinator code path never executes.

There are four independent reasons:

  • lSystem.LoadLibraryExW is not a real symbol name. HookSymbols will never resolve it, and since the hook is marked optional (true), it fails silently — LoadLibraryExW_Hook is never installed. Additionally, running HookSymbols against kernelbase.dll would force a download of the kernelbase PDB, which is large and slow. Use GetProcAddress instead, as in taskbar-auto-hide-custom-activation-area.wh.cpp#L436-L441:

    HMODULE kernelBaseModule = GetModuleHandle(L"kernelbase.dll");
    auto pKernelBaseLoadLibraryExW =
        (decltype(&LoadLibraryExW))GetProcAddress(kernelBaseModule, "LoadLibraryExW");
    WindhawkUtils::SetFunctionHook(pKernelBaseLoadLibraryExW, LoadLibraryExW_Hook,
                                   &LoadLibraryExW_Original);
  • Hooks registered after Wh_ModInit returns are not applied until Wh_ApplyHookOperations() is called. LoadLibraryExW_Hook calls HookSymbols but never calls it — see #L329-L339.

  • The already-loaded case is not handled. When the mod is enabled mid-session (the common case), Taskbar.View.dll is already in the process and will never be loaded again, so a LoadLibraryExW hook can't catch it. Add a Wh_ModAfterInit that checks GetModuleHandle(L"Taskbar.View.dll") / GetModuleHandle(L"ExplorerExtensions.dll") — see #L451-L466.

  • There's no guard against hooking the same module twice. LoadLibraryExW is called again for an already-loaded DLL (it just bumps the refcount), so HookSymbols would run repeatedly on the same module — which invalidates the symbol cache and forces re-resolution each time. Guard with an std::atomic<bool> g_taskbarViewDllLoaded + .exchange(true) as the reference mods do.

3. The UpdateIsExpanded symbol and prototype are wrong, and it's the wrong function for this.

The real symbol, used identically by three mods in this repo, is:

{LR"(public: void __cdecl winrt::Taskbar::implementation::ViewCoordinator::UpdateIsExpanded(unsigned __int64,enum TaskbarTipTest::TaskbarExpandCollapseReason))"},

with the prototype void(WINAPI*)(void* pThis, HWND hMMTaskbarWnd, int reason) (taskbar-auto-hide-custom-activation-area.wh.cpp#L272-L310). There is no bool isExpanded parameter — the second argument is the taskbar HWND and the third is a reason enum. So UpdateIsExpanded_Original(pThis, true) would neither mean "keep it expanded" nor pass the right arguments; if the symbol ever did resolve with the declared 2-argument prototype, this would be an argument-count mismatch against the real 3-argument function.

To force the taskbar to stay expanded, hook ViewCoordinator::ShouldTaskbarBeExpanded and return true, as in taskbar-auto-hide-when-maximized.wh.cpp#L717-L739. UpdateIsExpanded is then only used to re-evaluate the state when the flyout closes (#L746-L757).

4. Secondary taskbars are not handled.

On multi-monitor setups the secondary taskbars hide via CSecondaryTray::_AutoHide, not TrayUI::_Hide, so they will still disappear while a flyout is open. See taskbar-auto-hide-when-maximized.wh.cpp#L794-L806 for the matching hook. The re-arm logic also only ever targets the primary Shell_TrayWnd.

5. FindWindowW(L"Shell_TrayWnd", nullptr) isn't scoped to the current process.

It searches all top-level windows on the desktop and can return a taskbar owned by a different process, in which case SetTimer/KillTimer won't do what you expect. Use a process-filtered lookup — FindCurrentProcessTaskbarWnd is a ready-made example.

6. IsTargetWindowActive queries the wrong DWM attribute.

BOOL cloaked = FALSE;
HRESULT hr = DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, &cloaked, sizeof(cloaked));
if (FAILED(hr)) {
    DwmGetWindowAttribute(hWnd, DWMWA_CLOAKED, &cloaked, sizeof(cloaked));
}

DWMWA_EXTENDED_FRAME_BOUNDS returns a RECT and has nothing to do with cloaking; passing a 4-byte buffer makes the call fail with E_INVALIDARG, which is the only reason the DWMWA_CLOAKED fallback ever runs. The cloaking check is therefore load-bearing on an error path — and if the first call ever succeeded it would leave cloaked holding a rectangle coordinate. This should just be a single call:

BOOL cloaked = FALSE;
if (FAILED(DwmGetWindowAttribute(hWnd, DWMWA_CLOAKED, &cloaked, sizeof(cloaked)))) {
    cloaked = FALSE;
}
return !cloaked;

7. Disabling the mod can leave the taskbar permanently visible.

If the mod is disabled while it is suppressing a hide, nothing ever re-triggers the hide and the taskbar stays on screen until something else pokes it. A mod's effects should disappear when it's disabled. Wh_ModUninit should re-arm the taskbar's own hide timer after killing its own, the same way the re-arm path does:

SetTimer(hTaskbar, kTrayUITimerHide, 0, nullptr);

(see taskbar-auto-hide-when-maximized.wh.cpp#L893).

8. The README has no screenshot or GIF.

The mod's effect is visual, and the comparable mods in this family all include one (e.g. taskbar-auto-hide-when-maximized). A short GIF showing the taskbar staying put while Quick Settings is open would help a lot. Only i.imgur.com and raw.githubusercontent.com are allowed image hosts.

Optional improvements

Minor polish — none of this affects users, so it's your call.

  • No Wh_Log calls anywhere, and the HookSymbols return values are ignored. Wh_ModInit returns TRUE even when nothing was hooked, so a failure is completely invisible. Logging is free in production (it compiles to a check of a disabled flag) and makes bug reports actionable — the reference mods log the hook results and bail on failure.

  • The symbol-hook array in LoadLibraryExW_Hook is commented // Taskbar.View.dll, but it's also resolved against ExplorerExtensions.dll. The comment should list every module the array is used with: // Taskbar.View.dll, ExplorerExtensions.dll.

  • wcsstr(lpLibFileName, L"Taskbar.View.dll") is a case-sensitive substring match on whatever path string the caller passed. Comparing module handles is more robust — that's what GetTaskbarViewModuleHandle() + == module does in the reference mods.

  • #include <windhawk_api.h> is redundant; windhawk_utils.h already includes it.

  • #define TIMER_REARM_ID / TIMER_POLL_INTERVAL / kTrayUITimerHide would be better as constexpr/enum constants — the repo convention for the TrayUI timer IDs is an enum (kTrayUITimerHide = 2, kTrayUITimerUnhide = 3).

  • IsTargetWindowActive doesn't check whether the window is "active"; it checks visible-and-not-cloaked. Something like IsWindowVisibleAndUncloaked would read better.

  • TIMER_REARM_ID 8821 is an arbitrary ID set on a window the mod doesn't own, so it could in principle collide with a taskbar-internal or another mod's timer. Not likely, but if you keep the timer, consider deriving the ID from something mod-specific.

  • The PR title ("Add Physics for Panels mod suite") and the unfilled changelog template don't match the submission — worth tidying up so reviewers/users aren't confused.

Functionality notes

Non-critical observations and ideas about the feature behavior itself.

  • The flyout detection is broad. Any uncloaked, visible Windows.UI.Core.CoreWindow belonging to ShellExperienceHost.exe counts as "a flyout is open" — but that process hosts several unrelated pieces of shell UI, so this can keep the taskbar pinned open in situations that have nothing to do with Quick Settings / Notification Center / Calendar. Narrowing it (e.g. by also checking that the window is the foreground window, or matching the specific flyout windows) would make the behavior more predictable.

  • The 250 ms poll does real work on the Explorer UI thread: it walks every top-level Windows.UI.Core.CoreWindow on the desktop and calls OpenProcess + QueryFullProcessImageNameW for each. It only runs while a flyout is open, so it's bounded, but caching the ShellExperienceHost PID (or switching to SetWinEventHook for EVENT_OBJECT_CLOAKED / EVENT_OBJECT_UNCLOAKED / EVENT_OBJECT_HIDE on the flyout windows) would avoid the repeated process queries entirely. Hooking ShouldTaskbarBeExpanded (item 3) also removes most of the need for polling, since Windows will ask you again on its own.

  • On the "does this belong as a separate mod" question: it doesn't duplicate anything currently in the catalog — taskbar-auto-hide-when-maximized and taskbar-auto-hide-keyboard-only control when the taskbar unhides rather than keeping it shown for flyouts. Still, it's a narrow behavior in a crowded family, so it may be worth asking whether it fits better as an option in one of those mods (an issue on the existing mod) than as a fourth auto-hide mod.

  • TrayUI::_Hide is blocked but the taskbar's own hide timer isn't killed, so TrayUI will keep retrying the hide and the hook will keep re-scanning windows. taskbar-auto-hide-when-maximized calls KillTimer(hWnd, kTrayUITimerHide) when it suppresses a hide (#L780-L792).

  • There are no settings at all. Per-flyout toggles (Quick Settings / Notification Center / Calendar) would be a natural addition if users ask for it.


Next steps:

  • /ai-review - after pushing fixes, to get a review of the updated code. You can repeat this as many times as you need, but each review is thorough and usually there's no need for more than 2-3 iterations.
  • /ready-for-reviewer - once you're satisfied with the state of the pull request, to hand it over to a human reviewer. If some findings above are left unaddressed, add a short note explaining why.

See the review process for details.

@windhawk-reviewer windhawk-reviewer Bot added waiting-for-author The author's turn: request an AI review, or respond to one that was posted. and removed waiting-for-ai-review An AI review was requested and is being prepared. labels Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-for-author The author's turn: request an AI review, or respond to one that was posted.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants