mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-01 18:50:16 +03:00
eterm:
- persist keybindings in keybindings.cfg with configurable primary and secondary modifiers - hot-reload bindings and merge newly introduced defaults - open keybindings.cfg in a terminal-font-sized code editor tab - indicate unsaved keybinding changes using the modified-tab marker - share tab and split commands between terminals and the keybindings editor - add bindings for terminal actions, tab navigation, numbered tabs, splits, renaming, and tab movement - expose keybindings through terminal and tab context menus - add tab split and move-left/right context menu actions
This commit is contained in:
+53
-55
@@ -5,8 +5,8 @@
|
||||
|
||||
namespace eterm {
|
||||
|
||||
void App::TerminalSplitterClient::onTabCreated( UITab* tab, UIWidget* ) {
|
||||
if ( mApp.terminalIcon )
|
||||
void App::TerminalSplitterClient::onTabCreated( UITab* tab, UIWidget* widget ) {
|
||||
if ( mApp.terminalIcon && widget && widget->isType( UI_TYPE_TERMINAL ) )
|
||||
tab->setIcon( mApp.terminalIcon->createDrawable( PixelDensity::dpToPxI( 12 ) ) );
|
||||
mApp.configureTab( tab );
|
||||
}
|
||||
@@ -393,6 +393,12 @@ void App::configureTab( UITab* tab ) {
|
||||
addItem( i18n( "close_tabs_to_the_right", "Close Tabs To The Right" ), "",
|
||||
"close-tabs-to-the-right" );
|
||||
|
||||
menu->addSeparator();
|
||||
addItem( i18n( "split_left", "Split Left" ), "split-horizontal", "split-left" );
|
||||
addItem( i18n( "split_right", "Split Right" ), "split-horizontal", "split-right" );
|
||||
addItem( i18n( "split_top", "Split Top" ), "split-vertical", "split-top" );
|
||||
addItem( i18n( "split_bottom", "Split Bottom" ), "split-vertical", "split-bottom" );
|
||||
|
||||
menu->addSeparator();
|
||||
auto* exclusiveMode = menu->addCheckBox(
|
||||
i18n( "enable_exclusive_mode", "Enable Exclusive Mode" ), terminal->getExclusiveMode(),
|
||||
@@ -404,6 +410,11 @@ void App::configureTab( UITab* tab ) {
|
||||
|
||||
menu->addSeparator();
|
||||
const bool canMove = clickedTab->getTabWidget()->getTabCount() > 1;
|
||||
const Uint32 tabIndex = clickedTab->getTabWidget()->getTabIndex( clickedTab );
|
||||
addItem( i18n( "move_tab_left", "Move Tab Left" ), "", "move-tab-left" )
|
||||
->setEnabled( canMove && tabIndex > 0 );
|
||||
addItem( i18n( "move_tab_right", "Move Tab Right" ), "", "move-tab-right" )
|
||||
->setEnabled( canMove && tabIndex + 1 < clickedTab->getTabWidget()->getTabCount() );
|
||||
addItem( i18n( "move_tab_to_start", "Move Tab To Start" ), "window", "move-tab-to-start" )
|
||||
->setEnabled( canMove );
|
||||
addItem( i18n( "move_tab_to_end", "Move Tab To End" ), "window", "move-tab-to-end" )
|
||||
@@ -511,6 +522,7 @@ UITerminal* App::createTerminal( UITabWidget* target ) {
|
||||
addItem( i18n( "new_terminal", "New Terminal" ), "terminal", "create-new-terminal" );
|
||||
menu->addSeparator();
|
||||
addItem( i18n( "settings", "Settings" ), "settings", "open-settings" );
|
||||
addItem( i18n( "key_bindings", "Keybindings" ), "keybindings", "open-keybindings" );
|
||||
menu->on( Event::OnItemClicked, [this, terminal]( const Event* itemEvent ) {
|
||||
if ( !itemEvent->getNode()->isType( UI_TYPE_MENUITEM ) )
|
||||
return;
|
||||
@@ -530,68 +542,33 @@ UITerminal* App::createTerminal( UITabWidget* target ) {
|
||||
return terminal;
|
||||
}
|
||||
|
||||
UITerminal* App::createTerminalSplit( SplitDirection direction, UITerminal* terminal ) {
|
||||
auto* source = terminal ? tabSplitter->tabWidgetFromWidget( terminal ) : nullptr;
|
||||
UITerminal* App::createTerminalSplit( SplitDirection direction, UIWidget* widget ) {
|
||||
auto* source = widget ? tabSplitter->tabWidgetFromWidget( widget ) : nullptr;
|
||||
auto* target = source ? tabSplitter->splitTabWidget( direction, source ) : nullptr;
|
||||
return target ? createTerminal( target ) : nullptr;
|
||||
}
|
||||
|
||||
void App::moveTab( UIWidget* widget, int offset ) {
|
||||
if ( !widget || offset == 0 )
|
||||
return;
|
||||
auto* tabs = tabSplitter->tabWidgetFromWidget( widget );
|
||||
auto* tab = tabs ? tabs->getTabFromOwnedWidget( widget ) : nullptr;
|
||||
if ( !tab )
|
||||
return;
|
||||
const Uint32 index = tabs->getTabIndex( tab );
|
||||
if ( offset < 0 && index > 0 )
|
||||
tabs->moveTab( tab, index - 1 );
|
||||
else if ( offset > 0 && index + 1 < tabs->getTabCount() )
|
||||
tabs->moveTab( tab, index + 1 );
|
||||
}
|
||||
|
||||
void App::addTabKeyBindings( UITerminal* terminal ) {
|
||||
tabSplitter->registerSplitterCommands( *terminal );
|
||||
terminal->setCommand( "create-new-terminal", [this] { createNewTerminal(); } );
|
||||
terminal->setCommand( "open-settings", [this] { showSettings(); } );
|
||||
terminal->setCommand( "debug-widget-tree-view",
|
||||
[this] { UIWidgetInspector::create( scene ); } );
|
||||
registerTabCommands( *terminal, terminal );
|
||||
terminal->setCommand( "terminal-rename", [this, terminal] { renameSession( terminal ); } );
|
||||
terminal->setCommand( UITerminal::getExclusiveModeToggleCommandName(), [terminal] {
|
||||
terminal->setExclusiveMode( !terminal->getExclusiveMode() );
|
||||
} );
|
||||
terminal->setCommand( "next-tab", [this, terminal] {
|
||||
if ( auto* tabs = tabSplitter->tabWidgetFromWidget( terminal ) )
|
||||
tabs->focusNextTab();
|
||||
} );
|
||||
terminal->setCommand( "previous-tab", [this, terminal] {
|
||||
if ( auto* tabs = tabSplitter->tabWidgetFromWidget( terminal ) )
|
||||
tabs->focusPreviousTab();
|
||||
} );
|
||||
terminal->setCommand( "split-right", [this, terminal] {
|
||||
createTerminalSplit( SplitDirection::Right, terminal );
|
||||
} );
|
||||
terminal->setCommand( "split-bottom", [this, terminal] {
|
||||
createTerminalSplit( SplitDirection::Bottom, terminal );
|
||||
} );
|
||||
terminal->setCommand(
|
||||
"split-left", [this, terminal] { createTerminalSplit( SplitDirection::Left, terminal ); } );
|
||||
terminal->setCommand(
|
||||
"split-top", [this, terminal] { createTerminalSplit( SplitDirection::Top, terminal ); } );
|
||||
terminal->setCommand( "switch-to-previous-split",
|
||||
[this, terminal] { tabSplitter->switchPreviousSplit( terminal ); } );
|
||||
terminal->setCommand( "switch-to-next-split",
|
||||
[this, terminal] { tabSplitter->switchNextSplit( terminal ); } );
|
||||
terminal->addKeyBinding( { KEY_T, KeyMod::getDefaultModifier() | KEYMOD_SHIFT },
|
||||
"create-new-terminal" );
|
||||
terminal->addKeyBinding( { KEY_COMMA, KeyMod::getDefaultModifier() }, "open-settings" );
|
||||
terminal->addKeyBinding( { KEY_W, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "close-tab" );
|
||||
terminal->addKeyBinding( { KEY_F11, KeyMod::getDefaultModifier() | KEYMOD_SHIFT },
|
||||
"debug-widget-tree-view" );
|
||||
terminal->addKeyBinding( { KEY_PAGEDOWN, KEYMOD_CTRL }, "next-tab" );
|
||||
terminal->addKeyBinding( { KEY_PAGEUP, KEYMOD_CTRL }, "previous-tab" );
|
||||
terminal->addKeyBinding( { KEY_TAB, KEYMOD_CTRL }, "next-tab" );
|
||||
terminal->addKeyBinding( { KEY_TAB, KEYMOD_CTRL | KEYMOD_SHIFT }, "previous-tab" );
|
||||
terminal->addKeyBinding( { KEY_L, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT },
|
||||
"split-right" );
|
||||
terminal->addKeyBinding( { KEY_K, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT },
|
||||
"split-bottom" );
|
||||
terminal->addKeyBinding( { KEY_J, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT },
|
||||
"split-left" );
|
||||
terminal->addKeyBinding( { KEY_I, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT },
|
||||
"split-top" );
|
||||
terminal->addKeyBinding(
|
||||
{ KEY_J, KeyMod::getDefaultModifier() | KeyMod::getDefaultSecondaryModifier() },
|
||||
"switch-to-previous-split" );
|
||||
terminal->addKeyBinding(
|
||||
{ KEY_L, KeyMod::getDefaultModifier() | KeyMod::getDefaultSecondaryModifier() },
|
||||
"switch-to-next-split" );
|
||||
applyKeybindings( terminal );
|
||||
}
|
||||
|
||||
bool App::closeWindow( EE::Window::Window* ) {
|
||||
@@ -809,6 +786,11 @@ int App::run( int argc, char* argv[] ) {
|
||||
scene = app.getUI();
|
||||
if ( !appWindow || !appWindow->isOpen() || !scene )
|
||||
return EXIT_FAILURE;
|
||||
keybindingsPath = config->getConfigPath() + "keybindings.cfg";
|
||||
loadKeybindings();
|
||||
fileWatcher = std::make_unique<efsw::FileWatcher>();
|
||||
fileWatcher->addWatch( config->getConfigPath(), this );
|
||||
fileWatcher->watch();
|
||||
scene->setColorSchemePreference( config->theme.uiColorScheme );
|
||||
scene->getUIThemeManager()->setDefaultFontSize( config->font.uiSize );
|
||||
FileSystem::changeWorkingDirectory( initialWorkingDirectory );
|
||||
@@ -829,6 +811,19 @@ int App::run( int argc, char* argv[] ) {
|
||||
tint: var(--primary);
|
||||
cursor: hand;
|
||||
}
|
||||
Tab.tab_modified > Tab::close {
|
||||
foreground-image: url("data:image/svg,<svg viewBox='0 0 24 24' width='12' height='12' fill='#ffffff'><path d='M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z'></path></svg>");
|
||||
foreground-tint: var(--primary);
|
||||
foreground-size: 6dp 6dp;
|
||||
foreground-position: center;
|
||||
opacity: 1;
|
||||
}
|
||||
Tab.tab_modified > Tab::close:hover {
|
||||
foreground-image: url("data:image/svg,<svg width='16' height='16' viewBox='0 0 16 16'><path fill='#ffffff' fill-rule='evenodd' d='M 2.3432061,13.657206 A 8.0002061,8.0002061 0 1 1 13.657206,2.3432061 8.0002061,8.0002061 0 0 1 2.3432061,13.657206 Z m 3.687,-8.6869999 a 0.75,0.75 0 0 0 -1.06,1.06 l 1.97,1.97 -1.97,1.97 a 0.75,0.75 0 1 0 1.06,1.0599999 l 1.97,-1.9699999 1.97,1.9699999 A 0.75,0.75 0 1 0 11.030206,9.9702061 l -1.9699999,-1.97 1.9699999,-1.97 a 0.75,0.75 0 1 0 -1.0599999,-1.06 l -1.97,1.97 z' /></svg>");
|
||||
foreground-tint: var(--tab-close-hover);
|
||||
foreground-size: 10dp 10dp;
|
||||
foreground-position: center;
|
||||
}
|
||||
)css" );
|
||||
|
||||
auto& resourceScope = *scene->getResourceScope();
|
||||
@@ -976,6 +971,8 @@ int App::run( int argc, char* argv[] ) {
|
||||
appWindow->runMainLoop( [this] {
|
||||
appWindow->getInput()->update();
|
||||
SceneManager::instance()->update();
|
||||
if ( keybindingsChanged.exchange( false, std::memory_order_acq_rel ) )
|
||||
reloadKeybindings();
|
||||
queueExitedTabs();
|
||||
// Process-exit events are drained from UITerminal scheduled updates. Removing a tab from
|
||||
// that callback would mutate the scheduled-widget set while it is being traversed.
|
||||
@@ -998,6 +995,7 @@ int App::run( int argc, char* argv[] ) {
|
||||
secondsCounter.restart();
|
||||
}
|
||||
} );
|
||||
fileWatcher.reset();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "appconfig.hpp"
|
||||
#include <args/args.hxx>
|
||||
#include <atomic>
|
||||
#include <eepp/core/small_vector.hpp>
|
||||
#include <eepp/ee.hpp>
|
||||
#include <eepp/ui/iconmanager.hpp>
|
||||
@@ -12,6 +13,7 @@
|
||||
#include <eepp/ui/uiapplication.hpp>
|
||||
#include <eepp/ui/uilinearlayout.hpp>
|
||||
#include <eepp/ui/uimessagebox.hpp>
|
||||
#include <efsw/efsw.hpp>
|
||||
#include <eterm/ui/uiterminal.hpp>
|
||||
|
||||
using namespace EE;
|
||||
@@ -41,7 +43,7 @@ struct TerminalLaunchConfig {
|
||||
bool closeOnExit{ false };
|
||||
};
|
||||
|
||||
class App {
|
||||
class App : private efsw::FileWatchListener {
|
||||
public:
|
||||
int run( int argc, char* argv[] );
|
||||
|
||||
@@ -91,10 +93,48 @@ class App {
|
||||
|
||||
UITerminal* createTerminal( UITabWidget* target = nullptr );
|
||||
|
||||
UITerminal* createTerminalSplit( SplitDirection direction, UITerminal* terminal );
|
||||
UITerminal* createTerminalSplit( SplitDirection direction, UIWidget* widget );
|
||||
|
||||
void moveTab( UIWidget* widget, int offset );
|
||||
|
||||
template <typename T> void registerTabCommands( T& commandTarget, UIWidget* widget ) {
|
||||
tabSplitter->registerSplitterCommands( commandTarget );
|
||||
commandTarget.setCommand( "create-new-terminal", [this] { createNewTerminal(); } );
|
||||
commandTarget.setCommand( "open-settings", [this] { showSettings(); } );
|
||||
commandTarget.setCommand( "open-keybindings", [this] { openKeybindings(); } );
|
||||
commandTarget.setCommand( "debug-widget-tree-view",
|
||||
[this] { UIWidgetInspector::create( scene ); } );
|
||||
commandTarget.setCommand( "move-tab-left", [this, widget] { moveTab( widget, -1 ); } );
|
||||
commandTarget.setCommand( "move-tab-right", [this, widget] { moveTab( widget, 1 ); } );
|
||||
commandTarget.setCommand( "split-right", [this, widget] {
|
||||
createTerminalSplit( SplitDirection::Right, widget );
|
||||
} );
|
||||
commandTarget.setCommand( "split-bottom", [this, widget] {
|
||||
createTerminalSplit( SplitDirection::Bottom, widget );
|
||||
} );
|
||||
commandTarget.setCommand(
|
||||
"split-left", [this, widget] { createTerminalSplit( SplitDirection::Left, widget ); } );
|
||||
commandTarget.setCommand(
|
||||
"split-top", [this, widget] { createTerminalSplit( SplitDirection::Top, widget ); } );
|
||||
}
|
||||
|
||||
void addTabKeyBindings( UITerminal* terminal );
|
||||
|
||||
KeyBindings::ShortcutMap getDefaultKeybindings() const;
|
||||
|
||||
void loadKeybindings();
|
||||
|
||||
void reloadKeybindings();
|
||||
|
||||
void applyKeybindings( UITerminal* terminal );
|
||||
|
||||
void applyKeybindings( UICodeEditor* editor );
|
||||
|
||||
void openKeybindings();
|
||||
|
||||
void handleFileAction( efsw::WatchID, const std::string& dir, const std::string& filename,
|
||||
efsw::Action action, const std::string& oldFilename ) override;
|
||||
|
||||
void showSettings();
|
||||
|
||||
void openFontPicker( bool uiFont, bool fallbackFont = false );
|
||||
@@ -117,6 +157,7 @@ class App {
|
||||
UIIcon* terminalIcon{ nullptr };
|
||||
UIMessageBox* closeDialog{ nullptr };
|
||||
UIWindow* settingsWindow{ nullptr };
|
||||
UICodeEditor* keybindingsEditor{ nullptr };
|
||||
UIWidget* closeDialogWidget{ nullptr };
|
||||
UIWindow* maximizedTabWidgetWindow{ nullptr };
|
||||
UITabWidget* maximizedTabWidget{ nullptr };
|
||||
@@ -124,6 +165,10 @@ class App {
|
||||
TerminalLaunchConfig terminalConfig;
|
||||
std::unique_ptr<eterm::AppConfig> config;
|
||||
std::map<std::string, TerminalColorScheme> terminalColorSchemes;
|
||||
std::unordered_map<std::string, std::string> keybindings;
|
||||
std::string keybindingsPath;
|
||||
std::unique_ptr<efsw::FileWatcher> fileWatcher;
|
||||
std::atomic<bool> keybindingsChanged{ false };
|
||||
const TerminalColorScheme* selectedColorScheme{ nullptr };
|
||||
Float terminalFontSize{ 12 };
|
||||
bool warnBeforeClose{ false };
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
#include "eterm.hpp"
|
||||
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/inifile.hpp>
|
||||
#include <eepp/ui/uicodeeditor.hpp>
|
||||
|
||||
namespace eterm {
|
||||
|
||||
KeyBindings::ShortcutMap App::getDefaultKeybindings() const {
|
||||
return {
|
||||
{ { KEY_C, KEYMOD_CTRL | KEYMOD_SHIFT }, "terminal-copy" },
|
||||
{ { KEY_V, KEYMOD_CTRL | KEYMOD_SHIFT }, "terminal-paste" },
|
||||
{ { KEY_T, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "create-new-terminal" },
|
||||
{ { KEY_E,
|
||||
KeyMod::getDefaultModifier() | KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT },
|
||||
UITerminal::getExclusiveModeToggleCommandName() },
|
||||
{ { KEY_S, KeyMod::getDefaultModifier() | KeyMod::getDefaultSecondaryModifier() },
|
||||
"terminal-rename" },
|
||||
{ { KEY_COMMA, KeyMod::getDefaultModifier() }, "open-settings" },
|
||||
{ { KEY_W, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "close-tab" },
|
||||
{ { KEY_F11, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "debug-widget-tree-view" },
|
||||
{ { KEY_PAGEDOWN, KEYMOD_CTRL }, "next-tab" },
|
||||
{ { KEY_PAGEUP, KEYMOD_CTRL }, "previous-tab" },
|
||||
{ { KEY_TAB, KEYMOD_CTRL }, "next-tab" },
|
||||
{ { KEY_TAB, KEYMOD_CTRL | KEYMOD_SHIFT }, "previous-tab" },
|
||||
{ { KEY_1, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "switch-to-tab-1" },
|
||||
{ { KEY_2, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "switch-to-tab-2" },
|
||||
{ { KEY_3, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "switch-to-tab-3" },
|
||||
{ { KEY_4, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "switch-to-tab-4" },
|
||||
{ { KEY_5, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "switch-to-tab-5" },
|
||||
{ { KEY_6, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "switch-to-tab-6" },
|
||||
{ { KEY_7, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "switch-to-tab-7" },
|
||||
{ { KEY_8, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "switch-to-tab-8" },
|
||||
{ { KEY_9, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "switch-to-tab-9" },
|
||||
{ { KEY_0, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "switch-to-last-tab" },
|
||||
{ { KEY_LEFT, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "move-tab-left" },
|
||||
{ { KEY_RIGHT, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "move-tab-right" },
|
||||
{ { KEY_L, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "split-right" },
|
||||
{ { KEY_K, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "split-bottom" },
|
||||
{ { KEY_J, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "split-left" },
|
||||
{ { KEY_I, KeyMod::getDefaultSecondaryModifier() | KEYMOD_SHIFT }, "split-top" },
|
||||
{ { KEY_J, KeyMod::getDefaultModifier() | KeyMod::getDefaultSecondaryModifier() },
|
||||
"switch-to-previous-split" },
|
||||
{ { KEY_L, KeyMod::getDefaultModifier() | KeyMod::getDefaultSecondaryModifier() },
|
||||
"switch-to-next-split" },
|
||||
};
|
||||
}
|
||||
|
||||
void App::loadKeybindings() {
|
||||
IniFile ini( keybindingsPath );
|
||||
bool changed = false;
|
||||
std::string defaultModifier = ini.getValue( "modifier", "mod" );
|
||||
if ( defaultModifier.empty() ) {
|
||||
defaultModifier = KeyMod::getDefaultModifierString();
|
||||
ini.setValue( "modifier", "mod", defaultModifier );
|
||||
changed = true;
|
||||
}
|
||||
if ( const Uint32 modifier = KeyMod::getKeyMod( defaultModifier ); modifier != KEYMOD_NONE )
|
||||
KeyMod::setDefaultModifier( modifier );
|
||||
|
||||
std::string defaultSecondaryModifier = ini.getValue( "modifier", "mod2" );
|
||||
if ( defaultSecondaryModifier.empty() ) {
|
||||
defaultSecondaryModifier = KeyMod::getDefaultSecondaryModifierString();
|
||||
ini.setValue( "modifier", "mod2", defaultSecondaryModifier );
|
||||
changed = true;
|
||||
}
|
||||
if ( const Uint32 modifier = KeyMod::getKeyMod( defaultSecondaryModifier );
|
||||
modifier != KEYMOD_NONE && !( modifier & KeyMod::getDefaultModifier() ) ) {
|
||||
KeyMod::setDefaultSecondaryModifier( modifier );
|
||||
} else {
|
||||
defaultSecondaryModifier = KeyMod::getDefaultSecondaryModifierString();
|
||||
ini.setValue( "modifier", "mod2", defaultSecondaryModifier );
|
||||
changed = true;
|
||||
}
|
||||
|
||||
KeyBindings formatter( appWindow->getInput() );
|
||||
auto defaults = getDefaultKeybindings();
|
||||
const bool hasKeybindings = ini.findKey( "eterm" ) != IniFile::noID;
|
||||
if ( hasKeybindings ) {
|
||||
keybindings = ini.getKeyUnorderedMap( "eterm" );
|
||||
} else {
|
||||
keybindings.clear();
|
||||
for ( const auto& shortcut : KeyBindings::getOrderedShortcuts( defaults ) ) {
|
||||
const auto& command = defaults.find( shortcut )->second;
|
||||
const std::string shortcutString = formatter.getShortcutString( shortcut );
|
||||
keybindings[shortcutString] = command;
|
||||
ini.setValue( "eterm", shortcutString, command );
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if ( hasKeybindings ) {
|
||||
std::unordered_map<std::string, std::string> commands;
|
||||
for ( const auto& [shortcut, command] : keybindings )
|
||||
commands[command] = shortcut;
|
||||
for ( const auto& shortcut : KeyBindings::getOrderedShortcuts( defaults ) ) {
|
||||
const auto& command = defaults.find( shortcut )->second;
|
||||
const std::string shortcutString = formatter.getShortcutString( shortcut );
|
||||
if ( commands.find( command ) == commands.end() &&
|
||||
keybindings.find( shortcutString ) == keybindings.end() ) {
|
||||
keybindings[shortcutString] = command;
|
||||
commands[command] = shortcutString;
|
||||
ini.setValue( "eterm", shortcutString, command );
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( changed )
|
||||
ini.writeFile();
|
||||
}
|
||||
|
||||
void App::applyKeybindings( UITerminal* terminal ) {
|
||||
terminal->getKeyBindings().reset();
|
||||
terminal->getKeyBindings().addKeybindsStringUnordered( keybindings );
|
||||
}
|
||||
|
||||
void App::applyKeybindings( UICodeEditor* editor ) {
|
||||
editor->getKeyBindings().setKeybinds( *UICodeEditor::getDefaultKeybindings() );
|
||||
editor->addKeyBinding( { KEY_S, KeyMod::getDefaultModifier() }, "save-doc", true );
|
||||
editor->getKeyBindings().addKeybindsStringUnordered( keybindings );
|
||||
}
|
||||
|
||||
void App::reloadKeybindings() {
|
||||
if ( !FileSystem::fileExists( keybindingsPath ) )
|
||||
return;
|
||||
keybindings.clear();
|
||||
loadKeybindings();
|
||||
forEachTerminal( [this]( UITerminal* terminal ) { applyKeybindings( terminal ); } );
|
||||
if ( keybindingsEditor && tabSplitter->ownedWidgetExists( keybindingsEditor ) )
|
||||
applyKeybindings( keybindingsEditor );
|
||||
}
|
||||
|
||||
void App::openKeybindings() {
|
||||
if ( keybindingsEditor && tabSplitter->ownedWidgetExists( keybindingsEditor ) ) {
|
||||
if ( auto* tabs = tabSplitter->tabWidgetFromWidget( keybindingsEditor ) )
|
||||
tabs->setTabSelected( tabs->getTabFromOwnedWidget( keybindingsEditor ) );
|
||||
keybindingsEditor->setFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
auto* editor = UICodeEditor::New();
|
||||
if ( editor->loadFromFile( keybindingsPath ) == TextDocument::LoadStatus::Failed ) {
|
||||
editor->close();
|
||||
return;
|
||||
}
|
||||
editor->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent );
|
||||
editor->setFontSize( terminalFontSize );
|
||||
editor->setSyntaxDefinition( editor->getDocument().guessSyntax() );
|
||||
editor->getDocument().setCommand( "save-doc", [editor] { editor->getDocument().save(); } );
|
||||
registerTabCommands( editor->getDocument(), editor );
|
||||
applyKeybindings( editor );
|
||||
editor->on( Event::OnClose, [this, editor]( const Event* ) {
|
||||
if ( keybindingsEditor == editor )
|
||||
keybindingsEditor = nullptr;
|
||||
} );
|
||||
|
||||
auto* current = tabSplitter->getCurWidget();
|
||||
auto* target =
|
||||
current ? tabSplitter->tabWidgetFromWidget( current ) : tabSplitter->getFirstTabWidget();
|
||||
keybindingsEditor = editor;
|
||||
auto* tab = tabSplitter->createWidgetInTabWidget( target, editor, "keybindings.cfg" ).first;
|
||||
const auto updateTabState = [editor, tab]( const Event* ) {
|
||||
const bool dirty = editor->getDocument().isDirty();
|
||||
tab->removeClass( dirty ? "tab_clear" : "tab_modified" );
|
||||
tab->addClass( dirty ? "tab_modified" : "tab_clear" );
|
||||
};
|
||||
editor->on( Event::OnTextChanged, updateTabState );
|
||||
editor->on( Event::OnDocumentUndoRedo, updateTabState );
|
||||
editor->on( Event::OnDocumentSave, updateTabState );
|
||||
updateTabState( nullptr );
|
||||
editor->setFocus();
|
||||
}
|
||||
|
||||
void App::handleFileAction( efsw::WatchID, const std::string&, const std::string& filename,
|
||||
efsw::Action action, const std::string& oldFilename ) {
|
||||
if ( ( filename == "keybindings.cfg" || oldFilename == "keybindings.cfg" ) &&
|
||||
( action == efsw::Actions::Add || action == efsw::Actions::Modified ||
|
||||
action == efsw::Actions::Moved ) ) {
|
||||
keybindingsChanged.store( true, std::memory_order_release );
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace eterm
|
||||
Reference in New Issue
Block a user