10#include <QAbstractItemModel>
18 class MenuModelManager :
public QObject
20 const std::function<void (QModelIndex)> ClickHandler_;
22 QAbstractItemModel& Model_;
24 const MenuModelOptions Options_;
26 explicit MenuModelManager (QMenu& menu,
27 QAbstractItemModel& model,
28 std::function<
void (QModelIndex)> clickHandler,
29 MenuModelOptions options)
31 , ClickHandler_ { std::move (clickHandler) }
34 , Options_ { std::move (options) }
39 &QObject::deleteLater);
44 &QAbstractItemModel::modelReset,
46 &MenuModelManager::RegenerateMenu);
48 &QAbstractItemModel::rowsMoved,
50 &MenuModelManager::MoveRows);
52 &QAbstractItemModel::rowsRemoved,
54 &MenuModelManager::RemoveRows);
56 &QAbstractItemModel::rowsInserted,
58 &MenuModelManager::InsertRows);
61 void RegenerateMenu ()
65 const auto rc = Model_.rowCount ();
67 Menu_.addActions (MakeActionsForRange (0, rc - 1));
69 if (!Options_.AdditionalActions_.isEmpty ())
71 Menu_.addSeparator ();
72 Menu_.addActions (Options_.AdditionalActions_);
76 void MoveRows (
const QModelIndex& parent,
int start,
int end,
77 const QModelIndex& destParent,
int row)
79 if (!CheckRootParent (parent, destParent) || !CheckIndexes (start, end, row))
82 const auto& actions = Menu_.actions ();
83 const auto& moved = actions.mid (start, end - start + 1);
84 const auto& target = actions [row];
85 Menu_.insertActions (target, moved);
88 void RemoveRows (
const QModelIndex& parent,
int first,
int last)
90 if (!CheckRootParent (parent) || !CheckIndexes (first, last))
93 const auto& actions = Menu_.actions ();
94 for (
const auto action : actions.mid (first, last - first + 1))
95 Menu_.removeAction (action);
98 QList<QAction*> MakeActionsForRange (
int first,
int last)
100 QList<QAction*> actions;
101 actions.reserve (last - first + 1);
102 for (
int i = first; i <= last; ++i)
104 const auto& idx = Model_.index (i, 0);
105 const auto& icon = idx.data (Qt::DecorationRole).value<QIcon> ();
106 const auto& text = idx.data (Qt::DisplayRole).toString ();
107 const auto& tooltip = idx.data (Qt::ToolTipRole).toString ();
109 const auto act =
new QAction { icon, text, &Menu_ };
110 act->setToolTip (tooltip);
114 [
this, idx = QPersistentModelIndex { idx }] { ClickHandler_ (idx); });
120 void InsertRows (
const QModelIndex& parent,
int first,
int last)
122 if (!CheckRootParent (parent))
125 const auto& newActions = MakeActionsForRange (first, last);
126 const auto& existing = Menu_.actions ();
127 const auto curActionsCount = existing.size ();
128 if (first == curActionsCount)
129 Menu_.addActions (newActions);
130 else if (first < curActionsCount)
131 Menu_.insertActions (existing [first], newActions);
134 qWarning () <<
"invalid actions position" << &Menu_ << &Model_ << curActionsCount << first << last;
135 qDeleteAll (newActions);
139 template<
typename... Idxs>
140 bool CheckRootParent (
const Idxs&... idxes)
142 if ((idxes.isValid () || ...))
144 ((qWarning () <<
"ignoring row operations for non-root indexes" << &Menu_ << &Model_) << ... << idxes);
151 template<
typename... Idxs>
152 bool CheckIndexes (Idxs... idxes)
154 if (((idxes >= Menu_.actions ().size ()) || ...))
156 ((qWarning () <<
"out of bounds indexes" << &Menu_ << &Model_) << ... << idxes);
167 QAbstractItemModel& model,
168 std::function<
void (QModelIndex)> clickHandler,
171 static QHash<QMenu*, MenuModelManager*> menu2manager;
172 if (
auto mgr = menu2manager.take (&menu))
175 const auto mgr =
new MenuModelManager { menu, model, std::move (clickHandler), std::move (options) };
176 menu2manager [&menu] = mgr;
void SetMenuModel(QMenu &menu, QAbstractItemModel &model, std::function< void(QModelIndex)> clickHandler, MenuModelOptions options)