First <ESC> after an edit in a textEdit cancels the edit.

(Second will exit the dialog.)

Fixes https://gitlab.com/kicad/code/kicad/issues/14514
This commit is contained in:
Jeff Young 2023-04-10 15:12:35 +01:00
parent f01e083f7c
commit a914f6e992
5 changed files with 95 additions and 33 deletions

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -67,10 +67,6 @@ public:
BEGIN_EVENT_TABLE( DIALOG_SHIM, wxDialog ) 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 ) EVT_CHAR_HOOK( DIALOG_SHIM::OnCharHook )
END_EVENT_TABLE() END_EVENT_TABLE()
@ -325,30 +321,38 @@ bool DIALOG_SHIM::Enable( bool enable )
// Recursive descent doing a SelectAll() in wxTextCtrls. // Recursive descent doing a SelectAll() in wxTextCtrls.
// MacOS User Interface Guidelines state that when tabbing to a text control all its // 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. // 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 ) for( wxWindow* child : children )
{ {
if( wxTextCtrl* childTextCtrl = dynamic_cast<wxTextCtrl*>( child ) ) if( wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( 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 // 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 // selection in non-active controls, and other window managers do the selection
// automatically anyway. // automatically anyway.
#if defined( __WXMAC__ ) || defined( __WXMSW__ ) #if defined( __WXMAC__ ) || defined( __WXMSW__ )
if( !childTextCtrl->GetStringSelection().IsEmpty() ) if( !textCtrl->GetStringSelection().IsEmpty() )
{ {
// Respect an existing selection // Respect an existing selection
} }
else if( childTextCtrl->IsEditable() ) else if( textCtrl->IsEditable() )
{ {
childTextCtrl->SelectAll(); textCtrl->SelectAll();
} }
#else #else
ignore_unused( childTextCtrl ); ignore_unused( textCtrl );
#endif #endif
} }
else if( wxStyledTextCtrl* scintilla = dynamic_cast<wxStyledTextCtrl*>( child ) ) else if( wxStyledTextCtrl* scintilla = dynamic_cast<wxStyledTextCtrl*>( child ) )
{ {
m_beforeEditValues[ scintilla ] = scintilla->GetText();
scintilla->Connect( wxEVT_SET_FOCUS, wxFocusEventHandler( DIALOG_SHIM::onChildSetFocus ),
nullptr, this );
if( !scintilla->GetSelectedText().IsEmpty() ) if( !scintilla->GetSelectedText().IsEmpty() )
{ {
// Respect an existing selection // Respect an existing selection
@ -533,11 +537,6 @@ void DIALOG_SHIM::OnButton( wxCommandEvent& aEvent )
{ {
const int id = aEvent.GetId(); 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( IsQuasiModal() )
{ {
if( id == GetAffirmativeId() ) if( id == GetAffirmativeId() )
@ -555,7 +554,7 @@ void DIALOG_SHIM::OnButton( wxCommandEvent& aEvent )
ignore_unused( TransferDataFromWindow() ); ignore_unused( TransferDataFromWindow() );
} }
} }
else if( id == GetEscapeId() || (id == wxID_CANCEL && GetEscapeId() == wxID_ANY) ) else if( id == wxID_CANCEL )
{ {
EndQuasiModal( 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<wxTextCtrl*>( aEvent.GetEventObject() ) )
m_beforeEditValues[ textCtrl ] = textCtrl->GetValue();
else if( wxStyledTextCtrl* scintilla = dynamic_cast<wxStyledTextCtrl*>( aEvent.GetEventObject() ) )
m_beforeEditValues[ scintilla ] = scintilla->GetText();
}
void DIALOG_SHIM::OnCharHook( wxKeyEvent& aEvt ) void DIALOG_SHIM::OnCharHook( wxKeyEvent& aEvt )
{ {
if( aEvt.GetKeyCode() == 'U' && aEvt.GetModifiers() == wxMOD_CONTROL ) if( aEvt.GetKeyCode() == 'U' && aEvt.GetModifiers() == wxMOD_CONTROL )
@ -627,25 +637,36 @@ void DIALOG_SHIM::OnCharHook( wxKeyEvent& aEvt )
return; return;
} }
} }
else if( aEvt.GetKeyCode() == WXK_ESCAPE )
{
wxObject* eventSource = aEvt.GetEventObject();
if( wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( 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<wxStyledTextCtrl*>( eventSource ) )
{
// First escape after an edit cancels edit
if( scintilla->GetText() != m_beforeEditValues[ scintilla ] )
{
scintilla->SetText( m_beforeEditValues[ scintilla ] );
scintilla->SelectAll();
return;
}
}
}
aEvt.Skip(); 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<int, wxString>& aLabels ) static void recursiveDescent( wxSizer* aSizer, std::map<int, wxString>& aLabels )
{ {
wxStdDialogButtonSizer* sdbSizer = dynamic_cast<wxStdDialogButtonSizer*>( aSizer ); wxStdDialogButtonSizer* sdbSizer = dynamic_cast<wxStdDialogButtonSizer*>( aSizer );

View File

@ -494,6 +494,14 @@ void GRID_TRICKS::onCharHook( wxKeyEvent& ev )
m_grid->ForceRefresh(); m_grid->ForceRefresh();
} }
} }
else if( ev.GetKeyCode() == WXK_ESCAPE )
{
if( m_grid->IsCellEditControlShown() )
{
m_grid->CancelPendingChanges();
handled = true;
}
}
if( !handled ) if( !handled )
ev.Skip( true ); ev.Skip( true );

View File

@ -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 ) bool WX_GRID::CommitPendingChanges( bool aQuietMode )
{ {
if( !IsCellEditControlEnabled() ) if( !IsCellEditControlEnabled() )

View File

@ -181,6 +181,8 @@ protected:
virtual void OnCharHook( wxKeyEvent& aEvt ); virtual void OnCharHook( wxKeyEvent& aEvt );
private: private:
void selectAllInTextCtrls( wxWindowList& children );
/** /**
* Properly handle the wxCloseEvent when in the quasimodal mode when not calling * Properly handle the wxCloseEvent when in the quasimodal mode when not calling
* EndQuasiModal which is possible with any dialog derived from #DIALOG_SHIM. * EndQuasiModal which is possible with any dialog derived from #DIALOG_SHIM.
@ -193,8 +195,7 @@ private:
*/ */
void OnButton( wxCommandEvent& aEvent ); void OnButton( wxCommandEvent& aEvent );
void OnGridEditorShown( wxGridEvent& event ); void onChildSetFocus( wxFocusEvent& aEvent );
void OnGridEditorHidden( wxGridEvent& event );
DECLARE_EVENT_TABLE(); DECLARE_EVENT_TABLE();
@ -223,6 +224,9 @@ protected:
// The size asked by the caller, used the first time the dialog is created // The size asked by the caller, used the first time the dialog is created
wxSize m_initialSize; wxSize m_initialSize;
// Used to support first-esc-cancels-edit logic
std::map<wxWindow*, wxString> m_beforeEditValues;
}; };
#endif // DIALOG_SHIM_ #endif // DIALOG_SHIM_

View File

@ -86,6 +86,7 @@ public:
* @return false if validation failed * @return false if validation failed
*/ */
bool CommitPendingChanges( bool aQuietMode = false ); bool CommitPendingChanges( bool aQuietMode = false );
bool CancelPendingChanges();
/** /**
* Set a UNITS_PROVIDER to enable use of unit- and eval-based Getters. * Set a UNITS_PROVIDER to enable use of unit- and eval-based Getters.