From 82ceade76a4913f9c6fd8dfda1fb291bf08e1d89 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Tue, 12 Apr 2005 04:28:40 +0000 Subject: [PATCH] Fixes for wxPopupTransientWindow for wxMSW. Includes Patch #1181190, and also fixes for automatically dismissing the popup whenever the mouse is clicked outside of the popup window. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@33535 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/popupwin.h | 10 ++++ samples/popup/popup.cpp | 101 +++++++++++++++++++++------------ src/common/popupcmn.cpp | 120 ++++++++++++++++++++++++++++++++++++++-- src/msw/popupwin.cpp | 3 + 4 files changed, 194 insertions(+), 40 deletions(-) diff --git a/include/wx/popupwin.h b/include/wx/popupwin.h index 9cd390d2fa..2915205ed8 100644 --- a/include/wx/popupwin.h +++ b/include/wx/popupwin.h @@ -124,6 +124,15 @@ protected: // remove our event handlers void PopHandlers(); + // get alerted when child gets deleted from under us + void OnDestroy(wxWindowDestroyEvent& event); + + void OnEnter(wxMouseEvent& event); + void OnLeave(wxMouseEvent& event); + void OnLeftDown(wxMouseEvent& event); + void OnChildEnter(wxMouseEvent& event); + void OnChildLeave(wxMouseEvent& event); + // the child of this popup if any wxWindow *m_child; @@ -138,6 +147,7 @@ protected: wxPopupWindowHandler *m_handlerPopup; wxPopupFocusHandler *m_handlerFocus; + DECLARE_EVENT_TABLE() DECLARE_DYNAMIC_CLASS(wxPopupTransientWindow) DECLARE_NO_COPY_CLASS(wxPopupTransientWindow) }; diff --git a/samples/popup/popup.cpp b/samples/popup/popup.cpp index 2f2b58f35f..4741bfe738 100644 --- a/samples/popup/popup.cpp +++ b/samples/popup/popup.cpp @@ -109,11 +109,19 @@ BEGIN_EVENT_TABLE(SimpleTransientPopup,wxPopupTransientWindow) EVT_SPINCTRL( Minimal_PopupSpinctrl, SimpleTransientPopup::OnSpinCtrl ) END_EVENT_TABLE() -SimpleTransientPopup::SimpleTransientPopup( wxWindow *parent ) : - wxPopupTransientWindow( parent ) +SimpleTransientPopup::SimpleTransientPopup( wxWindow *parent ) + :wxPopupTransientWindow( parent ) { m_panel = new wxScrolledWindow( this, wxID_ANY ); m_panel->SetBackgroundColour( *wxLIGHT_GREY ); + + // Keep this code to verify if mouse events work, they're required if + // you're making a control like a combobox where the items are highlighted + // under the cursor, the m_panel is set focus in the Popup() function + //m_panel->Connect(wxEVT_MOTION, + // wxMouseEventHandler(SimpleTransientPopup::OnMouse), + // NULL, this); + wxStaticText *text = new wxStaticText( m_panel, wxID_ANY, wxT("wx.PopupTransientWindow is a\n") wxT("wx.PopupWindow which disappears\n") @@ -139,7 +147,7 @@ SimpleTransientPopup::~SimpleTransientPopup() void SimpleTransientPopup::Popup(wxWindow *focus) { wxLogMessage( wxT("0x%lx SimpleTransientPopup::Popup"), long(this) ); - wxPopupTransientWindow::Popup(focus); + wxPopupTransientWindow::Popup(focus ? focus : m_panel); } void SimpleTransientPopup::OnDismiss() @@ -179,7 +187,7 @@ void SimpleTransientPopup::OnKillFocus(wxFocusEvent &event) void SimpleTransientPopup::OnMouse(wxMouseEvent &event) { - wxLogMessage( wxT("0x%lx SimpleTransientPopup::OnMouse pos(%d, %d)"), long(this), event.GetX(), event.GetY()); + wxLogMessage( wxT("0x%lx SimpleTransientPopup::OnMouse pos(%d, %d)"), long(event.GetEventObject()), event.GetX(), event.GetY()); event.Skip(); } @@ -215,6 +223,8 @@ public: void OnStartScrolledPopup(wxCommandEvent& event); private: + SimpleTransientPopup *m_simplePopup; + SimpleTransientPopup *m_scrolledPopup; DECLARE_EVENT_TABLE() }; @@ -231,6 +241,8 @@ public: void OnStartScrolledPopup(wxCommandEvent& event); private: + SimpleTransientPopup *m_simplePopup; + SimpleTransientPopup *m_scrolledPopup; wxTextCtrl *m_logWin; wxLog *m_logOld; DECLARE_EVENT_TABLE() @@ -282,6 +294,8 @@ END_EVENT_TABLE() MyFrame::MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title) { + m_simplePopup = m_scrolledPopup = NULL; + SetIcon(wxICON(sample)); #if wxUSE_MENUS @@ -303,23 +317,24 @@ MyFrame::MyFrame(const wxString& title) SetMenuBar(menuBar); #endif // wxUSE_MENUS - wxButton *button1 = new wxButton( this, Minimal_StartSimplePopup, wxT("Show simple popup"), wxPoint(20,20) ); - wxButton *button2 = new wxButton( this, Minimal_StartScrolledPopup, wxT("Show scrolled popup"), wxPoint(20,70) ); + wxPanel *panel = new wxPanel(this, -1); + wxButton *button1 = new wxButton( panel, Minimal_StartSimplePopup, wxT("Show simple popup"), wxPoint(20,20) ); + wxButton *button2 = new wxButton( panel, Minimal_StartScrolledPopup, wxT("Show scrolled popup"), wxPoint(20,70) ); - m_logWin = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, - wxDefaultSize, wxTE_MULTILINE ); + m_logWin = new wxTextCtrl( panel, wxID_ANY, wxEmptyString, wxDefaultPosition, + wxDefaultSize, wxTE_MULTILINE ); m_logWin->SetEditable(false); wxLogTextCtrl* logger = new wxLogTextCtrl( m_logWin ); m_logOld = logger->SetActiveTarget( logger ); logger->SetTimestamp( NULL ); wxBoxSizer *topSizer = new wxBoxSizer( wxVERTICAL ); - topSizer->Add( button1, 0 ); - topSizer->Add( button2, 0 ); - topSizer->Add( m_logWin, 1, wxEXPAND ); + topSizer->Add( button1, 0, wxALL, 5 ); + topSizer->Add( button2, 0, wxALL, 5 ); + topSizer->Add( m_logWin, 1, wxEXPAND|wxALL, 5 ); - SetAutoLayout( true ); - SetSizer( topSizer ); + panel->SetAutoLayout( true ); + panel->SetSizer( topSizer ); #if wxUSE_STATUSBAR // create a status bar just for fun (by default with 1 pane only) @@ -339,26 +354,28 @@ MyFrame::~MyFrame() void MyFrame::OnStartSimplePopup(wxCommandEvent& event) { wxLogMessage( wxT("================================================") ); - SimpleTransientPopup* popup = new SimpleTransientPopup( this ); + delete m_simplePopup; + m_simplePopup = new SimpleTransientPopup( this ); wxWindow *btn = (wxWindow*) event.GetEventObject(); wxPoint pos = btn->ClientToScreen( wxPoint(0,0) ); wxSize sz = btn->GetSize(); - popup->Position( pos, sz ); - wxLogMessage( wxT("0x%lx Simple Popup Shown pos(%d, %d) size(%d, %d)"), long(popup), pos.x, pos.y, sz.x, sz.y ); - popup->Popup(); + m_simplePopup->Position( pos, sz ); + wxLogMessage( wxT("0x%lx Simple Popup Shown pos(%d, %d) size(%d, %d)"), long(m_simplePopup), pos.x, pos.y, sz.x, sz.y ); + m_simplePopup->Popup(); } void MyFrame::OnStartScrolledPopup(wxCommandEvent& event) { wxLogMessage( wxT("================================================") ); - SimpleTransientPopup* popup = new SimpleTransientPopup( this ); - popup->GetChild()->SetScrollbars(1, 1, 1000, 1000); + delete m_scrolledPopup; + m_scrolledPopup = new SimpleTransientPopup( this ); + m_scrolledPopup->GetChild()->SetScrollbars(1, 1, 1000, 1000); wxWindow *btn = (wxWindow*) event.GetEventObject(); wxPoint pos = btn->ClientToScreen( wxPoint(0,0) ); wxSize sz = btn->GetSize(); - popup->Position( pos, sz ); - wxLogMessage( wxT("0x%lx Scrolled Popup Shown pos(%d, %d) size(%d, %d)"), long(popup), pos.x, pos.y, sz.x, sz.y ); - popup->Popup(); + m_scrolledPopup->Position( pos, sz ); + wxLogMessage( wxT("0x%lx Scrolled Popup Shown pos(%d, %d) size(%d, %d)"), long(m_scrolledPopup), pos.x, pos.y, sz.x, sz.y ); + m_scrolledPopup->Popup(); } void MyFrame::OnTestDialog(wxCommandEvent& WXUNUSED(event)) @@ -392,36 +409,50 @@ BEGIN_EVENT_TABLE(MyDialog, wxDialog) END_EVENT_TABLE() MyDialog::MyDialog(const wxString& title) - : wxDialog(NULL, wxID_ANY, title, wxPoint(50,50), wxSize(400,300)) + :wxDialog(NULL, wxID_ANY, title, wxPoint(50,50), wxSize(400,300)) { + m_simplePopup = m_scrolledPopup = NULL; + wxPanel *panel = new wxPanel(this, -1); - new wxButton( this, Minimal_StartSimplePopup, wxT("Show simple popup"), wxPoint(20,20) ); - new wxButton( this, Minimal_StartScrolledPopup, wxT("Show scrolled popup"), wxPoint(20,60) ); + wxButton *button1 = new wxButton( panel, Minimal_StartSimplePopup, wxT("Show simple popup"), wxPoint(20,20) ); + wxButton *button2 = new wxButton( panel, Minimal_StartScrolledPopup, wxT("Show scrolled popup"), wxPoint(20,60) ); - new wxButton( this, wxID_OK, wxT("OK"), wxPoint(20,200) ); + wxButton *okButton = new wxButton( panel, wxID_OK, wxT("OK"), wxPoint(20,200) ); + + wxBoxSizer *topSizer = new wxBoxSizer( wxVERTICAL ); + topSizer->Add( button1, 0, wxALL, 5 ); + topSizer->Add( button2, 0, wxALL, 5 ); + topSizer->AddSpacer(40); + topSizer->Add( okButton, 0, wxALL, 5 ); + + panel->SetAutoLayout( true ); + panel->SetSizer( topSizer ); + topSizer->Fit(this); } void MyDialog::OnStartSimplePopup(wxCommandEvent& event) { wxLogMessage( wxT("================================================") ); - SimpleTransientPopup* popup = new SimpleTransientPopup( this ); + delete m_simplePopup; + m_simplePopup = new SimpleTransientPopup( this ); wxWindow *btn = (wxWindow*) event.GetEventObject(); wxPoint pos = btn->ClientToScreen( wxPoint(0,0) ); wxSize sz = btn->GetSize(); - popup->Position( pos, sz ); - wxLogMessage( wxT("0x%lx Dialog Simple Popup Shown pos(%d, %d) size(%d, %d)"), long(popup), pos.x, pos.y, sz.x, sz.y ); - popup->Popup(); + m_simplePopup->Position( pos, sz ); + wxLogMessage( wxT("0x%lx Dialog Simple Popup Shown pos(%d, %d) size(%d, %d)"), long(m_simplePopup), pos.x, pos.y, sz.x, sz.y ); + m_simplePopup->Popup(); } void MyDialog::OnStartScrolledPopup(wxCommandEvent& event) { wxLogMessage( wxT("================================================") ); - SimpleTransientPopup* popup = new SimpleTransientPopup( this ); - popup->GetChild()->SetScrollbars(1, 1, 1000, 1000); + delete m_scrolledPopup; + m_scrolledPopup = new SimpleTransientPopup( this ); + m_scrolledPopup->GetChild()->SetScrollbars(1, 1, 1000, 1000); wxWindow *btn = (wxWindow*) event.GetEventObject(); wxPoint pos = btn->ClientToScreen( wxPoint(0,0) ); wxSize sz = btn->GetSize(); - popup->Position( pos, sz ); - wxLogMessage( wxT("0x%lx Dialog Scrolled Popup Shown pos(%d, %d) size(%d, %d)"), long(popup), pos.x, pos.y, sz.x, sz.y ); - popup->Popup(); + m_scrolledPopup->Position( pos, sz ); + wxLogMessage( wxT("0x%lx Dialog Scrolled Popup Shown pos(%d, %d) size(%d, %d)"), long(m_scrolledPopup), pos.x, pos.y, sz.x, sz.y ); + m_scrolledPopup->Popup(); } diff --git a/src/common/popupcmn.cpp b/src/common/popupcmn.cpp index 47099f03ba..a620970488 100644 --- a/src/common/popupcmn.cpp +++ b/src/common/popupcmn.cpp @@ -66,7 +66,8 @@ IMPLEMENT_DYNAMIC_CLASS(wxPopupTransientWindow, wxPopupWindow) class wxPopupWindowHandler : public wxEvtHandler { public: - wxPopupWindowHandler(wxPopupTransientWindow *popup) { m_popup = popup; } + wxPopupWindowHandler(wxPopupTransientWindow *popup) : m_popup(popup) {} + ~wxPopupWindowHandler(); protected: // event handlers @@ -82,10 +83,8 @@ private: class wxPopupFocusHandler : public wxEvtHandler { public: - wxPopupFocusHandler(wxPopupTransientWindow *popup) - { - m_popup = popup; - } + wxPopupFocusHandler(wxPopupTransientWindow *popup) : m_popup(popup) {} + ~wxPopupFocusHandler(); protected: void OnKillFocus(wxFocusEvent& event); @@ -169,6 +168,14 @@ void wxPopupWindowBase::Position(const wxPoint& ptOrigin, // wxPopupTransientWindow // ---------------------------------------------------------------------------- +BEGIN_EVENT_TABLE(wxPopupTransientWindow, wxPopupWindow) +#ifdef __WXMSW__ + EVT_ENTER_WINDOW(wxPopupTransientWindow::OnEnter) + EVT_LEAVE_WINDOW(wxPopupTransientWindow::OnLeave) + EVT_LEFT_DOWN(wxPopupTransientWindow::OnLeftDown) +#endif +END_EVENT_TABLE() + void wxPopupTransientWindow::Init() { m_child = @@ -205,6 +212,10 @@ void wxPopupTransientWindow::PopHandlers() } #ifdef __WXMSW__ + if ( HasCapture() ) + { + ReleaseCapture(); + } if ( m_focus ) { if ( m_handlerFocus && !m_focus->RemoveEventHandler(m_handlerFocus) ) @@ -244,6 +255,10 @@ void wxPopupTransientWindow::Popup(wxWindow *winFocus) Show(); + // There is is a problem if these are still valid + wxASSERT_MSG(!m_handlerPopup, wxT("Popup handler not deleted")); + wxASSERT_MSG(!m_handlerFocus, wxT("Focus handler not deleted")); + delete m_handlerPopup; m_handlerPopup = new wxPopupWindowHandler(this); @@ -272,6 +287,31 @@ void wxPopupTransientWindow::Popup(wxWindow *winFocus) PushEventHandler(m_handlerFocus); #endif // __WXMSW__ + // catch destroy events, if you close a program with a popup shown in MSW + // you get a segfault if m_child or m_focus are deleted before this is + m_child->Connect(wxEVT_DESTROY, + wxWindowDestroyEventHandler(wxPopupTransientWindow::OnDestroy), + NULL, this); + m_focus->Connect(wxEVT_DESTROY, + wxWindowDestroyEventHandler(wxPopupTransientWindow::OnDestroy), + NULL, this); +#ifdef __WXMSW__ + // Assume that the mouse is currently outside of the popup window + CaptureMouse(); + + // Connect the child Enter/Leave events too, incase the child completly + // covers the popup (because then the popup's enter/leave events won't be + // sent. + if (m_child != this) + { + m_child->Connect(wxEVT_ENTER_WINDOW, + wxMouseEventHandler(wxPopupTransientWindow::OnChildEnter), + NULL, this); + m_child->Connect(wxEVT_LEAVE_WINDOW, + wxMouseEventHandler(wxPopupTransientWindow::OnChildLeave), + NULL, this); + } +#endif } bool wxPopupTransientWindow::Show( bool show ) @@ -354,6 +394,66 @@ bool wxPopupTransientWindow::ProcessLeftDown(wxMouseEvent& WXUNUSED(event)) return false; } +void wxPopupTransientWindow::OnDestroy(wxWindowDestroyEvent& event) +{ + if (event.GetEventObject() == m_child) + m_child = NULL; + if (event.GetEventObject() == m_focus) + m_focus = NULL; +} + +void wxPopupTransientWindow::OnEnter(wxMouseEvent& /*event*/) +{ + if ( HasCapture() ) + { + ReleaseCapture(); + } +} + +void wxPopupTransientWindow::OnLeave(wxMouseEvent& /*event*/) +{ + CaptureMouse(); +} + +void wxPopupTransientWindow::OnLeftDown(wxMouseEvent& event) +{ + if (m_handlerPopup && m_child && m_child != this) + { + m_child->GetEventHandler()->ProcessEvent(event); + } +} + + + +// If the child is the same size as the popup window then handle the event, +// otherwise assume that there is enough of the popup showing that it will get +// it's own Enter/Leave events. A more reliable way to detect this situation +// would be appreciated... + +void wxPopupTransientWindow::OnChildEnter(wxMouseEvent& event) +{ + if (m_child) + { + wxSize cs = m_child->GetSize(); + wxSize ps = GetClientSize(); + + if ((cs.x * cs.y) >= (ps.x * ps.y)) + OnEnter(event); + } +} + +void wxPopupTransientWindow::OnChildLeave(wxMouseEvent& event) +{ + if (m_child) + { + wxSize cs = m_child->GetSize(); + wxSize ps = GetClientSize(); + + if ((cs.x * cs.y) >= (ps.x * ps.y)) + OnLeave(event); + } +} + #if wxUSE_COMBOBOX && defined(__WXUNIVERSAL__) // ---------------------------------------------------------------------------- @@ -411,6 +511,11 @@ void wxPopupComboWindow::OnKeyDown(wxKeyEvent& event) // ---------------------------------------------------------------------------- // wxPopupWindowHandler // ---------------------------------------------------------------------------- +wxPopupWindowHandler::~wxPopupWindowHandler() +{ + if (m_popup && (m_popup->m_handlerPopup == this)) + m_popup->m_handlerPopup = NULL; +} void wxPopupWindowHandler::OnLeftDown(wxMouseEvent& event) { @@ -499,6 +604,11 @@ void wxPopupWindowHandler::OnLeftDown(wxMouseEvent& event) // ---------------------------------------------------------------------------- // wxPopupFocusHandler // ---------------------------------------------------------------------------- +wxPopupFocusHandler::~wxPopupFocusHandler() +{ + if (m_popup && (m_popup->m_handlerFocus == this)) + m_popup->m_handlerFocus = NULL; +} void wxPopupFocusHandler::OnKillFocus(wxFocusEvent& event) { diff --git a/src/msw/popupwin.cpp b/src/msw/popupwin.cpp index 78a9a676bf..20f337d99a 100644 --- a/src/msw/popupwin.cpp +++ b/src/msw/popupwin.cpp @@ -107,6 +107,9 @@ bool wxPopupWindow::Show(bool show) { wxLogLastError(_T("SetWindowPos")); } + + // and set it as the foreground window so the mouse can be captured + ::SetForegroundWindow(GetHwnd()); } return true; -- 2.47.2