File 2000-commandbar-checkmarks.patch of Package kf6-kconfigwidgets

diff --git a/src/kcommandbar.cpp b/src/kcommandbar.cpp
index 2c33df738cf2f8b1aae3c2b9f22f970997890c5f..16f78e90c6eec29de2baccc40ba40135385a2316 100644
--- a/src/kcommandbar.cpp
+++ b/src/kcommandbar.cpp
@@ -8,6 +8,7 @@
 #include "kconfigwidgets_debug.h"
 
 #include <QAction>
+#include <QActionGroup>
 #include <QCoreApplication>
 #include <QGraphicsOpacityEffect>
 #include <QHeaderView>
@@ -95,6 +96,7 @@ public:
     {
         connect(this, &CommandBarFilterModel::modelAboutToBeReset, this, [this]() {
             m_hasActionsWithIcons = false;
+            m_hasCheckableActions = false;
         });
     }
 
@@ -103,6 +105,11 @@ public:
         return m_hasActionsWithIcons;
     }
 
+    bool hasCheckableActions() const
+    {
+        return m_hasCheckableActions;
+    }
+
     Q_SLOT void setFilterString(const QString &string)
     {
         // MUST reset the model here, we want to repopulate
@@ -141,8 +148,14 @@ protected:
             accept = resAction.matched;
         }
 
-        if (accept && !m_hasActionsWithIcons) {
-            m_hasActionsWithIcons |= !index.data(Qt::DecorationRole).isNull();
+        if (accept) {
+            if (!m_hasActionsWithIcons) {
+                m_hasActionsWithIcons |= !index.data(Qt::DecorationRole).isNull();
+            }
+
+            if (!m_hasCheckableActions) {
+                m_hasCheckableActions |= !index.data(Qt::CheckStateRole).isNull();
+            }
         }
 
         return accept;
@@ -151,6 +164,7 @@ protected:
 private:
     QString m_pattern;
     mutable bool m_hasActionsWithIcons = false;
+    mutable bool m_hasCheckableActions = false;
 };
 // END CommandBarFilterModel
 
@@ -216,20 +230,56 @@ public:
         initStyleOption(&option, index);
         option.text.clear(); // clear old text
         QStyle *style = option.widget->style();
-        style->drawControl(QStyle::CE_ItemViewItem, &option, painter, option.widget);
-
-        const int hMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, option.widget);
-
-        QRect textRect = option.rect;
+        style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter);
 
         const CommandBarFilterModel *model = static_cast<const CommandBarFilterModel *>(index.model());
+        const bool isRightToLeft = option.direction == Qt::RightToLeft;
+        const int hMargin = 2 * style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, option.widget);
+
+        QRect outputRect = option.rect;
+        outputRect.adjust(hMargin, 0, -hMargin, 0);
+
+        if (model->hasCheckableActions()) {
+            // draw checkbox
+            QRect checkMarkRect = style->subElementRect(QStyle::SE_CheckBoxIndicator, &option);
+            if (option.features & QStyleOptionViewItem::HasCheckIndicator) {
+                checkMarkRect = QStyle::alignedRect(option.direction, option.displayAlignment, checkMarkRect.size(), outputRect);
+                QStyleOptionViewItem optionCopy = option;
+                optionCopy.rect = checkMarkRect;
+                optionCopy.state = option.state & ~QStyle::State_HasFocus;
+                switch (option.checkState) {
+                case Qt::Unchecked:
+                    optionCopy.state |= QStyle::State_Off;
+                    break;
+                case Qt::PartiallyChecked:
+                    optionCopy.state |= QStyle::State_NoChange;
+                    break;
+                case Qt::Checked:
+                    optionCopy.state |= QStyle::State_On;
+                    break;
+                }
+                const QAction *action = index.data(Qt::UserRole).value<QAction *>();
+                Q_ASSERT(action);
+                const QActionGroup *actionGroup = action->actionGroup();
+                if (actionGroup && actionGroup->isExclusive()) {
+                    style->drawPrimitive(QStyle::PE_IndicatorRadioButton, &optionCopy, painter);
+                } else {
+                    style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &optionCopy, painter);
+                }
+            }
+            adjustRect(outputRect, checkMarkRect.width() + hMargin, isRightToLeft);
+        }
+
         if (model->hasActionsWithIcons()) {
-            const int iconWidth = option.decorationSize.width() + (hMargin * 2);
-            if (option.direction == Qt::RightToLeft) {
-                textRect.adjust(0, 0, -iconWidth, 0);
-            } else {
-                textRect.adjust(iconWidth, 0, 0, 0);
+            // draw icon
+            QRect iconRect(QPoint(0, 0), option.decorationSize);
+            if (option.features & QStyleOptionViewItem::HasDecoration) {
+                iconRect = QStyle::alignedRect(option.direction, option.displayAlignment, iconRect.size(), outputRect);
+                const QIcon::Mode iconMode = option.state & QStyle::State_Selected ? QIcon::Selected : QIcon::Normal;
+                const QIcon::State iconState = option.state & QStyle::State_Open ? QIcon::On : QIcon::Off;
+                option.icon.paint(painter, iconRect, option.decorationAlignment, iconMode, iconState);
             }
+            adjustRect(outputRect, iconRect.width() + hMargin, isRightToLeft);
         }
 
         const QString original = index.data().toString();
@@ -264,8 +314,7 @@ public:
             return QTextLayout::FormatRange{fr.start + actionNameStart, fr.length, f};
         });
 
-        textRect.adjust(hMargin, 0, -hMargin, 0);
-        paintItemText(painter, original, textRect, option, std::move(formats));
+        paintItemText(painter, original, outputRect, option, std::move(formats));
 
         painter->restore();
     }
@@ -277,6 +326,19 @@ public Q_SLOTS:
     }
 
 private:
+    /**
+     * Increases rect.left or decreases rect.right by x
+     * depending on the layout direction
+     */
+    static void adjustRect(QRect &rect, int x, bool isRightToLeft)
+    {
+        if (isRightToLeft) {
+            rect.adjust(0, 0, -x, 0);
+        } else {
+            rect.adjust(x, 0, 0, 0);
+        }
+    }
+
     QString m_filterString;
 };
 
@@ -451,7 +513,7 @@ private:
 
     int horizontalMargin(const QStyleOptionViewItem &option) const
     {
-        return option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option) + 2;
+        return 2 * option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option);
     }
 };
 
diff --git a/src/kcommandbarmodel_p.cpp b/src/kcommandbarmodel_p.cpp
index 08e793845a98b4bb85de89c8f26c3438ce3e238c..8be23ee6969e0dde7ec5e1a566e65cd65cf4e944 100644
--- a/src/kcommandbarmodel_p.cpp
+++ b/src/kcommandbarmodel_p.cpp
@@ -112,6 +112,20 @@ void KCommandBarModel::refresh(const QList<KCommandBar::ActionGroup> &actionGrou
     endResetModel();
 }
 
+Qt::ItemFlags KCommandBarModel::flags(const QModelIndex &index) const
+{
+    if (!index.isValid()) {
+        return Qt::NoItemFlags;
+    }
+
+    Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
+    if (m_rows.at(index.row()).action->isCheckable()) {
+        flags |= Qt::ItemIsUserCheckable;
+    }
+
+    return flags;
+}
+
 QVariant KCommandBarModel::data(const QModelIndex &index, int role) const
 {
     if (!index.isValid()) {
@@ -141,6 +155,12 @@ QVariant KCommandBarModel::data(const QModelIndex &index, int role) const
         }
         return toolTip;
     }
+    case Qt::CheckStateRole: {
+        if (entry.action->isCheckable()) {
+            return entry.action->isChecked() ? Qt::Checked : Qt::Unchecked;
+        }
+        break;
+    }
     case Qt::UserRole: {
         return QVariant::fromValue(entry.action);
     }
diff --git a/src/kcommandbarmodel_p.h b/src/kcommandbarmodel_p.h
index 655b1a81e7be39a2d38b7e05f8bbed4f802e4605..b0bec68f21f554ff010f42d28a5d4e80782404d0 100644
--- a/src/kcommandbarmodel_p.h
+++ b/src/kcommandbarmodel_p.h
@@ -53,6 +53,8 @@ public:
         return Column_Count;
     }
 
+    Qt::ItemFlags flags(const QModelIndex &index) const override;
+
     /**
      * reimplemented function to update score that is calculated by KFuzzyMatcher
      */
openSUSE Build Service is sponsored by