From 64ac3db840a3b4496131a11817926859b4eab870 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 2 Dec 2011 00:50:41 +0000 Subject: [PATCH] Update all controls using in-place editors to handle Escape/Return correctly. Define EVT_CHAR_HOOK handlers to ensure that pressing Escape/Return while an in-place edit control is active affects only it and is not used for the keyboard navigation. Closes #9102. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@69897 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/generic/private/listctrl.h | 5 +++ include/wx/msw/listctrl.h | 4 ++ include/wx/treectrl.h | 7 +++ src/common/treebase.cpp | 24 ++++++++++ src/generic/datavgen.cpp | 63 +++++++++++++++++++++++---- src/generic/listctrl.cpp | 27 +++++++++++- src/msw/listctrl.cpp | 22 ++++++++++ 7 files changed, 143 insertions(+), 9 deletions(-) diff --git a/include/wx/generic/private/listctrl.h b/include/wx/generic/private/listctrl.h index 4ee47ff1d4..30dc76f736 100644 --- a/include/wx/generic/private/listctrl.h +++ b/include/wx/generic/private/listctrl.h @@ -402,6 +402,10 @@ public: wxTextCtrl *GetText() const { return m_text; } + // Check if the given key event should stop editing and return true if it + // does or false otherwise. + bool CheckForEndEditKey(const wxKeyEvent& event); + // Different reasons for calling EndEdit(): // // It was called because: @@ -557,6 +561,7 @@ public: // called to switch the selection from the current item to newCurrent, void OnArrowChar( size_t newCurrent, const wxKeyEvent& event ); + void OnCharHook( wxKeyEvent &event ); void OnChar( wxKeyEvent &event ); void OnKeyDown( wxKeyEvent &event ); void OnKeyUp( wxKeyEvent &event ); diff --git a/include/wx/msw/listctrl.h b/include/wx/msw/listctrl.h index 280c3f842b..172197bf62 100644 --- a/include/wx/msw/listctrl.h +++ b/include/wx/msw/listctrl.h @@ -466,6 +466,10 @@ private: // destroy m_textCtrl if it's currently valid and reset it to NULL void DeleteEditControl(); + // Intercept Escape and Enter keys to avoid them being stolen from our + // in-place editor control. + void OnCharHook(wxKeyEvent& event); + DECLARE_DYNAMIC_CLASS(wxListCtrl) DECLARE_EVENT_TABLE() diff --git a/include/wx/treectrl.h b/include/wx/treectrl.h index b93495de26..b07eccb344 100644 --- a/include/wx/treectrl.h +++ b/include/wx/treectrl.h @@ -439,6 +439,13 @@ protected: bool m_quickBestSize; +private: + // Intercept Escape and Return keys to ensure that our in-place edit + // control always gets them before they're used for dialog navigation or + // anything else. + void OnCharHook(wxKeyEvent& event); + + wxDECLARE_NO_COPY_CLASS(wxTreeCtrlBase); }; diff --git a/src/common/treebase.cpp b/src/common/treebase.cpp index 06e31ddbc3..4c0965c435 100644 --- a/src/common/treebase.cpp +++ b/src/common/treebase.cpp @@ -179,6 +179,8 @@ wxTreeCtrlBase::wxTreeCtrlBase() // quick DoGetBestSize calculation m_quickBestSize = true; + + Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(wxTreeCtrlBase::OnCharHook)); } wxTreeCtrlBase::~wxTreeCtrlBase() @@ -349,4 +351,26 @@ bool wxTreeCtrlBase::IsEmpty() const return !GetRootItem().IsOk(); } +void wxTreeCtrlBase::OnCharHook(wxKeyEvent& event) +{ + if ( GetEditControl() ) + { + bool discardChanges = false; + switch ( event.GetKeyCode() ) + { + case WXK_ESCAPE: + discardChanges = true; + // fall through + + case WXK_RETURN: + EndEditLabel(GetSelection(), discardChanges); + + // Do not call Skip() below. + return; + } + } + + event.Skip(); +} + #endif // wxUSE_TREECTRL diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 63e562a411..8b1b000778 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -48,6 +48,7 @@ #include "wx/headerctrl.h" #include "wx/dnd.h" #include "wx/stopwatch.h" +#include "wx/weakref.h" //----------------------------------------------------------------------------- // classes @@ -599,6 +600,7 @@ public: wxBitmap CreateItemBitmap( unsigned int row, int &indent ); #endif // wxUSE_DRAG_AND_DROP void OnPaint( wxPaintEvent &event ); + void OnCharHook( wxKeyEvent &event ); void OnChar( wxKeyEvent &event ); void OnVerticalNavigation(unsigned int newCurrent, const wxKeyEvent& event); void OnLeftKey(); @@ -696,6 +698,10 @@ public: void OnColumnsCountChanged(); + // Called by wxDataViewCtrl and our own OnRenameTimer() to start edit the + // specified item in the given column. + void StartEditing(const wxDataViewItem& item, const wxDataViewColumn* col); + private: int RecalculateCount(); @@ -753,6 +759,12 @@ private: // This is the tree node under the cursor wxDataViewTreeNode * m_underMouse; + // The control used for editing or NULL. + wxWeakRef m_editorCtrl; + + // Id m_editorCtrl is non-NULL, pointer to the associated renderer. + wxDataViewRenderer* m_editorRenderer; + private: DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow) DECLARE_EVENT_TABLE() @@ -1338,6 +1350,7 @@ BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow) EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse) EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus) EVT_KILL_FOCUS (wxDataViewMainWindow::OnKillFocus) + EVT_CHAR_HOOK (wxDataViewMainWindow::OnCharHook) EVT_CHAR (wxDataViewMainWindow::OnChar) END_EVENT_TABLE() @@ -1349,6 +1362,8 @@ wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID i { SetOwner( parent ); + m_editorRenderer = NULL; + m_lastOnSame = false; m_renameTimer = new wxDataViewRenameTimer( this ); @@ -2005,9 +2020,25 @@ void wxDataViewMainWindow::OnRenameTimer() wxDataViewItem item = GetItemByRow( m_currentRow ); - wxRect labelRect = GetItemRect(item, m_currentCol); + StartEditing( item, m_currentCol ); +} - m_currentCol->GetRenderer()->StartEditing( item, labelRect ); +void +wxDataViewMainWindow::StartEditing(const wxDataViewItem& item, + const wxDataViewColumn* col) +{ + wxDataViewRenderer* renderer = col->GetRenderer(); + if (renderer->GetMode() != wxDATAVIEW_CELL_EDITABLE) + return; + + const wxRect itemRect = GetItemRect(item, col); + if ( renderer->StartEditing(item, itemRect) ) + { + // Save the renderer to be able to finish/cancel editing it later and + // save the control to be able to detect if we're still editing it. + m_editorRenderer = renderer; + m_editorCtrl = renderer->GetEditorCtrl(); + } } //----------------------------------------------------------------------------- @@ -3404,6 +3435,27 @@ wxDataViewMainWindow::FindColumnForEditing(const wxDataViewItem& item, wxDataVie return candidate; } +void wxDataViewMainWindow::OnCharHook(wxKeyEvent& event) +{ + if ( m_editorCtrl ) + { + // Handle any keys special for the in-place editor and return without + // calling Skip() below. + switch ( event.GetKeyCode() ) + { + case WXK_ESCAPE: + m_editorRenderer->CancelEditing(); + return; + + case WXK_RETURN: + m_editorRenderer->FinishEditing(); + return; + } + } + + event.Skip(); +} + void wxDataViewMainWindow::OnChar( wxKeyEvent &event ) { wxWindow * const parent = GetParent(); @@ -4939,12 +4991,7 @@ void wxDataViewCtrl::StartEditor( const wxDataViewItem & item, unsigned int colu if (!col) return; - wxDataViewRenderer* renderer = col->GetRenderer(); - if (renderer->GetMode() != wxDATAVIEW_CELL_EDITABLE) - return; - - const wxRect itemRect = GetItemRect(item, col); - renderer->StartEditing(item, itemRect); + m_clientArea->StartEditing(item, col); } #endif // !wxUSE_GENERICDATAVIEWCTRL diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index 5d4767f939..03e26f11e5 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -1461,6 +1461,12 @@ bool wxListTextCtrlWrapper::AcceptChanges() } void wxListTextCtrlWrapper::OnChar( wxKeyEvent &event ) +{ + if ( !CheckForEndEditKey(event) ) + event.Skip(); +} + +bool wxListTextCtrlWrapper::CheckForEndEditKey(const wxKeyEvent& event) { switch ( event.m_keyCode ) { @@ -1473,8 +1479,10 @@ void wxListTextCtrlWrapper::OnChar( wxKeyEvent &event ) break; default: - event.Skip(); + return false; } + + return true; } void wxListTextCtrlWrapper::OnKeyUp( wxKeyEvent &event ) @@ -1518,6 +1526,7 @@ void wxListTextCtrlWrapper::OnKillFocus( wxFocusEvent &event ) BEGIN_EVENT_TABLE(wxListMainWindow, wxWindow) EVT_PAINT (wxListMainWindow::OnPaint) EVT_MOUSE_EVENTS (wxListMainWindow::OnMouse) + EVT_CHAR_HOOK (wxListMainWindow::OnCharHook) EVT_CHAR (wxListMainWindow::OnChar) EVT_KEY_DOWN (wxListMainWindow::OnKeyDown) EVT_KEY_UP (wxListMainWindow::OnKeyUp) @@ -2713,6 +2722,22 @@ void wxListMainWindow::OnKeyUp( wxKeyEvent &event ) event.Skip(); } +void wxListMainWindow::OnCharHook( wxKeyEvent &event ) +{ + if ( m_textctrlWrapper ) + { + // When an in-place editor is active we should ensure that it always + // gets the key events that are special to it. + if ( m_textctrlWrapper->CheckForEndEditKey(event) ) + { + // Skip the call to wxEvent::Skip() below. + return; + } + } + + event.Skip(); +} + void wxListMainWindow::OnChar( wxKeyEvent &event ) { wxWindow *parent = GetParent(); diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index 1f06aa3074..5bc5c08c56 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -228,6 +228,7 @@ public: BEGIN_EVENT_TABLE(wxListCtrl, wxControl) EVT_PAINT(wxListCtrl::OnPaint) + EVT_CHAR_HOOK(wxListCtrl::OnCharHook) END_EVENT_TABLE() // ============================================================================ @@ -2973,6 +2974,27 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) } } +void wxListCtrl::OnCharHook(wxKeyEvent& event) +{ + if ( GetEditControl() ) + { + // We need to ensure that Escape is not stolen from the in-place editor + // by the containing dialog. + // + // Notice that we don't have to care about Enter key here as we return + // false from MSWShouldPreProcessMessage() for it. + if ( event.GetKeyCode() == WXK_ESCAPE ) + { + EndEditLabel(true /* cancel */); + + // Don't call Skip() below. + return; + } + } + + event.Skip(); +} + WXLRESULT wxListCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { -- 2.45.2