diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index 58142c589..8bd08ed36 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -41,6 +41,8 @@ --term-back-color: #1e2127; --term-font-color: #abb2bf; --highlight-primary: #FFFFFF7A; + --disabled-color: #727679; + --disabled-border: #43474c; droppable-hovering-color: #FFFFFF20; } @@ -168,6 +170,12 @@ SelectButton:selectedpressed { background-color: var(--primary); } +pushbutton:disabled, +selectbutton:disabled { + color: var(--disabled-color); + border-color: var(--disabled-border); +} + PushButton::icon, SelectButton::icon, TableView::cell::icon, @@ -1030,6 +1038,8 @@ console { --floating-icon: #cbcdcd; --term-back-color: #eff0f1; --term-font-color: #232627; + --disabled-color: #727679; + --disabled-border: #d3d4d5; droppable-hovering-color: #00000020; } @@ -1061,6 +1071,9 @@ listview::row:selected listview::cell::icon, tableview::row:selected tableview::cell::expander, treeview::row:selected treeview::cell::expander, listview::row:selected listview::cell::expander, +tableview::row:selected tableview::cell checkbox, +treeview::row:selected treeview::cell checkbox, +listview::row:selected listview::cell checkbox, tableview::row:hover tableview::cell::text, treeview::row:hover treeview::cell::text, listview::row:hover listview::cell::text, @@ -1070,6 +1083,9 @@ listview::row:hover listview::cell::icon, tableview::row:hover tableview::cell::expander, treeview::row:hover treeview::cell::expander, listview::row:hover listview::cell::expander, +tableview::row:hover tableview::cell checkbox, +treeview::row:hover treeview::cell checkbox, +listview::row:hover listview::cell checkbox, tableview::header::column:hover, treeview::header::column:hover, listview::header::column:hover { diff --git a/src/eepp/scene/eventdispatcher.cpp b/src/eepp/scene/eventdispatcher.cpp index e36588418..54e49d401 100644 --- a/src/eepp/scene/eventdispatcher.cpp +++ b/src/eepp/scene/eventdispatcher.cpp @@ -93,17 +93,16 @@ void EventDispatcher::update( const Time& time ) { mNodeDragging->onCalculateDrag( mMousePos, mInput->getPressTrigger() ); if ( mInput->getPressTrigger() ) { - if ( NULL != mOverNode ) { - mOverNode->onMouseDown( mMousePosi, mInput->getPressTrigger() ); - sendMsg( mOverNode, NodeMessage::MouseDown, mInput->getPressTrigger() ); - } - if ( !mFirstPress ) { mDownNode = mOverNode; mMouseDownPos = mMousePosi; - mFirstPress = true; } + + if ( NULL != mOverNode ) { + mOverNode->onMouseDown( mMousePosi, mInput->getPressTrigger() ); + sendMsg( mOverNode, NodeMessage::MouseDown, mInput->getPressTrigger() ); + } } if ( mInput->getReleaseTrigger() ) { diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index bad8e5567..1f786237e 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -1787,7 +1787,14 @@ void App::updateTerminalMenu() { } void App::createPluginManagerUI() { - UIPluginManager::New( mUISceneNode, mPluginManager.get() )->showWhenReady(); + UIPluginManager::New( mUISceneNode, mPluginManager.get(), [&]( const std::string& path ) { + UITab* tab = mSplitter->isDocumentOpen( path ); + if ( !tab ) { + loadFileFromPath( path ); + } else { + tab->getTabWidget()->setTabSelected( tab ); + } + } )->showWhenReady(); } void App::updateDocumentMenu() { diff --git a/src/tools/ecode/plugins/linter/linterplugin.cpp b/src/tools/ecode/plugins/linter/linterplugin.cpp index c93965636..fc9b84305 100644 --- a/src/tools/ecode/plugins/linter/linterplugin.cpp +++ b/src/tools/ecode/plugins/linter/linterplugin.cpp @@ -50,83 +50,125 @@ LinterPlugin::~LinterPlugin() { } } -void LinterPlugin::load( const PluginManager* pluginManager ) { - std::string path( pluginManager->getResourcesPath() + "plugins/linters.json" ); - if ( FileSystem::fileExists( pluginManager->getPluginsPath() + "linters.json" ) ) - path = pluginManager->getPluginsPath() + "linters.json"; - if ( !FileSystem::fileExists( path ) ) - return; - try { - std::ifstream stream( path ); - json j; - stream >> j; +bool LinterPlugin::hasFileConfig() { + return !mConfigPath.empty(); +} - for ( auto& obj : j ) { - Linter linter; - auto fp = obj["file_patterns"]; +std::string LinterPlugin::getFileConfigPath() { + return mConfigPath; +} - for ( auto& pattern : fp ) - linter.files.push_back( pattern.get() ); - - auto wp = obj["warning_pattern"]; - - if ( wp.is_array() ) { - for ( auto& warningPattern : wp ) - linter.warningPattern.push_back( warningPattern.get() ); - } else { - linter.warningPattern = { wp.get() }; - } - - linter.command = obj["command"].get(); - - if ( obj.contains( "expected_exitcodes" ) ) { - auto ee = obj["expected_exitcodes"]; - if ( ee.is_array() ) { - for ( auto& number : ee ) - if ( number.is_number() ) - linter.expectedExitCodes.push_back( number.get() ); - } else if ( ee.is_number() ) { - linter.expectedExitCodes.push_back( ee.get() ); +size_t LinterPlugin::linterFilePatternPosition( const std::vector& patterns ) { + for ( size_t i = 0; i < mLinters.size(); ++i ) { + for ( const std::string& filePattern : mLinters[i].files ) { + for ( const std::string& pattern : patterns ) { + if ( filePattern == pattern ) { + return i; } } + } + } + return std::string::npos; +} - if ( obj.contains( "warning_pattern_order" ) ) { - auto& wpo = obj["warning_pattern_order"]; - if ( wpo.contains( "line" ) && wpo["line"].is_number() ) - linter.warningPatternOrder.line = wpo["line"].get(); - if ( wpo.contains( "col" ) && wpo["col"].is_number() ) - linter.warningPatternOrder.col = wpo["col"].get(); - if ( wpo.contains( "message" ) && wpo["message"].is_number() ) - linter.warningPatternOrder.message = wpo["message"].get(); - if ( wpo.contains( "type" ) && wpo["type"].is_number() ) - linter.warningPatternOrder.type = wpo["type"].get(); - } +void LinterPlugin::loadLinterConfig( const std::string& path ) { + std::ifstream stream( path ); + json j; + stream >> j; - if ( obj.contains( "column_starts_at_zero" ) ) - linter.columnsStartAtZero = obj["column_starts_at_zero"].get(); + for ( auto& obj : j ) { + Linter linter; + if ( !obj.contains( "file_patterns" ) || !obj.contains( "warning_pattern" ) || + !obj.contains( "command" ) ) + continue; - if ( obj.contains( "deduplicate" ) ) - linter.deduplicate = obj["deduplicate"].get(); + auto fp = obj["file_patterns"]; - if ( obj.contains( "use_tmp_folder" ) ) - linter.useTmpFolder = obj["use_tmp_folder"].get(); + for ( auto& pattern : fp ) + linter.files.push_back( pattern.get() ); - if ( obj.contains( "no_errors_exit_code" ) && - obj["no_errors_exit_code"].is_number_integer() ) { - linter.hasNoErrorsExitCode = true; - linter.noErrorsExitCode = obj["no_errors_exit_code"].get(); - } + auto wp = obj["warning_pattern"]; - mLinters.emplace_back( std::move( linter ) ); + if ( wp.is_array() ) { + for ( auto& warningPattern : wp ) + linter.warningPattern.push_back( warningPattern.get() ); + } else { + linter.warningPattern = { wp.get() }; } - mReady = true; - } catch ( json::exception& e ) { - mReady = false; - Log::error( "Parsing linter failed:\n%s", e.what() ); + linter.command = obj["command"].get(); + + if ( obj.contains( "expected_exitcodes" ) ) { + auto ee = obj["expected_exitcodes"]; + if ( ee.is_array() ) { + for ( auto& number : ee ) + if ( number.is_number() ) + linter.expectedExitCodes.push_back( number.get() ); + } else if ( ee.is_number() ) { + linter.expectedExitCodes.push_back( ee.get() ); + } + } + + if ( obj.contains( "warning_pattern_order" ) ) { + auto& wpo = obj["warning_pattern_order"]; + if ( wpo.contains( "line" ) && wpo["line"].is_number() ) + linter.warningPatternOrder.line = wpo["line"].get(); + if ( wpo.contains( "col" ) && wpo["col"].is_number() ) + linter.warningPatternOrder.col = wpo["col"].get(); + if ( wpo.contains( "message" ) && wpo["message"].is_number() ) + linter.warningPatternOrder.message = wpo["message"].get(); + if ( wpo.contains( "type" ) && wpo["type"].is_number() ) + linter.warningPatternOrder.type = wpo["type"].get(); + } + + if ( obj.contains( "column_starts_at_zero" ) ) + linter.columnsStartAtZero = obj["column_starts_at_zero"].get(); + + if ( obj.contains( "deduplicate" ) ) + linter.deduplicate = obj["deduplicate"].get(); + + if ( obj.contains( "use_tmp_folder" ) ) + linter.useTmpFolder = obj["use_tmp_folder"].get(); + + if ( obj.contains( "no_errors_exit_code" ) && + obj["no_errors_exit_code"].is_number_integer() ) { + linter.hasNoErrorsExitCode = true; + linter.noErrorsExitCode = obj["no_errors_exit_code"].get(); + } + + // If the file pattern is repeated, we will overwrite the previous linter. + // The previous linter should be the "default" linter that comes with ecode. + size_t pos = linterFilePatternPosition( linter.files ); + if ( pos != std::string::npos ) { + mLinters[pos] = linter; + } else { + mLinters.emplace_back( std::move( linter ) ); + } } } +void LinterPlugin::load( const PluginManager* pluginManager ) { + std::vector paths; + std::string path( pluginManager->getResourcesPath() + "plugins/linters.json" ); + if ( FileSystem::fileExists( path ) ) + paths.emplace_back( path ); + path = pluginManager->getPluginsPath() + "linters.json"; + if ( FileSystem::fileExists( path ) || FileSystem::fileWrite( path, "[]\n" ) ) { + mConfigPath = path; + paths.emplace_back( path ); + } + if ( paths.empty() ) + return; + for ( const auto& path : paths ) { + try { + loadLinterConfig( path ); + } catch ( json::exception& e ) { + Log::error( "Parsing linter \"%s\" failed:\n%s", path.c_str(), e.what() ); + } + } + mReady = !mLinters.empty(); +} + void LinterPlugin::onRegister( UICodeEditor* editor ) { Lock l( mDocMutex ); diff --git a/src/tools/ecode/plugins/linter/linterplugin.hpp b/src/tools/ecode/plugins/linter/linterplugin.hpp index 1911c7cae..40b90b595 100644 --- a/src/tools/ecode/plugins/linter/linterplugin.hpp +++ b/src/tools/ecode/plugins/linter/linterplugin.hpp @@ -60,6 +60,10 @@ class LinterPlugin : public UICodeEditorPlugin { std::string getDescription() { return Definition().description; } + bool hasFileConfig(); + + std::string getFileConfigPath(); + void onRegister( UICodeEditor* ); void onUnregister( UICodeEditor* ); @@ -94,6 +98,7 @@ class LinterPlugin : public UICodeEditorPlugin { std::mutex mWorkMutex; std::condition_variable mWorkerCondition; Int32 mWorkersCount{ 0 }; + std::string mConfigPath; bool mReady{ false }; bool mShuttingDown{ false }; @@ -116,6 +121,10 @@ class LinterPlugin : public UICodeEditorPlugin { void invalidateEditors( TextDocument* doc ); std::string getMatchString( const LinterType& type ); + + void loadLinterConfig( const std::string& path ); + + size_t linterFilePatternPosition( const std::vector& patterns ); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/pluginmanager.cpp b/src/tools/ecode/plugins/pluginmanager.cpp index 37a11e191..15ac7b3d9 100644 --- a/src/tools/ecode/plugins/pluginmanager.cpp +++ b/src/tools/ecode/plugins/pluginmanager.cpp @@ -1,4 +1,5 @@ #include "pluginmanager.hpp" +#include #include #include #include @@ -171,7 +172,8 @@ class UIPluginManagerTable : public UITableView { } }; -UIWindow* UIPluginManager::New( UISceneNode* sceneNode, PluginManager* manager ) { +UIWindow* UIPluginManager::New( UISceneNode* sceneNode, PluginManager* manager, + std::function loadFileCb ) { if ( !UIWidgetCreator::isWidgetRegistered( "UIPluginManagerTable" ) ) UIWidgetCreator::registerWidget( "UIPluginManagerTable", [] { return eeNew( UIPluginManagerTable, () ); } ); @@ -189,7 +191,7 @@ UIWindow* UIPluginManager::New( UISceneNode* sceneNode, PluginManager* manager ) - + @@ -199,18 +201,40 @@ UIWindow* UIPluginManager::New( UISceneNode* sceneNode, PluginManager* manager ) ->asType(); UIWidget* cont = win->getContainer(); UIPushButton* close = cont->find( "plugin-manager-close" ); + UIPushButton* prefs = cont->find( "plugin-manager-preferences" ); UIPluginManagerTable* tv = win->getContainer()->find( "plugin-manager-table" ); close->addEventListener( Event::MouseClick, [win]( const Event* event ) { - const MouseEvent* mevent = static_cast( event ); - if ( mevent->getFlags() & EE_BUTTON_LMASK ) + if ( event->asMouseEvent()->getFlags() & EE_BUTTON_LMASK ) win->closeWindow(); } ); + prefs->setText( sceneNode->i18n( "preferences", "Preferences" ) ); win->setTitle( sceneNode->i18n( "plugin_manager", "Plugin Manager" ) ); tv->setModel( PluginsModel::New( manager ) ); tv->setColumnsVisible( { PluginsModel::Title, PluginsModel::Description, PluginsModel::Version } ); tv->setAutoColumnsWidth( true ); + prefs->addEventListener( Event::MouseClick, [tv, manager, loadFileCb]( const Event* event ) { + if ( event->asMouseEvent()->getFlags() & EE_BUTTON_LMASK && + !tv->getSelection().isEmpty() ) { + const PluginDefinition* def = + manager->getDefinitionIndex( tv->getSelection().first().row() ); + if ( def == nullptr || !manager->isEnabled( def->id ) ) + return; + auto* plugin = manager->get( def->id ); + if ( !plugin->hasFileConfig() ) + return; + if ( FileSystem::fileExists( plugin->getFileConfigPath() ) ) + loadFileCb( plugin->getFileConfigPath() ); + } + } ); + tv->setOnSelection( [&, prefs, manager]( const ModelIndex& index ) { + const PluginDefinition* def = manager->getDefinitionIndex( index.row() ); + if ( def == nullptr ) + return; + prefs->setEnabled( manager->isEnabled( def->id ) && + manager->get( def->id )->hasFileConfig() ); + } ); win->center(); return win; } diff --git a/src/tools/ecode/plugins/pluginmanager.hpp b/src/tools/ecode/plugins/pluginmanager.hpp index 356603fde..13980945c 100644 --- a/src/tools/ecode/plugins/pluginmanager.hpp +++ b/src/tools/ecode/plugins/pluginmanager.hpp @@ -99,14 +99,15 @@ class PluginsModel : public Model { PluginManager* getManager() const; - protected: + protected: PluginManager* mManager; std::vector mColumnNames{ "Id", "Title", "Enabled", "Description", "Version" }; }; class UIPluginManager { public: - static UIWindow* New( UISceneNode* sceneNode, PluginManager* manager ); + static UIWindow* New( UISceneNode* sceneNode, PluginManager* manager, + std::function loadFileCb ); }; } // namespace ecode