From ff29ed3c719a3dfd55c0dbbbe9998586155f8da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 14 Mar 2024 22:57:44 -0300 Subject: [PATCH] Fixes not reloading loaded document from an file system modified event after the first reload (SpartanJ/ecode#196). Improved project search UI. --- bin/assets/ui/breeze.css | 10 ++ include/eepp/scene/event.hpp | 1 + include/eepp/ui/uiwidget.hpp | 8 ++ src/eepp/ui/uicheckbox.cpp | 2 + src/eepp/ui/uicodeeditor.cpp | 6 +- src/eepp/ui/uiradiobutton.cpp | 2 + src/eepp/ui/uiwidget.cpp | 21 ++- src/tools/ecode/applayout.xml.hpp | 8 +- src/tools/ecode/ecode.cpp | 12 +- src/tools/ecode/globalsearchcontroller.cpp | 32 +++++ src/tools/ecode/projectsearch.cpp | 145 +++++++++++---------- 11 files changed, 168 insertions(+), 79 deletions(-) diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index 16c99f5e0..bbaf767f3 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -206,6 +206,16 @@ ListView::cell::icon { margin-right: 4dp; } +CheckBox, +RadioButton { + text-decoration: none; +} + +CheckBox:focus, +RadioButton:focus { + text-decoration: underline; +} + CheckBox::active, CheckBox::inactive { width: 12dp; diff --git a/include/eepp/scene/event.hpp b/include/eepp/scene/event.hpp index 5650038ae..7669add13 100644 --- a/include/eepp/scene/event.hpp +++ b/include/eepp/scene/event.hpp @@ -50,6 +50,7 @@ class EE_API Event { OnDocumentLoaded, OnDocumentChanged, OnDocumentClosed, + OnDocumentReloaded, OnDocumentSyntaxDefinitionChange, OnDocumentDirtyOnFileSysten, OnFontStyleChanged, diff --git a/include/eepp/ui/uiwidget.hpp b/include/eepp/ui/uiwidget.hpp index dfaa5b5c2..a35eaba20 100644 --- a/include/eepp/ui/uiwidget.hpp +++ b/include/eepp/ui/uiwidget.hpp @@ -251,6 +251,14 @@ class EE_API UIWidget : public UINode { void setTabStop(); + void unsetTabStop(); + + bool isTabFocusable() const; + + void setTabFocusable(); + + void unsetTabFocusable(); + UIWidget* getPrevTabWidget() const; UIWidget* getNextTabWidget() const; diff --git a/src/eepp/ui/uicheckbox.cpp b/src/eepp/ui/uicheckbox.cpp index b6b2472dc..891f3f895 100644 --- a/src/eepp/ui/uicheckbox.cpp +++ b/src/eepp/ui/uicheckbox.cpp @@ -26,6 +26,7 @@ UICheckBox::UICheckBox( const std::string& tag ) : mActiveButton->setPosition( 0, 0 ); mActiveButton->setSize( 8, 8 ); mActiveButton->addEventListener( Event::OnSizeChange, cb ); + mActiveButton->unsetTabFocusable(); mInactiveButton = UIWidget::NewWithTag( tag + "::inactive" ); mInactiveButton->setVisible( true ); @@ -34,6 +35,7 @@ UICheckBox::UICheckBox( const std::string& tag ) : mInactiveButton->setPosition( 0, 0 ); mInactiveButton->setSize( 8, 8 ); mInactiveButton->addEventListener( Event::OnSizeChange, cb ); + mInactiveButton->unsetTabFocusable(); onPaddingChange(); diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index f8796d543..35c3d0b9e 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -579,8 +579,10 @@ void UICodeEditor::onFontStyleChanged() { void UICodeEditor::onDocumentLoaded( TextDocument* ) {} void UICodeEditor::onDocumentReloaded( TextDocument* ) { - onDocumentClosed( mDoc.get() ); - onDocumentLoaded(); + DocEvent event( this, mDoc.get(), Event::OnDocumentReloaded ); + sendEvent( &event ); + invalidateDraw(); + invalidateLongestLineWidth(); } void UICodeEditor::onDocumentLoaded() { diff --git a/src/eepp/ui/uiradiobutton.cpp b/src/eepp/ui/uiradiobutton.cpp index d9bfb8e87..7610ad481 100644 --- a/src/eepp/ui/uiradiobutton.cpp +++ b/src/eepp/ui/uiradiobutton.cpp @@ -24,6 +24,7 @@ UIRadioButton::UIRadioButton() : mActiveButton->setPosition( 0, 0 ); mActiveButton->setSize( 8, 8 ); mActiveButton->addEventListener( Event::OnSizeChange, cb ); + mActiveButton->unsetTabFocusable(); mInactiveButton = UIWidget::NewWithTag( "radiobutton::inactive" ); mInactiveButton->setVisible( true ); @@ -32,6 +33,7 @@ UIRadioButton::UIRadioButton() : mInactiveButton->setPosition( 0, 0 ); mInactiveButton->setSize( 9, 8 ); mInactiveButton->addEventListener( Event::OnSizeChange, cb ); + mInactiveButton->unsetTabFocusable(); onPaddingChange(); diff --git a/src/eepp/ui/uiwidget.cpp b/src/eepp/ui/uiwidget.cpp index 44b57f4c5..fcb753b1f 100644 --- a/src/eepp/ui/uiwidget.cpp +++ b/src/eepp/ui/uiwidget.cpp @@ -1369,7 +1369,8 @@ std::vector UIWidget::getPropertiesImplemented() const { PropertyId::BorderBottomLeftRadius, PropertyId::BorderBottomRightRadius, PropertyId::BorderSmooth, - PropertyId::BackgroundSmooth }; + PropertyId::BackgroundSmooth, + PropertyId::Focusable }; } std::string UIWidget::getPropertyString( const std::string& property ) const { @@ -1522,6 +1523,8 @@ std::string UIWidget::getPropertyString( const PropertyDefinition* propertyDef, return mBackground ? ( mBackground->getBackgroundDrawable().isSmooth() ? "true" : "false" ) : "false"; + case PropertyId::Focusable: + return isTabFocusable() ? "true" : "false"; default: break; } @@ -2063,6 +2066,22 @@ void UIWidget::setTabStop() { mFlags |= UI_TAB_STOP; } +void UIWidget::unsetTabStop() { + mFlags &= ~UI_TAB_STOP; +} + +bool UIWidget::isTabFocusable() const { + return ( mFlags & UI_TAB_FOCUSABLE ) != 0; +} + +void UIWidget::setTabFocusable() { + mFlags |= UI_TAB_FOCUSABLE; +} + +void UIWidget::unsetTabFocusable() { + mFlags &= ~UI_TAB_FOCUSABLE; +} + UIWidget* UIWidget::getPrevWidget() const { UIWidget* found = NULL; UIWidget* possible = NULL; diff --git a/src/tools/ecode/applayout.xml.hpp b/src/tools/ecode/applayout.xml.hpp index 3e66626ac..51e4ff723 100644 --- a/src/tools/ecode/applayout.xml.hpp +++ b/src/tools/ecode/applayout.xml.hpp @@ -504,12 +504,12 @@ R"html( - + - + - + @@ -517,7 +517,7 @@ R"html( tooltip='@string(escape_sequence_tooltip, "Replace \\, \t, \n, \r and \uXXXX (Unicode characters) with the corresponding control")' /> - + diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 87faa14bd..9e6b87857 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -733,7 +733,8 @@ App::~App() { delete mFileWatcher; mFileWatcher = nullptr; } - mDirTree->resetPluginManager(); + if ( mDirTree ) + mDirTree->resetPluginManager(); mPluginManager.reset(); eeSAFE_DELETE( mSplitter ); @@ -1948,10 +1949,13 @@ void App::onRealDocumentLoaded( UICodeEditor* editor, const std::string& path ) } TextDocument& doc = editor->getDocument(); + std::string filePath = + doc.hasFilepath() ? doc.getFilePath() + : ( !doc.getLoadingFilePath().empty() ? doc.getLoadingFilePath() : "" ); - if ( mFileWatcher && doc.hasFilepath() && - ( !mDirTree || !mDirTree->isDirInTree( doc.getFileInfo().getFilepath() ) ) ) { - std::string dir( FileSystem::fileRemoveFileName( doc.getFileInfo().getFilepath() ) ); + if ( mFileWatcher && !filePath.empty() && + ( !mDirTree || !mDirTree->isDirInTree( filePath ) ) ) { + std::string dir( FileSystem::fileRemoveFileName( filePath ) ); mThreadPool->run( [this, dir] { if ( mFileWatcher && !dirInFolderWatches( dir ) ) { auto watchId = mFileWatcher->addWatch( dir, mFileSystemListener ); diff --git a/src/tools/ecode/globalsearchcontroller.cpp b/src/tools/ecode/globalsearchcontroller.cpp index 7879b4419..1118a61c4 100644 --- a/src/tools/ecode/globalsearchcontroller.cpp +++ b/src/tools/ecode/globalsearchcontroller.cpp @@ -232,6 +232,8 @@ void GlobalSearchController::initGlobalSearchBar( }; mGlobalSearchInput->on( Event::OnPressEnter, pressEnterCb ); mGlobalSearchWhereInput->on( Event::OnPressEnter, pressEnterCb ); + mGlobalSearchWhereInput->on( Event::OnTabNavigate, + [this]( auto ) { mGlobalSearchInput->setFocus(); } ); auto switchInputToTree = [this]( const Event* event ) { const KeyEvent* keyEvent = static_cast( event ); Uint32 keyCode = keyEvent->getKeyCode(); @@ -358,6 +360,36 @@ void GlobalSearchController::initGlobalSearchBar( keyEvent->getKeyCode() == KEY_RETURN ) mGlobalSearchBarLayout->execute( "replace-in-files" ); } ); + + UIWidget* menuBtn = + mGlobalSearchBarLayout->find( "global_search_filters_menu_button" ); + menuBtn->onClick( [this, menuBtn]( auto ) { + UIPopUpMenu* menu = UIPopUpMenu::New(); + menu->add( mApp->i18n( "add_include_filter", "Add Include Filter" ) ) + ->setId( "add-include-filter" ); + menu->add( mApp->i18n( "add_exclude_filter", "Add Exclude Filter" ) ) + ->setId( "add-exclude-filter" ); + menu->on( Event::OnItemClicked, [this]( const Event* event ) { + const auto& id = event->getNode()->getId(); + + String appendTxt = ""; + bool addComma = !mGlobalSearchWhereInput->getText().empty() && + mGlobalSearchWhereInput->getText().back() != ','; + + if ( "add-include-filter" == id ) { + appendTxt = ( addComma ? String{ "," } : String{ "" } ) + "*.txt"; + } else if ( "add-exclude-filter" == id ) { + appendTxt = ( addComma ? String{ "," } : String{ "" } ) + "-*.txt"; + } + + if ( !appendTxt.empty() ) + mGlobalSearchWhereInput->setText( mGlobalSearchWhereInput->getText() + appendTxt ); + } ); + + menu->showAtScreenPosition( menuBtn->convertToWorldSpace( { 0, 0 } ) ); + menu->setFocus(); + } ); + mGlobalSearchTree = mGlobalSearchTreeSearch; } diff --git a/src/tools/ecode/projectsearch.cpp b/src/tools/ecode/projectsearch.cpp index 3d92c30ff..9a8cc5f34 100644 --- a/src/tools/ecode/projectsearch.cpp +++ b/src/tools/ecode/projectsearch.cpp @@ -153,8 +153,12 @@ void ProjectSearch::find( const std::vector files, const std::strin : std::vector(); for ( auto& file : files ) { bool skip = false; + std::string_view fsv( file ); + if ( !basePath.empty() && String::startsWith( file, basePath ) ) + fsv = fsv.substr( basePath.size() ); + for ( const auto& filter : pathFilters ) { - bool matches = String::globMatch( file, filter.first ); + bool matches = String::globMatch( fsv, filter.first ); if ( ( matches && filter.second ) || ( !matches && !filter.second ) ) { skip = true; break; @@ -186,82 +190,87 @@ void ProjectSearch::find( const std::vector files, std::string stri if ( files.empty() ) result( {} ); FileSystem::dirAddSlashAtEnd( basePath ); - FindData* findData = eeNew( FindData, () ); - findData->resCount = files.size(); - if ( !caseSensitive ) - String::toLowerInPlace( string ); - const auto occ = - type == TextDocument::FindReplaceType::Normal - ? String::BMH::createOccTable( (const unsigned char*)string.c_str(), string.size() ) - : std::vector(); - std::vector search; - search.resize( files.size() ); - size_t pos = 0; - size_t count = 0; - for ( const auto& file : files ) { - bool skip = false; - std::string_view fsv( file ); - if ( !basePath.empty() && String::startsWith( file, basePath ) ) - fsv = fsv.substr( basePath.size() ); + pool->run( [files = std::move( files ), string = std::move( string ), pool = std::move( pool ), + result = std::move( result ), caseSensitive, wholeWord, type, + pathFilters = std::move( pathFilters ), + basePath = std::move( basePath )]() mutable { + FindData* findData = eeNew( FindData, () ); + findData->resCount = files.size(); + if ( !caseSensitive ) + String::toLowerInPlace( string ); + const auto occ = + type == TextDocument::FindReplaceType::Normal + ? String::BMH::createOccTable( (const unsigned char*)string.c_str(), string.size() ) + : std::vector(); + std::vector search; + search.resize( files.size() ); + size_t pos = 0; + size_t count = 0; + for ( const auto& file : files ) { + bool skip = false; + std::string_view fsv( file ); + if ( !basePath.empty() && String::startsWith( file, basePath ) ) + fsv = fsv.substr( basePath.size() ); - for ( const auto& filter : pathFilters ) { - bool matches = String::globMatch( fsv, filter.first ); - if ( ( matches && filter.second ) || ( !matches && !filter.second ) ) { - skip = true; - break; + for ( const auto& filter : pathFilters ) { + bool matches = String::globMatch( fsv, filter.first ); + if ( ( matches && filter.second ) || ( !matches && !filter.second ) ) { + skip = true; + break; + } } + if ( skip ) { + search[pos++] = false; + continue; + } + search[pos++] = true; + count++; } - if ( skip ) { - search[pos++] = false; - continue; + + findData->resCount = count; + + if ( count == 0 ) { + result( findData->res ); + eeDelete( findData ); + return; } - search[pos++] = true; - count++; - } - findData->resCount = count; - - if ( count == 0 ) { - result( findData->res ); - eeDelete( findData ); - return; - } - - pos = 0; - for ( const auto& file : files ) { - if ( !search[pos] ) { + pos = 0; + for ( const auto& file : files ) { + if ( !search[pos] ) { + pos++; + continue; + } pos++; - continue; - } - pos++; - pool->run( - [findData, file, string, caseSensitive, wholeWord, occ, type] { - auto fileRes = - type == TextDocument::FindReplaceType::Normal - ? searchInFileHorspool( file, string, caseSensitive, wholeWord, occ ) - : searchInFileLuaPattern( file, string, caseSensitive, wholeWord ); - if ( !fileRes.empty() ) { - Lock l( findData->resMutex ); - findData->res.push_back( { file, fileRes } ); - } - }, - [result, findData]( const auto& ) { - int count; - { - Lock l( findData->countMutex ); - findData->resCount--; - count = findData->resCount; - } - if ( count == 0 ) { - result( findData->res ); - eeDelete( findData ); + pool->run( + [findData, file, string, caseSensitive, wholeWord, occ, type] { + auto fileRes = + type == TextDocument::FindReplaceType::Normal + ? searchInFileHorspool( file, string, caseSensitive, wholeWord, occ ) + : searchInFileLuaPattern( file, string, caseSensitive, wholeWord ); + if ( !fileRes.empty() ) { + Lock l( findData->resMutex ); + findData->res.push_back( { file, fileRes } ); + } + }, + [result, findData]( const auto& ) { + int count; + { + Lock l( findData->countMutex ); + findData->resCount--; + count = findData->resCount; + } + if ( count == 0 ) { + result( findData->res ); + eeDelete( findData ); #if EE_PLATFORM == EE_PLATFORM_LINUX - malloc_trim( 0 ); + malloc_trim( 0 ); #endif - } - } ); - } + } + } ); + } + } ); } void ProjectSearch::ResultModel::removeLastNewLineCharacter() {