File indexing completed on 2024-04-28 04:39:10
0001 /* 0002 SPDX-FileCopyrightText: 2013 Aleix Pol <aleixpol@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "projectmodelitemdelegate.h" 0008 0009 #include "vcsoverlayproxymodel.h" 0010 #include <debug.h> 0011 0012 #include <project/projectmodel.h> 0013 #include <language/duchain/duchainutils.h> 0014 #include <language/duchain/duchainlock.h> 0015 #include <language/duchain/duchain.h> 0016 #include <language/util/navigationtooltip.h> 0017 #include <util/path.h> 0018 0019 #include <QHelpEvent> 0020 #include <QToolTip> 0021 #include <QAbstractItemView> 0022 #include <QPainter> 0023 0024 using namespace KDevelop; 0025 0026 ProjectModelItemDelegate::ProjectModelItemDelegate(QObject* parent) 0027 : QItemDelegate(parent) 0028 {} 0029 0030 static QIcon::Mode IconMode( QStyle::State state ) 0031 { 0032 if (!(state & QStyle::State_Enabled)) { 0033 return QIcon::Disabled; 0034 } else if (state & QStyle::State_Selected) { 0035 return QIcon::Selected; 0036 } else { 0037 return QIcon::Normal; 0038 } 0039 } 0040 0041 static QIcon::State IconState(QStyle::State state) 0042 { 0043 return (state & QStyle::State_Open) ? QIcon::On : QIcon::Off; 0044 } 0045 0046 void ProjectModelItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& opt, const QModelIndex& index) const 0047 { 0048 // Qt5.5 HiDPI Fix part (1/2) 0049 // This fix is based on how Qt5.5's QItemDelegate::paint implementation deals with the same issue 0050 // Unfortunately, there doesn't seem to be a clean way to use the base implementation 0051 // and have the added functionality this class provides 0052 QPixmap decoData; 0053 QRect decorationRect; 0054 QIcon icon; 0055 QIcon::Mode mode = QIcon::Mode::Disabled; 0056 QIcon::State state = QIcon::State::Off; 0057 { 0058 QVariant value; 0059 value = index.data(Qt::DecorationRole); 0060 if (value.isValid()) { 0061 decoData = decoration(opt, value); 0062 0063 if (value.type() == QVariant::Icon) { 0064 icon = qvariant_cast<QIcon>(value); 0065 mode = IconMode(opt.state); 0066 state = IconState(opt.state); 0067 QSize size = icon.actualSize( opt.decorationSize, mode, state ); 0068 if (size.isEmpty()) { 0069 // For items with an empty icon, set size to opt.decorationSize to make them have the same indent as items with a valid icon 0070 size = opt.decorationSize; 0071 } 0072 decorationRect = QRect(QPoint(0, 0), size); 0073 } else { 0074 decorationRect = QRect(QPoint(0, 0), decoData.size()); 0075 } 0076 } else { 0077 decorationRect = QRect(); 0078 } 0079 } 0080 0081 0082 QRect checkRect; //unused in practice 0083 0084 QRect spaceLeft = opt.rect; 0085 spaceLeft.setLeft(decorationRect.right()); 0086 QString displayData = index.data(Qt::DisplayRole).toString(); 0087 QRect displayRect = textRectangle(painter, spaceLeft, opt.font, displayData); 0088 displayRect.setLeft(spaceLeft.left()); 0089 0090 QRect branchNameRect(displayRect.topRight(), opt.rect.bottomRight()); 0091 0092 doLayout(opt, &checkRect, &decorationRect, &displayRect, false); 0093 branchNameRect.setLeft(branchNameRect.left() + displayRect.left()); 0094 branchNameRect.setTop(displayRect.top()); 0095 0096 drawStyledBackground(painter, opt); 0097 // drawCheck(painter, opt, checkRect, checkState); 0098 0099 // Qt5.5 HiDPI Fix part (2/2) 0100 // use the QIcon from above if possible 0101 if (!icon.isNull()) { 0102 icon.paint(painter, decorationRect, opt.decorationAlignment, mode, state ); 0103 } else { 0104 drawDecoration(painter, opt, decorationRect, decoData); 0105 } 0106 0107 drawDisplay(painter, opt, displayRect, displayData); 0108 0109 /// FIXME: this can apparently trigger a nested eventloop, see 0110 /// https://bugs.kde.org/show_bug.cgi?id=355099 0111 QString branchNameData = index.data(VcsOverlayProxyModel::VcsStatusRole).toString(); 0112 drawBranchName(painter, opt, branchNameRect, branchNameData); 0113 drawFocus(painter, opt, displayRect); 0114 0115 } 0116 0117 void ProjectModelItemDelegate::drawBranchName(QPainter* painter, const QStyleOptionViewItem& option, 0118 const QRect& rect, const QString& branchName) const 0119 { 0120 QString text = option.fontMetrics.elidedText(branchName, Qt::ElideRight, rect.width()); 0121 0122 bool selected = option.state & QStyle::State_Selected; 0123 QPalette::ColorGroup colorGroup = selected ? QPalette::Active : QPalette::Disabled; 0124 painter->save(); 0125 painter->setPen(option.palette.color(colorGroup, QPalette::Text)); 0126 painter->drawText(rect, text); 0127 painter->restore(); 0128 } 0129 0130 void ProjectModelItemDelegate::drawStyledBackground(QPainter* painter, const QStyleOptionViewItem& option) const 0131 { 0132 QStyleOptionViewItem opt(option); 0133 QStyle *style = opt.widget->style(); 0134 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, opt.widget); 0135 } 0136 0137 void ProjectModelItemDelegate::drawDisplay(QPainter* painter, const QStyleOptionViewItem& option, 0138 const QRect& rect, const QString& text) const 0139 { 0140 QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) 0141 ? QPalette::Normal : QPalette::Disabled; 0142 if (option.state & QStyle::State_Editing) { 0143 painter->save(); 0144 painter->setPen(option.palette.color(cg, QPalette::Text)); 0145 painter->drawRect(rect.adjusted(0, 0, -1, -1)); 0146 painter->restore(); 0147 } 0148 0149 if(text.isEmpty()) { 0150 return; 0151 } 0152 0153 if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) { 0154 cg = QPalette::Inactive; 0155 } 0156 if (option.state & QStyle::State_Selected) { 0157 painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); 0158 } else { 0159 painter->setPen(option.palette.color(cg, QPalette::Text)); 0160 } 0161 0162 QFontMetrics fm(painter->fontMetrics()); 0163 painter->drawText(rect, fm.elidedText(text, Qt::ElideRight, rect.width())); 0164 } 0165 0166 bool ProjectModelItemDelegate::helpEvent(QHelpEvent* event, 0167 QAbstractItemView* view, const QStyleOptionViewItem& option, 0168 const QModelIndex& index) 0169 { 0170 if (!event || !view) { 0171 return false; 0172 } 0173 0174 if (event->type() == QEvent::ToolTip) { 0175 // explicitly close current tooltip, as its autoclose margins overlap items 0176 if ((m_tooltippedIndex != index) && m_tooltip) { 0177 m_tooltip->close(); 0178 m_tooltip.clear(); 0179 } 0180 0181 const ProjectBaseItem* it = index.data(ProjectModel::ProjectItemRole).value<ProjectBaseItem*>(); 0182 0183 // show navigation tooltip for files 0184 if (it && it->file()) { 0185 if (!m_tooltip) { 0186 m_tooltippedIndex = index; 0187 KDevelop::DUChainReadLocker lock(KDevelop::DUChain::lock()); 0188 const TopDUContext* top = DUChainUtils::standardContextForUrl(it->file()->path().toUrl()); 0189 0190 if (top) { 0191 if (auto* navigationWidget = top->createNavigationWidget()) { 0192 // force possible existing normal tooltip for other list item to hide 0193 // Seems that is still only done with a small delay though, 0194 // but the API seems not to allow more control. 0195 QToolTip::hideText(); 0196 0197 m_tooltip = new KDevelop::NavigationToolTip(view, event->globalPos() + QPoint(40, 0), navigationWidget); 0198 m_tooltip->resize(navigationWidget->sizeHint() + QSize(10, 10)); 0199 auto rect = view->visualRect(m_tooltippedIndex); 0200 rect.moveTopLeft(view->mapToGlobal(rect.topLeft())); 0201 m_tooltip->setHandleRect(rect); 0202 ActiveToolTip::showToolTip(m_tooltip); 0203 } 0204 } 0205 } 0206 0207 // tooltip successfully handled by us? 0208 if (m_tooltip) { 0209 return true; 0210 } 0211 } 0212 } 0213 0214 return QItemDelegate::helpEvent(event, view, option, index); 0215 } 0216 0217 #include "moc_projectmodelitemdelegate.cpp"