diff --git a/include/eepp/ui/tools/uicodeeditorsplitter.hpp b/include/eepp/ui/tools/uicodeeditorsplitter.hpp index bc20b0115..ef3bd1e09 100644 --- a/include/eepp/ui/tools/uicodeeditorsplitter.hpp +++ b/include/eepp/ui/tools/uicodeeditorsplitter.hpp @@ -116,6 +116,8 @@ class EE_API UICodeEditorSplitter { std::vector> getTabFromOwnedWidgetId( const std::string& id ); + bool ownedWidgetExists( UIWidget* widget ); + bool removeTabWithOwnedWidgetId( const std::string& id, bool destroyOwnedNode = true, bool immediateClose = false ); diff --git a/src/eepp/ui/tools/uicodeeditorsplitter.cpp b/src/eepp/ui/tools/uicodeeditorsplitter.cpp index 484e350cf..95bd449f1 100644 --- a/src/eepp/ui/tools/uicodeeditorsplitter.cpp +++ b/src/eepp/ui/tools/uicodeeditorsplitter.cpp @@ -596,6 +596,23 @@ UICodeEditorSplitter::getTabFromOwnedWidgetId( const std::string& id ) { return ret; } +bool UICodeEditorSplitter::ownedWidgetExists( UIWidget* widget ) { + if ( !widget ) + return false; + bool found = false; + forEachTabWidgetStoppable( [&found, widget]( UITabWidget* tabWidget ) { + for ( size_t i = 0; i < tabWidget->getTabCount(); ++i ) { + UITab* tab = tabWidget->getTab( i ); + if ( tab->getOwnedWidget() == widget ) { + found = true; + return true; + } + } + return false; + } ); + return found; +} + bool UICodeEditorSplitter::removeTabWithOwnedWidgetId( const std::string& id, bool destroyOwnedNode, bool immediateClose ) { auto ret = getTabFromOwnedWidgetId( id ); diff --git a/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp b/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp index 25e016e85..041f0d13a 100644 --- a/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp +++ b/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp @@ -264,6 +264,16 @@ class TerminalDisplay : public ITerminalDisplay { void setKeepAlive( bool keepAlive ); + bool useFrameBuffer() const; + + const std::string& getProgram() const { return mProgram; } + + const std::vector& getArgs() const { return mArgs; } + + const std::unordered_map& getEnv() { return mEnv; } + + const std::string& getWorkingDir() const { return mWorkingDir; } + protected: EE::Window::Window* mWindow; std::vector mBuffer; diff --git a/src/modules/eterm/include/eterm/ui/uiterminal.hpp b/src/modules/eterm/include/eterm/ui/uiterminal.hpp index f017bb1a8..3b7e507b4 100644 --- a/src/modules/eterm/include/eterm/ui/uiterminal.hpp +++ b/src/modules/eterm/include/eterm/ui/uiterminal.hpp @@ -114,6 +114,8 @@ class UITerminal : public UIWidget { void setColorScheme( const TerminalColorScheme& colorScheme ); + void restart(); + protected: std::string mTitle; bool mIsCustomTitle{ false }; @@ -183,6 +185,8 @@ class UITerminal : public UIWidget { virtual void updateScrollPosition(); virtual void onScrollChange(); + + void registerNewTerminal(); }; }} // namespace eterm::UI diff --git a/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp b/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp index a9eb81f21..4f58a2f89 100644 --- a/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp +++ b/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp @@ -1799,4 +1799,8 @@ Rectf TerminalDisplay::updateIMELocation() { return r; } +bool TerminalDisplay::useFrameBuffer() const { + return mUseFrameBuffer; +} + }} // namespace eterm::Terminal diff --git a/src/modules/eterm/src/eterm/ui/uiterminal.cpp b/src/modules/eterm/src/eterm/ui/uiterminal.cpp index 597866665..db1f41f82 100644 --- a/src/modules/eterm/src/eterm/ui/uiterminal.cpp +++ b/src/modules/eterm/src/eterm/ui/uiterminal.cpp @@ -47,13 +47,8 @@ void UITerminal::draw() { } } -UITerminal::UITerminal( const std::shared_ptr& terminalDisplay ) : - UIWidget( "terminal" ), - mKeyBindings( getInput() ), - mVScroll( UIScrollBar::NewVertical() ), - mTerm( terminalDisplay ) { - mFlags |= UI_TAB_STOP | UI_SCROLLABLE; - if ( !terminalDisplay ) +void UITerminal::registerNewTerminal() { + if ( !mTerm ) return; mTerm->pushEventCallback( [this]( const TerminalDisplay::Event& event ) { switch ( event.type ) { @@ -77,7 +72,17 @@ UITerminal::UITerminal( const std::shared_ptr& terminalDisplay } } } ); +} +UITerminal::UITerminal( const std::shared_ptr& terminalDisplay ) : + UIWidget( "terminal" ), + mKeyBindings( getInput() ), + mVScroll( UIScrollBar::NewVertical() ), + mTerm( terminalDisplay ) { + mFlags |= UI_TAB_STOP | UI_SCROLLABLE; + if ( !terminalDisplay ) + return; + registerNewTerminal(); mVScroll->setParent( this ); mVScroll->on( Event::OnValueChange, [this]( const Event* ) { updateScroll(); } ); @@ -599,4 +604,12 @@ bool UITerminal::onCreateContextMenu( const Vector2i& position, const Uint32& fl return true; } +void UITerminal::restart() { + auto win = SceneManager::instance()->getUISceneNode()->getWindow(); + mTerm = TerminalDisplay::create( + win, mTerm->getFont(), mTerm->getFontSize(), mTerm->getSize(), mTerm->getProgram(), + mTerm->getArgs(), mTerm->getWorkingDir(), mTerm->getTerminal()->getHistorySize(), nullptr, + mTerm->useFrameBuffer(), mTerm->getKeepAlive(), mTerm->getEnv() ); +} + }} // namespace eterm::UI diff --git a/src/tools/ecode/projectbuild.cpp b/src/tools/ecode/projectbuild.cpp index e67a57bbb..1a94d50cd 100644 --- a/src/tools/ecode/projectbuild.cpp +++ b/src/tools/ecode/projectbuild.cpp @@ -109,6 +109,8 @@ json ProjectBuild::serialize( const ProjectBuild::Map& builds ) { step["enabled"] = run->enabled; if ( run->runInTerminal ) step["run_in_terminal"] = run->runInTerminal; + if ( run->reusePreviousTerminal ) + step["reuse_previous_terminal"] = run->reusePreviousTerminal; jrun.push_back( step ); } } @@ -360,6 +362,13 @@ ProjectBuildManager::~ProjectBuildManager() { if ( mUISceneNode && !SceneManager::instance()->isShuttingDown() && mSidePanel && mTab ) { mSidePanel->removeTab( mTab ); } + + if ( mUISceneNode && !SceneManager::instance()->isShuttingDown() && mApp->getSplitter() && + mLastUsedTerm && mApp->getSplitter()->ownedWidgetExists( mLastUsedTerm ) && + mLastUsedTermCloseCbId ) { + mLastUsedTerm->removeEventListener( mLastUsedTermCloseCbId ); + } + if ( mUISceneNode && mUISceneNode->getRoot()->querySelector( "#build_settings_new_name" ) ) addNewBuild(); @@ -550,6 +559,7 @@ ProjectBuild::Map ProjectBuild::deserialize( const json& j, const std::string& p rstep->workingDir = step.value( "working_dir", "" ); rstep->enabled = step.value( "enabled", true ); rstep->runInTerminal = step.value( "run_in_terminal", false ); + rstep->reusePreviousTerminal = step.value( "reuse_previous_terminal", false ); b.mRun.emplace_back( std::move( rstep ) ); } } @@ -838,14 +848,26 @@ void ProjectBuildManager::runConfig( StatusAppOutputController* saoc ) { auto cmd = finalBuild.cmd + ( !finalBuild.args.empty() ? ( " " + finalBuild.args ) : "" ); if ( finalBuild.runInTerminal ) { - UITerminal* term = mApp->getTerminalManager()->createTerminalInSplitter( - finalBuild.workingDir, "", {}, {}, false ); + bool mustReuseLastUsedTerm = finalBuild.reusePreviousTerminal && mLastUsedTerm && + mApp->getSplitter()->ownedWidgetExists( mLastUsedTerm ); + + UITerminal* term = mustReuseLastUsedTerm + ? mLastUsedTerm + : mApp->getTerminalManager()->createTerminalInSplitter( + finalBuild.workingDir, "", {}, {}, false ); Log::info( "Running \"%s\" in terminal", cmd ); if ( term == nullptr || term->getTerm() == nullptr ) { mApp->getTerminalManager()->openInExternalTerminal( cmd, finalBuild.workingDir ); } else { + if ( mustReuseLastUsedTerm ) { + term->restart(); + } else { + mLastUsedTermCloseCbId = + term->on( Event::OnClose, [this]( auto ) { mLastUsedTerm = nullptr; } ); + } term->executeFile( cmd ); + mLastUsedTerm = term; } } else { Log::info( "Running \"%s\" in app", cmd ); diff --git a/src/tools/ecode/projectbuild.hpp b/src/tools/ecode/projectbuild.hpp index bdf7ce648..ce12c78d9 100644 --- a/src/tools/ecode/projectbuild.hpp +++ b/src/tools/ecode/projectbuild.hpp @@ -15,6 +15,11 @@ using namespace EE; using namespace EE::System; using namespace EE::UI; +namespace eterm::UI { +class UITerminal; +} +using namespace eterm::UI; + namespace ecode { class App; @@ -100,6 +105,7 @@ struct ProjectBuildStep { std::string name; bool enabled{ true }; bool runInTerminal{ false }; + bool reusePreviousTerminal{ false }; }; using ProjectBuildSteps = std::vector>; @@ -353,6 +359,7 @@ class ProjectBuildManager { UISceneNode* mUISceneNode{ nullptr }; UITab* mTab{ nullptr }; App* mApp{ nullptr }; + UITerminal* mLastUsedTerm{ nullptr }; std::unique_ptr mProcess; std::unique_ptr mProcessRun; ProjectBuild mNewBuild; @@ -364,6 +371,7 @@ class ProjectBuildManager { bool mCancelRun{ false }; bool mRunning{ false }; std::unordered_map> mCbs; + Uint32 mLastUsedTermCloseCbId{ 0 }; void runBuild( const std::string& buildName, const std::string& buildType, const ProjectBuildi18nFn& i18n, const ProjectBuildCommandsRes& res, diff --git a/src/tools/ecode/uibuildsettings.cpp b/src/tools/ecode/uibuildsettings.cpp index 932f0c66e..c7d391f19 100644 --- a/src/tools/ecode/uibuildsettings.cpp +++ b/src/tools/ecode/uibuildsettings.cpp @@ -238,6 +238,8 @@ class UIBuildStep : public UILinearLayout { UIDataBindString::New( &mStep->workingDir, findByClass( "input_working_dir" ) ); mDataBindHolder += UIDataBindBool::New( &mStep->runInTerminal, findByClass( "run_in_terminal" ) ); + mDataBindHolder += UIDataBindBool::New( &mStep->reusePreviousTerminal, + findByClass( "reuse_previous_terminal" ) ); } protected: @@ -274,7 +276,10 @@ class UIBuildStep : public UILinearLayout { - + + + + )xml"; @@ -283,8 +288,20 @@ class UIBuildStep : public UILinearLayout { if ( !isBuildOrClean() ) { findByClass( "enabled_checkbox" )->setVisible( false ); auto runInTerminal = findByClass( "run_in_terminal" )->asType(); + + auto reusePreviousTerminal = + findByClass( "reuse_previous_terminal" )->asType(); + runInTerminal->setVisible( true ); runInTerminal->setChecked( buildStep->runInTerminal ); + runInTerminal->on( Event::OnValueChange, + [reusePreviousTerminal, runInTerminal]( auto ) { + reusePreviousTerminal->setEnabled( runInTerminal->isChecked() ); + } ); + + reusePreviousTerminal->setVisible( true ); + reusePreviousTerminal->setEnabled( buildStep->runInTerminal ); + reusePreviousTerminal->setChecked( buildStep->reusePreviousTerminal ); } findByClass( "details_but" )->onClick( [this]( const MouseEvent* event ) {