diff --git a/include/eepp/core/memorymanager.hpp b/include/eepp/core/memorymanager.hpp index 6a709be4e..d4067c4d4 100644 --- a/include/eepp/core/memorymanager.hpp +++ b/include/eepp/core/memorymanager.hpp @@ -12,12 +12,14 @@ namespace EE { class EE_API AllocatedPointer { public: - AllocatedPointer( void* Data, const std::string& File, int Line, size_t Memory ); + AllocatedPointer( void* data, const std::string& File, int Line, size_t memory, + bool track = false ); std::string mFile; int mLine; size_t mMemory; void* mData; + bool mTrack; }; typedef std::map AllocatedPointerMap; @@ -27,31 +29,31 @@ class EE_API MemoryManager { public: static void* addPointer( const AllocatedPointer& aAllocatedPointer ); - static void* addPointerInPlace( void* Place, const AllocatedPointer& aAllocatedPointer ); + static void* addPointerInPlace( void* place, const AllocatedPointer& aAllocatedPointer ); - static bool removePointer( void* Data ); + static bool removePointer( void* data, const char* file, const size_t& line ); static void showResults(); - template static T* deletePtr( T* Data ) { - delete Data; - return Data; + template static T* deletePtr( T* data ) { + delete data; + return data; } - template static T* deleteArrayPtr( T* Data ) { - delete[] Data; - return Data; + template static T* deleteArrayPtr( T* data ) { + delete[] data; + return data; } - template static T* free( T* Data ) { - ::free( Data ); + template static T* free( T* data ) { + ::free( data ); #if defined( __GNUC__ ) && __GNUC__ >= 12 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wuse-after-free" - return Data; + return data; #pragma GCC diagnostic pop #else - return Data; + return data; #endif } @@ -67,6 +69,10 @@ class EE_API MemoryManager { }; #ifdef EE_MEMORY_MANAGER +#define eeNewTracked( classType, constructor ) \ + (classType*)EE::MemoryManager::addPointer( EE::AllocatedPointer( \ + new classType constructor, __FILE__, __LINE__, sizeof( classType ), true ) ) + #define eeNew( classType, constructor ) \ (classType*)EE::MemoryManager::addPointer( EE::AllocatedPointer( \ new classType constructor, __FILE__, __LINE__, sizeof( classType ) ) ) @@ -84,25 +90,30 @@ class EE_API MemoryManager { EE::MemoryManager::addPointer( EE::AllocatedPointer( EE::MemoryManager::allocate( amount ), \ __FILE__, __LINE__, amount ) ) -#define eeDelete( data ) \ - { \ - if ( EE::MemoryManager::removePointer( EE::MemoryManager::deletePtr( data ) ) == false ) \ - printf( "Deleting at '%s' %d\n", __FILE__, __LINE__ ); \ +#define eeDelete( data ) \ + { \ + if ( EE::MemoryManager::removePointer( EE::MemoryManager::deletePtr( data ), __FILE__, \ + __LINE__ ) == false ) \ + printf( "Deleting at '%s' %d\n", __FILE__, __LINE__ ); \ } -#define eeDeleteArray( data ) \ - { \ - if ( EE::MemoryManager::removePointer( EE::MemoryManager::deleteArrayPtr( data ) ) == \ - false ) \ - printf( "Deleting at '%s' %d\n", __FILE__, __LINE__ ); \ +#define eeDeleteArray( data ) \ + { \ + if ( EE::MemoryManager::removePointer( EE::MemoryManager::deleteArrayPtr( data ), \ + __FILE__, __LINE__ ) == false ) \ + printf( "Deleting at '%s' %d\n", __FILE__, __LINE__ ); \ } -#define eeFree( data ) \ - { \ - if ( EE::MemoryManager::removePointer( EE::MemoryManager::free( data ) ) == false ) \ - printf( "Deleting at '%s' %d\n", __FILE__, __LINE__ ); \ +#define eeFree( data ) \ + { \ + if ( EE::MemoryManager::removePointer( EE::MemoryManager::free( data ), __FILE__, \ + __LINE__ ) == false ) \ + printf( "Deleting at '%s' %d\n", __FILE__, __LINE__ ); \ } #else + +#define eeNewTracked( classType, constructor ) new classType constructor + #define eeNew( classType, constructor ) new classType constructor #define eeNewInPlace( place, classType, constructor ) new place classType constructor diff --git a/include/eepp/scene/event.hpp b/include/eepp/scene/event.hpp index e63a2072a..5847b6582 100644 --- a/include/eepp/scene/event.hpp +++ b/include/eepp/scene/event.hpp @@ -2,6 +2,7 @@ #define EE_UICUIEVENT_HPP #include +#include namespace EE { namespace Scene { @@ -85,6 +86,7 @@ class EE_API Event { OnMenuShow, OnMenuHide, OnEditorTabReady, + OnTitleChange, NoEvent = eeINDEX_NOT_FOUND }; @@ -115,6 +117,17 @@ class EE_API DropEvent : public Event { Node* droppedNode; }; +class EE_API TextEvent : public Event { + public: + TextEvent( Node* node, const Uint32& eventType, const std::string& txt ) : + Event( node, eventType ), text( txt ) {} + + const std::string& getText() const { return text; } + + protected: + std::string text; +}; + }} // namespace EE::Scene #endif diff --git a/include/eepp/scene/node.hpp b/include/eepp/scene/node.hpp index 777ff5f7e..c54a41525 100644 --- a/include/eepp/scene/node.hpp +++ b/include/eepp/scene/node.hpp @@ -217,6 +217,8 @@ class EE_API Node : public Transformable { void sendCommonEvent( const Uint32& Event ); + void sendTextEvent( const Uint32& event, const std::string& text ); + void childsCloseAll(); const std::string& getId() const; diff --git a/include/eepp/system/inifile.hpp b/include/eepp/system/inifile.hpp index 6a940066f..fb62738c3 100644 --- a/include/eepp/system/inifile.hpp +++ b/include/eepp/system/inifile.hpp @@ -123,6 +123,10 @@ class EE_API IniFile { int getValueI( std::string const keyname, std::string const valuename, int const defValue = 0 ) const; + /** Gets the value as an unsigned long */ + unsigned long getValueU( std::string const keyname, std::string const valuename, + unsigned long const defValue = 0 ) const; + /** Gets the value as boolean */ bool getValueB( std::string const keyname, std::string const valuename, bool const defValue = false ) const; @@ -164,6 +168,15 @@ class EE_API IniFile { bool setValueI( std::string const keyname, std::string const valuename, int const value, bool create = true ); + /** Sets a unsigned long value from a keyname and a valuename + * @param keyname The key name + * @param valuename The value name + * @param value The value to assign + * @param create If true it will create the keyname if doesn't exists + */ + bool setValueU( std::string const keyname, std::string const valuename, + unsigned long const value, bool create = true ); + /** Sets a boolean value from a keyname and a valuename * @param keyname The key name * @param valuename The value name diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index b0ab95598..d592989b1 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -284,6 +284,8 @@ class EE_API TextDocument { void execute( const std::string& command ); + void setCommands( const std::map& cmds ); + void setCommand( const std::string& command, DocumentCommand func ); bool hasCommand( const std::string& command ); diff --git a/include/eepp/ui/tools/uicodeeditorsplitter.hpp b/include/eepp/ui/tools/uicodeeditorsplitter.hpp index e82f94db0..2b6fe4d2c 100644 --- a/include/eepp/ui/tools/uicodeeditorsplitter.hpp +++ b/include/eepp/ui/tools/uicodeeditorsplitter.hpp @@ -26,6 +26,8 @@ class EE_API UICodeEditorSplitter { virtual void onCodeEditorFocusChange( UICodeEditor* editor ) = 0; + virtual void onWidgetFocusChange( UIWidget* widget ) = 0; + virtual void onDocumentStateChanged( UICodeEditor* editor, TextDocument& doc ) = 0; virtual void onDocumentModified( UICodeEditor* editor, TextDocument& doc ) = 0; @@ -39,18 +41,20 @@ class EE_API UICodeEditorSplitter { virtual void onDocumentLoaded( UICodeEditor* codeEditor, const std::string& path ) = 0; }; + static std::vector getUnlockedCommands(); + static UICodeEditorSplitter* New( UICodeEditorSplitter::Client* client, UISceneNode* sceneNode, const std::vector& colorSchemes = {}, const std::string& initColorScheme = "" ); virtual ~UICodeEditorSplitter(); - virtual bool tryTabClose( UICodeEditor* editor ); + virtual bool tryTabClose(UIWidget* widget ); - void closeEditorTab( UICodeEditor* editor ); + void closeTab( UIWidget* widget ); - UISplitter* splitEditor( const SplitDirection& direction, UICodeEditor* editor, - bool openCurEditor = true ); + UISplitter* split( const SplitDirection& direction, UIWidget* editor, + bool openCurEditor = true ); void switchToTab( Int32 index ); @@ -66,10 +70,18 @@ class EE_API UICodeEditorSplitter { UITabWidget* tabWidgetFromEditor( UICodeEditor* editor ) const; + UITabWidget* tabWidgetFromWidget( UIWidget* widget ) const; + UISplitter* splitterFromEditor( UICodeEditor* editor ) const; + UISplitter* splitterFromWidget( UIWidget* widget ) const; + std::pair createCodeEditorInTabWidget( UITabWidget* tabWidget ); + std::pair createWidgetInTabWidget( UITabWidget* tabWidget, UIWidget* widget, + const std::string& tabName, + bool focus = true ); + UICodeEditor* createCodeEditor(); void focusSomeEditor( Node* searchFrom = nullptr ); @@ -101,6 +113,8 @@ class EE_API UICodeEditorSplitter { void applyColorScheme( const SyntaxColorScheme& colorScheme ); + void forEachWidgetStoppable( std::function run ) const; + void forEachEditor( std::function run ) const; void forEachDoc( std::function run ) const; @@ -149,9 +163,16 @@ class EE_API UICodeEditorSplitter { Node* getBaseLayout() const; + UIWidget* getCurWidget() const; + + void setCurentWidget( UIWidget* curWidget ); + + bool curWidgetExists() const; + protected: UISceneNode* mUISceneNode{ nullptr }; UICodeEditor* mCurEditor{ nullptr }; + UIWidget* mCurWidget{ nullptr }; std::map mColorSchemes; std::string mCurrentColorScheme; std::vector mTabWidgets; diff --git a/include/eepp/ui/uihelper.hpp b/include/eepp/ui/uihelper.hpp index 1be954c97..5e4210bf8 100644 --- a/include/eepp/ui/uihelper.hpp +++ b/include/eepp/ui/uihelper.hpp @@ -99,7 +99,9 @@ enum UINodeType { UI_TYPE_TABLEVIEW, UI_TYPE_TABLECELL, UI_TYPE_LISTVIEW, - UI_TYPE_USER = 10000 + UI_TYPE_MODULES = 10000, + UI_TYPE_TERMINAL = 10001, + UI_TYPE_USER = 100000 }; enum class ScrollBarMode : Uint32 { Auto, AlwaysOn, AlwaysOff }; diff --git a/include/eepp/ui/uitabwidget.hpp b/include/eepp/ui/uitabwidget.hpp index addd5e753..12ae50d7b 100644 --- a/include/eepp/ui/uitabwidget.hpp +++ b/include/eepp/ui/uitabwidget.hpp @@ -54,6 +54,8 @@ class EE_API UITabWidget : public UIWidget { UITabWidget* add( UITab* tab ); + UITab* getTabFromOwnedWidget( const UIWidget* widget ); + UITab* getTab( const Uint32& index ); UITab* getTab( const String& text ); diff --git a/premake4.lua b/premake4.lua index 1b12601b6..4e63bd44f 100644 --- a/premake4.lua +++ b/premake4.lua @@ -348,7 +348,7 @@ function build_base_cpp_configuration( package_name ) set_xcode_config() configuration "debug" - defines { "DEBUG" } + defines { "DEBUG", "EE_DEBUG", "EE_MEMORY_MANAGER" } flags { "Symbols" } if not is_vs() then buildoptions{ "-Wall" } @@ -1179,16 +1179,20 @@ solution "eepp" set_kind() language "C++" files { "src/tools/ecode/**.cpp" } - includedirs { "src/thirdparty/efsw/include", "src/thirdparty" } - links { "efsw-static" } - + includedirs { "src/thirdparty/efsw/include", "src/thirdparty", "src/modules/eterm/include/" } + links { "efsw-static", "eterm-static" } if not os.is("windows") and not os.is("haiku") then links { "pthread" } end if os.is("macosx") then links { "CoreFoundation.framework", "CoreServices.framework" } end - + if os.is_real("linux") then + links { "util" } + end + if os.is("haiku") then + links { "bsd" } + end build_link_configuration( "ecode", true ) project "eterm" diff --git a/premake5.lua b/premake5.lua index 736a93ae8..de711d2e9 100644 --- a/premake5.lua +++ b/premake5.lua @@ -195,7 +195,7 @@ function build_base_cpp_configuration( package_name ) buildoptions { "-Wall" } filter "configurations:debug*" - defines { "DEBUG" } + defines { "DEBUG", "EE_DEBUG", "EE_MEMORY_MANAGER" } symbols "On" targetname ( package_name .. "-debug" ) @@ -926,13 +926,17 @@ workspace "eepp" set_kind() language "C++" files { "src/tools/ecode/**.cpp" } - incdirs { "src/thirdparty/efsw/include", "src/thirdparty" } - links { "efsw-static" } + incdirs { "src/thirdparty/efsw/include", "src/thirdparty", "src/modules/eterm/include/" } + links { "efsw-static", "eterm-static" } build_link_configuration( "ecode", true ) filter "system:macosx" links { "CoreFoundation.framework", "CoreServices.framework" } filter { "system:not windows", "system:not haiku" } links { "pthread" } + filter "system:linux" + links { "util" } + filter "system:haiku" + links { "bsd" } project "eterm" set_kind() diff --git a/projects/linux/ee.creator.user b/projects/linux/ee.creator.user index fb21b8afe..5f8e07e46 100644 --- a/projects/linux/ee.creator.user +++ b/projects/linux/ee.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -107,7 +107,7 @@ Desktop Desktop {388e5431-b31b-42b3-b9ad-9002d279d75d} - 12 + 10 0 19 diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 08495769c..1da00af4c 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -1013,6 +1013,7 @@ ../../src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp ../../src/modules/eterm/include/eterm/terminal/terminalemulator.hpp ../../src/modules/eterm/include/eterm/terminal/terminaltypes.hpp +../../src/modules/eterm/include/eterm/ui/uiterminal.hpp ../../src/modules/eterm/src/eterm/system/autohandle.cpp ../../src/modules/eterm/src/eterm/system/pipe.cpp ../../src/modules/eterm/src/eterm/system/process.cpp @@ -1026,6 +1027,7 @@ ../../src/modules/eterm/src/eterm/terminal/types.hpp ../../src/modules/eterm/src/eterm/terminal/wide.hpp ../../src/modules/eterm/src/eterm/terminal/windowserrors.hpp +../../src/modules/eterm/src/eterm/ui/uiterminal.cpp ../../src/test/eetest.cpp ../../src/tests/test_all/test.cpp ../../src/tests/test_all/test.hpp diff --git a/projects/linux/ee.includes b/projects/linux/ee.includes index 4a587d98d..583984a46 100644 --- a/projects/linux/ee.includes +++ b/projects/linux/ee.includes @@ -10,3 +10,5 @@ /usr/lib64/gcc/x86_64-suse-linux/10/include ../../src/modules/eterm/src/ ../../src/modules/eterm/include/ +../../src/modules/eterm/include/eterm/ui +../../src/modules/eterm/src/eterm/ui diff --git a/src/eepp/core/memorymanager.cpp b/src/eepp/core/memorymanager.cpp index aa62ff304..b35a34b19 100644 --- a/src/eepp/core/memorymanager.cpp +++ b/src/eepp/core/memorymanager.cpp @@ -19,18 +19,20 @@ static size_t sPeakMemoryUsage = 0; static AllocatedPointer sBiggestAllocation = AllocatedPointer( NULL, "", 0, 0 ); static Mutex sAlloMutex; -AllocatedPointer::AllocatedPointer( void* Data, const std::string& File, int Line, size_t Memory ) { - mData = Data; - mFile = File; - mLine = Line; - mMemory = Memory; +AllocatedPointer::AllocatedPointer( void* data, const std::string& file, int line, size_t memory, + bool track ) { + mData = data; + mFile = file; + mLine = line; + mMemory = memory; + mTrack = track; } -void* MemoryManager::addPointerInPlace( void* Place, const AllocatedPointer& aAllocatedPointer ) { - AllocatedPointerMapIt it = sMapPointers.find( Place ); +void* MemoryManager::addPointerInPlace( void* place, const AllocatedPointer& aAllocatedPointer ) { + AllocatedPointerMapIt it = sMapPointers.find( place ); if ( it != sMapPointers.end() ) { - removePointer( Place ); + removePointer( place, aAllocatedPointer.mFile.c_str(), aAllocatedPointer.mLine ); } return addPointer( aAllocatedPointer ); @@ -52,16 +54,23 @@ void* MemoryManager::addPointer( const AllocatedPointer& aAllocatedPointer ) { sBiggestAllocation = aAllocatedPointer; } + if ( aAllocatedPointer.mTrack ) + eePRINTL( "Allocating pointer %p at '%s' %d", aAllocatedPointer.mData, + aAllocatedPointer.mFile.c_str(), aAllocatedPointer.mLine ); + return aAllocatedPointer.mData; } -bool MemoryManager::removePointer( void* Data ) { +bool MemoryManager::removePointer( void* data, const char* file, const size_t& line ) { Lock l( sAlloMutex ); - AllocatedPointerMapIt it = sMapPointers.find( Data ); + AllocatedPointerMapIt it = sMapPointers.find( data ); + + if ( it->second.mTrack ) + eePRINTL( "Deleting pointer %p at '%s' %d", data, file, line ); if ( it == sMapPointers.end() ) { - eePRINTL( "Trying to delete pointer %p created that does not exist!", Data ); + eePRINTL( "Trying to delete pointer %p created that does not exist!", data ); return false; } diff --git a/src/eepp/scene/node.cpp b/src/eepp/scene/node.cpp index 3699b2c04..d2cbb110a 100644 --- a/src/eepp/scene/node.cpp +++ b/src/eepp/scene/node.cpp @@ -303,6 +303,11 @@ void Node::sendCommonEvent( const Uint32& event ) { sendEvent( &CommonEvent ); } +void Node::sendTextEvent( const Uint32& event, const std::string& text ) { + TextEvent tevent( this, event, text ); + sendEvent( &tevent ); +} + Uint32 Node::onTextInput( const TextInputEvent& event ) { sendEvent( &event ); return 0; diff --git a/src/eepp/system/inifile.cpp b/src/eepp/system/inifile.cpp index b5cfbeca6..a5899f8c7 100644 --- a/src/eepp/system/inifile.cpp +++ b/src/eepp/system/inifile.cpp @@ -305,6 +305,14 @@ bool IniFile::setValueI( std::string const keyname, std::string const valuename, return setValue( keyname, valuename, svalue, create ); } +bool IniFile::setValueU( const std::string keyname, const std::string valuename, + const unsigned long value, bool create ) { + char svalue[MAX_VALUEDATA]; + + String::formatBuffer( svalue, MAX_VALUEDATA, "%u", value ); + return setValue( keyname, valuename, svalue, create ); +} + bool IniFile::setValueF( std::string const keyname, std::string const valuename, double const value, bool create ) { char svalue[MAX_VALUEDATA]; @@ -356,6 +364,14 @@ int IniFile::getValueI( std::string const keyname, std::string const valuename, return atoi( getValue( keyname, valuename, svalue ).c_str() ); } +unsigned long IniFile::getValueU( const std::string keyname, const std::string valuename, + const unsigned long defValue ) const { + char svalue[MAX_VALUEDATA]; + + String::formatBuffer( svalue, MAX_VALUEDATA, "%u", defValue ); + return atoi( getValue( keyname, valuename, svalue ).c_str() ); +} + bool IniFile::getValueB( const std::string keyname, const std::string valuename, const bool defValue ) const { std::string val = getValue( keyname, valuename, defValue ? "1" : "0" ); diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index 4c41d5f2c..e5bb069a7 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -1445,6 +1445,10 @@ void TextDocument::execute( const std::string& command ) { } } +void TextDocument::setCommands( const std::map& cmds ) { + mCommands.insert( cmds.begin(), cmds.end() ); +} + void TextDocument::setCommand( const std::string& command, TextDocument::DocumentCommand func ) { mCommands[command] = func; } diff --git a/src/eepp/ui/tools/uicodeeditorsplitter.cpp b/src/eepp/ui/tools/uicodeeditorsplitter.cpp index c290d58c7..0110f5382 100644 --- a/src/eepp/ui/tools/uicodeeditorsplitter.cpp +++ b/src/eepp/ui/tools/uicodeeditorsplitter.cpp @@ -22,7 +22,7 @@ UICodeEditorSplitter::getLocalDefaultKeybindings() { { { KEY_S, KeyMod::getDefaultModifier() }, "save-doc" }, { { KEY_L, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "lock-toggle" }, { { KEY_T, KeyMod::getDefaultModifier() }, "create-new" }, - { { KEY_W, KeyMod::getDefaultModifier() }, "close-doc" }, + { { KEY_W, KeyMod::getDefaultModifier() }, "close-tab" }, { { KEY_TAB, KeyMod::getDefaultModifier() }, "next-doc" }, { { KEY_TAB, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "previous-doc" }, { { KEY_J, KEYMOD_LALT | KEYMOD_SHIFT }, "split-left" }, @@ -57,6 +57,24 @@ UICodeEditorSplitter::getLocalDefaultKeybindings() { }; } +std::vector UICodeEditorSplitter::getUnlockedCommands() { + const std::vector unlockedCmds{ "lock-toggle", + "create-new", + "close-tab", + "next-doc", + "previous-doc", + "split-left", + "split-right", + "split-top", + "split-bottom", + "split-swap", + "switch-to-previous-split", + "switch-to-next-split", + "switch-to-previous-colorscheme", + "switch-to-next-colorscheme" }; + return unlockedCmds; +} + UICodeEditorSplitter* UICodeEditorSplitter::New( UICodeEditorSplitter::Client* client, UISceneNode* sceneNode, const std::vector& colorSchemes, @@ -89,12 +107,26 @@ UITabWidget* UICodeEditorSplitter::tabWidgetFromEditor( UICodeEditor* editor ) c return nullptr; } +UITabWidget* UICodeEditorSplitter::tabWidgetFromWidget( UIWidget* widget ) const { + if ( widget ) + return ( (UITab*)widget->getData() )->getTabWidget(); + return nullptr; +} + UISplitter* UICodeEditorSplitter::splitterFromEditor( UICodeEditor* editor ) const { if ( editor && editor->getParent()->getParent()->getParent()->isType( UI_TYPE_SPLITTER ) ) return editor->getParent()->getParent()->getParent()->asType(); return nullptr; } +UISplitter* UICodeEditorSplitter::splitterFromWidget( UIWidget* widget ) const { + if ( widget && widget->getParent() && widget->getParent()->getParent() && + widget->getParent()->getParent()->getParent() && + widget->getParent()->getParent()->getParent()->isType( UI_TYPE_SPLITTER ) ) + return widget->getParent()->getParent()->getParent()->asType(); + return nullptr; +} + UICodeEditor* UICodeEditorSplitter::createCodeEditor() { UICodeEditor* editor = UICodeEditor::NewOpt( false, true ); TextDocument& doc = editor->getDocument(); @@ -187,7 +219,7 @@ UICodeEditor* UICodeEditorSplitter::createCodeEditor() { } ); doc.setCommand( "switch-to-previous-split", [&] { switchPreviousSplit( mCurEditor ); } ); doc.setCommand( "switch-to-next-split", [&] { switchNextSplit( mCurEditor ); } ); - doc.setCommand( "close-doc", [&] { tryTabClose( mCurEditor ); } ); + doc.setCommand( "close-tab", [&] { tryTabClose( mCurEditor ); } ); doc.setCommand( "create-new", [&] { auto d = createCodeEditorInTabWidget( tabWidgetFromEditor( mCurEditor ) ); d.first->getTabWidget()->setTabSelected( d.first ); @@ -223,12 +255,12 @@ UICodeEditor* UICodeEditorSplitter::createCodeEditor() { switchToTab( tabWidget->getTabCount() - 1 ); } } ); - doc.setCommand( "split-right", [&] { splitEditor( SplitDirection::Right, mCurEditor ); } ); - doc.setCommand( "split-bottom", [&] { splitEditor( SplitDirection::Bottom, mCurEditor ); } ); - doc.setCommand( "split-left", [&] { splitEditor( SplitDirection::Left, mCurEditor ); } ); - doc.setCommand( "split-top", [&] { splitEditor( SplitDirection::Top, mCurEditor ); } ); + doc.setCommand( "split-right", [&] { split( SplitDirection::Right, mCurWidget ); } ); + doc.setCommand( "split-bottom", [&] { split( SplitDirection::Bottom, mCurWidget ); } ); + doc.setCommand( "split-left", [&] { split( SplitDirection::Left, mCurWidget ); } ); + doc.setCommand( "split-top", [&] { split( SplitDirection::Top, mCurWidget ); } ); doc.setCommand( "split-swap", [&] { - if ( UISplitter* splitter = splitterFromEditor( mCurEditor ) ) + if ( UISplitter* splitter = splitterFromWidget( mCurWidget ) ) splitter->swap(); } ); doc.setCommand( "open-containing-folder", [&] { @@ -257,11 +289,7 @@ UICodeEditor* UICodeEditorSplitter::createCodeEditor() { event->getNode()->asType()->getDocument() ); } ); editor->addKeyBinds( getLocalDefaultKeybindings() ); - editor->addUnlockedCommands( { "lock-toggle", "create-new", "close-doc", "next-doc", - "previous-doc", "split-left", "split-right", "split-top", - "split-bottom", "split-swap", "switch-to-previous-split", - "switch-to-next-split", "switch-to-previous-colorscheme", - "switch-to-next-colorscheme" } ); + editor->addUnlockedCommands( getUnlockedCommands() ); if ( nullptr == mCurEditor ) { mAboutToAddEditor = editor; @@ -386,16 +414,31 @@ void UICodeEditorSplitter::loadAsyncFileFromPathInNewTab( void UICodeEditorSplitter::setCurrentEditor( UICodeEditor* editor ) { eeASSERT( checkEditorExists( editor ) ); bool isNew = mCurEditor != editor; + bool isNewW = mCurWidget != editor; mCurEditor = editor; + mCurWidget = editor; + if ( isNewW ) + mClient->onWidgetFocusChange( editor ); if ( isNew ) mClient->onCodeEditorFocusChange( editor ); if ( editor ) mClient->onDocumentStateChanged( editor, editor->getDocument() ); } +void UICodeEditorSplitter::setCurentWidget( UIWidget* curWidget ) { + if ( curWidget->isType( UI_TYPE_CODEEDITOR ) ) { + setCurrentEditor( curWidget->asType() ); + return; + } + bool isNewW = mCurWidget != curWidget; + mCurWidget = curWidget; + if ( isNewW ) + mClient->onWidgetFocusChange( curWidget ); +} + std::pair UICodeEditorSplitter::createCodeEditorInTabWidget( UITabWidget* tabWidget ) { - eeASSERT( curEditorExists() ); + eeASSERT( curWidgetExists() ); if ( nullptr == tabWidget ) return std::make_pair( (UITab*)nullptr, (UICodeEditor*)nullptr ); UICodeEditor* editor = createCodeEditor(); @@ -413,8 +456,34 @@ UICodeEditorSplitter::createCodeEditorInTabWidget( UITabWidget* tabWidget ) { return std::make_pair( tab, editor ); } +std::pair +UICodeEditorSplitter::createWidgetInTabWidget( UITabWidget* tabWidget, UIWidget* widget, + const std::string& tabName, bool focus ) { + eeASSERT( curWidgetExists() ); + if ( nullptr == tabWidget ) + return std::make_pair( (UITab*)nullptr, (UIWidget*)nullptr ); + UITab* tab = tabWidget->add( tabName, widget ); + widget->setData( (UintPtr)tab ); + widget->addEventListener( Event::OnFocus, [&]( const Event* event ) { + setCurentWidget( event->getNode()->asType() ); + } ); + widget->addEventListener( Event::OnTitleChange, [&]( const Event* event ) { + const TextEvent* tevent = static_cast( event ); + UIWidget* widget = event->getNode()->asType(); + UITabWidget* tabWidget = tabWidgetFromWidget( widget ); + UITab* tab = tabWidget->getTabFromOwnedWidget( widget ); + if ( !tab ) + return; + tab->setText( tevent->getText() ); + } ); + if ( focus ) + tabWidget->setTabSelected( tab ); + return std::make_pair( tab, widget ); +} + void UICodeEditorSplitter::removeUnusedTab( UITabWidget* tabWidget ) { if ( tabWidget && tabWidget->getTabCount() >= 2 && + tabWidget->getTab( 0 )->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) && tabWidget->getTab( 0 ) ->getOwnedWidget() ->asType() @@ -425,7 +494,7 @@ void UICodeEditorSplitter::removeUnusedTab( UITabWidget* tabWidget ) { } UITabWidget* UICodeEditorSplitter::createEditorWithTabWidget( Node* parent, bool openCurEditor ) { - eeASSERT( curEditorExists() ); + eeASSERT( curWidgetExists() ); if ( nullptr == mBaseLayout ) mBaseLayout = parent; UICodeEditor* prevCurEditor = mCurEditor; @@ -439,11 +508,19 @@ UITabWidget* UICodeEditorSplitter::createEditorWithTabWidget( Node* parent, bool tabWidget->setAllowSwitchTabsInEmptySpaces( true ); tabWidget->addEventListener( Event::OnTabSelected, [&]( const Event* event ) { UITabWidget* tabWidget = event->getNode()->asType(); - setCurrentEditor( tabWidget->getTabSelected()->getOwnedWidget()->asType() ); + if ( tabWidget->getTabSelected()->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) { + setCurrentEditor( + tabWidget->getTabSelected()->getOwnedWidget()->asType() ); + } else { + setCurentWidget( tabWidget->getTabSelected()->getOwnedWidget()->asType() ); + } } ); tabWidget->setTabTryCloseCallback( [&]( UITab* tab ) -> bool { - tryTabClose( tab->getOwnedWidget()->asType() ); - return false; + if ( tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) { + tryTabClose( tab->getOwnedWidget()->asType() ); + return false; + } + return true; } ); tabWidget->addEventListener( Event::OnTabClosed, [&]( const Event* event ) { onTabClosed( static_cast( event ) ); @@ -467,6 +544,8 @@ UITab* UICodeEditorSplitter::isDocumentOpen( const std::string& path, return nullptr; for ( size_t i = 0; i < tabWidget->getTabCount(); i++ ) { UITab* tab = tabWidget->getTab( i ); + if ( !tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) + continue; UICodeEditor* editor = tab->getOwnedWidget()->asType(); if ( editor->getDocument().getFilePath() == path ) { @@ -477,6 +556,8 @@ UITab* UICodeEditorSplitter::isDocumentOpen( const std::string& path, for ( auto tabWidget : mTabWidgets ) { for ( size_t i = 0; i < tabWidget->getTabCount(); i++ ) { UITab* tab = tabWidget->getTab( i ); + if ( !tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) + continue; UICodeEditor* editor = tab->getOwnedWidget()->asType(); if ( editor->getDocument().getFilePath() == path ) { @@ -496,8 +577,10 @@ void UICodeEditorSplitter::applyColorScheme( const SyntaxColorScheme& colorSchem void UICodeEditorSplitter::forEachEditor( std::function run ) const { for ( auto tabWidget : mTabWidgets ) - for ( size_t i = 0; i < tabWidget->getTabCount(); i++ ) - run( tabWidget->getTab( i )->getOwnedWidget()->asType() ); + for ( size_t i = 0; i < tabWidget->getTabCount(); i++ ) { + if ( tabWidget->getTab( i )->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) + run( tabWidget->getTab( i )->getOwnedWidget()->asType() ); + } } void UICodeEditorSplitter::forEachDoc( std::function run ) const { @@ -516,7 +599,19 @@ void UICodeEditorSplitter::forEachEditorStoppable( std::function run ) const { for ( auto tabWidget : mTabWidgets ) { for ( size_t i = 0; i < tabWidget->getTabCount(); i++ ) { - if ( run( tabWidget->getTab( i )->getOwnedWidget()->asType() ) ) { + if ( tabWidget->getTab( i )->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) && + run( tabWidget->getTab( i )->getOwnedWidget()->asType() ) ) { + return; + } + } + } +} + +void UICodeEditorSplitter::forEachWidgetStoppable( std::function run ) const { + for ( auto tabWidget : mTabWidgets ) { + for ( size_t i = 0; i < tabWidget->getTabCount(); i++ ) { + if ( tabWidget->getTab( i )->getOwnedWidget()->isWidget() && + run( tabWidget->getTab( i )->getOwnedWidget()->asType() ) ) { return; } } @@ -526,7 +621,8 @@ void UICodeEditorSplitter::forEachEditorStoppable( void UICodeEditorSplitter::forEachDocStoppable( std::function run ) const { for ( auto tabWidget : mTabWidgets ) { for ( size_t i = 0; i < tabWidget->getTabCount(); i++ ) { - if ( run( *tabWidget->getTab( i ) + if ( tabWidget->getTab( i )->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) && + run( *tabWidget->getTab( i ) ->getOwnedWidget() ->asType() ->getDocumentRef() @@ -568,6 +664,10 @@ Node* UICodeEditorSplitter::getBaseLayout() const { return mBaseLayout; } +UIWidget* UICodeEditorSplitter::getCurWidget() const { + return mCurWidget; +} + std::vector UICodeEditorSplitter::getAllEditors() { std::vector editors; forEachEditor( [&]( UICodeEditor* editor ) { editors.push_back( editor ); } ); @@ -598,50 +698,65 @@ void UICodeEditorSplitter::zoomReset() { forEachEditor( []( UICodeEditor* editor ) { editor->fontSizeReset(); } ); } -bool UICodeEditorSplitter::tryTabClose( UICodeEditor* editor ) { - if ( nullptr != editor && editor->isDirty() ) { - UIMessageBox* msgBox = - UIMessageBox::New( UIMessageBox::OK_CANCEL, - "Do you really want to close this tab?\nAll changes will be lost." ); - msgBox->addEventListener( Event::MsgBoxConfirmClick, - [&, editor]( const Event* ) { closeEditorTab( editor ); } ); - msgBox->addEventListener( Event::OnClose, [&]( const Event* ) { - msgBox = nullptr; - if ( mCurEditor ) - mCurEditor->setFocus(); - } ); - msgBox->setTitle( "Close Tab?" ); - msgBox->center(); - msgBox->show(); +bool UICodeEditorSplitter::tryTabClose( UIWidget* widget ) { + if ( !widget ) return false; + + if ( widget->isType( UI_TYPE_CODEEDITOR ) ) { + UICodeEditor* editor = widget->asType(); + if ( nullptr != editor && editor->isDirty() ) { + UIMessageBox* msgBox = UIMessageBox::New( + UIMessageBox::OK_CANCEL, + "Do you really want to close this tab?\nAll changes will be lost." ); + msgBox->addEventListener( Event::MsgBoxConfirmClick, + [&, editor]( const Event* ) { closeTab( editor ); } ); + msgBox->addEventListener( Event::OnClose, [&]( const Event* ) { + msgBox = nullptr; + if ( mCurEditor ) + mCurEditor->setFocus(); + } ); + msgBox->setTitle( "Close Tab?" ); + msgBox->center(); + msgBox->show(); + return false; + } else { + closeTab( editor ); + return true; + } } else { - closeEditorTab( editor ); + closeTab( widget ); return true; } } -void UICodeEditorSplitter::closeEditorTab( UICodeEditor* editor ) { - if ( editor ) { - UITabWidget* tabWidget = tabWidgetFromEditor( editor ); - if ( tabWidget ) { - if ( !( editor->getDocument().isEmpty() && - !tabWidget->getParent()->isType( UI_TYPE_SPLITTER ) && - tabWidget->getTabCount() == 1 ) ) { - tabWidget->removeTab( (UITab*)editor->getData() ); +void UICodeEditorSplitter::closeTab( UIWidget* widget ) { + if ( widget ) { + if ( widget->isType( UI_TYPE_CODEEDITOR ) ) { + UICodeEditor* editor = widget->asType(); + UITabWidget* tabWidget = tabWidgetFromEditor( editor ); + if ( tabWidget ) { + if ( !( editor->getDocument().isEmpty() && + !tabWidget->getParent()->isType( UI_TYPE_SPLITTER ) && + tabWidget->getTabCount() == 1 ) ) { + tabWidget->removeTab( (UITab*)editor->getData() ); + } } + } else { + UITabWidget* tabWidget = tabWidgetFromWidget( widget ); + tabWidget->removeTab( (UITab*)widget->getData() ); } } } -UISplitter* UICodeEditorSplitter::splitEditor( const SplitDirection& direction, - UICodeEditor* editor, bool openCurEditor ) { - if ( !editor ) +UISplitter* UICodeEditorSplitter::split( const SplitDirection& direction, UIWidget* widget, + bool openCurEditor ) { + if ( !widget ) return nullptr; UIOrientation orientation = direction == SplitDirection::Left || direction == SplitDirection::Right ? UIOrientation::Horizontal : UIOrientation::Vertical; - UITabWidget* tabWidget = tabWidgetFromEditor( editor ); + UITabWidget* tabWidget = tabWidgetFromWidget( widget ); if ( !tabWidget ) return nullptr; Node* parent = tabWidget->getParent(); @@ -750,15 +865,14 @@ void UICodeEditorSplitter::switchNextSplit( UICodeEditor* editor ) { void UICodeEditorSplitter::focusSomeEditor( Node* searchFrom ) { UICodeEditor* editor = - searchFrom ? searchFrom->findByType( UI_TYPE_CODEEDITOR ) - : mUISceneNode->getRoot()->findByType( UI_TYPE_CODEEDITOR ); + searchFrom && searchFrom->isType( UI_TYPE_CODEEDITOR ) + ? searchFrom->findByType( UI_TYPE_CODEEDITOR ) + : ( mBaseLayout ? mBaseLayout->findByType( UI_TYPE_CODEEDITOR ) + : nullptr ); - if ( searchFrom && !editor ) - editor = mUISceneNode->getRoot()->findByType( UI_TYPE_CODEEDITOR ); - - if ( editor && tabWidgetFromEditor( editor ) && !tabWidgetFromEditor( editor )->isClosing() && - tabWidgetFromEditor( editor )->getTabCount() > 1 ) { - UITabWidget* tabW = tabWidgetFromEditor( editor ); + UITabWidget* tabW = nullptr; + if ( editor && ( tabW = tabWidgetFromEditor( editor ) ) && !tabW->isClosing() && + tabW->getTabCount() > 1 ) { if ( tabW && tabW->getTabSelected()->getOwnedWidget() != editor ) { tabW->setTabSelected( tabW->getTabSelected() ); return; @@ -782,10 +896,6 @@ void UICodeEditorSplitter::focusSomeEditor( Node* searchFrom ) { return; } } - - UITabWidget* tabW = mUISceneNode->getRoot()->findByType( UI_TYPE_TABWIDGET ); - if ( tabW && tabW->getTabCount() > 0 ) - tabW->setTabSelected( tabW->getTabSelected() ); } void UICodeEditorSplitter::closeTabWidgets( UISplitter* splitter ) { @@ -828,6 +938,18 @@ bool UICodeEditorSplitter::curEditorExists() const { return found || mCurEditor == nullptr || mAboutToAddEditor == mCurEditor || mFirstCodeEditor; } +bool UICodeEditorSplitter::curWidgetExists() const { + bool found = false; + forEachWidgetStoppable( [&]( UIWidget* widget ) { + if ( widget == mCurWidget ) { + found = true; + return true; + } + return false; + } ); + return found || mCurWidget == nullptr; +} + UICodeEditor* UICodeEditorSplitter::getCurEditor() const { eeASSERT( curEditorExists() ); return mCurEditor; @@ -855,10 +977,10 @@ void UICodeEditorSplitter::closeSplitter( UISplitter* splitter ) { } void UICodeEditorSplitter::onTabClosed( const TabEvent* tabEvent ) { - UICodeEditor* editor = tabEvent->getTab()->getOwnedWidget()->asType(); + UIWidget* widget = tabEvent->getTab()->getOwnedWidget()->asType(); UITabWidget* tabWidget = tabEvent->getTab()->getTabWidget(); if ( tabWidget->getTabCount() == 0 ) { - UISplitter* splitter = splitterFromEditor( editor ); + UISplitter* splitter = splitterFromWidget( widget ); if ( splitter ) { if ( splitter->isFull() ) { tabWidget->close(); @@ -903,6 +1025,7 @@ void UICodeEditorSplitter::onTabClosed( const TabEvent* tabEvent ) { } } mCurEditor = nullptr; + mCurWidget = nullptr; auto d = createCodeEditorInTabWidget( tabWidget ); d.first->getTabWidget()->setTabSelected( d.first ); } else { @@ -912,7 +1035,7 @@ void UICodeEditorSplitter::onTabClosed( const TabEvent* tabEvent ) { } if ( tabEvent->getTab()->getOwnedWidget() == mCurEditor ) focusSomeEditor( nullptr ); - eeASSERT( curEditorExists() ); + eeASSERT( curWidgetExists() ); } }}} // namespace EE::UI::Tools diff --git a/src/eepp/ui/uitab.cpp b/src/eepp/ui/uitab.cpp index 1447d07c3..6e6e9c8d9 100644 --- a/src/eepp/ui/uitab.cpp +++ b/src/eepp/ui/uitab.cpp @@ -216,6 +216,8 @@ UIPushButton* UITab::setText( const String& text ) { tTabW->orderTabs(); } + + sendTextEvent( Event::OnTitleChange, mText.toUtf8() ); } return this; } diff --git a/src/eepp/ui/uitabwidget.cpp b/src/eepp/ui/uitabwidget.cpp index bd7f4a127..fb71fae9c 100644 --- a/src/eepp/ui/uitabwidget.cpp +++ b/src/eepp/ui/uitabwidget.cpp @@ -466,6 +466,18 @@ UITabWidget* UITabWidget::add( UITab* tab ) { return this; } +UITab* UITabWidget::getTabFromOwnedWidget( const UIWidget* widget ) { + for ( Uint32 i = 0; i < mTabs.size(); i++ ) { + if ( mTabs[i]->isType( UI_TYPE_TAB ) ) { + UITab* tTab = mTabs[i]->asType(); + + if ( tTab->getOwnedWidget() == widget ) + return tTab; + } + } + return nullptr; +} + UITab* UITabWidget::getTab( const Uint32& index ) { eeASSERT( index < mTabs.size() ); return mTabs[index]; diff --git a/src/eepp/ui/uiwidget.cpp b/src/eepp/ui/uiwidget.cpp index 4dad774b3..bf22065ad 100644 --- a/src/eepp/ui/uiwidget.cpp +++ b/src/eepp/ui/uiwidget.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,10 @@ UIWidget::UIWidget( const std::string& tag ) : UIWidget::UIWidget() : UIWidget( "widget" ) {} UIWidget::~UIWidget() { + if ( mUISceneNode && mUISceneNode->getUIEventDispatcher() && + mUISceneNode->getUIEventDispatcher()->getNodeDragging() == this ) + mUISceneNode->getUIEventDispatcher()->setNodeDragging( nullptr ); + if ( !SceneManager::instance()->isShootingDown() && NULL != mUISceneNode ) mUISceneNode->onWidgetDelete( this ); eeSAFE_DELETE( mStyle ); diff --git a/src/eepp/ui/uiwindow.cpp b/src/eepp/ui/uiwindow.cpp index 6d54e7f23..7de2b793d 100644 --- a/src/eepp/ui/uiwindow.cpp +++ b/src/eepp/ui/uiwindow.cpp @@ -1162,6 +1162,9 @@ const Uint8& UIWindow::getBaseAlpha() const { } void UIWindow::setTitle( const String& text ) { + if ( mTitle && text == mTitle->getText() ) + return; + if ( NULL == mTitle ) { mTitle = UITextView::NewWithTag( "window::title" ); mTitle->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); @@ -1177,6 +1180,7 @@ void UIWindow::setTitle( const String& text ) { fixTitleSize(); mTitle->setText( text ); + sendTextEvent( Event::OnTitleChange, text.toUtf8() ); } void UIWindow::fixTitleSize() { diff --git a/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp b/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp index f8080d315..5fc73c8e2 100644 --- a/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp +++ b/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp @@ -178,7 +178,7 @@ class TerminalDisplay : public ITerminalDisplay { virtual void onMouseDoubleClick( const Vector2i& pos, const Uint32& flags ); - virtual void onMouseMotion( const Vector2i& pos, const Uint32& flags ); + virtual void onMouseMove( const Vector2i& pos, const Uint32& flags ); virtual void onMouseDown( const Vector2i& pos, const Uint32& flags ); diff --git a/src/modules/eterm/include/eterm/ui/uiterminal.hpp b/src/modules/eterm/include/eterm/ui/uiterminal.hpp new file mode 100644 index 000000000..21d588b63 --- /dev/null +++ b/src/modules/eterm/include/eterm/ui/uiterminal.hpp @@ -0,0 +1,72 @@ +#ifndef ETERM_UI_UITERMINAL_HPP +#define ETERM_UI_UITERMINAL_HPP + +#include +#include + +using namespace EE::UI; +using namespace eterm::Terminal; + +namespace eterm { namespace UI { + +class EE_API UITerminal : public UIWidget { + public: + static UITerminal* New( Font* font, const Float& fontSize, const Sizef& pixelsSize, + std::string program = "", const std::vector& args = {}, + const std::string& workingDir = "", const size_t& historySize = 10000, + IProcessFactory* processFactory = nullptr, + const bool& useFrameBuffer = false ); + + static UITerminal* New( const std::shared_ptr& terminalDisplay ); + + virtual ~UITerminal(); + + virtual Uint32 getType() const; + + virtual bool isType( const Uint32& type ) const; + + virtual void draw(); + + const std::shared_ptr& getTerm() const; + + virtual void scheduledUpdate( const Time& time ); + + const std::string& getTitle() const; + + void setTitle( const std::string& title ); + + protected: + std::string mTitle; + bool mIsCustomTitle{ false }; + bool mDraggingSel{ false }; + + UITerminal( const std::shared_ptr& terminalDisplay ); + + std::shared_ptr mTerm; + + virtual Uint32 onTextInput( const TextInputEvent& event ); + + virtual Uint32 onKeyDown( const KeyEvent& event ); + + virtual Uint32 onKeyUp( const KeyEvent& event ); + + virtual Uint32 onMouseMove( const Vector2i& position, const Uint32& flags ); + + virtual Uint32 onMouseDown( const Vector2i& position, const Uint32& flags ); + + virtual Uint32 onMouseDoubleClick( const Vector2i& position, const Uint32& flags ); + + virtual Uint32 onMouseUp( const Vector2i& position, const Uint32& flags ); + + virtual void onPositionChange(); + + virtual void onSizeChange(); + + virtual Uint32 onFocus(); + + virtual Uint32 onFocusLoss(); +}; + +}} // namespace eterm::UI + +#endif diff --git a/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp b/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp index 104d19ad4..20824e7b8 100644 --- a/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp +++ b/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp @@ -695,7 +695,7 @@ void TerminalDisplay::onMouseDoubleClick( const Vector2i& pos, const Uint32& fla } } -void TerminalDisplay::onMouseMotion( const Vector2i& pos, const Uint32& flags ) { +void TerminalDisplay::onMouseMove( const Vector2i& pos, const Uint32& flags ) { if ( ( flags & EE_BUTTON_LMASK ) && ( mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_EMPTY || mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_READY ) ) { @@ -712,6 +712,28 @@ void TerminalDisplay::onMouseMotion( const Vector2i& pos, const Uint32& flags ) void TerminalDisplay::onMouseDown( const Vector2i& pos, const Uint32& flags ) { auto gridPos{ positionToGrid( pos ) }; + if ( ( flags & EE_BUTTON_LMASK ) && + mLastDoubleClick.getElapsedTime() < Milliseconds( 300.f ) ) { + mTerminal->selstart( gridPos.x, gridPos.y, SNAP_LINE ); + } else if ( flags & EE_BUTTON_LMASK ) { + if ( mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_IDLE ) { + mTerminal->selstart( gridPos.x, gridPos.y, 0 ); + invalidateLines(); + } else if ( mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_READY ) { + mTerminal->selclear(); + } + } else if ( flags & EE_BUTTON_MMASK ) { + auto selection = mTerminal->getSelection(); + if ( !selection.empty() ) { + for ( auto& chr : selection ) + onTextInput( chr ); + } + } + mTerminal->mousereport( TerminalMouseEventType::MouseButtonDown, positionToGrid( pos ), flags, + mWindow->getInput()->getModState() ); +} + +void TerminalDisplay::onMouseUp( const Vector2i& pos, const Uint32& flags ) { Uint32 smod = sanitizeMod( mWindow->getInput()->getModState() ); auto scIt = terminalKeyMap.MouseShortcuts().find( flags ); @@ -738,29 +760,6 @@ void TerminalDisplay::onMouseDown( const Vector2i& pos, const Uint32& flags ) { } } - if ( ( flags & EE_BUTTON_LMASK ) && - mLastDoubleClick.getElapsedTime() < Milliseconds( 300.f ) ) { - mTerminal->selstart( gridPos.x, gridPos.y, SNAP_LINE ); - } else if ( flags & EE_BUTTON_LMASK ) { - if ( mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_IDLE ) { - auto gridPos{ positionToGrid( pos ) }; - mTerminal->selstart( gridPos.x, gridPos.y, 0 ); - invalidateLines(); - } else if ( mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_READY ) { - mTerminal->selclear(); - } - } else if ( flags & EE_BUTTON_MMASK ) { - auto selection = mTerminal->getSelection(); - if ( !selection.empty() ) { - for ( auto& chr : selection ) - onTextInput( chr ); - } - } - mTerminal->mousereport( TerminalMouseEventType::MouseButtonDown, positionToGrid( pos ), flags, - mWindow->getInput()->getModState() ); -} - -void TerminalDisplay::onMouseUp( const Vector2i& pos, const Uint32& flags ) { if ( ( flags & EE_BUTTON_LMASK ) ) { auto selection = mTerminal->getSelection(); if ( !selection.empty() && selection != "\n" ) @@ -1089,7 +1088,8 @@ void TerminalDisplay::drawGrid( const Vector2f& pos ) { y += lineHeight; } - if ( !mEmulator->isScrolling() && !IS_SET( MODE_HIDE ) && mDirtyCursor ) { + if ( !mEmulator->isScrolling() && !IS_SET( MODE_HIDE ) && + ( !mUseFrameBuffer || mDirtyCursor ) ) { mDirtyCursor = false; Color drawcol; if ( IS_SET( MODE_REVERSE ) ) { @@ -1172,7 +1172,7 @@ void TerminalDisplay::draw( const Vector2f& pos ) { Primitives p; p.setForceDraw( false ); p.setColor( defaultBg ); - p.drawRectangle( Rectf( mPosition.asFloat(), mSize.asFloat() ) ); + p.drawRectangle( Rectf( mPosition.floor().asFloat(), mSize.asFloat() ) ); if ( !mFrameBuffer || mDirty ) drawGrid( pos ); diff --git a/src/modules/eterm/src/eterm/ui/uiterminal.cpp b/src/modules/eterm/src/eterm/ui/uiterminal.cpp new file mode 100644 index 000000000..e5ffe94ac --- /dev/null +++ b/src/modules/eterm/src/eterm/ui/uiterminal.cpp @@ -0,0 +1,140 @@ +#include +#include +#include +#include + +using namespace EE::Scene; + +namespace eterm { namespace UI { + +UITerminal* UITerminal::New( Font* font, const Float& fontSize, const Sizef& pixelsSize, + std::string program, const std::vector& args, + const std::string& workingDir, const size_t& historySize, + IProcessFactory* processFactory, const bool& useFrameBuffer ) { + auto win = SceneManager::instance()->getUISceneNode()->getWindow(); + auto terminal = + TerminalDisplay::create( win, font, fontSize, pixelsSize, program, args, workingDir, + historySize, processFactory, useFrameBuffer ); + return UITerminal::New( terminal ); +} + +UITerminal* UITerminal::New( const std::shared_ptr& terminalDisplay ) { + return eeNew( UITerminal, ( terminalDisplay ) ); +} + +UITerminal::~UITerminal() {} + +Uint32 UITerminal::getType() const { + return UI_TYPE_TERMINAL; +} + +bool UITerminal::isType( const Uint32& type ) const { + return getType() == type || UIWidget::isType( type ); +} + +void UITerminal::draw() { + mTerm->setPosition( mScreenPosi.asFloat() ); + mTerm->draw(); +} + +UITerminal::UITerminal( const std::shared_ptr& terminalDisplay ) : + UIWidget( "terminal" ), mTerm( terminalDisplay ) { + mFlags |= UI_TAB_STOP; + mTerm->pushEventCallback( [&]( const TerminalDisplay::Event& event ) { + if ( event.type == TerminalDisplay::EventType::TITLE && getParent() ) { + if ( !mIsCustomTitle && mTitle != event.eventData ) { + mTitle = event.eventData; + sendTextEvent( Event::OnTitleChange, mTitle ); + } + } + } ); + subscribeScheduledUpdate(); +} + +const std::shared_ptr& UITerminal::getTerm() const { + return mTerm; +} + +void UITerminal::scheduledUpdate( const Time& ) { + mTerm->update(); + if ( mTerm->isDirty() ) + invalidateDraw(); +} + +const std::string& UITerminal::getTitle() const { + return mTitle; +} + +void UITerminal::setTitle( const std::string& title ) { + if ( title != mTitle ) { + mTitle = title; + mIsCustomTitle = true; + sendTextEvent( Event::OnTitleChange, title ); + } +} + +Uint32 UITerminal::onTextInput( const TextInputEvent& event ) { + mTerm->onTextInput( event.getChar() ); + return 1; +} + +Uint32 UITerminal::onKeyDown( const KeyEvent& event ) { + mTerm->onKeyDown( event.getKeyCode(), event.getChar(), event.getMod(), event.getScancode() ); + return 1; +} + +Uint32 UITerminal::onKeyUp( const KeyEvent& ) { + return 1; +} + +Uint32 UITerminal::onMouseMove( const Vector2i& position, const Uint32& flags ) { + mTerm->onMouseMove( position, flags ); + return 1; +} + +Uint32 UITerminal::onMouseDown( const Vector2i& position, const Uint32& flags ) { + if ( ( flags & EE_BUTTON_LMASK ) && + mTerm->getTerminal()->getSelectionMode() == TerminalSelectionMode::SEL_IDLE ) { + mDraggingSel = true; + } else if ( ( flags & EE_BUTTON_LMASK ) && mDraggingSel ) { + return 1; + } + mTerm->onMouseDown( position, flags ); + return 1; +} + +Uint32 UITerminal::onMouseDoubleClick( const Vector2i& position, const Uint32& flags ) { + mTerm->onMouseDoubleClick( position, flags ); + return 1; +} + +Uint32 UITerminal::onMouseUp( const Vector2i& position, const Uint32& flags ) { + if ( ( flags & EE_BUTTON_LMASK ) && mDraggingSel ) + mDraggingSel = false; + mTerm->onMouseUp( position, flags ); + return 1; +} + +void UITerminal::onPositionChange() { + mTerm->setPosition( mScreenPosi.asFloat() ); + UIWidget::onPositionChange(); +} + +void UITerminal::onSizeChange() { + mTerm->setSize( getPixelsSize() ); + UIWidget::onSizeChange(); +} + +Uint32 UITerminal::onFocus() { + mTerm->setFocus( true ); + invalidateDraw(); + return UIWidget::onFocus(); +} + +Uint32 UITerminal::onFocusLoss() { + mTerm->setFocus( false ); + invalidateDraw(); + return UIWidget::onFocusLoss(); +} + +}} // namespace eterm::UI diff --git a/src/tools/ecode/appconfig.cpp b/src/tools/ecode/appconfig.cpp index 59e085942..a026c3275 100644 --- a/src/tools/ecode/appconfig.cpp +++ b/src/tools/ecode/appconfig.cpp @@ -229,7 +229,7 @@ json saveNode( Node* node ) { res["type"] = "splitter"; res["split"] = splitter->getSplitPartition().toString(); res["orientation"] = - splitter->getOrientation() == UIOrientation::Horizontal ? "horizontal" : "vertial"; + splitter->getOrientation() == UIOrientation::Horizontal ? "horizontal" : "vertical"; res["first"] = saveNode( splitter->getFirstWidget() ); res["last"] = saveNode( splitter->getLastWidget() ); } else if ( node->isType( UI_TYPE_TABWIDGET ) ) { @@ -309,7 +309,7 @@ static void loadDocuments( UICodeEditorSplitter* editorSplitter, std::shared_ptr curTabWidget ); } } else if ( j["type"] == "splitter" ) { - UISplitter* splitter = editorSplitter->splitEditor( + UISplitter* splitter = editorSplitter->split( j["orientation"] == "horizontal" ? UICodeEditorSplitter::SplitDirection::Right : UICodeEditorSplitter::SplitDirection::Bottom, curTabWidget->getTabSelected()->getOwnedWidget()->asType(), false ); @@ -392,4 +392,4 @@ void AppConfig::loadProject( std::string projectFolder, UICodeEditorSplitter* ed } } -} +} // namespace ecode diff --git a/src/tools/ecode/docsearchcontroller.cpp b/src/tools/ecode/docsearchcontroller.cpp index 3bac6e6ec..28ec5a9c8 100644 --- a/src/tools/ecode/docsearchcontroller.cpp +++ b/src/tools/ecode/docsearchcontroller.cpp @@ -28,7 +28,7 @@ void DocSearchController::initSearchBar( auto& kbind = mSearchBarLayout->getKeyBindings(); kbind.addKeybindsString( { { mApp->getKeybind( "repeat-find" ), "repeat-find" }, - { mApp->getKeybind( "find-prev" ), "find-prev" }, + { mApp->getKeybind( "find-prev" ), "find-prev" } } ); kbind.addKeybindsStringUnordered( keybindings ); @@ -104,8 +104,8 @@ void DocSearchController::initSearchBar( } ); mSearchBarLayout->addCommand( "close-searchbar", [&] { hideSearchBar(); - if ( mEditorSplitter->getCurEditor() ) - mEditorSplitter->getCurEditor()->setFocus(); + if ( mEditorSplitter->getCurWidget() ) + mEditorSplitter->getCurWidget()->setFocus(); if ( mSearchState.editor ) { if ( mEditorSplitter->editorExists( mSearchState.editor ) ) { mSearchState.editor->setHighlightWord( "" ); diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index bdba08b86..5061228d1 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -17,8 +17,7 @@ void appLoop() { } bool App::onCloseRequestCallback( EE::Window::Window* ) { - if ( nullptr != mEditorSplitter->getCurEditor() && - mEditorSplitter->getCurEditor()->isDirty() ) { + if ( mEditorSplitter->isAnyEditorDirty() ) { UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::OK_CANCEL, i18n( "confirm_ecode_exit", @@ -152,9 +151,9 @@ void App::openFileDialog() { loadFileFromPath( file ); } ); dialog->addEventListener( Event::OnWindowClose, [&]( const Event* ) { - if ( mEditorSplitter && mEditorSplitter->getCurEditor() && + if ( mEditorSplitter && mEditorSplitter->getCurWidget() && !SceneManager::instance()->isShootingDown() ) - mEditorSplitter->getCurEditor()->setFocus(); + mEditorSplitter->getCurWidget()->setFocus(); } ); dialog->center(); dialog->show(); @@ -174,9 +173,9 @@ void App::openFolderDialog() { loadFolder( path ); } ); dialog->addEventListener( Event::OnWindowClose, [&]( const Event* ) { - if ( mEditorSplitter && mEditorSplitter->getCurEditor() && + if ( mEditorSplitter && mEditorSplitter->getCurWidget() && !SceneManager::instance()->isShootingDown() ) - mEditorSplitter->getCurEditor()->setFocus(); + mEditorSplitter->getCurWidget()->setFocus(); } ); dialog->center(); dialog->show(); @@ -198,9 +197,9 @@ void App::openFontDialog( std::string& fontPath, bool loadingMonoFont ) { dialog->setTitle( "Select Font File" ); dialog->setCloseShortcut( KEY_ESCAPE ); dialog->addEventListener( Event::OnWindowClose, [&]( const Event* ) { - if ( mEditorSplitter && mEditorSplitter->getCurEditor() && + if ( mEditorSplitter && mEditorSplitter->getCurWidget() && !SceneManager::instance()->isShootingDown() ) - mEditorSplitter->getCurEditor()->setFocus(); + mEditorSplitter->getCurWidget()->setFocus(); } ); dialog->addEventListener( Event::OpenFile, [&, loadingMonoFont]( const Event* event ) { auto newPath = event->getNode()->asType()->getFullPath(); @@ -658,8 +657,8 @@ UIMenu* App::createWindowMenu() { "New UI scale factor assigned.\nPlease restart the application." ); msg->show(); setFocusEditorOnClose( msg ); - } else if ( mEditorSplitter && mEditorSplitter->getCurEditor() ) { - mEditorSplitter->getCurEditor()->setFocus(); + } else if ( mEditorSplitter && mEditorSplitter->getCurWidget() ) { + mEditorSplitter->getCurWidget()->setFocus(); } } else { UIMessageBox* msg = UIMessageBox::New( UIMessageBox::OK, "Invalid value!" ); @@ -865,8 +864,8 @@ UIMenu* App::createViewMenu() { void App::setFocusEditorOnClose( UIMessageBox* msgBox ) { msgBox->addEventListener( Event::OnClose, [&]( const Event* ) { - if ( mEditorSplitter && mEditorSplitter->getCurEditor() ) - mEditorSplitter->getCurEditor()->setFocus(); + if ( mEditorSplitter && mEditorSplitter->getCurWidget() ) + mEditorSplitter->getCurWidget()->setFocus(); } ); } @@ -1482,8 +1481,13 @@ void App::updateProjectSettingsMenu() { } void App::updateDocumentMenu() { - if ( !mEditorSplitter->getCurEditor() ) + if ( !mEditorSplitter->getCurWidget() || + !mEditorSplitter->getCurWidget()->isType( UI_TYPE_CODEEDITOR ) ) { + mSettingsMenu->getItemId( "doc-menu" )->setEnabled( false ); return; + } + + mSettingsMenu->getItemId( "doc-menu" )->setEnabled( true ); const TextDocument& doc = mEditorSplitter->getCurEditor()->getDocument(); @@ -1534,10 +1538,10 @@ void App::updateDocumentMenu() { ->setActive( mEditorSplitter->getCurEditor()->isLocked() ); } -static void -updateKeybindings( IniFile& ini, const std::string& group, Input* input, - std::unordered_map& keybindings, - const std::unordered_map& defKeybindings ) { +static void updateKeybindings( IniFile& ini, const std::string& group, Input* input, + std::unordered_map& keybindings, + const std::unordered_map& defKeybindings, + bool forceRebind = false ) { KeyBindings bindings( input ); bool added = false; @@ -1552,7 +1556,7 @@ updateKeybindings( IniFile& ini, const std::string& group, Input* input, for ( const auto& key : keybindings ) invertedKeybindings[key.second] = key.first; - if ( defKeybindings.size() != keybindings.size() ) { + if ( defKeybindings.size() != keybindings.size() || forceRebind ) { for ( const auto& key : defKeybindings ) { auto foundCmd = invertedKeybindings.find( key.second ); auto& shortcutStr = key.first; @@ -1562,6 +1566,22 @@ updateKeybindings( IniFile& ini, const std::string& group, Input* input, invertedKeybindings[key.second] = shortcutStr; ini.setValue( group, shortcutStr, key.second ); added = true; + } else if ( foundCmd == invertedKeybindings.end() ) { + // Override the shortcut if the command that holds that + // shortcut does not exists anymore + auto kb = keybindings.find( shortcutStr ); + if ( kb != keybindings.end() ) { + bool found = false; + for ( const auto& val : defKeybindings ) + if ( val.second == kb->second ) + found = true; + if ( !found ) { + keybindings[shortcutStr] = key.second; + invertedKeybindings[key.second] = shortcutStr; + ini.setValue( group, shortcutStr, key.second ); + added = true; + } + } } } } @@ -1569,11 +1589,11 @@ updateKeybindings( IniFile& ini, const std::string& group, Input* input, ini.writeFile(); } -static void -updateKeybindings( IniFile& ini, const std::string& group, Input* input, - std::unordered_map& keybindings, - std::unordered_map& invertedKeybindings, - const std::map& defKeybindings ) { +static void updateKeybindings( IniFile& ini, const std::string& group, Input* input, + std::unordered_map& keybindings, + std::unordered_map& invertedKeybindings, + const std::map& defKeybindings, + bool forceRebind = false ) { KeyBindings bindings( input ); bool added = false; @@ -1587,7 +1607,7 @@ updateKeybindings( IniFile& ini, const std::string& group, Input* input, for ( const auto& key : keybindings ) invertedKeybindings[key.second] = key.first; - if ( defKeybindings.size() != keybindings.size() ) { + if ( defKeybindings.size() != keybindings.size() || forceRebind ) { for ( const auto& key : defKeybindings ) { auto foundCmd = invertedKeybindings.find( key.second ); auto shortcutStr = bindings.getShortcutString( key.first ); @@ -1597,6 +1617,22 @@ updateKeybindings( IniFile& ini, const std::string& group, Input* input, invertedKeybindings[key.second] = shortcutStr; ini.setValue( group, shortcutStr, key.second ); added = true; + } else if ( foundCmd == invertedKeybindings.end() ) { + // Override the shortcut if the command that holds that + // shortcut does not exists anymore + auto kb = keybindings.find( shortcutStr ); + if ( kb != keybindings.end() ) { + bool found = false; + for ( const auto& val : defKeybindings ) + if ( val.second == kb->second ) + found = true; + if ( !found ) { + keybindings[shortcutStr] = key.second; + invertedKeybindings[key.second] = shortcutStr; + ini.setValue( group, shortcutStr, key.second ); + added = true; + } + } } } } @@ -1616,18 +1652,26 @@ void App::loadKeybindings() { ini.writeFile(); } + bool forceRebind = false; + auto version = ini.getValueU( "version", "version", 0 ); + if ( version != ecode::Version::getVersionNum() ) { + ini.setValueU( "version", "version", ecode::Version::getVersionNum() ); + ini.writeFile(); + forceRebind = true; + } + Uint32 defModKeyCode = KeyMod::getKeyMod( defMod ); - if ( 0 != defModKeyCode ) + if ( KEYMOD_NONE != defModKeyCode ) KeyMod::setDefaultModifier( defModKeyCode ); updateKeybindings( ini, "editor", mWindow->getInput(), mKeybindings, mKeybindingsInvert, - getDefaultKeybindings() ); + getDefaultKeybindings(), forceRebind ); updateKeybindings( ini, "global_search", mWindow->getInput(), mGlobalSearchKeybindings, - GlobalSearchController::getDefaultKeybindings() ); + GlobalSearchController::getDefaultKeybindings(), forceRebind ); updateKeybindings( ini, "document_search", mWindow->getInput(), mDocumentSearchKeybindings, - DocSearchController::getDefaultKeybindings() ); + DocSearchController::getDefaultKeybindings(), forceRebind ); } } @@ -1644,7 +1688,9 @@ void App::onDocumentCursorPosChange( UICodeEditor*, TextDocument& doc ) { } void App::updateDocInfo( TextDocument& doc ) { - if ( mConfig.editor.showDocInfo && mDocInfoText && mEditorSplitter->getCurEditor() ) { + if ( mConfig.editor.showDocInfo && mDocInfoText && mEditorSplitter->getCurEditor() && + mEditorSplitter->getCurEditor()->isType( UI_TYPE_CODEEDITOR ) ) { + mDocInfoText->setVisible( true ); mDocInfoText->setText( String::format( "line: %lld / %lu col: %lld %s", doc.getSelection().start().line() + 1, doc.linesCount(), mEditorSplitter->getCurEditor()->getCurrentColumnCount(), @@ -1665,9 +1711,19 @@ void App::syncProjectTreeWithEditor( UICodeEditor* editor ) { } } +void App::onWidgetFocusChange( UIWidget* widget ) { + if ( mConfig.editor.showDocInfo && mDocInfoText ) { + mDocInfoText->setVisible( widget && widget->isType( UI_TYPE_CODEEDITOR ) ); + } + updateDocumentMenu(); + if ( widget && !widget->isType( UI_TYPE_CODEEDITOR ) ) { + if ( widget->isType( UI_TYPE_TERMINAL ) ) + setAppTitle( widget->asType()->getTitle() ); + } +} + void App::onCodeEditorFocusChange( UICodeEditor* editor ) { updateDocInfo( editor->getDocument() ); - updateDocumentMenu(); mDocSearchController->onCodeEditorFocusChange( editor ); syncProjectTreeWithEditor( editor ); } @@ -1907,6 +1963,33 @@ NotificationCenter* App::getNotificationCenter() const { return mNotificationCenter.get(); } +void App::createNewTerminal() { + UIWidget* curWidget = mEditorSplitter->getCurWidget(); + if ( !curWidget ) + return; + UITabWidget* tabWidget = mEditorSplitter->tabWidgetFromWidget( curWidget ); + if ( !tabWidget ) { + if ( !mEditorSplitter->getTabWidgets().empty() ) { + tabWidget = mEditorSplitter->getTabWidgets()[0]; + } else { + return; + } + } + UITerminal* term = UITerminal::New( mFontMono, PixelDensity::dpToPx( 11 ), Sizef( 16, 16 ) ); + mEditorSplitter->createWidgetInTabWidget( tabWidget, term, "Shell", true ); + term->addEventListener( Event::OnTitleChange, [&]( const Event* event ) { + if ( event->getNode() != mEditorSplitter->getCurWidget() ) + return; + setAppTitle( event->getNode()->asType()->getTitle() ); + } ); +} + +std::map> App::getGlobalCommands() { + std::map> cmds; + + return cmds; +} + void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { const CodeEditorConfig& config = mConfig.editor; const DocumentConfig& docc = !mCurrentProject.empty() && !mProjectDocConfig.useGlobalSettings @@ -1947,10 +2030,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { doc.setCommand( "save-as-doc", [&] { saveFileDialog( mEditorSplitter->getCurEditor() ); } ); doc.setCommand( "save-all", [&] { saveAll(); } ); doc.setCommand( "find-replace", [&] { mDocSearchController->showFindView(); } ); - doc.setCommand( "open-global-search", [&] { - mGlobalSearchController->showGlobalSearch( - mGlobalSearchController->isUsingSearchReplaceTree() ); - } ); + doc.setCommand( "open-global-search", [&] { mGlobalSearchController->showGlobalSearch(); } ); doc.setCommand( "open-locatebar", [&] { mFileLocator->showLocateBar(); } ); doc.setCommand( "repeat-find", [&] { mDocSearchController->findNextText( mDocSearchController->getSearchState() ); @@ -2013,18 +2093,20 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { msgBox->setTitle( mWindowTitle ); msgBox->getTextInput()->setHint( "Any https or http URL" ); - msgBox->setCloseShortcut( { KEY_ESCAPE, 0 } ); + msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } ); msgBox->showWhenReady(); msgBox->addEventListener( Event::MsgBoxConfirmClick, [&, msgBox]( const Event* ) { std::string url( msgBox->getTextInput()->getText().toUtf8() ); downloadFileWeb( url ); - if ( mEditorSplitter->getCurEditor() ) - mEditorSplitter->getCurEditor()->setFocus(); + if ( mEditorSplitter->getCurWidget() ) + mEditorSplitter->getCurWidget()->setFocus(); msgBox->closeWindow(); } ); } ); doc.setCommand( "move-panel-left", [&] { panelPosition( PanelPosition::Left ); } ); doc.setCommand( "move-panel-right", [&] { panelPosition( PanelPosition::Right ); } ); + doc.setCommand( "create-new-terminal", [&] { createNewTerminal(); } ); + editor->addUnlockedCommand( "create-new-terminal" ); editor->addEventListener( Event::OnDocumentSave, [&]( const Event* event ) { UICodeEditor* editor = event->getNode()->asType(); @@ -2204,7 +2286,7 @@ UIPopUpMenu* App::createToolsMenu() { if ( txt == "Locate..." ) { mFileLocator->showLocateBar(); } else if ( txt == "Project Find..." ) { - mGlobalSearchController->showGlobalSearch(); + mGlobalSearchController->showGlobalSearch( false ); } else if ( txt == "Go to line..." ) { mFileLocator->goToLine(); } else if ( txt == "Load current document directory as folder" ) { @@ -2231,6 +2313,8 @@ void App::createSettingsMenu() { mSettingsMenu = UIPopUpMenu::New(); mSettingsMenu->setId( "settings_menu" ); mSettingsMenu->add( "New", findIcon( "document-new" ), getKeybind( "create-new" ) ); + mSettingsMenu->add( "New Terminal", findIcon( "terminal" ), + getKeybind( "create-new-terminal" ) ); mSettingsMenu->add( "Open File...", findIcon( "document-open" ), getKeybind( "open-file" ) ); mSettingsMenu->add( "Open Folder...", findIcon( "document-open" ), getKeybind( "open-folder" ) ); @@ -2268,14 +2352,14 @@ void App::createSettingsMenu() { fileTypeMenu->setSubMenu( newMenu ); } } ); - mSettingsMenu->addSubMenu( "Document", nullptr, createDocumentMenu() ); + mSettingsMenu->addSubMenu( "Document", nullptr, createDocumentMenu() )->setId( "doc-menu" ); mSettingsMenu->addSubMenu( "Edit", nullptr, createEditMenu() ); mSettingsMenu->addSubMenu( "View", nullptr, createViewMenu() ); mSettingsMenu->addSubMenu( "Tools", nullptr, createToolsMenu() ); mSettingsMenu->addSubMenu( "Window", nullptr, createWindowMenu() ); mSettingsMenu->addSubMenu( "Help", findIcon( "help" ), createHelpMenu() ); mSettingsMenu->addSeparator(); - mSettingsMenu->add( "Close", findIcon( "document-close" ), getKeybind( "close-doc" ) ); + mSettingsMenu->add( "Close", findIcon( "document-close" ), getKeybind( "close-tab" ) ); mSettingsMenu->add( "Close Folder", findIcon( "document-close" ), getKeybind( "close-folder" ) ); mSettingsMenu->addSeparator(); @@ -2289,6 +2373,8 @@ void App::createSettingsMenu() { const String& name = event->getNode()->asType()->getText(); if ( name == "New" ) { runCommand( "create-new" ); + } else if ( name == "New Terminal" ) { + runCommand( "create-new-terminal" ); } else if ( name == "Open File..." ) { runCommand( "open-file" ); } else if ( name == "Open Folder..." ) { @@ -2302,7 +2388,7 @@ void App::createSettingsMenu() { } else if ( name == "Save All" ) { runCommand( "save-all" ); } else if ( name == "Close" ) { - runCommand( "close-doc" ); + runCommand( "close-tab" ); } else if ( name == "Close Folder" ) { runCommand( "close-folder" ); } else if ( name == "Quit" ) { @@ -2761,8 +2847,8 @@ void App::loadFolder( const std::string& path ) { updateRecentFolders(); updateProjectSettingsMenu(); - if ( mEditorSplitter->getCurEditor() ) - mEditorSplitter->getCurEditor()->setFocus(); + if ( mEditorSplitter->getCurWidget() ) + mEditorSplitter->getCurWidget()->setFocus(); } FontTrueType* App::loadFont( const std::string& name, std::string fontPath, @@ -3157,6 +3243,7 @@ void App::init( std::string file, const Float& pidelDensity, const std::string& { "global-settings", 0xedcf }, { "folder-user", 0xed84 }, { "help", 0xf045 }, + { "terminal", 0xf1f6 }, }; for ( const auto& icon : icons ) iconTheme->add( UIGlyphIcon::New( icon.first, iconFont, icon.second ) ); diff --git a/src/tools/ecode/ecode.hpp b/src/tools/ecode/ecode.hpp index fcd2fe4de..06d7b5759 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -13,6 +13,9 @@ #include "widgetcommandexecuter.hpp" #include #include +#include + +using namespace eterm::UI; namespace ecode { @@ -77,6 +80,10 @@ class App : public UICodeEditorSplitter::Client { NotificationCenter* getNotificationCenter() const; + void createNewTerminal(); + + std::map> getGlobalCommands(); + protected: EE::Window::Window* mWindow{ nullptr }; UISceneNode* mUISceneNode{ nullptr }; @@ -230,6 +237,8 @@ class App : public UICodeEditorSplitter::Client { void onDocumentCursorPosChange( UICodeEditor* editor, TextDocument& ); + void onWidgetFocusChange( UIWidget* widget ); + void onCodeEditorFocusChange( UICodeEditor* editor ); bool setAutoComplete( bool enable ); diff --git a/src/tools/ecode/filelocator.cpp b/src/tools/ecode/filelocator.cpp index b347a7a39..8bbaf14da 100644 --- a/src/tools/ecode/filelocator.cpp +++ b/src/tools/ecode/filelocator.cpp @@ -61,7 +61,8 @@ void FileLocator::initLocateBar( UILocateBar* locateBar, UITextInput* locateInpu String number( mLocateInput->getText().substr( 2 ) ); Int64 val; if ( String::fromString( val, number ) && val - 1 >= 0 ) { - mEditorSplitter->getCurEditor()->goToLine( { val - 1, 0 } ); + if ( mEditorSplitter->getCurEditor() ) + mEditorSplitter->getCurEditor()->goToLine( { val - 1, 0 } ); mLocateTable->setVisible( false ); } } else { @@ -84,7 +85,8 @@ void FileLocator::initLocateBar( UILocateBar* locateBar, UITextInput* locateInpu } ); mLocateBarLayout->addCommand( "close-locatebar", [&] { hideLocateBar(); - mEditorSplitter->getCurEditor()->setFocus(); + if ( mEditorSplitter->getCurWidget() ) + mEditorSplitter->getCurWidget()->setFocus(); } ); mLocateBarLayout->getKeyBindings().addKeybindsString( { { "escape", "close-locatebar" }, diff --git a/src/tools/ecode/globalsearchcontroller.cpp b/src/tools/ecode/globalsearchcontroller.cpp index 8f0eedde6..1884368d2 100644 --- a/src/tools/ecode/globalsearchcontroller.cpp +++ b/src/tools/ecode/globalsearchcontroller.cpp @@ -47,6 +47,10 @@ size_t GlobalSearchController::replaceInFiles( const std::string& replaceText, return count; } +void GlobalSearchController::showGlobalSearch() { + showGlobalSearch( isUsingSearchReplaceTree() ); +} + void GlobalSearchController::initGlobalSearchBar( UIGlobalSearchBar* globalSearchBar, const GlobalSearchBarConfig& globalSearchBarConfig, std::unordered_map keybindings ) { @@ -132,8 +136,8 @@ void GlobalSearchController::initGlobalSearchBar( } ); mGlobalSearchBarLayout->addCommand( "close-global-searchbar", [&] { hideGlobalSearchBar(); - if ( mEditorSplitter->getCurEditor() ) - mEditorSplitter->getCurEditor()->setFocus(); + if ( mEditorSplitter->getCurWidget() ) + mEditorSplitter->getCurWidget()->setFocus(); } ); mGlobalSearchBarLayout->addCommand( "expand-all", [&] { mGlobalSearchTree->expandAll(); diff --git a/src/tools/ecode/globalsearchcontroller.hpp b/src/tools/ecode/globalsearchcontroller.hpp index 1f19e5207..6ad83b321 100644 --- a/src/tools/ecode/globalsearchcontroller.hpp +++ b/src/tools/ecode/globalsearchcontroller.hpp @@ -50,7 +50,9 @@ class GlobalSearchController { size_t replaceInFiles( const std::string& replaceText, std::shared_ptr model ); - void showGlobalSearch( bool searchAndReplace = false ); + void showGlobalSearch(); + + void showGlobalSearch( bool searchAndReplace ); void updateColorScheme( const SyntaxColorScheme& colorScheme ); diff --git a/src/tools/eterm/eterm.cpp b/src/tools/eterm/eterm.cpp index 4c1d4604f..d50144e05 100644 --- a/src/tools/eterm/eterm.cpp +++ b/src/tools/eterm/eterm.cpp @@ -10,7 +10,7 @@ void inputCallback( InputEvent* event ) { switch ( event->Type ) { case InputEvent::MouseMotion: { - terminal->onMouseMotion( win->getInput()->getMousePos(), + terminal->onMouseMove( win->getInput()->getMousePos(), win->getInput()->getPressTrigger() ); break; }