ecode: IgnoreMatcherManager fixes. Implemented a new plugin manager.

This commit is contained in:
Martín Lucas Golini
2022-09-16 02:41:03 -03:00
parent 11050a01bb
commit dd88478dd8
22 changed files with 545 additions and 277 deletions

View File

@@ -35,6 +35,50 @@ template <typename T> class ItemListModel final : public Model {
std::vector<T>& mData;
};
template <typename K, typename V> class ItemPairListModel final : public Model {
public:
static std::shared_ptr<ItemPairListModel> create( std::vector<std::pair<K, V>>& data ) {
return std::make_shared( *new ItemPairListModel<K, V>( 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<K, V>& data ) : mData( data ) {}
std::vector<std::pair<K, V>>& mData;
std::vector<std::string> mColumnNames{ "Title", "Description" };
};
}}} // namespace EE::UI::Models
#endif // EE_UI_MODELS_ITEMLISTMODEL_HPP

View File

@@ -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;

View File

@@ -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,

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 8.0.1, 2022-09-03T02:48:17. -->
<!-- Written by QtCreator 8.0.1, 2022-09-16T02:40:37. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
@@ -109,7 +109,7 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{388e5431-b31b-42b3-b9ad-9002d279d75d}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">10</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">15</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">19</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">../../make/linux</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
@@ -1105,7 +1105,7 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">eepp-UIEditor-debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="QString" key="RunConfiguration.Arguments">--xml=/home/downloads/temp/eepp/search_box.xml -u</value>
<value type="QString" key="RunConfiguration.Arguments">-u</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">true</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
@@ -1203,6 +1203,7 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">ecode-debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="QString" key="RunConfiguration.Arguments">../</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">true</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
@@ -1429,109 +1430,9 @@
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">21</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.1</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Replacement for &quot;Desktop&quot;</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Replacement for &quot;Desktop&quot;</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{6d057187-158a-4883-8d5b-d470a6b6b025}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/programming/eepp/projects/linux</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Default</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="QString" key="Analyzer.Valgrind.FilterExternalIssues">false</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">/usr/bin/kcachegrind</value>
<value type="QString" key="Analyzer.Valgrind.LeakCheckOnFinish">2</value>
<value type="QString" key="Analyzer.Valgrind.SelfModifyingCodeDetection">2</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="QString" key="Analyzer.Valgrind.ShowReachable">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">/usr/bin/valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="QString">11</value>
<value type="QString">14</value>
<value type="QString">12</value>
<value type="QString">13</value>
<value type="QString">2</value>
<value type="QString">3</value>
<value type="QString">4</value>
<value type="QString">5</value>
<value type="QString">6</value>
<value type="QString">7</value>
<value type="QString">8</value>
<value type="QString">9</value>
<value type="QString">10</value>
<value type="QString">0</value>
<value type="QString">1</value>
</valuelist>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">2</value>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>

View File

@@ -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

View File

@@ -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<size_t> 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();
}

View File

@@ -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() ) {

View File

@@ -1,5 +1,6 @@
#include "appconfig.hpp"
#include "ecode.hpp"
#include "plugins/pluginmanager.hpp"
#include "thirdparty/json.hpp"
#include <eepp/network/uri.hpp>
#include <eepp/system/filesystem.hpp>
@@ -42,7 +43,7 @@ static std::vector<std::string> urlDecode( const std::vector<std::string>& vec )
void AppConfig::load( const std::string& confPath, std::string& keybindingsPath,
std::string& initColorScheme, std::vector<std::string>& recentFiles,
std::vector<std::string>& 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<std::string, bool> 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<std::string>& recentFiles,
const std::vector<std::string>& 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<std::string>& 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<std::string>& 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();
}

View File

@@ -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<std::string, bool> 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<std::string>& recentFiles,
std::vector<std::string>& recentFolders, const std::string& resPath,
const Float& displayDPI );
const Float& displayDPI, PluginManager* pluginManager );
void save( const std::vector<std::string>& recentFiles,
const std::vector<std::string>& 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 );

View File

@@ -342,6 +342,18 @@ void App::runCommand( const std::string& command ) {
}
}
void App::initPluginManager() {
mPluginManager = std::make_unique<PluginManager>( 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<UIMenuCheckBox>()->isActive() );
} else if ( item->getId() == "enable-linter" ) {
setLinter( item->asType<UIMenuCheckBox>()->isActive() );
} else if ( item->getId() == "enable-code-formatter" ) {
setFormatter( item->asType<UIMenuCheckBox>()->isActive() );
} else if ( item->getId() == "hide-tabbar-on-single-tab" ) {
mConfig.editor.hideTabBarOnSingleTab = item->asType<UIMenuCheckBox>()->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<UITerminal>()->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<std::string> 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<UICodeEditor>();
@@ -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 ) );

View File

@@ -7,6 +7,7 @@
#include "filesystemlistener.hpp"
#include "globalsearchcontroller.hpp"
#include "notificationcenter.hpp"
#include "plugins/pluginmanager.hpp"
#include "projectdirectorytree.hpp"
#include "terminalmanager.hpp"
#include <eepp/ee.hpp>
@@ -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<TerminalManager> mTerminalManager;
std::unique_ptr<PluginManager> 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

View File

@@ -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] );

View File

@@ -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;

View File

@@ -15,20 +15,15 @@ namespace ecode {
#define AUTO_COMPLETE_THREADED 0
#endif
AutoCompletePlugin::AutoCompletePlugin() :
#if AUTO_COMPLETE_THREADED
AutoCompletePlugin( ThreadPool::createShared( eemin<int>( 2, Sys::getCPUCount() ) ) )
#else
AutoCompletePlugin( nullptr )
#endif
{
UICodeEditorPlugin* AutoCompletePlugin::New( const PluginManager* pluginManager ) {
return eeNew( AutoCompletePlugin, ( pluginManager ) );
}
AutoCompletePlugin::AutoCompletePlugin( std::shared_ptr<ThreadPool> 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

View File

@@ -1,6 +1,7 @@
#ifndef ECODE_AUTOCOMPLETEPLUGIN_HPP
#define ECODE_AUTOCOMPLETEPLUGIN_HPP
#include "../pluginmanager.hpp"
#include <eepp/config.hpp>
#include <eepp/system/clock.hpp>
#include <eepp/system/mutex.hpp>
@@ -18,18 +19,22 @@ class AutoCompletePlugin : public UICodeEditorPlugin {
public:
typedef std::unordered_set<std::string> 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<ThreadPool> 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

View File

@@ -22,11 +22,14 @@ namespace ecode {
#define FORMATTER_THREADED 0
#endif
FormatterPlugin::FormatterPlugin( const std::string& formattersPath,
std::shared_ptr<ThreadPool> 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;

View File

@@ -1,6 +1,7 @@
#ifndef ECODE_FORMATTERPLUGIN_HPP
#define ECODE_FORMATTERPLUGIN_HPP
#include "../pluginmanager.hpp"
#include <eepp/config.hpp>
#include <eepp/system/mutex.hpp>
#include <eepp/system/threadpool.hpp>
@@ -20,13 +21,20 @@ class FormatterPlugin : public UICodeEditorPlugin {
std::string err;
};
FormatterPlugin( const std::string& formatterPath, std::shared_ptr<ThreadPool> 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<NativeFormatterResult( const std::string& file )>& 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 );

View File

@@ -22,10 +22,14 @@ namespace ecode {
#define LINTER_THREADED 0
#endif
LinterPlugin::LinterPlugin( const std::string& lintersPath, std::shared_ptr<ThreadPool> 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;

View File

@@ -1,6 +1,7 @@
#ifndef ECODE_LINTERPLUGIN_HPP
#define ECODE_LINTERPLUGIN_HPP
#include "../pluginmanager.hpp"
#include <eepp/config.hpp>
#include <eepp/system/mutex.hpp>
#include <eepp/system/threadpool.hpp>
@@ -43,16 +44,21 @@ struct LinterMatch {
class LinterPlugin : public UICodeEditorPlugin {
public:
LinterPlugin( const std::string& lintersPath, std::shared_ptr<ThreadPool> 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<TextDocument> doc );

View File

@@ -0,0 +1,194 @@
#include "pluginmanager.hpp"
#include <eepp/ui/uitableview.hpp>
namespace ecode {
PluginManager::PluginManager( const std::string& resourcesPath, const std::string& pluginsPath,
std::shared_ptr<ThreadPool> 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<std::string, UICodeEditorPlugin*>( 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<std::string, bool>& PluginManager::getPluginsEnabled() const {
return mPluginsEnabled;
}
void PluginManager::onNewEditor( UICodeEditor* editor ) {
for ( auto& plugin : mPlugins )
editor->registerPlugin( plugin.second );
}
void PluginManager::setPluginsEnabled( const std::map<std::string, bool>& pluginsEnabled ) {
mPluginsEnabled = pluginsEnabled;
for ( const auto& plugin : pluginsEnabled ) {
if ( plugin.second && get( plugin.first ) == nullptr )
setEnabled( plugin.first, true );
}
}
const std::shared_ptr<ThreadPool>& PluginManager::getThreadPool() const {
return mThreadPool;
}
const std::map<std::string, PluginDefinition>& 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> PluginsModel::create( PluginManager* manager ) {
return std::make_shared<PluginsModel>( 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(
<window
id="plugin-manager-window"
lw="800dp" lh="400dp"
padding="8dp"
window-title="Plugin Manager"
window-flags="default|maximize|shadow"
window-min-size="300dp 300dp">
<vbox lw="mp" lh="mp">
<tableview id="plugin-manager-table" lw="mp" lh="fixed" layout_weight="1">
</tableview>
<vbox lw="mp" lh="wc">
<hbox margin-top="4dp" layout-gravity="right">
<pushbutton id="plugin-manager-enabled" enabled="false" />
<!-- <pushbutton id="plugin-manager-preferences" enabled="false" text="Enabled" /> -->
<pushbutton id="plugin-manager-close" text="Close" icon="close" margin-left="4dp" />
</hbox>
</vbox>
</vbox>
</window>
)xml" )
->asType<UIWindow>();
UIWidget* cont = win->getContainer();
UIPushButton* enable = cont->find<UIPushButton>( "plugin-manager-enabled" );
UIPushButton* close = cont->find<UIPushButton>( "plugin-manager-close" );
UITableView* tv = win->getContainer()->find<UITableView>( "plugin-manager-table" );
close->addEventListener( Event::MouseClick, [win]( const Event* event ) {
const MouseEvent* mevent = static_cast<const MouseEvent*>( 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<const MouseEvent*>( 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

View File

@@ -0,0 +1,112 @@
#ifndef ECODE_PLUGINMANAGER_HPP
#define ECODE_PLUGINMANAGER_HPP
#include <eepp/ui/models/model.hpp>
#include <eepp/ui/uicodeeditor.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uiwindow.hpp>
#include <memory>
#include <string>
using namespace EE;
using namespace EE::System;
using namespace EE::UI;
using namespace EE::UI::Models;
namespace ecode {
class PluginManager;
typedef std::function<UICodeEditorPlugin*( const PluginManager* pluginManager )> 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<ThreadPool> 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<std::string, bool>& getPluginsEnabled() const;
void onNewEditor( UICodeEditor* editor );
void setPluginsEnabled( const std::map<std::string, bool>& pluginsEnabled );
const std::shared_ptr<ThreadPool>& getThreadPool() const;
std::function<void( UICodeEditorPlugin* )> onPluginEnabled;
const std::map<std::string, PluginDefinition>& getDefinitions() const;
const PluginDefinition* getDefinitionIndex( const Int64& index ) const;
protected:
std::string mResourcesPath;
std::string mPluginsPath;
std::map<std::string, UICodeEditorPlugin*> mPlugins;
std::map<std::string, bool> mPluginsEnabled;
std::map<std::string, PluginDefinition> mDefinitions;
std::shared_ptr<ThreadPool> mThreadPool;
bool hasDefinition( const std::string& id );
};
class PluginsModel : public Model {
public:
enum Columns { Id, Enabled, Title, Description, Version };
static std::shared_ptr<PluginsModel> 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<std::string> mColumnNames{ "Id", "Enabled", "Title", "Description", "Version" };
};
class UIPluginManager {
public:
static UIWindow* New( UISceneNode* sceneNode, PluginManager* manager );
};
} // namespace ecode
#endif // ECODE_PLUGINMANAGER_HPP

View File

@@ -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