Refactor window deletion to be delayed

This commit is contained in:
ζeh Matt 2023-06-27 03:50:29 +03:00
parent 3b7975f4a0
commit 5497601d42
No known key found for this signature in database
GPG Key ID: 18CE582C71A225B0
2 changed files with 49 additions and 61 deletions

View File

@ -80,8 +80,7 @@ static constexpr float window_scroll_locations[][2] = {
namespace WindowCloseFlags namespace WindowCloseFlags
{ {
static constexpr uint32_t None = 0; static constexpr uint32_t None = 0;
static constexpr uint32_t IterateReverse = (1 << 0); static constexpr uint32_t CloseSingle = (1 << 0);
static constexpr uint32_t CloseSingle = (1 << 1);
} // namespace WindowCloseFlags } // namespace WindowCloseFlags
static void WindowDrawCore(DrawPixelInfo& dpi, WindowBase& w, int32_t left, int32_t top, int32_t right, int32_t bottom); static void WindowDrawCore(DrawPixelInfo& dpi, WindowBase& w, int32_t left, int32_t top, int32_t right, int32_t bottom);
@ -99,6 +98,8 @@ void WindowVisitEach(std::function<void(WindowBase*)> func)
auto windowList = g_window_list; auto windowList = g_window_list;
for (auto& w : windowList) for (auto& w : windowList)
{ {
if (w->flags & WF_DEAD)
continue;
func(w.get()); func(w.get());
} }
} }
@ -129,6 +130,8 @@ void WindowUpdateAllViewports()
*/ */
void WindowUpdateAll() void WindowUpdateAll()
{ {
WindowFlushDead();
// Periodic update happens every second so 40 ticks. // Periodic update happens every second so 40 ticks.
if (gCurrentRealTimeTicks >= gWindowUpdateTicks) if (gCurrentRealTimeTicks >= gWindowUpdateTicks)
{ {
@ -165,6 +168,8 @@ static void WindowCloseSurplus(int32_t cap, WindowClass avoid_classification)
WindowBase* foundW{}; WindowBase* foundW{};
for (auto& w : g_window_list) 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))) if (!(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT | WF_NO_AUTO_CLOSE)))
{ {
foundW = w.get(); foundW = w.get();
@ -205,76 +210,40 @@ void WindowSetWindowLimit(int32_t value)
*/ */
void WindowClose(WindowBase& w) void WindowClose(WindowBase& w)
{ {
auto itWindow = WindowGetIterator(&w); WindowEventCloseCall(&w);
if (itWindow == g_window_list.end())
return;
// Explicit copy of the shared ptr to keep the memory valid.
std::shared_ptr<WindowBase> window = *itWindow;
WindowEventCloseCall(window.get());
// Remove viewport // Remove viewport
window->RemoveViewport(); w.RemoveViewport();
// Invalidate the window (area) // Invalidate the window (area)
window->Invalidate(); w.Invalidate();
// The window list may have been modified in the close event w.flags |= WF_DEAD;
itWindow = WindowGetIterator(&w); }
if (itWindow != g_window_list.end())
g_window_list.erase(itWindow); void WindowFlushDead()
{
// Remove all windows in g_window_list that have the WF_DEAD flag
g_window_list.remove_if([](const std::shared_ptr<WindowBase>& w) -> bool { return w->flags & WF_DEAD; });
} }
template<typename TPred> static void WindowCloseByCondition(TPred pred, uint32_t flags = WindowCloseFlags::None) template<typename TPred> static void WindowCloseByCondition(TPred pred, uint32_t flags = WindowCloseFlags::None)
{ {
bool listUpdated; for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); ++it)
do
{ {
listUpdated = false; auto& wnd = *(*it);
if (wnd.flags & WF_DEAD)
continue;
auto closeSingle = [&](std::shared_ptr<WindowBase> window) -> bool { if (pred(&wnd))
if (!pred(window.get())) {
WindowClose(wnd);
if (flags & WindowCloseFlags::CloseSingle)
{ {
return false; return;
} }
}
// Keep track of current amount, if a new window is created upon closing }
// we need to break this current iteration and restart.
size_t previousCount = g_window_list.size();
WindowClose(*window.get());
if ((flags & WindowCloseFlags::CloseSingle) != 0)
{
// Only close a single one.
return true;
}
if (previousCount >= g_window_list.size())
{
// A new window was created during the close event.
return true;
}
// Keep closing windows.
return false;
};
// The closest to something like for_each_if is using find_if in order to avoid duplicate code
// to change the loop direction.
auto windowList = g_window_list;
if ((flags & WindowCloseFlags::IterateReverse) != 0)
listUpdated = std::find_if(windowList.rbegin(), windowList.rend(), closeSingle) != windowList.rend();
else
listUpdated = std::find_if(windowList.begin(), windowList.end(), closeSingle) != windowList.end();
// If requested to close only a single window and a new window was created during close
// we ignore it.
if ((flags & WindowCloseFlags::CloseSingle) != 0)
break;
} while (listUpdated);
} }
/** /**
@ -314,6 +283,8 @@ WindowBase* WindowFindByClass(WindowClass cls)
{ {
for (auto& w : g_window_list) for (auto& w : g_window_list)
{ {
if (w->flags & WF_DEAD)
continue;
if (w->classification == cls) if (w->classification == cls)
{ {
return w.get(); return w.get();
@ -333,6 +304,8 @@ WindowBase* WindowFindByNumber(WindowClass cls, rct_windownumber number)
{ {
for (auto& w : g_window_list) for (auto& w : g_window_list)
{ {
if (w->flags & WF_DEAD)
continue;
if (w->classification == cls && w->number == number) if (w->classification == cls && w->number == number)
{ {
return w.get(); return w.get();
@ -363,7 +336,7 @@ void WindowCloseTop()
} }
auto pred = [](WindowBase* w) -> bool { return !(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)); }; auto pred = [](WindowBase* w) -> bool { return !(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)); };
WindowCloseByCondition(pred, WindowCloseFlags::CloseSingle | WindowCloseFlags::IterateReverse); WindowCloseByCondition(pred, WindowCloseFlags::CloseSingle);
} }
/** /**
@ -380,7 +353,6 @@ void WindowCloseAll()
void WindowCloseAllExceptClass(WindowClass cls) void WindowCloseAllExceptClass(WindowClass cls)
{ {
WindowCloseByClass(WindowClass::Dropdown); WindowCloseByClass(WindowClass::Dropdown);
WindowCloseByCondition([cls](WindowBase* w) -> bool { WindowCloseByCondition([cls](WindowBase* w) -> bool {
return w->classification != cls && !(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)); return w->classification != cls && !(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT));
}); });
@ -416,6 +388,9 @@ WindowBase* WindowFindFromPoint(const ScreenCoordsXY& screenCoords)
for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++)
{ {
auto& w = *it; auto& w = *it;
if (w->flags & WF_DEAD)
continue;
if (screenCoords.x < w->windowPos.x || screenCoords.x >= w->windowPos.x + w->width || screenCoords.y < w->windowPos.y if (screenCoords.x < w->windowPos.x || screenCoords.x >= w->windowPos.x + w->width || screenCoords.y < w->windowPos.y
|| screenCoords.y >= w->windowPos.y + w->height) || screenCoords.y >= w->windowPos.y + w->height)
continue; continue;
@ -811,6 +786,8 @@ WindowBase* WindowGetMain()
{ {
for (auto& w : g_window_list) for (auto& w : g_window_list)
{ {
if (w->flags & WF_DEAD)
continue;
if (w->classification == WindowClass::MainWindow) if (w->classification == WindowClass::MainWindow)
{ {
return w.get(); return w.get();
@ -1198,6 +1175,8 @@ static void WindowDrawCore(DrawPixelInfo& dpi, WindowBase& w, int32_t left, int3
for (auto it = WindowGetIterator(&w); it != g_window_list.end(); it++) for (auto it = WindowGetIterator(&w); it != g_window_list.end(); it++)
{ {
auto* v = (*it).get(); auto* v = (*it).get();
if (v->flags & WF_DEAD)
continue;
if ((&w == v || (v->flags & WF_TRANSPARENT)) && WindowIsVisible(*v)) if ((&w == v || (v->flags & WF_TRANSPARENT)) && WindowIsVisible(*v))
{ {
WindowDrawSingle(dpi, *v, left, top, right, bottom); WindowDrawSingle(dpi, *v, left, top, right, bottom);
@ -1943,6 +1922,8 @@ bool WindowIsVisible(WindowBase& w)
for (auto it = std::next(itPos); it != g_window_list.end(); it++) for (auto it = std::next(itPos); it != g_window_list.end(); it++)
{ {
auto& w_other = *(*it); auto& w_other = *(*it);
if (w_other.flags & WF_DEAD)
continue;
// if covered by a higher window, no rendering needed // if covered by a higher window, no rendering needed
if (w_other.windowPos.x <= w.windowPos.x && w_other.windowPos.y <= w.windowPos.y if (w_other.windowPos.x <= w.windowPos.x && w_other.windowPos.y <= w.windowPos.y
@ -1989,6 +1970,8 @@ Viewport* WindowGetPreviousViewport(Viewport* current)
for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++)
{ {
auto& w = **it; auto& w = **it;
if (w.flags & WF_DEAD)
continue;
if (w.viewport != nullptr) if (w.viewport != nullptr)
{ {
if (foundPrevious) if (foundPrevious)
@ -2050,6 +2033,9 @@ WindowBase* WindowGetListening()
for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++) for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++)
{ {
auto& w = **it; auto& w = **it;
if (w.flags & WF_DEAD)
continue;
auto viewport = w.viewport; auto viewport = w.viewport;
if (viewport != nullptr) if (viewport != nullptr)
{ {

View File

@ -294,6 +294,7 @@ enum WINDOW_FLAGS
WF_SCROLLING_TO_LOCATION = (1 << 3), WF_SCROLLING_TO_LOCATION = (1 << 3),
WF_TRANSPARENT = (1 << 4), WF_TRANSPARENT = (1 << 4),
WF_NO_BACKGROUND = (1 << 5), // Instead of half transparency, completely remove the window background WF_NO_BACKGROUND = (1 << 5), // Instead of half transparency, completely remove the window background
WF_DEAD = (1U << 6), // Window is closed and will be deleted in the next update.
WF_7 = (1 << 7), WF_7 = (1 << 7),
WF_RESIZABLE = (1 << 8), WF_RESIZABLE = (1 << 8),
WF_NO_AUTO_CLOSE = (1 << 9), // Don't auto close this window if too many windows are open WF_NO_AUTO_CLOSE = (1 << 9), // Don't auto close this window if too many windows are open
@ -584,6 +585,7 @@ T* WindowFocusOrCreate(WindowClass cls, int32_t width, int32_t height, uint32_t
} }
void WindowClose(WindowBase& window); void WindowClose(WindowBase& window);
void WindowFlushDead();
void WindowCloseByClass(WindowClass cls); void WindowCloseByClass(WindowClass cls);
void WindowCloseByNumber(WindowClass cls, rct_windownumber number); void WindowCloseByNumber(WindowClass cls, rct_windownumber number);
void WindowCloseByNumber(WindowClass cls, EntityId number); void WindowCloseByNumber(WindowClass cls, EntityId number);