Merge branch 'msw-dismiss-unfocused-popup'

Allow wxPopupTransientWindow to work without wxPU_CONTAINS_CONTROLS too
in wxMSW and don't use wxPU_CONTAINS_CONTROLS by default, notably
allowing wxTipWindow to be shown without stealing focus.

See https://github.com/wxWidgets/wxWidgets/pull/1942

Closes #18636.
This commit is contained in:
Vadim Zeitlin
2020-07-11 19:56:32 +02:00
8 changed files with 119 additions and 13 deletions

View File

@@ -40,6 +40,10 @@ public:
// Return the top level window parent of this popup or null.
wxWindow* MSWGetOwner() const { return m_owner; }
// This is a way to notify non-wxPU_CONTAINS_CONTROLS windows about the
// events that should result in their dismissal.
virtual void MSWDismissUnfocusedPopup() { }
private:
wxWindow* m_owner;

View File

@@ -133,13 +133,6 @@ public:
wxPopupTransientWindow() { }
wxPopupTransientWindow(wxWindow *parent, int style = wxBORDER_NONE)
{ Create(parent, style); }
bool Create(wxWindow *parent, int style = wxBORDER_NONE)
{
return wxPopupTransientWindowBase::Create
(
parent, style | wxPU_CONTAINS_CONTROLS
);
}
// Implement base class pure virtuals.
virtual void Popup(wxWindow *focus = NULL) wxOVERRIDE;
@@ -151,6 +144,9 @@ public:
WXWPARAM wParam,
WXLPARAM lParam) wxOVERRIDE;
// Override to dismiss the popup.
virtual void MSWDismissUnfocusedPopup() wxOVERRIDE;
private:
void DismissOnDeactivate();

View File

@@ -58,6 +58,10 @@
#include "wx/tipdlg.h"
#endif // wxUSE_STARTUP_TIPS
#if wxUSE_TIPWINDOW
#include "wx/tipwin.h"
#endif // wxUSE_TIPWINDOW
#if wxUSE_PROGRESSDLG
#if wxUSE_STOPWATCH && wxUSE_LONGLONG
#include "wx/datetime.h" // wxDateTime
@@ -280,6 +284,10 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(DIALOGS_NOTIFY_MSG, MyFrame::OnNotifMsg)
#endif // wxUSE_NOTIFICATION_MESSAGE
#if wxUSE_TIPWINDOW
EVT_MENU(DIALOGS_SHOW_TIP, MyFrame::OnShowTip)
EVT_UPDATE_UI(DIALOGS_SHOW_TIP, MyFrame::OnUpdateShowTipUI)
#endif // wxUSE_TIPWINDOW
#if wxUSE_RICHTOOLTIP
EVT_MENU(DIALOGS_RICHTIP_DIALOG, MyFrame::OnRichTipDialog)
#endif // wxUSE_RICHTOOLTIP
@@ -590,6 +598,10 @@ bool MyApp::OnInit()
#endif // wxUSE_NOTIFICATION_MESSAGE
menuDlg->AppendSubMenu(menuNotif, "&User notifications");
#if wxUSE_TIPWINDOW
menuDlg->AppendCheckItem(DIALOGS_SHOW_TIP, "Show &tip window\tShift-Ctrl-H");
#endif // wxUSE_TIPWINDOW
#if wxUSE_RICHTOOLTIP
menuDlg->Append(DIALOGS_RICHTIP_DIALOG, "Rich &tooltip dialog...\tCtrl-H");
menuDlg->AppendSeparator();
@@ -713,6 +725,10 @@ MyFrame::MyFrame(const wxString& title)
SetOwnBackgroundColour(m_canvas->GetBackgroundColour());
#endif // wxUSE_INFOBAR
#if wxUSE_TIPWINDOW
m_tipWindow = NULL;
#endif // wxUSE_TIPWINDOW
#ifdef __WXMSW__
// Test MSW-specific function allowing to access the "system" menu.
wxMenu * const menu = MSWGetSystemMenu();
@@ -2389,6 +2405,35 @@ void MyFrame::OnNotifMsg(wxCommandEvent& WXUNUSED(event))
#endif // wxUSE_NOTIFICATION_MESSAGE
#if wxUSE_TIPWINDOW
void MyFrame::OnShowTip(wxCommandEvent& WXUNUSED(event))
{
if ( m_tipWindow )
{
m_tipWindow->Close();
}
else
{
m_tipWindow = new wxTipWindow
(
this,
"This is just some text to be shown in the tip "
"window, broken into multiple lines, each less "
"than 60 logical pixels wide.",
FromDIP(60),
&m_tipWindow
);
}
}
void MyFrame::OnUpdateShowTipUI(wxUpdateUIEvent& event)
{
event.Check(m_tipWindow != NULL);
}
#endif // wxUSE_TIPWINDOW
#if wxUSE_RICHTOOLTIP
#include "wx/richtooltip.h"

View File

@@ -544,6 +544,13 @@ private:
SettingsData m_settingsData;
#endif // USE_SETTINGS_DIALOG
#if wxUSE_TIPWINDOW
void OnShowTip(wxCommandEvent& event);
void OnUpdateShowTipUI(wxUpdateUIEvent& event);
wxTipWindow *m_tipWindow;
#endif // wxUSE_TIPWINDOW
wxDECLARE_EVENT_TABLE();
};
@@ -623,6 +630,7 @@ enum
DIALOGS_REPLACE,
DIALOGS_REQUEST,
DIALOGS_NOTIFY_MSG,
DIALOGS_SHOW_TIP,
DIALOGS_RICHTIP_DIALOG,
DIALOGS_PROPERTY_SHEET,
DIALOGS_PROPERTY_SHEET_TOOLBOOK,

View File

@@ -108,7 +108,9 @@ wxBEGIN_EVENT_TABLE(SimpleTransientPopup,wxPopupTransientWindow)
wxEND_EVENT_TABLE()
SimpleTransientPopup::SimpleTransientPopup( wxWindow *parent, bool scrolled )
:wxPopupTransientWindow( parent )
:wxPopupTransientWindow( parent,
wxBORDER_NONE |
wxPU_CONTAINS_CONTROLS )
{
m_panel = new wxScrolledWindow( this, wxID_ANY );
m_panel->SetBackgroundColour( *wxLIGHT_GREY );

View File

@@ -136,7 +136,9 @@ wxTipWindow::wxTipWindow(wxWindow *parent,
// set size, position and show it
m_view = new wxTipWindowView(this);
m_view->Adjust(text, maxLength);
#if !wxUSE_POPUPWIN
m_view->SetFocus();
#endif
int x, y;
wxGetMousePosition(&x, &y);

View File

@@ -180,10 +180,10 @@ void wxPopupTransientWindow::Popup(wxWindow* focus)
{
Show();
// We can only set focus to one of our children as setting it to another
// window would result in an immediate loss of activation and popup
// disappearance.
if ( focus && IsDescendant(focus) )
// We can only set focus when using wxPU_CONTAINS_CONTROLS and then only to
// one of our children as setting it to another window would result in an
// immediate loss of activation and popup disappearance.
if ( HasFlag(wxPU_CONTAINS_CONTROLS) && focus && IsDescendant(focus) )
focus->SetFocus();
}
@@ -195,7 +195,7 @@ void wxPopupTransientWindow::Dismiss()
void wxPopupTransientWindow::DismissOnDeactivate()
{
// Hide the window automatically when it loses activation.
Dismiss();
DismissAndNotify();
// Activation might have gone to a different window or maybe
// even a different application, don't let our owner continue
@@ -210,6 +210,19 @@ void wxPopupTransientWindow::DismissOnDeactivate()
}
}
void wxPopupTransientWindow::MSWDismissUnfocusedPopup()
{
// When we use wxPU_CONTAINS_CONTROLS, we can react to the popup
// deactivation in MSWHandleMessage(), but if we don't have focus, we don't
// get any events ourselves, so we rely on wxWindow to forward them to us.
if ( !HasFlag(wxPU_CONTAINS_CONTROLS) )
{
// It doesn't seem necessary to use CallAfter() here, as dismissing
// this window shouldn't affect the focus, as it never has it anyhow.
DismissAndNotify();
}
}
bool
wxPopupTransientWindow::MSWHandleMessage(WXLRESULT *result,
WXUINT message,

View File

@@ -3844,6 +3844,42 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
}
}
// Special hook for dismissing the current popup if it's active. It's a bit
// ugly to have to do this here, but the only alternatives seem to be
// installing a WH_CBT hook in wxPopupTransientWindow code, which is not
// really much better.
#if wxUSE_POPUPWIN
if ( wxCurrentPopupWindow )
{
switch ( message )
{
case WM_NCLBUTTONDOWN:
case WM_NCLBUTTONUP:
case WM_NCLBUTTONDBLCLK:
case WM_NCRBUTTONDOWN:
case WM_NCRBUTTONUP:
case WM_NCRBUTTONDBLCLK:
case WM_NCMBUTTONDOWN:
case WM_NCMBUTTONUP:
case WM_NCMBUTTONDBLCLK:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
case WM_SETFOCUS:
case WM_KILLFOCUS:
wxCurrentPopupWindow->MSWDismissUnfocusedPopup();
}
}
#endif // wxUSE_POPUPWIN
if ( !processed )
return false;