From fd29d86da1435af54343d0d6f61802b22dfd8303 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Jul 2020 14:52:14 +0200 Subject: [PATCH 1/6] Demonstrate the use of wxTipWindow in the dialogs sample As we show wxRichToolTip in this sample, it's only logical to show the simple wxTipWindow in it too. --- samples/dialogs/dialogs.cpp | 45 +++++++++++++++++++++++++++++++++++++ samples/dialogs/dialogs.h | 8 +++++++ 2 files changed, 53 insertions(+) diff --git a/samples/dialogs/dialogs.cpp b/samples/dialogs/dialogs.cpp index c5c1cc1da7..28f99fa8b8 100644 --- a/samples/dialogs/dialogs.cpp +++ b/samples/dialogs/dialogs.cpp @@ -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 @@ -279,6 +283,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 @@ -588,6 +596,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(); @@ -711,6 +723,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(); @@ -2357,6 +2373,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" diff --git a/samples/dialogs/dialogs.h b/samples/dialogs/dialogs.h index 085e9eaeba..a54d8ae620 100644 --- a/samples/dialogs/dialogs.h +++ b/samples/dialogs/dialogs.h @@ -543,6 +543,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(); }; @@ -621,6 +628,7 @@ enum DIALOGS_REPLACE, DIALOGS_REQUEST, DIALOGS_NOTIFY_MSG, + DIALOGS_SHOW_TIP, DIALOGS_RICHTIP_DIALOG, DIALOGS_PROPERTY_SHEET, DIALOGS_PROPERTY_SHEET_TOOLBOOK, From 3ceb425a73db3c89d83b497df17dd68b28f2b28b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Jul 2020 15:14:45 +0200 Subject: [PATCH 2/6] Call OnDismiss() in wxMSW wxPopupTransientWindow on deactivation The window should be notified about its dismissal, as it happens in the result of the user action and not due to a call to Dismiss() from the program itself. --- src/msw/popupwin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/msw/popupwin.cpp b/src/msw/popupwin.cpp index c3f42860f9..0c42cd5aa9 100644 --- a/src/msw/popupwin.cpp +++ b/src/msw/popupwin.cpp @@ -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 From 03e79fce63974cf603d4c16bf914425c6d75aea2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Jul 2020 17:40:20 +0200 Subject: [PATCH 3/6] Don't set focus to wxTipWindowView when using wxPopupWindow At least under MSW this results in an activation loss for the previously active TLW, as it activates wxTipWindow itself, which looks bad. And, of course, setting focus is completely unnecessary in the first place, as this window doesn't accept any input and wxEVT_KILL_FOCUS is handled only when not using wxPopupWindow. --- src/generic/tipwin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/generic/tipwin.cpp b/src/generic/tipwin.cpp index f73dbf82bd..b3f56f51d6 100644 --- a/src/generic/tipwin.cpp +++ b/src/generic/tipwin.cpp @@ -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); From 2ac90f9d38737a6d275309ed443d8870c89f69e0 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Jul 2020 19:55:03 +0200 Subject: [PATCH 4/6] Don't call SetFocus() for popups not using wxPU_CONTAINS_CONTROLS Under MSW only popup windows with wxPU_CONTAINS_CONTROLS can have focus, attempting to set it to a [child of a] popup without it will just result in a debug error message from wxWindow::SetFocus() and nothing else, so just avoid doing it entirely. --- src/msw/popupwin.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/msw/popupwin.cpp b/src/msw/popupwin.cpp index 0c42cd5aa9..10900fa61f 100644 --- a/src/msw/popupwin.cpp +++ b/src/msw/popupwin.cpp @@ -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(); } From 3bcbc8fe8eb8bd69c71c4ca9c263ec3e014f026b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Jul 2020 23:58:29 +0200 Subject: [PATCH 5/6] Implement dismissal for unfocused wxPopupTransientWindow Popups not using wxPU_CONTAINS_CONTROLS were not automatically dismissed at all any longer, as the only auto-dismissal mechanism related on getting WM_ACTIVATE, which they never did, so implement a different logic for dismissing them: do it on any change of focus and also any mouse press (but not move and not key press neither). This will allow not using wxPU_CONTAINS_CONTROLS for popups that don't need focus, but still must disappear on their own. --- include/wx/msw/popupwin.h | 4 ++++ include/wx/popupwin.h | 3 +++ src/msw/popupwin.cpp | 13 +++++++++++++ src/msw/window.cpp | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/include/wx/msw/popupwin.h b/include/wx/msw/popupwin.h index bba24abfae..7a5ce58598 100644 --- a/include/wx/msw/popupwin.h +++ b/include/wx/msw/popupwin.h @@ -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; diff --git a/include/wx/popupwin.h b/include/wx/popupwin.h index bd26b5445a..ba5a1421d5 100644 --- a/include/wx/popupwin.h +++ b/include/wx/popupwin.h @@ -151,6 +151,9 @@ public: WXWPARAM wParam, WXLPARAM lParam) wxOVERRIDE; + // Override to dismiss the popup. + virtual void MSWDismissUnfocusedPopup() wxOVERRIDE; + private: void DismissOnDeactivate(); diff --git a/src/msw/popupwin.cpp b/src/msw/popupwin.cpp index 10900fa61f..6491a6f007 100644 --- a/src/msw/popupwin.cpp +++ b/src/msw/popupwin.cpp @@ -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, diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 2e5be999d9..2a2864cedf 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -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; From 41410610eff483d102efacbe41ae20059b06f62b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 10 Jul 2020 00:00:56 +0200 Subject: [PATCH 6/6] Don't force wxPU_CONTAINS_CONTROLS on wxPopupTransientWindow Popups not using this style work too now (since the parent commit), so this style should be used only if the popup actually requires focus and not always, unconditionally. Turn on this style explicitly in the sample which shows a popup window with wxTextCtrl inside it (which requires focus to work). --- include/wx/popupwin.h | 7 ------- samples/popup/popup.cpp | 4 +++- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/include/wx/popupwin.h b/include/wx/popupwin.h index ba5a1421d5..c3964098b6 100644 --- a/include/wx/popupwin.h +++ b/include/wx/popupwin.h @@ -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; diff --git a/samples/popup/popup.cpp b/samples/popup/popup.cpp index 1e0cf61912..09a80b9df2 100644 --- a/samples/popup/popup.cpp +++ b/samples/popup/popup.cpp @@ -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 );