refs #640, changed view base classes to use new context menus

* keep menu actions on heap where possible
* adjusted signatures
This commit is contained in:
Klaus Basan
2016-04-29 17:45:55 +02:00
parent 930ebeee30
commit acc1d4fd74
4 changed files with 138 additions and 82 deletions

View File

@@ -11,6 +11,7 @@
#include "blackmisc/fileutils.h"
#include "blackmisc/buildconfig.h"
#include "blackgui/models/allmodels.h"
#include "blackgui/menus/menuaction.h"
#include "blackgui/stylesheetutility.h"
#include "blackgui/guiutility.h"
#include "blackgui/guiapplication.h"
@@ -59,7 +60,7 @@ namespace BlackGui
filter->setObjectName("Filter shortcut for " + this->objectName());
QShortcut *clearSelection = new QShortcut(CShortcut::keyClearSelection(), this, SLOT(clearSelection()), nullptr, Qt::WidgetShortcut);
clearSelection->setObjectName("Clear selection shortcut for " + this->objectName());
QShortcut *saveJson = new QShortcut(CShortcut::keySaveViews(), this, SLOT(ps_saveJsonShortcut()), nullptr, Qt::WidgetShortcut);
QShortcut *saveJson = new QShortcut(CShortcut::keySaveViews(), this, SLOT(ps_saveJsonAction()), nullptr, Qt::WidgetShortcut);
saveJson->setObjectName("Save JSON for " + this->objectName());
QShortcut *deleteRow = new QShortcut(CShortcut::keyDelete(), this, SLOT(ps_removeSelectedRows()), nullptr, Qt::WidgetShortcut);
deleteRow->setObjectName("Delete selected rows for " + this->objectName());
@@ -174,116 +175,133 @@ namespace BlackGui
return menu;
}
void CViewBaseNonTemplate::customMenu(QMenu &menu) const
CMenuActions CViewBaseNonTemplate::initMenuActions(CViewBaseNonTemplate::MenuFlag menu)
{
if (this->m_menuFlagActions.contains(menu)) { return this->m_menuFlagActions.value(menu); }
CMenuActions ma;
switch (menu)
{
case MenuRefresh: { ma.addAction(BlackMisc::CIcons::refresh16(), "Update", CMenuAction::pathViewUpdates(), { this, &CViewBaseNonTemplate::requestUpdate }); break; }
case MenuBackend: { ma.addAction(BlackMisc::CIcons::refresh16(), "Reload from backend", CMenuAction::pathViewUpdates(), { this, &CViewBaseNonTemplate::requestNewBackendData}); break; }
case MenuDisplayAutomatically:
{
QAction *a = ma.addAction(CIcons::appMappings16(), "Automatically display (when loaded)", CMenuAction::pathViewUpdates(), { this, &CViewBaseNonTemplate::ps_toggleAutoDisplay });
a->setCheckable(true);
a->setChecked(this->displayAutomatically());
break;
}
case MenuRemoveSelectedRows: { ma.addAction(BlackMisc::CIcons::delete16(), "Remove selected rows", CMenuAction::pathViewAddRemove(), { this, &CViewBaseNonTemplate::ps_removeSelectedRows }, CShortcut::keyDelete()); break; }
case MenuClear: { ma.addAction(BlackMisc::CIcons::delete16(), "Clear", CMenuAction::pathViewAddRemove(), { this, &CViewBaseNonTemplate::ps_clear }); break; }
case MenuFilter:
{
ma.addAction(CIcons::filter16(), "Filter", CMenuAction::pathViewFilter(), { this, &CViewBaseNonTemplate::ps_displayFilterDialog }, CShortcut::keyDisplayFilter());
ma.addAction(CIcons::filter16(), "Remove Filter", CMenuAction::pathViewFilter(), { this, &CViewBaseNonTemplate::ps_removeFilter });
break;
}
case MenuLoad: { ma.addAction(CIcons::disk16(), "Load from file", CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::ps_loadJsonAction }); break; }
case MenuSave: { ma.addAction(CIcons::disk16(), "Save data in file", CMenuAction::pathViewLoadSave(), { this, &CViewBaseNonTemplate::ps_saveJsonAction }, CShortcut::keySaveViews()); break; }
default:
break;
}
this->m_menuFlagActions.insert(menu, ma);
return ma;
}
void CViewBaseNonTemplate::customMenu(CMenuActions &menuActions)
{
// delegate?
if (this->m_menu) { this->m_menu->customMenu(menu); }
if (this->m_menu) { this->m_menu->customMenu(menuActions); }
// separator
if (!menu.isEmpty() && ((this->m_menus & MenuStandard) > 0)) { menu.addSeparator(); }
// standard view menus
if (this->m_menus.testFlag(MenuRefresh)) { menuActions.addActions(this->initMenuActions(MenuRefresh)); }
if (this->m_menus.testFlag(MenuBackend)) { menuActions.addActions(this->initMenuActions(MenuBackend)); }
if (m_showingLoadIndicator)
{
// just in case, if this ever will be dangling
menuActions.addAction(BlackMisc::CIcons::preloader16(), "Hide load indicator", CMenuAction::pathViewUpdates(), nullptr, { this, &CViewBaseNonTemplate::hideLoadIndicator });
}
// standard menus
int items = menu.actions().size();
if (this->m_menus.testFlag(MenuRefresh)) { menu.addAction(BlackMisc::CIcons::refresh16(), "Update", this, &CViewBaseNonTemplate::requestUpdate); }
if (this->m_menus.testFlag(MenuBackend)) { menu.addAction(BlackMisc::CIcons::refresh16(), "Reload from backend", this, &CViewBaseNonTemplate::requestNewBackendData); }
if (this->m_menus.testFlag(MenuClear)) { menu.addAction(BlackMisc::CIcons::delete16(), "Clear", this, &CViewBaseNonTemplate::ps_clear); }
if (this->m_menus.testFlag(MenuClear)) { menuActions.addActions(this->initMenuActions(MenuClear)); }
if (this->m_menus.testFlag(MenuDisplayAutomatically))
{
// here I expect only one action
QAction *a = menuActions.addActions(this->initMenuActions(MenuDisplayAutomatically)).first();
a->setChecked(this->displayAutomatically());
}
if (this->m_menus.testFlag(MenuRemoveSelectedRows))
{
if (this->hasSelection())
{
menu.addAction(BlackMisc::CIcons::delete16(), "Remove selected rows", this, &CViewBaseNonTemplate::ps_removeSelectedRows, CShortcut::keyDelete());
menuActions.addActions(this->initMenuActions(MenuRemoveSelectedRows));
}
}
if (this->m_menus.testFlag(MenuDisplayAutomatically))
{
QAction *a = menu.addAction(CIcons::appMappings16(), "Automatically display (when loaded)", this, &CViewBaseNonTemplate::ps_toggleAutoDisplay);
a->setCheckable(true);
a->setChecked(this->displayAutomatically());
}
if (menu.actions().size() > items) { menu.addSeparator(); }
items = menu.actions().size();
if (this->m_menus.testFlag(MenuFilter))
{
menu.addAction(CIcons::filter16(), "Filter", this, &CViewBaseNonTemplate::ps_displayFilterDialog, CShortcut::keyDisplayFilter());
menu.addAction(CIcons::filter16(), "Remove Filter", this, &CViewBaseNonTemplate::ps_removeFilter);
menuActions.addActions(this->initMenuActions(MenuFilter));
}
if (menu.actions().size() > items) { menu.addSeparator(); }
// selection menus
items = menu.actions().size();
SelectionMode sm = this->selectionMode();
// selection menus, not in menu action list because it depends on current selection
const SelectionMode sm = this->selectionMode();
if (sm == MultiSelection || sm == ExtendedSelection)
{
menu.addAction(QIcon(), "Select all", this, &CViewBaseNonTemplate::selectAll, Qt::CTRL + Qt::Key_A);
menuActions.addAction("Select all", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::selectAll }, Qt::CTRL + Qt::Key_A);
}
if (sm != NoSelection)
{
menuActions.addAction("Clear selection", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::clearSelection }, CShortcut::keyClearSelection());
}
if ((this->m_originalSelectionMode == MultiSelection || this->m_originalSelectionMode == ExtendedSelection) && this->m_menus.testFlag(MenuToggleSelectionMode))
{
if (sm != MultiSelection)
{
QAction *a = menu.addAction(QIcon(), "Switch to multi selection", this, &CViewBaseNonTemplate::ps_toggleSelectionMode);
QAction *a = menuActions.addAction("Switch to multi selection", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::ps_toggleSelectionMode });
a->setData(MultiSelection);
}
if (sm != ExtendedSelection)
{
QAction *a = menu.addAction(QIcon(), "Switch to extended selection", this, &CViewBaseNonTemplate::ps_toggleSelectionMode);
QAction *a = menuActions.addAction("Switch to extended selection", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::ps_toggleSelectionMode });
a->setData(ExtendedSelection);
}
if (sm != SingleSelection)
{
QAction *a = menu.addAction(QIcon(), "Switch to single selection", this, &CViewBaseNonTemplate::ps_toggleSelectionMode);
QAction *a = menuActions.addAction("Switch to single selection", CMenuAction::pathViewSelection(), nullptr, { this, &CViewBaseNonTemplate::ps_toggleSelectionMode });
a->setData(SingleSelection);
}
}
if (sm != NoSelection)
{
menu.addAction(QIcon(), "Clear selection", this, &CViewBaseNonTemplate::clearSelection, CShortcut::keyClearSelection());
}
if (menu.actions().size() > items) { menu.addSeparator(); }
// load/save
items = menu.actions().size();
if (m_menus.testFlag(MenuLoad)) { menu.addAction(CIcons::disk16(), "Load from file", this, &CViewBaseNonTemplate::ps_loadJson); }
if (m_menus.testFlag(MenuSave) && !isEmpty()) { menu.addAction(CIcons::disk16(), "Save data in file", this, &CViewBaseNonTemplate::ps_saveJson, CShortcut::keySaveViews()); }
if (menu.actions().size() > items) { menu.addSeparator(); }
if (m_menus.testFlag(MenuLoad)) { menuActions.addActions(this->initMenuActions(MenuLoad)); }
if (m_menus.testFlag(MenuSave) && !isEmpty()) { menuActions.addActions(this->initMenuActions(MenuSave)); }
// resizing
menu.addAction(BlackMisc::CIcons::resize16(), "Resize", this, &CViewBaseNonTemplate::presizeOrFullResizeToContents);
menuActions.addAction(BlackMisc::CIcons::resize16(), "Resize", CMenuAction::pathViewResize(), nullptr, { this, &CViewBaseNonTemplate::presizeOrFullResizeToContents });
// resize to content might decrease performance,
// so I only allow changing to "content resizing" if size matches
bool enabled = !this->reachedResizeThreshold();
bool autoResize = this->m_resizeMode == ResizingAuto;
const bool enabled = !this->reachedResizeThreshold();
const bool autoResize = this->m_resizeMode == ResizingAuto;
// when not auto let set how we want to resize rows
if (m_rowResizeMode == Interactive)
{
QAction *a = menu.addAction(BlackMisc::CIcons::resizeVertical16(), " Resize rows to content (auto)", this, &CViewBaseNonTemplate::rowsResizeModeToContent);
QAction *a = menuActions.addAction(BlackMisc::CIcons::resizeVertical16(), " Resize rows to content (auto)", CMenuAction::pathViewResize(), nullptr, { this, &CViewBaseNonTemplate::rowsResizeModeToContent });
a->setEnabled(enabled && !autoResize);
}
else
{
QAction *a = menu.addAction(BlackMisc::CIcons::resizeVertical16(), "Resize rows interactive", this, &CViewBaseNonTemplate::rowsResizeModeToInteractive);
QAction *a = menuActions.addAction(BlackMisc::CIcons::resizeVertical16(), "Resize rows interactive", CMenuAction::pathViewResize(), nullptr, { this, &CViewBaseNonTemplate::rowsResizeModeToInteractive });
a->setEnabled(!autoResize);
}
QAction *actionInteractiveResize = new QAction(&menu);
QAction *actionInteractiveResize = menuActions.addAction(CIcons::viewMultiColumn(), "Resize (auto)", CMenuAction::pathViewResize(), nullptr);
actionInteractiveResize->setObjectName(this->objectName().append("ActionResizing"));
actionInteractiveResize->setIconText("Resize (auto)");
actionInteractiveResize->setIcon(CIcons::viewMultiColumn());
actionInteractiveResize->setCheckable(true);
actionInteractiveResize->setChecked(autoResize);
actionInteractiveResize->setEnabled(enabled);
menu.addAction(actionInteractiveResize);
connect(actionInteractiveResize, &QAction::toggled, this, &CViewBaseNonTemplate::ps_toggleResizeMode);
if (m_showingLoadIndicator)
{
// just in case, if this ever will be dangling
menu.addAction(BlackMisc::CIcons::preloader16(), "Hide load indicator", this, &CViewBaseNonTemplate::hideLoadIndicator);
}
}
void CViewBaseNonTemplate::paintEvent(QPaintEvent *event)
@@ -405,7 +423,14 @@ namespace BlackGui
this->m_filterWidget->show();
}
void CViewBaseNonTemplate::ps_saveJsonShortcut()
void CViewBaseNonTemplate::ps_loadJsonAction()
{
if (this->isEmpty()) { return; }
if (!this->m_menus.testFlag(MenuLoad)) { return; }
this->ps_loadJson();
}
void CViewBaseNonTemplate::ps_saveJsonAction()
{
if (this->isEmpty()) { return; }
if (!this->m_menus.testFlag(MenuSave)) { return; }
@@ -518,8 +543,10 @@ namespace BlackGui
void CViewBaseNonTemplate::ps_customMenuRequested(QPoint pos)
{
QMenu menu;
this->customMenu(menu);
if (menu.isEmpty()) { return; }
CMenuActions menuActions;
this->customMenu(menuActions);
if (menuActions.isEmpty()) { return; }
menuActions.toQMenu(menu, true);
QPoint globalPos = this->mapToGlobal(pos);
menu.exec(globalPos);

View File

@@ -17,6 +17,7 @@
#include "blackgui/filters/filterwidget.h"
#include "blackgui/models/modelfilter.h"
#include "blackgui/menus/menudelegate.h"
#include "blackgui/menus/menuaction.h"
#include "blackgui/loadindicator.h"
#include "blackgui/blackguiexport.h"
#include "blackmisc/icons.h"
@@ -30,6 +31,7 @@
#include <QPoint>
#include <QFont>
#include <QList>
#include <QMultiMap>
namespace BlackGui
{
@@ -281,7 +283,7 @@ namespace BlackGui
//! Method creating the menu
//! \remarks override this method to contribute to the menu
//! \sa BlackGui::Views::CViewBaseNonTemplate::ps_customMenuRequested
virtual void customMenu(QMenu &menu) const;
virtual void customMenu(BlackGui::Menus::CMenuActions &menuActions);
//! \name Functions from QTableView
//! @{
@@ -319,6 +321,9 @@ namespace BlackGui
//! Default file for load/save operations
QString getDefaultFilename(bool load) const;
//! Init menu actions
BlackGui::Menus::CMenuActions initMenuActions(MenuFlag menu);
QString m_saveFileName; //!< save file name (JSON)
ResizeMode m_resizeMode = PresizeSubset; //!< mode
RowsResizeMode m_rowResizeMode = Interactive; //!< row resize mode for row height
@@ -336,8 +341,9 @@ namespace BlackGui
bool m_enableDeleteSelectedRows = false; //!< selected rows can be deleted
QWidget *m_filterWidget = nullptr; //!< filter widget or dialog
Menu m_menus = MenuDefault; //!< Default menu settings
BlackGui::Menus::IMenuDelegate *m_menu = nullptr; //!< custom menu if any
BlackGui::Menus::IMenuDelegate *m_menu = nullptr; //!< custom menu if any
BlackGui::CLoadIndicator *m_loadIndicator = nullptr; //!< load indicator if needed
QMap<MenuFlag, BlackGui::Menus::CMenuActions> m_menuFlagActions; //!< initialized actions
protected slots:
//! Helper method with template free signature serving as callback from threaded worker
@@ -367,11 +373,14 @@ namespace BlackGui
//! Load JSON
virtual BlackMisc::CStatusMessage ps_loadJson() = 0;
//! Load JSON for action/menu, no return signatur
void ps_loadJsonAction();
//! Save JSON
virtual BlackMisc::CStatusMessage ps_saveJson() const = 0;
//! Save JSON called by shortcut
virtual void ps_saveJsonShortcut();
//! Save JSON for action/menu, no return signatur
void ps_saveJsonAction();
// ------------ slots of CViewDbObjects ----------------
// need to be declared here and overridden, as this is the only part with valid Q_OBJECT

View File

@@ -17,6 +17,7 @@
using namespace BlackMisc;
using namespace BlackGui;
using namespace BlackGui::Models;
using namespace BlackGui::Menus;
namespace BlackGui
{
@@ -85,15 +86,21 @@ namespace BlackGui
}
template <class ModelClass, class ContainerType, class ObjectType, class KeyType>
void CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::customMenu(QMenu &menu) const
void CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::customMenu(Menus::CMenuActions &menuActions)
{
if (this->m_menus.testFlag(CViewBaseNonTemplate::MenuHighlightDbData))
{
QAction *a = menu.addAction(CIcons::database16(), "Highlight DB data", this, &CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::ps_toggleHighlightDbData);
a->setCheckable(true);
if (!this->m_menuFlagActions.contains(CViewBaseNonTemplate::MenuHighlightDbData))
{
CMenuActions ma;
QAction *added = ma.addAction(CIcons::database16(), "Highlight DB data", CMenuAction::pathViewDatabase(), { this, &CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::ps_toggleHighlightDbData });
added->setCheckable(true);
this->m_menuFlagActions.insert(CViewBaseNonTemplate::MenuHighlightDbData, ma);
}
QAction *a = menuActions.addActions(this->initMenuActions(CViewBaseNonTemplate::MenuHighlightDbData)).first();
a->setChecked(this->derivedModel()->highlightDbData());
}
CViewBase<ModelClass, ContainerType, ObjectType>::customMenu(menu);
CViewBase<ModelClass, ContainerType, ObjectType>::customMenu(menuActions);
}
template <class ModelClass, class ContainerType, class ObjectType, class KeyType>
@@ -111,28 +118,39 @@ namespace BlackGui
}
template <class ModelClass, class ContainerType, class ObjectType, class KeyType>
void COrderableViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::customMenu(QMenu &menu) const
void COrderableViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::customMenu(CMenuActions &menuActions)
{
if (this->m_menus.testFlag(CViewBaseNonTemplate::MenuOrderable) && this->hasSelection())
{
const int maxOrder = this->rowCount() - 1;
QMenu *menuOrder = menu.addMenu(CIcons::arrowMediumEast16(), "Order");
if (this->m_menuActions.isEmpty())
{
// predefine menus
this->m_menuActions = QList<QAction *>({ nullptr, nullptr, nullptr, nullptr});
QLineEdit *leOrder = new QLineEdit(&menu);
leOrder->setPlaceholderText("New order 0-" + QString::number(maxOrder));
const QIntValidator *v = new QIntValidator(0, maxOrder, leOrder);
leOrder->setValidator(v);
QWidgetAction *orderAction = new QWidgetAction(&menu);
orderAction->setDefaultWidget(leOrder);
menuOrder->addAction(orderAction);
QObject::connect(leOrder, &QLineEdit::returnPressed, this, &COrderableViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::ps_orderToLineEdit);
if (!this->m_menuActions[0])
{
this->m_leOrder = new QLineEdit(this);
this->m_validator = new QIntValidator(0, maxOrder, this);
this->m_leOrder->setValidator(this->m_validator);
QWidgetAction *orderAction = new QWidgetAction(this);
orderAction->setDefaultWidget(this->m_leOrder);
QObject::connect(this->m_leOrder, &QLineEdit::returnPressed, this, &COrderableViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::ps_orderToLineEdit);
this->m_menuActions[0] = orderAction;
}
}
menuOrder->addAction(CIcons::arrowMediumNorth16(), "To top", this, &COrderableViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::ps_orderToTop);
menuOrder->addAction(CIcons::arrowMediumSouth16(), "To bottom", this, &COrderableViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::ps_orderToBottom);
menuOrder->addAction(CIcons::arrowMediumWest16(), "Freeze current order", this, &COrderableViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::ps_freezeCurrentOrder);
menu.addSeparator();
this->m_validator->setRange(0, maxOrder);
this->m_leOrder->setValidator(this->m_validator);
this->m_leOrder->setPlaceholderText("New order 0-" + QString::number(maxOrder));
menuActions.addMenuViewOrder();
menuActions.addAction(this->m_menuActions[0], CMenuAction::pathViewOrder());
this->m_menuActions[1] = menuActions.addAction(CIcons::arrowMediumNorth16(), "To top", CMenuAction::pathViewOrder(), { this, &COrderableViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::ps_orderToTop });
this->m_menuActions[2] = menuActions.addAction(CIcons::arrowMediumSouth16(), "To bottom", CMenuAction::pathViewOrder(), { this, &COrderableViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::ps_orderToBottom });
this->m_menuActions[3] = menuActions.addAction(CIcons::arrowMediumWest16(), "Freeze current order", CMenuAction::pathViewOrder(), { this, &COrderableViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::ps_freezeCurrentOrder });
}
CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::customMenu(menu);
CViewWithDbObjects<ModelClass, ContainerType, ObjectType, KeyType>::customMenu(menuActions);
}
template <class ModelClass, class ContainerType, class ObjectType, class KeyType>

View File

@@ -45,7 +45,7 @@ namespace BlackGui
explicit CViewWithDbObjects(QWidget *parent = nullptr);
//! \copydoc BlackGui::Views::CViewBaseNonTemplate::customMenu
virtual void customMenu(QMenu &menu) const override;
virtual void customMenu(BlackGui::Menus::CMenuActions &menuActions) override;
protected slots:
//! \copydoc BlackGui::Views::CViewBase::ps_toggleHighlightDbData
@@ -61,7 +61,7 @@ namespace BlackGui
explicit COrderableViewWithDbObjects(QWidget *parent = nullptr);
//! \copydoc BlackGui::Views::CViewBaseNonTemplate::customMenu
virtual void customMenu(QMenu &menu) const override;
virtual void customMenu(BlackGui::Menus::CMenuActions &menuActions) override;
//! Move selected items
void moveSelectedItems(int order);
@@ -81,6 +81,8 @@ namespace BlackGui
private:
QList<QAction *> m_menuActions;
QLineEdit *m_leOrder = nullptr;
QIntValidator *m_validator = nullptr;
};
} // namespace
} // namespace