From acc1d4fd7483f49b85acf1e90f71c14af56709ca Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Fri, 29 Apr 2016 17:45:55 +0200 Subject: [PATCH] refs #640, changed view base classes to use new context menus * keep menu actions on heap where possible * adjusted signatures --- src/blackgui/views/viewbase.cpp | 141 ++++++++++++++++----------- src/blackgui/views/viewbase.h | 17 +++- src/blackgui/views/viewdbobjects.cpp | 56 +++++++---- src/blackgui/views/viewdbobjects.h | 6 +- 4 files changed, 138 insertions(+), 82 deletions(-) diff --git a/src/blackgui/views/viewbase.cpp b/src/blackgui/views/viewbase.cpp index 4f5025077..6243dd4d0 100644 --- a/src/blackgui/views/viewbase.cpp +++ b/src/blackgui/views/viewbase.cpp @@ -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); diff --git a/src/blackgui/views/viewbase.h b/src/blackgui/views/viewbase.h index 6cba4c013..77bbd31bc 100644 --- a/src/blackgui/views/viewbase.h +++ b/src/blackgui/views/viewbase.h @@ -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 #include #include +#include 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 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 diff --git a/src/blackgui/views/viewdbobjects.cpp b/src/blackgui/views/viewdbobjects.cpp index 200d5874b..33567eceb 100644 --- a/src/blackgui/views/viewdbobjects.cpp +++ b/src/blackgui/views/viewdbobjects.cpp @@ -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 - void CViewWithDbObjects::customMenu(QMenu &menu) const + void CViewWithDbObjects::customMenu(Menus::CMenuActions &menuActions) { if (this->m_menus.testFlag(CViewBaseNonTemplate::MenuHighlightDbData)) { - QAction *a = menu.addAction(CIcons::database16(), "Highlight DB data", this, &CViewWithDbObjects::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::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::customMenu(menu); + CViewBase::customMenu(menuActions); } template @@ -111,28 +118,39 @@ namespace BlackGui } template - void COrderableViewWithDbObjects::customMenu(QMenu &menu) const + void COrderableViewWithDbObjects::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({ 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::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::ps_orderToLineEdit); + this->m_menuActions[0] = orderAction; + } + } - menuOrder->addAction(CIcons::arrowMediumNorth16(), "To top", this, &COrderableViewWithDbObjects::ps_orderToTop); - menuOrder->addAction(CIcons::arrowMediumSouth16(), "To bottom", this, &COrderableViewWithDbObjects::ps_orderToBottom); - menuOrder->addAction(CIcons::arrowMediumWest16(), "Freeze current order", this, &COrderableViewWithDbObjects::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::ps_orderToTop }); + this->m_menuActions[2] = menuActions.addAction(CIcons::arrowMediumSouth16(), "To bottom", CMenuAction::pathViewOrder(), { this, &COrderableViewWithDbObjects::ps_orderToBottom }); + this->m_menuActions[3] = menuActions.addAction(CIcons::arrowMediumWest16(), "Freeze current order", CMenuAction::pathViewOrder(), { this, &COrderableViewWithDbObjects::ps_freezeCurrentOrder }); } - CViewWithDbObjects::customMenu(menu); + CViewWithDbObjects::customMenu(menuActions); } template diff --git a/src/blackgui/views/viewdbobjects.h b/src/blackgui/views/viewdbobjects.h index 20aad7fea..049427b45 100644 --- a/src/blackgui/views/viewdbobjects.h +++ b/src/blackgui/views/viewdbobjects.h @@ -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 m_menuActions; + QLineEdit *m_leOrder = nullptr; + QIntValidator *m_validator = nullptr; }; } // namespace } // namespace