File indexing completed on 2025-01-05 05:23:46

0001 /*
0002     This file is part of the Okteta Kasten Framework, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2022 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "structureitemdelegate.hpp"
0010 
0011 // tool
0012 #include "structuresselectionmodel.hpp"
0013 #include "aboutstructuredialog.hpp"
0014 // KF
0015 #include <KCategorizedView>
0016 #include <KLocalizedString>
0017 // Qt
0018 #include <QApplication>
0019 #include <QStyleOptionViewItem>
0020 #include <QCheckBox>
0021 #include <QPushButton>
0022 #include <QStyle>
0023 #include <QPainter>
0024 #include <QFont>
0025 
0026 
0027 static QFont nameFont(const QFont& font)
0028 {
0029     QFont nameFont(font);
0030     nameFont.setBold(true);
0031 
0032     return nameFont;
0033 }
0034 
0035 StructureItemDelegate::StructureItemDelegate(QAbstractItemView* itemView, QObject* parent)
0036     : KWidgetItemDelegate(itemView, parent)
0037     , m_sampleCheckBox(new QCheckBox)
0038     , m_sampleAboutButton(new QPushButton)
0039 {
0040     m_sampleAboutButton->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); // only for getting size matters
0041 }
0042 
0043 StructureItemDelegate::~StructureItemDelegate() = default;
0044 
0045 void StructureItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
0046                                   const QModelIndex& index) const
0047 {
0048     if (!index.isValid()) {
0049         return;
0050     }
0051 
0052     QRect iconRect;
0053     QRect textRect;
0054     layoutItem(option, index, nullptr, &iconRect, &textRect, nullptr, nullptr, LayoutForActualSize);
0055 
0056     painter->save();
0057 
0058     // draw background
0059     const QWidget* widget = option.widget;
0060     QStyle* style = widget ? widget->style() : QApplication::style();
0061     style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, widget);
0062 
0063     // draw icon
0064     const QIcon icon = QIcon::fromTheme(index.model()->data(index, Qt::DecorationRole).toString());
0065     icon.paint(painter, iconRect);
0066 
0067     // draw text
0068     if (option.state & QStyle::State_Selected) {
0069         painter->setPen(option.palette.highlightedText().color());
0070     }
0071 
0072     // ... name
0073     painter->save();
0074     const QFont nameFont = ::nameFont(option.font);
0075     QFontMetrics nameFontMetrics(nameFont);
0076     painter->setFont(nameFont);
0077     painter->drawText(textRect,
0078                       Qt::AlignLeft | Qt::AlignTop,
0079                       nameFontMetrics.elidedText(index.model()->data(index, Qt::DisplayRole).toString(), Qt::ElideRight, textRect.width()));
0080     painter->restore();
0081 
0082     // ... comment
0083     painter->drawText(textRect,
0084                       Qt::AlignLeft | Qt::AlignBottom,
0085                       option.fontMetrics.elidedText(index.model()->data(index, StructuresSelectionModel::CommentRole).toString(), Qt::ElideRight, textRect.width()));
0086 
0087     painter->restore();
0088 }
0089 
0090 QSize StructureItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
0091 {
0092     QRect checkBoxRect;
0093     QRect iconRect;
0094     QRect textRect;
0095     QRect aboutButtonRect;
0096     int focusFrameHMargin;
0097     layoutItem(option, index, &checkBoxRect, &iconRect, &textRect, &aboutButtonRect, &focusFrameHMargin, LayoutForSizeHint);
0098 
0099     const int width = checkBoxRect.width() + iconRect.width() + textRect.width() + aboutButtonRect.width() + 8 * focusFrameHMargin;
0100     const int height = qMax(checkBoxRect.height(), qMax(iconRect.height(), qMax(textRect.height(), aboutButtonRect.height()))) + 2 * focusFrameHMargin;
0101     return QSize(width, height);
0102 }
0103 
0104 QList<QWidget*> StructureItemDelegate::createItemWidgets(const QModelIndex& index) const
0105 {
0106     Q_UNUSED(index);
0107 
0108     auto* enabledCheckBox = new QCheckBox;
0109     connect(enabledCheckBox, &QAbstractButton::clicked,
0110             this, &StructureItemDelegate::handleCheckClicked);
0111 
0112     auto* aboutButton = new QPushButton;
0113     aboutButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information")));
0114     aboutButton->setToolTip(i18nc("@info:tooltip", "About"));
0115     connect(aboutButton, &QAbstractButton::clicked,
0116             this, &StructureItemDelegate::handleAboutClicked);
0117 
0118     const QList<QEvent::Type> blockedEventTypes {
0119         QEvent::MouseButtonPress,
0120         QEvent::MouseButtonRelease,
0121         QEvent::MouseButtonDblClick,
0122         QEvent::KeyPress,
0123         QEvent::KeyRelease,
0124     };
0125     setBlockedEventTypes(enabledCheckBox, blockedEventTypes);
0126     setBlockedEventTypes(aboutButton, blockedEventTypes);
0127 
0128     const QList<QWidget*> widgetList {
0129         enabledCheckBox,
0130         aboutButton,
0131     };
0132 
0133     return widgetList;
0134 }
0135 
0136 void StructureItemDelegate::updateItemWidgets(const QList<QWidget*> widgets,
0137                                               const QStyleOptionViewItem &option,
0138                                               const QPersistentModelIndex& index) const
0139 {
0140     QRect checkBoxRect;
0141     QRect aboutButtonRect;
0142     layoutItem(option, index, &checkBoxRect, nullptr, nullptr, &aboutButtonRect, nullptr, LayoutForActualSize);
0143 
0144     // check box
0145     auto* checkBox = static_cast<QCheckBox*>(widgets[0]);
0146     checkBox->resize(checkBoxRect.size());
0147     checkBox->move(checkBoxRect.topLeft());
0148 
0149     // about button
0150     auto* aboutButton = static_cast<QPushButton *>(widgets[1]);
0151     aboutButton->resize(aboutButtonRect.size());
0152     aboutButton->move(aboutButtonRect.topLeft());
0153 
0154     if (!index.isValid()) {
0155         checkBox->setVisible(false);
0156         aboutButton->setVisible(false);
0157     } else {
0158         checkBox->setChecked(index.data(Qt::CheckStateRole).toBool());
0159     }
0160 }
0161 
0162 void StructureItemDelegate::layoutItem(const QStyleOptionViewItem& option,
0163                                        const QModelIndex& index,
0164                                        QRect* checkBoxRectToUpdate,
0165                                        QRect* iconRectToUpdate,
0166                                        QRect* textRectToUpdate,
0167                                        QRect* aboutButtonRectToUpdate,
0168                                        int* focusFrameHMarginToUpdate,
0169                                        LayoutMode layoutMode) const
0170 {
0171     const QWidget* widget = option.widget;
0172     QStyle* style = widget ? widget->style() : QApplication::style();
0173     // "+ 1" as used in QItemDelegate
0174     const int focusFrameHMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, widget) + 1;
0175     if (focusFrameHMarginToUpdate) {
0176         *focusFrameHMarginToUpdate = focusFrameHMargin;
0177     }
0178 
0179     const QSize checkBoxSize = m_sampleCheckBox->sizeHint();
0180     const QSize aboutButtonSize = m_sampleAboutButton->sizeHint();
0181     const int marginedTop = option.rect.top() + focusFrameHMargin;
0182     const int marginedHeight = option.rect.height() - 2 * focusFrameHMargin;
0183     // KWidgetItemDelegate::updateItemWidgets(...) docs say:
0184     // "All the positioning and sizing should be done in item coordinates."
0185     // So translating the positions when updating the widget rects
0186     const QPoint widgetTranslation(-option.rect.left(), -option.rect.top());
0187 
0188     const int checkBoxXOffset = option.rect.left() + ((option.direction == Qt::LeftToRight) ?
0189         focusFrameHMargin :
0190         (option.rect.width() - checkBoxSize.width() - focusFrameHMargin));
0191     const int checkBoxYOffset = marginedTop  + (marginedHeight - checkBoxSize.height()) / 2;
0192 
0193     const QRect checkBoxRect(QPoint(checkBoxXOffset, checkBoxYOffset), checkBoxSize);
0194     if (checkBoxRectToUpdate) {
0195         *checkBoxRectToUpdate = checkBoxRect.translated(widgetTranslation);
0196     }
0197 
0198     const int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize);
0199     const int iconXOffset = (option.direction == Qt::LeftToRight) ?
0200         (checkBoxRect.right() + 1 + 2 * focusFrameHMargin) :
0201         (checkBoxRect.left() - iconSize - 2 * focusFrameHMargin);
0202     const int iconYOffset = marginedTop + (marginedHeight - iconSize) / 2;
0203 
0204     const QRect iconRect(iconXOffset, iconYOffset, iconSize, iconSize);
0205     if (iconRectToUpdate) {
0206         *iconRectToUpdate = iconRect;
0207     }
0208 
0209     const int aboutButtonXOffset = option.rect.left() + ((option.direction == Qt::LeftToRight) ?
0210         (option.rect.width() - aboutButtonSize.width() - focusFrameHMargin) :
0211         focusFrameHMargin);
0212     const int aboutButtonYOffset = marginedTop + (marginedHeight - aboutButtonSize.height()) / 2;
0213 
0214     const QRect aboutButtonRect(QPoint(aboutButtonXOffset, aboutButtonYOffset), aboutButtonSize);
0215     if (aboutButtonRectToUpdate) {
0216         *aboutButtonRectToUpdate = aboutButtonRect.translated(widgetTranslation);
0217     }
0218 
0219     if (textRectToUpdate) {
0220         if (layoutMode == LayoutForSizeHint) {
0221             const QFont nameFont = ::nameFont(option.font);
0222             const QFontMetrics nameFontMetrics(nameFont);
0223             const QString name = index.model()->data(index, Qt::DisplayRole).toString();
0224             const QString comment = index.model()->data(index, StructuresSelectionModel::CommentRole).toString();
0225             const int contentWidthHint = qMax(nameFontMetrics.boundingRect(name).width(), option.fontMetrics.boundingRect(comment).width());
0226             const int contentHeightHint = nameFontMetrics.height() + option.fontMetrics.height();
0227             *textRectToUpdate = QRect(0, 0, contentWidthHint, contentHeightHint);
0228         } else {
0229             const int contentHeight = option.rect.height() - 2 * focusFrameHMargin;
0230             const int contentWidth = ((option.direction == Qt::LeftToRight) ?
0231                 (aboutButtonRect.left() - iconRect.right()) :
0232                 (iconRect.left() - aboutButtonRect.right()))
0233                 - 1 - 4 * focusFrameHMargin;
0234             const int contentXOffset = ((option.direction == Qt::LeftToRight) ?
0235                 iconRect.right() : aboutButtonRect.right()) + 1 + 2 * focusFrameHMargin;
0236             const int contentYOffset = marginedTop;
0237             *textRectToUpdate = QRect(contentXOffset, contentYOffset, contentWidth, contentHeight);
0238         }
0239     }
0240 }
0241 
0242 void StructureItemDelegate::handleAboutClicked()
0243 {
0244     const QModelIndex index = focusedIndex();
0245     if (!index.isValid()) {
0246         return;
0247     }
0248 
0249     const StructureMetaData metaData =
0250         index.data(StructuresSelectionModel::MetaDataRole).value<StructureMetaData>();
0251 
0252     auto* aboutDialog = new AboutStructureDialog(metaData, itemView());
0253     aboutDialog->show();
0254 }
0255 
0256 void StructureItemDelegate::handleCheckClicked(bool checked)
0257 {
0258     const QModelIndex index = focusedIndex();
0259 
0260     if (!index.isValid()) {
0261         return;
0262     }
0263     auto* model = const_cast<QAbstractItemModel *>(index.model());
0264 
0265     model->setData(index, checked, Qt::CheckStateRole);
0266 }
0267 
0268 #include "moc_structureitemdelegate.cpp"