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"