GUI: Add guard to prevent recursive call of handleMouseWheel in dialogs

This fixes bug #14404.

The rercusive call was introduced with commit 325260f that changed
the implementation of Widget::handleMouseWheel to call handleMouseWheel
on the widget boss. If the widget boss is a dialog, its implementation
is to call handleMouseWheel on the widget under the cursor. And
we can end up with an infinite loop.

As indicated in bug #13106, which was fixed by commit 325260f, a
similar infinite loop when using the mouse wheel to scroll tabs in
the options dialog was caused by the commit and quickly fixed. But
the fix was only for that particular case. Here the fix should
be more global.
This commit is contained in:
Thierry Crozat 2023-04-12 23:34:10 +01:00
parent b4b27be65b
commit 059781c89c
2 changed files with 13 additions and 5 deletions

View File

@ -40,7 +40,7 @@ namespace GUI {
Dialog::Dialog(int x, int y, int w, int h, bool scale)
: GuiObject(x, y, w, h, scale),
_mouseWidget(nullptr), _focusedWidget(nullptr), _dragWidget(nullptr), _tickleWidget(nullptr), _visible(false),
_backgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) {
_backgroundType(GUI::ThemeEngine::kDialogBackgroundDefault), _handlingMouseWheel(false) {
// Some dialogs like LauncherDialog use internally a fixed size, even though
// their widgets rely on the layout to be initialized correctly by the theme.
// Thus we need to catch screen changes here too. If we do not do that, it
@ -55,7 +55,7 @@ Dialog::Dialog(int x, int y, int w, int h, bool scale)
Dialog::Dialog(const Common::String &name)
: GuiObject(name),
_mouseWidget(nullptr), _focusedWidget(nullptr), _dragWidget(nullptr), _tickleWidget(nullptr), _visible(false),
_backgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) {
_backgroundType(GUI::ThemeEngine::kDialogBackgroundDefault), _handlingMouseWheel(false) {
// It may happen that we have 3x scaler in launcher (960xY) and then 640x480
// game will be forced to 1x. At this stage GUI will not be aware of
@ -236,17 +236,24 @@ void Dialog::handleMouseUp(int x, int y, int button, int clickCount) {
}
void Dialog::handleMouseWheel(int x, int y, int direction) {
Widget *w;
// Guard against recursive call.
// This can happen as we call handleMouseWheel() on the widget under the mouse,
// and the default implementation of Widget::handleMouseWheel() is to call it
// for the widget boss, which can be this dialog.
if (_handlingMouseWheel)
return;
_handlingMouseWheel = true;
// This may look a bit backwards, but I think it makes more sense for
// the mouse wheel to primarily affect the widget the mouse is at than
// the widget that happens to be focused.
w = findWidget(x, y);
Widget *w = findWidget(x, y);
if (!w)
w = _focusedWidget;
if (w)
w->handleMouseWheel(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), direction);
_handlingMouseWheel = false;
}
void Dialog::handleKeyDown(Common::KeyState state) {

View File

@ -66,6 +66,7 @@ protected:
private:
int _result;
bool _handlingMouseWheel;
public:
Dialog(int x, int y, int w, int h, bool scale = false);