Files
renderdoc/qrenderdoc/Widgets/Extended/RDHeaderView.cpp
T
baldurk 43fa3cd94b Use BufferViewer for viewing constant buffers
* Unifying these views means that constant buffers have all the same
  reformatting and it avoids having multiple paths for what is now effectively
  the same control (a buffer can either have fixed data, repeating data, or
  both)
2022-05-20 14:15:30 +01:00

989 lines
27 KiB
C++

/******************************************************************************
* 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 <QAbstractScrollArea>
#include <QLabel>
#include <QMouseEvent>
#include <QPainter>
#include <QPixmap>
#include <QPointer>
#include <QScrollBar>
#include <QTreeView>
#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<QTreeView *>(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<int> &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<QSize>())
sz = sz.expandedTo(v.value<QSize>());
}
int oldSize = m_sections[col].size;
m_sections[col].size = sz.width();
emit sectionResized(col, oldSize, sz.width());
}
}
void RDHeaderView::resizeSections(const QList<int> &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<QAbstractItemView *>(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<int> 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<float> 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<int> &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<RDHeaderView> 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::ResizeType, int> 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<QString>())
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 &current, 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();
}