diff --git a/src/openrct2-ui/WindowManager.cpp b/src/openrct2-ui/WindowManager.cpp index ebc58c7c2a..33200c958a 100644 --- a/src/openrct2-ui/WindowManager.cpp +++ b/src/openrct2-ui/WindowManager.cpp @@ -502,6 +502,11 @@ public: window_invalidate(mainWindow); } } + + void UpdateMouseWheel() override + { + window_all_wheel_input(); + } }; IWindowManager * OpenRCT2::Ui::CreateWindowManager() diff --git a/src/openrct2-ui/interface/Window.cpp b/src/openrct2-ui/interface/Window.cpp index b758f2c1ab..57ca61ddaa 100644 --- a/src/openrct2-ui/interface/Window.cpp +++ b/src/openrct2-ui/interface/Window.cpp @@ -1,16 +1,26 @@ #include #include #include +#include +#include +#include +#include #include +#include #include #include #include "Theme.h" #include "Window.h" +using namespace OpenRCT2; + +// The amount of pixels to scroll per wheel click +constexpr sint32 WINDOW_SCROLL_PIXELS = 17; + #define RCT2_NEW_WINDOW (gWindowNextSlot) #define RCT2_LAST_WINDOW (gWindowNextSlot - 1) -using namespace OpenRCT2; +static sint32 _previousAbsoluteWheel = 0; static bool window_fits_between_others(sint32 x, sint32 y, sint32 width, sint32 height) { @@ -265,3 +275,243 @@ rct_window * window_create_centred(sint32 width, sint32 height, rct_window_event sint32 y = std::max(TOP_TOOLBAR_HEIGHT + 1, (screenHeight - height) / 2); return window_create(x, y, width, height, event_handlers, cls, flags); } + +static sint32 window_get_widget_index(rct_window *w, rct_widget *widget) +{ + sint32 i = 0; + for (rct_widget *widget2 = w->widgets; widget2->type != WWT_LAST; widget2++, i++) + if (widget == widget2) + return i; + return -1; +} + +static sint32 window_get_scroll_index(rct_window *w, sint32 targetWidgetIndex) +{ + if (w->widgets[targetWidgetIndex].type != WWT_SCROLL) + return -1; + + sint32 scrollIndex = 0; + rct_widgetindex widgetIndex = 0; + for (rct_widget *widget = w->widgets; widget->type != WWT_LAST; widget++, widgetIndex++) { + if (widgetIndex == targetWidgetIndex) + break; + if (widget->type == WWT_SCROLL) + scrollIndex++; + } + + return scrollIndex; +} + +static rct_widget *window_get_scroll_widget(rct_window *w, sint32 scrollIndex) +{ + for (rct_widget *widget = w->widgets; widget->type != WWT_LAST; widget++) { + if (widget->type != WWT_SCROLL) + continue; + + if (scrollIndex == 0) + return widget; + scrollIndex--; + } + + return nullptr; +} + +/** + * + * rct2: 0x006E78E3 + */ +static void window_scroll_wheel_input(rct_window *w, sint32 scrollIndex, sint32 wheel) +{ + rct_scroll *scroll = &w->scrolls[scrollIndex]; + rct_widget *widget = window_get_scroll_widget(w, scrollIndex); + rct_widgetindex widgetIndex = window_get_widget_index(w, widget); + + if (scroll->flags & VSCROLLBAR_VISIBLE) { + sint32 size = widget->bottom - widget->top - 1; + if (scroll->flags & HSCROLLBAR_VISIBLE) + size -= 11; + size = std::max(0, scroll->v_bottom - size); + scroll->v_top = std::min(std::max(0, scroll->v_top + wheel), size); + } else { + sint32 size = widget->right - widget->left - 1; + if (scroll->flags & VSCROLLBAR_VISIBLE) + size -= 11; + size = std::max(0, scroll->h_right - size); + scroll->h_left = std::min(std::max(0, scroll->h_left + wheel), size); + } + + widget_scroll_update_thumbs(w, widgetIndex); + widget_invalidate(w, widgetIndex); +} + +/** + * + * rct2: 0x006E793B + */ +static sint32 window_wheel_input(rct_window *w, sint32 wheel) +{ + sint32 i = 0; + for (rct_widget *widget = w->widgets; widget->type != WWT_LAST; widget++) { + if (widget->type != WWT_SCROLL) + continue; + + // Originally always checked first scroll view, bug maybe? + rct_scroll *scroll = &w->scrolls[i]; + if (scroll->flags & (HSCROLLBAR_VISIBLE | VSCROLLBAR_VISIBLE)) { + window_scroll_wheel_input(w, i, wheel); + return 1; + } + i++; + } + + return 0; +} + +/** + * + * rct2: 0x006E79FB + */ +static void window_viewport_wheel_input(rct_window *w, sint32 wheel) +{ + if (gScreenFlags & (SCREEN_FLAGS_TRACK_MANAGER | SCREEN_FLAGS_TITLE_DEMO)) + return; + + if (wheel < 0) + window_zoom_in(w, true); + else if (wheel > 0) + window_zoom_out(w, true); +} + +static bool window_other_wheel_input(rct_window* w, rct_widgetindex widgetIndex, sint32 wheel) +{ + // HACK: Until we have a new window system that allows us to add new events like mouse wheel easily, + // this selective approach will have to do. + + // Allow mouse wheel scrolling to increment or decrement the land tool size for various windows + auto widgetType = w->widgets[widgetIndex].type; + + // Lower widgetIndex once or twice we got a type that matches, to allow scrolling on the increase/decrease buttons too + sint32 attempts = 0; + while (widgetType != WWT_IMGBTN && widgetType != WWT_SPINNER && widgetIndex > 0) + { + switch (widgetType) + { + case WWT_TRNBTN: // + and - for preview widget + case WWT_BUTTON: // + and - for spinner widget + { + if (attempts > 0) + { + // Verify that the previous button was of the same type + auto previousType = w->widgets[widgetIndex + 1].type; + if (previousType != widgetType) + { + return false; + } + } + break; + } + default: + // The widget type is not an increment or decrement button + return false; + } + + attempts++; + if (attempts > 2) + { + // We're 2 buttons up, and no preview or spinner widget was found + return false; + } + + widgetIndex--; + widgetType = w->widgets[widgetIndex].type; + } + + rct_widgetindex buttonWidgetIndex; + uint16 expectedType; + uint32 expectedContent[2]; + switch (widgetType) + { + case WWT_IMGBTN: + buttonWidgetIndex = wheel < 0 ? widgetIndex + 2 : widgetIndex + 1; + expectedType = WWT_TRNBTN; + expectedContent[0] = IMAGE_TYPE_REMAP | SPR_LAND_TOOL_DECREASE; + expectedContent[1] = IMAGE_TYPE_REMAP | SPR_LAND_TOOL_INCREASE; + break; + case WWT_SPINNER: + buttonWidgetIndex = wheel < 0 ? widgetIndex + 1 : widgetIndex + 2; + expectedType = WWT_BUTTON; + expectedContent[0] = STR_NUMERIC_UP; + expectedContent[1] = STR_NUMERIC_DOWN; + break; + default: return false; + } + + if (widget_is_disabled(w, buttonWidgetIndex)) + { + return false; + } + + auto button1Type = w->widgets[widgetIndex + 1].type; + auto button1Image = w->widgets[widgetIndex + 1].image; + auto button2Type = w->widgets[widgetIndex + 2].type; + auto button2Image = w->widgets[widgetIndex + 2].image; + if (button1Type != expectedType || button2Type != expectedType || button1Image != expectedContent[0] + || button2Image != expectedContent[1]) + { + return false; + } + + window_event_mouse_down_call(w, buttonWidgetIndex); + return true; +} + +/** + * + * rct2: 0x006E7868 + */ +void window_all_wheel_input() +{ + // Get wheel value + CursorState * cursorState = (CursorState *)context_get_cursor_state(); + sint32 absolute_wheel = cursorState->wheel; + sint32 relative_wheel = absolute_wheel - _previousAbsoluteWheel; + sint32 pixel_scroll = relative_wheel * WINDOW_SCROLL_PIXELS; + _previousAbsoluteWheel = absolute_wheel; + + if (relative_wheel == 0) + return; + + // Check window cursor is over + if (!(input_test_flag(INPUT_FLAG_5))) { + rct_window *w = window_find_from_point(cursorState->x, cursorState->y); + if (w != nullptr) { + // Check if main window + if (w->classification == WC_MAIN_WINDOW || w->classification == WC_VIEWPORT) { + window_viewport_wheel_input(w, relative_wheel); + return; + } + + // Check scroll view, cursor is over + rct_widgetindex widgetIndex = window_find_widget_from_point(w, cursorState->x, cursorState->y); + if (widgetIndex != -1) { + rct_widget *widget = &w->widgets[widgetIndex]; + if (widget->type == WWT_SCROLL) { + sint32 scrollIndex = window_get_scroll_index(w, widgetIndex); + rct_scroll *scroll = &w->scrolls[scrollIndex]; + if (scroll->flags & (HSCROLLBAR_VISIBLE | VSCROLLBAR_VISIBLE)) { + window_scroll_wheel_input(w, window_get_scroll_index(w, widgetIndex), pixel_scroll); + return; + } + } else { + if (window_other_wheel_input(w, widgetIndex, pixel_scroll)) { + return; + } + } + + // Check other scroll views on window + if (window_wheel_input(w, pixel_scroll)) + return; + } + } + } +} diff --git a/src/openrct2-ui/interface/Window.h b/src/openrct2-ui/interface/Window.h index 5b7ea76263..93afd59151 100644 --- a/src/openrct2-ui/interface/Window.h +++ b/src/openrct2-ui/interface/Window.h @@ -18,3 +18,5 @@ #include #include + +void window_all_wheel_input(); diff --git a/src/openrct2/interface/Window.cpp b/src/openrct2/interface/Window.cpp index f19cc51ce4..d5f086fd11 100644 --- a/src/openrct2/interface/Window.cpp +++ b/src/openrct2/interface/Window.cpp @@ -34,6 +34,8 @@ #include "../sprites.h" #include "../world/Map.h" #include "../world/Sprite.h" +#include "../ui/UiContext.h" +#include "../ui/WindowManager.h" #include "Viewport.h" #include "Widget.h" #include "Window.h" @@ -43,9 +45,6 @@ #define RCT2_LAST_WINDOW (gWindowNextSlot - 1) #define RCT2_NEW_WINDOW (gWindowNextSlot) -// The amount of pixels to scroll per wheel click -#define WINDOW_SCROLL_PIXELS 17 - rct_window g_window_list[WINDOW_LIMIT_MAX + WINDOW_LIMIT_RESERVED]; rct_window * gWindowFirst; rct_window * gWindowNextSlot = nullptr; @@ -86,53 +85,10 @@ static constexpr const float window_scroll_locations[][2] = { {0.125f, 0.125f}, }; -static sint32 _previousAbsoluteWheel = 0; - static bool window_fits_between_others(sint32 x, sint32 y, sint32 width, sint32 height); -static void window_all_wheel_input(); static sint32 window_draw_split(rct_drawpixelinfo *dpi, rct_window *w, sint32 left, sint32 top, sint32 right, sint32 bottom); static void window_draw_single(rct_drawpixelinfo *dpi, rct_window *w, sint32 left, sint32 top, sint32 right, sint32 bottom); -static sint32 window_get_widget_index(rct_window *w, rct_widget *widget) -{ - sint32 i = 0; - for (rct_widget *widget2 = w->widgets; widget2->type != WWT_LAST; widget2++, i++) - if (widget == widget2) - return i; - return -1; -} - -static sint32 window_get_scroll_index(rct_window *w, sint32 targetWidgetIndex) -{ - if (w->widgets[targetWidgetIndex].type != WWT_SCROLL) - return -1; - - sint32 scrollIndex = 0; - rct_widgetindex widgetIndex = 0; - for (rct_widget *widget = w->widgets; widget->type != WWT_LAST; widget++, widgetIndex++) { - if (widgetIndex == targetWidgetIndex) - break; - if (widget->type == WWT_SCROLL) - scrollIndex++; - } - - return scrollIndex; -} - -static rct_widget *window_get_scroll_widget(rct_window *w, sint32 scrollIndex) -{ - for (rct_widget *widget = w->widgets; widget->type != WWT_LAST; widget++) { - if (widget->type != WWT_SCROLL) - continue; - - if (scrollIndex == 0) - return widget; - scrollIndex--; - } - - return nullptr; -} - /** * * rct2: 0x006ED7B0 @@ -178,207 +134,8 @@ void window_update_all() } } - window_all_wheel_input(); -} - -/** - * - * rct2: 0x006E78E3 - */ -static void window_scroll_wheel_input(rct_window *w, sint32 scrollIndex, sint32 wheel) -{ - rct_scroll *scroll = &w->scrolls[scrollIndex]; - rct_widget *widget = window_get_scroll_widget(w, scrollIndex); - rct_widgetindex widgetIndex = window_get_widget_index(w, widget); - - if (scroll->flags & VSCROLLBAR_VISIBLE) { - sint32 size = widget->bottom - widget->top - 1; - if (scroll->flags & HSCROLLBAR_VISIBLE) - size -= 11; - size = std::max(0, scroll->v_bottom - size); - scroll->v_top = std::min(std::max(0, scroll->v_top + wheel), size); - } else { - sint32 size = widget->right - widget->left - 1; - if (scroll->flags & VSCROLLBAR_VISIBLE) - size -= 11; - size = std::max(0, scroll->h_right - size); - scroll->h_left = std::min(std::max(0, scroll->h_left + wheel), size); - } - - widget_scroll_update_thumbs(w, widgetIndex); - widget_invalidate(w, widgetIndex); -} - -/** - * - * rct2: 0x006E793B - */ -static sint32 window_wheel_input(rct_window *w, sint32 wheel) -{ - sint32 i = 0; - for (rct_widget *widget = w->widgets; widget->type != WWT_LAST; widget++) { - if (widget->type != WWT_SCROLL) - continue; - - // Originally always checked first scroll view, bug maybe? - rct_scroll *scroll = &w->scrolls[i]; - if (scroll->flags & (HSCROLLBAR_VISIBLE | VSCROLLBAR_VISIBLE)) { - window_scroll_wheel_input(w, i, wheel); - return 1; - } - i++; - } - - return 0; -} - -/** - * - * rct2: 0x006E79FB - */ -static void window_viewport_wheel_input(rct_window *w, sint32 wheel) -{ - if (gScreenFlags & (SCREEN_FLAGS_TRACK_MANAGER | SCREEN_FLAGS_TITLE_DEMO)) - return; - - if (wheel < 0) - window_zoom_in(w, true); - else if (wheel > 0) - window_zoom_out(w, true); -} - -static bool window_other_wheel_input(rct_window* w, rct_widgetindex widgetIndex, sint32 wheel) -{ - // HACK: Until we have a new window system that allows us to add new events like mouse wheel easily, - // this selective approach will have to do. - - // Allow mouse wheel scrolling to increment or decrement the land tool size for various windows - auto widgetType = w->widgets[widgetIndex].type; - - // Lower widgetIndex once or twice we got a type that matches, to allow scrolling on the increase/decrease buttons too - sint32 attempts = 0; - while (widgetType != WWT_IMGBTN && widgetType != WWT_SPINNER && widgetIndex > 0) - { - switch (widgetType) - { - case WWT_TRNBTN: // + and - for preview widget - case WWT_BUTTON: // + and - for spinner widget - { - if (attempts > 0) - { - // Verify that the previous button was of the same type - auto previousType = w->widgets[widgetIndex + 1].type; - if (previousType != widgetType) - { - return false; - } - } - break; - } - default: - // The widget type is not an increment or decrement button - return false; - } - - attempts++; - if (attempts > 2) - { - // We're 2 buttons up, and no preview or spinner widget was found - return false; - } - - widgetIndex--; - widgetType = w->widgets[widgetIndex].type; - } - - rct_widgetindex buttonWidgetIndex; - uint16 expectedType; - uint32 expectedContent[2]; - switch (widgetType) - { - case WWT_IMGBTN: - buttonWidgetIndex = wheel < 0 ? widgetIndex + 2 : widgetIndex + 1; - expectedType = WWT_TRNBTN; - expectedContent[0] = IMAGE_TYPE_REMAP | SPR_LAND_TOOL_DECREASE; - expectedContent[1] = IMAGE_TYPE_REMAP | SPR_LAND_TOOL_INCREASE; - break; - case WWT_SPINNER: - buttonWidgetIndex = wheel < 0 ? widgetIndex + 1 : widgetIndex + 2; - expectedType = WWT_BUTTON; - expectedContent[0] = STR_NUMERIC_UP; - expectedContent[1] = STR_NUMERIC_DOWN; - break; - default: return false; - } - - if (widget_is_disabled(w, buttonWidgetIndex)) - { - return false; - } - - auto button1Type = w->widgets[widgetIndex + 1].type; - auto button1Image = w->widgets[widgetIndex + 1].image; - auto button2Type = w->widgets[widgetIndex + 2].type; - auto button2Image = w->widgets[widgetIndex + 2].image; - if (button1Type != expectedType || button2Type != expectedType || button1Image != expectedContent[0] - || button2Image != expectedContent[1]) - { - return false; - } - - window_event_mouse_down_call(w, buttonWidgetIndex); - return true; -} - -/** - * - * rct2: 0x006E7868 - */ -static void window_all_wheel_input() -{ - // Get wheel value - CursorState * cursorState = (CursorState *)context_get_cursor_state(); - sint32 absolute_wheel = cursorState->wheel; - sint32 relative_wheel = absolute_wheel - _previousAbsoluteWheel; - sint32 pixel_scroll = relative_wheel * WINDOW_SCROLL_PIXELS; - _previousAbsoluteWheel = absolute_wheel; - - if (relative_wheel == 0) - return; - - // Check window cursor is over - if (!(input_test_flag(INPUT_FLAG_5))) { - rct_window *w = window_find_from_point(cursorState->x, cursorState->y); - if (w != nullptr) { - // Check if main window - if (w->classification == WC_MAIN_WINDOW || w->classification == WC_VIEWPORT) { - window_viewport_wheel_input(w, relative_wheel); - return; - } - - // Check scroll view, cursor is over - rct_widgetindex widgetIndex = window_find_widget_from_point(w, cursorState->x, cursorState->y); - if (widgetIndex != -1) { - rct_widget *widget = &w->widgets[widgetIndex]; - if (widget->type == WWT_SCROLL) { - sint32 scrollIndex = window_get_scroll_index(w, widgetIndex); - rct_scroll *scroll = &w->scrolls[scrollIndex]; - if (scroll->flags & (HSCROLLBAR_VISIBLE | VSCROLLBAR_VISIBLE)) { - window_scroll_wheel_input(w, window_get_scroll_index(w, widgetIndex), pixel_scroll); - return; - } - } else { - if (window_other_wheel_input(w, widgetIndex, pixel_scroll)) { - return; - } - } - - // Check other scroll views on window - if (window_wheel_input(w, pixel_scroll)) - return; - } - } - } + auto windowManager = OpenRCT2::GetContext()->GetUiContext()->GetWindowManager(); + windowManager->UpdateMouseWheel(); } static void window_close_surplus(sint32 cap, sint8 avoid_classification) diff --git a/src/openrct2/ui/DummyWindowManager.cpp b/src/openrct2/ui/DummyWindowManager.cpp index a8107db144..a7687ad509 100644 --- a/src/openrct2/ui/DummyWindowManager.cpp +++ b/src/openrct2/ui/DummyWindowManager.cpp @@ -33,6 +33,7 @@ namespace OpenRCT2::Ui void HandleKeyboard(bool /*isTitle*/) override { } std::string GetKeyboardShortcutString(sint32 /*shortcut*/) override { return std::string(); } void SetMainView(sint32 x, sint32 y, sint32 zoom, sint32 rotation) override { } + void UpdateMouseWheel() override { } }; IWindowManager * CreateDummyWindowManager() diff --git a/src/openrct2/ui/WindowManager.h b/src/openrct2/ui/WindowManager.h index e9cc682d20..6d6bdbbda4 100644 --- a/src/openrct2/ui/WindowManager.h +++ b/src/openrct2/ui/WindowManager.h @@ -43,6 +43,7 @@ namespace OpenRCT2::Ui virtual void HandleKeyboard(bool isTitle) abstract; virtual std::string GetKeyboardShortcutString(sint32 shortcut) abstract; virtual void SetMainView(sint32 x, sint32 y, sint32 zoom, sint32 rotation) abstract; + virtual void UpdateMouseWheel() abstract; }; IWindowManager * CreateDummyWindowManager();