diff --git a/include/eepp/ui/uitabwidget.hpp b/include/eepp/ui/uitabwidget.hpp index 95069fff0..4d671c926 100644 --- a/include/eepp/ui/uitabwidget.hpp +++ b/include/eepp/ui/uitabwidget.hpp @@ -98,6 +98,8 @@ class EE_API UITabWidget : public UIWidget { void insertTab( UITab* tab, const Uint32& index ); + bool moveTab( UITab* tab, Uint32 index ); + virtual void setTheme( UITheme* theme ); UITab* getTabSelected() const; @@ -207,7 +209,7 @@ class EE_API UITabWidget : public UIWidget { UIScrollBar* getTabScroll() const; - void swapTabs( UITab* left, UITab* right ); + bool swapTabs( UITab* left, UITab* right ); void setSplitFunction( SplitFunctionCb cb, Float splitEdgePercent = 0.1 ); diff --git a/projects/linux/ee.creator.user b/projects/linux/ee.creator.user index 201e2b95e..245217c8b 100644 --- a/projects/linux/ee.creator.user +++ b/projects/linux/ee.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 7770cbb16..cc53b4b8a 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -647,7 +647,9 @@ void UICodeEditor::onFontStyleChanged() { void UICodeEditor::onDocumentLoaded( TextDocument* ) { if ( mInvalidateOnLoaded ) { - onDocumentLoaded(); + ensureMainThread( [this] { + onDocumentLoaded(); + } ); mInvalidateOnLoaded = false; } } diff --git a/src/eepp/ui/uitabwidget.cpp b/src/eepp/ui/uitabwidget.cpp index cf0c63c2f..e76a4316f 100644 --- a/src/eepp/ui/uitabwidget.cpp +++ b/src/eepp/ui/uitabwidget.cpp @@ -981,9 +981,9 @@ void UITabWidget::tryCloseTab( UITab* tab, FocusTabBehavior focusTabBehavior ) { removeTab( tab, true, false, focusTabBehavior ); } -void UITabWidget::swapTabs( UITab* left, UITab* right ) { +bool UITabWidget::swapTabs( UITab* left, UITab* right ) { if ( !left || !right || left->getTabWidget() != this || right->getTabWidget() != this ) - return; + return false; Uint32 leftIndex = getTabIndex( left ); Uint32 rightIndex = getTabIndex( right ); if ( leftIndex != eeINDEX_NOT_FOUND && rightIndex != eeINDEX_NOT_FOUND ) { @@ -995,7 +995,36 @@ void UITabWidget::swapTabs( UITab* left, UITab* right ) { mTabs[rightIndex] = left; posTabs(); zorderTabs(); + return true; } + return false; +} + +bool UITabWidget::moveTab( UITab* tab, Uint32 index ) { + auto tabIndex = getTabIndex( tab ); + + if ( tabIndex == eeINDEX_NOT_FOUND || mTabs.empty() ) + return false; + + if ( index >= mTabs.size() ) + index = mTabs.size() - 1; + + if ( tabIndex == index ) + return false; + + if ( tabIndex > index ) { + while ( tabIndex > index ) { + swapTabs( mTabs[tabIndex], mTabs[tabIndex - 1] ); + tabIndex--; + } + } else { + while ( tabIndex < index ) { + swapTabs( mTabs[tabIndex], mTabs[tabIndex + 1] ); + tabIndex++; + } + } + + return true; } void UITabWidget::setSplitFunction( SplitFunctionCb cb, Float splitEdgePercent ) { diff --git a/src/tools/ecode/appconfig.cpp b/src/tools/ecode/appconfig.cpp index a64ac0428..769f7847c 100644 --- a/src/tools/ecode/appconfig.cpp +++ b/src/tools/ecode/appconfig.cpp @@ -221,6 +221,7 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath, term.scrollback = ini.getValueI( "terminal", "scrollback", 10000 ); term.unsupportedOSWarnDisabled = ini.getValueB( "terminal", "unsupported_os_warn_disabled", false ); + term.closeTerminalTabOnExit = ini.getValueB( "terminal", "close_terminal_tab_on_exit", false ); workspace.restoreLastSession = ini.getValueB( "workspace", "restore_last_session", false ); workspace.checkForUpdatesAtStartup = @@ -242,7 +243,8 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath, iniInfo = FileInfo( ini.path() ); - pluginManager->setPluginsEnabled( pluginsEnabled, sync ); + if ( !pluginManager->pluginsDisabled() ) + pluginManager->setPluginsEnabled( pluginsEnabled, sync ); } void AppConfig::save( const std::vector& recentFiles, @@ -377,6 +379,7 @@ void AppConfig::save( const std::vector& recentFiles, NewTerminalOrientation::toString( term.newTerminalOrientation ) ); ini.setValue( "terminal", "scrollback", String::toString( term.scrollback ) ); ini.setValueB( "terminal", "unsupported_os_warn_disabled", term.unsupportedOSWarnDisabled ); + ini.setValueB( "terminal", "close_terminal_tab_on_exit", term.closeTerminalTabOnExit ); ini.setValueB( "window", "vsync", context.VSync ); ini.setValue( "window", "glversion", @@ -389,9 +392,11 @@ void AppConfig::save( const std::vector& recentFiles, workspace.checkForUpdatesAtStartup ); ini.setValueB( "workspace", "session_snapshot", workspace.sessionSnapshot ); - const auto& pluginsEnabled = pluginManager->getPluginsEnabled(); - for ( const auto& plugin : pluginsEnabled ) - ini.setValueB( "plugins", plugin.first, plugin.second ); + if ( !pluginManager->pluginsDisabled() ) { + const auto& pluginsEnabled = pluginManager->getPluginsEnabled(); + for ( const auto& plugin : pluginsEnabled ) + ini.setValueB( "plugins", plugin.first, plugin.second ); + } for ( const auto& langExt : languagesExtensions.priorities ) ini.setValue( "languages_extensions", langExt.first, langExt.second ); diff --git a/src/tools/ecode/appconfig.hpp b/src/tools/ecode/appconfig.hpp index 0307befa1..269829187 100644 --- a/src/tools/ecode/appconfig.hpp +++ b/src/tools/ecode/appconfig.hpp @@ -175,6 +175,7 @@ struct TerminalConfig { NewTerminalOrientation::Horizontal }; Uint64 scrollback{ 10000 }; bool unsupportedOSWarnDisabled{ false }; + bool closeTerminalTabOnExit{ false }; }; struct WorkspaceConfig { diff --git a/src/tools/ecode/backward.cpp b/src/tools/ecode/backward.cpp index 18f7499c1..3a7bb5ace 100644 --- a/src/tools/ecode/backward.cpp +++ b/src/tools/ecode/backward.cpp @@ -176,7 +176,7 @@ class WindowsSignalHandling { } static void handle_stacktrace( int skip_frames = 0 ) { - std::string crashesPath( Sys::getProcessPath() ); + std::string crashesPath( Sys::getConfigPath( "ecode" ) ); FileSystem::dirAddSlashAtEnd( crashesPath ); crashesPath += "crashes"; FileSystem::dirAddSlashAtEnd( crashesPath ); diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 4f7cd7b16..fbc527195 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -614,11 +614,13 @@ void App::initPluginManager() { mPluginManager->registerPlugin( AIAssistantPlugin::Definition() ); mPluginManager->registerPlugin( SpellCheckerPlugin::Definition() ); mPluginManager->registerPlugin( DiscordRPCplugin::Definition() ); + mPluginManager->setPluginsDisabled( mDisablePlugins ); } std::pair App::generateConfigPath() { if ( mPortableMode ) { - std::string path( Sys::getProcessPath() + "config" ); + std::string path( mProfilePath.empty() ? ( Sys::getProcessPath() + "config" ) + : mProfilePath ); FileSystem::dirAddSlashAtEnd( path ); bool fileExists = FileSystem::fileExists( path ); @@ -1482,14 +1484,9 @@ void App::onTabCreated( UITab* tab, UIWidget* ) { menuAdd( "split_top", "Split Top", "split-vertical", "split-top" ); menuAdd( "split_bottom", "Split Bottom", "split-vertical", "split-bottom" ); - menu->addSeparator(); - menuAdd( "open_containing_folder_ellipsis", "Open Containing Folder...", "folder-open", "open-containing-folder" ); - menuAdd( "open_in_new_window_ellipsis", "Open in New Window...", "window", - "open-in-new-window" ); - menuAdd( "copy_containing_folder_path_ellipsis", "Copy Containing Folder Path...", "copy", "copy-containing-folder-path" ); @@ -1497,6 +1494,21 @@ void App::onTabCreated( UITab* tab, UIWidget* ) { menuAdd( "copy_file_path_and_position", "Copy File Path and Position", "copy", "copy-file-path-and-position" ); + + menu->addSeparator(); + + menuAdd( "open_in_new_window", "Open in New Window", "window", "open-in-new-window" ); + + menuAdd( "move_to_new_window", "Move to New Window", "window", "move-to-new-window" ); + } + + if ( tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) || + tab->getOwnedWidget()->isType( UI_TYPE_TERMINAL ) ) { + menu->addSeparator(); + + menuAdd( "move_tab_to_start", "Move Tab To Start", "window", "move-tab-to-start" ); + + menuAdd( "move_tab_to_end", "Move Tab To End", "window", "move-tab-to-end" ); } menu->addEventListener( Event::OnItemClicked, [tab, this]( const Event* event ) { @@ -2496,6 +2508,40 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { Sys::execute( cmd ); } } ); + doc.setCommand( "move-to-new-window", [this] { + auto editor = mSplitter->getCurEditor(); + if ( editor == nullptr || editor->getDocumentRef() == nullptr || + editor->getDocumentRef()->getFilePath().empty() ) + return; + UITabWidget* tabWidget = mSplitter->tabWidgetFromEditor( editor ); + if ( tabWidget ) + tabWidget->removeTab( (UITab*)editor->getData() ); + std::string processPath = Sys::getProcessFilePath(); + if ( !processPath.empty() ) { + auto cmd( processPath + " -x \"" + editor->getDocumentRef()->getFilePath() + "\"" ); + Sys::execute( cmd ); + } + } ); + doc.setCommand( "move-tab-to-start", [this] { + auto widget = mSplitter->getCurWidget(); + if ( widget == nullptr || widget->getData() == 0 ) + return; + UITabWidget* tabWidget = mSplitter->tabWidgetFromWidget( widget ); + if ( tabWidget ) { + UITab* tab = (UITab*)widget->getData(); + tabWidget->moveTab( tab, 0 ); + } + } ); + doc.setCommand( "move-tab-to-end", [this] { + auto widget = mSplitter->getCurWidget(); + if ( widget == nullptr || widget->getData() == 0 ) + return; + UITabWidget* tabWidget = mSplitter->tabWidgetFromWidget( widget ); + if ( tabWidget ) { + UITab* tab = (UITab*)widget->getData(); + tabWidget->moveTab( tab, tabWidget->getTabCount() ); + } + } ); registerUnlockedCommands( doc ); editor->on( Event::OnDocumentSave, [this]( const Event* event ) { @@ -3551,11 +3597,7 @@ bool App::needsRedirectToRunningProcess( std::string file ) { return true; } -void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDensity, - const std::string& colorScheme, bool terminal, bool frameBuffer, bool benchmarkMode, - std::string css, const std::string& fileToOpen, bool stdOutLogs, - bool disableFileLogs, bool openClean, bool portable, std::string language, - bool incognito, bool prematureExit ) { +void App::init( InitParameters& params ) { Http::setThreadPool( mThreadPool ); DisplayManager* displayManager = Engine::instance()->getDisplayManager(); Display* currentDisplay = displayManager->getDisplayIndex( 0 ); @@ -3565,11 +3607,13 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe return; } - mIncognito = incognito; - mPortableMode = portable; + mIncognito = params.incognito; + mPortableMode = params.portable || !params.profile.empty(); + mProfilePath = params.profile; mDisplayDPI = currentDisplay->getDPI(); - mUseFrameBuffer = frameBuffer; - mBenchmarkMode = benchmarkMode; + mUseFrameBuffer = params.frameBuffer; + mBenchmarkMode = params.benchmarkMode; + mDisablePlugins = params.disablePlugins; mResPath = Sys::getProcessPath(); #if EE_PLATFORM == EE_PLATFORM_LINUX @@ -3581,13 +3625,13 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe mResPath += "assets"; FileSystem::dirAddSlashAtEnd( mResPath ); - bool firstRun = loadConfig( logLevel, currentDisplay->getSize(), prematureExit, stdOutLogs, - disableFileLogs ); + bool firstRun = loadConfig( params.logLevel, currentDisplay->getSize(), params.prematureExit, + params.stdOutLogs, params.disableFileLogs ); - if ( prematureExit ) + if ( params.prematureExit ) return; - if ( !openClean && needsRedirectToRunningProcess( file ) ) + if ( !params.openClean && needsRedirectToRunningProcess( params.file ) ) return; currentDisplay = displayManager->getDisplayIndex( mConfig.windowState.displayIndex < @@ -3598,15 +3642,15 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe #if EE_PLATFORM == EE_PLATFORM_ANDROID mConfig.windowState.pixelDensity = - pidelDensity > 0 - ? pidelDensity + params.pidelDensity > 0 + ? params.pidelDensity : ( mConfig.windowState.pixelDensity > 0 ? mConfig.windowState.pixelDensity : currentDisplay->getPixelDensity() > 2 ? currentDisplay->getPixelDensity() / 2 : currentDisplay->getPixelDensity() ); #else mConfig.windowState.pixelDensity = - pidelDensity > 0 - ? pidelDensity + params.pidelDensity > 0 + ? params.pidelDensity : ( mConfig.windowState.pixelDensity > 0 ? mConfig.windowState.pixelDensity : currentDisplay->getPixelDensity() ); #endif @@ -3955,19 +3999,19 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe mUISceneNode->setThreadPool( mThreadPool ); mUIColorScheme = mConfig.ui.colorScheme; - if ( language.empty() ) - language = mConfig.ui.language; - if ( !language.empty() ) - mUISceneNode->getTranslator().setCurrentLanguage( language ); + if ( params.language.empty() ) + params.language = mConfig.ui.language; + if ( !params.language.empty() ) + mUISceneNode->getTranslator().setCurrentLanguage( params.language ); std::string currentLanguage( mUISceneNode->getTranslator().getCurrentLanguage() ); mi18nPath = mResPath + "i18n" + FileSystem::getOSSlash(); std::string langPath( mi18nPath + currentLanguage + ".xml" ); if ( currentLanguage != "en" && FileSystem::fileExists( langPath ) ) mUISceneNode->getTranslator().loadFromFile( langPath ); - if ( !colorScheme.empty() ) { - mUIColorScheme = - colorScheme == "light" ? ColorSchemePreference::Light : ColorSchemePreference::Dark; + if ( !params.colorScheme.empty() ) { + mUIColorScheme = params.colorScheme == "light" ? ColorSchemePreference::Light + : ColorSchemePreference::Dark; } mUISceneNode->setColorSchemePreference( mUIColorScheme ); @@ -3987,12 +4031,12 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe setTheme( getThemePath() ); - if ( css.empty() ) - css = mConfigPath + "style.css"; + if ( params.css.empty() ) + params.css = mConfigPath + "style.css"; - if ( FileSystem::fileExists( css ) ) { + if ( FileSystem::fileExists( params.css ) ) { CSS::StyleSheetParser parser; - if ( parser.loadFromFile( css ) ) + if ( parser.loadFromFile( params.css ) ) mUISceneNode->combineStyleSheet( parser.getStyleSheet(), false ); } @@ -4051,7 +4095,7 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe mUISceneNode->on( Event::KeyDown, [this]( const Event* event ) { trySendUnlockedCmd( *static_cast( event ) ); } ); - if ( logLevel == LogLevel::Debug ) + if ( params.logLevel == LogLevel::Debug ) mUISceneNode->setVerbose( true ); mDocInfo->setVisible( mConfig.editor.showDocInfo ); @@ -4174,15 +4218,15 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe file = ""; #endif - if ( terminal && file.empty() && fileToOpen.empty() ) { + if ( params.terminal && params.file.empty() && params.fileToOpen.empty() ) { showSidePanel( false ); showStatusBar( false ); mTerminalManager->createNewTerminal(); } else { - initProjectTreeView( file, openClean ); + initProjectTreeView( params.file, params.openClean ); } - mFileToOpen = fileToOpen; + mFileToOpen = params.fileToOpen; Log::info( "Init ProjectTreeView took: %.2f ms", globalClock.getElapsedTime().asMilliseconds() ); @@ -4348,10 +4392,18 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { "Try to set the default language the editor will be loaded. The language must be supported " "in order to this option do something.", { "language" }, "" ); + args::ValueFlag profile( parser, "profile", "Start with profile at ", + { "profile" }, "" ); + args::Flag disablePlugins( parser, "disable-plugins", + "Disable all plugins. This option is not persisted and is effective " + "only when the command opens a new window.", + { "disable-plugins" } ); + #ifdef EE_TEXT_SHAPER_ENABLED args::Flag textShaper( parser, "text-shaper", "Enables text-shaping capabilities", { "text-shaper" } ); #endif + std::vector args; try { args = Sys::parseArguments( argc, argv ); @@ -4408,15 +4460,29 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { Text::TextShaperEnabled = true; #endif + App::InitParameters params; + params.logLevel = logLevel.Get(); + params.file = folder ? folder.Get() : fileOrFolderPos.Get(); + params.pidelDensity = pixelDensityConf ? pixelDensityConf.Get() : 0.f; + params.colorScheme = prefersColorScheme ? prefersColorScheme.Get() : ""; + params.terminal = terminal.Get(); + params.frameBuffer = fb.Get(); + params.benchmarkMode = benchmarkMode.Get(); + params.css = css.Get(); + params.fileToOpen = file.Get(); + params.stdOutLogs = verbose.Get(); + params.disableFileLogs = disableFileLogs.Get(); + params.openClean = openClean.Get(); + params.portable = portable.Get(); + params.language = language.Get(); + params.incognito = incognito.Get(); + params.prematureExit = health || ( healthLang && !healthLang.Get().empty() ) || + ( exportLangPath && !exportLangPath.Get().empty() ); + params.profile = profile.Get(); + params.disablePlugins = disablePlugins.Get(); + appInstance = eeNew( App, ( jobs, args ) ); - appInstance->init( logLevel.Get(), folder ? folder.Get() : fileOrFolderPos.Get(), - pixelDensityConf ? pixelDensityConf.Get() : 0.f, - prefersColorScheme ? prefersColorScheme.Get() : "", terminal.Get(), fb.Get(), - benchmarkMode.Get(), css.Get(), file.Get(), verbose.Get(), - disableFileLogs.Get(), openClean.Get(), portable.Get(), language.Get(), - incognito.Get(), - health || ( healthLang && !healthLang.Get().empty() ) || - ( exportLangPath && !exportLangPath.Get().empty() ) ); + appInstance->init( params ); if ( exportLangPath && !exportLangPath.Get().empty() ) { Sys::windowAttachConsole(); diff --git a/src/tools/ecode/ecode.hpp b/src/tools/ecode/ecode.hpp index 95af10a22..7198389c1 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -38,11 +38,28 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider { ~App(); - void init( const LogLevel& logLevel, std::string file, const Float& pidelDensity, - const std::string& colorScheme, bool terminal, bool frameBuffer, bool benchmarkMode, - std::string css, const std::string& fileToOpen, bool stdOutLogs, - bool disableFileLogs, bool openClean, bool portable, std::string language, - bool incognito, bool prematureExit ); + struct InitParameters { + LogLevel logLevel{ LogLevel::Info }; + std::string file; + Float pidelDensity{ 0.f }; + std::string colorScheme; + bool terminal{ false }; + bool frameBuffer{ false }; + bool benchmarkMode{ false }; + std::string css; + std::string fileToOpen; + bool stdOutLogs{ false }; + bool disableFileLogs{ false}; + bool openClean{ false }; + bool portable{ false }; + std::string language; + bool incognito{ false }; + bool prematureExit{ false }; + std::string profile; + bool disablePlugins{ false }; + }; + + void init( InitParameters& ); void createWidgetInspector(); @@ -552,6 +569,8 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider { const std::string& getLanguagesPath() const { return mLanguagesPath; } + bool pluginsDisabled() const { return mDisablePlugins; } + protected: std::vector mArgs; EE::Window::Window* mWindow{ nullptr }; @@ -605,6 +624,7 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider { bool mDirTreeReady{ false }; bool mUseFrameBuffer{ false }; bool mBenchmarkMode{ false }; + bool mDisablePlugins{ false }; bool mPortableMode{ false }; bool mPortableModeFailed{ false }; bool mDestroyingApp{ false }; @@ -656,6 +676,7 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider { std::condition_variable mAsyncResourcesLoadCond; std::vector mColorSchemes; bool mAsyncResourcesLoaded{ false }; + std::string mProfilePath; void sortSidePanel(); diff --git a/src/tools/ecode/plugins/plugincontextprovider.hpp b/src/tools/ecode/plugins/plugincontextprovider.hpp index 295d80f9b..4b963d0d2 100644 --- a/src/tools/ecode/plugins/plugincontextprovider.hpp +++ b/src/tools/ecode/plugins/plugincontextprovider.hpp @@ -134,6 +134,8 @@ class PluginContextProvider { virtual AppConfig& getConfig() = 0; virtual bool isDirTreeReady() const = 0; + + virtual bool pluginsDisabled() const = 0; }; } // namespace ecode diff --git a/src/tools/ecode/plugins/pluginmanager.hpp b/src/tools/ecode/plugins/pluginmanager.hpp index b3a416315..d9c8ae358 100644 --- a/src/tools/ecode/plugins/pluginmanager.hpp +++ b/src/tools/ecode/plugins/pluginmanager.hpp @@ -257,8 +257,8 @@ class PluginRequestHandle { protected: PluginIDType mId{ 0 }; - PluginImmediateResponse - mResponse; //! Some requests can be responded immediately, so the message comes in the handle + PluginImmediateResponse mResponse; //! Some requests can be responded immediately, so the + //! message comes in the handle }; class PluginManager { @@ -275,9 +275,8 @@ class PluginManager { using OnLoadFileCb = std::function; PluginManager( const std::string& resourcesPath, const std::string& pluginsPath, - const std::string& configPath, - std::shared_ptr pool, const OnLoadFileCb& loadFileCb, - PluginContextProvider* context ); + const std::string& configPath, std::shared_ptr pool, + const OnLoadFileCb& loadFileCb, PluginContextProvider* context ); ~PluginManager(); @@ -368,6 +367,10 @@ class PluginManager { void forEachPlugin( std::function fn ); + void setPluginsDisabled( bool pluginsDisabled ) { mPluginsDisabled = pluginsDisabled; } + + bool pluginsDisabled() const { return mPluginsDisabled; } + protected: using SubscribedPlugins = std::map>; @@ -392,6 +395,7 @@ class PluginManager { UnorderedSet mPluginsFSSubs; bool mClosing{ false }; bool mPluginReloadEnabled{ false }; + bool mPluginsDisabled{ false }; bool hasDefinition( const std::string& id ); diff --git a/src/tools/ecode/settingsmenu.cpp b/src/tools/ecode/settingsmenu.cpp index 954fe9e18..f8ca55dcd 100644 --- a/src/tools/ecode/settingsmenu.cpp +++ b/src/tools/ecode/settingsmenu.cpp @@ -963,6 +963,11 @@ UIMenu* SettingsMenu::createTerminalMenu() { mTerminalMenu->addSubMenu( i18n( "new_terminal_behavior", "New Terminal Behavior" ), findIcon( "terminal" ), newTerminalBehaviorSubMenu ); + mTerminalMenu + ->addCheckBox( i18n( "close_terminal_tab_on_exit", "Close Terminal Tab on Exit" ), + mApp->getConfig().term.closeTerminalTabOnExit ) + ->setId( "close-terminal-tab-on-exit" ); + mTerminalMenu ->add( i18n( "configure_terminal_shell", "Configure Terminal Shell" ), findIcon( "terminal" ), getKeybind( "configure-terminal-shell" ) ) @@ -980,7 +985,14 @@ UIMenu* SettingsMenu::createTerminalMenu() { mTerminalMenu->on( Event::OnItemClicked, [this]( const Event* event ) { const std::string& id( event->getNode()->getId() ); - if ( mSplitter->getCurWidget() && mSplitter->getCurWidget()->isType( UI_TYPE_TERMINAL ) ) { + if ( "close-terminal-tab-on-exit" == id ) { + bool active = event->getNode()->asType()->isActive(); + mApp->getConfig().term.closeTerminalTabOnExit = active; + mSplitter->forEachWidgetType( UI_TYPE_TERMINAL, [active]( UIWidget* widget ) { + widget->asType()->getTerm()->setKeepAlive( !active ); + } ); + } else if ( mSplitter->getCurWidget() && + mSplitter->getCurWidget()->isType( UI_TYPE_TERMINAL ) ) { UITerminal* terminal = mSplitter->getCurWidget()->asType(); if ( "exclusive-mode" == id ) { terminal->setExclusiveMode( @@ -1027,10 +1039,6 @@ UIMenu* SettingsMenu::createEditMenu() { ->add( i18n( "open_containing_folder_ellipsis", "Open Containing Folder..." ), findIcon( "folder-open" ), getKeybind( "open-containing-folder" ) ) ->setId( "open-containing-folder" ); - mEditMenu - ->add( i18n( "open_in_new_window_ellipsis", "Open in New Window..." ), findIcon( "window" ), - getKeybind( "open-in-new-window" ) ) - ->setId( "open-in-new-window" ); mEditMenu ->add( i18n( "copy_containing_folder_path_ellipsis", "Copy Containing Folder Path..." ), findIcon( "copy" ), getKeybind( "copy-containing-folder-path" ) ) @@ -1039,17 +1047,32 @@ UIMenu* SettingsMenu::createEditMenu() { ->add( i18n( "copy_file_path", "Copy File Path" ), findIcon( "copy" ), getKeybind( "copy-file-path" ) ) ->setId( "copy-file-path" ); + + UIMenuSeparator* moveSep = mEditMenu->addSeparator(); + + mEditMenu + ->add( i18n( "open_in_new_window", "Open in New Window" ), findIcon( "window" ), + getKeybind( "open-in-new-window" ) ) + ->setId( "open-in-new-window" ); + + mEditMenu + ->add( i18n( "move_to_new_window", "Move to New Window" ), findIcon( "window" ), + getKeybind( "move-to-new-window" ) ) + ->setId( "move-to-new-window" ); + UIMenuSeparator* fileSep = mEditMenu->addSeparator(); mEditMenu ->add( i18n( "key_bindings", "Key Bindings" ), findIcon( "keybindings" ), getKeybind( "keybindings" ) ) ->setId( "keybindings" ); + mEditMenu->on( Event::OnItemClicked, [this]( const Event* event ) { if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) ) return; runCommand( event->getNode()->getId() ); } ); - mEditMenu->on( Event::OnMenuShow, [this, fileSep]( const Event* ) { + + mEditMenu->on( Event::OnMenuShow, [this, fileSep, moveSep]( const Event* ) { if ( !mSplitter->curEditorExistsAndFocused() ) { mEditMenu->getItemId( "undo" )->setEnabled( false ); mEditMenu->getItemId( "redo" )->setEnabled( false ); @@ -1058,7 +1081,9 @@ UIMenu* SettingsMenu::createEditMenu() { mEditMenu->getItemId( "open-containing-folder" )->setVisible( false ); mEditMenu->getItemId( "copy-containing-folder-path" )->setVisible( false ); mEditMenu->getItemId( "open-in-new-window" )->setVisible( false ); + mEditMenu->getItemId( "move-to-new-window" )->setVisible( false ); mEditMenu->getItemId( "copy-file-path" )->setVisible( false ); + moveSep->setVisible( false ); fileSep->setVisible( false ); return; } @@ -1070,7 +1095,9 @@ UIMenu* SettingsMenu::createEditMenu() { mEditMenu->getItemId( "open-containing-folder" )->setVisible( doc->hasFilepath() ); mEditMenu->getItemId( "copy-containing-folder-path" )->setVisible( doc->hasFilepath() ); mEditMenu->getItemId( "open-in-new-window" )->setVisible( doc->hasFilepath() ); + mEditMenu->getItemId( "move-to-new-window" )->setVisible( doc->hasFilepath() ); mEditMenu->getItemId( "copy-file-path" )->setVisible( doc->hasFilepath() ); + moveSep->setVisible( true ); fileSep->setVisible( doc->hasFilepath() ); } ); return mEditMenu; diff --git a/src/tools/ecode/terminalmanager.cpp b/src/tools/ecode/terminalmanager.cpp index 944e4a3f9..140eda5b6 100644 --- a/src/tools/ecode/terminalmanager.cpp +++ b/src/tools/ecode/terminalmanager.cpp @@ -524,6 +524,16 @@ UITerminal* TerminalManager::createNewTerminal( term->setTitle( title ); auto csIt = mTerminalColorSchemes.find( mTerminalCurrentColorScheme ); term->getTerm()->getTerminal()->setAllowMemoryTrimnming( true ); + term->getTerm()->setKeepAlive( !mApp->getConfig().term.closeTerminalTabOnExit ); + term->getTerm()->pushEventCallback( [this, term]( const TerminalDisplay::Event& event ) { + if ( event.type == TerminalDisplay::EventType::PROCESS_EXIT && + mApp->getConfig().term.closeTerminalTabOnExit && term->getData() != 0 ) { + UITab* tab = (UITab*)term->getData(); + auto* tabWidget = mApp->getSplitter()->tabWidgetFromWidget( term ); + if ( tabWidget ) + tabWidget->removeTab( tab ); + } + } ); term->setColorScheme( csIt != mTerminalColorSchemes.end() ? mTerminalColorSchemes.at( mTerminalCurrentColorScheme ) : TerminalColorScheme::getDefault() ); @@ -566,6 +576,26 @@ UITerminal* TerminalManager::createNewTerminal( term->setExclusiveMode( !term->getExclusiveMode() ); mApp->updateTerminalMenu(); } ); + term->setCommand( "move-tab-to-start", [this] { + auto widget = mApp->getSplitter()->getCurWidget(); + if ( widget == nullptr || widget->getData() == 0 ) + return; + UITabWidget* tabWidget = mApp->getSplitter()->tabWidgetFromWidget( widget ); + if ( tabWidget ) { + UITab* tab = (UITab*)widget->getData(); + tabWidget->moveTab( tab, 0 ); + } + } ); + term->setCommand( "move-tab-to-end", [this] { + auto widget = mApp->getSplitter()->getCurWidget(); + if ( widget == nullptr || widget->getData() == 0 ) + return; + UITabWidget* tabWidget = mApp->getSplitter()->tabWidgetFromWidget( widget ); + if ( tabWidget ) { + UITab* tab = (UITab*)widget->getData(); + tabWidget->moveTab( tab, tabWidget->getTabCount() ); + } + } ); mApp->registerUnlockedCommands( *term ); term->setFocus(); return term;