Skip to content

Instantly share code, notes, and snippets.

@photonlili
Last active February 15, 2022 08:42
Show Gist options
  • Select an option

  • Save photonlili/90705564f3182b82bd9f to your computer and use it in GitHub Desktop.

Select an option

Save photonlili/90705564f3182b82bd9f to your computer and use it in GitHub Desktop.
QHeaderView with custom checkbox
#include "checkedheader.h"
#include <QDebug>
CheckedHeader::CheckedHeader(Qt::Orientation orientation, QWidget *parent)
: QHeaderView(orientation, parent)
{
connect(this, &CheckedHeader::sectionResized, [&](int logicalIndex, int oldSize, int newSize){
for( int i = visualIndex(logicalIndex); i < visibleCount(); ++i ){
int index = this->logicalIndex(i);
menuRect[index].moveTo(menuRect[index].x()+ newSize-oldSize, menuRect[index].y());
}
});
setSectionsMovable(true);
connect(this, &CheckedHeader::sectionMoved, this, &CheckedHeader::onSectionMoved);
}
void CheckedHeader::onSectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
{ int offset = 0;
if( oldVisualIndex < newVisualIndex ){
// move right;
for( int i = oldVisualIndex; i <= newVisualIndex; ++i ){
int index = this->logicalIndex(i);
QRect oldRect = menuRect[index];
if( i != newVisualIndex ){
menuRect[index].moveTo(oldRect.x()- sectionSize(logicalIndex), oldRect.y());
offset += sectionSize(index);
}
else
menuRect[index].moveTo(oldRect.x()+offset,oldRect.y());
}
}
else {
// move left
for( int i = oldVisualIndex; i >= newVisualIndex; --i ){
int index = this->logicalIndex(i);
QRect oldRect = menuRect[index];
if( i != newVisualIndex ){
menuRect[index].moveTo(oldRect.x() + sectionSize(logicalIndex), oldRect.y());
offset += sectionSize(index);
}
else
menuRect[index].moveTo(oldRect.x() - offset,oldRect.y());
}
}
}
void CheckedHeader::setChecked(bool checked, int index)
{
if (isEnabled() && menuOn.value(index) != checked)
{
menuOn[index] = checked;
updateSection(index);
emit toggled(checked, index);
}
}
void CheckedHeader::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
painter->save();
QHeaderView::paintSection(painter, rect, logicalIndex);
painter->restore();
QStyleOptionButton option;
if (isEnabled())
option.state |= QStyle::State_Enabled;
option.rect = menuRect[logicalIndex];
if (menuOn.value(logicalIndex))
option.state |= QStyle::State_On;
else
option.state |= QStyle::State_Off;
if (menuOn.value(logicalIndex))
style()->drawControl(QStyle::CE_CheckBox, &option, painter);
}
void CheckedHeader::mousePressEvent(QMouseEvent *event)
{
int index = logicalIndexAt(event->pos());
if (isEnabled() && menuRect[index].contains(event->pos()))
{
menuOn[index] = !menuOn.value(index);
updateSection(index);
emit toggled(menuOn.value(index), index);
QHeaderView::mousePressEvent(event);
}
else
{
QHeaderView::mousePressEvent(event);
}
}
void CheckedHeader::mouseMoveEvent(QMouseEvent *event)
{
int index = logicalIndexAt(event->pos());
if( isEnabled() && menuRect[index].contains(event->pos()))
{
menuOn[index] = true;
updateSection(index);
}
else {
menuOn[index] = false;
updateSection(index);
}
QHeaderView::mouseMoveEvent(event);
}
QSize CheckedHeader::sizeHint() const
{
// Get the base implementation size.
QSize baseSize = QHeaderView::sizeHint();
// Override the height with a custom value.
//baseSize.setHeight( 55 );
return baseSize;
}
void CheckedHeader::changeSectionVisibility(int logicalIndex, bool visible)
{
if( isSectionHidden(logicalIndex) && visible ){
showSection(logicalIndex);
emit sectionVisibleChanged(logicalIndex, visible);
}
else if(!isSectionHidden(logicalIndex) && !visible ){
hideSection(logicalIndex);
emit sectionVisibleChanged(logicalIndex, visible);
}
}
QRect CheckedHeader::checkBoxRect(const QRect &sourceRect) const
{
QStyleOptionButton checkBoxStyleOption;
QRect checkBoxRect = style()->subElementRect(QStyle::SE_CheckBoxIndicator,
&checkBoxStyleOption);
QPoint checkBoxPoint(sourceRect.x() + sourceRect.width() - checkBoxRect.width() - 5,
sourceRect.y() + sourceRect.height() / 2 - checkBoxRect.height() / 2);
return QRect(checkBoxPoint, checkBoxRect.size());
}
void CheckedHeader::initMenuRect()
{
int offset = 0;
for( int i = 0; i < visibleCount(); ++i ){
QRect sourceRect = QRect(offset,0,sectionSize(logicalIndex(i)),sizeHint().height());
menuRect[i] = checkBoxRect(sourceRect);
offset += sectionSize(i);
}
}
int CheckedHeader::visibleCount() const
{
int counter = 0;
for( int i = 0; i < count(); ++i ){
if( !isSectionHidden(i) )
counter++;
}
return counter;
}
#ifndef CHECKEDHEADER_H
#define CHECKEDHEADER_H
#include <QMap>
#include <QHeaderView>
#include <QPainter>
#include <QMouseEvent>
class CheckedHeader : public QHeaderView
{
Q_OBJECT
public:
explicit CheckedHeader(Qt::Orientation orientation, QWidget *parent = 0);
void setChecked( bool checked, int index );
void initMenuRect();
virtual QSize sizeHint() const;
int visibleCount() const;
void onSectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
void changeSectionVisibility(int logicalIndex, bool visible);
signals:
void toggled(bool checked, int index);
void sectionVisibleChanged( int logicalIndex, bool visible );
protected:
void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
private:
QRect checkBoxRect(const QRect &sourceRect) const;
QMap<int, bool> menuOn;
QMap<int, QRect> menuRect;
};
#endif // CHECKEDHEADER_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment