Commit 09dc3d44 authored by Nikita Sirgienko's avatar Nikita Sirgienko

[GSoC 2020] Increase Cantor Plugins Performance

Now all panels belongs to CantorShell instead of Cantor Part, so now there is only one instance of each Panel in Cantor.
The changing of Panels content during Worksheets changes will be done via new methods: saveState, restoreState.
parent 94e968ff
Pipeline #28016 canceled with stage
......@@ -48,8 +48,9 @@
#include "settings.h"
#include "ui_settings.h"
#include "backendchoosedialog.h"
#include <QMetaObject>
CantorShell::CantorShell() : KParts::MainWindow(), m_part(nullptr)
CantorShell::CantorShell() : KParts::MainWindow(), m_part(nullptr), m_panelHandler(nullptr)
{
// set the shell's ui resource file
setXMLFile(QLatin1String("cantor_shell.rc"));
......@@ -75,6 +76,8 @@ CantorShell::CantorShell() : KParts::MainWindow(), m_part(nullptr)
setDockOptions(QMainWindow::AnimatedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::VerticalTabs);
initPanels();
updateNewSubmenu();
}
......@@ -358,7 +361,7 @@ void CantorShell::addWorksheet(const QString& backendName)
{
connect(part, SIGNAL(setCaption(QString,QIcon)), this, SLOT(setTabCaption(QString,QIcon)));
connect(part, SIGNAL(worksheetSave(QUrl)), this, SLOT(onWorksheetSave(QUrl)));
connect(part, SIGNAL(requestOpenWorksheet(QUrl)), this, SLOT(load(QUrl)));
connect(part, SIGNAL(showHelp(QString)), this, SIGNAL(showHelp(QString)));
m_parts.append(part);
if (backend) // If backend empty (loading worksheet from file), then we connect to signal and wait
m_parts2Backends[part] = backend->id();
......@@ -396,11 +399,7 @@ void CantorShell::addWorksheet(const QString& backendName)
void CantorShell::activateWorksheet(int index)
{
QObject* pluginHandler=m_part->findChild<QObject*>(QLatin1String("PanelPluginHandler"));
if (pluginHandler)
disconnect(pluginHandler,SIGNAL(pluginsChanged()), this, SLOT(updatePanel()));
// Save part state before change worksheet
// Save part panels states before change worksheet
if (m_part)
{
QStringList visiblePanelNames;
......@@ -410,6 +409,16 @@ void CantorShell::activateWorksheet(int index)
visiblePanelNames << doc->objectName();
}
m_pluginsVisibility[m_part] = visiblePanelNames;
Cantor::WorksheetAccessInterface* wa=m_part->findChild<Cantor::WorksheetAccessInterface*>(Cantor::WorksheetAccessInterface::Name);
assert(wa);
PanelStates states;
QList<Cantor::PanelPlugin*> plugins=m_panelHandler.plugins(wa->session());
for(Cantor::PanelPlugin* plugin : plugins)
{
states.insert(plugin->name(), plugin->saveState());
}
m_pluginsStates[m_part] = states;
}
m_part=findPart(m_tabWidget->widget(index));
......@@ -418,8 +427,6 @@ void CantorShell::activateWorksheet(int index)
createGUI(m_part);
updateWindowTitle(m_part->url().fileName());
QObject* pluginHandler=m_part->findChild<QObject*>(QLatin1String("PanelPluginHandler"));
connect(pluginHandler, SIGNAL(pluginsChanged()), this, SLOT(updatePanel()));
updatePanel();
}
else
......@@ -482,6 +489,7 @@ void CantorShell::closeTab(int index)
m_tabWidget->removeTab(index);
bool isCurrectPartClosed = m_part ? widget == m_part->widget() : false;
if(widget->objectName()==QLatin1String("ErrorMessage"))
{
widget->deleteLater();
......@@ -495,13 +503,16 @@ void CantorShell::closeTab(int index)
m_parts.removeAll(part);
m_pluginsVisibility.remove(part);
m_parts2Backends.remove(part);
m_pluginsStates.remove(part);
delete part;
}
}
if (m_tabWidget->count() == 0)
setCaption(QString());
updatePanel();
if (isCurrectPartClosed || m_part == nullptr)
updatePanel();
}
bool CantorShell::reallyClose(bool checkAllParts) {
......@@ -645,35 +656,46 @@ KParts::ReadWritePart* CantorShell::findPart(QWidget* widget)
return nullptr;
}
void CantorShell::updatePanel()
void CantorShell::initPanels()
{
unplugActionList(QLatin1String("view_show_panel_list"));
m_panelHandler.loadPlugins();
//remove all of the previous panels (but do not delete the widgets)
foreach(QDockWidget* dock, m_panels)
QList<Cantor::PanelPlugin*> plugins = m_panelHandler.allPlugins();
foreach(Cantor::PanelPlugin* plugin, plugins)
{
QWidget* widget=dock->widget();
if(widget!=nullptr)
if(plugin==nullptr)
{
widget->setParent(this);
widget->hide();
qDebug()<<"somethings wrong";
continue;
}
dock->deleteLater();
}
m_panels.clear();
QList<QAction*> panelActions;
qDebug()<<"adding panel for "<<plugin->name();
plugin->setParentWidget(this);
plugin->connectToShell(this);
QDockWidget* docker=new QDockWidget(plugin->name(), this);
docker->setObjectName(plugin->name());
docker->setWidget(plugin->widget());
addDockWidget ( Qt::RightDockWidgetArea, docker );
docker->hide();
connect(plugin, &Cantor::PanelPlugin::visibilityRequested, this, &CantorShell::pluginVisibilityRequested);
connect(plugin, &Cantor::PanelPlugin::requestRunCommand, this, &CantorShell::pluginCommandRunRequested);
m_panels.append(docker);
Cantor::PanelPluginHandler* handler=m_part->findChild<Cantor::PanelPluginHandler*>(QLatin1String("PanelPluginHandler"));
if(!handler)
{
qDebug()<<"no PanelPluginHandle found for this part";
return;
}
}
QDockWidget* last=nullptr;
bool isNewWorksheet = !m_pluginsVisibility.contains(m_part);
void CantorShell::updatePanel()
{
unplugActionList(QLatin1String("view_show_panel_list"));
QList<QAction*> panelActions;
bool isNewWorksheet = !m_pluginsVisibility.contains(m_part);
if (isNewWorksheet)
{
KConfigGroup panelStatusGroup(KSharedConfig::openConfig(), QLatin1String("PanelsStatus"));
......@@ -685,50 +707,86 @@ void CantorShell::updatePanel()
}
}
QList<Cantor::PanelPlugin*> plugins=handler->plugins();
foreach(Cantor::PanelPlugin* plugin, plugins)
Cantor::WorksheetAccessInterface* wa = nullptr;
if (m_part)
wa = m_part->findChild<Cantor::WorksheetAccessInterface*>(Cantor::WorksheetAccessInterface::Name);
// Worksheet interface can be missing on m_part clossing (and m_part on this moment can be nullptr)
QList<Cantor::PanelPlugin*> plugins;
if (wa)
{
if(plugin==nullptr)
QDockWidget* last=nullptr;
plugins = m_panelHandler.plugins(wa->session());
for(Cantor::PanelPlugin* plugin : plugins)
{
qDebug()<<"somethings wrong";
continue;
}
qDebug()<<"adding panel for "<<plugin->name();
plugin->setParentWidget(this);
if(plugin==nullptr)
{
qDebug()<<"somethings wrong";
continue;
}
QDockWidget* docker=new QDockWidget(plugin->name(), this);
docker->setObjectName(plugin->name());
docker->setWidget(plugin->widget());
addDockWidget ( Qt::RightDockWidgetArea, docker );
qDebug()<<"adding panel for "<<plugin->name();
// Set visibility for dock from saved info
if (isNewWorksheet)
{
if (plugin->showOnStartup())
docker->show();
else
docker->hide();
}
else
{
if (m_pluginsVisibility[m_part].contains(plugin->name()))
docker->show();
if (m_pluginsStates.contains(m_part))
plugin->restoreState(m_pluginsStates[m_part][plugin->name()]);
else
docker->hide();
}
{
Cantor::PanelPlugin::State initState;
initState.session = wa->session();
plugin->restoreState(initState);
}
if(last!=nullptr)
tabifyDockWidget(last, docker);
last=docker;
QDockWidget* foundDocker = nullptr;
for (QDockWidget* docker : m_panels)
if (docker->objectName() == plugin->name())
{
foundDocker = docker;
break;
}
connect(plugin, &Cantor::PanelPlugin::visibilityRequested, this, &CantorShell::pluginVisibilityRequested);
if (!foundDocker)
{
qDebug() << "something wrong: can't find panel for plugin \"" << plugin->name() << "\"";
continue;
}
m_panels.append(docker);
// Set visibility for dock from saved info
if (isNewWorksheet)
{
if (plugin->showOnStartup())
foundDocker->show();
else
foundDocker->hide();
}
else
{
if (m_pluginsVisibility[m_part].contains(plugin->name()))
foundDocker->show();
else
foundDocker->hide();
}
if(last!=nullptr)
tabifyDockWidget(last, foundDocker);
last = foundDocker;
//Create the action to show/hide this panel
panelActions<<docker->toggleViewAction();
//Create the action to show/hide this panel
panelActions<<foundDocker->toggleViewAction();
}
}
// Hide plugins, which don't supported on current session
QList<Cantor::PanelPlugin*> allPlugins=m_panelHandler.allPlugins();
for(Cantor::PanelPlugin* plugin : allPlugins)
{
if (plugins.indexOf(plugin) == -1)
for (QDockWidget* docker : m_panels)
if (docker->objectName() == plugin->name())
{
docker->hide();
break;
}
}
plugActionList(QLatin1String("view_show_panel_list"), panelActions);
......@@ -769,7 +827,7 @@ void CantorShell::pluginVisibilityRequested()
Cantor::PanelPlugin* plugin = static_cast<Cantor::PanelPlugin*>(sender());
for (QDockWidget* docker: m_panels)
{
if (plugin->name() == docker->windowTitle())
if (plugin->name() == docker->objectName())
{
if (docker->isHidden())
docker->show();
......@@ -829,3 +887,11 @@ void CantorShell::updateBackendForPart(const QString& backend)
}
}
}
void CantorShell::pluginCommandRunRequested(const QString& cmd)
{
if (m_part)
{
QMetaObject::invokeMethod(m_part, "runCommand", Qt::QueuedConnection, Q_ARG(QString, cmd));
}
}
......@@ -28,6 +28,9 @@
#include <QStringList>
#include <QMap>
#include "lib/panelpluginhandler.h"
#include "lib/panelplugin.h"
class QTabWidget;
class KTextEdit;
class KRecentFilesAction;
......@@ -40,6 +43,8 @@ namespace KParts{
class ReadWritePart;
}
using PanelStates = QMap<QString, Cantor::PanelPlugin::State>;
/**
* This is the application "Shell". It has a menubar, toolbar, and
* statusbar but relies on the "Part" to do all the real work.
......@@ -75,6 +80,9 @@ protected:
*/
void readProperties(const KConfigGroup &) override;
Q_SIGNALS:
void showHelp(QString);
public Q_SLOTS:
void addWorksheet(const QString& backendName);
/// Use this method/slot to load whatever file/URL you have
......@@ -97,10 +105,12 @@ private Q_SLOTS:
void downloadExamples();
void openExample();
void initPanels();
void updatePanel();
void updateNewSubmenu();
void pluginVisibilityRequested();
void pluginCommandRunRequested(const QString& cmd);
private:
void setupActions();
......@@ -113,6 +123,7 @@ private:
private:
QMap<KParts::ReadWritePart*, QStringList> m_pluginsVisibility;
QMap<KParts::ReadWritePart*, PanelStates> m_pluginsStates;
QList<KParts::ReadWritePart *> m_parts;
QMap<KParts::ReadWritePart*, QString> m_parts2Backends;
KParts::ReadWritePart* m_part;
......@@ -122,6 +133,8 @@ private:
QDockWidget* m_helpDocker;
KRecentFilesAction* m_recentProjectsAction;
Cantor::PanelPluginHandler m_panelHandler;
// For better UX: set previous used filter in "Open" action as default filter
QString m_previousFilter;
};
......
......@@ -110,7 +110,6 @@ class WorksheetAccessInterfaceImpl : public Cantor::WorksheetAccessInterface
CantorPart::CantorPart( QWidget *parentWidget, QObject *parent, const QVariantList & args ): KParts::ReadWritePart(parent),
m_searchBar(nullptr),
m_panelHandler(new Cantor::PanelPluginHandler(this)),
m_initProgressDlg(nullptr),
m_showProgressDlg(true),
m_currectZoomAction(nullptr),
......@@ -118,8 +117,6 @@ CantorPart::CantorPart( QWidget *parentWidget, QObject *parent, const QVariantLi
m_statusBarBlocked(false),
m_sessionStatusCounter(0)
{
connect(m_panelHandler, &Cantor::PanelPluginHandler::pluginsChanged, this, &CantorPart::pluginsChanged);
QString backendName;
if(!args.isEmpty())
backendName = args.first().toString();
......@@ -693,7 +690,6 @@ void CantorPart::initialized()
connect(m_worksheet->session(), &Cantor::Session::error, this, &CantorPart::showSessionError);
loadAssistants();
m_panelHandler->setSession(m_worksheet->session());
adjustGuiToSession();
// Don't set modification flag, if we add command entry in empty worksheet
......@@ -773,12 +769,6 @@ void CantorPart::updateCaption()
emit setCaption(filename+QLatin1Char(' ') + i18n("[read-only]"), QIcon());
}
void CantorPart::pluginsChanged()
{
for (auto* plugin : m_panelHandler->plugins())
connect(plugin, &Cantor::PanelPlugin::requestRunCommand, this, &CantorPart::runCommand);
}
void CantorPart::loadAssistants()
{
qDebug()<<"loading assistants...";
......
......@@ -84,7 +84,6 @@ Q_SIGNALS:
void setCaption(const QString& caption, const QIcon& icon);
void showHelp(const QString& help);
void worksheetSave(const QUrl& url);
void requestOpenWorksheet(const QUrl& url);
void setBackendName(const QString& name);
public Q_SLOTS:
......@@ -132,7 +131,6 @@ protected Q_SLOTS:
void worksheetSessionLoginDone();
void initialized();
void pluginsChanged();
void runCommand(const QString& value);
void runAssistant();
......@@ -168,7 +166,6 @@ private:
WorksheetView *m_worksheetview;
SearchBar *m_searchBar;
QPointer<ScriptEditorWidget> m_scriptEditor;
Cantor::PanelPluginHandler* m_panelHandler;
QProgressDialog* m_initProgressDlg;
bool m_showProgressDlg;
......
......@@ -75,18 +75,24 @@ QString PanelPlugin::name()
return d->name;
}
Session* PanelPlugin::session()
Cantor::PanelPlugin::State Cantor::PanelPlugin::saveState()
{
return d->session;
Cantor::PanelPlugin::State state;
state.session = d->session;
return state;
}
void PanelPlugin::setSession(Session* session)
void Cantor::PanelPlugin::restoreState(const Cantor::PanelPlugin::State& state)
{
d->session=session;
onSessionChanged();
d->session = state.session;
}
Cantor::Session * Cantor::PanelPlugin::session()
{
return d->session;
}
void PanelPlugin::onSessionChanged()
void Cantor::PanelPlugin::connectToShell(QObject* /* cantorShell */)
{
}
......
......@@ -40,6 +40,12 @@ class CANTOR_EXPORT PanelPlugin : public QObject
{
Q_OBJECT
public:
struct State {
Session* session{nullptr};
QVector<QVariant> inners;
};
/**
* Create a new PanelPlugin
* @param parent the parent Object @see QObject
......@@ -91,14 +97,21 @@ class CANTOR_EXPORT PanelPlugin : public QObject
QWidget* parentWidget();
/**
* sets the session this plugin operates on
* Save state of panel to storable form
*
**/
void setSession(Session* session);
virtual State saveState();
/**
* returns the session
* Restore state
* Can contains only session - this is init state from Cantor shell
*/
Session* session();
virtual void restoreState(const State& state);
/**
* For proper connection to Cantor shell. All connections should be done here
*/
virtual void connectToShell(QObject* cantorShell);
/**
* Show on worksheet startup or not
......@@ -106,13 +119,13 @@ class CANTOR_EXPORT PanelPlugin : public QObject
*/
virtual bool showOnStartup();
protected:
Session* session();
Q_SIGNALS:
void requestRunCommand(const QString& cmd);
void visibilityRequested();
protected:
virtual void onSessionChanged();
private:
PanelPluginPrivate* d;
};
......
......@@ -35,14 +35,12 @@ class Cantor::PanelPluginHandlerPrivate
{
public:
QList<Cantor::PanelPlugin*> plugins;
Cantor::Session* session;
};
PanelPluginHandler::PanelPluginHandler( QObject* parent ) : QObject(parent) ,
d(new PanelPluginHandlerPrivate)
{
setObjectName(QStringLiteral("PanelPluginHandler"));
d->session=nullptr;
}
PanelPluginHandler::~PanelPluginHandler()
......@@ -52,34 +50,28 @@ PanelPluginHandler::~PanelPluginHandler()
void PanelPluginHandler::loadPlugins()
{
if(d->session==nullptr)
return;
qDebug()<<"loading panel plugins for session of type "<<d->session->backend()->name();
QStringList panelDirs;
foreach(const QString &dir, QCoreApplication::libraryPaths()){
foreach(const QString &dir, QCoreApplication::libraryPaths()) {
panelDirs << dir + QDir::separator() + QLatin1String("cantor/panels");
}
QPluginLoader loader;
const Cantor::Backend::Capabilities capabilities = d->session->backend()->capabilities();
const QStringList& extensions = d->session->backend()->extensions();
foreach(const QString &dir, panelDirs){
qDebug() << "dir: " << dir;
QStringList panels;
QDir panelDir = QDir(dir);
panels = panelDir.entryList();
foreach (const QString &panel, panels){
foreach (const QString &panel, panels)
{
if (panel==QLatin1String(".") || panel==QLatin1String(".."))
continue;
loader.setFileName(dir + QDir::separator() + panel);
if (!loader.load()){
qDebug() << "Error while loading panel: " << panel;
qDebug() << "Error while loading panel" << panel << ": \"" << loader.errorString() << "\"";
continue;
}
......@@ -89,47 +81,50 @@ void PanelPluginHandler::loadPlugins()
KPluginMetaData info(loader);
plugin->setPluginInfo(info);
bool supported=true;
foreach(const QString& req, plugin->requiredExtensions()){
// FIXME: That req.isEmpty() is there just because Help Panel has req
// empty, returning FALSE when the comparison must to return TRUE.
supported = supported && (extensions.contains(req) || req.isEmpty());
}
// This set session to null inside plugin
Cantor::PanelPlugin::State emptyState;
plugin->restoreState(emptyState);
supported = supported && ( (capabilities & plugin->requiredCapabilities()) == plugin->requiredCapabilities());
if(supported)
{
qDebug() << "plugin " << info.name()<<" is supported, requires extensions " << plugin->requiredExtensions();
d->plugins.append(plugin);
plugin->setSession(d->session);
}else
{
qDebug() << "plugin " << info.name() <<" is not supported";
plugin->deleteLater();
}
d->plugins.append(plugin);
}
}
emit pluginsChanged();
}
void PanelPluginHandler::setSession(Session* session)
{
qDeleteAll(d->plugins);
d->plugins.clear();
d->session=session;
loadPlugins();
}
QList<PanelPlugin*> PanelPluginHandler::plugins()
QList<Cantor::PanelPlugin *> Cantor::PanelPluginHandler::allPlugins()
{
return d->plugins;
}
void PanelPluginHandler::addPlugin(PanelPlugin* plugin)
QList<PanelPlugin*> PanelPluginHandler::plugins(Session* session)
{
d->plugins.append(plugin);
}
QList<PanelPlugin*> pluginsForSession;
if (session == nullptr)
return pluginsForSession;
const Cantor::Backend::Capabilities capabilities = session->backend()->capabilities();
const QStringList& extensions = session->backend()->extensions();
qDebug()<<"loading panel plugins for session of type "<<session->backend()->name();
for(Cantor::PanelPlugin* plugin : d->plugins)
{
bool supported=true;
foreach(const QString& req, plugin->requiredExtensions()){
// FIXME: That req.isEmpty() is there just