slint-ui / slint

Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
https://slint.dev
Other
17.5k stars 600 forks source link

Windows: Use of popup menu created through raw Win32 API causes UI using Slint widgets to go "mute" #5863

Open npwoods opened 2 months ago

npwoods commented 2 months ago

I've found a problem where creating a popup menu with old-fashioned raw Win32 APIs. Specifically after TrackPopupMenu is called, the UI seems to go "mute" - while mute the UI seems to be functional, but never actually redraws.

I've attached a demonstration of this problem. In the attached program, there are two buttons:

When the popup menu is uses, the text field fails to update. That said, we can tell that the UI is still "functional" because if you resize the window, the UI stops being mute and the text immediately updates. Furthermore if you hit the left bump button while the UI is mute, it seems to track them.

Furthermore, the normal UX button highlighting fails while the UI is mute.

Of course, everything about this report is only relevant on Windows. win32_popup_problem.zip

ogoffart commented 2 months ago

This may be an issue in winit.

(may or may not be related to #5206 and similar)

npwoods commented 2 months ago

I'm curious if we have any idea what this might be.

I did a lot of stepping through both Slint and Winit to see if I could figure out why a vanilla Win32 context menu would introduce problems, but it exceeded my time box.

npwoods commented 1 month ago

I've came up with a gross hack that seems to unfreeze Slint after these popup menus. Specifically I invoke the following Win32 calls. The act of changing the windows sizes seems to be important; it is not enough to call SetWindowPos() with the existing size.

// get the HWND's rectangle
let mut rect = zeroed();
GetWindowRect(hwnd, &mut rect);

// make the window a single pixel wider - the act of changing the size
// seems to "tickle" Slint into unfreezing
SetWindowPos(
    hwnd,
    0 as HWND,
    rect.left,
    rect.top,
    rect.right - rect.left + 1,
    rect.bottom - rect.top,
    0,
);

// and restore the old size
SetWindowPos(
    hwnd,
    0 as HWND,
    rect.left,
    rect.top,
    rect.right - rect.left,
    rect.bottom - rect.top,
    0,
);
npwoods commented 1 month ago

An interesting finding; if I specify uFlags as SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING, the fix continues to work. That said, if I specify SWP_NOSIZE or SWP_NOREDRAW, the fix stops working.

It is not an overall surprise that SWP_NOSIZE causes the hack to fail, but the way SWP_NOREDRAW spoils the hack is quite interesting.