diff --git a/src/xenia/ui/window.cc b/src/xenia/ui/window.cc index 020256dea..0d27eaec0 100644 --- a/src/xenia/ui/window.cc +++ b/src/xenia/ui/window.cc @@ -38,6 +38,10 @@ Window::~Window() { } void Window::AttachListener(WindowListener* listener) { + if (in_listener_loop_) { + pending_listener_attaches_.push_back(listener); + return; + } auto it = std::find(listeners_.begin(), listeners_.end(), listener); if (it != listeners_.end()) { return; @@ -47,6 +51,10 @@ void Window::AttachListener(WindowListener* listener) { } void Window::DetachListener(WindowListener* listener) { + if (in_listener_loop_) { + pending_listener_detaches_.push_back(listener); + return; + } auto it = std::find(listeners_.begin(), listeners_.end(), listener); if (it == listeners_.end()) { return; @@ -54,6 +62,46 @@ void Window::DetachListener(WindowListener* listener) { listeners_.erase(it); } +void Window::ForEachListener(std::function fn) { + assert_false(in_listener_loop_); + in_listener_loop_ = true; + for (auto listener : listeners_) { + fn(listener); + } + in_listener_loop_ = false; + while (!pending_listener_attaches_.empty()) { + auto listener = pending_listener_attaches_.back(); + pending_listener_attaches_.pop_back(); + AttachListener(listener); + } + while (!pending_listener_detaches_.empty()) { + auto listener = pending_listener_detaches_.back(); + pending_listener_detaches_.pop_back(); + DetachListener(listener); + } +} + +void Window::TryForEachListener(std::function fn) { + assert_false(in_listener_loop_); + in_listener_loop_ = true; + for (auto listener : listeners_) { + if (fn(listener)) { + break; + } + } + in_listener_loop_ = false; + while (!pending_listener_attaches_.empty()) { + auto listener = pending_listener_attaches_.back(); + pending_listener_attaches_.pop_back(); + AttachListener(listener); + } + while (!pending_listener_detaches_.empty()) { + auto listener = pending_listener_detaches_.back(); + pending_listener_detaches_.pop_back(); + DetachListener(listener); + } +} + void Window::set_imgui_input_enabled(bool value) { if (value == is_imgui_input_enabled_) { return; @@ -75,20 +123,14 @@ bool Window::MakeReady() { } void Window::OnMainMenuChange() { - for (auto listener : listeners_) { - listener->OnMainMenuChange(); - } + ForEachListener([](auto listener) { listener->OnMainMenuChange(); }); } void Window::OnClose() { UIEvent e(this); - for (auto listener : listeners_) { - listener->OnClosing(&e); - } + ForEachListener([&e](auto listener) { listener->OnClosing(&e); }); on_closing(&e); - for (auto listener : listeners_) { - listener->OnClosed(&e); - } + ForEachListener([&e](auto listener) { listener->OnClosed(&e); }); on_closed(&e); } @@ -111,15 +153,11 @@ void Window::Layout() { void Window::Invalidate() {} void Window::OnResize(UIEvent* e) { - for (auto listener : listeners_) { - listener->OnResize(e); - } + ForEachListener([e](auto listener) { listener->OnResize(e); }); } void Window::OnLayout(UIEvent* e) { - for (auto listener : listeners_) { - listener->OnLayout(e); - } + ForEachListener([e](auto listener) { listener->OnLayout(e); }); } void Window::OnPaint(UIEvent* e) { @@ -154,21 +192,15 @@ void Window::OnPaint(UIEvent* e) { ImGui::NewFrame(); context_->BeginSwap(); - for (auto listener : listeners_) { - listener->OnPainting(e); - } + ForEachListener([e](auto listener) { listener->OnPainting(e); }); on_painting(e); - for (auto listener : listeners_) { - listener->OnPaint(e); - } + ForEachListener([e](auto listener) { listener->OnPaint(e); }); on_paint(e); // Flush ImGui buffers before we swap. ImGui::Render(); - for (auto listener : listeners_) { - listener->OnPainted(e); - } + ForEachListener([e](auto listener) { listener->OnPainted(e); }); on_painted(e); context_->EndSwap(); @@ -180,21 +212,15 @@ void Window::OnPaint(UIEvent* e) { } void Window::OnVisible(UIEvent* e) { - for (auto listener : listeners_) { - listener->OnVisible(e); - } + ForEachListener([e](auto listener) { listener->OnVisible(e); }); } void Window::OnHidden(UIEvent* e) { - for (auto listener : listeners_) { - listener->OnHidden(e); - } + ForEachListener([e](auto listener) { listener->OnHidden(e); }); } void Window::OnGotFocus(UIEvent* e) { - for (auto listener : listeners_) { - listener->OnGotFocus(e); - } + ForEachListener([e](auto listener) { listener->OnGotFocus(e); }); } void Window::OnLostFocus(UIEvent* e) { @@ -202,9 +228,7 @@ void Window::OnLostFocus(UIEvent* e) { modifier_cntrl_pressed_ = false; modifier_alt_pressed_ = false; modifier_super_pressed_ = false; - for (auto listener : listeners_) { - listener->OnLostFocus(e); - } + ForEachListener([e](auto listener) { listener->OnLostFocus(e); }); } void Window::OnKeyPress(KeyEvent* e, bool is_down, bool is_char) { @@ -232,12 +256,10 @@ void Window::OnKeyDown(KeyEvent* e) { if (e->is_handled()) { return; } - for (auto listener : listeners_) { + TryForEachListener([e](auto listener) { listener->OnKeyDown(e); - if (e->is_handled()) { - return; - } - } + return e->is_handled(); + }); OnKeyPress(e, true, false); } @@ -246,21 +268,17 @@ void Window::OnKeyUp(KeyEvent* e) { if (e->is_handled()) { return; } - for (auto listener : listeners_) { + TryForEachListener([e](auto listener) { listener->OnKeyUp(e); - if (e->is_handled()) { - return; - } - } + return e->is_handled(); + }); OnKeyPress(e, false, false); } void Window::OnKeyChar(KeyEvent* e) { OnKeyPress(e, true, true); on_key_char(e); - for (auto listener : listeners_) { - listener->OnKeyChar(e); - } + ForEachListener([e](auto listener) { listener->OnKeyChar(e); }); OnKeyPress(e, false, true); } @@ -269,12 +287,10 @@ void Window::OnMouseDown(MouseEvent* e) { if (e->is_handled()) { return; } - for (auto listener : listeners_) { + TryForEachListener([e](auto listener) { listener->OnMouseDown(e); - if (e->is_handled()) { - return; - } - } + return e->is_handled(); + }); } void Window::OnMouseMove(MouseEvent* e) { @@ -282,12 +298,10 @@ void Window::OnMouseMove(MouseEvent* e) { if (e->is_handled()) { return; } - for (auto listener : listeners_) { + TryForEachListener([e](auto listener) { listener->OnMouseMove(e); - if (e->is_handled()) { - return; - } - } + return e->is_handled(); + }); } void Window::OnMouseUp(MouseEvent* e) { @@ -295,12 +309,10 @@ void Window::OnMouseUp(MouseEvent* e) { if (e->is_handled()) { return; } - for (auto listener : listeners_) { + TryForEachListener([e](auto listener) { listener->OnMouseUp(e); - if (e->is_handled()) { - return; - } - } + return e->is_handled(); + }); } void Window::OnMouseWheel(MouseEvent* e) { @@ -308,12 +320,10 @@ void Window::OnMouseWheel(MouseEvent* e) { if (e->is_handled()) { return; } - for (auto listener : listeners_) { + TryForEachListener([e](auto listener) { listener->OnMouseWheel(e); - if (e->is_handled()) { - return; - } - } + return e->is_handled(); + }); } } // namespace ui diff --git a/src/xenia/ui/window.h b/src/xenia/ui/window.h index cd7c64d4c..cd68db447 100644 --- a/src/xenia/ui/window.h +++ b/src/xenia/ui/window.h @@ -112,6 +112,9 @@ class Window { protected: Window(Loop* loop, const std::wstring& title); + void ForEachListener(std::function fn); + void TryForEachListener(std::function fn); + virtual bool MakeReady(); virtual bool OnCreate(); @@ -164,7 +167,10 @@ class Window { bool modifier_super_pressed_ = false; // All currently-attached listeners that get event notifications. + bool in_listener_loop_ = false; std::vector listeners_; + std::vector pending_listener_attaches_; + std::vector pending_listener_detaches_; }; } // namespace ui