mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-04 17:10:47 +00:00
Add custom painting and section handling to RDTableView & RDHeaderView
* This is used primarily for the buffer/mesh viewer to be able to pin the index/element column to the left side, group columns together with a noticeable separator, and other minor tweaks. * Unfortunately due to tight private coupling and lack of virtual functions in the QTableView and QHeaderView, a few unrelated functions have to be re-implemented to point to our own header.
This commit is contained in:
@@ -28,6 +28,20 @@
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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);
|
||||
@@ -37,12 +51,285 @@ 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);
|
||||
}
|
||||
|
||||
QHeaderView::setModel(model);
|
||||
|
||||
QObject::connect(model, &QAbstractItemModel::headerDataChanged, this,
|
||||
&RDHeaderView::headerDataChanged);
|
||||
QObject::connect(model, &QAbstractItemModel::columnsInserted, this, &RDHeaderView::columnsInserted);
|
||||
}
|
||||
|
||||
void RDHeaderView::reset()
|
||||
{
|
||||
if(m_customSizing)
|
||||
cacheSections();
|
||||
}
|
||||
|
||||
void RDHeaderView::cacheSections()
|
||||
{
|
||||
if(m_suppressSectionCache)
|
||||
return;
|
||||
|
||||
QAbstractItemModel *m = this->model();
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
|
||||
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,
|
||||
[this](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
|
||||
{
|
||||
return visualIndexAt(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_customSizing)
|
||||
return 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]);
|
||||
}
|
||||
}
|
||||
|
||||
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::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::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
int mousePos = event->x();
|
||||
int idx = logicalIndexAt(mousePos);
|
||||
|
||||
if(idx >= 0 && event->buttons() == Qt::LeftButton)
|
||||
if(sectionsMovable() && idx >= 0 && event->buttons() == Qt::LeftButton)
|
||||
{
|
||||
int secSize = sectionSize(idx);
|
||||
int secPos = sectionViewportPosition(idx);
|
||||
@@ -75,6 +362,14 @@ void RDHeaderView::mousePressEvent(QMouseEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
if(m_customSizing)
|
||||
{
|
||||
m_resizeState = checkResizing(event);
|
||||
m_cursorPos = QCursor::pos().x();
|
||||
|
||||
return QAbstractItemView::mousePressEvent(event);
|
||||
}
|
||||
|
||||
QHeaderView::mousePressEvent(event);
|
||||
}
|
||||
|
||||
@@ -86,9 +381,140 @@ void RDHeaderView::mouseMoveEvent(QMouseEvent *event)
|
||||
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)
|
||||
@@ -132,5 +558,197 @@ void RDHeaderView::mouseReleaseEvent(QMouseEvent *event)
|
||||
|
||||
m_movingSection = -1;
|
||||
|
||||
if(m_customSizing)
|
||||
{
|
||||
m_resizeState = qMakePair(NoResize, -1);
|
||||
|
||||
return QAbstractItemView::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
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();
|
||||
accumRect.setWidth(accumRect.width() - 1);
|
||||
|
||||
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);
|
||||
|
||||
QVariant textAlignment = m->headerData(section, orientation(), Qt::TextAlignmentRole);
|
||||
opt.rect = rect;
|
||||
opt.section = section;
|
||||
opt.textAlignment = Qt::AlignLeft | Qt::AlignVCenter;
|
||||
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 ¤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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,12 +36,88 @@ public:
|
||||
explicit RDHeaderView(Qt::Orientation orient, QWidget *parent = 0);
|
||||
~RDHeaderView();
|
||||
|
||||
int groupGapSize() const { return 6; }
|
||||
QSize sizeHint() const override;
|
||||
void setModel(QAbstractItemModel *model) override;
|
||||
void reset() override;
|
||||
|
||||
// these aren't virtual so we can't override them properly, but it's convenient for internal use
|
||||
// and any external calls that go to this type directly to use the correct version
|
||||
int sectionSize(int logicalIndex) const;
|
||||
int sectionViewportPosition(int logicalIndex) const;
|
||||
int visualIndexAt(int position) const;
|
||||
int logicalIndexAt(int position) const;
|
||||
int count() const;
|
||||
void resizeSection(int logicalIndex, int size);
|
||||
void resizeSections(const QList<int> &sizes);
|
||||
void resizeSections(QHeaderView::ResizeMode mode);
|
||||
|
||||
inline int logicalIndexAt(int x, int y) const;
|
||||
inline int logicalIndexAt(const QPoint &pos) const;
|
||||
|
||||
bool hasGroupGap(int columnIndex) const;
|
||||
bool hasGroupTitle(int columnIndex) const;
|
||||
|
||||
void setColumnGroupRole(int role) { m_columnGroupRole = role; }
|
||||
int columnGroupRole() const { return m_columnGroupRole; }
|
||||
void setPinnedColumns(int numColumns) { m_pinnedColumns = numColumns; }
|
||||
int pinnedColumns() const { return m_pinnedColumns; }
|
||||
void setCustomSizing(bool sizing) { m_customSizing = sizing; }
|
||||
int pinnedWidth() { return m_pinnedWidth; }
|
||||
public slots:
|
||||
void headerDataChanged(Qt::Orientation orientation, int logicalFirst, int logicalLast);
|
||||
void columnsInserted(const QModelIndex &parent, int first, int last);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
void paintSection(QPainter *painter, const QRect &rect, int section) const override;
|
||||
void currentChanged(const QModelIndex ¤t, const QModelIndex &old) override;
|
||||
|
||||
enum ResizeType
|
||||
{
|
||||
NoResize,
|
||||
LeftResize,
|
||||
RightResize
|
||||
};
|
||||
|
||||
QPair<ResizeType, int> checkResizing(QMouseEvent *event);
|
||||
QPair<ResizeType, int> m_resizeState;
|
||||
int m_cursorPos;
|
||||
|
||||
void cacheSections();
|
||||
|
||||
struct SectionData
|
||||
{
|
||||
int offset = 0;
|
||||
int size = 0;
|
||||
int group = 0;
|
||||
bool groupGap = false;
|
||||
};
|
||||
|
||||
QSize m_sizeHint;
|
||||
QVector<SectionData> m_sections;
|
||||
int m_pinnedWidth = 0;
|
||||
|
||||
bool m_suppressSectionCache = false;
|
||||
bool m_customSizing = false;
|
||||
|
||||
int m_columnGroupRole = 0;
|
||||
int m_pinnedColumns = 0;
|
||||
|
||||
int m_movingSection = -1;
|
||||
QLabel *m_sectionPreview;
|
||||
int m_sectionPreviewOffset = 0;
|
||||
};
|
||||
|
||||
inline int RDHeaderView::logicalIndexAt(int ax, int ay) const
|
||||
{
|
||||
return orientation() == Qt::Horizontal ? logicalIndexAt(ax) : logicalIndexAt(ay);
|
||||
}
|
||||
inline int RDHeaderView::logicalIndexAt(const QPoint &apos) const
|
||||
{
|
||||
return logicalIndexAt(apos.x(), apos.y());
|
||||
}
|
||||
@@ -23,8 +23,397 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include "RDTableView.h"
|
||||
#include <QAbstractButton>
|
||||
#include <QHeaderView>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
#include <QScrollBar>
|
||||
#include "RDHeaderView.h"
|
||||
|
||||
RDTableView::RDTableView(QWidget *parent) : QTableView(parent)
|
||||
{
|
||||
m_horizontalHeader = new RDHeaderView(Qt::Horizontal, this);
|
||||
m_horizontalHeader->setCustomSizing(true);
|
||||
setHorizontalHeader(m_horizontalHeader);
|
||||
|
||||
QObject::connect(m_horizontalHeader, &QHeaderView::sectionResized,
|
||||
[this](int, int, int) { viewport()->update(); });
|
||||
}
|
||||
|
||||
int RDTableView::columnViewportPosition(int column) const
|
||||
{
|
||||
return horizontalHeader()->sectionViewportPosition(column);
|
||||
}
|
||||
|
||||
int RDTableView::columnAt(int x) const
|
||||
{
|
||||
return horizontalHeader()->visualIndexAt(x);
|
||||
}
|
||||
|
||||
int RDTableView::columnWidth(int column) const
|
||||
{
|
||||
return horizontalHeader()->sectionSize(column);
|
||||
}
|
||||
|
||||
void RDTableView::setColumnWidth(int column, int width)
|
||||
{
|
||||
horizontalHeader()->resizeSection(column, width);
|
||||
|
||||
updateGeometries();
|
||||
}
|
||||
|
||||
void RDTableView::setColumnWidths(const QList<int> &widths)
|
||||
{
|
||||
horizontalHeader()->resizeSections(widths);
|
||||
|
||||
updateGeometries();
|
||||
}
|
||||
|
||||
void RDTableView::resizeColumnsToContents()
|
||||
{
|
||||
horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
|
||||
|
||||
updateGeometries();
|
||||
}
|
||||
|
||||
QRect RDTableView::visualRect(const QModelIndex &index) const
|
||||
{
|
||||
if(!index.isValid())
|
||||
return QRect();
|
||||
|
||||
const int row = index.row();
|
||||
const int col = index.column();
|
||||
|
||||
const int gridWidth = showGrid() ? 1 : 0;
|
||||
|
||||
return QRect(columnViewportPosition(col), rowViewportPosition(row), columnWidth(col) - gridWidth,
|
||||
rowHeight(row) - gridWidth);
|
||||
}
|
||||
|
||||
QRegion RDTableView::visualRegionForSelection(const QItemSelection &selection) const
|
||||
{
|
||||
QRegion selectionRegion;
|
||||
const QRect viewRect = viewport()->rect();
|
||||
|
||||
QAbstractItemModel *m = model();
|
||||
|
||||
for(const QItemSelectionRange &selRange : selection)
|
||||
{
|
||||
for(int row = selRange.top(); row <= selRange.bottom(); row++)
|
||||
{
|
||||
for(int col = selRange.left(); col <= selRange.right(); col++)
|
||||
{
|
||||
const QRect &rangeRect = visualRect(m->index(row, col));
|
||||
if(viewRect.intersects(rangeRect))
|
||||
selectionRegion += rangeRect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selectionRegion;
|
||||
}
|
||||
|
||||
QModelIndex RDTableView::indexAt(const QPoint &p) const
|
||||
{
|
||||
int row = rowAt(p.y());
|
||||
int col = columnAt(p.x());
|
||||
|
||||
if(row < 0 || col < 0)
|
||||
return QModelIndex();
|
||||
|
||||
return model()->index(row, col);
|
||||
}
|
||||
|
||||
void RDTableView::setColumnGroupRole(int role)
|
||||
{
|
||||
m_columnGroupRole = role;
|
||||
m_horizontalHeader->setColumnGroupRole(role);
|
||||
}
|
||||
|
||||
void RDTableView::setPinnedColumns(int numColumns)
|
||||
{
|
||||
m_pinnedColumns = numColumns;
|
||||
m_horizontalHeader->setPinnedColumns(numColumns);
|
||||
}
|
||||
|
||||
void RDTableView::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
const int gridWidth = showGrid() ? 1 : 0;
|
||||
QStyleOptionViewItem opt = viewOptions();
|
||||
|
||||
QPainter painter(viewport());
|
||||
|
||||
if(model()->rowCount() == 0 || model()->columnCount() == 0)
|
||||
return;
|
||||
|
||||
int firstRow = qMax(verticalHeader()->visualIndexAt(0), 0);
|
||||
int lastRow = verticalHeader()->visualIndexAt(viewport()->height());
|
||||
if(lastRow < 0)
|
||||
lastRow = verticalHeader()->count() - 1;
|
||||
lastRow = qMin(lastRow, verticalHeader()->count() - 1);
|
||||
|
||||
int firstCol = qMax(horizontalHeader()->visualIndexAt(horizontalHeader()->pinnedWidth() + 1), 0);
|
||||
int lastCol = horizontalHeader()->visualIndexAt(viewport()->width());
|
||||
if(lastCol < 0)
|
||||
lastCol = horizontalHeader()->count() - 1;
|
||||
lastCol = qMin(lastCol, horizontalHeader()->count() - 1);
|
||||
|
||||
firstCol = qMax(m_pinnedColumns, firstCol);
|
||||
|
||||
for(int row = firstRow; row <= lastRow; row++)
|
||||
{
|
||||
for(int col = firstCol; col <= lastCol; col++)
|
||||
{
|
||||
const QModelIndex index = model()->index(row, col);
|
||||
if(index.isValid())
|
||||
paintCell(&painter, index, opt);
|
||||
}
|
||||
for(int col = 0; col < m_pinnedColumns; col++)
|
||||
{
|
||||
const QModelIndex index = model()->index(row, col);
|
||||
if(index.isValid())
|
||||
paintCell(&painter, index, opt);
|
||||
}
|
||||
}
|
||||
|
||||
if(gridWidth)
|
||||
{
|
||||
QPen prevPen = painter.pen();
|
||||
QBrush prevBrush = painter.brush();
|
||||
|
||||
QColor gridCol(QRgb(style()->styleHint(QStyle::SH_Table_GridLineColor, &opt, this)));
|
||||
|
||||
painter.setPen(QPen(gridCol, 0, gridStyle()));
|
||||
painter.setBrush(QBrush(gridCol));
|
||||
|
||||
// draw bottom line of each row
|
||||
for(int row = firstRow; row <= lastRow; row++)
|
||||
{
|
||||
int y = rowViewportPosition(row) + rowHeight(row) - gridWidth;
|
||||
painter.drawLine(viewport()->rect().left(), y, viewport()->rect().right(), y);
|
||||
}
|
||||
|
||||
int gapSize = m_horizontalHeader->groupGapSize();
|
||||
|
||||
// draw lines for each column, and group gaps
|
||||
for(int col = firstCol; col <= lastCol; col++)
|
||||
{
|
||||
int x = columnViewportPosition(col) + columnWidth(col) - gridWidth;
|
||||
|
||||
if(m_horizontalHeader->hasGroupGap(col))
|
||||
painter.drawRect(x, viewport()->rect().top(), gapSize, viewport()->rect().height());
|
||||
else
|
||||
painter.drawLine(x, viewport()->rect().top(), x, viewport()->rect().bottom());
|
||||
}
|
||||
for(int col = 0; col < m_pinnedColumns; col++)
|
||||
{
|
||||
int x = columnViewportPosition(col) + columnWidth(col) - gridWidth;
|
||||
|
||||
if(m_horizontalHeader->hasGroupGap(col))
|
||||
painter.drawRect(x, viewport()->rect().top(), gapSize, viewport()->rect().height());
|
||||
else
|
||||
painter.drawLine(x, viewport()->rect().top(), x, viewport()->rect().bottom());
|
||||
}
|
||||
|
||||
painter.setPen(prevPen);
|
||||
painter.setBrush(prevBrush);
|
||||
}
|
||||
}
|
||||
|
||||
void RDTableView::paintCell(QPainter *painter, const QModelIndex &index,
|
||||
const QStyleOptionViewItem &opt)
|
||||
{
|
||||
QStyleOptionViewItem cellopt = opt;
|
||||
|
||||
cellopt.rect = QRect(columnViewportPosition(index.column()), rowViewportPosition(index.row()),
|
||||
columnWidth(index.column()), rowHeight(index.row()));
|
||||
|
||||
// erase the rect here since we need to draw over any overlapping non-pinned cells and
|
||||
// there's no way to just clip the above painting :(
|
||||
if(index.column() < m_pinnedColumns)
|
||||
painter->eraseRect(cellopt.rect);
|
||||
|
||||
if(selectionModel() && selectionModel()->isSelected(index))
|
||||
cellopt.state |= QStyle::State_Selected;
|
||||
|
||||
// draw the background, then the cell
|
||||
style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &cellopt, painter, this);
|
||||
itemDelegate(index)->paint(painter, cellopt, index);
|
||||
}
|
||||
|
||||
void RDTableView::scrollTo(const QModelIndex &index, ScrollHint hint)
|
||||
{
|
||||
if(!index.isValid())
|
||||
return;
|
||||
|
||||
QRect cellRect = QRect(columnViewportPosition(index.column()), rowViewportPosition(index.row()),
|
||||
columnWidth(index.column()), rowHeight(index.row()));
|
||||
|
||||
QRect dataRect = viewport()->rect();
|
||||
dataRect.setLeft(horizontalHeader()->pinnedWidth());
|
||||
|
||||
// if it's already visible then just bail, common case
|
||||
if(dataRect.contains(cellRect) && hint == QAbstractItemView::EnsureVisible)
|
||||
return;
|
||||
|
||||
// assume per-item vertical scrolling and per-pixel horizontal scrolling
|
||||
|
||||
// for any hint except position at center, we just ensure it's visible horizontally
|
||||
if(hint != QAbstractItemView::PositionAtCenter)
|
||||
{
|
||||
// scroll into view from the left
|
||||
if(dataRect.left() > cellRect.left())
|
||||
{
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() -
|
||||
(dataRect.left() - cellRect.left()));
|
||||
}
|
||||
|
||||
// scroll into view from the right
|
||||
if(dataRect.right() < cellRect.right())
|
||||
{
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() +
|
||||
(cellRect.right() - dataRect.right()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// center it horizontally from the left
|
||||
QPoint dataCenter = dataRect.center();
|
||||
QPoint cellCenter = cellRect.center();
|
||||
|
||||
if(dataCenter.x() > cellCenter.x())
|
||||
{
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() -
|
||||
(dataCenter.x() - cellCenter.x()));
|
||||
}
|
||||
|
||||
// center it horizontally from the right
|
||||
if(dataCenter.x() < cellCenter.x())
|
||||
{
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() +
|
||||
(cellCenter.x() - dataCenter.x()));
|
||||
}
|
||||
}
|
||||
|
||||
// collapse EnsureVisible to either PositionAtTop or PositionAtBottom depending on which side it's
|
||||
// on, or just return if we only had to make it visible horizontally
|
||||
if(hint == QAbstractItemView::EnsureVisible)
|
||||
{
|
||||
if(dataRect.bottom() < cellRect.bottom())
|
||||
hint = QAbstractItemView::PositionAtBottom;
|
||||
else if(dataRect.top() > cellRect.top())
|
||||
hint = QAbstractItemView::PositionAtTop;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
int firstRow = qMax(verticalHeader()->visualIndexAt(0), 0);
|
||||
int lastRow = verticalHeader()->visualIndexAt(viewport()->height());
|
||||
if(lastRow == -1)
|
||||
lastRow = verticalHeader()->count();
|
||||
|
||||
int visibleRows = lastRow - firstRow + 1;
|
||||
|
||||
// a partially displayed row doesn't count
|
||||
if(verticalHeader()->sectionViewportPosition(lastRow) + verticalHeader()->sectionSize(lastRow) >
|
||||
viewport()->height())
|
||||
visibleRows--;
|
||||
|
||||
if(hint == QAbstractItemView::PositionAtTop)
|
||||
{
|
||||
verticalScrollBar()->setValue(index.row());
|
||||
}
|
||||
else if(hint == QAbstractItemView::PositionAtBottom)
|
||||
{
|
||||
verticalScrollBar()->setValue(index.row() - visibleRows + 1);
|
||||
}
|
||||
else if(hint == QAbstractItemView::PositionAtCenter)
|
||||
{
|
||||
verticalScrollBar()->setValue(index.row() - (visibleRows + 1) / 2);
|
||||
}
|
||||
|
||||
update(index);
|
||||
}
|
||||
|
||||
void RDTableView::updateGeometries()
|
||||
{
|
||||
static bool recurse = false;
|
||||
if(recurse)
|
||||
return;
|
||||
recurse = true;
|
||||
|
||||
QAbstractButton *cornerButton = findChild<QAbstractButton *>();
|
||||
cornerButton->setVisible(false);
|
||||
|
||||
QRect geom = viewport()->geometry();
|
||||
|
||||
// assume no vertical header
|
||||
|
||||
int horizHeight =
|
||||
qBound(horizontalHeader()->minimumHeight(), horizontalHeader()->sizeHint().height(),
|
||||
horizontalHeader()->maximumHeight());
|
||||
|
||||
setViewportMargins(0, horizHeight, 0, 0);
|
||||
|
||||
horizontalHeader()->setGeometry(geom.left(), geom.top() - horizHeight, geom.width(), horizHeight);
|
||||
|
||||
// even though it's not visible we need to set the geometry right so that it looks up rows by
|
||||
// position properly.
|
||||
verticalHeader()->setGeometry(0, horizHeight, 0, geom.height());
|
||||
|
||||
// if the headers are hidden nothing else will update their geometries and some things like
|
||||
// scrolling etc depend on it being up to date, so hackily call the protected slot. Yuk!
|
||||
if(verticalHeader()->isHidden())
|
||||
QMetaObject::invokeMethod(verticalHeader(), "updateGeometries");
|
||||
if(horizontalHeader()->isHidden())
|
||||
QMetaObject::invokeMethod(horizontalHeader(), "updateGeometries");
|
||||
|
||||
// assume per-item vertical scrolling and per-pixel horizontal scrolling
|
||||
|
||||
// vertical scroll bar
|
||||
{
|
||||
int firstRow = qMax(verticalHeader()->visualIndexAt(0), 0);
|
||||
int lastRow = verticalHeader()->visualIndexAt(viewport()->height());
|
||||
bool last = false;
|
||||
if(lastRow == -1)
|
||||
{
|
||||
last = true;
|
||||
lastRow = verticalHeader()->count();
|
||||
}
|
||||
|
||||
int visibleRows = lastRow - firstRow + 1;
|
||||
|
||||
// a partially displayed row doesn't count
|
||||
if(verticalHeader()->sectionViewportPosition(lastRow) + verticalHeader()->sectionSize(lastRow) >
|
||||
viewport()->height())
|
||||
visibleRows--;
|
||||
|
||||
verticalScrollBar()->setRange(0, verticalHeader()->count() - visibleRows);
|
||||
verticalScrollBar()->setSingleStep(1);
|
||||
verticalScrollBar()->setPageStep(visibleRows);
|
||||
if(visibleRows >= verticalHeader()->count())
|
||||
verticalHeader()->setOffset(0);
|
||||
else if(last)
|
||||
verticalHeader()->setOffsetToLastSection();
|
||||
}
|
||||
|
||||
// horizontal scroll bar
|
||||
{
|
||||
int totalWidth = horizontalHeader()->sizeHint().width();
|
||||
|
||||
horizontalScrollBar()->setPageStep(viewport()->width() - horizontalHeader()->pinnedWidth());
|
||||
horizontalScrollBar()->setRange(0, totalWidth - viewport()->width());
|
||||
horizontalScrollBar()->setSingleStep(qMax(totalWidth / (horizontalHeader()->count() + 1), 2));
|
||||
}
|
||||
|
||||
recurse = false;
|
||||
QAbstractItemView::updateGeometries();
|
||||
}
|
||||
|
||||
void RDTableView::scrollContentsBy(int dx, int dy)
|
||||
{
|
||||
QTableView::scrollContentsBy(dx, dy);
|
||||
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <QTableView>
|
||||
#include "RDHeaderView.h"
|
||||
|
||||
class RDTableView : public QTableView
|
||||
{
|
||||
@@ -32,5 +33,38 @@ class RDTableView : public QTableView
|
||||
public:
|
||||
explicit RDTableView(QWidget *parent = 0);
|
||||
|
||||
// these aren't virtual so we can't override them properly, but it's convenient for internal use
|
||||
// and any external calls that go to this type directly to use the correct version
|
||||
RDHeaderView *horizontalHeader() const { return m_horizontalHeader; }
|
||||
int columnViewportPosition(int column) const;
|
||||
int columnAt(int x) const;
|
||||
int columnWidth(int column) const;
|
||||
void setColumnWidth(int column, int width);
|
||||
void setColumnWidths(const QList<int> &widths);
|
||||
void resizeColumnsToContents();
|
||||
|
||||
// these ones we CAN override, so even though the implementation is identical to QTableView we
|
||||
// reimplement so it can pick up the above functions
|
||||
QRect visualRect(const QModelIndex &index) const override;
|
||||
QRegion visualRegionForSelection(const QItemSelection &selection) const override;
|
||||
QModelIndex indexAt(const QPoint &p) const override;
|
||||
void scrollTo(const QModelIndex &index, ScrollHint hint = QAbstractItemView::EnsureVisible) override;
|
||||
|
||||
void setColumnGroupRole(int role);
|
||||
int columnGroupRole() const { return m_columnGroupRole; }
|
||||
QStyleOptionViewItem viewOptions() const override { return QTableView::viewOptions(); }
|
||||
void setPinnedColumns(int numColumns);
|
||||
int pinnedColumns() const { return m_pinnedColumns; }
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void updateGeometries() override;
|
||||
void scrollContentsBy(int dx, int dy) override;
|
||||
|
||||
void paintCell(QPainter *painter, const QModelIndex &index, const QStyleOptionViewItem &opt);
|
||||
|
||||
private:
|
||||
int m_pinnedColumns = 0;
|
||||
int m_columnGroupRole = 0;
|
||||
|
||||
RDHeaderView *m_horizontalHeader;
|
||||
};
|
||||
|
||||
@@ -357,6 +357,8 @@ uint32_t CalcIndex(BufferData *data, uint32_t vertID, int32_t baseVertex)
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int columnGroupRole = Qt::UserRole + 10000;
|
||||
|
||||
class BufferItemModel : public QAbstractItemModel
|
||||
{
|
||||
public:
|
||||
@@ -396,26 +398,29 @@ public:
|
||||
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
|
||||
{
|
||||
if(section < m_ColumnCount && orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
||||
if(section < m_ColumnCount && orientation == Qt::Horizontal)
|
||||
{
|
||||
if(section == 0)
|
||||
if(role == Qt::DisplayRole || role == columnGroupRole)
|
||||
{
|
||||
return meshView ? lit("VTX") : lit("Element");
|
||||
}
|
||||
else if(section == 1 && meshView)
|
||||
{
|
||||
return lit("IDX");
|
||||
}
|
||||
else
|
||||
{
|
||||
const FormatElement &el = elementForColumn(section);
|
||||
if(section == 0)
|
||||
{
|
||||
return meshView ? lit("VTX") : lit("Element");
|
||||
}
|
||||
else if(section == 1 && meshView)
|
||||
{
|
||||
return lit("IDX");
|
||||
}
|
||||
else
|
||||
{
|
||||
const FormatElement &el = elementForColumn(section);
|
||||
|
||||
if(el.format.compCount == 1)
|
||||
return el.name;
|
||||
if(el.format.compCount == 1 || role == columnGroupRole)
|
||||
return el.name;
|
||||
|
||||
QChar comps[] = {QLatin1Char('x'), QLatin1Char('y'), QLatin1Char('z'), QLatin1Char('w')};
|
||||
QChar comps[] = {QLatin1Char('x'), QLatin1Char('y'), QLatin1Char('z'), QLatin1Char('w')};
|
||||
|
||||
return QFormatStr("%1.%2").arg(el.name).arg(comps[componentForIndex(section)]);
|
||||
return QFormatStr("%1.%2").arg(el.name).arg(comps[componentForIndex(section)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,6 +453,14 @@ public:
|
||||
uint32_t row = index.row();
|
||||
int col = index.column();
|
||||
|
||||
if(role == columnGroupRole)
|
||||
{
|
||||
if(col < reservedColumnCount())
|
||||
return -1 - col;
|
||||
else
|
||||
return columnLookup[col - reservedColumnCount()];
|
||||
}
|
||||
|
||||
if((role == Qt::BackgroundRole || role == Qt::ForegroundRole) && col >= reservedColumnCount())
|
||||
{
|
||||
if(meshView)
|
||||
@@ -1088,6 +1101,9 @@ void BufferViewer::SetupRawView()
|
||||
ui->dockarea->addToolWindow(ui->vsinData, ToolWindowManager::EmptySpace);
|
||||
ui->dockarea->setToolWindowProperties(ui->vsinData, ToolWindowManager::HideCloseButton);
|
||||
|
||||
ui->vsinData->setPinnedColumns(1);
|
||||
ui->vsinData->setColumnGroupRole(columnGroupRole);
|
||||
|
||||
ui->formatSpecifier->setWindowTitle(tr("Buffer Format"));
|
||||
ui->dockarea->addToolWindow(ui->formatSpecifier, ToolWindowManager::AreaReference(
|
||||
ToolWindowManager::BottomOf,
|
||||
@@ -1191,6 +1207,14 @@ void BufferViewer::SetupMeshView()
|
||||
ui->vsoutData->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
ui->gsoutData->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
ui->vsinData->setPinnedColumns(2);
|
||||
ui->vsoutData->setPinnedColumns(2);
|
||||
ui->gsoutData->setPinnedColumns(2);
|
||||
|
||||
ui->vsinData->setColumnGroupRole(columnGroupRole);
|
||||
ui->vsoutData->setColumnGroupRole(columnGroupRole);
|
||||
ui->gsoutData->setColumnGroupRole(columnGroupRole);
|
||||
|
||||
QObject::connect(ui->vsinData->horizontalHeader(), &QHeaderView::customContextMenuRequested,
|
||||
[this](const QPoint &pos) { meshHeaderMenu(MeshDataStage::VSIn, pos); });
|
||||
QObject::connect(ui->vsoutData->horizontalHeader(), &QHeaderView::customContextMenuRequested,
|
||||
@@ -2307,17 +2331,21 @@ void BufferViewer::ApplyRowAndColumnDims(int numColumns, RDTableView *view)
|
||||
{
|
||||
int start = 0;
|
||||
|
||||
QList<int> widths;
|
||||
|
||||
// vertex/element
|
||||
view->setColumnWidth(start++, m_IdxColWidth);
|
||||
widths << m_IdxColWidth;
|
||||
|
||||
// mesh view only - index
|
||||
if(m_MeshView)
|
||||
view->setColumnWidth(start++, m_IdxColWidth);
|
||||
widths << m_IdxColWidth;
|
||||
|
||||
for(int i = start; i < numColumns; i++)
|
||||
view->setColumnWidth(i, m_DataColWidth);
|
||||
widths << m_DataColWidth;
|
||||
|
||||
view->verticalHeader()->setDefaultSectionSize(m_DataRowHeight);
|
||||
|
||||
view->setColumnWidths(widths);
|
||||
}
|
||||
|
||||
void BufferViewer::UpdateMeshConfig()
|
||||
|
||||
Reference in New Issue
Block a user