Fix for showing multiple popups in a row in wxMSW
There were at least 2 problems when showing a transient popup while another one was already shown in wxMSW, as could be seen by showing several wxRichToolTips in a row from a timer event handler, for example: First problem was that wxCurrentPopupWindow was incorrectly reset in this case by the old popup window when it was hidden, even though it should have been remaining set to the new popup window. Fix this by checking that we only reset wxCurrentPopupWindow when hiding the popup if it still points to this popup, but not if it has been changed to point to another one in the meanwhile. Second problem was more mysterious and resulted in simply not receiving the activation events for the new popup when showing it resulted in hiding the previous one. The working hypothesis is that hiding a window, which changes activation on its own, from WM_NCACTIVATE handler in our code confused ShowWindow(), which handles switching activation, which didn't expect this to happen, so the fix is to avoid doing anything immediately from this handler and wait until the next idle event to do it instead. These fixes ensure that showing several popups in a row works correctly, i.e. hides the previous popup when a new one is shown and also keeps the parent window appearing active during all the time and deactivates it if the focus switches to another top level window at the end.
This commit is contained in:
@@ -152,6 +152,8 @@ public:
|
|||||||
WXLPARAM lParam) wxOVERRIDE;
|
WXLPARAM lParam) wxOVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void DismissOnDeactivate(WXHWND hwndActive);
|
||||||
|
|
||||||
wxDECLARE_DYNAMIC_CLASS(wxPopupTransientWindow);
|
wxDECLARE_DYNAMIC_CLASS(wxPopupTransientWindow);
|
||||||
wxDECLARE_NO_COPY_CLASS(wxPopupTransientWindow);
|
wxDECLARE_NO_COPY_CLASS(wxPopupTransientWindow);
|
||||||
};
|
};
|
||||||
|
@@ -119,10 +119,32 @@ void wxPopupWindow::SetFocus()
|
|||||||
|
|
||||||
bool wxPopupWindow::Show(bool show)
|
bool wxPopupWindow::Show(bool show)
|
||||||
{
|
{
|
||||||
|
// Note that we're called from the ctor before the window is actually
|
||||||
|
// created to hide the popup initially. This call doesn't really hide the
|
||||||
|
// window, so don't do anything in this case, in particular don't change
|
||||||
|
// wxCurrentPopupWindow value.
|
||||||
|
if ( !GetHwnd() )
|
||||||
|
return wxPopupWindowBase::Show(show);
|
||||||
|
|
||||||
// It's important to update wxCurrentPopupWindow before showing the window,
|
// It's important to update wxCurrentPopupWindow before showing the window,
|
||||||
// to ensure that it already set by the time the owner gets WM_NCACTIVATE
|
// to ensure that it's already set by the time the owner gets WM_NCACTIVATE
|
||||||
// from inside Show() so that it knows to remain [appearing] active.
|
// from inside Show() so that it knows to remain [appearing] active, see
|
||||||
wxCurrentPopupWindow = show ? this : NULL;
|
// the WM_NCACTIVATE handler in wxWindow::MSWHandleMessage().
|
||||||
|
if ( show )
|
||||||
|
{
|
||||||
|
// There could have been a previous popup window which hasn't been
|
||||||
|
// hidden yet. This will happen now, when we show this one, as it will
|
||||||
|
// result in activation loss for the other one, so it's ok to overwrite
|
||||||
|
// the old pointer, even if it's non-NULL.
|
||||||
|
wxCurrentPopupWindow = this;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Only reset the pointer if it points to this window, otherwise we
|
||||||
|
// would lose the correct value in the situation described above.
|
||||||
|
if ( wxCurrentPopupWindow == this )
|
||||||
|
wxCurrentPopupWindow = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if ( HasFlag(wxPU_CONTAINS_CONTROLS) )
|
if ( HasFlag(wxPU_CONTAINS_CONTROLS) )
|
||||||
{
|
{
|
||||||
@@ -170,6 +192,24 @@ void wxPopupTransientWindow::Dismiss()
|
|||||||
Hide();
|
Hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wxPopupTransientWindow::DismissOnDeactivate(WXHWND hwndActive)
|
||||||
|
{
|
||||||
|
// Hide the window automatically when it loses activation.
|
||||||
|
Dismiss();
|
||||||
|
|
||||||
|
// Activation might have gone to a different window or maybe
|
||||||
|
// even a different application, don't let our owner continue
|
||||||
|
// to appear active in this case.
|
||||||
|
wxWindow* const owner = MSWGetOwner();
|
||||||
|
if ( owner )
|
||||||
|
{
|
||||||
|
if ( hwndActive != GetHwndOf(owner) )
|
||||||
|
{
|
||||||
|
::SendMessage(GetHwndOf(owner), WM_NCACTIVATE, FALSE, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
wxPopupTransientWindow::MSWHandleMessage(WXLRESULT *result,
|
wxPopupTransientWindow::MSWHandleMessage(WXLRESULT *result,
|
||||||
WXUINT message,
|
WXUINT message,
|
||||||
@@ -181,21 +221,15 @@ wxPopupTransientWindow::MSWHandleMessage(WXLRESULT *result,
|
|||||||
case WM_NCACTIVATE:
|
case WM_NCACTIVATE:
|
||||||
if ( !wParam )
|
if ( !wParam )
|
||||||
{
|
{
|
||||||
// Hide the window automatically when it loses activation.
|
// We need to dismiss this window, however doing it directly
|
||||||
Dismiss();
|
// from here seems to confuse ::ShowWindow(), which ends up
|
||||||
|
// calling this handler, and may result in losing activation
|
||||||
// Activation might have gone to a different window or maybe
|
// entirely, so postpone it slightly.
|
||||||
// even a different application, don't let our owner continue
|
//
|
||||||
// to appear active in this case.
|
// Note that we do use the currently active window, just in
|
||||||
wxWindow* const owner = MSWGetOwner();
|
// case it changes between now and the next idle event.
|
||||||
if ( owner )
|
CallAfter(&wxPopupTransientWindow::DismissOnDeactivate,
|
||||||
{
|
::GetActiveWindow());
|
||||||
const HWND hwndActive = ::GetActiveWindow();
|
|
||||||
if ( hwndActive != GetHwndOf(owner) )
|
|
||||||
{
|
|
||||||
::SendMessage(GetHwndOf(owner), WM_NCACTIVATE, FALSE, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user