RetroAchievements / RAIntegration

The DLL responsible to integrate emulators with RetroAchievements.org
https://retroachievements.org
MIT License
97 stars 23 forks source link

Cannot tab between fields in editor #758

Open Jamiras opened 3 years ago

Jamiras commented 3 years ago

As a developer, I expect to be able to tab between fields in an editor. In particular, from the title to the description and points fields in the achievement editor.

Jamiras commented 3 years ago

This appears to be a limitation of the way the DLL is integrating with the emulator, and the way windows handles tabbing between fields. In order to process a TAB as a move-to-the-next-field message, the message loop in the emulator must call IsDialogMessage for the window where the tabbing would take place. You can see how fceumm handles all of its child windows here.

While it could be possible to provide a function for the client to call inside it's message loop so we could call IsDialogMessage for each of our windows, this only works when the client has control over it's message loop. RALibretro uses SDL2 for it's window system, and the message loop is buried inside the SDL2 code. Allegro (used by Meka) is even worse. It doesn't call TranslateMessage or IsDialogMessage in it's message loop, which entirely prevents typing in our windows. To get around that, we have to parent our windows to the Meka console instead of the main emulator window.

Jamiras commented 3 years ago

A possible alternative is to subclass each of the input controls on an editor window and add code to handle the WM_KEYDOWN for VK_TAB that issues a WM_NEXTDLGCTL message. My attempts to implement a solution in this regard did move focus between fields, but the controls still generated the "ding" sound suggesting that they didn't support that key. While not an exact replacement for IsDialogMessage, the IsDialogMessage documentation says that TranslateMessage and DispatchMessage must not be called if IsDialogMessage handles the message, so I suspect the one of those to be causing the secondary processing of the message using this implementation.

CasualPokePlayer commented 1 year ago

IsDialogMessage ends up producing an interesting problem here. That filters out some messages sent to the controls, such as Enter and Tab events. This a bit bad for the code notes editor as that ends up not able to enter in code notes and tabbing doesn't work as expected. There's other cases where this produces undesirable results. This can be avoided however, by simply responding to the WM_GETDLGCODE message sent: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-isdialogmessagew#remarks https://learn.microsoft.com/en-us/windows/win32/dlgbox/wm-getdlgcode

BizHawk's WIP implementation actually does IsDialogMessage for message pumping RA's windows (this is buried in WinForm's message pumping code, not really controllable). So tabbing between fields does work, but it leads to the issues described above due to WM_GETDLGCODE not being properly responded to.

CasualPokePlayer commented 1 year ago

Seems that while WM_GETDLGCODE is a "solution," it's not really the proper one (well, not in all cases). These issues ultimately seem to just come from improperly created dialogs. For example here: https://github.com/RetroAchievements/RAIntegration/blob/73140a30a731a16d03e7cf7819af6bfa98cbd78d/src/RA_Shared.rc#L61 For Enter to be processed as a line break, ES_WANTRETURN needs to be set. Since this was not set, Enter does not have any effect when IsDialogMessage is used.

Although unfortunately in the case of tabbing, it seems WM_GETDLGCODE is the only solution (and that also requires subclassing the dialog in order to actually respond to it).