From f3e1720bc2ca063cfefa8962b8013e3dedbafdfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sat, 29 Aug 2020 21:31:06 -0300 Subject: [PATCH] IgnoreMatcher now check for subfolder ignore files and apply the ignore rules from that file. Added "Save All" to ecode. Some improvements to UIFileDialog. Some minor refactor. --- include/eepp/scene/keyevent.hpp | 8 +- .../eepp/ui/abstract/uiabstracttableview.hpp | 2 +- include/eepp/ui/abstract/uiabstractview.hpp | 11 +- .../eepp/ui/tools/uicodeeditorsplitter.hpp | 4 + include/eepp/ui/uifiledialog.hpp | 2 +- projects/linux/ee.files | 2 + src/eepp/scene/keyevent.cpp | 6 + src/eepp/ui/abstract/uiabstracttableview.cpp | 8 +- src/eepp/ui/tools/uicodeeditorsplitter.cpp | 22 ++ src/eepp/ui/uifiledialog.cpp | 29 ++- src/eepp/ui/uitableview.cpp | 2 +- src/eepp/ui/uitreeview.cpp | 4 +- src/tools/codeeditor/codeeditor.cpp | 191 ++++++++---------- src/tools/codeeditor/codeeditor.hpp | 98 ++++----- src/tools/codeeditor/ignorematcher.cpp | 9 +- src/tools/codeeditor/ignorematcher.hpp | 10 +- src/tools/codeeditor/projectdirectorytree.cpp | 16 +- src/tools/codeeditor/projectdirectorytree.hpp | 2 +- .../codeeditor/uitreeviewglobalsearch.cpp | 50 +++++ .../codeeditor/uitreeviewglobalsearch.hpp | 44 ++++ 20 files changed, 320 insertions(+), 200 deletions(-) create mode 100644 src/tools/codeeditor/uitreeviewglobalsearch.cpp create mode 100644 src/tools/codeeditor/uitreeviewglobalsearch.hpp diff --git a/include/eepp/scene/keyevent.hpp b/include/eepp/scene/keyevent.hpp index a84f8b168..4cfd48617 100644 --- a/include/eepp/scene/keyevent.hpp +++ b/include/eepp/scene/keyevent.hpp @@ -14,6 +14,8 @@ class EE_API KeyEvent : public Event { KeyEvent( Node* node, const Uint32& eventNum, const Keycode& keyCode, const Uint32& chr, const Uint32& mod ); + KeyEvent( const KeyEvent& event ); + ~KeyEvent(); const Keycode& getKeyCode() const; @@ -23,9 +25,9 @@ class EE_API KeyEvent : public Event { const Uint32& getMod() const; protected: - Keycode mKeyCode; - String::StringBaseType mChar; - Uint32 mMod; + Keycode mKeyCode{Keycode::KEY_UNKNOWN}; + String::StringBaseType mChar{0}; + Uint32 mMod{0}; }; class EE_API TextInputEvent : public Event { diff --git a/include/eepp/ui/abstract/uiabstracttableview.hpp b/include/eepp/ui/abstract/uiabstracttableview.hpp index 6d04d69f6..76668a8c3 100644 --- a/include/eepp/ui/abstract/uiabstracttableview.hpp +++ b/include/eepp/ui/abstract/uiabstracttableview.hpp @@ -148,7 +148,7 @@ class EE_API UIAbstractTableView : public UIAbstractView { virtual void onScrollChange(); - virtual void onOpenModelIndex( const ModelIndex& index ); + virtual void onOpenModelIndex( const ModelIndex& index, const Event* triggerEvent = nullptr ); virtual void onSortColumn( const size_t& colIndex ); diff --git a/include/eepp/ui/abstract/uiabstractview.hpp b/include/eepp/ui/abstract/uiabstractview.hpp index beb2b535d..d12642bed 100644 --- a/include/eepp/ui/abstract/uiabstractview.hpp +++ b/include/eepp/ui/abstract/uiabstractview.hpp @@ -16,11 +16,13 @@ enum class ModelEventType { Open, OpenTree, CloseTree }; class EE_API ModelEvent : public Event { public: ModelEvent( Model* model, const ModelIndex& index, Node* node, - const ModelEventType& modelEventType = ModelEventType::Open ) : - Event( node, Event::OnModelEvent ), + const ModelEventType& modelEventType = ModelEventType::Open, + const Event* triggerEvent = nullptr ) : + Event( node, EventType::OnModelEvent ), model( model ), index( index ), - modelEventType( modelEventType ) {} + modelEventType( modelEventType ), + triggerEvent( triggerEvent ) {} const Model* getModel() const { return model; } @@ -28,10 +30,13 @@ class EE_API ModelEvent : public Event { const ModelEventType& getModelEventType() const { return modelEventType; } + const Event* getTriggerEvent() const { return triggerEvent; } + protected: const Model* model; ModelIndex index; ModelEventType modelEventType; + const Event* triggerEvent{nullptr}; }; class EE_API UIAbstractView : public UIScrollableWidget { diff --git a/include/eepp/ui/tools/uicodeeditorsplitter.hpp b/include/eepp/ui/tools/uicodeeditorsplitter.hpp index 69207e36c..5f33311ce 100644 --- a/include/eepp/ui/tools/uicodeeditorsplitter.hpp +++ b/include/eepp/ui/tools/uicodeeditorsplitter.hpp @@ -111,6 +111,10 @@ class EE_API UICodeEditorSplitter { bool editorExists( UICodeEditor* editor ) const; + bool isAnyEditorDirty(); + + void forEachEditorStoppable( std::function run ); + protected: UISceneNode* mUISceneNode{nullptr}; UICodeEditor* mCurEditor{nullptr}; diff --git a/include/eepp/ui/uifiledialog.hpp b/include/eepp/ui/uifiledialog.hpp index e547fca12..b49575adf 100644 --- a/include/eepp/ui/uifiledialog.hpp +++ b/include/eepp/ui/uifiledialog.hpp @@ -122,7 +122,7 @@ class EE_API UIFileDialog : public UIWindow { void disableButtons(); - void openFileOrFolder(); + void openFileOrFolder( bool shouldOpenFolder ); void goFolderUp(); diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 14d74e6ea..e3fb8b722 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -1025,6 +1025,8 @@ ../../src/tools/codeeditor/projectsearch.hpp ../../src/tools/codeeditor/uicodeeditorsplitter.cpp ../../src/tools/codeeditor/uicodeeditorsplitter.hpp +../../src/tools/codeeditor/uitreeviewglobalsearch.cpp +../../src/tools/codeeditor/uitreeviewglobalsearch.hpp ../../src/tools/mapeditor/mapeditor.cpp ../../src/tools/textureatlaseditor/textureatlaseditor.cpp ../../src/tools/texturepacker/texturepacker.cpp diff --git a/src/eepp/scene/keyevent.cpp b/src/eepp/scene/keyevent.cpp index a82e0d9b7..16c9a20a5 100644 --- a/src/eepp/scene/keyevent.cpp +++ b/src/eepp/scene/keyevent.cpp @@ -7,6 +7,12 @@ KeyEvent::KeyEvent( Node* node, const Uint32& eventNum, const Keycode& keyCode, const Uint32& mod ) : Event( node, eventNum ), mKeyCode( keyCode ), mChar( chr ), mMod( mod ) {} +KeyEvent::KeyEvent( const KeyEvent& event ) : + Event( event.getNode(), event.getType() ), + mKeyCode( event.getKeyCode() ), + mChar( event.getChar() ), + mMod( event.getMod() ) {} + KeyEvent::~KeyEvent() {} const Keycode& KeyEvent::getKeyCode() const { diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index 583314e4f..1d1f7f607 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -335,7 +335,7 @@ UIWidget* UIAbstractTableView::createCell( UIWidget* rowWidget, const ModelIndex auto mouseEvent = static_cast( event ); auto idx = mouseEvent->getNode()->getParent()->asType()->getCurIndex(); if ( mouseEvent->getFlags() & EE_BUTTON_LMASK ) { - onOpenModelIndex( idx ); + onOpenModelIndex( idx, event ); } } ); return widget; @@ -422,8 +422,8 @@ void UIAbstractTableView::setSortIconSize( const size_t& sortIconSize ) { mSortIconSize = sortIconSize; } -void UIAbstractTableView::onOpenModelIndex( const ModelIndex& index ) { - ModelEvent event( getModel(), index, this ); +void UIAbstractTableView::onOpenModelIndex( const ModelIndex& index, const Event* triggerEvent ) { + ModelEvent event( getModel(), index, this, ModelEventType::Open, triggerEvent ); sendEvent( &event ); } @@ -470,7 +470,7 @@ Uint32 UIAbstractTableView::onTextInput( const TextInputEvent& event ) { mSearchText += String::toLower( event.getText() ); ModelIndex index = findRowWithText( mSearchText ); if ( index.isValid() ) - getSelection().set( index ); + setSelection( index ); return 1; } diff --git a/src/eepp/ui/tools/uicodeeditorsplitter.cpp b/src/eepp/ui/tools/uicodeeditorsplitter.cpp index e01d2d049..4d4e81ee1 100644 --- a/src/eepp/ui/tools/uicodeeditorsplitter.cpp +++ b/src/eepp/ui/tools/uicodeeditorsplitter.cpp @@ -400,6 +400,28 @@ void UICodeEditorSplitter::forEachEditor( std::function r run( tabWidget->getTab( i )->getOwnedWidget()->asType() ); } +void UICodeEditorSplitter::forEachEditorStoppable( std::function run ) { + for ( auto tabWidget : mTabWidgets ) { + for ( size_t i = 0; i < tabWidget->getTabCount(); i++ ) { + if ( run( tabWidget->getTab( i )->getOwnedWidget()->asType() ) ) { + return; + } + } + } +} + +bool UICodeEditorSplitter::isAnyEditorDirty() { + bool any = false; + forEachEditorStoppable( [&any]( UICodeEditor* editor ) { + if ( editor->isDirty() ) { + any = true; + return true; + } + return false; + } ); + return any; +} + void UICodeEditorSplitter::zoomIn() { forEachEditor( []( UICodeEditor* editor ) { editor->fontSizeGrow(); } ); } diff --git a/src/eepp/ui/uifiledialog.cpp b/src/eepp/ui/uifiledialog.cpp index 821aeed83..6d63ccc95 100644 --- a/src/eepp/ui/uifiledialog.cpp +++ b/src/eepp/ui/uifiledialog.cpp @@ -100,8 +100,18 @@ UIFileDialog::UIFileDialog( Uint32 dialogFlags, const std::string& defaultFilePa if ( modelEvent->getModelEventType() == ModelEventType::Open ) { Variant vPath( modelEvent->getModel()->data( modelEvent->getModelIndex(), Model::Role::Custom ) ); - if ( vPath.isValid() && vPath.is( Variant::Type::cstr ) ) - openFileOrFolder(); + if ( vPath.isValid() && vPath.is( Variant::Type::cstr ) ) { + bool shouldOpenFolder = false; + if ( getAllowFolderSelect() && modelEvent->getTriggerEvent() && + modelEvent->getTriggerEvent()->getType() == Event::EventType::KeyDown ) { + const KeyEvent* keyEvent = + static_cast( modelEvent->getTriggerEvent() ); + if ( keyEvent->getMod() & KEYMOD_CTRL ) { + shouldOpenFolder = true; + } + } + openFileOrFolder( shouldOpenFolder ); + } } } ); mList->setOnSelectionChange( [&] { @@ -110,9 +120,9 @@ UIFileDialog::UIFileDialog( Uint32 dialogFlags, const std::string& defaultFilePa auto* node = (FileSystemModel::Node*)mList->getSelection().first().data(); if ( !isSaveDialog() ) { if ( getAllowFolderSelect() || !FileSystem::isDirectory( node->fullPath() ) ) - mFile->setText( node->getName() ); + setFileName( node->getName() ); } else if ( !FileSystem::isDirectory( node->fullPath() ) ) { - mFile->setText( node->getName() ); + setFileName( node->getName() ); } } ); mList->setAutoExpandOnSingleColumn( true ); @@ -252,7 +262,8 @@ void UIFileDialog::setCurPath( const std::string& path ) { mCurPath = path; FileSystem::dirAddSlashAtEnd( mCurPath ); mPath->setText( mCurPath ); - mFile->setText( "" ); + if ( !isSaveDialog() ) + mFile->setText( "" ); refreshFolder(); } @@ -283,7 +294,7 @@ void UIFileDialog::disableButtons() { mButtonMaximize->setEnabled( false ); } -void UIFileDialog::openFileOrFolder() { +void UIFileDialog::openFileOrFolder( bool shouldOpenFolder = false ) { if ( mList->getSelection().isEmpty() ) return; auto* node = (FileSystemModel::Node*)mList->getSelection().first().data(); @@ -291,7 +302,11 @@ void UIFileDialog::openFileOrFolder() { std::string newPath = mCurPath + node->getName(); if ( FileSystem::isDirectory( newPath ) ) { - setCurPath( newPath ); + if ( shouldOpenFolder ) { + open(); + } else { + setCurPath( newPath ); + } } else { open(); } diff --git a/src/eepp/ui/uitableview.cpp b/src/eepp/ui/uitableview.cpp index 1fd1856b0..214c5220e 100644 --- a/src/eepp/ui/uitableview.cpp +++ b/src/eepp/ui/uitableview.cpp @@ -231,7 +231,7 @@ Uint32 UITableView::onKeyDown( const KeyEvent& event ) { case KEY_RETURN: case KEY_KP_ENTER: { if ( curIndex.isValid() ) - onOpenModelIndex( curIndex ); + onOpenModelIndex( curIndex, &event ); return 1; } default: diff --git a/src/eepp/ui/uitreeview.cpp b/src/eepp/ui/uitreeview.cpp index 9ae61cf8f..fc1c66f03 100644 --- a/src/eepp/ui/uitreeview.cpp +++ b/src/eepp/ui/uitreeview.cpp @@ -127,7 +127,7 @@ UIWidget* UITreeView::setupCell( UITableCell* widget, UIWidget* rowWidget, createOrUpdateColumns(); onOpenTreeModelIndex( idx, data.open ); } else { - onOpenModelIndex( idx ); + onOpenModelIndex( idx, event ); } } } ); @@ -586,7 +586,7 @@ Uint32 UITreeView::onKeyDown( const KeyEvent& event ) { metadata.open = !metadata.open; createOrUpdateColumns(); } else { - onOpenModelIndex( curIndex ); + onOpenModelIndex( curIndex, &event ); } } return 1; diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index 104914bed..9ed09ebd3 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -6,6 +6,18 @@ 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(); } @@ -33,21 +45,63 @@ void App::saveDoc() { if ( mEditorSplitter->getCurEditor()->save() ) updateEditorState(); } else { - saveFileDialog(); + saveFileDialog( mEditorSplitter->getCurEditor() ); } } +void App::saveAllProcess() { + if ( mTmpDocs.empty() ) + return; + + mEditorSplitter->forEachEditorStoppable( [&]( UICodeEditor* editor ) { + if ( editor->getDocument().isDirty() && + std::find( mTmpDocs.begin(), mTmpDocs.end(), &editor->getDocument() ) != + mTmpDocs.end() ) { + if ( editor->getDocument().hasFilepath() ) { + editor->save(); + updateEditorTabTitle( editor ); + mTmpDocs.erase( &editor->getDocument() ); + } else { + UIFileDialog* dialog = saveFileDialog( editor, false ); + dialog->addEventListener( Event::SaveFile, [&, editor]( const Event* ) { + updateEditorTabTitle( editor ); + } ); + dialog->addEventListener( Event::OnWindowClose, [&, editor]( const Event* ) { + mTmpDocs.erase( &editor->getDocument() ); + if ( !SceneManager::instance()->isShootingDown() && !mTmpDocs.empty() ) + saveAllProcess(); + } ); + return true; + } + } + return false; + } ); +} + +void App::saveAll() { + mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) { + if ( editor->isDirty() ) + mTmpDocs.insert( &editor->getDocument() ); + } ); + saveAllProcess(); +} + std::string App::titleFromEditor( UICodeEditor* editor ) { std::string title( editor->getDocument().getFilename() ); return editor->getDocument().isDirty() ? title + "*" : title; } -void App::updateEditorTitle( UICodeEditor* editor ) { +void App::updateEditorTabTitle( UICodeEditor* editor ) { std::string title( titleFromEditor( editor ) ); if ( editor->getData() ) { UITab* tab = (UITab*)editor->getData(); tab->setText( title ); } +} + +void App::updateEditorTitle( UICodeEditor* editor ) { + std::string title( titleFromEditor( editor ) ); + updateEditorTabTitle( editor ); setAppTitle( title ); } @@ -111,8 +165,11 @@ void App::openFolderDialog() { } void App::openFontDialog( std::string& fontPath ) { + std::string absoluteFontPath( fontPath ); + if ( isRelativePath( absoluteFontPath ) ) + absoluteFontPath = mResPath + fontPath; UIFileDialog* dialog = UIFileDialog::New( UIFileDialog::DefaultFlags, "*.ttf; *.otf; *.wolff", - FileSystem::fileRemoveFileName( fontPath ) ); + FileSystem::fileRemoveFileName( absoluteFontPath ) ); ModelIndex index = dialog->getList()->findRowWithText( FileSystem::fileNameFromPath( fontPath ), true, true ); if ( index.isValid() ) @@ -140,23 +197,24 @@ void App::openFontDialog( std::string& fontPath ) { dialog->show(); } -void App::saveFileDialog() { +UIFileDialog* App::saveFileDialog( UICodeEditor* editor, bool focusOnClose ) { + if ( !editor ) + return nullptr; UIFileDialog* dialog = UIFileDialog::New( UIFileDialog::DefaultFlags | UIFileDialog::SaveDialog, "." ); dialog->setWinFlags( UI_WIN_DEFAULT_FLAGS | UI_WIN_MAXIMIZE_BUTTON | UI_WIN_MODAL ); dialog->setTitle( "Save File As" ); dialog->setCloseShortcut( KEY_ESCAPE ); - std::string filename( mEditorSplitter->getCurEditor()->getDocument().getFilename() ); - if ( FileSystem::fileExtension( mEditorSplitter->getCurEditor()->getDocument().getFilename() ) - .empty() ) - filename += mEditorSplitter->getCurEditor()->getSyntaxDefinition().getFileExtension(); + std::string filename( editor->getDocument().getFilename() ); + if ( FileSystem::fileExtension( editor->getDocument().getFilename() ).empty() ) + filename += editor->getSyntaxDefinition().getFileExtension(); dialog->setFileName( filename ); - dialog->addEventListener( Event::SaveFile, [&]( const Event* event ) { - if ( mEditorSplitter->getCurEditor() ) { + dialog->addEventListener( Event::SaveFile, [&, editor]( const Event* event ) { + if ( editor ) { std::string path( event->getNode()->asType()->getFullPath() ); if ( !path.empty() && !FileSystem::isDirectory( path ) && FileSystem::fileCanWrite( FileSystem::fileRemoveFileName( path ) ) ) { - if ( mEditorSplitter->getCurEditor()->getDocument().save( path ) ) { + if ( editor->getDocument().save( path ) ) { updateEditorState(); } else { UIMessageBox* msg = @@ -172,12 +230,15 @@ void App::saveFileDialog() { } } } ); - dialog->addEventListener( Event::OnWindowClose, [&]( const Event* ) { - if ( mEditorSplitter->getCurEditor() && !SceneManager::instance()->isShootingDown() ) - mEditorSplitter->getCurEditor()->setFocus(); - } ); + if ( focusOnClose ) { + dialog->addEventListener( Event::OnWindowClose, [&, editor]( const Event* ) { + if ( editor && !SceneManager::instance()->isShootingDown() ) + editor->setFocus(); + } ); + } dialog->center(); dialog->show(); + return dialog; } bool App::findPrevText( SearchState& search ) { @@ -683,83 +744,6 @@ void App::hideGlobalSearchBar() { mGlobalSearchTree->setVisible( false ); } -class UITreeViewCellGlobalSearch : public UITreeViewCell { - public: - static UITreeViewCellGlobalSearch* New() { return eeNew( UITreeViewCellGlobalSearch, () ); } - - UITreeViewCellGlobalSearch() : UITreeViewCell() {} - - UIPushButton* setText( const String& text ); - - UIPushButton* updateText( const String& text ); -}; - -class UITreeViewGlobalSearch : public UITreeView { - public: - static UITreeViewGlobalSearch* New( const SyntaxColorScheme& colorScheme ) { - return eeNew( UITreeViewGlobalSearch, ( colorScheme ) ); - } - - UITreeViewGlobalSearch( const SyntaxColorScheme& colorScheme ) : - UITreeView(), mColorScheme( colorScheme ) { - mLineNumColor = Color::fromString( - mUISceneNode->getRoot()->getUIStyle()->getVariable( "--font-hint" ).getValue() ); - } - - UIWidget* createCell( UIWidget* rowWidget, const ModelIndex& index ) { - UITableCell* widget = index.column() == (Int64)getModel()->treeColumn() - ? UITreeViewCellGlobalSearch::New() - : UITableCell::New(); - return setupCell( widget, rowWidget, index ); - } - - const SyntaxColorScheme& getColorScheme() const { return mColorScheme; } - - const Color& getLineNumColor() const { return mLineNumColor; } - - void updateColorScheme( const SyntaxColorScheme& colorScheme ) { mColorScheme = colorScheme; } - - protected: - Color mLineNumColor; - SyntaxColorScheme mColorScheme; -}; - -UIPushButton* UITreeViewCellGlobalSearch::updateText( const String& text ) { - if ( getCurIndex().internalId() != -1 ) { - UITreeViewGlobalSearch* pp = getParent()->getParent()->asType(); - - ProjectSearch::ResultData* res = (ProjectSearch::ResultData*)getCurIndex().parent().data(); - - auto styleDef = SyntaxDefinitionManager::instance()->getStyleByExtension( res->file ); - - auto tokens = - SyntaxTokenizer::tokenize( styleDef, text, SYNTAX_TOKENIZER_STATE_NONE ).first; - - size_t start = 0; - for ( auto& token : tokens ) { - mTextBox->setFontFillColor( pp->getColorScheme().getSyntaxStyle( token.type ).color, - start, start + token.text.size() ); - start += token.text.size(); - } - - Uint32 from = text.find_first_not_of( ' ' ); - if ( from != String::InvalidPos ) - mTextBox->setFontFillColor( pp->getLineNumColor(), from, - text.find_first_of( ' ', from ) ); - } - return this; -} - -UIPushButton* UITreeViewCellGlobalSearch::setText( const String& text ) { - if ( text != mTextBox->getText() ) { - mTextBox->setVisible( !text.empty() ); - mTextBox->setText( text ); - updateText( text ); - updateLayout(); - } - return this; -} - void App::initGlobalSearchBar() { auto addClickListener = [&]( UIWidget* widget, std::string cmd ) { widget->addEventListener( Event::MouseClick, [this, cmd]( const Event* event ) { @@ -793,8 +777,9 @@ void App::initGlobalSearchBar() { eePRINTL( "Global search for \"%s\" took %.2fms", search.c_str(), clock->getElapsedTime().asMilliseconds() ); eeDelete( clock ); - mUISceneNode->runOnMainThread( [&, loader, res] { + mUISceneNode->runOnMainThread( [&, loader, res, search] { updateGlobalSearchBar(); + mGlobalSearchTree->setSearchStr( search ); mGlobalSearchTree->setModel( ProjectSearch::asModel( res ) ); if ( mGlobalSearchTree->getModel()->rowCount() < 50 ) mGlobalSearchTree->expandAll(); @@ -1636,8 +1621,7 @@ void App::onCodeEditorFocusChange( UICodeEditor* editor ) { void App::onColorSchemeChanged( const std::string& ) { updateColorSchemeMenu(); - mGlobalSearchTree->asType()->updateColorScheme( - mEditorSplitter->getCurrentColorScheme() ); + mGlobalSearchTree->updateColorScheme( mEditorSplitter->getCurrentColorScheme() ); } void App::onDocumentLoaded( UICodeEditor* codeEditor, const std::string& path ) { @@ -1685,6 +1669,7 @@ std::map App::getLocalKeybindings() { {{KEY_F, KEYMOD_CTRL | KEYMOD_SHIFT}, "open-global-search"}, {{KEY_L, KEYMOD_CTRL}, "go-to-line"}, {{KEY_M, KEYMOD_CTRL}, "menu-toggle"}, + {{KEY_S, KEYMOD_CTRL | KEYMOD_SHIFT}, "save-all"}, }; } @@ -1723,7 +1708,8 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { editor->addKeyBinds( getLocalKeybindings() ); editor->addUnlockedCommands( getUnlockedCommands() ); doc.setCommand( "save-doc", [&] { saveDoc(); } ); - doc.setCommand( "save-as-doc", [&] { saveFileDialog(); } ); + doc.setCommand( "save-as-doc", [&] { saveFileDialog( mEditorSplitter->getCurEditor() ); } ); + doc.setCommand( "save-all", [&] { saveAll(); } ); doc.setCommand( "find-replace", [&] { showFindView(); } ); doc.setCommand( "open-global-search", [&] { showGlobalSearch(); } ); doc.setCommand( "open-locatebar", [&] { showLocateBar(); } ); @@ -1875,6 +1861,7 @@ void App::createSettingsMenu() { mSettingsMenu->addSeparator(); mSettingsMenu->add( "Save", findIcon( "document-save" ), getKeybind( "save-doc" ) ); mSettingsMenu->add( "Save as...", findIcon( "document-save-as" ), getKeybind( "save-as-doc" ) ); + mSettingsMenu->add( "Save All", findIcon( "document-save-as" ), getKeybind( "save-all" ) ); mSettingsMenu->addSeparator(); mSettingsMenu->addSubMenu( "Filetype", nullptr, createFiletypeMenu() ); mSettingsMenu->addSubMenu( "Color Scheme", nullptr, createColorSchemeMenu() ); @@ -1904,6 +1891,8 @@ void App::createSettingsMenu() { runCommand( "save-doc" ); } else if ( name == "Save as..." ) { runCommand( "save-as-doc" ); + } else if ( name == "Save All" ) { + runCommand( "save-all" ); } else if ( name == "Close" ) { runCommand( "close-doc" ); } else if ( name == "Quit" ) { @@ -2073,16 +2062,6 @@ void App::loadFolder( const std::string& path ) { mEditorSplitter->getCurEditor()->setFocus(); } -static bool isRelativePath( const std::string& path ) { - if ( !path.empty() ) { - if ( path[0] == '/' ) - return false; - if ( path.size() >= 2 && String::isLetter( path[0] ) && path[1] == ':' ) - return false; - } - return true; -} - FontTrueType* App::loadFont( const std::string& name, std::string fontPath, const std::string& fallback ) { if ( fontPath.empty() ) diff --git a/src/tools/codeeditor/codeeditor.hpp b/src/tools/codeeditor/codeeditor.hpp index 56a524b18..daf714d31 100644 --- a/src/tools/codeeditor/codeeditor.hpp +++ b/src/tools/codeeditor/codeeditor.hpp @@ -2,28 +2,29 @@ #define EE_TOOLS_CODEEDITOR_HPP #include "projectdirectorytree.hpp" +#include "uitreeviewglobalsearch.hpp" #include -class UISearchBar : public UILinearLayout { +class WidgetCommandExecuter { public: typedef std::function CommandCallback; - static UISearchBar* New() { return eeNew( UISearchBar, () ); } - UISearchBar() : - UILinearLayout( "searchbar", UIOrientation::Horizontal ), - mKeyBindings( getUISceneNode()->getWindow()->getInput() ) {} - public: + WidgetCommandExecuter( const KeyBindings& keybindings ) : mKeyBindings( keybindings ) {} + void addCommand( const std::string& name, const CommandCallback& cb ) { mCommands[name] = cb; } + void execute( const std::string& command ) { auto cmdIt = mCommands.find( command ); if ( cmdIt != mCommands.end() ) cmdIt->second(); } + KeyBindings& getKeyBindings() { return mKeyBindings; } protected: KeyBindings mKeyBindings; std::unordered_map> mCommands; + Uint32 onKeyDown( const KeyEvent& event ) { std::string cmd = mKeyBindings.getCommandFromKeyBind( {event.getKeyCode(), event.getMod()} ); @@ -38,71 +39,41 @@ class UISearchBar : public UILinearLayout { } }; -class UILocateBar : public UILinearLayout { +class UISearchBar : public UILinearLayout, public WidgetCommandExecuter { + public: + static UISearchBar* New() { return eeNew( UISearchBar, () ); } + + UISearchBar() : + UILinearLayout( "searchbar", UIOrientation::Horizontal ), + WidgetCommandExecuter( getUISceneNode()->getWindow()->getInput() ) {} + + virtual Uint32 onKeyDown( const KeyEvent& event ) { + return WidgetCommandExecuter::onKeyDown( event ); + } +}; + +class UILocateBar : public UILinearLayout, public WidgetCommandExecuter { public: - typedef std::function CommandCallback; static UILocateBar* New() { return eeNew( UILocateBar, () ); } UILocateBar() : UILinearLayout( "locatebar", UIOrientation::Horizontal ), - mKeyBindings( getUISceneNode()->getWindow()->getInput() ) {} + WidgetCommandExecuter( getUISceneNode()->getWindow()->getInput() ) {} - public: - void addCommand( const std::string& name, const CommandCallback& cb ) { mCommands[name] = cb; } - void execute( const std::string& command ) { - auto cmdIt = mCommands.find( command ); - if ( cmdIt != mCommands.end() ) - cmdIt->second(); - } - KeyBindings& getKeyBindings() { return mKeyBindings; } - - protected: - KeyBindings mKeyBindings; - std::unordered_map> mCommands; - Uint32 onKeyDown( const KeyEvent& event ) { - std::string cmd = - mKeyBindings.getCommandFromKeyBind( {event.getKeyCode(), event.getMod()} ); - if ( !cmd.empty() ) { - auto cmdIt = mCommands.find( cmd ); - if ( cmdIt != mCommands.end() ) { - cmdIt->second(); - return 1; - } - } - return 0; + virtual Uint32 onKeyDown( const KeyEvent& event ) { + return WidgetCommandExecuter::onKeyDown( event ); } }; -class UIGlobalSearchBar : public UILinearLayout { +class UIGlobalSearchBar : public UILinearLayout, public WidgetCommandExecuter { public: - typedef std::function CommandCallback; static UIGlobalSearchBar* New() { return eeNew( UIGlobalSearchBar, () ); } + UIGlobalSearchBar() : UILinearLayout( "globalsearchbar", UIOrientation::Vertical ), - mKeyBindings( getUISceneNode()->getWindow()->getInput() ) {} + WidgetCommandExecuter( getUISceneNode()->getWindow()->getInput() ) {} - public: - void addCommand( const std::string& name, const CommandCallback& cb ) { mCommands[name] = cb; } - void execute( const std::string& command ) { - auto cmdIt = mCommands.find( command ); - if ( cmdIt != mCommands.end() ) - cmdIt->second(); - } - KeyBindings& getKeyBindings() { return mKeyBindings; } - - protected: - KeyBindings mKeyBindings; - std::unordered_map> mCommands; - Uint32 onKeyDown( const KeyEvent& event ) { - std::string cmd = - mKeyBindings.getCommandFromKeyBind( {event.getKeyCode(), event.getMod()} ); - if ( !cmd.empty() ) { - auto cmdIt = mCommands.find( cmd ); - if ( cmdIt != mCommands.end() ) { - cmdIt->second(); - return 1; - } - } - return 0; + virtual Uint32 onKeyDown( const KeyEvent& event ) { + return WidgetCommandExecuter::onKeyDown( event ); } }; @@ -182,7 +153,7 @@ class App : public UICodeEditorSplitter::Client { void openFontDialog( std::string& fontPath ); - void saveFileDialog(); + UIFileDialog* saveFileDialog( UICodeEditor* editor, bool focusOnClose = true ); bool findPrevText( SearchState& search ); @@ -214,6 +185,8 @@ class App : public UICodeEditorSplitter::Client { std::vector getUnlockedCommands(); + void saveAll(); + protected: EE::Window::Window* mWindow{nullptr}; UISceneNode* mUISceneNode{nullptr}; @@ -257,10 +230,13 @@ class App : public UICodeEditorSplitter::Client { UITreeView* mProjectTreeView{nullptr}; UITableView* mLocateTable{nullptr}; UITextInput* mLocateInput{nullptr}; - UITreeView* mGlobalSearchTree{nullptr}; + UITreeViewGlobalSearch* mGlobalSearchTree{nullptr}; UITextInput* mGlobalSearchInput; size_t mMenuIconSize; bool mDirTreeReady{false}; + std::unordered_set mTmpDocs; + + void saveAllProcess(); void initLocateBar(); @@ -276,6 +252,8 @@ class App : public UICodeEditorSplitter::Client { void updateEditorTitle( UICodeEditor* editor ); + void updateEditorTabTitle( UICodeEditor* editor ); + std::string titleFromEditor( UICodeEditor* editor ); bool tryTabClose( UICodeEditor* editor ); diff --git a/src/tools/codeeditor/ignorematcher.cpp b/src/tools/codeeditor/ignorematcher.cpp index ad82e5efb..af63b556a 100644 --- a/src/tools/codeeditor/ignorematcher.cpp +++ b/src/tools/codeeditor/ignorematcher.cpp @@ -177,7 +177,7 @@ bool GitIgnoreMatcher::parse() { return !mPatterns.empty(); } -bool GitIgnoreMatcher::match( const std::string& value ) { +bool GitIgnoreMatcher::match( const std::string& value ) const { for ( size_t i = 0; i < mPatterns.size(); i++ ) { if ( gitignore_glob_match( value, mPatterns[i].first ) ) { if ( mHasNegates ) { @@ -204,7 +204,12 @@ bool IgnoreMatcherManager::foundMatch() const { return mMatcher != nullptr; } -bool IgnoreMatcherManager::match( const std::string& value ) { +bool IgnoreMatcherManager::match( const std::string& value ) const { eeASSERT( foundMatch() ); return mMatcher->match( value ); } + +const std::string& IgnoreMatcherManager::getPath() const { + eeASSERT( foundMatch() ); + return mMatcher->getPath(); +} diff --git a/src/tools/codeeditor/ignorematcher.hpp b/src/tools/codeeditor/ignorematcher.hpp index a1d008e2a..f789637bd 100644 --- a/src/tools/codeeditor/ignorematcher.hpp +++ b/src/tools/codeeditor/ignorematcher.hpp @@ -16,7 +16,9 @@ class IgnoreMatcher { virtual bool canMatch() = 0; - virtual bool match( const std::string& value ) = 0; + virtual bool match( const std::string& value ) const = 0; + + const std::string& getPath() const { return mPath; } protected: std::string mPath; @@ -30,7 +32,7 @@ class GitIgnoreMatcher : public IgnoreMatcher { bool canMatch() override; - bool match( const std::string& value ) override; + bool match( const std::string& value ) const override; protected: bool parse() override; @@ -45,7 +47,9 @@ class IgnoreMatcherManager { bool foundMatch() const; - bool match( const std::string& value ); + bool match( const std::string& value ) const; + + const std::string& getPath() const; protected: std::unique_ptr mMatcher; diff --git a/src/tools/codeeditor/projectdirectorytree.cpp b/src/tools/codeeditor/projectdirectorytree.cpp index 751f997f8..3bbff640c 100644 --- a/src/tools/codeeditor/projectdirectorytree.cpp +++ b/src/tools/codeeditor/projectdirectorytree.cpp @@ -24,7 +24,7 @@ void ProjectDirectoryTree::scan( const ProjectDirectoryTree::ScanCompleteEvent& for ( auto& strPattern : acceptedPattern ) patterns.emplace_back( LuaPattern( strPattern ) ); std::set info; - getDirectoryFiles( files, names, mPath, info, ignoreHidden ); + getDirectoryFiles( files, names, mPath, info, ignoreHidden, mIgnoreMatcher ); size_t namesCount = names.size(); bool found; for ( size_t i = 0; i < namesCount; i++ ) { @@ -42,7 +42,7 @@ void ProjectDirectoryTree::scan( const ProjectDirectoryTree::ScanCompleteEvent& } } else { std::set info; - getDirectoryFiles( mFiles, mNames, mPath, info, ignoreHidden ); + getDirectoryFiles( mFiles, mNames, mPath, info, ignoreHidden, mIgnoreMatcher ); } mIsReady = true; #if EE_PLATFORM == EE_PLATFORM_EMSCRIPTEN @@ -120,15 +120,17 @@ void ProjectDirectoryTree::getDirectoryFiles( std::vector& files, std::vector& names, std::string directory, std::set currentDirs, - const bool& ignoreHidden ) { + const bool& ignoreHidden, + const IgnoreMatcherManager& ignoreMatcher ) { currentDirs.insert( directory ); - std::string localDirPath( directory.substr( mPath.size() ) ); + std::string localDirPath( directory.substr( + ignoreMatcher.foundMatch() ? ignoreMatcher.getPath().size() : mPath.size() ) ); std::vector pathFiles = FileSystem::filesGetInPath( directory, false, false, ignoreHidden ); for ( auto& file : pathFiles ) { std::string fullpath( directory + file ); std::string localpath( localDirPath + file ); - if ( mIgnoreMatcher.foundMatch() && mIgnoreMatcher.match( localpath ) ) + if ( ignoreMatcher.foundMatch() && ignoreMatcher.match( localpath ) ) continue; if ( FileSystem::isDirectory( fullpath ) ) { fullpath += FileSystem::getOSSlash(); @@ -139,7 +141,9 @@ void ProjectDirectoryTree::getDirectoryFiles( std::vector& files, if ( currentDirs.find( fullpath ) == currentDirs.end() ) continue; } - getDirectoryFiles( files, names, fullpath, currentDirs, ignoreHidden ); + IgnoreMatcherManager dirMatcher( fullpath ); + getDirectoryFiles( files, names, fullpath, currentDirs, ignoreHidden, + dirMatcher.foundMatch() ? dirMatcher : ignoreMatcher ); } else { files.emplace_back( fullpath ); names.emplace_back( file ); diff --git a/src/tools/codeeditor/projectdirectorytree.hpp b/src/tools/codeeditor/projectdirectorytree.hpp index 614c89a23..5011e4d77 100644 --- a/src/tools/codeeditor/projectdirectorytree.hpp +++ b/src/tools/codeeditor/projectdirectorytree.hpp @@ -75,7 +75,7 @@ class ProjectDirectoryTree { void getDirectoryFiles( std::vector& files, std::vector& names, std::string directory, std::set currentDirs, - const bool& ignoreHidden ); + const bool& ignoreHidden, const IgnoreMatcherManager& ignoreMatcher ); }; #endif // EE_TOOLS_PROJECTDIRECTORYTREE_HPP diff --git a/src/tools/codeeditor/uitreeviewglobalsearch.cpp b/src/tools/codeeditor/uitreeviewglobalsearch.cpp new file mode 100644 index 000000000..6e627069c --- /dev/null +++ b/src/tools/codeeditor/uitreeviewglobalsearch.cpp @@ -0,0 +1,50 @@ +#include "uitreeviewglobalsearch.hpp" + +UITreeViewGlobalSearch::UITreeViewGlobalSearch(const SyntaxColorScheme& colorScheme) : + UITreeView(), mColorScheme( colorScheme ) { + mLineNumColor = Color::fromString( + mUISceneNode->getRoot()->getUIStyle()->getVariable( "--font-hint" ).getValue() ); +} + +UIWidget* UITreeViewGlobalSearch::createCell(UIWidget* rowWidget, const ModelIndex& index) { + UITableCell* widget = index.column() == (Int64)getModel()->treeColumn() + ? UITreeViewCellGlobalSearch::New() + : UITableCell::New(); + return setupCell( widget, rowWidget, index ); +} + +UIPushButton* UITreeViewCellGlobalSearch::setText(const String& text) { + if ( text != mTextBox->getText() ) { + mTextBox->setVisible( !text.empty() ); + mTextBox->setText( text ); + updateText( text ); + updateLayout(); + } + return this; +} + +UIPushButton* UITreeViewCellGlobalSearch::updateText(const String& text) { + if ( getCurIndex().internalId() != -1 ) { + UITreeViewGlobalSearch* pp = getParent()->getParent()->asType(); + + ProjectSearch::ResultData* res = (ProjectSearch::ResultData*)getCurIndex().parent().data(); + + auto styleDef = SyntaxDefinitionManager::instance()->getStyleByExtension( res->file ); + + auto tokens = + SyntaxTokenizer::tokenize( styleDef, text, SYNTAX_TOKENIZER_STATE_NONE ).first; + + size_t start = 0; + for ( auto& token : tokens ) { + mTextBox->setFontFillColor( pp->getColorScheme().getSyntaxStyle( token.type ).color, + start, start + token.text.size() ); + start += token.text.size(); + } + + Uint32 from = text.find_first_not_of( ' ' ); + if ( from != String::InvalidPos ) + mTextBox->setFontFillColor( pp->getLineNumColor(), from, + text.find_first_of( ' ', from ) ); + } + return this; +} diff --git a/src/tools/codeeditor/uitreeviewglobalsearch.hpp b/src/tools/codeeditor/uitreeviewglobalsearch.hpp new file mode 100644 index 000000000..480b9596d --- /dev/null +++ b/src/tools/codeeditor/uitreeviewglobalsearch.hpp @@ -0,0 +1,44 @@ +#ifndef UITREEVIEWGLOBALSEARCH_HPP +#define UITREEVIEWGLOBALSEARCH_HPP + +#include +#include "projectsearch.hpp" + +class UITreeViewCellGlobalSearch : public UITreeViewCell { + public: + static UITreeViewCellGlobalSearch* New() { return eeNew( UITreeViewCellGlobalSearch, () ); } + + UITreeViewCellGlobalSearch() : UITreeViewCell() {} + + UIPushButton* setText( const String& text ); + + UIPushButton* updateText( const String& text ); +}; + +class UITreeViewGlobalSearch : public UITreeView { + public: + static UITreeViewGlobalSearch* New( const SyntaxColorScheme& colorScheme ) { + return eeNew( UITreeViewGlobalSearch, ( colorScheme ) ); + } + + UITreeViewGlobalSearch( const SyntaxColorScheme& colorScheme ); + + UIWidget* createCell( UIWidget* rowWidget, const ModelIndex& index ); + + const SyntaxColorScheme& getColorScheme() const { return mColorScheme; } + + const Color& getLineNumColor() const { return mLineNumColor; } + + void updateColorScheme( const SyntaxColorScheme& colorScheme ) { mColorScheme = colorScheme; } + + void setSearchStr( const String& searchStr ) { mSearchStr = searchStr; } + + const String& getSearchStr() const { return mSearchStr; } + + protected: + Color mLineNumColor; + SyntaxColorScheme mColorScheme; + String mSearchStr; +}; + +#endif // UITREEVIEWGLOBALSEARCH_HPP