Warning, file /kdevelop/kdevelop/plugins/quickopen/expandingtree/expandingdelegate.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org> 0003 SPDX-FileCopyrightText: 2007 David Nolden <david.nolden.kdevelop@art-master.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "expandingdelegate.h" 0009 0010 #include <QTextLine> 0011 #include <QPainter> 0012 #include <QBrush> 0013 #include <QTreeView> 0014 #include <QApplication> 0015 0016 #include "expandingwidgetmodel.h" 0017 #include <debug.h> 0018 0019 ExpandingDelegate::ExpandingDelegate(ExpandingWidgetModel* model, QObject* parent) 0020 : QItemDelegate(parent) 0021 , m_model(model) 0022 { 0023 } 0024 0025 //Gets the background-color in the way QItemDelegate does it 0026 static QColor getUsedBackgroundColor(const QStyleOptionViewItem& option, const QModelIndex& index) 0027 { 0028 if (option.showDecorationSelected && (option.state & QStyle::State_Selected)) { 0029 QPalette::ColorGroup cg = option.state & QStyle::State_Enabled 0030 ? QPalette::Normal : QPalette::Disabled; 0031 if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) { 0032 cg = QPalette::Inactive; 0033 } 0034 0035 return option.palette.brush(cg, QPalette::Highlight).color(); 0036 } else { 0037 QVariant value = index.data(Qt::BackgroundRole); 0038 if ((value).canConvert<QBrush>()) { 0039 return qvariant_cast<QBrush>(value).color(); 0040 } 0041 } 0042 0043 return QApplication::palette().base().color(); 0044 } 0045 0046 static void dampColors(QColor& col) 0047 { 0048 //Reduce the colors that are less visible to the eye, because they are closer to black when it comes to contrast 0049 //The most significant color to the eye is green. Then comes red, and then blue, with blue _much_ less significant. 0050 0051 col.setBlue(0); 0052 col.setRed(col.red() / 2); 0053 } 0054 0055 //A hack to compute more eye-focused contrast values 0056 static double readabilityContrast(QColor foreground, QColor background) 0057 { 0058 dampColors(foreground); 0059 dampColors(background); 0060 return abs(foreground.green() - background.green()) + abs(foreground.red() - background.red()) + abs(foreground.blue() - background.blue()); 0061 } 0062 0063 void ExpandingDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optionOld, const QModelIndex& index) const 0064 { 0065 QStyleOptionViewItem option(optionOld); 0066 0067 m_currentIndex = index; 0068 0069 adjustStyle(index, option); 0070 0071 const QModelIndex sourceIndex = model()->mapToSource(index); 0072 if (index.column() == 0) { 0073 model()->placeExpandingWidget(sourceIndex); 0074 } 0075 0076 //Make sure the decorations are painted at the top, because the center of expanded items will be filled with the embedded widget. 0077 if (model()->isPartiallyExpanded(sourceIndex) == ExpandingWidgetModel::ExpandUpwards) { 0078 m_cachedAlignment = Qt::AlignBottom; 0079 } else { 0080 m_cachedAlignment = Qt::AlignTop; 0081 } 0082 0083 option.decorationAlignment = m_cachedAlignment; 0084 option.displayAlignment = m_cachedAlignment; 0085 0086 //qCDebug( PLUGIN_QUICKOPEN ) << "Painting row " << index.row() << ", column " << index.column() << ", internal " << index.internalPointer() << ", drawselected " << option.showDecorationSelected << ", selected " << (option.state & QStyle::State_Selected); 0087 0088 m_cachedHighlights.clear(); 0089 m_backgroundColor = getUsedBackgroundColor(option, index); 0090 0091 if (model()->indexIsItem(sourceIndex)) { 0092 m_currentColumnStart = 0; 0093 m_cachedHighlights = createHighlighting(index, option); 0094 } 0095 0096 /*qCDebug( PLUGIN_QUICKOPEN ) << "Highlights for line:"; 0097 foreach (const QTextLayout::FormatRange& fr, m_cachedHighlights) 0098 qCDebug( PLUGIN_QUICKOPEN ) << fr.start << " len " << fr.length << " format ";*/ 0099 0100 QItemDelegate::paint(painter, option, index); 0101 0102 ///This is a bug workaround for the Qt raster paint engine: It paints over widgets embedded into the viewport when updating due to mouse events 0103 ///@todo report to Qt Software 0104 if (model()->isExpanded(sourceIndex) && model()->expandingWidget(sourceIndex)) { 0105 model()->expandingWidget(sourceIndex)->update(); 0106 } 0107 } 0108 0109 QVector<QTextLayout::FormatRange> ExpandingDelegate::createHighlighting(const QModelIndex& index, QStyleOptionViewItem& option) const 0110 { 0111 Q_UNUSED(index); 0112 Q_UNUSED(option); 0113 return QVector<QTextLayout::FormatRange>(); 0114 } 0115 0116 QSize ExpandingDelegate::basicSizeHint(const QModelIndex& index) const 0117 { 0118 return QItemDelegate::sizeHint(QStyleOptionViewItem(), index); 0119 } 0120 0121 QSize ExpandingDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const 0122 { 0123 const QModelIndex sourceIndex = model()->mapToSource(index); 0124 QSize s = QItemDelegate::sizeHint(option, index); 0125 if (model()->isExpanded(sourceIndex) && model()->expandingWidget(sourceIndex)) { 0126 QWidget* widget = model()->expandingWidget(sourceIndex); 0127 QSize widgetSize = widget->size(); 0128 0129 s.setHeight(widgetSize.height() + s.height() + 10); //10 is the sum that must match exactly the offsets used in ExpandingWidgetModel::placeExpandingWidgets 0130 } else if (model()->isPartiallyExpanded(sourceIndex) != ExpandingWidgetModel::ExpansionType::NotExpanded) { 0131 s.setHeight(s.height() + 30 + 10); 0132 } 0133 return s; 0134 } 0135 0136 void ExpandingDelegate::adjustStyle(const QModelIndex& index, QStyleOptionViewItem& option) const 0137 { 0138 Q_UNUSED(index) 0139 Q_UNUSED(option) 0140 } 0141 0142 void ExpandingDelegate::adjustRect(QRect& rect) const 0143 { 0144 const QModelIndex sourceIndex = model()->mapToSource(m_currentIndex); 0145 if (!model()->indexIsItem(sourceIndex) /*&& m_currentIndex.column() == 0*/) { 0146 rect.setLeft(model()->treeView()->columnViewportPosition(0)); 0147 int columnCount = model()->columnCount(sourceIndex.parent()); 0148 0149 if (!columnCount) { 0150 return; 0151 } 0152 rect.setRight(model()->treeView()->columnViewportPosition(columnCount - 1) + model()->treeView()->columnWidth(columnCount - 1)); 0153 } 0154 } 0155 0156 void ExpandingDelegate::drawDisplay(QPainter* painter, const QStyleOptionViewItem& option, const QRect& _rect, const QString& text) const 0157 { 0158 QRect rect(_rect); 0159 0160 adjustRect(rect); 0161 0162 QTextLayout layout(text, option.font, painter->device()); 0163 0164 QVector<QTextLayout::FormatRange> additionalFormats; 0165 0166 int missingFormats = text.length(); 0167 0168 for (int i = 0; i < m_cachedHighlights.count(); ++i) { 0169 if (m_cachedHighlights[i].start + m_cachedHighlights[i].length <= m_currentColumnStart) { 0170 continue; 0171 } 0172 0173 if (additionalFormats.isEmpty()) { 0174 if (i != 0 && m_cachedHighlights[i - 1].start + m_cachedHighlights[i - 1].length > m_currentColumnStart) { 0175 QTextLayout::FormatRange before; 0176 before.start = 0; 0177 before.length = m_cachedHighlights[i - 1].start + m_cachedHighlights[i - 1].length - m_currentColumnStart; 0178 before.format = m_cachedHighlights[i - 1].format; 0179 additionalFormats.append(before); 0180 } 0181 } 0182 0183 QTextLayout::FormatRange format; 0184 format.start = m_cachedHighlights[i].start - m_currentColumnStart; 0185 format.length = m_cachedHighlights[i].length; 0186 format.format = m_cachedHighlights[i].format; 0187 0188 additionalFormats.append(format); 0189 } 0190 0191 if (!additionalFormats.isEmpty()) { 0192 missingFormats = text.length() - (additionalFormats.back().length + additionalFormats.back().start); 0193 } 0194 0195 if (missingFormats > 0) { 0196 QTextLayout::FormatRange format; 0197 format.start = text.length() - missingFormats; 0198 format.length = missingFormats; 0199 QTextCharFormat fm; 0200 fm.setForeground(option.palette.text()); 0201 format.format = fm; 0202 additionalFormats.append(format); 0203 } 0204 0205 if (m_backgroundColor.isValid()) { 0206 QColor background = m_backgroundColor; 0207 // qCDebug(PLUGIN_QUICKOPEN) << text << "background:" << background.name(); 0208 //Now go through the formats, and make sure the contrast background/foreground is readable 0209 for (auto& additionalFormat : additionalFormats) { 0210 QColor currentBackground = background; 0211 if (additionalFormat.format.hasProperty(QTextFormat::BackgroundBrush)) { 0212 currentBackground = additionalFormat.format.background().color(); 0213 } 0214 0215 QColor currentColor = additionalFormat.format.foreground().color(); 0216 0217 double currentContrast = readabilityContrast(currentColor, currentBackground); 0218 QColor invertedColor(0xffffffff - additionalFormat.format.foreground().color().rgb()); 0219 double invertedContrast = readabilityContrast(invertedColor, currentBackground); 0220 0221 // qCDebug(PLUGIN_QUICKOPEN) << "values:" << invertedContrast << currentContrast << invertedColor.name() << currentColor.name(); 0222 0223 if (invertedContrast > currentContrast) { 0224 // qCDebug(PLUGIN_QUICKOPEN) << text << additionalFormats[a].length << "switching from" << currentColor.name() << "to" << invertedColor.name(); 0225 QBrush b(additionalFormat.format.foreground()); 0226 b.setColor(invertedColor); 0227 additionalFormat.format.setForeground(b); 0228 } 0229 } 0230 } 0231 0232 for (int a = additionalFormats.size() - 1; a >= 0; --a) { 0233 if (additionalFormats[a].length == 0) { 0234 additionalFormats.removeAt(a); 0235 } else { 0236 ///For some reason the text-formats seem to be invalid in some way, sometimes 0237 ///@todo Fix this properly, it sucks not copying everything over 0238 QTextCharFormat fm; 0239 fm.setForeground(QBrush(additionalFormats[a].format.foreground().color())); 0240 fm.setBackground(additionalFormats[a].format.background()); 0241 fm.setUnderlineStyle(additionalFormats[a].format.underlineStyle()); 0242 fm.setUnderlineColor(additionalFormats[a].format.underlineColor()); 0243 fm.setFontWeight(additionalFormats[a].format.fontWeight()); 0244 additionalFormats[a].format = fm; 0245 } 0246 } 0247 0248 // qCDebug( PLUGIN_QUICKOPEN ) << "Highlights for text [" << text << "] col start " << m_currentColumnStart << ":"; 0249 // foreach (const QTextLayout::FormatRange& fr, additionalFormats) 0250 // qCDebug( PLUGIN_QUICKOPEN ) << fr.start << " len " << fr.length << "foreground" << fr.format.foreground() << "background" << fr.format.background(); 0251 0252 layout.setFormats(additionalFormats); 0253 0254 QTextOption to; 0255 0256 to.setAlignment(m_cachedAlignment); 0257 0258 to.setWrapMode(QTextOption::WrapAnywhere); 0259 layout.setTextOption(to); 0260 0261 layout.beginLayout(); 0262 QTextLine line = layout.createLine(); 0263 line.setLineWidth(rect.width()); 0264 layout.endLayout(); 0265 0266 //We need to do some hand layouting here 0267 if (to.alignment() & Qt::AlignBottom) { 0268 layout.draw(painter, QPoint(rect.left(), rect.bottom() - ( int )line.height())); 0269 } else { 0270 layout.draw(painter, rect.topLeft()); 0271 } 0272 0273 return; 0274 0275 //if (painter->fontMetrics().width(text) > textRect.width() && !text.contains(QLatin1Char('\n'))) 0276 //str = elidedText(option.fontMetrics, textRect.width(), option.textElideMode, text); 0277 //qt_format_text(option.font, textRect, option.displayAlignment, str, 0, 0, 0, 0, painter); 0278 } 0279 0280 void ExpandingDelegate::drawDecoration(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QPixmap& pixmap) const 0281 { 0282 const QModelIndex sourceIndex = model()->mapToSource(m_currentIndex); 0283 if (model()->indexIsItem(sourceIndex)) { 0284 QItemDelegate::drawDecoration(painter, option, rect, pixmap); 0285 } 0286 } 0287 0288 void ExpandingDelegate::drawBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const 0289 { 0290 Q_UNUSED(index) 0291 QStyleOptionViewItem opt = option; 0292 //initStyleOption(&opt, index); 0293 //Problem: This isn't called at all, because drawBrackground is not virtual :-/ 0294 QStyle* style = model()->treeView()->style() ? model()->treeView()->style() : QApplication::style(); 0295 style->drawControl(QStyle::CE_ItemViewItem, &opt, painter); 0296 } 0297 0298 ExpandingWidgetModel* ExpandingDelegate::model() const 0299 { 0300 return m_model; 0301 } 0302 0303 void ExpandingDelegate::heightChanged() const 0304 { 0305 } 0306 0307 bool ExpandingDelegate::editorEvent(QEvent* event, QAbstractItemModel* /*model*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& index) 0308 { 0309 if (event->type() == QEvent::MouseButtonRelease) { 0310 const QModelIndex sourceIndex = model()->mapToSource(index); 0311 event->accept(); 0312 model()->setExpanded(sourceIndex, !model()->isExpanded(sourceIndex)); 0313 heightChanged(); 0314 0315 return true; 0316 } else { 0317 event->ignore(); 0318 } 0319 0320 return false; 0321 } 0322 0323 QVector<QTextLayout::FormatRange> ExpandingDelegate::highlightingFromVariantList(const QList<QVariant>& customHighlights) const 0324 { 0325 QVector<QTextLayout::FormatRange> ret; 0326 0327 for (int i = 0; i + 2 < customHighlights.count(); i += 3) { 0328 if (!customHighlights[i].canConvert(QVariant::Int) || !customHighlights[i + 1].canConvert(QVariant::Int) || !customHighlights[i + 2].canConvert<QTextFormat>()) { 0329 qCWarning(PLUGIN_QUICKOPEN) << "Unable to convert triple to custom formatting."; 0330 continue; 0331 } 0332 0333 QTextLayout::FormatRange format; 0334 format.start = customHighlights[i].toInt(); 0335 format.length = customHighlights[i + 1].toInt(); 0336 format.format = customHighlights[i + 2].value<QTextFormat>().toCharFormat(); 0337 0338 if (!format.format.isValid()) { 0339 qCWarning(PLUGIN_QUICKOPEN) << "Format is not valid"; 0340 } 0341 0342 ret << format; 0343 } 0344 0345 return ret; 0346 } 0347 0348 #include "moc_expandingdelegate.cpp"