From dd88478dd8dcfe2fdd206415125c54709b15ba7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 16 Sep 2022 02:41:03 -0300 Subject: [PATCH] ecode: IgnoreMatcherManager fixes. Implemented a new plugin manager. --- include/eepp/ui/models/itemlistmodel.hpp | 44 ++++ include/eepp/ui/uicodeeditor.hpp | 2 + include/eepp/ui/uiscenenode.hpp | 2 + projects/linux/ee.creator.user | 109 +--------- projects/linux/ee.files | 2 + src/eepp/ui/abstract/uiabstracttableview.cpp | 14 +- src/eepp/ui/uiscenenode.cpp | 4 + src/tools/ecode/appconfig.cpp | 25 ++- src/tools/ecode/appconfig.hpp | 9 +- src/tools/ecode/ecode.cpp | 159 +++++--------- src/tools/ecode/ecode.hpp | 12 +- src/tools/ecode/ignorematcher.cpp | 11 + src/tools/ecode/ignorematcher.hpp | 4 + .../autocomplete/autocompleteplugin.cpp | 15 +- .../autocomplete/autocompleteplugin.hpp | 23 ++- .../plugins/formatter/formatterplugin.cpp | 20 +- .../plugins/formatter/formatterplugin.hpp | 19 +- .../ecode/plugins/linter/linterplugin.cpp | 19 +- .../ecode/plugins/linter/linterplugin.hpp | 22 +- src/tools/ecode/plugins/pluginmanager.cpp | 194 ++++++++++++++++++ src/tools/ecode/plugins/pluginmanager.hpp | 112 ++++++++++ src/tools/ecode/terminalmanager.cpp | 1 + 22 files changed, 545 insertions(+), 277 deletions(-) create mode 100644 src/tools/ecode/plugins/pluginmanager.cpp create mode 100644 src/tools/ecode/plugins/pluginmanager.hpp diff --git a/include/eepp/ui/models/itemlistmodel.hpp b/include/eepp/ui/models/itemlistmodel.hpp index 4c5052ac2..e2c5dd1f0 100644 --- a/include/eepp/ui/models/itemlistmodel.hpp +++ b/include/eepp/ui/models/itemlistmodel.hpp @@ -35,6 +35,50 @@ template class ItemListModel final : public Model { std::vector& mData; }; +template class ItemPairListModel final : public Model { + public: + static std::shared_ptr create( std::vector>& data ) { + return std::make_shared( *new ItemPairListModel( data ) ); + } + + virtual ~ItemPairListModel() {} + + virtual size_t rowCount( const ModelIndex& ) const { return mData.size(); } + + virtual size_t columnCount( const ModelIndex& ) const { return 2; } + + virtual std::string columnName( const size_t& index ) const { + eeASSERT( index < 2 ); + return mColumnNames[index]; + } + + virtual void setColumnName( const size_t& index, const std::string& name ) { + eeASSERT( index < 2 ); + mColumnNames[index] = name; + } + + virtual Variant data( const ModelIndex& index, ModelRole role = ModelRole::Display ) const { + if ( role == ModelRole::Display ) { + switch ( index.column() ) { + case 0: + return Variant( mData[index.row()].first ); + case 1: + default: + return Variant( mData[index.row()].second ); + } + } + return {}; + } + + virtual void update() { onModelUpdate(); } + + private: + explicit ItemPairListModel( std::vector& data ) : mData( data ) {} + + std::vector>& mData; + std::vector mColumnNames{ "Title", "Description" }; +}; + }}} // namespace EE::UI::Models #endif // EE_UI_MODELS_ITEMLISTMODEL_HPP diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 7589dff75..20a95999c 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -31,12 +31,14 @@ class UIMenuItem; class UICodeEditorPlugin { public: + virtual std::string getId() = 0; virtual std::string getTitle() = 0; virtual std::string getDescription() = 0; virtual bool hasGUIConfig() { return false; } virtual bool hasFileConfig() { return false; } virtual UIWindow* getGUIConfig() { return nullptr; } virtual std::string getFileConfigPath() { return ""; } + virtual ~UICodeEditorPlugin() {} virtual void onRegister( UICodeEditor* ) = 0; virtual void onUnregister( UICodeEditor* ) = 0; diff --git a/include/eepp/ui/uiscenenode.hpp b/include/eepp/ui/uiscenenode.hpp index 76c02df73..a23c97ada 100644 --- a/include/eepp/ui/uiscenenode.hpp +++ b/include/eepp/ui/uiscenenode.hpp @@ -56,6 +56,8 @@ class EE_API UISceneNode : public SceneNode { String getTranslatorStringFromKey( const std::string& key, const String& defaultValue ); + String i18n( const std::string& key, const String& defaultValue ); + UIWidget* loadLayoutNodes( pugi::xml_node node, Node* parent, const Uint32& marker ); UIWidget* loadLayoutFromFile( const std::string& layoutPath, Node* parent = NULL, diff --git a/projects/linux/ee.creator.user b/projects/linux/ee.creator.user index 5ff483d7f..e9d89a186 100644 --- a/projects/linux/ee.creator.user +++ b/projects/linux/ee.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -109,7 +109,7 @@ {388e5431-b31b-42b3-b9ad-9002d279d75d} 10 0 - 15 + 19 ../../make/linux @@ -1105,7 +1105,7 @@ eepp-UIEditor-debug ProjectExplorer.CustomExecutableRunConfiguration - --xml=/home/downloads/temp/eepp/search_box.xml -u + -u true false false @@ -1203,6 +1203,7 @@ ecode-debug ProjectExplorer.CustomExecutableRunConfiguration + ../ true false false @@ -1429,109 +1430,9 @@ 21 - - ProjectExplorer.Project.Target.1 - - Desktop - Replacement for "Desktop" - Replacement for "Desktop" - {6d057187-158a-4883-8d5b-d470a6b6b025} - 0 - 0 - 0 - - /home/programming/eepp/projects/linux - - - - all - - true - GenericProjectManager.GenericMakeStep - - 1 - Build - Build - ProjectExplorer.BuildSteps.Build - - - - - clean - - true - GenericProjectManager.GenericMakeStep - - 1 - Clean - Clean - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Default - GenericProjectManager.GenericBuildConfiguration - - 1 - - - 0 - Deploy - Deploy - ProjectExplorer.BuildSteps.Deploy - - 1 - - false - ProjectExplorer.DefaultDeployConfiguration - - 1 - - true - true - false - /usr/bin/kcachegrind - 2 - 2 - true - true - /usr/bin/valgrind - - 11 - 14 - 12 - 13 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 0 - 1 - - - 2 - - ProjectExplorer.CustomExecutableRunConfiguration - - false - true - false - true - - 1 - - ProjectExplorer.Project.TargetCount - 2 + 1 ProjectExplorer.Project.Updater.FileVersion diff --git a/projects/linux/ee.files b/projects/linux/ee.files index fa388cd5a..eb5f3e784 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -1161,6 +1161,8 @@ ../../src/tools/ecode/notificationcenter.hpp ../../src/tools/ecode/plugins/linter/linterplugin.cpp ../../src/tools/ecode/plugins/linter/linterplugin.hpp +../../src/tools/ecode/plugins/pluginmanager.cpp +../../src/tools/ecode/plugins/pluginmanager.hpp ../../src/tools/ecode/projectdirectorytree.cpp ../../src/tools/ecode/projectdirectorytree.hpp ../../src/tools/ecode/projectsearch.cpp diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index 2056c195b..825c9a64a 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -130,8 +130,7 @@ void UIAbstractTableView::createOrUpdateColumns() { for ( size_t col = 0; col < count; col++ ) { if ( col != mMainColumn && !isColumnHidden( col ) ) { Float colWidth = getMaxColumnContentWidth( col, true ); - if ( colWidth == 0 ) - colWidth = columnData( col ).widget->getPixelsSize().getWidth(); + colWidth = eemax( colWidth, columnData( col ).widget->getPixelsSize().getWidth() ); usedWidth += colWidth; columnData( col ).width = colWidth; } @@ -336,8 +335,17 @@ void UIAbstractTableView::setColumnsVisible( const std::vector columns ) return; for ( size_t i = 0; i < getModel()->columnCount(); i++ ) columnData( i ).visible = false; - for ( auto col : columns ) + + bool foundMainColumn = false; + for ( auto col : columns ) { columnData( col ).visible = true; + if ( col == mMainColumn ) + foundMainColumn = true; + } + + if ( !foundMainColumn && !columns.empty() ) + mMainColumn = columns[0]; + createOrUpdateColumns(); } diff --git a/src/eepp/ui/uiscenenode.cpp b/src/eepp/ui/uiscenenode.cpp index 162ed349f..1d4f76246 100644 --- a/src/eepp/ui/uiscenenode.cpp +++ b/src/eepp/ui/uiscenenode.cpp @@ -193,6 +193,10 @@ String UISceneNode::getTranslatorStringFromKey( const std::string& key, return defaultValue; } +String UISceneNode::i18n( const std::string& key, const String& defaultValue ) { + return getTranslatorStringFromKey( key, defaultValue ); +} + void UISceneNode::setFocusLastWindow( UIWindow* window ) { if ( NULL == mParentNode && NULL != mEventDispatcher && !mWindowsList.empty() && window != mWindowsList.front() ) { diff --git a/src/tools/ecode/appconfig.cpp b/src/tools/ecode/appconfig.cpp index c152fd3cf..468fdb4eb 100644 --- a/src/tools/ecode/appconfig.cpp +++ b/src/tools/ecode/appconfig.cpp @@ -1,5 +1,6 @@ #include "appconfig.hpp" #include "ecode.hpp" +#include "plugins/pluginmanager.hpp" #include "thirdparty/json.hpp" #include #include @@ -42,7 +43,7 @@ static std::vector urlDecode( const std::vector& vec ) void AppConfig::load( const std::string& confPath, std::string& keybindingsPath, std::string& initColorScheme, std::vector& recentFiles, std::vector& recentFolders, const std::string& resPath, - const Float& displayDPI ) { + const Float& displayDPI, PluginManager* pluginManager ) { keybindingsPath = confPath + "keybindings.cfg"; ini.loadFromFile( confPath + "config.cfg" ); iniState.loadFromFile( confPath + "state.cfg" ); @@ -95,9 +96,6 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath, editor.colorPickerSelection = ini.getValueB( "editor", "color_picker_selection", true ); editor.colorPreview = ini.getValueB( "editor", "color_preview", true ); editor.minimap = ini.getValueB( "editor", "minimap", true ); - editor.autoComplete = ini.getValueB( "editor", "auto_complete", true ); - editor.linter = ini.getValueB( "editor", "linter", true ); - editor.formatter = ini.getValueB( "editor", "formatter", true ); editor.showDocInfo = ini.getValueB( "editor", "show_doc_info", true ); editor.hideTabBarOnSingleTab = ini.getValueB( "editor", "hide_tab_bar_on_single_tab", true ); editor.singleClickTreeNavigation = @@ -121,6 +119,15 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath, term.fontSize = ini.getValue( "terminal", "font_size", "11dp" ); term.colorScheme = ini.getValue( "terminal", "colorscheme", "eterm" ); + std::map pluginsEnabled; + const auto& creators = pluginManager->getDefinitions(); + for ( const auto& creator : creators ) + pluginsEnabled[creator.first] = + ini.getValueB( "plugins", creator.first, + "autocomplete" == creator.first || "linter" == creator.first || + "autoformatter" == creator.first ); + pluginManager->setPluginsEnabled( pluginsEnabled ); + iniInfo = FileInfo( ini.path() ); } @@ -128,7 +135,8 @@ void AppConfig::save( const std::vector& recentFiles, const std::vector& recentFolders, const std::string& panelPartition, EE::Window::Window* win, const std::string& colorSchemeName, const SearchBarConfig& searchBarConfig, - const GlobalSearchBarConfig& globalSearchBarConfig ) { + const GlobalSearchBarConfig& globalSearchBarConfig, + PluginManager* pluginManager ) { FileInfo configInfo( ini.path() ); if ( iniInfo.getModificationTime() != 0 && @@ -186,9 +194,6 @@ void AppConfig::save( const std::vector& recentFiles, ini.setValueB( "editor", "color_picker_selection", editor.colorPickerSelection ); ini.setValueB( "editor", "color_preview", editor.colorPreview ); ini.setValueB( "editor", "minimap", editor.minimap ); - ini.setValueB( "editor", "auto_complete", editor.autoComplete ); - ini.setValueB( "editor", "linter", editor.linter ); - ini.setValueB( "editor", "formatter", editor.formatter ); ini.setValueB( "editor", "show_doc_info", editor.showDocInfo ); ini.setValueB( "editor", "hide_tab_bar_on_single_tab", editor.hideTabBarOnSingleTab ); ini.setValueB( "editor", "single_click_tree_navigation", editor.singleClickTreeNavigation ); @@ -214,6 +219,10 @@ void AppConfig::save( const std::vector& recentFiles, ini.setValueI( "window", "multisamples", context.Multisamples ); ini.setValueI( "window", "frameratelimit", context.FrameRateLimit ); + const auto& pluginsEnabled = pluginManager->getPluginsEnabled(); + for ( const auto& plugin : pluginsEnabled ) + ini.setValueB( "plugins", plugin.first, plugin.second ); + ini.writeFile(); iniState.writeFile(); } diff --git a/src/tools/ecode/appconfig.hpp b/src/tools/ecode/appconfig.hpp index e996276da..a9e6d9b36 100644 --- a/src/tools/ecode/appconfig.hpp +++ b/src/tools/ecode/appconfig.hpp @@ -18,6 +18,7 @@ using namespace EE::Window; namespace ecode { class App; +class PluginManager; enum class PanelPosition { Left, Right }; @@ -54,10 +55,7 @@ struct CodeEditorConfig { bool colorPickerSelection{ false }; bool colorPreview{ false }; bool minimap{ true }; - bool autoComplete{ true }; bool showDocInfo{ true }; - bool linter{ true }; - bool formatter{ true }; bool hideTabBarOnSingleTab{ true }; bool singleClickTreeNavigation{ false }; bool syncProjectTreeWithEditor{ true }; @@ -109,6 +107,7 @@ struct AppConfig { CodeEditorConfig editor; DocumentConfig doc; TerminalConfig term; + std::map pluginsConfig; UIConfig ui; IniFile ini; IniFile iniState; @@ -119,13 +118,13 @@ struct AppConfig { void load( const std::string& confPath, std::string& keybindingsPath, std::string& initColorScheme, std::vector& recentFiles, std::vector& recentFolders, const std::string& resPath, - const Float& displayDPI ); + const Float& displayDPI, PluginManager* pluginManager ); void save( const std::vector& recentFiles, const std::vector& recentFolders, const std::string& panelPartition, EE::Window::Window* win, const std::string& colorSchemeName, const SearchBarConfig& searchBarConfig, - const GlobalSearchBarConfig& globalSearchBarConfig ); + const GlobalSearchBarConfig& globalSearchBarConfig, PluginManager* pluginManager ); void saveProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter, const std::string& configPath, const ProjectDocumentConfig& docConfig ); diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index c024dbba7..bad8e5567 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -342,6 +342,18 @@ void App::runCommand( const std::string& command ) { } } +void App::initPluginManager() { + mPluginManager = std::make_unique( mResPath, mPluginsPath, mThreadPool ); + mPluginManager->onPluginEnabled = [&]( UICodeEditorPlugin* plugin ) { + if ( mSplitter ) + mSplitter->forEachEditor( + [&]( UICodeEditor* editor ) { editor->registerPlugin( plugin ); } ); + }; + mPluginManager->registerPlugin( LinterPlugin::Definition() ); + mPluginManager->registerPlugin( FormatterPlugin::Definition() ); + mPluginManager->registerPlugin( AutoCompletePlugin::Definition() ); +} + void App::loadConfig( const LogLevel& logLevel ) { mConfigPath = Sys::getConfigPath( "ecode" ); if ( !FileSystem::fileExists( mConfigPath ) ) @@ -363,8 +375,10 @@ void App::loadConfig( const LogLevel& logLevel ) { Log::create( mConfigPath + "ecode.log", logLevel, true, true ); #endif + initPluginManager(); + mConfig.load( mConfigPath, mKeybindingsPath, mInitColorScheme, mRecentFiles, mRecentFolders, - mResPath, mDisplayDPI ); + mResPath, mDisplayDPI, mPluginManager.get() ); } void App::saveConfig() { @@ -373,7 +387,7 @@ void App::saveConfig() { mWindow, mSplitter ? mSplitter->getCurrentColorSchemeName() : mConfig.editor.colorScheme, mDocSearchController->getSearchBarConfig(), - mGlobalSearchController->getGlobalSearchBarConfig() ); + mGlobalSearchController->getGlobalSearchBarConfig(), mPluginManager.get() ); } static std::string keybindFormat( std::string str ) { @@ -1009,25 +1023,6 @@ UIMenu* App::createViewMenu() { "Enables the color picker tool when a double click selection\n" "is done over a word representing a color." ) ) ->setId( "enable-color-picker" ); - mViewMenu->addCheckBox( i18n( "enable_autocomplete", "Enable Auto Complete" ) ) - ->setActive( mConfig.editor.autoComplete ) - ->setTooltipText( - i18n( "enable_autocomplete_tooltip", - "Auto complete shows the completion popup as you type, so you can fill\n" - "in long words by typing only a few characters." ) ) - ->setId( "enable-autocomplete" ); - mViewMenu->addCheckBox( i18n( "enable_linter", "Enable Linter" ) ) - ->setActive( mConfig.editor.linter ) - ->setTooltipText( - i18n( "enable_linter_tooltip", - "Use static code analysis tool used to flag programming errors, bugs,\n" - "stylistic errors, and suspicious constructs." ) ) - ->setId( "enable-linter" ); - mViewMenu->addCheckBox( i18n( "enable_code_formatter", "Enable Code Formatter" ) ) - ->setActive( mConfig.editor.formatter ) - ->setTooltipText( i18n( "enable_code_formatter_tooltip", - "Enables the code formatter/prettifier plugin." ) ) - ->setId( "enable-code-formatter" ); mViewMenu->addCheckBox( i18n( "hide_tabbar_on_single_tab", "Hide tabbar on single tab" ) ) ->setActive( mConfig.editor.hideTabBarOnSingleTab ) ->setTooltipText( @@ -1108,12 +1103,6 @@ UIMenu* App::createViewMenu() { mSplitter->forEachEditor( [&]( UICodeEditor* editor ) { editor->setEnableColorPickerOnSelection( mConfig.editor.colorPickerSelection ); } ); - } else if ( item->getId() == "enable-autocomplete" ) { - setAutoComplete( item->asType()->isActive() ); - } else if ( item->getId() == "enable-linter" ) { - setLinter( item->asType()->isActive() ); - } else if ( item->getId() == "enable-code-formatter" ) { - setFormatter( item->asType()->isActive() ); } else if ( item->getId() == "hide-tabbar-on-single-tab" ) { mConfig.editor.hideTabBarOnSingleTab = item->asType()->isActive(); mSplitter->setHideTabBarOnSingleTab( mConfig.editor.hideTabBarOnSingleTab ); @@ -1218,6 +1207,16 @@ makeAutoClosePairs( const std::string& strPairs ) { UIMenu* App::createTerminalMenu() { mTerminalMenu = UIPopUpMenu::New(); +#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN + UIMenuSubMenu* termColorSchemeMenu = mTerminalMenu->addSubMenu( + i18n( "terminal_color_scheme", "Terminal Color Scheme" ), findIcon( "palette" ), + mTerminalManager->createColorSchemeMenu() ); + termColorSchemeMenu->addEventListener( + Event::OnMenuShow, [&, termColorSchemeMenu]( const Event* ) { + mTerminalManager->updateMenuColorScheme( termColorSchemeMenu ); + } ); +#endif + UIMenuCheckBox* exclusiveChk = mTerminalMenu->addCheckBox( i18n( "exclusive_mode", "Exclusive Mode" ), false, getKeybind( UITerminal::getExclusiveModeToggleCommandName() ) ); @@ -1787,6 +1786,10 @@ void App::updateTerminalMenu() { ->setActive( mSplitter->getCurWidget()->asType()->getExclusiveMode() ); } +void App::createPluginManagerUI() { + UIPluginManager::New( mUISceneNode, mPluginManager.get() )->showWhenReady(); +} + void App::updateDocumentMenu() { if ( !mSplitter->getCurWidget() || !mSplitter->getCurWidget()->isType( UI_TYPE_CODEEDITOR ) ) { mSettingsMenu->getItemId( "doc-menu" )->setEnabled( false ); @@ -2129,7 +2132,7 @@ std::vector App::getUnlockedCommands() { "open-global-search", "menu-toggle", "switch-side-panel", "download-file-web", "create-new-terminal", "terminal-split-left", "terminal-split-right", "terminal-split-top", "terminal-split-bottom", - "terminal-split-swap", "reopen-closed-tab" }; + "terminal-split-swap", "reopen-closed-tab", "plugin-manager" }; } bool App::isUnlockedCommand( const std::string& command ) { @@ -2428,6 +2431,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { doc.execute( "create-new-terminal" ); } ); doc.setCommand( "reopen-closed-tab", [&] { reopenClosedTab(); } ); + doc.setCommand( "plugin-manager", [&] { createPluginManagerUI(); } ); editor->addEventListener( Event::OnDocumentSave, [&]( const Event* event ) { UICodeEditor* editor = event->getNode()->asType(); @@ -2518,68 +2522,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { editor->showMinimap( config.minimap ); - if ( config.autoComplete && !mAutoCompletePlugin ) - setAutoComplete( config.autoComplete ); - - if ( config.linter && !mLinterPlugin ) - setLinter( config.linter ); - - if ( config.formatter && !mFormatterPlugin ) - setFormatter( config.formatter ); - - if ( config.autoComplete && mAutoCompletePlugin ) - editor->registerPlugin( mAutoCompletePlugin ); - - if ( config.linter && mLinterPlugin ) - editor->registerPlugin( mLinterPlugin ); - - if ( config.formatter && mFormatterPlugin ) - editor->registerPlugin( mFormatterPlugin ); -} - -bool App::setAutoComplete( bool enable ) { - mConfig.editor.autoComplete = enable; - if ( enable && !mAutoCompletePlugin ) { - mAutoCompletePlugin = eeNew( AutoCompletePlugin, ( mThreadPool ) ); - mSplitter->forEachEditor( - [&]( UICodeEditor* editor ) { editor->registerPlugin( mAutoCompletePlugin ); } ); - return true; - } - if ( !enable && mAutoCompletePlugin ) - eeSAFE_DELETE( mAutoCompletePlugin ); - return false; -} - -bool App::setLinter( bool enable ) { - mConfig.editor.linter = enable; - if ( enable && !mLinterPlugin ) { - std::string path( mResPath + "plugins/linters.json" ); - if ( FileSystem::fileExists( mPluginsPath + "linters.json" ) ) - path = mPluginsPath + "linters.json"; - mLinterPlugin = eeNew( LinterPlugin, ( path, mThreadPool ) ); - mSplitter->forEachEditor( - [&]( UICodeEditor* editor ) { editor->registerPlugin( mLinterPlugin ); } ); - return true; - } - if ( !enable && mLinterPlugin ) - eeSAFE_DELETE( mLinterPlugin ); - return false; -} - -bool App::setFormatter( bool enable ) { - mConfig.editor.formatter = enable; - if ( enable && !mFormatterPlugin ) { - std::string path( mResPath + "plugins/formatters.json" ); - if ( FileSystem::fileExists( mPluginsPath + "formatters.json" ) ) - path = mPluginsPath + "formatter.json"; - mFormatterPlugin = eeNew( FormatterPlugin, ( path, mThreadPool ) ); - mSplitter->forEachEditor( - [&]( UICodeEditor* editor ) { editor->registerPlugin( mFormatterPlugin ); } ); - return true; - } - if ( !enable && mFormatterPlugin ) - eeSAFE_DELETE( mFormatterPlugin ); - return false; + mPluginManager->onNewEditor( editor ); } void App::loadCurrentDirectory() { @@ -2707,8 +2650,8 @@ void App::createSettingsMenu() { getKeybind( "save-all" ) ) ->setId( "save-all" ); mSettingsMenu->addSeparator(); - UIMenuSubMenu* fileTypeMenu = mSettingsMenu->addSubMenu( i18n( "file_type", "File Type" ), - nullptr, createFileTypeMenu() ); + UIMenuSubMenu* fileTypeMenu = mSettingsMenu->addSubMenu( + i18n( "file_type", "File Type" ), findIcon( "file-code" ), createFileTypeMenu() ); fileTypeMenu->addEventListener( Event::OnMenuShow, [&, fileTypeMenu]( const Event* ) { if ( mFileTypeMenuesCreatedWithHeight != mUISceneNode->getPixelsSize().getHeight() ) { for ( UIPopUpMenu* menu : mFileTypeMenues ) @@ -2719,8 +2662,9 @@ void App::createSettingsMenu() { fileTypeMenu->setSubMenu( newMenu ); } } ); - UIMenuSubMenu* colorSchemeMenu = mSettingsMenu->addSubMenu( - i18n( "syntax_color_scheme", "Syntax Color Scheme" ), nullptr, createColorSchemeMenu() ); + UIMenuSubMenu* colorSchemeMenu = + mSettingsMenu->addSubMenu( i18n( "syntax_color_scheme", "Syntax Color Scheme" ), + findIcon( "palette" ), createColorSchemeMenu() ); colorSchemeMenu->addEventListener( Event::OnMenuShow, [&, colorSchemeMenu]( const Event* ) { if ( mColorSchemeMenuesCreatedWithHeight != mUISceneNode->getPixelsSize().getHeight() ) { for ( UIPopUpMenu* menu : mColorSchemeMenues ) @@ -2731,14 +2675,6 @@ void App::createSettingsMenu() { colorSchemeMenu->setSubMenu( newMenu ); } } ); -#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN - UIMenuSubMenu* termColorSchemeMenu = - mSettingsMenu->addSubMenu( i18n( "terminal_color_scheme", "Terminal Color Scheme" ), - nullptr, mTerminalManager->createColorSchemeMenu() ); - colorSchemeMenu->addEventListener( Event::OnMenuShow, [&, termColorSchemeMenu]( const Event* ) { - mTerminalManager->updateMenuColorScheme( termColorSchemeMenu ); - } ); -#endif mSettingsMenu ->addSubMenu( i18n( "document", "Document" ), findIcon( "file" ), createDocumentMenu() ) ->setId( "doc-menu" ); @@ -2749,6 +2685,8 @@ void App::createSettingsMenu() { mSettingsMenu->addSubMenu( i18n( "view", "View" ), nullptr, createViewMenu() ); mSettingsMenu->addSubMenu( i18n( "tools", "Tools" ), nullptr, createToolsMenu() ); mSettingsMenu->addSubMenu( i18n( "window", "Window" ), nullptr, createWindowMenu() ); + mSettingsMenu->add( i18n( "plugin_manager", "Plugin Manager" ), findIcon( "package" ) ) + ->setId( "plugin-manager" ); mSettingsMenu->addSubMenu( i18n( "help", "Help" ), findIcon( "help" ), createHelpMenu() ); mSettingsMenu->addSeparator(); mSettingsMenu @@ -3397,13 +3335,6 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe mUseFrameBuffer = frameBuffer; mBenchmarkMode = benchmarkMode; - loadConfig( logLevel ); - - currentDisplay = displayManager->getDisplayIndex( mConfig.windowState.displayIndex < - displayManager->getDisplayCount() - ? mConfig.windowState.displayIndex - : 0 ); - mDisplayDPI = currentDisplay->getDPI(); mResPath = Sys::getProcessPath(); #if EE_PLATFORM == EE_PLATFORM_MACOSX if ( String::contains( mResPath, "ecode.app" ) ) { @@ -3423,6 +3354,14 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe mResPath += "assets"; FileSystem::dirAddSlashAtEnd( mResPath ); + loadConfig( logLevel ); + + currentDisplay = displayManager->getDisplayIndex( mConfig.windowState.displayIndex < + displayManager->getDisplayCount() + ? mConfig.windowState.displayIndex + : 0 ); + mDisplayDPI = currentDisplay->getDPI(); + mConfig.windowState.pixelDensity = pidelDensity > 0 ? pidelDensity @@ -3790,6 +3729,8 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe { "arrow-down-s", 0xea4e }, { "arrow-right-s", 0xea6e }, { "match-case", 0xed8d }, + { "palette", 0xefc5 }, + { "file-code", 0xecd1 }, }; 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 d91148bd6..8f973cfa6 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -7,6 +7,7 @@ #include "filesystemlistener.hpp" #include "globalsearchcontroller.hpp" #include "notificationcenter.hpp" +#include "plugins/pluginmanager.hpp" #include "projectdirectorytree.hpp" #include "terminalmanager.hpp" #include @@ -144,6 +145,8 @@ class App : public UICodeEditorSplitter::Client { void updateTerminalMenu(); + void createPluginManagerUI(); + protected: EE::Window::Window* mWindow{ nullptr }; UISceneNode* mUISceneNode{ nullptr }; @@ -220,6 +223,7 @@ class App : public UICodeEditorSplitter::Client { std::string mLastFileFolder; ColorSchemePreference mUIColorScheme; std::unique_ptr mTerminalManager; + std::unique_ptr mPluginManager; void saveAllProcess(); @@ -305,12 +309,6 @@ class App : public UICodeEditorSplitter::Client { void onCodeEditorFocusChange( UICodeEditor* editor ); - bool setAutoComplete( bool enable ); - - bool setLinter( bool enable ); - - bool setFormatter( bool enable ); - void updateDocInfo( TextDocument& doc ); void setFocusEditorOnClose( UIMessageBox* msgBox ); @@ -345,6 +343,8 @@ class App : public UICodeEditorSplitter::Client { UIMessageBox* fileAlreadyExistsMsgBox(); void renameFile( const FileInfo& file ); + + void initPluginManager(); }; } // namespace ecode diff --git a/src/tools/ecode/ignorematcher.cpp b/src/tools/ecode/ignorematcher.cpp index 81bbd338a..5a485c16a 100644 --- a/src/tools/ecode/ignorematcher.cpp +++ b/src/tools/ecode/ignorematcher.cpp @@ -203,6 +203,11 @@ bool GitIgnoreMatcher::match( const std::string& value ) const { return false; } +IgnoreMatcherManager::IgnoreMatcherManager( IgnoreMatcherManager&& ignoreMatcher ) : + mMatchers( ignoreMatcher.mMatchers ) { + ignoreMatcher.mMatchers.clear(); +} + IgnoreMatcherManager::IgnoreMatcherManager( std::string rootPath ) { FileSystem::dirAddSlashAtEnd( rootPath ); GitIgnoreMatcher git( rootPath ); @@ -210,6 +215,12 @@ IgnoreMatcherManager::IgnoreMatcherManager( std::string rootPath ) { mMatchers.emplace_back( eeNew( GitIgnoreMatcher, ( rootPath ) ) ); } +IgnoreMatcherManager& IgnoreMatcherManager::operator=( IgnoreMatcherManager&& other ) { + mMatchers = other.mMatchers; + other.mMatchers.clear(); + return *this; +} + IgnoreMatcherManager::~IgnoreMatcherManager() { for ( size_t i = 0; i < mMatchers.size(); ++i ) eeDelete( mMatchers[i] ); diff --git a/src/tools/ecode/ignorematcher.hpp b/src/tools/ecode/ignorematcher.hpp index b9322223f..31a74e4d7 100644 --- a/src/tools/ecode/ignorematcher.hpp +++ b/src/tools/ecode/ignorematcher.hpp @@ -45,8 +45,12 @@ class GitIgnoreMatcher : public IgnoreMatcher { class IgnoreMatcherManager { public: + IgnoreMatcherManager( IgnoreMatcherManager&& ignoreMatcher ); + IgnoreMatcherManager( std::string rootPath ); + IgnoreMatcherManager& operator=( IgnoreMatcherManager&& other ); + virtual ~IgnoreMatcherManager(); bool foundMatch() const; diff --git a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp index 20ee50d0e..fd937848f 100644 --- a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp +++ b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp @@ -15,20 +15,15 @@ namespace ecode { #define AUTO_COMPLETE_THREADED 0 #endif -AutoCompletePlugin::AutoCompletePlugin() : -#if AUTO_COMPLETE_THREADED - AutoCompletePlugin( ThreadPool::createShared( eemin( 2, Sys::getCPUCount() ) ) ) -#else - AutoCompletePlugin( nullptr ) -#endif -{ +UICodeEditorPlugin* AutoCompletePlugin::New( const PluginManager* pluginManager ) { + return eeNew( AutoCompletePlugin, ( pluginManager ) ); } -AutoCompletePlugin::AutoCompletePlugin( std::shared_ptr pool ) : +AutoCompletePlugin::AutoCompletePlugin( const PluginManager* pluginManager ) : mSymbolPattern( "[%a_ñàáâãäåèéêëìíîïòóôõöùúûüýÿÑÀÁÂÃÄÅÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝ][%w_" "ñàáâãäåèéêëìíîïòóôõöùúûüýÿÑÀÁÂÃÄÅÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝ]*" ), mBoxPadding( PixelDensity::dpToPx( Rectf( 4, 4, 4, 4 ) ) ), - mPool( pool ) {} + mPool( pluginManager->getThreadPool() ) {} AutoCompletePlugin::~AutoCompletePlugin() { mClosing = true; @@ -478,4 +473,4 @@ void AutoCompletePlugin::updateSuggestions( const std::string& symbol, UICodeEdi } } -} +} // namespace ecode diff --git a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp index 0071dbc01..49cd494e1 100644 --- a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp +++ b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp @@ -1,6 +1,7 @@ #ifndef ECODE_AUTOCOMPLETEPLUGIN_HPP #define ECODE_AUTOCOMPLETEPLUGIN_HPP +#include "../pluginmanager.hpp" #include #include #include @@ -18,18 +19,22 @@ class AutoCompletePlugin : public UICodeEditorPlugin { public: typedef std::unordered_set SymbolsList; - AutoCompletePlugin(); + static PluginDefinition Definition() { + return { "autocomplete", "Auto Complete", + "Auto complete shows the completion popup as you type, so you can fill\n" + "in long words by typing only a few characters.", + AutoCompletePlugin::New }; + } - AutoCompletePlugin( std::shared_ptr pool ); + static UICodeEditorPlugin* New( const PluginManager* pluginManager ); virtual ~AutoCompletePlugin(); - std::string getTitle() { return "Auto Complete"; } + std::string getId() { return Definition().id; } - std::string getDescription() { - return "Auto complete shows the completion popup as you type, so you can fill\n" - "in long words by typing only a few characters."; - } + std::string getTitle() { return Definition().name; } + + std::string getDescription() { return Definition().description; } void onRegister( UICodeEditor* ); void onUnregister( UICodeEditor* ); @@ -96,6 +101,8 @@ class AutoCompletePlugin : public UICodeEditorPlugin { Float mRowHeight{ 0 }; Rectf mBoxRect; + AutoCompletePlugin( const PluginManager* pluginManager ); + void resetSuggestions( UICodeEditor* editor ); void updateSuggestions( const std::string& symbol, UICodeEditor* editor ); @@ -114,6 +121,6 @@ class AutoCompletePlugin : public UICodeEditorPlugin { void pickSuggestion( UICodeEditor* editor ); }; -} +} // namespace ecode #endif // ECODE_AUTOCOMPLETEPLUGIN_HPP diff --git a/src/tools/ecode/plugins/formatter/formatterplugin.cpp b/src/tools/ecode/plugins/formatter/formatterplugin.cpp index b510f1f78..1e486d0ad 100644 --- a/src/tools/ecode/plugins/formatter/formatterplugin.cpp +++ b/src/tools/ecode/plugins/formatter/formatterplugin.cpp @@ -22,11 +22,14 @@ namespace ecode { #define FORMATTER_THREADED 0 #endif -FormatterPlugin::FormatterPlugin( const std::string& formattersPath, - std::shared_ptr pool ) : - mPool( pool ) { +UICodeEditorPlugin* FormatterPlugin::New( const PluginManager* pluginManager ) { + return eeNew( FormatterPlugin, ( pluginManager ) ); +} + +FormatterPlugin::FormatterPlugin( const PluginManager* pluginManager ) : + mPool( pluginManager->getThreadPool() ) { #if FORMATTER_THREADED - mPool->run( [&, formattersPath] { load( formattersPath ); }, [] {} ); + mPool->run( [&, pluginManager] { load( pluginManager ); }, [] {} ); #else load( formattersPath ); #endif @@ -84,13 +87,16 @@ void FormatterPlugin::unregisterNativeFormatter( const std::string& cmd ) { mNativeFormatters.erase( cmd ); } -void FormatterPlugin::load( const std::string& formatterPath ) { +void FormatterPlugin::load( const PluginManager* pluginManager ) { registerNativeFormatters(); - if ( !FileSystem::fileExists( formatterPath ) ) + std::string path( pluginManager->getResourcesPath() + "plugins/formatters.json" ); + if ( FileSystem::fileExists( pluginManager->getPluginsPath() + "formatters.json" ) ) + path = pluginManager->getPluginsPath() + "formatter.json"; + if ( !FileSystem::fileExists( path ) ) return; try { - std::ifstream stream( formatterPath ); + std::ifstream stream( path ); json j; stream >> j; diff --git a/src/tools/ecode/plugins/formatter/formatterplugin.hpp b/src/tools/ecode/plugins/formatter/formatterplugin.hpp index 3a04aa7e7..ae2468bc9 100644 --- a/src/tools/ecode/plugins/formatter/formatterplugin.hpp +++ b/src/tools/ecode/plugins/formatter/formatterplugin.hpp @@ -1,6 +1,7 @@ #ifndef ECODE_FORMATTERPLUGIN_HPP #define ECODE_FORMATTERPLUGIN_HPP +#include "../pluginmanager.hpp" #include #include #include @@ -20,13 +21,20 @@ class FormatterPlugin : public UICodeEditorPlugin { std::string err; }; - FormatterPlugin( const std::string& formatterPath, std::shared_ptr pool ); + static PluginDefinition Definition() { + return { "autoformatter", "Auto Formatter", "Enables the code formatter/prettifier plugin.", + FormatterPlugin::New }; + } + + static UICodeEditorPlugin* New( const PluginManager* pluginManager ); virtual ~FormatterPlugin(); - std::string getTitle() { return "Auto Formatter"; } + std::string getId() { return Definition().id; } - std::string getDescription() { return "Enables the code formatter/prettifier plugin."; } + std::string getTitle() { return Definition().name; } + + std::string getDescription() { return Definition().description; } void onRegister( UICodeEditor* ); @@ -41,6 +49,7 @@ class FormatterPlugin : public UICodeEditorPlugin { const std::function& nativeFormatter ); void unregisterNativeFormatter( const std::string& cmd ); + protected: enum class FormatterType { Inplace, Output, Native }; @@ -63,7 +72,9 @@ class FormatterPlugin : public UICodeEditorPlugin { bool mShuttingDown{ false }; bool mReady{ false }; - void load( const std::string& formatterPath ); + FormatterPlugin( const PluginManager* pluginManager ); + + void load( const PluginManager* pluginManager ); void formatDoc( UICodeEditor* editor ); diff --git a/src/tools/ecode/plugins/linter/linterplugin.cpp b/src/tools/ecode/plugins/linter/linterplugin.cpp index 494847e6f..c93965636 100644 --- a/src/tools/ecode/plugins/linter/linterplugin.cpp +++ b/src/tools/ecode/plugins/linter/linterplugin.cpp @@ -22,10 +22,14 @@ namespace ecode { #define LINTER_THREADED 0 #endif -LinterPlugin::LinterPlugin( const std::string& lintersPath, std::shared_ptr pool ) : - mPool( pool ) { +UICodeEditorPlugin* LinterPlugin::New( const PluginManager* pluginManager ) { + return eeNew( LinterPlugin, ( pluginManager ) ); +} + +LinterPlugin::LinterPlugin( const PluginManager* pluginManager ) : + mPool( pluginManager->getThreadPool() ) { #if LINTER_THREADED - mPool->run( [&, lintersPath] { load( lintersPath ); }, [] {} ); + mPool->run( [&, pluginManager] { load( pluginManager ); }, [] {} ); #else load( lintersPath ); #endif @@ -46,11 +50,14 @@ LinterPlugin::~LinterPlugin() { } } -void LinterPlugin::load( const std::string& lintersPath ) { - if ( !FileSystem::fileExists( lintersPath ) ) +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( lintersPath ); + std::ifstream stream( path ); json j; stream >> j; diff --git a/src/tools/ecode/plugins/linter/linterplugin.hpp b/src/tools/ecode/plugins/linter/linterplugin.hpp index b63bc0b2c..181469b52 100644 --- a/src/tools/ecode/plugins/linter/linterplugin.hpp +++ b/src/tools/ecode/plugins/linter/linterplugin.hpp @@ -1,6 +1,7 @@ #ifndef ECODE_LINTERPLUGIN_HPP #define ECODE_LINTERPLUGIN_HPP +#include "../pluginmanager.hpp" #include #include #include @@ -43,16 +44,21 @@ struct LinterMatch { class LinterPlugin : public UICodeEditorPlugin { public: - LinterPlugin( const std::string& lintersPath, std::shared_ptr pool ); + static PluginDefinition Definition() { + return { "linter", "Linter", + "Use static code analysis tool used to flag programming errors, bugs,\n" + "stylistic errors, and suspicious constructs.", + LinterPlugin::New }; + } + static UICodeEditorPlugin* New( const PluginManager* pluginManager ); virtual ~LinterPlugin(); - std::string getTitle() { return "Linter"; } + std::string getId() { return Definition().id; } - std::string getDescription() { - return "Use static code analysis tool used to flag programming errors, bugs,\n" - "stylistic errors, and suspicious constructs."; - } + std::string getTitle() { return Definition().name; } + + std::string getDescription() { return Definition().description; } void onRegister( UICodeEditor* ); @@ -92,7 +98,9 @@ class LinterPlugin : public UICodeEditorPlugin { bool mReady{ false }; bool mShuttingDown{ false }; - void load( const std::string& lintersPath ); + LinterPlugin( const PluginManager* pluginManager ); + + void load( const PluginManager* pluginManager ); void lintDoc( std::shared_ptr doc ); diff --git a/src/tools/ecode/plugins/pluginmanager.cpp b/src/tools/ecode/plugins/pluginmanager.cpp new file mode 100644 index 000000000..3017250c8 --- /dev/null +++ b/src/tools/ecode/plugins/pluginmanager.cpp @@ -0,0 +1,194 @@ +#include "pluginmanager.hpp" +#include + +namespace ecode { + +PluginManager::PluginManager( const std::string& resourcesPath, const std::string& pluginsPath, + std::shared_ptr pool ) : + mResourcesPath( resourcesPath ), mPluginsPath( pluginsPath ), mThreadPool( pool ) {} + +PluginManager::~PluginManager() { + for ( auto& plugin : mPlugins ) + eeDelete( plugin.second ); +} + +void PluginManager::registerPlugin( const PluginDefinition& def ) { + mDefinitions[def.id] = def; +} + +UICodeEditorPlugin* ecode::PluginManager::get( const std::string& id ) { + auto findIt = mPlugins.find( id ); + if ( findIt != mPlugins.end() ) + return findIt->second; + return nullptr; +} + +bool PluginManager::setEnabled( const std::string& id, bool enable ) { + mPluginsEnabled[id] = enable; + UICodeEditorPlugin* plugin = get( id ); + if ( enable && plugin == nullptr && hasDefinition( id ) ) { + UICodeEditorPlugin* newPlugin = mDefinitions[id].creatorFn( this ); + mPlugins.insert( std::pair( id, newPlugin ) ); + if ( onPluginEnabled ) + onPluginEnabled( newPlugin ); + return true; + } + if ( !enable && plugin != nullptr ) { + eeSAFE_DELETE( plugin ); + mPlugins.erase( id ); + } + return false; +} + +bool PluginManager::isEnabled( const std::string& id ) const { + return mPluginsEnabled.find( id ) != mPluginsEnabled.end() ? mPluginsEnabled.at( id ) : false; +} + +const std::string& PluginManager::getResourcesPath() const { + return mResourcesPath; +} + +const std::string& PluginManager::getPluginsPath() const { + return mPluginsPath; +} + +const std::map& PluginManager::getPluginsEnabled() const { + return mPluginsEnabled; +} + +void PluginManager::onNewEditor( UICodeEditor* editor ) { + for ( auto& plugin : mPlugins ) + editor->registerPlugin( plugin.second ); +} + +void PluginManager::setPluginsEnabled( const std::map& pluginsEnabled ) { + mPluginsEnabled = pluginsEnabled; + for ( const auto& plugin : pluginsEnabled ) { + if ( plugin.second && get( plugin.first ) == nullptr ) + setEnabled( plugin.first, true ); + } +} + +const std::shared_ptr& PluginManager::getThreadPool() const { + return mThreadPool; +} + +const std::map& PluginManager::getDefinitions() const { + return mDefinitions; +} + +const PluginDefinition* PluginManager::getDefinitionIndex( const Int64& index ) const { + const PluginDefinition* def = nullptr; + Int64 i = 0; + for ( const auto& curDef : mDefinitions ) { + if ( index == i ) + def = &curDef.second; + ++i; + } + return def; +} + +bool PluginManager::hasDefinition( const std::string& id ) { + return mDefinitions.find( id ) != mDefinitions.end(); +} + +std::shared_ptr PluginsModel::create( PluginManager* manager ) { + return std::make_shared( manager ); +} + +size_t PluginsModel::rowCount( const ModelIndex& ) const { + return mManager->getDefinitions().size(); +} + +std::string PluginsModel::columnName( const size_t& col ) const { + eeASSERT( col < mColumnNames.size() ); + return mColumnNames[col]; +} + +Variant PluginsModel::data( const ModelIndex& index, ModelRole role ) const { + if ( role == ModelRole::Display ) { + const PluginDefinition* def = mManager->getDefinitionIndex( index.row() ); + if ( def == nullptr ) + return {}; + switch ( index.column() ) { + case Columns::Version: + return Variant( def->versionString.c_str() ); + case Columns::Description: + return Variant( def->description.c_str() ); + case Columns::Title: + return Variant( def->name.c_str() ); + case Columns::Id: + return Variant( def->id.c_str() ); + } + } + return {}; +} + +UIWindow* UIPluginManager::New( UISceneNode* sceneNode, PluginManager* manager ) { + UIWindow* win = sceneNode + ->loadLayoutFromString( R"xml( + + + + + + + + + + + + + + )xml" ) + ->asType(); + UIWidget* cont = win->getContainer(); + UIPushButton* enable = cont->find( "plugin-manager-enabled" ); + UIPushButton* close = cont->find( "plugin-manager-close" ); + UITableView* 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 ) + win->closeWindow(); + } ); + + win->setTitle( sceneNode->i18n( "plugin_manager", "Plugin Manager" ) ); + enable->setText( sceneNode->i18n( "enable", "Select a Plugin" ) ); + + auto updateButtonsState = [sceneNode, enable, manager]( const ModelIndex& index ) { + const PluginDefinition* def = manager->getDefinitionIndex( index.row() ); + if ( def == nullptr ) + return; + enable->setEnabled( true ); + enable->setText( manager->isEnabled( def->id ) ? sceneNode->i18n( "disable", "Disable" ) + : sceneNode->i18n( "enable", "Enable" ) ); + }; + enable->addEventListener( + Event::MouseClick, [updateButtonsState, tv, manager]( const Event* event ) { + const MouseEvent* mevent = static_cast( event ); + if ( mevent->getFlags() & EE_BUTTON_LMASK && !tv->getSelection().isEmpty() ) { + const PluginDefinition* def = + manager->getDefinitionIndex( tv->getSelection().first().row() ); + if ( def == nullptr ) + return; + manager->setEnabled( def->id, !manager->isEnabled( def->id ) ); + updateButtonsState( tv->getSelection().first() ); + } + } ); + tv->setOnSelection( updateButtonsState ); + tv->setModel( PluginsModel::create( manager ) ); + tv->setColumnsVisible( + { PluginsModel::Title, PluginsModel::Description, PluginsModel::Version } ); + tv->setAutoColumnsWidth( true ); + tv->setRowHeight( PixelDensity::dpToPx( 64 ) ); + win->center(); + return win; +} + +} // namespace ecode diff --git a/src/tools/ecode/plugins/pluginmanager.hpp b/src/tools/ecode/plugins/pluginmanager.hpp new file mode 100644 index 000000000..41a289679 --- /dev/null +++ b/src/tools/ecode/plugins/pluginmanager.hpp @@ -0,0 +1,112 @@ +#ifndef ECODE_PLUGINMANAGER_HPP +#define ECODE_PLUGINMANAGER_HPP + +#include +#include +#include +#include +#include +#include + +using namespace EE; +using namespace EE::System; +using namespace EE::UI; +using namespace EE::UI::Models; + +namespace ecode { + +class PluginManager; + +typedef std::function PluginCreatorFn; + +struct PluginDefinition { + std::string id; + std::string name; + std::string description; + PluginCreatorFn creatorFn; + int versionNumber{ 0 }; + std::string versionString{ "0" }; +}; + +class PluginManager { + public: + PluginManager( const std::string& resourcesPath, const std::string& pluginsPath, + std::shared_ptr pool ); + + ~PluginManager(); + + void registerPlugin( const PluginDefinition& def ); + + UICodeEditorPlugin* get( const std::string& id ); + + bool setEnabled( const std::string& id, bool enable ); + + bool isEnabled( const std::string& id ) const; + + const std::string& getResourcesPath() const; + + const std::string& getPluginsPath() const; + + const std::map& getPluginsEnabled() const; + + void onNewEditor( UICodeEditor* editor ); + + void setPluginsEnabled( const std::map& pluginsEnabled ); + + const std::shared_ptr& getThreadPool() const; + + std::function onPluginEnabled; + + const std::map& getDefinitions() const; + + const PluginDefinition* getDefinitionIndex( const Int64& index ) const; + + protected: + std::string mResourcesPath; + std::string mPluginsPath; + std::map mPlugins; + std::map mPluginsEnabled; + std::map mDefinitions; + std::shared_ptr mThreadPool; + + bool hasDefinition( const std::string& id ); +}; + +class PluginsModel : public Model { + public: + enum Columns { Id, Enabled, Title, Description, Version }; + + static std::shared_ptr create( PluginManager* manager ); + + PluginsModel( PluginManager* manager ) : mManager( manager ) {} + + virtual ~PluginsModel() {} + + virtual size_t rowCount( const ModelIndex& ) const; + + virtual size_t columnCount( const ModelIndex& ) const { return mColumnNames.size(); } + + virtual std::string columnName( const size_t& col ) const; + + virtual void setColumnName( const size_t& index, const std::string& name ) { + eeASSERT( index <= Columns::Version ); + mColumnNames[index] = name; + } + + virtual Variant data( const ModelIndex& index, ModelRole role = ModelRole::Display ) const; + + virtual void update() { onModelUpdate(); } + + protected: + PluginManager* mManager; + std::vector mColumnNames{ "Id", "Enabled", "Title", "Description", "Version" }; +}; + +class UIPluginManager { + public: + static UIWindow* New( UISceneNode* sceneNode, PluginManager* manager ); +}; + +} // namespace ecode + +#endif // ECODE_PLUGINMANAGER_HPP diff --git a/src/tools/ecode/terminalmanager.cpp b/src/tools/ecode/terminalmanager.cpp index 1e650332b..2913affdf 100644 --- a/src/tools/ecode/terminalmanager.cpp +++ b/src/tools/ecode/terminalmanager.cpp @@ -344,6 +344,7 @@ UITerminal* TerminalManager::createNewTerminal( const std::string& title, UITabW : mTerminalColorSchemes.begin()->first ); } ); term->setCommand( "reopen-closed-tab", [&] { mApp->reopenClosedTab(); } ); + term->setCommand( "plugin-manager", [&] { mApp->createPluginManagerUI(); } ); // debug-draw-highlight-toggle // debug-draw-boxes-toggle // debug-draw-debug-data