[UI] Fix ClearInput not called in ImGuiDrawer after deferred dialog removal

Also cleanup the code involved in dialog registration, and update the explanation of why dialog removal is delayed until the end of drawing (the original was written back when window listener and UI drawer callback registration during the execution of the callbacks was deferred, but that was wrong as that might result in execution of callbacks belonging to now-deleted objects).
This commit is contained in:
Triang3l 2022-10-31 18:57:54 +03:00
parent a37b57ca8d
commit 778333b1b5
2 changed files with 35 additions and 23 deletions

View File

@ -62,11 +62,11 @@ void ImGuiDrawer::AddDialog(ImGuiDialog* dialog) {
dialogs_.cend()) { dialogs_.cend()) {
return; return;
} }
if (dialog_loop_next_index_ == SIZE_MAX && dialogs_.empty()) { if (dialogs_.empty() && !IsDrawingDialogs()) {
// First dialog added. dialog_loop_next_index_ == SIZE_MAX is also checked // First dialog added. !IsDrawingDialogs() is also checked because in a
// because in a situation of removing the only dialog, then adding a dialog, // situation of removing the only dialog, then adding a dialog, from within
// from within a dialog's Draw function, the removal would not cause the // a dialog's Draw function, re-registering the ImGuiDrawer may result in
// listener and the drawer to be removed (it's deferred in this case). // ImGui being drawn multiple times in the current frame.
window_->AddInputListener(this, z_order_); window_->AddInputListener(this, z_order_);
if (presenter_) { if (presenter_) {
presenter_->AddUIDrawerFromUIThread(this, z_order_); presenter_->AddUIDrawerFromUIThread(this, z_order_);
@ -81,7 +81,7 @@ void ImGuiDrawer::RemoveDialog(ImGuiDialog* dialog) {
if (it == dialogs_.cend()) { if (it == dialogs_.cend()) {
return; return;
} }
if (dialog_loop_next_index_ != SIZE_MAX) { if (IsDrawingDialogs()) {
// Actualize the next dialog index after the erasure from the vector. // Actualize the next dialog index after the erasure from the vector.
size_t existing_index = size_t(std::distance(dialogs_.cbegin(), it)); size_t existing_index = size_t(std::distance(dialogs_.cbegin(), it));
if (dialog_loop_next_index_ > existing_index) { if (dialog_loop_next_index_ > existing_index) {
@ -89,17 +89,7 @@ void ImGuiDrawer::RemoveDialog(ImGuiDialog* dialog) {
} }
} }
dialogs_.erase(it); dialogs_.erase(it);
if (dialog_loop_next_index_ == SIZE_MAX && dialogs_.empty()) { DetachIfLastDialogRemoved();
if (presenter_) {
presenter_->RemoveUIDrawerFromUIThread(this);
}
window_->RemoveInputListener(this);
// Clear all input since no input will be received anymore, and when the
// drawer becomes active again, it'd have an outdated input state otherwise
// which will be persistent until new events actualize individual input
// properties.
ClearInput();
}
} }
void ImGuiDrawer::Initialize() { void ImGuiDrawer::Initialize() {
@ -301,7 +291,7 @@ void ImGuiDrawer::Draw(UIDrawContext& ui_draw_context) {
ImGui::NewFrame(); ImGui::NewFrame();
assert_true(dialog_loop_next_index_ == SIZE_MAX); assert_true(!IsDrawingDialogs());
dialog_loop_next_index_ = 0; dialog_loop_next_index_ = 0;
while (dialog_loop_next_index_ < dialogs_.size()) { while (dialog_loop_next_index_ < dialogs_.size()) {
dialogs_[dialog_loop_next_index_++]->Draw(); dialogs_[dialog_loop_next_index_++]->Draw();
@ -319,11 +309,11 @@ void ImGuiDrawer::Draw(UIDrawContext& ui_draw_context) {
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
} }
if (dialogs_.empty()) { // Detaching is deferred if the last dialog is removed during drawing, perform
// All dialogs have removed themselves during the draw, detach. // it now if needed.
presenter_->RemoveUIDrawerFromUIThread(this); DetachIfLastDialogRemoved();
window_->RemoveInputListener(this);
} else { if (!dialogs_.empty()) {
// Repaint (and handle input) continuously if still active. // Repaint (and handle input) continuously if still active.
presenter_->RequestUIPaintFromUIThread(); presenter_->RequestUIPaintFromUIThread();
} }
@ -557,5 +547,24 @@ void ImGuiDrawer::SwitchToPhysicalMouseAndUpdateMousePosition(
UpdateMousePosition(float(e.x()), float(e.y())); UpdateMousePosition(float(e.x()), float(e.y()));
} }
void ImGuiDrawer::DetachIfLastDialogRemoved() {
// IsDrawingDialogs() is also checked because in a situation of removing the
// only dialog, then adding a dialog, from within a dialog's Draw function,
// re-registering the ImGuiDrawer may result in ImGui being drawn multiple
// times in the current frame.
if (!dialogs_.empty() || IsDrawingDialogs()) {
return;
}
if (presenter_) {
presenter_->RemoveUIDrawerFromUIThread(this);
}
window_->RemoveInputListener(this);
// Clear all input since no input will be received anymore, and when the
// drawer becomes active again, it'd have an outdated input state otherwise
// which will be persistent until new events actualize individual input
// properties.
ClearInput();
}
} // namespace ui } // namespace ui
} // namespace xe } // namespace xe

View File

@ -74,6 +74,9 @@ class ImGuiDrawer : public WindowInputListener, public UIDrawer {
void UpdateMousePosition(float x, float y); void UpdateMousePosition(float x, float y);
void SwitchToPhysicalMouseAndUpdateMousePosition(const MouseEvent& e); void SwitchToPhysicalMouseAndUpdateMousePosition(const MouseEvent& e);
bool IsDrawingDialogs() const { return dialog_loop_next_index_ != SIZE_MAX; }
void DetachIfLastDialogRemoved();
Window* window_; Window* window_;
size_t z_order_; size_t z_order_;