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"