From 3393fa36d30f925dc39cdc80d1d84a7ce5a912d2 Mon Sep 17 00:00:00 2001 From: Duncan Date: Wed, 31 Jul 2024 15:06:19 +0100 Subject: [PATCH] Move Ui window functions into Ui library (#22426) * Move scroll widget functions * Move window move functions * De-snake * Move further functions to ui * Move widget function to widget file * Move window create to ui --- src/openrct2-ui/UiContext.cpp | 2 +- src/openrct2-ui/interface/Widget.cpp | 68 +++ src/openrct2-ui/interface/Widget.h | 2 + src/openrct2-ui/interface/Window.cpp | 607 ++++++++++++++++++++------- src/openrct2-ui/interface/Window.h | 50 +++ src/openrct2/interface/Widget.h | 2 - src/openrct2/interface/Window.cpp | 389 ----------------- src/openrct2/interface/Window.h | 48 +-- 8 files changed, 586 insertions(+), 582 deletions(-) diff --git a/src/openrct2-ui/UiContext.cpp b/src/openrct2-ui/UiContext.cpp index 1043ac29e0..dc7a3b6a58 100644 --- a/src/openrct2-ui/UiContext.cpp +++ b/src/openrct2-ui/UiContext.cpp @@ -802,7 +802,7 @@ private: if ((flags & SDL_WINDOW_MINIMIZED) == 0) { WindowResizeGui(_width, _height); - WindowRelocateWindows(_width, _height); + Windows::WindowRelocateWindows(_width, _height); } GfxInvalidateScreen(); diff --git a/src/openrct2-ui/interface/Widget.cpp b/src/openrct2-ui/interface/Widget.cpp index e26e95c8d2..63110063dd 100644 --- a/src/openrct2-ui/interface/Widget.cpp +++ b/src/openrct2-ui/interface/Widget.cpp @@ -1242,4 +1242,72 @@ namespace OpenRCT2::Ui widget.content &= ~0xFF; widget.content |= newPercentage; } + + /** + * + * rct2: 0x006EAF26 + */ + void WidgetScrollUpdateThumbs(WindowBase& w, WidgetIndex widget_index) + { + const auto& widget = w.widgets[widget_index]; + auto& scroll = w.scrolls[WindowGetScrollDataIndex(w, widget_index)]; + + if (scroll.flags & HSCROLLBAR_VISIBLE) + { + int32_t view_size = widget.width() - 21; + if (scroll.flags & VSCROLLBAR_VISIBLE) + view_size -= 11; + int32_t x = scroll.h_left * view_size; + if (scroll.h_right != 0) + x /= scroll.h_right; + scroll.h_thumb_left = x + 11; + + x = widget.width() - 2; + if (scroll.flags & VSCROLLBAR_VISIBLE) + x -= 11; + x += scroll.h_left; + if (scroll.h_right != 0) + x = (x * view_size) / scroll.h_right; + x += 11; + view_size += 10; + scroll.h_thumb_right = std::min(x, view_size); + + if (scroll.h_thumb_right - scroll.h_thumb_left < 20) + { + double barPosition = (scroll.h_thumb_right * 1.0) / view_size; + + scroll.h_thumb_left = static_cast(std::lround(scroll.h_thumb_left - (20 * barPosition))); + scroll.h_thumb_right = static_cast(std::lround(scroll.h_thumb_right + (20 * (1 - barPosition)))); + } + } + + if (scroll.flags & VSCROLLBAR_VISIBLE) + { + int32_t view_size = widget.height() - 21; + if (scroll.flags & HSCROLLBAR_VISIBLE) + view_size -= 11; + int32_t y = scroll.v_top * view_size; + if (scroll.v_bottom != 0) + y /= scroll.v_bottom; + scroll.v_thumb_top = y + 11; + + y = widget.height() - 2; + if (scroll.flags & HSCROLLBAR_VISIBLE) + y -= 11; + y += scroll.v_top; + if (scroll.v_bottom != 0) + y = (y * view_size) / scroll.v_bottom; + y += 11; + view_size += 10; + scroll.v_thumb_bottom = std::min(y, view_size); + + if (scroll.v_thumb_bottom - scroll.v_thumb_top < 20) + { + double barPosition = (scroll.v_thumb_bottom * 1.0) / view_size; + + scroll.v_thumb_top = static_cast(std::lround(scroll.v_thumb_top - (20 * barPosition))); + scroll.v_thumb_bottom = static_cast(std::lround(scroll.v_thumb_bottom + (20 * (1 - barPosition)))); + } + } + } } // namespace OpenRCT2::Ui diff --git a/src/openrct2-ui/interface/Widget.h b/src/openrct2-ui/interface/Widget.h index ce911bdafd..f2e2dfd6bb 100644 --- a/src/openrct2-ui/interface/Widget.h +++ b/src/openrct2-ui/interface/Widget.h @@ -190,4 +190,6 @@ namespace OpenRCT2::Ui void WidgetSetCheckboxValue(WindowBase& w, WidgetIndex widgetIndex, bool value); void WidgetProgressBarSetNewPercentage(Widget& widget, uint8_t newPercentage); + + void WidgetScrollUpdateThumbs(WindowBase& w, WidgetIndex widget_index); } // namespace OpenRCT2::Ui diff --git a/src/openrct2-ui/interface/Window.cpp b/src/openrct2-ui/interface/Window.cpp index e5c531c68b..eb78809375 100644 --- a/src/openrct2-ui/interface/Window.cpp +++ b/src/openrct2-ui/interface/Window.cpp @@ -14,6 +14,7 @@ #include "Widget.h" #include +#include #include #include #include @@ -209,96 +210,6 @@ static ScreenCoordsXY GetCentrePositionForNewWindow(int32_t width, int32_t heigh return ScreenCoordsXY{ (screenWidth - width) / 2, std::max(kTopToolbarHeight + 1, (screenHeight - height) / 2) }; } -WindowBase* WindowCreate( - std::unique_ptr&& wp, WindowClass cls, ScreenCoordsXY pos, int32_t width, int32_t height, uint32_t flags) -{ - if (flags & WF_AUTO_POSITION) - { - if (flags & WF_CENTRE_SCREEN) - { - pos = GetCentrePositionForNewWindow(width, height); - } - else - { - pos = GetAutoPositionForNewWindow(width, height); - } - } - - // Check if there are any window slots left - // include kWindowLimitReserved for items such as the main viewport and toolbars to not appear to be counted. - if (g_window_list.size() >= static_cast(Config::Get().general.WindowLimit + kWindowLimitReserved)) - { - // Close least recently used window - for (auto& w : g_window_list) - { - if (w->flags & WF_DEAD) - continue; - if (!(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT | WF_NO_AUTO_CLOSE))) - { - WindowClose(*w.get()); - break; - } - } - } - - // Find right position to insert new window - auto itDestPos = g_window_list.end(); - if (flags & WF_STICK_TO_BACK) - { - for (auto it = g_window_list.begin(); it != g_window_list.end(); it++) - { - if ((*it)->flags & WF_DEAD) - continue; - if (!((*it)->flags & WF_STICK_TO_BACK)) - { - itDestPos = it; - } - } - } - else if (!(flags & WF_STICK_TO_FRONT)) - { - for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) - { - if ((*it)->flags & WF_DEAD) - continue; - if (!((*it)->flags & WF_STICK_TO_FRONT)) - { - itDestPos = it.base(); - break; - } - } - } - - auto itNew = g_window_list.insert(itDestPos, std::move(wp)); - auto w = itNew->get(); - - // Setup window - w->classification = cls; - w->flags = flags; - - // Play sounds and flash the window - if (!(flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))) - { - w->flags |= WF_WHITE_BORDER_MASK; - OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::WindowOpen, 0, pos.x + (width / 2)); - } - - w->windowPos = pos; - w->width = width; - w->height = height; - w->min_width = width; - w->max_width = width; - w->min_height = height; - w->max_height = height; - - w->focus = std::nullopt; - - ColourSchemeUpdate(w); - w->Invalidate(); - w->OnOpen(); - return w; -} - static int32_t WindowGetWidgetIndex(const WindowBase& w, Widget* widget) { int32_t i = 0; @@ -570,45 +481,6 @@ void ApplyScreenSaverLockSetting() Config::Get().general.DisableScreensaver ? SDL_DisableScreenSaver() : SDL_EnableScreenSaver(); } -/** - * Initialises scroll widgets to their virtual size. - * rct2: 0x006EAEB8 - */ -void WindowInitScrollWidgets(WindowBase& w) -{ - Widget* widget; - int32_t widget_index, scroll_index; - - widget_index = 0; - scroll_index = 0; - for (widget = w.widgets; widget->type != WindowWidgetType::Last; widget++) - { - if (widget->type != WindowWidgetType::Scroll) - { - widget_index++; - continue; - } - - auto& scroll = w.scrolls[scroll_index]; - scroll.flags = 0; - ScreenSize scrollSize = w.OnScrollGetSize(scroll_index); - scroll.h_left = 0; - scroll.h_right = scrollSize.width + 1; - scroll.v_top = 0; - scroll.v_bottom = scrollSize.height + 1; - - if (widget->content & SCROLL_HORIZONTAL) - scroll.flags |= HSCROLLBAR_VISIBLE; - if (widget->content & SCROLL_VERTICAL) - scroll.flags |= VSCROLLBAR_VISIBLE; - - WidgetScrollUpdateThumbs(w, widget_index); - - widget_index++; - scroll_index++; - } -} - /** * * rct2: 0x006EB15C @@ -671,19 +543,6 @@ static void WindowInvalidatePressedImageButton(const WindowBase& w) } } -/** - * - * rct2: 0x006EA73F - */ -void InvalidateAllWindowsAfterInput() -{ - WindowVisitEach([](WindowBase* w) { - WindowUpdateScrollWidgets(*w); - WindowInvalidatePressedImageButton(*w); - w->OnResize(); - }); -} - void Window::OnDraw(DrawPixelInfo& dpi) { WindowDrawWidgets(*this, dpi); @@ -696,7 +555,7 @@ void Window::OnDrawWidget(WidgetIndex widgetIndex, DrawPixelInfo& dpi) void Window::InitScrollWidgets() { - WindowInitScrollWidgets(*this); + Windows::WindowInitScrollWidgets(*this); } void Window::InvalidateWidget(WidgetIndex widgetIndex) @@ -837,6 +696,96 @@ namespace OpenRCT2::Ui::Windows static TextInputSession* _textInput; static WidgetIdentifier _currentTextBox = { { WindowClass::Null, 0 }, 0 }; + WindowBase* WindowCreate( + std::unique_ptr&& wp, WindowClass cls, ScreenCoordsXY pos, int32_t width, int32_t height, uint32_t flags) + { + if (flags & WF_AUTO_POSITION) + { + if (flags & WF_CENTRE_SCREEN) + { + pos = GetCentrePositionForNewWindow(width, height); + } + else + { + pos = GetAutoPositionForNewWindow(width, height); + } + } + + // Check if there are any window slots left + // include kWindowLimitReserved for items such as the main viewport and toolbars to not appear to be counted. + if (g_window_list.size() >= static_cast(Config::Get().general.WindowLimit + kWindowLimitReserved)) + { + // Close least recently used window + for (auto& w : g_window_list) + { + if (w->flags & WF_DEAD) + continue; + if (!(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT | WF_NO_AUTO_CLOSE))) + { + WindowClose(*w.get()); + break; + } + } + } + + // Find right position to insert new window + auto itDestPos = g_window_list.end(); + if (flags & WF_STICK_TO_BACK) + { + for (auto it = g_window_list.begin(); it != g_window_list.end(); it++) + { + if ((*it)->flags & WF_DEAD) + continue; + if (!((*it)->flags & WF_STICK_TO_BACK)) + { + itDestPos = it; + } + } + } + else if (!(flags & WF_STICK_TO_FRONT)) + { + for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) + { + if ((*it)->flags & WF_DEAD) + continue; + if (!((*it)->flags & WF_STICK_TO_FRONT)) + { + itDestPos = it.base(); + break; + } + } + } + + auto itNew = g_window_list.insert(itDestPos, std::move(wp)); + auto w = itNew->get(); + + // Setup window + w->classification = cls; + w->flags = flags; + + // Play sounds and flash the window + if (!(flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))) + { + w->flags |= WF_WHITE_BORDER_MASK; + OpenRCT2::Audio::Play(OpenRCT2::Audio::SoundId::WindowOpen, 0, pos.x + (width / 2)); + } + + w->windowPos = pos; + w->width = width; + w->height = height; + w->min_width = width; + w->max_width = width; + w->min_height = height; + w->max_height = height; + + w->focus = std::nullopt; + + ColourSchemeUpdate(w); + w->Invalidate(); + w->OnOpen(); + return w; + } + WindowBase* WindowGetListening() { for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) @@ -934,4 +883,376 @@ namespace OpenRCT2::Ui::Windows { return _currentTextBox; } + + void WindowResize(WindowBase& w, int32_t dw, int32_t dh) + { + if (dw == 0 && dh == 0) + return; + + // Invalidate old region + w.Invalidate(); + + // Clamp new size to minimum and maximum + w.width = std::clamp(w.width + dw, w.min_width, w.max_width); + w.height = std::clamp(w.height + dh, w.min_height, w.max_height); + + w.OnResize(); + w.OnPrepareDraw(); + + // Update scroll widgets + for (auto& scroll : w.scrolls) + { + scroll.h_right = WINDOW_SCROLL_UNDEFINED; + scroll.v_bottom = WINDOW_SCROLL_UNDEFINED; + } + WindowUpdateScrollWidgets(w); + + // Invalidate new region + w.Invalidate(); + } + + /** + * + * rct2: 0x006EAE4E + * + * @param w The window (esi). + */ + void WindowUpdateScrollWidgets(WindowBase& w) + { + int32_t scrollIndex, width, height, scrollPositionChanged; + WidgetIndex widgetIndex; + Widget* widget; + + widgetIndex = 0; + scrollIndex = 0; + for (widget = w.widgets; widget->type != WindowWidgetType::Last; widget++, widgetIndex++) + { + if (widget->type != WindowWidgetType::Scroll) + continue; + + auto& scroll = w.scrolls[scrollIndex]; + ScreenSize scrollSize = w.OnScrollGetSize(scrollIndex); + width = scrollSize.width; + height = scrollSize.height; + + if (height == 0) + { + scroll.v_top = 0; + } + else if (width == 0) + { + scroll.h_left = 0; + } + width++; + height++; + + scrollPositionChanged = 0; + if ((widget->content & SCROLL_HORIZONTAL) && width != scroll.h_right) + { + scrollPositionChanged = 1; + scroll.h_right = width; + } + + if ((widget->content & SCROLL_VERTICAL) && height != scroll.v_bottom) + { + scrollPositionChanged = 1; + scroll.v_bottom = height; + } + + if (scrollPositionChanged) + { + WidgetScrollUpdateThumbs(w, widgetIndex); + w.Invalidate(); + } + scrollIndex++; + } + } + /** + * Initialises scroll widgets to their virtual size. + * rct2: 0x006EAEB8 + */ + void WindowInitScrollWidgets(WindowBase& w) + { + Widget* widget; + int32_t widget_index, scroll_index; + + widget_index = 0; + scroll_index = 0; + for (widget = w.widgets; widget->type != WindowWidgetType::Last; widget++) + { + if (widget->type != WindowWidgetType::Scroll) + { + widget_index++; + continue; + } + + auto& scroll = w.scrolls[scroll_index]; + scroll.flags = 0; + ScreenSize scrollSize = w.OnScrollGetSize(scroll_index); + scroll.h_left = 0; + scroll.h_right = scrollSize.width + 1; + scroll.v_top = 0; + scroll.v_bottom = scrollSize.height + 1; + + if (widget->content & SCROLL_HORIZONTAL) + scroll.flags |= HSCROLLBAR_VISIBLE; + if (widget->content & SCROLL_VERTICAL) + scroll.flags |= VSCROLLBAR_VISIBLE; + + WidgetScrollUpdateThumbs(w, widget_index); + + widget_index++; + scroll_index++; + } + } + + static void SnapLeft(WindowBase& w, int32_t proximity) + { + const auto* mainWindow = WindowGetMain(); + auto wBottom = w.windowPos.y + w.height; + auto wLeftProximity = w.windowPos.x - (proximity * 2); + auto wRightProximity = w.windowPos.x + (proximity * 2); + auto rightMost = INT32_MIN; + + WindowVisitEach([&](WindowBase* w2) { + if (w2 == &w || w2 == mainWindow) + return; + + auto right = w2->windowPos.x + w2->width; + + if (wBottom < w2->windowPos.y || w.windowPos.y > w2->windowPos.y + w2->height) + return; + + if (right < wLeftProximity || right > wRightProximity) + return; + + rightMost = std::max(rightMost, right); + }); + + if (0 >= wLeftProximity && 0 <= wRightProximity) + rightMost = std::max(rightMost, 0); + + if (rightMost != INT32_MIN) + w.windowPos.x = rightMost; + } + + static void SnapTop(WindowBase& w, int32_t proximity) + { + const auto* mainWindow = WindowGetMain(); + auto wRight = w.windowPos.x + w.width; + auto wTopProximity = w.windowPos.y - (proximity * 2); + auto wBottomProximity = w.windowPos.y + (proximity * 2); + auto bottomMost = INT32_MIN; + + WindowVisitEach([&](WindowBase* w2) { + if (w2 == &w || w2 == mainWindow) + return; + + auto bottom = w2->windowPos.y + w2->height; + + if (wRight < w2->windowPos.x || w.windowPos.x > w2->windowPos.x + w2->width) + return; + + if (bottom < wTopProximity || bottom > wBottomProximity) + return; + + bottomMost = std::max(bottomMost, bottom); + }); + + if (0 >= wTopProximity && 0 <= wBottomProximity) + bottomMost = std::max(bottomMost, 0); + + if (bottomMost != INT32_MIN) + w.windowPos.y = bottomMost; + } + + static void SnapRight(WindowBase& w, int32_t proximity) + { + const auto* mainWindow = WindowGetMain(); + auto wRight = w.windowPos.x + w.width; + auto wBottom = w.windowPos.y + w.height; + auto wLeftProximity = wRight - (proximity * 2); + auto wRightProximity = wRight + (proximity * 2); + auto leftMost = INT32_MAX; + + WindowVisitEach([&](WindowBase* w2) { + if (w2 == &w || w2 == mainWindow) + return; + + if (wBottom < w2->windowPos.y || w.windowPos.y > w2->windowPos.y + w2->height) + return; + + if (w2->windowPos.x < wLeftProximity || w2->windowPos.x > wRightProximity) + return; + + leftMost = std::min(leftMost, w2->windowPos.x); + }); + + auto screenWidth = ContextGetWidth(); + if (screenWidth >= wLeftProximity && screenWidth <= wRightProximity) + leftMost = std::min(leftMost, screenWidth); + + if (leftMost != INT32_MAX) + w.windowPos.x = leftMost - w.width; + } + + static void SnapBottom(WindowBase& w, int32_t proximity) + { + const auto* mainWindow = WindowGetMain(); + auto wRight = w.windowPos.x + w.width; + auto wBottom = w.windowPos.y + w.height; + auto wTopProximity = wBottom - (proximity * 2); + auto wBottomProximity = wBottom + (proximity * 2); + auto topMost = INT32_MAX; + + WindowVisitEach([&](WindowBase* w2) { + if (w2 == &w || w2 == mainWindow) + return; + + if (wRight < w2->windowPos.x || w.windowPos.x > w2->windowPos.x + w2->width) + return; + + if (w2->windowPos.y < wTopProximity || w2->windowPos.y > wBottomProximity) + return; + + topMost = std::min(topMost, w2->windowPos.y); + }); + + auto screenHeight = ContextGetHeight(); + if (screenHeight >= wTopProximity && screenHeight <= wBottomProximity) + topMost = std::min(topMost, screenHeight); + + if (topMost != INT32_MAX) + w.windowPos.y = topMost - w.height; + } + + void WindowMoveAndSnap(WindowBase& w, ScreenCoordsXY newWindowCoords, int32_t snapProximity) + { + auto originalPos = w.windowPos; + int32_t minY = (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) ? 1 : kTopToolbarHeight + 2; + + newWindowCoords.y = std::clamp(newWindowCoords.y, minY, ContextGetHeight() - 34); + + if (snapProximity > 0) + { + w.windowPos = newWindowCoords; + + SnapRight(w, snapProximity); + SnapBottom(w, snapProximity); + SnapLeft(w, snapProximity); + SnapTop(w, snapProximity); + + if (w.windowPos == originalPos) + return; + + newWindowCoords = w.windowPos; + w.windowPos = originalPos; + } + + WindowSetPosition(w, newWindowCoords); + } + + void WindowMovePosition(WindowBase& w, const ScreenCoordsXY& deltaCoords) + { + if (deltaCoords.x == 0 && deltaCoords.y == 0) + return; + + // Invalidate old region + w.Invalidate(); + + // Translate window and viewport + w.windowPos += deltaCoords; + if (w.viewport != nullptr) + { + w.viewport->pos += deltaCoords; + } + + // Invalidate new region + w.Invalidate(); + } + + void WindowSetPosition(WindowBase& w, const ScreenCoordsXY& screenCoords) + { + WindowMovePosition(w, screenCoords - w.windowPos); + } + + /** + * + * rct2: 0x006ED710 + * Called after a window resize to move windows if they + * are going to be out of sight. + */ + void WindowRelocateWindows(int32_t width, int32_t height) + { + int32_t new_location = 8; + WindowVisitEach([width, height, &new_location](WindowBase* w) { + // Work out if the window requires moving + if (w->windowPos.x + 10 < width) + { + if (w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)) + { + if (w->windowPos.y - 22 < height) + { + return; + } + } + if (w->windowPos.y + 10 < height) + { + return; + } + } + + // Calculate the new locations + auto newWinPos = w->windowPos; + w->windowPos = { new_location, new_location + kTopToolbarHeight + 1 }; + + // Move the next new location so windows are not directly on top + new_location += 8; + + // Adjust the viewport if required. + if (w->viewport != nullptr) + { + w->viewport->pos -= newWinPos - w->windowPos; + } + }); + } + + void WindowSetResize(WindowBase& w, int32_t minWidth, int32_t minHeight, int32_t maxWidth, int32_t maxHeight) + { + w.min_width = minWidth; + w.min_height = minHeight; + w.max_width = maxWidth; + w.max_height = maxHeight; + + // Clamp width and height to minimum and maximum + int32_t width = std::clamp(w.width, std::min(minWidth, maxWidth), std::max(minWidth, maxWidth)); + int32_t height = std::clamp(w.height, std::min(minHeight, maxHeight), std::max(minHeight, maxHeight)); + + // Resize window if size has changed + if (w.width != width || w.height != height) + { + w.Invalidate(); + w.width = width; + w.height = height; + w.Invalidate(); + } + } + + bool WindowCanResize(const WindowBase& w) + { + return (w.flags & WF_RESIZABLE) && (w.min_width != w.max_width || w.min_height != w.max_height); + } + + /** + * + * rct2: 0x006EA73F + */ + void InvalidateAllWindowsAfterInput() + { + WindowVisitEach([](WindowBase* w) { + Windows::WindowUpdateScrollWidgets(*w); + WindowInvalidatePressedImageButton(*w); + w->OnResize(); + }); + } } // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2-ui/interface/Window.h b/src/openrct2-ui/interface/Window.h index b107f167e4..34cc30ef2d 100644 --- a/src/openrct2-ui/interface/Window.h +++ b/src/openrct2-ui/interface/Window.h @@ -45,6 +45,42 @@ ScreenCoordsXY WindowGetViewportSoundIconPos(WindowBase& w); namespace OpenRCT2::Ui::Windows { + WindowBase* WindowCreate( + std::unique_ptr&& w, WindowClass cls, ScreenCoordsXY pos, int32_t width, int32_t height, uint32_t flags); + template::value>::type* = nullptr> + T* WindowCreate( + WindowClass cls, const ScreenCoordsXY& pos = {}, int32_t width = 0, int32_t height = 0, uint32_t flags = 0, + TArgs&&... args) + { + return static_cast(WindowCreate(std::make_unique(std::forward(args)...), cls, pos, width, height, flags)); + } + template::value>::type* = nullptr> + T* WindowCreate(WindowClass cls, int32_t width, int32_t height, uint32_t flags, TArgs&&... args) + { + return static_cast( + WindowCreate(std::make_unique(std::forward(args)...), cls, {}, width, height, flags | WF_AUTO_POSITION)); + } + template::value>::type* = nullptr> + T* WindowFocusOrCreate(WindowClass cls, const ScreenCoordsXY& pos, int32_t width, int32_t height, uint32_t flags = 0) + { + auto* w = WindowBringToFrontByClass(cls); + if (w == nullptr) + { + w = WindowCreate(cls, pos, width, height, flags); + } + return static_cast(w); + } + template::value>::type* = nullptr> + T* WindowFocusOrCreate(WindowClass cls, int32_t width, int32_t height, uint32_t flags = 0) + { + auto* w = WindowBringToFrontByClass(cls); + if (w == nullptr) + { + w = WindowCreate(cls, width, height, flags); + } + return static_cast(w); + } + void RideConstructionToolupdateEntranceExit(const ScreenCoordsXY& screenCoords); void RideConstructionToolupdateConstruct(const ScreenCoordsXY& screenCoords); void RideConstructionTooldownConstruct(const ScreenCoordsXY& screenCoords); @@ -86,4 +122,18 @@ namespace OpenRCT2::Ui::Windows bool IsUsingWidgetTextBox(); bool TextBoxCaretIsFlashed(); const WidgetIdentifier& GetCurrentTextBox(); + + void WindowResize(WindowBase& w, int32_t dw, int32_t dh); + void WindowInitScrollWidgets(WindowBase& w); + void WindowUpdateScrollWidgets(WindowBase& w); + + void WindowMovePosition(WindowBase& w, const ScreenCoordsXY& screenCoords); + void WindowSetPosition(WindowBase& w, const ScreenCoordsXY& screenCoords); + void WindowMoveAndSnap(WindowBase& w, ScreenCoordsXY newWindowCoords, int32_t snapProximity); + void WindowRelocateWindows(int32_t width, int32_t height); + + void WindowSetResize(WindowBase& w, int32_t minWidth, int32_t minHeight, int32_t maxWidth, int32_t maxHeight); + bool WindowCanResize(const WindowBase& w); + + void InvalidateAllWindowsAfterInput(); } // namespace OpenRCT2::Ui::Windows diff --git a/src/openrct2/interface/Widget.h b/src/openrct2/interface/Widget.h index 11d15c87c0..74289e9c68 100644 --- a/src/openrct2/interface/Widget.h +++ b/src/openrct2/interface/Widget.h @@ -62,5 +62,3 @@ enum SCROLL_VERTICAL = (1 << 1), SCROLL_BOTH = SCROLL_HORIZONTAL | SCROLL_VERTICAL }; - -void WidgetScrollUpdateThumbs(WindowBase& w, WidgetIndex widget_index); diff --git a/src/openrct2/interface/Window.cpp b/src/openrct2/interface/Window.cpp index 55a9d98c14..122c7d7e62 100644 --- a/src/openrct2/interface/Window.cpp +++ b/src/openrct2/interface/Window.cpp @@ -576,63 +576,6 @@ void WidgetInvalidateByNumber(WindowClass cls, rct_windownumber number, WidgetIn }); } -/** - * - * rct2: 0x006EAE4E - * - * @param w The window (esi). - */ -void WindowUpdateScrollWidgets(WindowBase& w) -{ - int32_t scrollIndex, width, height, scrollPositionChanged; - WidgetIndex widgetIndex; - Widget* widget; - - widgetIndex = 0; - scrollIndex = 0; - for (widget = w.widgets; widget->type != WindowWidgetType::Last; widget++, widgetIndex++) - { - if (widget->type != WindowWidgetType::Scroll) - continue; - - auto& scroll = w.scrolls[scrollIndex]; - ScreenSize scrollSize = w.OnScrollGetSize(scrollIndex); - width = scrollSize.width; - height = scrollSize.height; - - if (height == 0) - { - scroll.v_top = 0; - } - else if (width == 0) - { - scroll.h_left = 0; - } - width++; - height++; - - scrollPositionChanged = 0; - if ((widget->content & SCROLL_HORIZONTAL) && width != scroll.h_right) - { - scrollPositionChanged = 1; - scroll.h_right = width; - } - - if ((widget->content & SCROLL_VERTICAL) && height != scroll.v_bottom) - { - scrollPositionChanged = 1; - scroll.v_bottom = height; - } - - if (scrollPositionChanged) - { - WidgetScrollUpdateThumbs(w, widgetIndex); - w.Invalidate(); - } - scrollIndex++; - } -} - int32_t WindowGetScrollDataIndex(const WindowBase& w, WidgetIndex widget_index) { int32_t i, result; @@ -1211,78 +1154,6 @@ void WindowDrawViewport(DrawPixelInfo& dpi, WindowBase& w) ViewportRender(dpi, w.viewport, { { dpi.x, dpi.y }, { dpi.x + dpi.width, dpi.y + dpi.height } }); } -void WindowSetPosition(WindowBase& w, const ScreenCoordsXY& screenCoords) -{ - WindowMovePosition(w, screenCoords - w.windowPos); -} - -void WindowMovePosition(WindowBase& w, const ScreenCoordsXY& deltaCoords) -{ - if (deltaCoords.x == 0 && deltaCoords.y == 0) - return; - - // Invalidate old region - w.Invalidate(); - - // Translate window and viewport - w.windowPos += deltaCoords; - if (w.viewport != nullptr) - { - w.viewport->pos += deltaCoords; - } - - // Invalidate new region - w.Invalidate(); -} - -void WindowResize(WindowBase& w, int32_t dw, int32_t dh) -{ - if (dw == 0 && dh == 0) - return; - - // Invalidate old region - w.Invalidate(); - - // Clamp new size to minimum and maximum - w.width = std::clamp(w.width + dw, w.min_width, w.max_width); - w.height = std::clamp(w.height + dh, w.min_height, w.max_height); - - w.OnResize(); - w.OnPrepareDraw(); - - // Update scroll widgets - for (auto& scroll : w.scrolls) - { - scroll.h_right = WINDOW_SCROLL_UNDEFINED; - scroll.v_bottom = WINDOW_SCROLL_UNDEFINED; - } - WindowUpdateScrollWidgets(w); - - // Invalidate new region - w.Invalidate(); -} - -void WindowSetResize(WindowBase& w, int32_t minWidth, int32_t minHeight, int32_t maxWidth, int32_t maxHeight) -{ - w.min_width = minWidth; - w.min_height = minHeight; - w.max_width = maxWidth; - w.max_height = maxHeight; - - // Clamp width and height to minimum and maximum - int32_t width = std::clamp(w.width, std::min(minWidth, maxWidth), std::max(minWidth, maxWidth)); - int32_t height = std::clamp(w.height, std::min(minHeight, maxHeight), std::max(minHeight, maxHeight)); - - // Resize window if size has changed - if (w.width != width || w.height != height) - { - w.Invalidate(); - w.width = width; - w.height = height; - w.Invalidate(); - } -} - /** * * rct2: 0x006EE212 @@ -1345,47 +1216,6 @@ void ToolCancel() } } -/** - * - * rct2: 0x006ED710 - * Called after a window resize to move windows if they - * are going to be out of sight. - */ -void WindowRelocateWindows(int32_t width, int32_t height) -{ - int32_t new_location = 8; - WindowVisitEach([width, height, &new_location](WindowBase* w) { - // Work out if the window requires moving - if (w->windowPos.x + 10 < width) - { - if (w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)) - { - if (w->windowPos.y - 22 < height) - { - return; - } - } - if (w->windowPos.y + 10 < height) - { - return; - } - } - - // Calculate the new locations - auto newWinPos = w->windowPos; - w->windowPos = { new_location, new_location + kTopToolbarHeight + 1 }; - - // Move the next new location so windows are not directly on top - new_location += 8; - - // Adjust the viewport if required. - if (w->viewport != nullptr) - { - w->viewport->pos -= newWinPos - w->windowPos; - } - }); -} - /** * rct2: 0x0066B905 */ @@ -1515,157 +1345,6 @@ void WindowUpdateViewportRideMusic() } } -static void window_snap_left(WindowBase& w, int32_t proximity) -{ - const auto* mainWindow = WindowGetMain(); - auto wBottom = w.windowPos.y + w.height; - auto wLeftProximity = w.windowPos.x - (proximity * 2); - auto wRightProximity = w.windowPos.x + (proximity * 2); - auto rightMost = INT32_MIN; - - WindowVisitEach([&](WindowBase* w2) { - if (w2 == &w || w2 == mainWindow) - return; - - auto right = w2->windowPos.x + w2->width; - - if (wBottom < w2->windowPos.y || w.windowPos.y > w2->windowPos.y + w2->height) - return; - - if (right < wLeftProximity || right > wRightProximity) - return; - - rightMost = std::max(rightMost, right); - }); - - if (0 >= wLeftProximity && 0 <= wRightProximity) - rightMost = std::max(rightMost, 0); - - if (rightMost != INT32_MIN) - w.windowPos.x = rightMost; -} - -static void window_snap_top(WindowBase& w, int32_t proximity) -{ - const auto* mainWindow = WindowGetMain(); - auto wRight = w.windowPos.x + w.width; - auto wTopProximity = w.windowPos.y - (proximity * 2); - auto wBottomProximity = w.windowPos.y + (proximity * 2); - auto bottomMost = INT32_MIN; - - WindowVisitEach([&](WindowBase* w2) { - if (w2 == &w || w2 == mainWindow) - return; - - auto bottom = w2->windowPos.y + w2->height; - - if (wRight < w2->windowPos.x || w.windowPos.x > w2->windowPos.x + w2->width) - return; - - if (bottom < wTopProximity || bottom > wBottomProximity) - return; - - bottomMost = std::max(bottomMost, bottom); - }); - - if (0 >= wTopProximity && 0 <= wBottomProximity) - bottomMost = std::max(bottomMost, 0); - - if (bottomMost != INT32_MIN) - w.windowPos.y = bottomMost; -} - -static void window_snap_right(WindowBase& w, int32_t proximity) -{ - const auto* mainWindow = WindowGetMain(); - auto wRight = w.windowPos.x + w.width; - auto wBottom = w.windowPos.y + w.height; - auto wLeftProximity = wRight - (proximity * 2); - auto wRightProximity = wRight + (proximity * 2); - auto leftMost = INT32_MAX; - - WindowVisitEach([&](WindowBase* w2) { - if (w2 == &w || w2 == mainWindow) - return; - - if (wBottom < w2->windowPos.y || w.windowPos.y > w2->windowPos.y + w2->height) - return; - - if (w2->windowPos.x < wLeftProximity || w2->windowPos.x > wRightProximity) - return; - - leftMost = std::min(leftMost, w2->windowPos.x); - }); - - auto screenWidth = ContextGetWidth(); - if (screenWidth >= wLeftProximity && screenWidth <= wRightProximity) - leftMost = std::min(leftMost, screenWidth); - - if (leftMost != INT32_MAX) - w.windowPos.x = leftMost - w.width; -} - -static void window_snap_bottom(WindowBase& w, int32_t proximity) -{ - const auto* mainWindow = WindowGetMain(); - auto wRight = w.windowPos.x + w.width; - auto wBottom = w.windowPos.y + w.height; - auto wTopProximity = wBottom - (proximity * 2); - auto wBottomProximity = wBottom + (proximity * 2); - auto topMost = INT32_MAX; - - WindowVisitEach([&](WindowBase* w2) { - if (w2 == &w || w2 == mainWindow) - return; - - if (wRight < w2->windowPos.x || w.windowPos.x > w2->windowPos.x + w2->width) - return; - - if (w2->windowPos.y < wTopProximity || w2->windowPos.y > wBottomProximity) - return; - - topMost = std::min(topMost, w2->windowPos.y); - }); - - auto screenHeight = ContextGetHeight(); - if (screenHeight >= wTopProximity && screenHeight <= wBottomProximity) - topMost = std::min(topMost, screenHeight); - - if (topMost != INT32_MAX) - w.windowPos.y = topMost - w.height; -} - -void WindowMoveAndSnap(WindowBase& w, ScreenCoordsXY newWindowCoords, int32_t snapProximity) -{ - auto originalPos = w.windowPos; - int32_t minY = (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) ? 1 : kTopToolbarHeight + 2; - - newWindowCoords.y = std::clamp(newWindowCoords.y, minY, ContextGetHeight() - 34); - - if (snapProximity > 0) - { - w.windowPos = newWindowCoords; - - window_snap_right(w, snapProximity); - window_snap_bottom(w, snapProximity); - window_snap_left(w, snapProximity); - window_snap_top(w, snapProximity); - - if (w.windowPos == originalPos) - return; - - newWindowCoords = w.windowPos; - w.windowPos = originalPos; - } - - WindowSetPosition(w, newWindowCoords); -} - -int32_t WindowCanResize(const WindowBase& w) -{ - return (w.flags & WF_RESIZABLE) && (w.min_width != w.max_width || w.min_height != w.max_height); -} - /** * * rct2: 0x006EE3C3 @@ -1803,74 +1482,6 @@ Viewport* WindowGetViewport(WindowBase* w) return w->viewport; } -/** - * - * rct2: 0x006EAF26 - */ -void WidgetScrollUpdateThumbs(WindowBase& w, WidgetIndex widget_index) -{ - const auto& widget = w.widgets[widget_index]; - auto& scroll = w.scrolls[WindowGetScrollDataIndex(w, widget_index)]; - - if (scroll.flags & HSCROLLBAR_VISIBLE) - { - int32_t view_size = widget.width() - 21; - if (scroll.flags & VSCROLLBAR_VISIBLE) - view_size -= 11; - int32_t x = scroll.h_left * view_size; - if (scroll.h_right != 0) - x /= scroll.h_right; - scroll.h_thumb_left = x + 11; - - x = widget.width() - 2; - if (scroll.flags & VSCROLLBAR_VISIBLE) - x -= 11; - x += scroll.h_left; - if (scroll.h_right != 0) - x = (x * view_size) / scroll.h_right; - x += 11; - view_size += 10; - scroll.h_thumb_right = std::min(x, view_size); - - if (scroll.h_thumb_right - scroll.h_thumb_left < 20) - { - double barPosition = (scroll.h_thumb_right * 1.0) / view_size; - - scroll.h_thumb_left = static_cast(std::lround(scroll.h_thumb_left - (20 * barPosition))); - scroll.h_thumb_right = static_cast(std::lround(scroll.h_thumb_right + (20 * (1 - barPosition)))); - } - } - - if (scroll.flags & VSCROLLBAR_VISIBLE) - { - int32_t view_size = widget.height() - 21; - if (scroll.flags & HSCROLLBAR_VISIBLE) - view_size -= 11; - int32_t y = scroll.v_top * view_size; - if (scroll.v_bottom != 0) - y /= scroll.v_bottom; - scroll.v_thumb_top = y + 11; - - y = widget.height() - 2; - if (scroll.flags & HSCROLLBAR_VISIBLE) - y -= 11; - y += scroll.v_top; - if (scroll.v_bottom != 0) - y = (y * view_size) / scroll.v_bottom; - y += 11; - view_size += 10; - scroll.v_thumb_bottom = std::min(y, view_size); - - if (scroll.v_thumb_bottom - scroll.v_thumb_top < 20) - { - double barPosition = (scroll.v_thumb_bottom * 1.0) / view_size; - - scroll.v_thumb_top = static_cast(std::lround(scroll.v_thumb_top - (20 * barPosition))); - scroll.v_thumb_bottom = static_cast(std::lround(scroll.v_thumb_bottom + (20 * (1 - barPosition)))); - } - } -} - void WindowBase::ResizeFrame() { // Frame diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index ccb8f87b88..31e29718a8 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -509,41 +509,6 @@ WindowBase* WindowBringToFrontByClass(WindowClass cls); WindowBase* WindowBringToFrontByClassWithFlags(WindowClass cls, uint16_t flags); WindowBase* WindowBringToFrontByNumber(WindowClass cls, rct_windownumber number); -WindowBase* WindowCreate( - std::unique_ptr&& w, WindowClass cls, ScreenCoordsXY pos, int32_t width, int32_t height, uint32_t flags); -template::value>::type* = nullptr> -T* WindowCreate( - WindowClass cls, const ScreenCoordsXY& pos = {}, int32_t width = 0, int32_t height = 0, uint32_t flags = 0, TArgs&&... args) -{ - return static_cast(WindowCreate(std::make_unique(std::forward(args)...), cls, pos, width, height, flags)); -} -template::value>::type* = nullptr> -T* WindowCreate(WindowClass cls, int32_t width, int32_t height, uint32_t flags, TArgs&&... args) -{ - return static_cast( - WindowCreate(std::make_unique(std::forward(args)...), cls, {}, width, height, flags | WF_AUTO_POSITION)); -} -template::value>::type* = nullptr> -T* WindowFocusOrCreate(WindowClass cls, const ScreenCoordsXY& pos, int32_t width, int32_t height, uint32_t flags = 0) -{ - auto* w = WindowBringToFrontByClass(cls); - if (w == nullptr) - { - w = WindowCreate(cls, pos, width, height, flags); - } - return static_cast(w); -} -template::value>::type* = nullptr> -T* WindowFocusOrCreate(WindowClass cls, int32_t width, int32_t height, uint32_t flags = 0) -{ - auto* w = WindowBringToFrontByClass(cls); - if (w == nullptr) - { - w = WindowCreate(cls, width, height, flags); - } - return static_cast(w); -} - void WindowClose(WindowBase& window); void WindowFlushDead(); void WindowCloseByClass(WindowClass cls); @@ -566,8 +531,7 @@ void WindowInvalidateAll(); void WidgetInvalidate(WindowBase& w, WidgetIndex widgetIndex); void WidgetInvalidateByClass(WindowClass cls, WidgetIndex widgetIndex); void WidgetInvalidateByNumber(WindowClass cls, rct_windownumber number, WidgetIndex widgetIndex); -void WindowInitScrollWidgets(WindowBase& w); -void WindowUpdateScrollWidgets(WindowBase& w); + int32_t WindowGetScrollDataIndex(const WindowBase& w, WidgetIndex widget_index); void WindowPushOthersRight(WindowBase& w); @@ -590,11 +554,6 @@ void WindowDraw(DrawPixelInfo& dpi, WindowBase& w, int32_t left, int32_t top, in void WindowDrawWidgets(WindowBase& w, DrawPixelInfo& dpi); void WindowDrawViewport(DrawPixelInfo& dpi, WindowBase& w); -void WindowSetPosition(WindowBase& w, const ScreenCoordsXY& screenCoords); -void WindowMovePosition(WindowBase& w, const ScreenCoordsXY& screenCoords); -void WindowResize(WindowBase& w, int32_t dw, int32_t dh); -void WindowSetResize(WindowBase& w, int32_t minWidth, int32_t minHeight, int32_t maxWidth, int32_t maxHeight); - bool ToolSet(const WindowBase& w, WidgetIndex widgetIndex, Tool tool); void ToolCancel(); @@ -605,16 +564,11 @@ void WindowUpdateViewportRideMusic(); Viewport* WindowGetViewport(WindowBase* window); // Open window functions -void WindowRelocateWindows(int32_t width, int32_t height); void WindowResizeGui(int32_t width, int32_t height); void WindowResizeGuiScenarioEditor(int32_t width, int32_t height); -void InvalidateAllWindowsAfterInput(); void TextinputCancel(); -void WindowMoveAndSnap(WindowBase& w, ScreenCoordsXY newWindowCoords, int32_t snapProximity); -int32_t WindowCanResize(const WindowBase& w); - bool WindowIsVisible(WindowBase& w); bool SceneryToolIsActive();