File indexing completed on 2025-01-05 04:59:46
0001 /* 0002 * SPDX-FileCopyrightText: 2014-2016 Kevin Ottens <ervin@kde.org> 0003 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0004 */ 0005 0006 0007 #include "itemdelegate.h" 0008 0009 #include <QApplication> 0010 #include <QPainter> 0011 #include <QStyleOptionViewItem> 0012 0013 #include <KLocalizedString> 0014 0015 #include "domain/task.h" 0016 #include "presentation/querytreemodelbase.h" 0017 #include "utils/datetime.h" 0018 0019 using namespace Widgets; 0020 0021 ItemDelegate::ItemDelegate(QObject *parent) 0022 : QStyledItemDelegate(parent) 0023 { 0024 } 0025 0026 QSize ItemDelegate::sizeHint(const QStyleOptionViewItem &option, 0027 const QModelIndex &index) const 0028 { 0029 QStyleOptionViewItem opt = option; 0030 initStyleOption(&opt, index); 0031 // Make sure they all get the height needed for a check indicator 0032 opt.features = QStyleOptionViewItem::HasCheckIndicator; 0033 // and for a date on the right 0034 opt.text += ' ' + QLocale().dateFormat(QLocale::ShortFormat).toUpper() + ' '; 0035 QSize sz = QStyledItemDelegate::sizeHint(opt, index); 0036 const auto projectInfo = index.data(Presentation::QueryTreeModelBase::ProjectRole); 0037 const auto dataSourceInfo = index.data(Presentation::QueryTreeModelBase::DataSourceRole); 0038 const auto contextListInfo = index.data(Presentation::QueryTreeModelBase::ContextListRole); 0039 const auto hasAdditionalInfo = projectInfo.isValid() || dataSourceInfo.isValid() || contextListInfo.isValid(); 0040 if (hasAdditionalInfo) 0041 sz.rheight() += opt.fontMetrics.height(); 0042 return sz; 0043 } 0044 0045 void ItemDelegate::paint(QPainter *painter, 0046 const QStyleOptionViewItem &option, 0047 const QModelIndex &index) const 0048 { 0049 const auto data = index.data(Presentation::QueryTreeModelBase::ObjectRole); 0050 auto task = data.value<Domain::Task::Ptr>(); 0051 0052 auto opt = QStyleOptionViewItem(option); 0053 initStyleOption(&opt, index); 0054 const auto widget = opt.widget; 0055 const auto style = widget ? widget->style() : QApplication::style(); 0056 0057 const auto isDone = task ? task->isDone() : false; 0058 const auto isEnabled = (opt.state & QStyle::State_Enabled); 0059 const auto isActive = (opt.state & QStyle::State_Active); 0060 const auto isSelected = (opt.state & QStyle::State_Selected); 0061 const auto isEditing = (opt.state & QStyle::State_Editing); 0062 0063 const auto startDate = task ? task->startDate() : QDate(); 0064 const auto dueDate = task ? task->dueDate() : QDate(); 0065 const auto projectInfo = index.data(Presentation::QueryTreeModelBase::ProjectRole); 0066 const auto dataSourceInfo = index.data(Presentation::QueryTreeModelBase::DataSourceRole); 0067 const auto contextListInfo = index.data(Presentation::QueryTreeModelBase::ContextListRole); 0068 const auto hasAdditionalInfo = projectInfo.isValid() || dataSourceInfo.isValid() || contextListInfo.isValid(); 0069 0070 const auto currentDate = Utils::DateTime::currentDate(); 0071 const auto onStartDate = startDate.isValid() && startDate <= currentDate; 0072 const auto pastDueDate = dueDate.isValid() && dueDate < currentDate; 0073 const auto onDueDate = dueDate.isValid() && dueDate == currentDate; 0074 0075 const auto baseFont = opt.font; 0076 const auto summaryFont = [=] { 0077 auto font = baseFont; 0078 font.setStrikeOut(isDone); 0079 font.setBold(!isDone && (onStartDate || onDueDate || pastDueDate)); 0080 return font; 0081 }(); 0082 const auto summaryMetrics = QFontMetrics(summaryFont); 0083 0084 const auto colorGroup = (isEnabled && !isActive) ? QPalette::Inactive 0085 : isEnabled ? QPalette::Normal 0086 : QPalette::Disabled; 0087 const auto colorRole = (isSelected && !isEditing) ? QPalette::HighlightedText : QPalette::Text; 0088 0089 const auto baseColor = opt.palette.color(colorGroup, colorRole); 0090 const auto summaryColor = isDone ? baseColor 0091 : pastDueDate ? QColor(Qt::red) 0092 : onDueDate ? QColor("orange") 0093 : baseColor; 0094 0095 const auto summaryText = opt.text; 0096 const auto dueDateText = dueDate.isValid() ? QLocale().toString(dueDate, QLocale::ShortFormat) 0097 : QString(); 0098 0099 const auto textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1; 0100 const auto dueDateWidth = dueDate.isValid() ? (summaryMetrics.horizontalAdvance(dueDateText) + 2 * textMargin) : 0; 0101 0102 0103 const auto checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget); 0104 const auto textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &opt, widget) 0105 .adjusted(textMargin, 0, - textMargin, 0); 0106 auto summaryRect = textRect.adjusted(0, 0, -dueDateWidth, 0); 0107 if (hasAdditionalInfo) 0108 summaryRect.setHeight(summaryRect.height() - opt.fontMetrics.height()); 0109 auto dueDateRect = textRect.adjusted(textRect.width() - dueDateWidth, 0, 0, 0); 0110 dueDateRect.setHeight(summaryRect.height()); 0111 0112 const auto additionalInfoRect = QRect(textRect.x(), summaryRect.bottom(), textRect.width(), textRect.height() - summaryRect.height()); 0113 0114 // Draw background 0115 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, widget); 0116 0117 // Draw the check box 0118 if (task) { 0119 auto checkOption = opt; 0120 checkOption.rect = checkRect; 0121 checkOption.state = option.state & ~QStyle::State_HasFocus; 0122 checkOption.state |= isDone ? QStyle::State_On : QStyle::State_Off; 0123 style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &checkOption, painter, widget); 0124 } 0125 0126 // Draw the summary 0127 if (!summaryText.isEmpty()) { 0128 painter->setPen(summaryColor); 0129 painter->setFont(summaryFont); 0130 painter->drawText(summaryRect, Qt::AlignVCenter, 0131 summaryMetrics.elidedText(summaryText, Qt::ElideRight, summaryRect.width())); 0132 } 0133 0134 // Draw the due date 0135 if (!dueDateText.isEmpty()) { 0136 painter->drawText(dueDateRect, Qt::AlignCenter, dueDateText); 0137 } 0138 0139 // Draw the second line 0140 if (hasAdditionalInfo) { 0141 const auto additionalInfo = projectInfo.isValid() && !projectInfo.toString().isEmpty() ? i18n("Project: %1", projectInfo.toString()) 0142 : dataSourceInfo.isValid() ? dataSourceInfo.toString() 0143 : i18n("Inbox"); 0144 0145 QFont additionalInfoFont = baseFont; 0146 additionalInfoFont.setItalic(true); 0147 additionalInfoFont.setPointSize(additionalInfoFont.pointSize() - 1); 0148 painter->setFont(additionalInfoFont); 0149 painter->drawText(additionalInfoRect, Qt::AlignLeft, additionalInfo); 0150 } 0151 } 0152 0153 QWidget *ItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const 0154 { 0155 m_editingState = EditingState::JustCreatedEditor; 0156 return QStyledItemDelegate::createEditor(parent, option, index); 0157 } 0158 0159 void ItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const 0160 { 0161 m_editingState = EditingState::NotEditing; 0162 QStyledItemDelegate::setModelData(editor, model, index); 0163 } 0164 0165 void ItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const 0166 { 0167 // The first call gets through (initial value) 0168 // And later call is rejected, the user's own value takes precedence 0169 if (m_editingState == EditingState::JustCreatedEditor) { 0170 m_editingState = EditingState::Editing; 0171 QStyledItemDelegate::setEditorData(editor, index); 0172 } 0173 } 0174 0175 #include "moc_itemdelegate.cpp"