From a914f6e99270806dbcb5456596d7a61a9e71d71b Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Mon, 10 Apr 2023 15:12:35 +0100 Subject: [PATCH] First after an edit in a textEdit cancels the edit. (Second will exit the dialog.) Fixes https://gitlab.com/kicad/code/kicad/issues/14514 --- common/dialog_shim.cpp | 83 ++++++++++++++++++++++++-------------- common/grid_tricks.cpp | 8 ++++ common/widgets/wx_grid.cpp | 28 +++++++++++++ include/dialog_shim.h | 8 +++- include/widgets/wx_grid.h | 1 + 5 files changed, 95 insertions(+), 33 deletions(-) diff --git a/common/dialog_shim.cpp b/common/dialog_shim.cpp index 9372d4ca66..98ecab8b08 100644 --- a/common/dialog_shim.cpp +++ b/common/dialog_shim.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2012-2022 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2012-2023 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -67,10 +67,6 @@ public: BEGIN_EVENT_TABLE( DIALOG_SHIM, wxDialog ) - // If dialog has a grid and the grid has an active cell editor - // Esc key closes cell editor, otherwise Esc key closes the dialog. - EVT_GRID_EDITOR_SHOWN( DIALOG_SHIM::OnGridEditorShown ) - EVT_GRID_EDITOR_HIDDEN( DIALOG_SHIM::OnGridEditorHidden ) EVT_CHAR_HOOK( DIALOG_SHIM::OnCharHook ) END_EVENT_TABLE() @@ -325,30 +321,38 @@ bool DIALOG_SHIM::Enable( bool enable ) // Recursive descent doing a SelectAll() in wxTextCtrls. // MacOS User Interface Guidelines state that when tabbing to a text control all its // text should be selected. Since wxWidgets fails to implement this, we do it here. -static void selectAllInTextCtrls( wxWindowList& children ) +void DIALOG_SHIM::selectAllInTextCtrls( wxWindowList& children ) { for( wxWindow* child : children ) { - if( wxTextCtrl* childTextCtrl = dynamic_cast( child ) ) + if( wxTextCtrl* textCtrl = dynamic_cast( child ) ) { + m_beforeEditValues[ textCtrl ] = textCtrl->GetValue(); + textCtrl->Connect( wxEVT_SET_FOCUS, wxFocusEventHandler( DIALOG_SHIM::onChildSetFocus ), + nullptr, this ); + // We don't currently run this on GTK because some window managers don't hide the // selection in non-active controls, and other window managers do the selection // automatically anyway. #if defined( __WXMAC__ ) || defined( __WXMSW__ ) - if( !childTextCtrl->GetStringSelection().IsEmpty() ) + if( !textCtrl->GetStringSelection().IsEmpty() ) { // Respect an existing selection } - else if( childTextCtrl->IsEditable() ) + else if( textCtrl->IsEditable() ) { - childTextCtrl->SelectAll(); + textCtrl->SelectAll(); } #else - ignore_unused( childTextCtrl ); + ignore_unused( textCtrl ); #endif } else if( wxStyledTextCtrl* scintilla = dynamic_cast( child ) ) { + m_beforeEditValues[ scintilla ] = scintilla->GetText(); + scintilla->Connect( wxEVT_SET_FOCUS, wxFocusEventHandler( DIALOG_SHIM::onChildSetFocus ), + nullptr, this ); + if( !scintilla->GetSelectedText().IsEmpty() ) { // Respect an existing selection @@ -533,11 +537,6 @@ void DIALOG_SHIM::OnButton( wxCommandEvent& aEvent ) { const int id = aEvent.GetId(); - // If we are pressing a button to exit, we need to enable the escapeID - // otherwise the dialog does not process cancel - if( id == wxID_CANCEL ) - SetEscapeId( wxID_ANY ); - if( IsQuasiModal() ) { if( id == GetAffirmativeId() ) @@ -555,7 +554,7 @@ void DIALOG_SHIM::OnButton( wxCommandEvent& aEvent ) ignore_unused( TransferDataFromWindow() ); } } - else if( id == GetEscapeId() || (id == wxID_CANCEL && GetEscapeId() == wxID_ANY) ) + else if( id == wxID_CANCEL ) { EndQuasiModal( wxID_CANCEL ); } @@ -572,6 +571,17 @@ void DIALOG_SHIM::OnButton( wxCommandEvent& aEvent ) } +void DIALOG_SHIM::onChildSetFocus( wxFocusEvent& aEvent ) +{ + // When setting focus to a text control reset the before-edit value. + + if( wxTextCtrl* textCtrl = dynamic_cast( aEvent.GetEventObject() ) ) + m_beforeEditValues[ textCtrl ] = textCtrl->GetValue(); + else if( wxStyledTextCtrl* scintilla = dynamic_cast( aEvent.GetEventObject() ) ) + m_beforeEditValues[ scintilla ] = scintilla->GetText(); +} + + void DIALOG_SHIM::OnCharHook( wxKeyEvent& aEvt ) { if( aEvt.GetKeyCode() == 'U' && aEvt.GetModifiers() == wxMOD_CONTROL ) @@ -627,25 +637,36 @@ void DIALOG_SHIM::OnCharHook( wxKeyEvent& aEvt ) return; } } + else if( aEvt.GetKeyCode() == WXK_ESCAPE ) + { + wxObject* eventSource = aEvt.GetEventObject(); + + if( wxTextCtrl* textCtrl = dynamic_cast( eventSource ) ) + { + // First escape after an edit cancels edit + if( textCtrl->GetValue() != m_beforeEditValues[ textCtrl ] ) + { + textCtrl->SetValue( m_beforeEditValues[ textCtrl ] ); + textCtrl->SelectAll(); + return; + } + } + else if( wxStyledTextCtrl* scintilla = dynamic_cast( eventSource ) ) + { + // First escape after an edit cancels edit + if( scintilla->GetText() != m_beforeEditValues[ scintilla ] ) + { + scintilla->SetText( m_beforeEditValues[ scintilla ] ); + scintilla->SelectAll(); + return; + } + } + } aEvt.Skip(); } -void DIALOG_SHIM::OnGridEditorShown( wxGridEvent& event ) -{ - SetEscapeId( wxID_NONE ); - event.Skip(); -} - - -void DIALOG_SHIM::OnGridEditorHidden( wxGridEvent& event ) -{ - SetEscapeId( wxID_ANY ); - event.Skip(); -} - - static void recursiveDescent( wxSizer* aSizer, std::map& aLabels ) { wxStdDialogButtonSizer* sdbSizer = dynamic_cast( aSizer ); diff --git a/common/grid_tricks.cpp b/common/grid_tricks.cpp index 1e126bb689..1841776716 100644 --- a/common/grid_tricks.cpp +++ b/common/grid_tricks.cpp @@ -494,6 +494,14 @@ void GRID_TRICKS::onCharHook( wxKeyEvent& ev ) m_grid->ForceRefresh(); } } + else if( ev.GetKeyCode() == WXK_ESCAPE ) + { + if( m_grid->IsCellEditControlShown() ) + { + m_grid->CancelPendingChanges(); + handled = true; + } + } if( !handled ) ev.Skip( true ); diff --git a/common/widgets/wx_grid.cpp b/common/widgets/wx_grid.cpp index 54c99dc475..e584267d49 100644 --- a/common/widgets/wx_grid.cpp +++ b/common/widgets/wx_grid.cpp @@ -420,6 +420,34 @@ void WX_GRID::DrawRowLabel( wxDC& dc, int row ) } +bool WX_GRID::CancelPendingChanges() +{ + if( !IsCellEditControlEnabled() ) + return true; + + HideCellEditControl(); + + // do it after HideCellEditControl() + m_cellEditCtrlEnabled = false; + + int row = m_currentCellCoords.GetRow(); + int col = m_currentCellCoords.GetCol(); + + wxString oldval = GetCellValue( row, col ); + wxString newval; + + wxGridCellAttr* attr = GetCellAttr( row, col ); + wxGridCellEditor* editor = attr->GetEditor( this, row, col ); + + bool changed = editor->EndEdit( row, col, this, oldval, &newval ); + + editor->DecRef(); + attr->DecRef(); + + return true; +} + + bool WX_GRID::CommitPendingChanges( bool aQuietMode ) { if( !IsCellEditControlEnabled() ) diff --git a/include/dialog_shim.h b/include/dialog_shim.h index ca0c5cfd80..ac6bdc56c4 100644 --- a/include/dialog_shim.h +++ b/include/dialog_shim.h @@ -181,6 +181,8 @@ protected: virtual void OnCharHook( wxKeyEvent& aEvt ); private: + void selectAllInTextCtrls( wxWindowList& children ); + /** * Properly handle the wxCloseEvent when in the quasimodal mode when not calling * EndQuasiModal which is possible with any dialog derived from #DIALOG_SHIM. @@ -193,8 +195,7 @@ private: */ void OnButton( wxCommandEvent& aEvent ); - void OnGridEditorShown( wxGridEvent& event ); - void OnGridEditorHidden( wxGridEvent& event ); + void onChildSetFocus( wxFocusEvent& aEvent ); DECLARE_EVENT_TABLE(); @@ -223,6 +224,9 @@ protected: // The size asked by the caller, used the first time the dialog is created wxSize m_initialSize; + + // Used to support first-esc-cancels-edit logic + std::map m_beforeEditValues; }; #endif // DIALOG_SHIM_ diff --git a/include/widgets/wx_grid.h b/include/widgets/wx_grid.h index 09d979b572..a41182171b 100644 --- a/include/widgets/wx_grid.h +++ b/include/widgets/wx_grid.h @@ -86,6 +86,7 @@ public: * @return false if validation failed */ bool CommitPendingChanges( bool aQuietMode = false ); + bool CancelPendingChanges(); /** * Set a UNITS_PROVIDER to enable use of unit- and eval-based Getters.