diff --git a/src/slic3r/GUI/Widgets/DropDown.cpp b/src/slic3r/GUI/Widgets/DropDown.cpp index 59832067c8..d698f9f7dc 100644 --- a/src/slic3r/GUI/Widgets/DropDown.cpp +++ b/src/slic3r/GUI/Widgets/DropDown.cpp @@ -563,6 +563,34 @@ void DropDown::messureSize() subDropDown->text_off = text_off; subDropDown->use_content_width = true; subDropDown->Create(GetParent()); +#ifdef __WXGTK__ + // Orca: Keep the wx parent as the combobox so wxPopupTransientWindow installs + // its capture handlers on the main dropdown, but make the native GTK + // popup transient for the currently open popup to satisfy Wayland's + // xdg-shell rule that a popup's parent must be the topmost mapped popup. + gtk_window_set_transient_for(GTK_WINDOW(subDropDown->GetHandle()), GTK_WINDOW(GetHandle())); + // Orca: On Wayland, while the sub holds an xdg_popup grab, motion events for + // the cursor over main may not be delivered (Mutter drops motion + // outside the grabbing surface). Poll on idle and synthesize a + // mouseMove on main so its hover highlight tracks and it can dismiss + // the sub when the cursor leaves the parent (group) item. + DropDown* sub = subDropDown; + sub->Bind(wxEVT_IDLE, [sub](wxIdleEvent& e) { + e.Skip(); + if (!sub->IsShown() || !sub->mainDropDown->IsShown()) + return; + wxPoint screen_pt = wxGetMousePosition(); + if (sub->GetScreenRect().Contains(screen_pt) || !sub->mainDropDown->GetScreenRect().Contains(screen_pt)) + return; + wxPoint main_pt = sub->mainDropDown->ScreenToClient(screen_pt); + wxMouseEvent ev(wxEVT_MOTION); + ev.SetEventObject(sub->mainDropDown); + ev.m_x = main_pt.x; + ev.m_y = main_pt.y; + sub->mainDropDown->mouseMove(ev); + e.RequestMore(); + }); +#endif subDropDown->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &e) { e.SetEventObject(this); e.SetId(GetId()); @@ -759,6 +787,15 @@ void DropDown::Dismiss() PopupWindow::Dismiss(); } +bool DropDown::ShouldDismissOnTopWindowDeactivate() +{ + // On Wayland, mapping a chained xdg_popup with grab makes the parent + // toplevel inactive, which would otherwise cascade-dismiss the whole + // chain. Skip when our chain peer is shown. + return !((mainDropDown && mainDropDown->IsShown()) || + (subDropDown && subDropDown->IsShown())); +} + void DropDown::OnDismiss() { if (mainDropDown) { diff --git a/src/slic3r/GUI/Widgets/DropDown.hpp b/src/slic3r/GUI/Widgets/DropDown.hpp index 8b7ab19d61..639a13e526 100644 --- a/src/slic3r/GUI/Widgets/DropDown.hpp +++ b/src/slic3r/GUI/Widgets/DropDown.hpp @@ -110,6 +110,8 @@ protected: void OnDismiss() override; + bool ShouldDismissOnTopWindowDeactivate() override; + private: void paintEvent(wxPaintEvent& evt); void paintNow(); diff --git a/src/slic3r/GUI/Widgets/PopupWindow.cpp b/src/slic3r/GUI/Widgets/PopupWindow.cpp index a8412bcfc9..8fb1cda774 100644 --- a/src/slic3r/GUI/Widgets/PopupWindow.cpp +++ b/src/slic3r/GUI/Widgets/PopupWindow.cpp @@ -86,7 +86,8 @@ void PopupWindow::OnMouseEvent2(wxMouseEvent &evt) void PopupWindow::topWindowActiavate(wxActivateEvent &event) { event.Skip(); - if (!event.GetActive() && IsShown()) DismissAndNotify(); + if (!event.GetActive() && IsShown() && ShouldDismissOnTopWindowDeactivate()) + DismissAndNotify(); } #endif diff --git a/src/slic3r/GUI/Widgets/PopupWindow.hpp b/src/slic3r/GUI/Widgets/PopupWindow.hpp index 55231accce..45afea8213 100644 --- a/src/slic3r/GUI/Widgets/PopupWindow.hpp +++ b/src/slic3r/GUI/Widgets/PopupWindow.hpp @@ -17,6 +17,12 @@ public: #ifdef __WXMSW__ void BindUnfocusEvent(); #endif +protected: + // Orca: Hook so derived classes (e.g. DropDown chains) can skip auto-dismissal + // when the toplevel deactivates as a side effect of their own popup grab + // (notably on Wayland, where mapping a chained xdg_popup with grab makes + // the parent toplevel briefly inactive). + virtual bool ShouldDismissOnTopWindowDeactivate() { return true; } private: #ifdef __WXOSX__ void OnMouseEvent2(wxMouseEvent &evt);