File indexing completed on 2024-09-15 03:37:06
0001 /* 0002 SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 #include "kcommandbarmodel_p.h" 0007 #include "kcommandbar.h" // For ActionGroup 0008 #include "kconfigwidgets_debug.h" 0009 0010 #include <KLocalizedString> 0011 0012 #include <QAction> 0013 #include <QMenu> 0014 0015 #include <unordered_set> 0016 0017 QString KCommandBarModel::Item::displayName() const 0018 { 0019 const QString group = KLocalizedString::removeAcceleratorMarker(groupName); 0020 const QString command = KLocalizedString::removeAcceleratorMarker(action->text()); 0021 0022 return group + QStringLiteral(": ") + command; 0023 } 0024 0025 KCommandBarModel::KCommandBarModel(QObject *parent) 0026 : QAbstractTableModel(parent) 0027 { 0028 m_clearHistoryAction = new QAction(tr("Clear History"), this); 0029 m_clearHistoryAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-history"))); 0030 connect(m_clearHistoryAction, &QAction::triggered, this, [this]() { 0031 m_lastTriggered.clear(); 0032 }); 0033 } 0034 0035 void fillRows(QList<KCommandBarModel::Item> &rows, const QString &title, const QList<QAction *> &actions, std::unordered_set<QAction *> &uniqueActions) 0036 { 0037 for (const auto &action : actions) { 0038 // We don't want diabled actions 0039 if (!action->isEnabled()) { 0040 continue; 0041 } 0042 0043 // Is this action actually a menu? 0044 if (auto menu = action->menu()) { 0045 auto menuActionList = menu->actions(); 0046 0047 // Empty? => Maybe the menu loads action on aboutToShow()? 0048 if (menuActionList.isEmpty()) { 0049 Q_EMIT menu->aboutToShow(); 0050 menuActionList = menu->actions(); 0051 } 0052 0053 const QString menuTitle = menu->title(); 0054 fillRows(rows, menuTitle, menuActionList, uniqueActions); 0055 continue; 0056 } 0057 0058 if (action->text().isEmpty() && !action->isSeparator()) { 0059 qCWarning(KCONFIG_WIDGETS_LOG) << "Action" << action << "in group" << title << "has no text"; 0060 continue; 0061 } 0062 0063 if (uniqueActions.insert(action).second) { 0064 rows.push_back(KCommandBarModel::Item{title, action, -1}); 0065 } 0066 } 0067 } 0068 0069 void KCommandBarModel::refresh(const QList<KCommandBar::ActionGroup> &actionGroups) 0070 { 0071 int totalActions = std::accumulate(actionGroups.begin(), actionGroups.end(), 0, [](int a, const KCommandBar::ActionGroup &ag) { 0072 return a + ag.actions.count(); 0073 }); 0074 ++totalActions; // for m_clearHistoryAction 0075 0076 QList<Item> temp_rows; 0077 std::unordered_set<QAction *> uniqueActions; 0078 temp_rows.reserve(totalActions); 0079 for (const auto &ag : actionGroups) { 0080 const auto &agActions = ag.actions; 0081 fillRows(temp_rows, ag.name, agActions, uniqueActions); 0082 } 0083 0084 temp_rows.push_back({tr("Command Bar"), m_clearHistoryAction, -1}); 0085 0086 /** 0087 * For each action in last triggered actions, 0088 * - Find it in the actions 0089 * - Use the score variable to set its score 0090 * 0091 * Items in m_lastTriggered are stored in descending order 0092 * by their usage i.e., the first item in the vector is the most 0093 * recently invoked action. 0094 * 0095 * Here we traverse them in reverse order, i.e., from least recent to 0096 * most recent and then assign a score to them in a way that most recent 0097 * ends up having the highest score. Thus when proxy model does the sorting 0098 * later, most recent item will end up on the top 0099 */ 0100 int score = 0; 0101 std::for_each(m_lastTriggered.crbegin(), m_lastTriggered.crend(), [&score, &temp_rows](const QString &act) { 0102 auto it = std::find_if(temp_rows.begin(), temp_rows.end(), [act](const KCommandBarModel::Item &i) { 0103 return i.action->text() == act; 0104 }); 0105 if (it != temp_rows.end()) { 0106 it->score = score++; 0107 } 0108 }); 0109 0110 beginResetModel(); 0111 m_rows = std::move(temp_rows); 0112 endResetModel(); 0113 } 0114 0115 QVariant KCommandBarModel::data(const QModelIndex &index, int role) const 0116 { 0117 if (!index.isValid()) { 0118 return {}; 0119 } 0120 0121 const auto &entry = m_rows[index.row()]; 0122 const int col = index.column(); 0123 0124 switch (role) { 0125 case Qt::DisplayRole: 0126 if (col == Column_Command) { 0127 return entry.displayName(); 0128 } 0129 Q_ASSERT(col == Column_Shortcut); 0130 return entry.action->shortcut().toString(); 0131 case Qt::DecorationRole: 0132 if (col == Column_Command) { 0133 return entry.action->icon(); 0134 } 0135 break; 0136 case Qt::ToolTipRole: { 0137 QString toolTip = entry.displayName(); 0138 if (!entry.action->shortcut().isEmpty()) { 0139 toolTip += QLatin1Char('\n'); 0140 toolTip += entry.action->shortcut().toString(); 0141 } 0142 return toolTip; 0143 } 0144 case Qt::UserRole: { 0145 return QVariant::fromValue(entry.action); 0146 } 0147 case Role::Score: 0148 return entry.score; 0149 } 0150 0151 return {}; 0152 } 0153 0154 void KCommandBarModel::actionTriggered(const QString &name) 0155 { 0156 if (m_lastTriggered.size() == 6) { 0157 m_lastTriggered.pop_back(); 0158 } 0159 m_lastTriggered.push_front(name); 0160 } 0161 0162 QStringList KCommandBarModel::lastUsedActions() const 0163 { 0164 return m_lastTriggered; 0165 } 0166 0167 void KCommandBarModel::setLastUsedActions(const QStringList &actionNames) 0168 { 0169 m_lastTriggered = actionNames; 0170 0171 while (m_lastTriggered.size() > 6) { 0172 m_lastTriggered.pop_back(); 0173 } 0174 } 0175 0176 #include "moc_kcommandbarmodel_p.cpp"