Proof of concept for having the context actions statically defined in the module.

Needs work for correctly placing the items in menu and on toolbar.
Old system still needs to be removed left in place to keep app useable.
This commit is contained in:
eelke 2019-08-14 09:06:48 +02:00
parent 7f09d5fe07
commit 601d071d0f
21 changed files with 303 additions and 70 deletions

View file

@ -12,8 +12,8 @@
#include <QVBoxLayout>
#include <unordered_set>
CatalogInspector::CatalogInspector(IPluginContentWidgetContext *context_, QWidget *parent)
: PluginContentWidget(context_, parent)
CatalogInspector::CatalogInspector(IPluginContentWidgetContext *context_, PluginModule *module, QWidget *parent)
: PluginContentWidget(context_, module, parent)
{
m_tabWidget = new QTabWidget(this);
m_tablesPage = new CatalogTablesPage(this);
@ -93,8 +93,8 @@ void CatalogInspectorModule::moduleAction_open(
const ModuleActionParameters &params
)
{
auto ct = new CatalogInspector(context, nullptr);
context->addContentWidget(this, ct);
auto ct = new CatalogInspector(context, this);
context->addContentWidget(ct);
auto nsf = params.at("namespace-filter").toString();
NamespaceFilter filter = NamespaceFilter::User;
if (nsf == "pg_catalog") filter = NamespaceFilter::PgCatalog;

View file

@ -16,7 +16,8 @@ class QTabWidget;
class CatalogInspector : public PluginContentWidget {
Q_OBJECT
public:
explicit CatalogInspector(IPluginContentWidgetContext *context, QWidget *parent = nullptr);
explicit CatalogInspector(IPluginContentWidgetContext *context, PluginModule *module,
QWidget *parent = nullptr);
~CatalogInspector();
void setCatalog(std::shared_ptr<PgDatabaseCatalog> cat);

View file

@ -5,8 +5,8 @@
#include "UserConfiguration.h"
#include <QTextStream>
CodeGenerator::CodeGenerator(IPluginContentWidgetContext *context, QWidget *parent) :
PluginContentWidget(context, parent),
CodeGenerator::CodeGenerator(IPluginContentWidgetContext *context, PluginModule *module, QWidget *parent) :
PluginContentWidget(context, module, parent),
ui(new Ui::CodeGenerator)
{
ui->setupUi(this);

View file

@ -16,7 +16,7 @@ class CodeGenerator : public PluginContentWidget
Q_OBJECT
public:
CodeGenerator(IPluginContentWidgetContext *context, QWidget *parent = nullptr);
CodeGenerator(IPluginContentWidgetContext *context, PluginModule *module, QWidget *parent = nullptr);
~CodeGenerator();
void Init(std::shared_ptr<PgDatabaseCatalog> catalog, QString query, std::shared_ptr<const Pgsql::Result> dbres);

View file

@ -16,8 +16,8 @@
#include "plugin_support/IPluginContentWidgetContext.h"
CrudTab::CrudTab(IPluginContentWidgetContext *context, QWidget *parent)
: PluginContentWidget(context, parent)
CrudTab::CrudTab(IPluginContentWidgetContext *context, PluginModule *module, QWidget *parent)
: PluginContentWidget(context, module, parent)
, ui(new Ui::CrudTab)
{
ui->setupUi(this);
@ -140,8 +140,8 @@ void CrudPageModule::moduleAction_open(
{
// create new widget for specified table
// hand widget to context for display
CrudTab *ct = new CrudTab(context, nullptr);
context->addContentWidget(this, ct); // maybe CrudTab should do this
CrudTab *ct = new CrudTab(context, this);
context->addContentWidget(ct); // maybe CrudTab should do this
ct->setConfig(params.at("oid").toUInt());
}

View file

@ -20,7 +20,8 @@ class CrudTab : public PluginContentWidget
Q_OBJECT
public:
explicit CrudTab(IPluginContentWidgetContext *context, QWidget *parent = nullptr);
explicit CrudTab(IPluginContentWidgetContext *context, PluginModule *module,
QWidget *parent = nullptr);
~CrudTab() override;
void setConfig(Oid oid);

View file

@ -38,9 +38,12 @@ void DatabaseWindow::newCreateTablePage()
void DatabaseWindow::newCodeGenPage(QString query, std::shared_ptr<const Pgsql::Result> dbres)
{
auto cgtab = new CodeGenerator(context(), this);
cgtab->Init(m_database->catalog(), query, dbres);
addPage(cgtab, "Codegen");
// TODO should this call be this direct or should it go through module system
// yes it should otherwise context cannot properly setup toolbar and menu!!!
// auto cgtab = new CodeGenerator(context(), pluginModule(), this);
// cgtab->Init(m_database->catalog(), query, dbres);
// addPage(cgtab, "Codegen");
//
}
void DatabaseWindow::setConfig(const ConnectionConfig &config)

View file

@ -104,8 +104,9 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
else {
auto str = value.toString();
auto s = str.leftRef(100);
auto f = s.indexOf('\n');
option->text = ((f > 0) ? s.left(f) : s).toString();
// auto f = s.indexOf('\n');
// option->text = ((f > 0) ? s.left(f) : s).toString();
option->text = s.toString();
}
}
else {

View file

@ -22,8 +22,8 @@
#include "plugin_support/IPluginContentWidgetContext.h"
QueryTool::QueryTool(IPluginContentWidgetContext *context_, QWidget *parent)
: PluginContentWidget(context_, parent)
QueryTool::QueryTool(IPluginContentWidgetContext *context_, PluginModule *module, QWidget *parent)
: PluginContentWidget(context_, module, parent)
, ui(new Ui::QueryTab)
, m_dbConnection(*getGlobalAsioIoService())
{
@ -632,8 +632,6 @@ void QueryTool::exportData()
void QueryTool::initActions()
{
{
auto ac = new QAction(QIcon(":/icons/script_save.png"), tr("Save SQL"), this);
ac->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));

View file

@ -34,7 +34,7 @@ class PgDatabaseCatalog;
class QueryTool : public PluginContentWidget {
Q_OBJECT
public:
QueryTool(IPluginContentWidgetContext *context, QWidget *parent = nullptr);
QueryTool(IPluginContentWidgetContext *context, PluginModule *module, QWidget *parent = nullptr);
~QueryTool() override;
void newdoc();

View file

@ -26,12 +26,43 @@ void QueryToolModule::init()
ma.setIcon(QIcon(":/icons/folder.png"));
registerStaticAction(ma);
}
{
auto ca = makeContextAction<QueryTool>(tr("Save SQL"), &QueryTool::save);
ca->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
// ca->setMenuLocation(MenuPath("File/Save"));
// ca->setToolbarLocation(ToolbarLocation("main", "save"));
// how we tell the system we want this to become a menu button with this as it's default action
registerContextAction(ca);
}
// {
// auto ca = makeContextAction<QueryTool>(tr("Save SQL as"), &QueryTool::saveAs);
// ca->setMenuLocation(MenuPath("File/Save"));
// ca->setToolbarLocation(ToolbarLocation("main", "save"));
// // how we tell the system we want this to become a secondary action for the previous button?
// registerContextAction(ca);
// }
// {
// auto ca = makeContextAction<QueryTool>(tr("Save copy of SQL as"), &QueryTool::saveCopyAs);
// ca->setMenuLocation(MenuPath("File/Save"));
// ca->setToolbarLocation(ToolbarLocation("main", "save"));
// // how we tell the system we want this to become a secondary action for the previous button?
// registerContextAction(ca);
// }
}
void QueryToolModule::staticAction_new(IPluginContentWidgetContext* context)
{
auto *ct = new QueryTool(context, nullptr);
context->addContentWidget(this, ct);
auto *ct = new QueryTool(context, this);
// Should we let constructor of PluginContentWidget do this?
// Saves a line but what if we don't want it.
context->addContentWidget(ct);
// should content widget now to which module it belongs?
// That way the context would not need to keep track of this.
ct->newdoc();
}
@ -41,9 +72,11 @@ void QueryToolModule::staticAction_open(IPluginContentWidgetContext* context)
QString file_name = QFileDialog::getOpenFileName(context->container(),
tr("Open sql query"), home_dir, tr("SQL files (*.sql *.txt)"));
if ( ! file_name.isEmpty()) {
auto *ct = new QueryTool(context, nullptr);
context->addContentWidget(this, ct);
ct->load(file_name);
auto *ct = new QueryTool(context, this);
context->addContentWidget(ct);
if (!ct->load(file_name)) {
// TODO load has failed remove widget or never add it?
}
}
}

View file

@ -47,7 +47,7 @@ public:
const ModuleActionParameters &action_params
) = 0;
virtual void addContentWidget(PluginModule *module, PluginContentWidget *widget) = 0;
virtual void addContentWidget(PluginContentWidget *widget) = 0;
virtual void removeContentWidget(PluginContentWidget *widget) = 0;
/** Return a widget you can use as a parent

View file

@ -32,9 +32,9 @@ namespace LMainWindow_details {
m_window->statusBar()->showMessage(msg);
}
void addContentWidget(PluginModule *module, PluginContentWidget *widget) override
void addContentWidget(PluginContentWidget *widget) override
{
PluginContentWidgetContextBase::addContentWidget(module, widget);
PluginContentWidgetContextBase::addContentWidget(widget);
m_window->addPage(widget, "");
}
@ -207,9 +207,11 @@ void LMainWindow::tabWidget_currentChanged(int index)
void LMainWindow::addModuleWidgetActionsForPage(PluginContentWidget *page)
{
m_context->addWidgetActionsToToolbar(page, m_mainToolBar);
m_context->addContextActionsToMenu(page, menuBar());
}
void LMainWindow::removeModuleWidgetActionsForPage(PluginContentWidget *page)
{
m_context->removeContextActionsFromMenu(page, menuBar());
m_context->removeWidgetActionsFromToolbar(page, m_mainToolBar);
}

View file

@ -1,11 +1,22 @@
#include "PluginContentWidget.h"
PluginContentWidget::PluginContentWidget(IPluginContentWidgetContext *context, QWidget* parent)
PluginContentWidget::PluginContentWidget(IPluginContentWidgetContext *context,
PluginModule *module, QWidget* parent)
: QWidget(parent)
, m_context(context)
, m_pluginModule(module)
{}
bool PluginContentWidget::canClose()
{
return true;
}
QList<QAction *> PluginContentWidget::actions()
{ return QList<QAction*>(); }
PluginModule *PluginContentWidget::pluginModule()
{ return m_pluginModule; }
IPluginContentWidgetContext *PluginContentWidget::context()
{ return m_context; }

View file

@ -20,17 +20,19 @@ class PluginModule;
///
class PluginContentWidget: public QWidget{
public:
PluginContentWidget(IPluginContentWidgetContext *context, QWidget* parent = nullptr);
PluginContentWidget(IPluginContentWidgetContext *context, PluginModule *module, QWidget* parent = nullptr);
/// Returns the toolbar buttons for this page
virtual bool canClose();
virtual QList<QAction *> actions() { return QList<QAction*>(); }
virtual QList<QAction *> actions();
PluginModule *pluginModule();
IPluginContentWidgetContext *context();
protected:
IPluginContentWidgetContext *context() { return m_context; }
private:
IPluginContentWidgetContext *m_context;
IPluginContentWidgetContext *m_context = nullptr;
PluginModule *m_pluginModule = nullptr;
};
#endif // PGLPAGE_H

View file

@ -4,19 +4,43 @@
#include "PluginRegister.h"
#include <QAction>
#include <QDebug>
#include <QMenuBar>
#include <QToolBar>
#include <vector>
LWidgetData::LWidgetData(PluginModule *module)
LWidgetData::LWidgetData(PluginModule *module, PluginContentWidget *widget)
: m_module(module)
, m_widget(widget)
{}
void LWidgetData::init(PluginContentWidget *widget)
void LWidgetData::init()
{
}
void LWidgetData::addToMenu(QMenuBar *menubar)
{
auto&& menu = menubar->actions().first()->menu();
auto ti = std::type_index(typeid(*m_widget));
auto&& actions = m_module->actionsForContext(ti);
m_menuActions.reserve(actions.size());
for (auto&& actiondef : actions) {
auto ac = actiondef->createAction(m_widget);
menu->addAction(ac);
m_menuActions.push_back(ac);
}
}
void LWidgetData::removeFromMenu(QMenuBar *menubar)
{
for (auto&& action : m_menuActions) {
delete action;
}
m_menuActions.clear();
}
PluginContentWidgetContextBase::PluginContentWidgetContextBase() = default;
@ -41,13 +65,13 @@ void PluginContentWidgetContextBase::moduleAction(
qWarning() << QString("module not found %1").arg(module_identifier);
}
void PluginContentWidgetContextBase::addContentWidget(PluginModule *module, PluginContentWidget *widget)
void PluginContentWidgetContextBase::addContentWidget(PluginContentWidget *widget)
{
auto res = m_widgetLst.emplace(widget, module);
auto res = m_widgetLst.emplace(widget, LWidgetData{widget->pluginModule(), widget});
if (!res.second)
throw std::runtime_error("Unexpected conflicting key on insertiong PluginContentWidgetContextBase::addContentWidget");
res.first->second.init(widget);
res.first->second.init();
}
void PluginContentWidgetContextBase::removeContentWidget(PluginContentWidget *widget)
@ -64,6 +88,7 @@ void PluginContentWidgetContextBase::addWidgetActionsToToolbar(PluginContentWidg
{
auto && actions = widget->actions();
toolbar->addActions(actions);
}
void PluginContentWidgetContextBase::removeWidgetActionsFromToolbar(PluginContentWidget *widget, QToolBar *toolbar)
@ -73,3 +98,20 @@ void PluginContentWidgetContextBase::removeWidgetActionsFromToolbar(PluginConten
toolbar->removeAction(ac);
}
void PluginContentWidgetContextBase::addContextActionsToMenu(PluginContentWidget *widget, QMenuBar *menubar)
{
auto res = m_widgetLst.find(widget);
if (res == m_widgetLst.end())
return;
res->second.addToMenu(menubar);
}
void PluginContentWidgetContextBase::removeContextActionsFromMenu(PluginContentWidget *widget, QMenuBar *menubar)
{
auto res = m_widgetLst.find(widget);
if (res == m_widgetLst.end())
return;
res->second.removeFromMenu(menubar);
}

View file

@ -3,10 +3,12 @@
#include "plugin_support/IPluginContentWidgetContext.h"
#include <QList>
#include <typeindex>
class LContextAction;
class QToolBar;
class QAction;
class QMenuBar;
/// Maintains the list of actions added to a toolbar for a specific widget
/// it facilitates the removal of all those actions.
@ -32,13 +34,19 @@ private:
class LWidgetData {
public:
LWidgetData(PluginModule *module);
LWidgetData(PluginModule *module, PluginContentWidget *widget);
PluginModule* module() { return m_module; }
void init(PluginContentWidget *widget);
PluginContentWidget *widget() { return m_widget; }
void init();
void addToMenu(QMenuBar* menubar);
void removeFromMenu(QMenuBar* menubar);
private:
PluginModule *m_module;
PluginContentWidget *m_widget;
WidgetToolbarManager m_toolbarManager;
std::vector<QAction*> m_menuActions; ///< List of actions we put in the menu
};
/// Provides base implementation of IPluginContentWidgetContext
@ -53,16 +61,19 @@ public:
const ModuleActionParameters &action_params
) override;
void addContentWidget(PluginModule *module, PluginContentWidget *widget) override;
void addContentWidget(PluginContentWidget *widget) override;
void removeContentWidget(PluginContentWidget *widget) override;
void addWidgetActionsToToolbar(PluginContentWidget *widget, QToolBar *toolbar);
void removeWidgetActionsFromToolbar(PluginContentWidget *widget, QToolBar *toolbar);
void addContextActionsToMenu(PluginContentWidget *widget, QMenuBar *menubar);
void removeContextActionsFromMenu(PluginContentWidget *widget, QMenuBar *menubar);
private:
using WidgetLst = std::map<PluginContentWidget*, LWidgetData>;
WidgetLst m_widgetLst;
WidgetLst m_widgetLst; /// Keeps track of which widget belongs to which module
};
#endif // PLUGINCONTENTWIDGETCONTEXTBASE_H

View file

@ -1,5 +1,8 @@
#include "plugin_support/PluginModule.h"
#include "PluginContentWidget.h"
#include <QDebug>
#include <typeinfo>
#include <typeindex>
PluginModule::PluginModule(QString name, QString ident)
: m_name(std::move(name))
@ -14,7 +17,6 @@ void PluginModule::setDisplayCategory(QString category)
void PluginModule::registerStaticAction(StaticAction action)
{
qDebug() << "registerMenuAction " << action.text();
m_menuActions.emplace_back(std::move(action));
}
@ -39,3 +41,28 @@ const PluginModule::ModuleAction* PluginModule::findModuleAction(const QString &
return &res->second;
}
void PluginModule::registerContextAction(std::shared_ptr<ContextBaseAction> action)
{
auto find_result_iter = m_contextMap.find(action->contextTypeIndex());
if (find_result_iter != m_contextMap.end())
find_result_iter->second.push_back(action);
else
m_contextMap.emplace(action->contextTypeIndex(), ContextActionContainer({ action }));
}
const PluginModule::ContextActionContainer &PluginModule::actionsForContext(std::type_index ti)
{
static const ContextActionContainer empty_result;
auto find_result_iter = m_contextMap.find(ti);
if (find_result_iter != m_contextMap.end())
return find_result_iter->second;
return empty_result;
}
const PluginModule::ContextActionContainer& PluginModule::actionsForContext(PluginContentWidget *widget)
{
return actionsForContext(std::type_index(typeid(*widget)));
}

View file

@ -7,11 +7,20 @@
#include <QObject>
#include <functional>
#include <map>
#include <unordered_map>
#include <set>
#include <vector>
#include <typeindex>
class QAction;
class IPluginContentWidgetContext;
class PluginContentWidget;
/** Defines available actions for the application framework.
*
* There are static and context actions.
* There can be multiple contexts which are seperated by there typeid/type_index.
*/
class PluginModule: public QObject {
Q_OBJECT
public:
@ -19,6 +28,8 @@ public:
using ModuleAction = std::function<void(IPluginContentWidgetContext*, const ModuleActionParameters &)>;
using ModuleActionMap = std::map<QString, ModuleAction>;
using ContextActionContainer = std::vector<std::shared_ptr<ContextBaseAction>>;
PluginModule(QString name, QString ident);
virtual void init() {}
@ -41,7 +52,15 @@ public:
/// When the action is not found nullptr is returned.
const ModuleAction* findModuleAction(const QString &module_action) const;
void registerContextAction(std::shared_ptr<ContextBaseAction> action);
std::set<std::type_index> contextTypeIndexes();
const ContextActionContainer& actionsForContext(std::type_index ti);
const ContextActionContainer& actionsForContext(PluginContentWidget *widget);
private:
using ContextMap = std::unordered_map<std::type_index, ContextActionContainer>;
/// Name shown to end users
QString m_name;
/// Unique identifier
@ -50,6 +69,7 @@ private:
StaticActionList m_menuActions;
ModuleActionMap m_moduleActions;
ContextMap m_contextMap;
};

View file

@ -1,67 +1,75 @@
#include "StaticAction.h"
StaticAction::StaticAction(QString text, Func func)
: m_text(std::move(text))
, m_func(std::move(func))
BaseAction::BaseAction(const QString &text)
: m_text(text)
{}
const QIcon& StaticAction::icon() const
const QIcon& BaseAction::icon() const
{
return m_icon;
}
const MenuLocation& StaticAction::menuLocation() const
const MenuLocation& BaseAction::menuLocation() const
{
return m_menuLocation;
}
void StaticAction::setIcon(QIcon icon)
void BaseAction::setIcon(QIcon icon)
{
m_icon = std::move(icon);
}
void StaticAction::setMenuLocation(MenuLocation menu_location)
void BaseAction::setMenuLocation(MenuLocation menu_location)
{
m_menuLocation = std::move(menu_location);
}
void StaticAction::setToolbarLocation(ToolbarLocation toolbar_location)
void BaseAction::setToolbarLocation(ToolbarLocation toolbar_location)
{
m_toolbarLocation = toolbar_location;
}
void StaticAction::setShortcut(QKeySequence shortcut)
void BaseAction::setShortcut(QKeySequence shortcut)
{
m_shortcut = std::move(shortcut);
}
void StaticAction::setText(QString text)
void BaseAction::setText(QString text)
{
m_text = std::move(text);
}
void StaticAction::setToolTip(QString tooltip)
void BaseAction::setToolTip(QString tooltip)
{
m_toolTip = std::move(tooltip);
}
const QKeySequence& StaticAction::shortcut() const
const QKeySequence& BaseAction::shortcut() const
{
return m_shortcut;
}
const QString& StaticAction::text() const
const QString& BaseAction::text() const
{
return m_text;
}
const QString& StaticAction::toolTip() const
const QString& BaseAction::toolTip() const
{
return m_toolTip;
}
StaticAction::StaticAction(QString text, Func func)
: BaseAction(std::move(text))
, m_func(std::move(func))
{}
void StaticAction::perform(IPluginContentWidgetContext *context) const
{
if (m_func)
m_func(context);
}
ContextBaseAction::~ContextBaseAction()
{}

View file

@ -3,24 +3,20 @@
#include "MenuLocation.h"
#include "ToolbarLocation.h"
#include "plugin_support/PluginContentWidget.h"
#include <QAction>
#include <QIcon>
#include <QKeySequence>
#include <QString>
#include <functional>
#include <typeindex>
class IPluginContentWidgetContext;
/** An action for in a menu or toolbar that does not pertain to a specific
* widget. It often will create a widget for instance a New or Open action.
* It does need a context.
*
*/
class StaticAction {
class BaseAction {
public:
using Func = std::function<void(IPluginContentWidgetContext *context)>;
StaticAction(QString text, Func func);
explicit BaseAction(const QString &text);
const QIcon& icon() const;
const MenuLocation& menuLocation() const;
@ -34,7 +30,6 @@ public:
const QString& text() const;
const QString& toolTip() const;
void perform(IPluginContentWidgetContext *context) const;
private:
QString m_text;
QString m_toolTip;
@ -43,7 +38,85 @@ private:
MenuLocation m_menuLocation;
ToolbarLocation m_toolbarLocation;
};
/** An action for in a menu or toolbar that does not pertain to a specific
* widget. It often will create a widget for instance a New or Open action.
* It does need a context.
*
*/
class StaticAction: public BaseAction {
public:
using Func = std::function<void(IPluginContentWidgetContext *context)>;
StaticAction(QString text, Func func);
void perform(IPluginContentWidgetContext *context) const;
private:
Func m_func;
};
class ContextBaseAction: public BaseAction {
public:
using BaseAction::BaseAction;
virtual ~ContextBaseAction();
virtual std::type_index contextTypeIndex() const = 0;
virtual QAction* createAction(PluginContentWidget *widget)
{
auto action = new QAction(widget);
action->setText(text());
setupConnectionForAction(action, widget);
return action;
}
virtual void setupConnectionForAction(QAction *action, PluginContentWidget *context) = 0;
};
class QAction;
/** Defines an action that can be performed within a certain context.
* For instance the save action for a query can only be called when a query is loaded.
*
* Note Func should be something that QAction::triggered can connect to. If not you
* could get quite a vague error.
*/
template <typename Context, typename Func>
class ContextAction: public ContextBaseAction {
public:
//using Func = void (Context::*)(bool);
ContextAction(QString text, Func func)
: ContextBaseAction(text)
, m_func(func)
{}
std::type_index contextTypeIndex() const override
{
return std::type_index(typeid(Context));
}
// Mostly a helper for the code that creates the QAction
// without the helper that would need template code to.
virtual void setupConnectionForAction(QAction *action, PluginContentWidget *context) override
{
QObject::connect(action, &QAction::triggered, dynamic_cast<Context*>(context), m_func);
}
private:
Func m_func;
};
template <typename Context, typename Func>
auto makeContextAction(QString text, Func func)
{
return std::make_shared<ContextAction<Context, Func>>(text, func);
}
#endif // MENUACTION_H