/****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2019-2022 Baldur Karlsson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ #include "RDHeaderView.h" #include #include #include #include #include #include #include #include #include "Code/QRDUtils.h" ///////////////////////////////////////////////////////////////////////////////// // // this file contains a few hardcoded assumptions for my use case, especially // with the 'custom sizing' mode that allows merging sections and pinning sections // and so on. // // * No handling for moving/rearranging/hiding sections with the custom sizing // mode. Just needs more careful handling and distinguishing between logical // and visual indices. // * Probably a few places vertical orientation isn't handled right, but that // shouldn't be too bad. // ///////////////////////////////////////////////////////////////////////////////// RDHeaderView::RDHeaderView(Qt::Orientation orient, QWidget *parent) : QHeaderView(orient, parent) { m_sectionPreview = new QLabel(this); setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter); QTreeView *treeView = qobject_cast(parent); if(treeView) { QObject::connect(treeView, &QTreeView::expanded, [this](const QModelIndex &) { rowsChanged(QModelIndex(), 0, 0); }); QObject::connect(treeView, &QTreeView::collapsed, [this](const QModelIndex &) { rowsChanged(QModelIndex(), 0, 0); }); } } RDHeaderView::~RDHeaderView() { } QSize RDHeaderView::sizeHint() const { if(!m_customSizing) return QHeaderView::sizeHint(); return m_sizeHint; } void RDHeaderView::setModel(QAbstractItemModel *model) { QAbstractItemModel *m = this->model(); if(m) { QObject::disconnect(m, &QAbstractItemModel::headerDataChanged, this, &RDHeaderView::headerDataChanged); QObject::disconnect(m, &QAbstractItemModel::columnsInserted, this, &RDHeaderView::columnsInserted); QObject::disconnect(m, &QAbstractItemModel::rowsInserted, this, &RDHeaderView::rowsChanged); QObject::disconnect(m, &QAbstractItemModel::rowsRemoved, this, &RDHeaderView::rowsChanged); } QHeaderView::setModel(model); if(model) { QObject::connect(model, &QAbstractItemModel::headerDataChanged, this, &RDHeaderView::headerDataChanged); QObject::connect(model, &QAbstractItemModel::columnsInserted, this, &RDHeaderView::columnsInserted); QObject::connect(model, &QAbstractItemModel::rowsInserted, this, &RDHeaderView::rowsChanged); QObject::connect(model, &QAbstractItemModel::rowsRemoved, this, &RDHeaderView::rowsChanged); QObject::connect(model, &QAbstractItemModel::dataChanged, [this](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { if(roles.contains(Qt::DisplayRole)) rowsChanged(QModelIndex(), 0, 0); }); } } void RDHeaderView::reset() { if(m_customSizing) cacheSections(); QHeaderView::reset(); } void RDHeaderView::cacheSections() { if(m_suppressSectionCache) return; QAbstractItemModel *m = this->model(); if(!m) return; int oldCount = m_sections.count(); m_sections.resize(m->columnCount()); // give new sections a default minimum size for(int col = oldCount; col < m_sections.count(); col++) m_sections[col].size = 10; for(int col = 0; col < m_sections.count(); col++) { if(m_columnGroupRole > 0) { QVariant v = m->data(m->index(0, col), m_columnGroupRole); if(v.isValid()) m_sections[col].group = v.toInt(); else m_sections[col].group = -m_columnGroupRole - col; if(col > 0) { m_sections[col - 1].groupGap = (m_sections[col].group != m_sections[col - 1].group) && m_sections[col].group >= 0; } } else { m_sections[col].group = col; m_sections[col].groupGap = true; } } int accum = 0; for(int col = 0; col < m_sections.count(); col++) { if(col == m_pinnedColumns) m_pinnedWidth = accum; m_sections[col].offset = accum; accum += m_sections[col].size; if(hasGroupGap(col)) accum += groupGapSize(); } if(m_pinnedColumns >= m_sections.count()) m_pinnedWidth = m_pinnedColumns; QHeaderView::initializeSections(); QStyleOptionHeader opt; initStyleOption(&opt); QFont f = font(); f.setBold(true); opt.section = 0; opt.fontMetrics = QFontMetrics(f); opt.text = m->headerData(0, orientation(), Qt::DisplayRole).toString(); m_sizeHint = style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), this); m_sizeHint.setWidth(accum); viewport()->update(viewport()->rect()); } int RDHeaderView::sectionSize(int logicalIndex) const { if(m_customSizing) { if(logicalIndex < 0 || logicalIndex >= m_sections.count()) return 0; return m_sections[logicalIndex].size; } return QHeaderView::sectionSize(logicalIndex); } int RDHeaderView::sectionViewportPosition(int logicalIndex) const { if(m_customSizing) { if(logicalIndex < 0 || logicalIndex >= m_sections.count()) return -1; int offs = m_sections[logicalIndex].offset; if(logicalIndex >= m_pinnedColumns) offs -= offset(); return offs; } return QHeaderView::sectionViewportPosition(logicalIndex); } int RDHeaderView::visualIndexAt(int position) const { if(m_customSizing) { if(m_sections.isEmpty()) return -1; if(position >= m_pinnedWidth) position += offset(); SectionData search; search.offset = position; auto it = std::lower_bound( m_sections.begin(), m_sections.end(), search, [](const SectionData &a, const SectionData &b) { return a.offset <= b.offset; }); if(it != m_sections.begin()) --it; if(it->offset <= position && position < (it->offset + it->size + (it->groupGap ? groupGapSize() : 0))) return (it - m_sections.begin()); return -1; } return QHeaderView::visualIndexAt(position); } int RDHeaderView::logicalIndexAt(int position) const { if(m_customSizing) return visualIndexAt(position); return QHeaderView::logicalIndexAt(position); } int RDHeaderView::count() const { if(m_customSizing) return m_sections.count(); return QHeaderView::count(); } void RDHeaderView::resizeSection(int logicalIndex, int size) { if(!m_customSizing) return QHeaderView::resizeSection(logicalIndex, size); if(logicalIndex >= 0 && logicalIndex < m_sections.count()) { int oldSize = m_sections[logicalIndex].size; m_sections[logicalIndex].size = size; emit sectionResized(logicalIndex, oldSize, size); } cacheSections(); } void RDHeaderView::resizeSections(QHeaderView::ResizeMode mode) { if(!m_sectionStretchHints.isEmpty()) { resizeSectionsWithHints(); return; } if(!m_customSizing) return QHeaderView::resizeSections(mode); if(mode != ResizeToContents) return; QAbstractItemModel *m = this->model(); int rowCount = m->rowCount(); for(int col = 0; col < m_sections.count(); col++) { QSize sz; for(int row = 0; row < rowCount; row++) { QVariant v = m->data(m->index(row, col), Qt::SizeHintRole); if(v.isValid() && v.canConvert()) sz = sz.expandedTo(v.value()); } int oldSize = m_sections[col].size; m_sections[col].size = sz.width(); emit sectionResized(col, oldSize, sz.width()); } } void RDHeaderView::resizeSections(const QList &sizes) { if(!m_customSizing) { for(int i = 0; i < qMin(sizes.count(), QHeaderView::count()); i++) { QHeaderView::resizeSection(i, sizes[i]); } return; } for(int i = 0; i < qMin(sizes.count(), m_sections.count()); i++) { int oldSize = m_sections[i].size; m_sections[i].size = sizes[i]; emit sectionResized(i, oldSize, sizes[i]); } cacheSections(); } bool RDHeaderView::hasGroupGap(int columnIndex) const { if(columnIndex >= 0 && columnIndex < m_sections.count()) return m_sections[columnIndex].groupGap; return false; } bool RDHeaderView::hasGroupTitle(int columnIndex) const { if(columnIndex == m_sections.count() - 1) return true; if(columnIndex >= 0 && columnIndex < m_sections.count()) return m_sections[columnIndex].groupGap || m_sections[columnIndex].group < 0; return false; } void RDHeaderView::cacheSectionMinSizes() { m_sectionMinSizes.resize(count()); for(int i = 0; i < m_sectionMinSizes.count(); i++) { int sz = 0; // see if we can fetch the column/row size hint from the item view QAbstractItemView *view = qobject_cast(parent()); if(view) { if(orientation() == Qt::Horizontal) sz = view->sizeHintForColumn(i); else sz = view->sizeHintForRow(i); } // also include the size for the header as another minimum if(orientation() == Qt::Horizontal) sz = qMax(sz, sectionSizeFromContents(i).width()); else sz = qMax(sz, sectionSizeFromContents(i).height()); // finally respect the minimum section size specified sz = qMax(sz, minimumSectionSize()); // update the minimum size for this section and count the total which we'll need m_sectionMinSizes[i] = sz; } } void RDHeaderView::resizeSectionsWithHints() { if(m_sectionMinSizes.isEmpty() || m_sectionStretchHints.isEmpty()) return; QVector sizes = m_sectionMinSizes; int minsizesTotal = 0; int stretchHintTotal = 0; for(int i = 0; i < count() && i < sizes.count() && i < m_sectionStretchHints.count(); i++) { if(isSectionHidden(i)) sizes[i] = 0; else if(m_sectionStretchHints[i] >= 0) stretchHintTotal += m_sectionStretchHints[i]; minsizesTotal += sizes[i]; } if(stretchHintTotal == 0) return; int available = 0; if(orientation() == Qt::Horizontal) available = rect().width(); else available = rect().height(); // see if we even have any extra space to allocate if(available > minsizesTotal) { // this is how much space we can allocate to stretch sections available -= minsizesTotal; // distribute the available space between the sections. Dividing by the total stretch tells us // how many 'whole' multiples we can allocate: int wholeMultiples = available / stretchHintTotal; if(wholeMultiples > 0) { for(int i = 0; i < sizes.count() && i < m_sectionStretchHints.count(); i++) { int hint = m_sectionStretchHints[i]; if(hint > 0 && !isSectionHidden(i)) sizes[i] += wholeMultiples * hint; } } available -= wholeMultiples * stretchHintTotal; // we now have a small amount (less than stretchHintTotal) of extra space to allocate. // we still want to assign this leftover proportional to the hints, otherwise we'd end up with a // stair-stepping effect. // To do this we calculate hint/total for each section then loop around adding on fractional // components to the sizes until one is above 1, then it gets a pixel, and we keep going until // all the remainder is allocated QVector fractions, increment; fractions.resize(sizes.count()); increment.resize(sizes.count()); // set up increments for(int i = 0; i < sizes.count(); i++) { // don't assign any space to sections with negative hints, or sections without hints, or // hidden sections if(i >= m_sectionStretchHints.count() || m_sectionStretchHints[i] <= 0 || isSectionHidden(i)) { increment[i] = 0.0f; continue; } increment[i] = float(m_sectionStretchHints[i]) / float(stretchHintTotal); } while(available > 0) { // loop along each section incrementing it. for(int i = 0; i < fractions.count(); i++) { fractions[i] += increment[i]; // if we have a whole pixel now, assign it if(fractions[i] > 1.0f) { fractions[i] -= 1.0f; sizes[i]++; available--; // if we've assigned all pixels, stop if(available == 0) break; } } } } resizeSections(sizes.toList()); } void RDHeaderView::setColumnStretchHints(const QList &hints) { if(hints.count() != count()) qCritical() << "Got" << hints.count() << "hints, but have" << count() << "columns"; m_sectionStretchHints = hints; // we take control of the sizing, we don't currently support custom resizing AND stretchy size // hints. QHeaderView::setSectionResizeMode(QHeaderView::Fixed); cacheSectionMinSizes(); resizeSectionsWithHints(); } void RDHeaderView::setPinnedColumns(int numColumns, QAbstractScrollArea *scroll) { m_pinnedColumns = numColumns; QObject::connect(scroll->horizontalScrollBar(), &QScrollBar::valueChanged, [this]() { viewport()->update(); }); } void RDHeaderView::setRootIndex(const QModelIndex &index) { QHeaderView::setRootIndex(index); // need to enqueue this after the root index is actually processed (this function is called // *before* the root index changes). if(!m_sectionStretchHints.isEmpty()) { QPointer ptr = this; GUIInvoke::defer(this, [this]() { cacheSectionMinSizes(); resizeSectionsWithHints(); }); } } void RDHeaderView::headerDataChanged(Qt::Orientation orientation, int logicalFirst, int logicalLast) { if(m_customSizing) cacheSections(); } void RDHeaderView::columnsInserted(const QModelIndex &parent, int first, int last) { if(m_customSizing) cacheSections(); } void RDHeaderView::rowsChanged(const QModelIndex &, int, int) { if(!m_sectionStretchHints.isEmpty()) { cacheSectionMinSizes(); resizeSectionsWithHints(); } } void RDHeaderView::mousePressEvent(QMouseEvent *event) { int mousePos = event->x(); int idx = logicalIndexAt(mousePos); if(sectionsMovable() && idx >= 0 && event->buttons() == Qt::LeftButton) { int secSize = sectionSize(idx); int secPos = sectionViewportPosition(idx); int handleWidth = style()->pixelMetric(QStyle::PM_HeaderGripMargin, 0, this); if(secPos >= 0 && secSize > 0 && mousePos >= secPos + handleWidth && mousePos <= secPos + secSize - handleWidth) { m_movingSection = idx; m_sectionPreview->resize(secSize, height()); QPixmap preview(m_sectionPreview->size()); preview.fill(QColor::fromRgba(qRgba(0, 0, 0, 100))); QPainter painter(&preview); painter.setOpacity(0.75f); paintSection(&painter, QRect(QPoint(0, 0), m_sectionPreview->size()), idx); painter.end(); m_cursorPos = QCursor::pos().x(); m_sectionPreview->setPixmap(preview); m_sectionPreviewOffset = mousePos - secPos; m_sectionPreview->move(mousePos - m_sectionPreviewOffset, 0); m_sectionPreview->show(); QHeaderView::mousePressEvent(event); return; } } if(m_customSizing) { m_resizeState = checkResizing(event); m_cursorPos = QCursor::pos().x(); return QAbstractItemView::mousePressEvent(event); } QHeaderView::mousePressEvent(event); } void RDHeaderView::mouseMoveEvent(QMouseEvent *event) { if(m_movingSection >= 0) { m_sectionPreview->move(event->x() - m_sectionPreviewOffset, 0); return; } if(m_customSizing) { if(m_resizeState.first == NoResize || m_resizeState.second < 0 || m_resizeState.second >= m_sections.count()) { auto res = checkResizing(event); bool hasCursor = testAttribute(Qt::WA_SetCursor); if(res.first != NoResize) { if(!hasCursor) setCursor(Qt::SplitHCursor); } else if(hasCursor) { unsetCursor(); } } else { int curX = QCursor::pos().x(); int delta = curX - m_cursorPos; int idx = m_resizeState.second; if(m_resizeState.first == LeftResize && idx > 0) idx--; // batch the cache update m_suppressSectionCache = true; int firstCol = idx; int lastCol = idx; // idx is the last in a group, so search backwards to see if there are neighbour sections we // should share the resize with while(firstCol > 0 && m_sections[firstCol - 1].group == m_sections[lastCol].group) firstCol--; // how much space could we lose on the columns, in total int freeSpace = 0; for(int col = firstCol; col <= lastCol; col++) freeSpace += m_sections[col].size - minimumSectionSize(); int numCols = lastCol - firstCol + 1; // spread the delta amonst the colummns int perSectionDelta = delta / numCols; // call resizeSection to emit the sectionResized signal but we set m_suppressSectionCache so // we won't cache sections. for(int col = firstCol; col <= lastCol; col++) resizeSection(col, qMax(minimumSectionSize(), m_sections[col].size + perSectionDelta)); // if there was an uneven spread, a few pixels will remain int remainder = delta - perSectionDelta * numCols; // loop around for the remainder pixels, assigning them one by one to the smallest/largest // column. // this is inefficient but remainder is very small - at most 3. int step = remainder < 0 ? -1 : 1; for(int i = 0; i < qAbs(remainder); i++) { int chosenCol = firstCol; for(int col = firstCol; col <= lastCol; col++) { if(step > 0 && m_sections[col].size < m_sections[chosenCol].size) chosenCol = col; else if(step < 0 && m_sections[col].size > m_sections[chosenCol].size) chosenCol = col; } resizeSection(chosenCol, qMax(minimumSectionSize(), m_sections[chosenCol].size + step)); } // only updating the cursor when the section is moving means that it becomes 'sticky'. If we // try to size down below the minimum size and keep going then it doesn't start resizing up // until it passes the divider again. int appliedDelta = delta; // if we were resizing down, at best we removed the remaining free space if(delta < 0) appliedDelta = qMax(delta, -freeSpace); m_cursorPos += appliedDelta; m_suppressSectionCache = false; cacheSections(); } return QAbstractItemView::mouseMoveEvent(event); } QHeaderView::mouseMoveEvent(event); } QPair RDHeaderView::checkResizing(QMouseEvent *event) { int mousePos = event->x(); int idx = logicalIndexAt(mousePos); bool hasCursor = testAttribute(Qt::WA_SetCursor); bool cursorSet = false; bool leftResize = idx > 0 && (m_sections[idx - 1].group != m_sections[idx].group); bool rightResize = idx >= 0 && hasGroupTitle(idx); if(leftResize || rightResize) { int secSize = sectionSize(idx); int secPos = sectionViewportPosition(idx); int handleWidth = style()->pixelMetric(QStyle::PM_HeaderGripMargin, 0, this); int gapWidth = 0; if(hasGroupGap(idx)) gapWidth = groupGapSize(); if(leftResize && secPos >= 0 && secSize > 0 && mousePos < secPos + handleWidth) { return qMakePair(LeftResize, idx); } if(rightResize && secPos >= 0 && secSize > 0 && mousePos > secPos + secSize - handleWidth - gapWidth) { return qMakePair(RightResize, idx); } } return qMakePair(NoResize, -1); } void RDHeaderView::mouseReleaseEvent(QMouseEvent *event) { if(m_movingSection >= 0) { int mousePos = event->x(); int idx = logicalIndexAt(mousePos); m_sectionPreview->hide(); if(idx >= 0) { int secSize = sectionSize(idx); int secPos = sectionPosition(idx); int srcSection = visualIndex(m_movingSection); int dstSection = visualIndex(idx); if(srcSection >= 0 && dstSection >= 0 && srcSection != dstSection) { // the half-way point of the section decides whether we're dropping to the left // or the right of it. if(mousePos < secPos + secSize / 2) { // if we're moving from the left, place it to the left of dstSection if(srcSection < dstSection) moveSection(srcSection, dstSection - 1); else moveSection(srcSection, dstSection); } else { // if we're moving it from the right, place it to the right of dstSection if(srcSection > dstSection) moveSection(srcSection, dstSection + 1); else moveSection(srcSection, dstSection); } return; } } } m_movingSection = -1; if(m_customSizing) m_resizeState = qMakePair(NoResize, -1); QHeaderView::mouseReleaseEvent(event); } void RDHeaderView::paintEvent(QPaintEvent *e) { if(!m_customSizing) return QHeaderView::paintEvent(e); if(count() == 0) return; QPainter painter(viewport()); int start = qMax(visualIndexAt(e->rect().left()), 0); int end = visualIndexAt(e->rect().right()); if(end == -1) end = count() - 1; // make sure we always paint the whole header for any merged headers while(start > 0 && !hasGroupTitle(start - 1)) start--; while(end < m_sections.count() && !hasGroupTitle(end)) end++; QRect accumRect; for(int i = start; i <= end; ++i) { int pos = sectionViewportPosition(i); int size = sectionSize(i); if(!hasGroupGap(i) && pos < 0) { size += pos; pos = 0; } // either set or accumulate this section's rect if(accumRect.isEmpty()) accumRect.setRect(pos, 0, size, viewport()->height()); else accumRect.setWidth(accumRect.width() + size); if(hasGroupTitle(i)) { painter.save(); if(accumRect.left() < m_pinnedWidth && i >= m_pinnedColumns) accumRect.setLeft(m_pinnedWidth); paintSection(&painter, accumRect, i); painter.restore(); // if we have more sections to go, reset so we can accumulate the next group if(i < end) accumRect = QRect(); } } // clear the remainder of the header if there's a gap if(accumRect.right() < e->rect().right()) { QStyleOption opt; opt.init(this); opt.state |= QStyle::State_Horizontal; opt.rect = QRect(accumRect.right() + 1, 0, e->rect().right() - accumRect.right(), viewport()->height()); style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this); } } void RDHeaderView::paintSection(QPainter *painter, const QRect &rect, int section) const { if(!m_customSizing) return QHeaderView::paintSection(painter, rect, section); if(!rect.isValid()) return; QStyleOptionHeader opt; initStyleOption(&opt); QAbstractItemModel *m = this->model(); if(hasFocus()) opt.state |= (QStyle::State_Active | QStyle::State_HasFocus); else opt.state &= ~(QStyle::State_Active | QStyle::State_HasFocus); opt.rect = rect; opt.section = section; opt.textAlignment = defaultAlignment(); opt.iconAlignment = Qt::AlignVCenter; QVariant variant; if(m_columnGroupRole) { variant = m->headerData(section, orientation(), m_columnGroupRole); if(variant.isValid() && variant.canConvert()) opt.text = variant.toString(); } if(opt.text.isEmpty()) opt.text = m->headerData(section, orientation(), Qt::DisplayRole).toString(); int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this); if(textElideMode() != Qt::ElideNone) opt.text = opt.fontMetrics.elidedText(opt.text, textElideMode(), rect.width() - margin); if(section == 0 && section == m_sections.count() - 1) opt.position = QStyleOptionHeader::OnlyOneSection; else if(section == 0) opt.position = QStyleOptionHeader::Beginning; else if(section == m_sections.count() - 1) opt.position = QStyleOptionHeader::End; else opt.position = QStyleOptionHeader::Middle; opt.orientation = orientation(); bool prevSel = section > 0 && selectionModel()->isColumnSelected(section - 1, QModelIndex()); bool nextSel = section + 1 < m_sections.count() && selectionModel()->isColumnSelected(section + 1, QModelIndex()); if(prevSel && nextSel) opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected; else if(prevSel) opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected; else if(nextSel) opt.selectedPosition = QStyleOptionHeader::NextIsSelected; else opt.selectedPosition = QStyleOptionHeader::NotAdjacent; style()->drawControl(QStyle::CE_Header, &opt, painter, this); } void RDHeaderView::currentChanged(const QModelIndex ¤t, const QModelIndex &old) { if(!m_customSizing) return QHeaderView::currentChanged(current, old); // not optimal at all if(current != old) { QRect r = viewport()->rect(); if(old.isValid()) { QRect rect = r; if(orientation() == Qt::Horizontal) { rect.setLeft(sectionViewportPosition(old.column())); rect.setWidth(sectionSize(old.column())); } else { rect.setTop(sectionViewportPosition(old.column())); rect.setHeight(sectionSize(old.column())); } viewport()->update(rect); } if(current.isValid()) { QRect rect = r; if(orientation() == Qt::Horizontal) { rect.setLeft(sectionViewportPosition(current.column())); rect.setWidth(sectionSize(current.column())); } else { rect.setTop(sectionViewportPosition(current.column())); rect.setHeight(sectionSize(current.column())); } viewport()->update(rect); } } } void RDHeaderView::updateGeometries() { if(!m_sectionStretchHints.isEmpty()) resizeSectionsWithHints(); QHeaderView::updateGeometries(); }