Hotkeys: add support for alternate hotkeys

Fixes: https://gitlab.com/kicad/code/kicad/-/issues/4422
This commit is contained in:
Mike Williams 2023-07-13 10:08:00 -04:00
parent 4cad021ef4
commit c0a5be4e9f
11 changed files with 132 additions and 52 deletions

View File

@ -191,7 +191,7 @@ void PANEL_HOTKEYS_EDITOR::ImportHotKeys()
if( filename.IsEmpty() )
return;
std::map<std::string, int> importedHotKeys;
std::map<std::string, std::pair<int, int>> importedHotKeys;
ReadHotKeyConfig( filename, importedHotKeys );
m_frame->SetMruPath( wxFileName( filename ).GetPath() );
@ -201,7 +201,10 @@ void PANEL_HOTKEYS_EDITOR::ImportHotKeys()
for( HOTKEY& hotkey: section.m_HotKeys )
{
if( importedHotKeys.count( hotkey.m_Actions[ 0 ]->GetName() ) )
hotkey.m_EditKeycode = importedHotKeys[ hotkey.m_Actions[ 0 ]->GetName() ];
{
hotkey.m_EditKeycode = importedHotKeys[ hotkey.m_Actions[ 0 ]->GetName() ].first;
hotkey.m_EditKeycodeAlt = importedHotKeys[ hotkey.m_Actions[ 0 ]->GetName() ].second;
}
}
}

View File

@ -114,7 +114,10 @@ void HOTKEY_STORE::Init( std::vector<TOOL_ACTION*> aActionsList, bool aIncludeRe
hotkey.m_Actions.push_back( action );
if( !hotkey.m_EditKeycode )
{
hotkey.m_EditKeycode = action->GetHotKey();
hotkey.m_EditKeycodeAlt = action->GetHotKeyAlt();
}
}
wxString currentApp;
@ -170,7 +173,7 @@ void HOTKEY_STORE::SaveAllHotkeys()
for( HOTKEY& hotkey : section.m_HotKeys )
{
for( TOOL_ACTION* action : hotkey.m_Actions )
action->SetHotKey( hotkey.m_EditKeycode );
action->SetHotKey( hotkey.m_EditKeycode, hotkey.m_EditKeycodeAlt );
}
}
}
@ -181,7 +184,10 @@ void HOTKEY_STORE::ResetAllHotkeysToDefault()
for( HOTKEY_SECTION& section : m_hk_sections )
{
for( HOTKEY& hotkey : section.m_HotKeys )
hotkey.m_EditKeycode = hotkey.m_Actions[ 0 ]->GetDefaultHotKey();
{
hotkey.m_EditKeycode = hotkey.m_Actions[ 0 ]->GetDefaultHotKey();
hotkey.m_EditKeycodeAlt = hotkey.m_Actions[ 0 ]->GetDefaultHotKeyAlt();
}
}
}
@ -191,7 +197,10 @@ void HOTKEY_STORE::ResetAllHotkeysToOriginal()
for( HOTKEY_SECTION& section : m_hk_sections )
{
for( HOTKEY& hotkey : section.m_HotKeys )
hotkey.m_EditKeycode = hotkey.m_Actions[ 0 ]->GetHotKey();
{
hotkey.m_EditKeycode = hotkey.m_Actions[ 0 ]->GetHotKey();
hotkey.m_EditKeycodeAlt = hotkey.m_Actions[ 0 ]->GetHotKeyAlt();
}
}
}
@ -219,7 +228,7 @@ bool HOTKEY_STORE::CheckKeyConflicts( TOOL_ACTION* aAction, long aKey, HOTKEY**
if( hotkey.m_Actions[0] == aAction )
continue;
if( hotkey.m_EditKeycode == aKey )
if( hotkey.m_EditKeycode == aKey || hotkey.m_EditKeycodeAlt == aKey )
{
// We can use the same key for a different action if both actions are contextual and
// for different tools.

View File

@ -348,7 +348,8 @@ void DisplayHotkeyList( EDA_BASE_FRAME* aParent )
}
void ReadHotKeyConfig( const wxString& aFileName, std::map<std::string, int>& aHotKeys )
void ReadHotKeyConfig( const wxString& aFileName,
std::map<std::string, std::pair<int, int>>& aHotKeys )
{
wxString fileName = aFileName;
@ -377,18 +378,20 @@ void ReadHotKeyConfig( const wxString& aFileName, std::map<std::string, int>& aH
{
wxStringTokenizer lineTokenizer( fileTokenizer.GetNextToken(), wxS( "\t" ) );
wxString cmdName = lineTokenizer.GetNextToken();
wxString keyName = lineTokenizer.GetNextToken();
wxString cmdName = lineTokenizer.GetNextToken();
wxString primary = lineTokenizer.GetNextToken();
wxString secondary = lineTokenizer.GetNextToken();
if( !cmdName.IsEmpty() )
aHotKeys[ cmdName.ToStdString() ] = KeyCodeFromKeyName( keyName );
aHotKeys[cmdName.ToStdString()] = std::pair<int, int>(
KeyCodeFromKeyName( primary ), KeyCodeFromKeyName( secondary ) );
}
}
void ReadHotKeyConfigIntoActions( const wxString& aFileName, std::vector<TOOL_ACTION*>& aActions )
{
std::map<std::string, int> hotkeys;
std::map<std::string, std::pair<int, int>> hotkeys;
// Read the existing config (all hotkeys)
ReadHotKeyConfig( aFileName, hotkeys );
@ -396,13 +399,16 @@ void ReadHotKeyConfigIntoActions( const wxString& aFileName, std::vector<TOOL_AC
// Set each tool action hotkey to the config file hotkey if present
for( TOOL_ACTION* action : aActions )
if( hotkeys.find( action->GetName() ) != hotkeys.end() )
action->SetHotKey( hotkeys[action->GetName()] );
{
std::pair<int, int> keys = hotkeys[action->GetName()];
action->SetHotKey( keys.first, keys.second );
}
}
int WriteHotKeyConfig( const std::vector<TOOL_ACTION*>& aActions )
{
std::map<std::string, int> hotkeys;
std::map<std::string, std::pair<int, int>> hotkeys;
wxFileName fn( "user" );
fn.SetExt( HotkeyFileExtension );
@ -413,14 +419,16 @@ int WriteHotKeyConfig( const std::vector<TOOL_ACTION*>& aActions )
// Overlay the current app's hotkey definitions onto the map
for( const TOOL_ACTION* action : aActions )
hotkeys[ action->GetName() ] = action->GetHotKey();
hotkeys[ action->GetName() ] = std::pair<int, int>( action->GetHotKey(), action->GetHotKeyAlt() );
// Write entire hotkey set
wxFFileOutputStream outStream( fn.GetFullPath() );
wxTextOutputStream txtStream( outStream, wxEOL_UNIX );
for( const std::pair<const std::string, int>& entry : hotkeys )
txtStream << entry.first << "\t" << KeyNameFromKeyCode( entry.second ) << endl;
for( const std::pair<const std::string, std::pair<int, int>>& entry : hotkeys )
txtStream << entry.first
<< "\t" << KeyNameFromKeyCode( entry.second.first )
<< "\t" << KeyNameFromKeyCode( entry.second.second ) << endl;
txtStream.Flush();
outStream.Close();

View File

@ -268,7 +268,7 @@ int ACTION_MANAGER::GetHotKey( const TOOL_ACTION& aAction ) const
void ACTION_MANAGER::UpdateHotKeys( bool aFullUpdate )
{
static std::map<std::string, int> legacyHotKeyMap;
static std::map<std::string, int> userHotKeyMap;
static std::map<std::string, std::pair<int, int>> userHotKeyMap;
static bool mapsInitialized = false;
m_actionHotKeys.clear();
@ -285,23 +285,28 @@ void ACTION_MANAGER::UpdateHotKeys( bool aFullUpdate )
{
TOOL_ACTION* action = ii.second;
int hotkey = 0;
int alt = 0;
if( aFullUpdate )
hotkey = processHotKey( action, legacyHotKeyMap, userHotKeyMap );
else
hotkey = action->GetHotKey();
processHotKey( action, legacyHotKeyMap, userHotKeyMap );
hotkey = action->GetHotKey();
alt = action->GetHotKeyAlt();
if( hotkey > 0 )
m_actionHotKeys[hotkey].push_back( action );
if( alt > 0 )
m_actionHotKeys[alt].push_back( action );
m_hotkeys[action->GetId()] = hotkey;
}
}
int ACTION_MANAGER::processHotKey( TOOL_ACTION* aAction,
const std::map<std::string, int>& aLegacyMap,
const std::map<std::string, int>& aHotKeyMap )
void ACTION_MANAGER::processHotKey( TOOL_ACTION* aAction,
const std::map<std::string, int>& aLegacyMap,
const std::map<std::string, std::pair<int, int>>& aHotKeyMap )
{
aAction->m_hotKey = aAction->m_defaultHotKey;
@ -309,7 +314,8 @@ int ACTION_MANAGER::processHotKey( TOOL_ACTION* aAction,
aAction->SetHotKey( aLegacyMap.at( aAction->m_legacyName ) );
if( aHotKeyMap.count( aAction->m_name ) )
aAction->SetHotKey( aHotKeyMap.at( aAction->m_name ) );
return aAction->m_hotKey;
{
std::pair<int, int> keys = aHotKeyMap.at( aAction->m_name );
aAction->SetHotKey( keys.first, keys.second );
}
}

View File

@ -42,6 +42,7 @@ TOOL_ACTION::TOOL_ACTION( const std::string& aName, TOOL_ACTION_SCOPE aScope,
m_name( aName ),
m_scope( aScope ),
m_defaultHotKey( aDefaultHotKey ),
m_defaultHotKeyAlt( 0 ),
m_legacyName( aLegacyHotKeyName ),
m_label( aLabel ),
m_tooltip( aTooltip ),
@ -57,6 +58,7 @@ TOOL_ACTION::TOOL_ACTION( const std::string& aName, TOOL_ACTION_SCOPE aScope,
TOOL_ACTION::TOOL_ACTION() :
m_scope( AS_GLOBAL ),
m_defaultHotKey( 0 ),
m_defaultHotKeyAlt( 0 ),
m_icon( BITMAPS::INVALID_BITMAP ),
m_id( -1 ),
m_flags( AF_NONE )
@ -69,6 +71,7 @@ TOOL_ACTION::TOOL_ACTION( const TOOL_ACTION_ARGS& aArgs ) :
m_name( aArgs.m_name.value_or( "" ) ),
m_scope( aArgs.m_scope.value_or( AS_CONTEXT ) ),
m_defaultHotKey( aArgs.m_defaultHotKey.value_or( 0 ) ),
m_defaultHotKeyAlt( aArgs.m_defaultHotKeyAlt.value_or( 0 ) ),
m_hotKey( aArgs.m_defaultHotKey.value_or( 0 ) ),
m_legacyName( aArgs.m_legacyName.value_or( "" ) ),
m_label( TowxString( aArgs.m_menuText.value_or( "" ) ) ),
@ -153,9 +156,10 @@ wxString TOOL_ACTION::GetTooltip( bool aIncludeHotkey ) const
}
void TOOL_ACTION::SetHotKey( int aKeycode )
void TOOL_ACTION::SetHotKey( int aKeycode, int aKeycodeAlt )
{
m_hotKey = aKeycode;
m_hotKeyAlt = aKeycodeAlt;
}

View File

@ -43,9 +43,11 @@
enum ID_WHKL_MENU_IDS
{
ID_EDIT_HOTKEY = 2001,
ID_EDIT_ALT,
ID_RESET,
ID_DEFAULT,
ID_CLEAR
ID_CLEAR,
ID_CLEAR_ALT,
};
@ -291,6 +293,7 @@ void WIDGET_HOTKEY_LIST::updateFromClientData()
const HOTKEY& changed_hk = hkdata->GetChangedHotkey();
wxString label = changed_hk.m_Actions[ 0 ]->GetLabel();
wxString key_text = KeyNameFromKeyCode( changed_hk.m_EditKeycode );
wxString alt_text = KeyNameFromKeyCode( changed_hk.m_EditKeycodeAlt );
wxString description = changed_hk.m_Actions[ 0 ]->GetDescription();
if( label.IsEmpty() )
@ -307,14 +310,15 @@ void WIDGET_HOTKEY_LIST::updateFromClientData()
description.Replace( wxS( "\r" ), wxS( " " ) );
SetItemText( i, 0, label );
SetItemText( i, 1, key_text);
SetItemText( i, 2, description );
SetItemText( i, 1, key_text );
SetItemText( i, 2, alt_text );
SetItemText( i, 3, description );
}
}
}
void WIDGET_HOTKEY_LIST::changeHotkey( HOTKEY& aHotkey, long aKey )
void WIDGET_HOTKEY_LIST::changeHotkey( HOTKEY& aHotkey, long aKey, bool alternate )
{
// See if this key code is handled in hotkeys names list
bool exists;
@ -323,12 +327,17 @@ void WIDGET_HOTKEY_LIST::changeHotkey( HOTKEY& aHotkey, long aKey )
if( exists && aHotkey.m_EditKeycode != aKey )
{
if( aKey == 0 || resolveKeyConflicts( aHotkey.m_Actions[ 0 ], aKey ) )
aHotkey.m_EditKeycode = aKey;
{
if( alternate )
aHotkey.m_EditKeycodeAlt = aKey;
else
aHotkey.m_EditKeycode = aKey;
}
}
}
void WIDGET_HOTKEY_LIST::editItem( wxTreeListItem aItem )
void WIDGET_HOTKEY_LIST::editItem( wxTreeListItem aItem, int aEditId )
{
WIDGET_HOTKEY_CLIENT_DATA* hkdata = getExpectedHkClientData( aItem );
@ -336,7 +345,8 @@ void WIDGET_HOTKEY_LIST::editItem( wxTreeListItem aItem )
return;
wxString name = GetItemText( aItem, 0 );
wxString current_key = GetItemText( aItem, 1 );
wxString current_key =
aEditId == ID_EDIT_HOTKEY ? GetItemText( aItem, 1 ) : GetItemText( aItem, 2 );
wxKeyEvent key_event = HK_PROMPT_DIALOG::PromptForKey( this, name, current_key );
long key = MapKeypressToKeycode( key_event );
@ -355,7 +365,7 @@ void WIDGET_HOTKEY_LIST::editItem( wxTreeListItem aItem )
return;
}
changeHotkey( hkdata->GetChangedHotkey(), key );
changeHotkey( hkdata->GetChangedHotkey(), key, aEditId == ID_EDIT_ALT );
updateFromClientData();
}
}
@ -371,11 +381,19 @@ void WIDGET_HOTKEY_LIST::resetItem( wxTreeListItem aItem, int aResetId )
HOTKEY& changed_hk = hkdata->GetChangedHotkey();
if( aResetId == ID_RESET )
changeHotkey( changed_hk, changed_hk.m_Actions[ 0 ]->GetHotKey() );
{
changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetHotKey(), false );
changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetHotKey(), true );
}
else if( aResetId == ID_CLEAR )
changeHotkey( changed_hk, 0 );
changeHotkey( changed_hk, 0, false );
else if( aResetId == ID_CLEAR_ALT )
changeHotkey( changed_hk, 0, true );
else if( aResetId == ID_DEFAULT )
changeHotkey( changed_hk, changed_hk.m_Actions[ 0 ]->GetDefaultHotKey() );
{
changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetDefaultHotKey(), false );
changeHotkey( changed_hk, changed_hk.m_Actions[0]->GetDefaultHotKeyAlt(), true );
}
updateFromClientData();
}
@ -383,7 +401,7 @@ void WIDGET_HOTKEY_LIST::resetItem( wxTreeListItem aItem, int aResetId )
void WIDGET_HOTKEY_LIST::onActivated( wxTreeListEvent& aEvent )
{
editItem( aEvent.GetItem());
editItem( aEvent.GetItem(), ID_EDIT_HOTKEY );
}
@ -400,9 +418,11 @@ void WIDGET_HOTKEY_LIST::onContextMenu( wxTreeListEvent& aEvent )
if( hkdata )
{
menu.Append( ID_EDIT_HOTKEY, _( "Edit..." ) );
menu.Append( ID_EDIT_ALT, _( "Edit Alternate..." ) );
menu.Append( ID_RESET, _( "Undo Changes" ) );
menu.Append( ID_CLEAR, _( "Clear Assigned Hotkey" ) );
menu.Append( ID_DEFAULT, _( "Restore Default" ) );
menu.Append( ID_CLEAR_ALT, _( "Clear Assigned Alternate" ) );
menu.Append( ID_DEFAULT, _( "Restore Defaults" ) );
menu.Append( wxID_SEPARATOR );
PopupMenu( &menu );
@ -415,13 +435,15 @@ void WIDGET_HOTKEY_LIST::onMenu( wxCommandEvent& aEvent )
switch( aEvent.GetId() )
{
case ID_EDIT_HOTKEY:
editItem( m_context_menu_item );
case ID_EDIT_ALT:
editItem( m_context_menu_item, aEvent.GetId() );
break;
case ID_RESET:
case ID_CLEAR:
case ID_CLEAR_ALT:
case ID_DEFAULT:
resetItem( m_context_menu_item, aEvent.GetId());
resetItem( m_context_menu_item, aEvent.GetId() );
break;
default:
@ -473,6 +495,7 @@ WIDGET_HOTKEY_LIST::WIDGET_HOTKEY_LIST( wxWindow* aParent, HOTKEY_STORE& aHotkey
AppendColumn( command_header, 450, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
AppendColumn( _( "Hotkey" ), 120, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
AppendColumn( _( "Alternate" ), 120, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
AppendColumn( _( "Description" ), 900, wxALIGN_LEFT, wxCOL_RESIZABLE | wxCOL_SORTABLE );
@ -487,7 +510,8 @@ WIDGET_HOTKEY_LIST::WIDGET_HOTKEY_LIST( wxWindow* aParent, HOTKEY_STORE& aHotkey
dv->GetColumn( 0 )->SetMinWidth( aParent->GetTextExtent( command_header ).x * 2 + pad );
dv->GetColumn( 1 )->SetMinWidth( aParent->GetTextExtent( longKey ).x + pad );
dv->GetColumn( 2 )->SetMinWidth( aParent->GetTextExtent( command_header ).x * 5 + pad );
dv->GetColumn( 2 )->SetMinWidth( aParent->GetTextExtent( longKey ).x + pad );
dv->GetColumn( 3 )->SetMinWidth( aParent->GetTextExtent( command_header ).x * 5 + pad );
CallAfter( [&]()
{
@ -579,6 +603,14 @@ void WIDGET_HOTKEY_LIST::updateColumnWidths()
col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
col->SetWidth( col->GetWidth() );
#if defined( __WXGTK__ ) && !wxCHECK_VERSION( 3, 1, 0 )
col->SetResizeable( true );
#endif
col = GetDataView()->GetColumn( 3 );
col->SetWidth( wxCOL_WIDTH_AUTOSIZE );
col->SetWidth( col->GetWidth() );
#if defined( __WXGTK__ ) && !wxCHECK_VERSION( 3, 1, 0 )
col->SetResizeable( true );
#endif

View File

@ -35,13 +35,15 @@ struct HOTKEY
{
std::vector<TOOL_ACTION*> m_Actions;
int m_EditKeycode;
int m_EditKeycodeAlt;
HOTKEY() :
m_EditKeycode( 0 )
{ }
HOTKEY( TOOL_ACTION* aAction ) :
m_EditKeycode( aAction->GetHotKey() )
m_EditKeycode( aAction->GetHotKey() ),
m_EditKeycodeAlt( aAction->GetHotKeyAlt() )
{
m_Actions.push_back( aAction );
}
@ -111,4 +113,4 @@ private:
std::vector<HOTKEY_SECTION> m_hk_sections;
};
#endif // HOTKEY_STORE__H
#endif // HOTKEY_STORE__H

View File

@ -101,7 +101,8 @@ void DisplayHotkeyList( EDA_BASE_FRAME* aFrame );
*
* If \a aFileName is empty it will read in the default hotkeys file.
*/
void ReadHotKeyConfig( const wxString& aFileName, std::map<std::string, int>& aHotKeys );
void ReadHotKeyConfig( const wxString& aFileName,
std::map<std::string, std::pair<int, int>>& aHotKeys );
/**
* Reads a hotkey config file into a list of actions

View File

@ -180,8 +180,8 @@ public:
private:
// Resolve a hotkey by applying legacy and current settings over the action's
// default hotkey.
int processHotKey( TOOL_ACTION* aAction, const std::map<std::string, int>& aLegacyMap,
const std::map<std::string, int>& aHotKeyMap );
void processHotKey( TOOL_ACTION* aAction, const std::map<std::string, int>& aLegacyMap,
const std::map<std::string, std::pair<int, int>>& aHotKeyMap );
///< Tool manager needed to run actions
TOOL_MANAGER* m_toolMgr;

View File

@ -97,6 +97,15 @@ public:
return *this;
}
/**
* The default alternate hotkey to assign to the action.
*/
TOOL_ACTION_ARGS& DefaultHotkeyAlt( int aDefaultHotkeyAlt )
{
m_defaultHotKeyAlt = aDefaultHotkeyAlt;
return *this;
}
/**
* The legacy hotkey name from the old system.
*
@ -183,6 +192,7 @@ protected:
std::optional<int> m_uiid;
std::optional<int> m_defaultHotKey;
std::optional<int> m_defaultHotKeyAlt;
std::optional<std::string_view> m_legacyName;
std::optional<std::string_view> m_menuText;
@ -245,12 +255,14 @@ public:
* Return the default hotkey (if any) for the action.
*/
int GetDefaultHotKey() const { return m_defaultHotKey; }
int GetDefaultHotKeyAlt() const { return m_defaultHotKeyAlt; }
/**
* Return the hotkey keycode which initiates the action.
*/
int GetHotKey() const { return m_hotKey; }
void SetHotKey( int aKeycode );
int GetHotKeyAlt() const { return m_hotKeyAlt; }
void SetHotKey( int aKeycode, int aKeycodeAlt = 0 );
/**
* Return the unique id of the TOOL_ACTION object.
@ -364,8 +376,10 @@ protected:
std::string m_name;
TOOL_ACTION_SCOPE m_scope;
const int m_defaultHotKey; // Default hot key
const int m_defaultHotKey; // Default hot key
const int m_defaultHotKeyAlt; // Default hot key alternate
int m_hotKey; // The current hotkey (post-user-settings-application)
int m_hotKeyAlt; // The alternate hotkey (post-user-settings-application)
const std::string m_legacyName; // Name for reading legacy hotkey settings
wxString m_label; // Menu label

View File

@ -92,7 +92,7 @@ protected:
* Method editItem
* Prompt the user for a new hotkey given a list item.
*/
void editItem( wxTreeListItem aItem );
void editItem( wxTreeListItem aItem, int aEditId );
/**
* Method resetItem
@ -171,8 +171,9 @@ private:
*
* @param aHotkey the change-able hotkey to try to change
* @param aKey the key code to change it to
* @param alternate Change the secondary hotkey
*/
void changeHotkey( HOTKEY& aHotkey, long aKey );
void changeHotkey( HOTKEY& aHotkey, long aKey, bool alternate );
/**
* Recalculates column widths after model has changed