diff --git a/include/eepp/scene/event.hpp b/include/eepp/scene/event.hpp index fe7594a64..56b57ea10 100644 --- a/include/eepp/scene/event.hpp +++ b/include/eepp/scene/event.hpp @@ -79,6 +79,7 @@ class EE_API Event { OnActiveWidgetChange, OnWindowReady, OnCreateContextMenu, + OnDocumentMoved, UserEvent, NoEvent = eeINDEX_NOT_FOUND }; diff --git a/include/eepp/system/filesystem.hpp b/include/eepp/system/filesystem.hpp index 1633a44bb..76f7bda0b 100644 --- a/include/eepp/system/filesystem.hpp +++ b/include/eepp/system/filesystem.hpp @@ -150,6 +150,9 @@ class EE_API FileSystem { const std::string& fileName, const std::string& separator = ".", const std::string& fileExtension = "" ); + + /** @returns True if the path provided is relative. */ + static bool isRelativePath( const std::string& path ); }; }} // namespace EE::System diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index d34b83254..f528740dc 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ class EE_API TextDocument { virtual void onDocumentSaved( TextDocument* ) = 0; virtual void onDocumentClosed( TextDocument* ) = 0; virtual void onDocumentDirtyOnFileSystem( TextDocument* ) = 0; + virtual void onDocumentMoved( TextDocument* ) = 0; }; TextDocument( bool verbose = true ); @@ -67,6 +69,10 @@ class EE_API TextDocument { bool loadFromFile( const std::string& path ); + bool loadAsyncFromFile( const std::string& path, std::shared_ptr pool, + std::function onLoaded = + std::function() ); + bool loadFromMemory( const Uint8* data, const Uint32& size ); bool loadFromPack( Pack* pack, std::string filePackPath ); @@ -404,6 +410,8 @@ class EE_API TextDocument { bool hasSyntaxDefinition() const; + void notifyDocumentMoved( const std::string& newPath ); + protected: friend class UndoStack; UndoStack mUndoStack; @@ -457,6 +465,8 @@ class EE_API TextDocument { void notifyDirtyOnFileSystem(); + void notifyDocumentMoved(); + void insertAtStartOfSelectedLines( const String& text, bool skipEmpty ); void removeFromStartOfSelectedLines( const String& text, bool skipEmpty ); diff --git a/include/eepp/ui/doc/textposition.hpp b/include/eepp/ui/doc/textposition.hpp index c0da6071a..eeb456f09 100644 --- a/include/eepp/ui/doc/textposition.hpp +++ b/include/eepp/ui/doc/textposition.hpp @@ -48,26 +48,40 @@ class EE_API TextPosition { } TextPosition operator+( const TextPosition& other ) const { - return {mLine + other.line(), mColumn + other.column()}; + return { mLine + other.line(), mColumn + other.column() }; } TextPosition operator+=( const TextPosition& other ) const { - return {mLine + other.line(), mColumn + other.column()}; + return { mLine + other.line(), mColumn + other.column() }; } TextPosition operator-( const TextPosition& other ) const { - return {mLine - other.line(), mColumn - other.column()}; + return { mLine - other.line(), mColumn - other.column() }; } TextPosition operator-=( const TextPosition& other ) const { - return {mLine - other.line(), mColumn - other.column()}; + return { mLine - other.line(), mColumn - other.column() }; } - std::string toString() { return String::format( "L%lld,C%lld", mLine, mColumn ); } + std::string toString() const { return String::format( "L%lld,C%lld", mLine, mColumn ); } + + static TextPosition fromString( const std::string& pos ) { + auto split = String::split( pos, ',' ); + if ( split.size() == 2 && !split[0].empty() && !split[1].empty() ) { + if ( split[0][0] == 'L' || split[0][0] == 'l' ) + split[0] = split[0].substr( 1 ); + if ( split[1][0] == 'C' || split[0][0] == 'c' ) + split[1] = split[1].substr( 1 ); + Int64 l, c; + if ( String::fromString( l, split[0] ) && String::fromString( c, split[1] ) ) + return TextPosition( l, c ); + } + return {}; + } private: - Int64 mLine{0xffffffff}; - Int64 mColumn{0xffffffff}; + Int64 mLine{ 0xffffffff }; + Int64 mColumn{ 0xffffffff }; }; }}} // namespace EE::UI::Doc diff --git a/include/eepp/ui/doc/textrange.hpp b/include/eepp/ui/doc/textrange.hpp index cc27d62a3..334b7f8f8 100644 --- a/include/eepp/ui/doc/textrange.hpp +++ b/include/eepp/ui/doc/textrange.hpp @@ -61,10 +61,19 @@ class EE_API TextRange { bool inSameLine() const { return isValid() && mStart.line() == mEnd.line(); } - std::string toString() { + std::string toString() const { return String::format( "%s - %s", mStart.toString().c_str(), mEnd.toString().c_str() ); } + static TextRange fromString( const std::string& range ) { + auto split = String::split( range, "-" ); + if ( split.size() == 2 ) { + return { TextPosition::fromString( String::trim( split[0] ) ), + TextPosition::fromString( String::trim( split[1] ) ) }; + } + return {}; + } + private: TextPosition mStart; TextPosition mEnd; diff --git a/include/eepp/ui/tools/uicodeeditorsplitter.hpp b/include/eepp/ui/tools/uicodeeditorsplitter.hpp index 6f5c33b51..08056c039 100644 --- a/include/eepp/ui/tools/uicodeeditorsplitter.hpp +++ b/include/eepp/ui/tools/uicodeeditorsplitter.hpp @@ -75,8 +75,18 @@ class EE_API UICodeEditorSplitter { bool loadFileFromPath( const std::string& path, UICodeEditor* codeEditor = nullptr ); + void loadAsyncFileFromPath( const std::string& path, std::shared_ptr pool, + UICodeEditor* codeEditor = nullptr, + std::function onLoaded = + std::function() ); + void loadFileFromPathInNewTab( const std::string& path ); + void loadAsyncFileFromPathInNewTab( + const std::string& path, std::shared_ptr pool, + std::function onLoaded = + std::function() ); + void removeUnusedTab( UITabWidget* tabWidget ); UITabWidget* createEditorWithTabWidget( Node* parent ); @@ -129,6 +139,8 @@ class EE_API UICodeEditorSplitter { void setHideTabBarOnSingleTab( bool hideTabBarOnSingleTab ); + const std::vector& getTabWidgets() const; + protected: UISceneNode* mUISceneNode{ nullptr }; UICodeEditor* mCurEditor{ nullptr }; diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 26190ddd6..20c78b44f 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -114,6 +114,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { bool loadFromFile( const std::string& path ); + bool loadAsyncFromFile( const std::string& path, std::shared_ptr pool, + std::function, bool )> onLoaded = + std::function, bool )>() ); + bool loadFromURL( const std::string& url, const EE::Network::Http::Request::FieldTable& headers = Http::Request::FieldTable() ); @@ -417,6 +421,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void copyFilePath(); + void scrollToCursor( bool centered = true ); + + void scrollToMakeVisible( const TextPosition& position, bool centered = false ); + protected: struct LastXOffset { TextPosition position; @@ -547,6 +555,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { virtual void onDocumentSaved( TextDocument* ); + virtual void onDocumentMoved( TextDocument* ); + void onDocumentClosed( TextDocument* doc ); virtual void onDocumentDirtyOnFileSystem( TextDocument* doc ); @@ -555,8 +565,6 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { int getVisibleLinesCount(); - void scrollToMakeVisible( const TextPosition& position, bool centered = false ); - void setScrollX( const Float& val, bool emmitEvent = true ); void setScrollY( const Float& val, bool emmitEvent = true ); diff --git a/include/eepp/ui/uitextinput.hpp b/include/eepp/ui/uitextinput.hpp index f7ea05d73..6e4904cf9 100644 --- a/include/eepp/ui/uitextinput.hpp +++ b/include/eepp/ui/uitextinput.hpp @@ -177,6 +177,8 @@ class EE_API UITextInput : public UITextView, public TextDocument::Client { virtual void onDocumentSaved( TextDocument* ); + virtual void onDocumentMoved( TextDocument* ); + void onDocumentClosed( TextDocument* ){}; void onDocumentDirtyOnFileSystem( TextDocument* ){}; diff --git a/src/eepp/system/filesystem.cpp b/src/eepp/system/filesystem.cpp index 1e0f87847..f4474ae7a 100644 --- a/src/eepp/system/filesystem.cpp +++ b/src/eepp/system/filesystem.cpp @@ -727,4 +727,16 @@ std::string FileSystem::fileGetNumberedFileNameFromPath( std::string directoryPa return ""; } +bool FileSystem::isRelativePath( const std::string& path ) { + if ( !path.empty() ) { + if ( path[0] == '/' ) + return false; +#if EE_PLATFORM == EE_PLATFORM_WIN + if ( path.size() >= 2 && String::isLetter( path[0] ) && path[1] == ':' ) + return false; +#endif + } + return true; +} + }} // namespace EE::System diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index 0d8492e3a..e1ea2dd92 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -266,6 +266,13 @@ bool TextDocument::getBOM() const { return mIsBOM; } +void TextDocument::notifyDocumentMoved( const std::string& path ) { + mFilePath = path; + mFileRealPath = FileInfo::isLink( mFilePath ) ? FileInfo( FileInfo( mFilePath ).linksTo() ) + : FileInfo( mFilePath ); + notifyDocumentMoved(); +} + bool TextDocument::loadFromFile( const std::string& path ) { mLoading = true; if ( !FileSystem::fileExists( path ) && PackManager::instance()->isFallbackToPacksActive() ) { @@ -288,6 +295,19 @@ bool TextDocument::loadFromFile( const std::string& path ) { return ret; } +bool TextDocument::loadAsyncFromFile( const std::string& path, std::shared_ptr pool, + std::function onLoaded ) { + mLoading = true; + pool->run( + [&, path, onLoaded] { + bool loaded = loadFromFile( path ); + if ( onLoaded ) + onLoaded( this, loaded ); + }, + [] {} ); + return true; +} + bool TextDocument::loadFromMemory( const Uint8* data, const Uint32& size ) { IOStreamMemory stream( (const char*)data, size ); return loadFromStream( stream, mFilePath, true ); @@ -1881,6 +1901,12 @@ void TextDocument::notifyDirtyOnFileSystem() { } } +void TextDocument::notifyDocumentMoved() { + for ( auto& client : mClients ) { + client->onDocumentMoved( this ); + } +} + void TextDocument::initializeCommands() { mCommands["reset"] = [&] { reset(); }; mCommands["save"] = [&] { save(); }; diff --git a/src/eepp/ui/tools/uicodeeditorsplitter.cpp b/src/eepp/ui/tools/uicodeeditorsplitter.cpp index 836c783f4..c41029fb0 100644 --- a/src/eepp/ui/tools/uicodeeditorsplitter.cpp +++ b/src/eepp/ui/tools/uicodeeditorsplitter.cpp @@ -319,6 +319,41 @@ bool UICodeEditorSplitter::loadFileFromPath( const std::string& path, UICodeEdit return ret; } +void UICodeEditorSplitter::loadAsyncFileFromPath( + const std::string& path, std::shared_ptr pool, UICodeEditor* codeEditor, + std::function onLoaded ) { +#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN || defined( __EMSCRIPTEN_PTHREADS__ ) + if ( FileSystem::isDirectory( path ) ) + return; + if ( nullptr == codeEditor ) + codeEditor = mCurEditor; + codeEditor->setColorScheme( mColorSchemes[mCurrentColorScheme] ); + bool isUrl = String::startsWith( path, "https://" ) || String::startsWith( path, "http://" ); + if ( isUrl ) { + codeEditor->loadAsyncFromURL( + path, Http::Request::FieldTable(), + [&, codeEditor, path, onLoaded]( std::shared_ptr, bool ) { + mClient->onDocumentLoaded( codeEditor, path ); + if ( onLoaded ) + onLoaded( codeEditor, path ); + } ); + } else { + codeEditor->loadAsyncFromFile( + path, pool, [&, codeEditor, path, onLoaded]( std::shared_ptr, bool ) { + mClient->onDocumentLoaded( codeEditor, path ); + if ( onLoaded ) + onLoaded( codeEditor, path ); + } ); + } +#else + loadFileFromPath( path, codeEditor ); + if ( nullptr == codeEditor ) + codeEditor = mCurEditor; + if ( onLoaded ) + onLoaded( codeEditor, path ); +#endif +} + void UICodeEditorSplitter::loadFileFromPathInNewTab( const std::string& path ) { auto d = createCodeEditorInTabWidget( tabWidgetFromEditor( mCurEditor ) ); UITabWidget* tabWidget = d.first->getTabWidget(); @@ -327,6 +362,16 @@ void UICodeEditorSplitter::loadFileFromPathInNewTab( const std::string& path ) { tabWidget->setTabSelected( addedTab ); } +void UICodeEditorSplitter::loadAsyncFileFromPathInNewTab( + const std::string& path, std::shared_ptr pool, + std::function onLoaded ) { + auto d = createCodeEditorInTabWidget( tabWidgetFromEditor( mCurEditor ) ); + UITabWidget* tabWidget = d.first->getTabWidget(); + UITab* addedTab = d.first; + loadAsyncFileFromPath( path, pool, d.second, onLoaded ); + tabWidget->setTabSelected( addedTab ); +} + void UICodeEditorSplitter::setCurrentEditor( UICodeEditor* editor ) { bool isNew = mCurEditor != editor; mCurEditor = editor; @@ -351,7 +396,7 @@ UICodeEditorSplitter::createCodeEditorInTabWidget( UITabWidget* tabWidget ) { } void UICodeEditorSplitter::removeUnusedTab( UITabWidget* tabWidget ) { - if ( tabWidget && tabWidget->getTabCount() == 2 && + if ( tabWidget && tabWidget->getTabCount() >= 2 && tabWidget->getTab( 0 ) ->getOwnedWidget() ->asType() @@ -475,6 +520,10 @@ void UICodeEditorSplitter::setHideTabBarOnSingleTab( bool hideTabBarOnSingleTab } } +const std::vector& UICodeEditorSplitter::getTabWidgets() const { + return mTabWidgets; +} + std::vector UICodeEditorSplitter::getAllEditors() { std::vector editors; forEachEditor( [&]( UICodeEditor* editor ) { editors.push_back( editor ); } ); @@ -762,7 +811,9 @@ void UICodeEditorSplitter::onTabClosed( const TabEvent* tabEvent ) { auto d = createCodeEditorInTabWidget( tabWidget ); d.first->getTabWidget()->setTabSelected( d.first ); } else { - tabWidget->setTabSelected( eemin( tabWidget->getTabCount() - 1, tabEvent->getTabIndex() ) ); + if ( tabWidget->getTabSelectedIndex() >= tabWidget->getTabCount() ) + tabWidget->setTabSelected( + eemin( tabWidget->getTabCount() - 1, tabEvent->getTabIndex() ) ); } if ( tabEvent->getTab()->getOwnedWidget() == mCurEditor ) setCurrentEditor( nullptr ); diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 3f46ee9d6..fb37e9a44 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -188,6 +188,19 @@ void UICodeEditor::draw() { if ( mFont == NULL ) return; + if ( mDisplayLoaderIfDocumentLoading && mDoc->isLoading() ) { + UILoader* loader = getLoader(); + loader->setParent( this ); + loader->setVisible( true ); + loader->setEnabled( false ); + loader->setPixelsSize( getPixelsSize() ); + } else if ( mLoader != nullptr && !mDoc->isLoading() && mLoader->isVisible() ) { + mLoader->setVisible( false ); + } + + if ( mDoc->isLoading() ) + return; + if ( mDirtyEditor ) updateEditor(); @@ -273,16 +286,6 @@ void UICodeEditor::draw() { for ( auto& module : mModules ) module->postDraw( this, startScroll, lineHeight, cursor ); - - if ( mDisplayLoaderIfDocumentLoading && mDoc->isLoading() ) { - UILoader* loader = getLoader(); - loader->setParent( this ); - loader->setVisible( true ); - loader->setEnabled( false ); - loader->setPixelsSize( getPixelsSize() ); - } else if ( mLoader != nullptr && !mDoc->isLoading() && mLoader->isVisible() ) { - mLoader->setVisible( false ); - } } void UICodeEditor::scheduledUpdate( const Time& ) { @@ -346,6 +349,31 @@ bool UICodeEditor::loadFromFile( const std::string& path ) { return ret; } +bool UICodeEditor::loadAsyncFromFile( + const std::string& path, std::shared_ptr pool, + std::function, bool )> onLoaded ) { + bool wasLocked = isLocked(); + if ( !wasLocked ) + setLocked( true ); + bool ret = mDoc->loadAsyncFromFile( path, pool, + [this, onLoaded, wasLocked]( TextDocument*, bool success ) { + runOnMainThread( [&, onLoaded, wasLocked] { + invalidateEditor(); + updateLongestLineWidth(); + mHighlighter.changeDoc( mDoc.get() ); + invalidateDraw(); + if ( !wasLocked ) + setLocked( false ); + onDocumentLoaded(); + if ( onLoaded ) + onLoaded( mDoc, success ); + } ); + } ); + if ( !ret && !wasLocked ) + setLocked( false ); + return ret; +} + bool UICodeEditor::loadFromURL( const std::string& url, const Http::Request::FieldTable& headers ) { bool ret = mDoc->loadFromURL( url, headers ); if ( ret ) { @@ -367,14 +395,14 @@ bool UICodeEditor::loadAsyncFromURL( bool ret = mDoc->loadAsyncFromURL( url, headers, [this, onLoaded, wasLocked]( TextDocument*, bool success ) { - runOnMainThread( [&, onLoaded] { + runOnMainThread( [&, onLoaded, wasLocked] { invalidateEditor(); updateLongestLineWidth(); mHighlighter.changeDoc( mDoc.get() ); invalidateDraw(); - onDocumentLoaded(); if ( !wasLocked ) setLocked( false ); + onDocumentLoaded(); if ( onLoaded ) onLoaded( mDoc, success ); } ); @@ -1173,6 +1201,10 @@ void UICodeEditor::copyFilePath() { getUISceneNode()->getWindow()->getClipboard()->setText( mDoc->getFilePath() ); } +void UICodeEditor::scrollToCursor( bool centered ) { + scrollToMakeVisible( mDoc->getSelection().start(), centered ); +} + void UICodeEditor::updateEditor() { mDoc->setPageSize( getVisibleLinesCount() ); if ( mDoc->getActiveClient() == this ) @@ -1219,6 +1251,11 @@ void UICodeEditor::onDocumentSaved( TextDocument* doc ) { sendEvent( &event ); } +void UICodeEditor::onDocumentMoved( TextDocument* doc ) { + DocEvent event( this, doc, Event::OnDocumentMoved ); + sendEvent( &event ); +} + void UICodeEditor::onDocumentClosed( TextDocument* doc ) { DocEvent event( this, doc, Event::OnDocumentClosed ); sendEvent( &event ); diff --git a/src/eepp/ui/uitextinput.cpp b/src/eepp/ui/uitextinput.cpp index 2458b49a3..ce63796a8 100644 --- a/src/eepp/ui/uitextinput.cpp +++ b/src/eepp/ui/uitextinput.cpp @@ -430,6 +430,8 @@ void UITextInput::onDocumentUndoRedo( const TextDocument::UndoRedo& ) { void UITextInput::onDocumentSaved( TextDocument* ) {} +void UITextInput::onDocumentMoved( TextDocument* ) {} + UITextInput* UITextInput::setMaxLength( const Uint32& maxLength ) { mMaxLength = maxLength; return this; diff --git a/src/tools/codeeditor/appconfig.cpp b/src/tools/codeeditor/appconfig.cpp index dc1f4dfbe..803b3ccc7 100644 --- a/src/tools/codeeditor/appconfig.cpp +++ b/src/tools/codeeditor/appconfig.cpp @@ -159,14 +159,38 @@ void AppConfig::save( const std::vector& recentFiles, iniState.writeFile(); } +struct ProjectPath { + std::string path; + TextRange selection{ { 0, 0 }, { 0, 0 } }; + ProjectPath() {} + ProjectPath( const std::string& path, const TextRange& selection ) : + path( path ), selection( selection ) {} + + std::string toString() { return URI::encode( path ) + ";" + selection.toString(); } + + static ProjectPath fromString( const std::string& str ) { + auto split = String::split( str, ';' ); + if ( !split.empty() ) { + ProjectPath pp; + pp.path = URI::decode( split[0] ); + pp.selection = split.size() >= 2 + ? TextRange::fromString( split[1] ) + : TextRange( TextPosition( 0, 0 ), TextPosition( 0, 0 ) ); + return pp; + } + return {}; + } +}; + void AppConfig::saveProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter, const std::string& configPath ) { FileSystem::dirAddSlashAtEnd( projectFolder ); std::vector editors = editorSplitter->getAllEditors(); - std::vector paths; + std::vector paths; for ( auto editor : editors ) if ( editor->getDocument().hasFilepath() ) - paths.emplace_back( editor->getDocument().getFilePath() ); + paths.emplace_back( ProjectPath{ editor->getDocument().getFilePath(), + editor->getDocument().getSelection() } ); std::string projectsPath( configPath + "projects" + FileSystem::getOSSlash() ); if ( !FileSystem::fileExists( projectsPath ) ) FileSystem::makeDir( projectsPath ); @@ -175,12 +199,16 @@ void AppConfig::saveProject( std::string projectFolder, UICodeEditorSplitter* ed IniFile ini( projectCfgPath, false ); ini.setValue( "path", "folder_path", projectFolder ); for ( size_t i = 0; i < paths.size(); i++ ) - ini.setValue( "files", String::format( "file_name_%lu", i ), paths[i] ); + ini.setValue( "files", String::format( "file_name_%lu", i ), paths[i].toString() ); + ini.setValueI( "files", "current_page", + !editorSplitter->getTabWidgets().empty() + ? editorSplitter->getTabWidgets()[0]->getTabSelectedIndex() + : 0 ); ini.writeFile(); } void AppConfig::loadProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter, - const std::string& configPath ) { + const std::string& configPath, std::shared_ptr pool ) { FileSystem::dirAddSlashAtEnd( projectFolder ); std::string projectsPath( configPath + "projects" + FileSystem::getOSSlash() ); MD5::Result hash = MD5::fromString( projectFolder ); @@ -190,11 +218,32 @@ void AppConfig::loadProject( std::string projectFolder, UICodeEditorSplitter* ed IniFile ini( projectCfgPath ); bool found; size_t i = 0; + std::vector paths; do { std::string val( ini.getValue( "files", String::format( "file_name_%lu", i ) ) ); found = !val.empty(); - if ( found && FileSystem::fileExists( val ) ) - editorSplitter->loadFileFromPathInNewTab( val ); + if ( found ) { + auto pp = ProjectPath::fromString( val ); + if ( FileSystem::fileExists( pp.path ) ) + paths.emplace_back( pp ); + } i++; } while ( found ); + + Int64 currentPage = ini.getValueI( "files", "current_page" ); + size_t totalToLoad = paths.size(); + + for ( auto& pp : paths ) { + editorSplitter->loadAsyncFileFromPathInNewTab( + pp.path, pool, + [pp, editorSplitter, totalToLoad, currentPage]( UICodeEditor* editor, + const std::string& ) { + editor->getDocument().setSelection( pp.selection ); + editor->scrollToCursor(); + + if ( !editorSplitter->getTabWidgets().empty() && + editorSplitter->getTabWidgets()[0]->getTabCount() == totalToLoad ) + editorSplitter->switchToTab( currentPage ); + } ); + } } diff --git a/src/tools/codeeditor/appconfig.hpp b/src/tools/codeeditor/appconfig.hpp index 8578aa904..3cfc389c5 100644 --- a/src/tools/codeeditor/appconfig.hpp +++ b/src/tools/codeeditor/appconfig.hpp @@ -86,7 +86,7 @@ struct AppConfig { const std::string& configPath ); void loadProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter, - const std::string& configPath ); + const std::string& configPath, std::shared_ptr pool ); }; #endif // APPCONFIG_HPP diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index 438b3cbac..813feca84 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -7,18 +7,6 @@ App* appInstance = nullptr; -static bool isRelativePath( const std::string& path ) { - if ( !path.empty() ) { - if ( path[0] == '/' ) - return false; -#if EE_PLATFORM == EE_PLATFORM_WIN - if ( path.size() >= 2 && String::isLetter( path[0] ) && path[1] == ':' ) - return false; -#endif - } - return true; -} - void appLoop() { appInstance->mainLoop(); } @@ -187,7 +175,7 @@ void App::openFolderDialog() { void App::openFontDialog( std::string& fontPath ) { std::string absoluteFontPath( fontPath ); - if ( isRelativePath( absoluteFontPath ) ) + if ( FileSystem::isRelativePath( absoluteFontPath ) ) absoluteFontPath = mResPath + fontPath; UIFileDialog* dialog = UIFileDialog::New( UIFileDialog::DefaultFlags, "*.ttf; *.otf; *.wolff; *.otb", @@ -1302,11 +1290,19 @@ void App::loadFileFromPath( const std::string& path, bool inNewTab, UICodeEditor #endif } } else { +#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN || defined( __EMSCRIPTEN_PTHREADS__ ) + if ( inNewTab ) { + mEditorSplitter->loadAsyncFileFromPathInNewTab( path, mThreadPool ); + } else { + mEditorSplitter->loadAsyncFileFromPath( path, mThreadPool, codeEditor ); + } +#else if ( inNewTab ) { mEditorSplitter->loadFileFromPathInNewTab( path ); } else { mEditorSplitter->loadFileFromPath( path, codeEditor ); } +#endif } } @@ -1499,6 +1495,13 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { } } ); + editor->addEventListener( Event::OnDocumentMoved, [&]( const Event* event ) { + if ( !appInstance ) + return; + UICodeEditor* editor = event->getNode()->asType(); + updateEditorTabTitle( editor ); + } ); + if ( config.autoComplete && !mAutoCompleteModule ) setAutoComplete( config.autoComplete ); @@ -2039,7 +2042,7 @@ void App::loadFolder( const std::string& path ) { mCurrentProject = rpath; loadDirTree( rpath ); - mConfig.loadProject( rpath, mEditorSplitter, mConfigPath ); + mConfig.loadProject( rpath, mEditorSplitter, mConfigPath, mThreadPool ); mFileSystemModel = FileSystemModel::New( rpath, FileSystemModel::Mode::FilesAndDirectories, { true, true, true } ); @@ -2064,7 +2067,7 @@ void App::loadFolder( const std::string& path ) { FontTrueType* App::loadFont( const std::string& name, std::string fontPath, const std::string& fallback ) { - if ( isRelativePath( fontPath ) ) + if ( FileSystem::isRelativePath( fontPath ) ) fontPath = mResPath + fontPath; if ( fontPath.empty() || !FileSystem::fileExists( fontPath ) ) fontPath = fallback; @@ -2103,7 +2106,7 @@ void App::init( const std::string& file, const Float& pidelDensity, winSettings.Height = mConfig.window.size.getHeight(); if ( winSettings.Icon.empty() ) { winSettings.Icon = mConfig.window.winIcon; - if ( isRelativePath( winSettings.Icon ) ) + if ( FileSystem::isRelativePath( winSettings.Icon ) ) winSettings.Icon = mResPath + winSettings.Icon; } ContextSettings contextSettings = engine->createContextSettings( &mConfig.ini, "window" ); diff --git a/src/tools/codeeditor/filesystemlistener.cpp b/src/tools/codeeditor/filesystemlistener.cpp index 61d8a79d4..54b49e069 100644 --- a/src/tools/codeeditor/filesystemlistener.cpp +++ b/src/tools/codeeditor/filesystemlistener.cpp @@ -18,6 +18,23 @@ void FileSystemListener::handleFileAction( efsw::WatchID, const std::string& dir if ( mDirTree ) mDirTree.get()->onChange( (ProjectDirectoryTree::Action)action, file, oldFilename ); + + if ( action == efsw::Actions::Moved ) { + FileInfo oldFile( FileSystem::isRelativePath( oldFilename ) ? dir + oldFilename + : oldFilename ); + if ( file.isLink() ) + file = FileInfo( file.linksTo() ); + + if ( isFileOpen( oldFile ) ) + notifyMove( oldFile, file ); + + if ( oldFile.isLink() ) { + oldFile = FileInfo( oldFile.linksTo() ); + + if ( isFileOpen( oldFile ) ) + notifyMove( oldFile, file ); + } + } } case efsw::Actions::Modified: { if ( file.isLink() ) @@ -52,3 +69,10 @@ void FileSystemListener::notifyChange( const FileInfo& file ) { doc.setDirtyOnFileSystem( true ); } ); } + +void FileSystemListener::notifyMove( const FileInfo& oldFile, const FileInfo& newFile ) { + mSplitter->forEachDoc( [&]( TextDocument& doc ) { + if ( oldFile.getFilepath() == doc.getFileInfo().getFilepath() ) + doc.notifyDocumentMoved( newFile.getFilepath() ); + } ); +} diff --git a/src/tools/codeeditor/filesystemlistener.hpp b/src/tools/codeeditor/filesystemlistener.hpp index a7135fe3c..2a4308e32 100644 --- a/src/tools/codeeditor/filesystemlistener.hpp +++ b/src/tools/codeeditor/filesystemlistener.hpp @@ -35,6 +35,8 @@ class FileSystemListener : public efsw::FileWatchListener { bool isFileOpen( const FileInfo& file ); void notifyChange( const FileInfo& file ); + + void notifyMove( const FileInfo& oldFile, const FileInfo& newFile ); }; #endif // FILESYSTEMLISTENER_HPP