feat: 调整插件模板;移除脚本编译元数据支持;完善插件接口;

This commit is contained in:
寂静的羽夏 2025-01-04 21:04:12 +08:00
parent b66f802c3a
commit 0713b4d8dc
17 changed files with 320 additions and 769 deletions

1
.gitmodules vendored
View File

@ -7,6 +7,7 @@
[submodule "3rdparty/AngelScript"]
path = 3rdparty/AngelScript
url = git@github.com:Wing-summer/AngelScript.git
branch=stable
[submodule "3rdparty/json"]
path = 3rdparty/json
url = git@github.com:nlohmann/json.git

@ -1 +1 @@
Subproject commit 387b48653eb0840765bcbf24773de50ed632ad59
Subproject commit d259eab41fd08fbde83cf1a7b01f4def6a09e895

2
3rdparty/json vendored

@ -1 +1 @@
Subproject commit 663058e7d18241338aec42846d4f77995275ccf6
Subproject commit 60c48755e3f717eace7830d7bbc0d8f1a5e0cc8a

View File

@ -18,6 +18,9 @@
#define NOMINMAX 1
#endif
#include <lmcons.h>
#ifdef PASCAL
#undef PASCAL
#endif
#include <windows.h>
#endif

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ from colorama import Fore, Style
def install():
installer_path = os.path.dirname(os.path.abspath(__file__))
install_path = os.path.join(getQtTemplateDir(), "winghexplugin")
shutil.rmtree(install_path, ignore_errors=True) # uninstall first
shutil.copytree(os.path.join(installer_path, "winghexplugin"),
install_path, dirs_exist_ok=True)
print(Fore.GREEN + "WingHexExplorer2 plugin Template was installed under: " + install_path + Style.RESET_ALL)
@ -27,7 +28,7 @@ def getQtTemplateDir() -> pathlib.Path:
# https://docs.huihoo.com/qt/qtcreator/4.2/creator-project-wizards.html#locating-wizards
home = pathlib.Path.home()
if sys.platform == "win32":
return home / "AppData/QtProject/qtcreator/templates/wizards"
return home / "AppData/Roaming/QtProject/qtcreator/templates/wizards"
else:
return home / ".config/QtProject/qtcreator/templates/wizards"

View File

@ -18,7 +18,9 @@ endif()
option(TEST_MODE TRUE)
set(WINGHEX_SDK "%{WingHexSDKPath}")
if(NOT EXISTS "${WINGHEX_SDK}")
message(FATAL_ERROR "Please config the SDK Path - WINGHEX_SDK")
endif()
set(PLUGIN_INTERFACE_FOUND FALSE)
set(PLUGIN_SETPAGE_FOUND FALSE)
@ -96,7 +98,11 @@ if(TEST_MODE)
# this variable. Because this test plugin is a subproject of the main
# project, use CMAKE_BINARY_DIR
set(WINGHEX_PATH "%{WingHexEXEPath}")
if(NOT EXISTS "${WINGHEX_PATH}")
message(
FATAL_ERROR "Please config the WingHexExplorer2 Path - WINGHEX_PATH"
)
endif()
set(WINGHEX_PLUGIN_PATH "${WINGHEX_PATH}/plugin")
add_custom_command(

View File

@ -12,8 +12,6 @@
"options":
[
{ "key": "WingHexSDKPath", "value": "%{SDKPath}" },
{ "key": "WingHexEXEPath", "value": "%{WingHexPath}" },
{ "key": "ProjectFile", "value": "%{JS: value('CMakeFile') }" },
{ "key": "PluginNameLower", "value": "%{JS: value('PluginName').toLowerCase()}"},
{ "key": "PluginJsonFile", "value": "%{JS: Util.fileName(value('PluginName'), 'json.in')}" },
@ -123,28 +121,6 @@
{
"text": "https://www.%{JS: encodeURIComponent(value('VendorName').toLowerCase())}.com"
}
},
{
"name": "SDKPath",
"persistenceKey": "SDKPath",
"trDisplayName": "SDK Path:",
"mandatory": true,
"type": "PathChooser",
"data":
{
"kind": "existingDirectory"
}
},
{
"name": "WingHexPath",
"persistenceKey": "WingHexPath",
"type": "PathChooser",
"trDisplayName": "WingHexExplorer2 Installation:",
"mandatory": true,
"data":
{
"kind": "existingDirectory"
}
}
]
},

View File

@ -50,175 +50,6 @@ int asBuilder::Build() {
if (r < 0)
return r;
// After the script has been built, the metadata strings should be
// stored for later lookup by function id, type id, and variable index
for (int n = 0; n < (int)foundDeclarations.size(); n++) {
SMetadataDecl &decl = foundDeclarations[n];
module->SetDefaultNamespace(decl.nameSpace.toUtf8());
if (decl.type == MDT_TYPE) {
// Find the type id
int typeId = module->GetTypeIdByDecl(decl.declaration.toUtf8());
Q_ASSERT(typeId >= 0);
if (typeId >= 0)
typeMetadataMap.insert(typeId, decl.metadata);
} else if (decl.type == MDT_FUNC) {
if (decl.parentClass.isEmpty()) {
// Find the function id
asIScriptFunction *func =
module->GetFunctionByDecl(decl.declaration.toUtf8());
Q_ASSERT(func);
if (func)
funcMetadataMap.insert(func->GetId(), decl.metadata);
} else {
// Find the method id
int typeId = module->GetTypeIdByDecl(decl.parentClass.toUtf8());
Q_ASSERT(typeId > 0);
auto it = classMetadataMap.find(typeId);
if (it == classMetadataMap.end()) {
classMetadataMap.insert(typeId,
SClassMetadata(decl.parentClass));
it = classMetadataMap.find(typeId);
}
asITypeInfo *type = engine->GetTypeInfoById(typeId);
asIScriptFunction *func =
type->GetMethodByDecl(decl.declaration.toUtf8());
Q_ASSERT(func);
if (func)
it.value().funcMetadataMap.insert(func->GetId(),
decl.metadata);
}
} else if (decl.type == MDT_VIRTPROP) {
if (decl.parentClass.isEmpty()) {
// Find the global virtual property accessors
asIScriptFunction *func = module->GetFunctionByName(
(QStringLiteral("get_") + decl.declaration).toUtf8());
if (func)
funcMetadataMap.insert(func->GetId(), decl.metadata);
func = module->GetFunctionByName(
(QStringLiteral("set_") + decl.declaration).toUtf8());
if (func)
funcMetadataMap.insert(func->GetId(), decl.metadata);
} else {
// Find the method virtual property accessors
int typeId = module->GetTypeIdByDecl(decl.parentClass.toUtf8());
Q_ASSERT(typeId > 0);
auto it = classMetadataMap.find(typeId);
if (it == classMetadataMap.end()) {
classMetadataMap.insert(typeId,
SClassMetadata(decl.parentClass));
it = classMetadataMap.find(typeId);
}
asITypeInfo *type = engine->GetTypeInfoById(typeId);
asIScriptFunction *func = type->GetMethodByName(
(QStringLiteral("get_") + decl.declaration).toUtf8());
if (func)
it.value().funcMetadataMap.insert(func->GetId(),
decl.metadata);
func = type->GetMethodByName(
(QStringLiteral("set_") + decl.declaration).toUtf8());
if (func)
it.value().funcMetadataMap.insert(func->GetId(),
decl.metadata);
}
} else if (decl.type == MDT_VAR) {
if (decl.parentClass.isEmpty()) {
// Find the global variable index
int varIdx =
module->GetGlobalVarIndexByName(decl.declaration.toUtf8());
Q_ASSERT(varIdx >= 0);
if (varIdx >= 0)
varMetadataMap.insert(varIdx, decl.metadata);
} else {
int typeId = module->GetTypeIdByDecl(decl.parentClass.toUtf8());
Q_ASSERT(typeId > 0);
// Add the classes if needed
auto it = classMetadataMap.find(typeId);
if (it == classMetadataMap.end()) {
classMetadataMap.insert(typeId,
SClassMetadata(decl.parentClass));
it = classMetadataMap.find(typeId);
}
// Add the variable to class
asITypeInfo *objectType = engine->GetTypeInfoById(typeId);
int idx = -1;
// Search through all properties to get proper declaration
for (asUINT i = 0; i < (asUINT)objectType->GetPropertyCount();
++i) {
const char *name;
objectType->GetProperty(i, &name);
if (decl.declaration == name) {
idx = i;
break;
}
}
// If found, add it
Q_ASSERT(idx >= 0);
if (idx >= 0)
it.value().varMetadataMap.insert(idx, decl.metadata);
}
} else if (decl.type == MDT_FUNC_OR_VAR) {
if (decl.parentClass.isEmpty()) {
// Find the global variable index
int varIdx =
module->GetGlobalVarIndexByName(decl.name.toUtf8());
if (varIdx >= 0)
varMetadataMap.insert(varIdx, decl.metadata);
else {
asIScriptFunction *func =
module->GetFunctionByDecl(decl.declaration.toUtf8());
Q_ASSERT(func);
if (func)
funcMetadataMap.insert(func->GetId(), decl.metadata);
}
} else {
int typeId = module->GetTypeIdByDecl(decl.parentClass.toUtf8());
Q_ASSERT(typeId > 0);
// Add the classes if needed
auto it = classMetadataMap.find(typeId);
if (it == classMetadataMap.end()) {
classMetadataMap.insert(typeId,
SClassMetadata(decl.parentClass));
it = classMetadataMap.find(typeId);
}
// Add the variable to class
asITypeInfo *objectType = engine->GetTypeInfoById(typeId);
int idx = -1;
// Search through all properties to get proper declaration
for (asUINT i = 0; i < (asUINT)objectType->GetPropertyCount();
++i) {
const char *name;
objectType->GetProperty(i, &name);
if (decl.name == name) {
idx = i;
break;
}
}
// If found, add it
if (idx >= 0)
it.value().varMetadataMap.insert(idx, decl.metadata);
else {
// Look for the matching method instead
asITypeInfo *type = engine->GetTypeInfoById(typeId);
asIScriptFunction *func =
type->GetMethodByDecl(decl.declaration.toUtf8());
if (func)
it.value().funcMetadataMap.insert(func->GetId(),
decl.metadata);
}
}
}
}
module->SetDefaultNamespace("");
return 0;

View File

@ -98,47 +98,11 @@ QString AsPreprocesser::GetSectionName(unsigned int idx) const {
return includedScripts.at(idx);
}
QVector<QString> AsPreprocesser::GetMetadataForType(int typeId) {
return typeMetadataMap.value(typeId);
}
QVector<QString> AsPreprocesser::GetMetadataForFunc(asIScriptFunction *func) {
return funcMetadataMap.value(func->GetId());
}
QVector<QString> AsPreprocesser::GetMetadataForVar(int varIdx) {
return varMetadataMap.value(varIdx);
}
QVector<QString> AsPreprocesser::GetMetadataForTypeProperty(int typeId,
int varIdx) {
if (classMetadataMap.contains(typeId)) {
return varMetadataMap.value(varIdx);
}
return {};
}
QVector<QString>
AsPreprocesser::GetMetadataForTypeMethod(int typeId,
asIScriptFunction *method) {
if (method) {
if (classMetadataMap.contains(typeId)) {
return funcMetadataMap.value(method->GetId());
}
}
return {};
}
void AsPreprocesser::ClearAll() {
includedScripts.clear();
currentClass.clear();
currentNamespace.clear();
foundDeclarations.clear();
typeMetadataMap.clear();
funcMetadataMap.clear();
varMetadataMap.clear();
}
int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
@ -335,26 +299,8 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
continue;
}
// Is this the start of metadata?
if (token == "[") {
// Get the metadata string
pos = ExtractMetadata(modifiedScript, pos, metadata);
// Determine what this metadata is for
int type;
ExtractDeclaration(modifiedScript, pos, name, declaration, type);
// Store away the declaration in a map for lookup after the build
// has completed
if (type > 0) {
SMetadataDecl decl(metadata, name, declaration, type,
currentClass, currentNamespace);
foundDeclarations.push_back(decl);
}
} else
// Is this a preprocessor directive?
if (token == "#" && (qsizetype(pos + 1) < modifiedScript.size())) {
// Is this a preprocessor directive?
if (token == "#" && (qsizetype(pos + 1) < modifiedScript.size())) {
int start = pos++;
t = engine->ParseToken(modifiedScript.data() + pos,
@ -679,175 +625,3 @@ void AsPreprocesser::AddScriptSection(const QString &section,
data.script = code;
modifiedScripts.append(data);
}
int AsPreprocesser::ExtractMetadata(QByteArray &modifiedScript, int pos,
QVector<QString> &metadata) {
metadata.clear();
// Extract all metadata. They can be separated by whitespace and comments
for (;;) {
QString metadataString;
// Overwrite the metadata with space characters to allow compilation
modifiedScript[pos] = ' ';
// Skip opening brackets
pos += 1;
int level = 1;
asUINT len = 0;
while (level > 0 && pos < (int)modifiedScript.size()) {
asETokenClass t = engine->ParseToken(
modifiedScript.data() + pos, modifiedScript.size() - pos, &len);
if (t == asTC_KEYWORD) {
if (modifiedScript[pos] == '[')
level++;
else if (modifiedScript[pos] == ']')
level--;
}
// Copy the metadata to our buffer
if (level > 0)
metadataString.append(modifiedScript.mid(pos, len));
// Overwrite the metadata with space characters to allow compilation
if (t != asTC_WHITESPACE)
OverwriteCode(modifiedScript, pos, len);
pos += len;
}
metadata.push_back(metadataString);
// Check for more metadata. Possibly separated by comments
asETokenClass t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
while (t == asTC_COMMENT || t == asTC_WHITESPACE) {
pos += len;
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
}
if (modifiedScript[pos] != '[')
break;
}
return pos;
}
int AsPreprocesser::ExtractDeclaration(QByteArray &modifiedScript, int pos,
QString &name, QString &declaration,
int &type) {
declaration.clear();
type = 0;
int start = pos;
QString token;
asUINT len = 0;
asETokenClass t = asTC_WHITESPACE;
// Skip white spaces, comments, and leading decorators
do {
pos += len;
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
token = modifiedScript.mid(pos, len);
} while (t == asTC_WHITESPACE || t == asTC_COMMENT || token == "private" ||
token == "protected" || token == "shared" || token == "external" ||
token == "final" || token == "abstract");
// We're expecting, either a class, interface, function, or variable
// declaration
if (t == asTC_KEYWORD || t == asTC_IDENTIFIER) {
token = modifiedScript.mid(pos, len);
if (token == "interface" || token == "class" || token == "enum") {
// Skip white spaces and comments
do {
pos += len;
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
} while (t == asTC_WHITESPACE || t == asTC_COMMENT);
if (t == asTC_IDENTIFIER) {
type = MDT_TYPE;
declaration = modifiedScript.mid(pos, len);
pos += len;
return pos;
}
} else {
// For function declarations, store everything up to the start of
// the statement block, except for succeeding decorators (final,
// override, etc)
// For variable declaration store just the name as there can only be
// one
// We'll only know if the declaration is a variable or function
// declaration when we see the statement block, or absense of a
// statement block.
bool hasParenthesis = false;
int nestedParenthesis = 0;
declaration.append(modifiedScript.mid(pos, len));
pos += len;
for (; pos < (int)modifiedScript.size();) {
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
token = modifiedScript.mid(pos, len);
if (t == asTC_KEYWORD) {
if (token == "{" && nestedParenthesis == 0) {
if (hasParenthesis) {
// We've found the end of a function signature
type = MDT_FUNC;
} else {
// We've found a virtual property. Just keep the
// name
declaration = name;
type = MDT_VIRTPROP;
}
return pos;
}
if ((token == "=" && !hasParenthesis) || token == ";") {
if (hasParenthesis) {
// The declaration is ambigous. It can be a variable
// with initialization, or a function prototype
type = MDT_FUNC_OR_VAR;
} else {
// Substitute the declaration with just the name
declaration = name;
type = MDT_VAR;
}
return pos;
} else if (token == "(") {
nestedParenthesis++;
// This is the first parenthesis we encounter. If the
// parenthesis isn't followed by a statement block, then
// this is a variable declaration, in which case we
// should only store the type and name of the variable,
// not the initialization parameters.
hasParenthesis = true;
} else if (token == ")") {
nestedParenthesis--;
}
} else if (t == asTC_IDENTIFIER) {
// If a parenthesis is already found then the name is
// already known so it must not be overwritten
if (!hasParenthesis)
name = token;
}
// Skip trailing decorators
if (!hasParenthesis || nestedParenthesis > 0 ||
t != asTC_IDENTIFIER ||
(token != "final" && token != "override" &&
token != "delete" && token != "property"))
declaration += token;
pos += len;
}
}
}
return start;
}

View File

@ -107,22 +107,6 @@ public:
QString GetSectionName(unsigned int idx) const;
// Get metadata declared for classes, interfaces, and enums
QVector<QString> GetMetadataForType(int typeId);
// Get metadata declared for functions
QVector<QString> GetMetadataForFunc(asIScriptFunction *func);
// Get metadata declared for global variables
QVector<QString> GetMetadataForVar(int varIdx);
// Get metadata declared for class variables
QVector<QString> GetMetadataForTypeProperty(int typeId, int varIdx);
// Get metadata declared for class methods
QVector<QString> GetMetadataForTypeMethod(int typeId,
asIScriptFunction *method);
protected:
void ClearAll();
int ProcessScriptSection(const QByteArray &script, int length,
@ -147,51 +131,9 @@ protected:
PRAGMACALLBACK_t pragmaCallback;
void *pragmaParam;
int ExtractMetadata(QByteArray &modifiedScript, int pos,
QVector<QString> &metadata);
int ExtractDeclaration(QByteArray &modifiedScript, int pos, QString &name,
QString &declaration, int &type);
enum METADATATYPE {
MDT_TYPE = 1,
MDT_FUNC = 2,
MDT_VAR = 3,
MDT_VIRTPROP = 4,
MDT_FUNC_OR_VAR = 5
};
// Temporary structure for storing metadata and declaration
struct SMetadataDecl {
SMetadataDecl(const QVector<QString> &m, const QString &n,
const QString &d, int t, const QString &c,
const QString &ns)
: metadata(m), name(n), declaration(d), type(t), parentClass(c),
nameSpace(ns) {}
QVector<QString> metadata;
QString name;
QString declaration;
int type;
QString parentClass;
QString nameSpace;
};
QVector<SMetadataDecl> foundDeclarations;
QString currentClass;
QString currentNamespace;
// Storage of metadata for global declarations
QMap<int, QVector<QString>> typeMetadataMap;
QMap<int, QVector<QString>> funcMetadataMap;
QMap<int, QVector<QString>> varMetadataMap;
// Storage of metadata for class member declarations
struct SClassMetadata {
SClassMetadata(const QString &aName) : className(aName) {}
QString className;
QMap<int, QVector<QString>> funcMetadataMap;
QMap<int, QVector<QString>> varMetadataMap;
};
QMap<int, SClassMetadata> classMetadataMap;
QVector<QString> includedScripts;
QVector<QString> definedWords;

View File

@ -210,7 +210,6 @@ EditorView::FindError EditorView::find(const FindDialog::Result &result) {
m_findResults->clear();
auto lineWidth = m_hex->renderer()->hexLineWidth();
auto docLen = d->length();
for (auto &ritem : results) {
FindResult r;
r.offset = ritem;
@ -616,8 +615,8 @@ FindResultModel::FindInfo EditorView::readContextFinding(qsizetype offset,
auto header = doc->read(offset, qMin(findSize, halfSize));
QByteArray tailer;
if (header.size() < findSize) {
tailer = doc->read(
offset, qMin(findSize, qsizetype(maxDisplayBytes) - halfSize));
auto len = qMin(findSize, qsizetype(maxDisplayBytes) - halfSize);
tailer = doc->read(offset + findSize - len, len);
}
auto left = qsizetype(maxDisplayBytes) - header.size() - tailer.size();

View File

@ -220,6 +220,8 @@ MainWindow::MainWindow(SplashDialog *splash) : FramelessMainWindow() {
auto plgview = m_toolBtneditors.value(PLUGIN_VIEWS);
plgview->setEnabled(!plgview->menu()->isEmpty());
finishBuildDockSystem();
// load saved docking layout
if (splash)
splash->setInfoText(tr("SetupDockingLayout"));
@ -443,7 +445,9 @@ void MainWindow::buildUpDockSystem(QWidget *container) {
qApp->processEvents();
m_bottomViewArea = bottomRightArea;
}
void MainWindow::finishBuildDockSystem() {
// set the first tab visible
for (auto &item : m_dock->openedDockAreas()) {
for (int i = 0; i < item->dockWidgetsCount(); ++i) {
@ -455,7 +459,6 @@ void MainWindow::buildUpDockSystem(QWidget *container) {
item->setCurrentIndex(i);
break;
}
qApp->processEvents();
}
}

View File

@ -91,6 +91,8 @@ private:
private:
void buildUpRibbonBar();
void buildUpDockSystem(QWidget *container);
void finishBuildDockSystem();
ads::CDockAreaWidget *buildUpLogDock(ads::CDockManager *dock,
ads::DockWidgetArea area,
ads::CDockAreaWidget *areaw = nullptr);

View File

@ -159,9 +159,6 @@ signals:
const QString &encoding = QString());
Q_REQUIRED_RESULT QByteArray readBytes(qsizetype offset, qsizetype count);
// an extension for AngelScript
// void read(? &in); // this function can read bytes to input container
Q_REQUIRED_RESULT qsizetype searchForward(qsizetype begin,
const QByteArray &ba);
Q_REQUIRED_RESULT qsizetype searchBackward(qsizetype begin,
@ -515,6 +512,8 @@ struct SenderInfo {
#define WINGAPI_ARG(type, data) QArgument<type>(#type, data)
#define WINGAPI_RETURN_ARG(type, data) QReturnArgument<type>(#type, data)
enum class AppTheme { Dark, Light };
class IWingPlugin : public QObject {
Q_OBJECT
public:
@ -577,6 +576,7 @@ public:
virtual bool init(const std::unique_ptr<QSettings> &set) = 0;
virtual void unload(std::unique_ptr<QSettings> &set) = 0;
virtual const QString pluginName() const = 0;
virtual QIcon pluginIcon() const { return {}; }
virtual const QString pluginAuthor() const = 0;
virtual uint pluginVersion() const = 0;
virtual const QString pluginComment() const = 0;
@ -616,10 +616,6 @@ public:
return {};
}
signals:
// QHash< obj-names, decl-members >
bool registerScriptObj(const QString &obj, const QStringList &members);
public:
virtual void eventSelectionChanged(const QByteArrayList &selections,
bool isPreview) {
@ -649,6 +645,9 @@ signals:
void error(const QString &message);
void info(const QString &message);
// theme
WingHex::AppTheme currentAppTheme();
// not available for AngelScript
// only for plugin UI extenstion

View File

@ -21,6 +21,7 @@
#include "class/languagemanager.h"
#include "class/logger.h"
#include "class/settingmanager.h"
#include "class/skinmanager.h"
#include "class/wingfiledialog.h"
#include "class/winginputdialog.h"
#include "class/wingmessagebox.h"
@ -769,6 +770,17 @@ void PluginSystem::connectBaseInterface(IWingPlugin *plg) {
Logger::critical(
packLogMessage(plg->metaObject()->className(), message));
});
connect(plg, &IWingPlugin::currentAppTheme, this,
[]() -> WingHex::AppTheme {
auto theme = SkinManager::instance().currentTheme();
switch (theme) {
case SkinManager::Theme::Dark:
return WingHex::AppTheme::Dark;
case SkinManager::Theme::Light:
return WingHex::AppTheme::Light;
}
return WingHex::AppTheme::Dark; // fallback to default theme
});
connect(plg, &IWingPlugin::createDialog, this,
[=](QWidget *w) -> QDialog * {
if (!checkThreadAff()) {

View File

@ -49,7 +49,9 @@ PluginSettingDialog::PluginSettingDialog(QWidget *parent)
auto pico = ICONRES("plugin");
ui->plglist->clear();
for (auto &p : plgsys.plugins()) {
ui->plglist->addItem(new QListWidgetItem(pico, p->pluginName()));
auto pco = p->pluginIcon();
ui->plglist->addItem(
new QListWidgetItem(pco.isNull() ? pico : pco, p->pluginName()));
}
ui->txtc->clear();
@ -96,7 +98,7 @@ void PluginSettingDialog::on_plglist_itemSelectionChanged() {
auto &plgsys = PluginSystem::instance();
auto plg = plgsys.plugin(ui->plglist->currentRow());
ui->txtc->clear();
ui->txtc->append(tr("pluginName") + " : " + plg->pluginName());
ui->txtc->append(tr("pluginAuthor") + " : " + plg->pluginAuthor());
ui->txtc->append(tr("pluginVersion") + " : " +