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