Details
-
Bug
-
Resolution: Out of scope
-
P3: Somewhat important
-
4.5.3
-
None
Description
Please look at the attached code example. It used an extended QHeaderView with a QTableWidget that offers a line edit in each column header.
When you toggle the check box below the line edits are supposed to be enabled/disabled.
The attached code works as expected on Windows and cocoa, but on Mac OS X carbon there is an "initial update problem" - the line edits are not drawn correctly when they should be drawn for the first time. When you click on the header after enabling the checkbox or resize the main widget everything looks alright.
Most notably when enabled you can see that the line edits are there but actually not updated correctly since the cursor shape changes on hover.
Look at the commented code labelled "WORKAROUND:" - when adding this signal it also works on Mac Os, but it would be better if the behavior was the same across all platforms.
#include <QApplication> #include <QTableWidget> #include <QCheckBox> #include <QBoxLayout> #include <QHeaderView> #include <QSize> #include <QLineEdit> #include <private/qheaderview_p.h> #include <QDebug> class LineHeaderView : public QHeaderView { Q_OBJECT static const int FilterMargin = 4; public: LineHeaderView( QWidget* parent=0 ) : QHeaderView(Qt::Horizontal,parent) , m_filterList(), m_filterEditEnabled(false) { } public slots: void setFilterEditEnabled( bool enabled ) { if( enabled != m_filterEditEnabled) { m_filterEditEnabled = enabled; initializeFilterEditList(); } } protected: virtual void paintSection( QPainter* painter, const QRect& rect, int logicalIndex ) const { QRect cR = captionRect(logicalIndex, rect); if (!rect.isValid()) return; // get the state of the section QStyleOptionHeader opt; initStyleOption(&opt); // setup the style options structure opt.rect = rect; opt.section = logicalIndex; // draw caption if text is CaptionString opt.text = model() ? model()->headerData(logicalIndex, orientation(), Qt::DisplayRole).toString() : QString(); QPointF oldBO = painter->brushOrigin(); // now, draw the section QRegion clipRegion = painter->clipRegion(); painter->setClipRect(opt.rect); // 1. draw the section background style()->drawControl(QStyle::CE_HeaderSection, &opt, painter, this); // 2.1. now draw the foreground items in the non-filter part opt.rect = cR; QStyleOptionHeader subopt = opt; subopt.rect = style()->subElementRect(QStyle::SE_HeaderLabel, &opt, this); style()->drawControl(QStyle::CE_HeaderLabel, &subopt, painter, this); painter->setClipRegion(clipRegion); painter->setBrushOrigin(oldBO); if( m_filterEditEnabled ) { QRect filterRect = rect.adjusted(FilterMargin, cR.height(), -FilterMargin-1, -FilterMargin-1 ); QLineEdit* le = m_filterList[logicalIndex].m_filterEdit; if( le ) { le->setGeometry(filterRect); le->show(); } } } virtual QSize sectionSizeFromContents( int logicalIndex ) const { // use SizeHintRole QVariant variant = model()->headerData(logicalIndex, orientation(), Qt::SizeHintRole); QStyleOptionHeader opt; initStyleOption(&opt); opt.section = logicalIndex; opt.text = model() ? model()->headerData(logicalIndex, orientation(), Qt::DisplayRole).toString() : QString(); QSize sH = style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), this); // filter is only applied for horizontal headers... if( orientation() == Qt::Horizontal && m_filterEditEnabled ) { int h = fontMetrics().lineSpacing() + 4; // store caption size hint for painting m_filterList[logicalIndex].m_captionSizeHint = sH; sH.setHeight( sH.height() + h + FilterMargin ); } return sH; } virtual QRect captionRect( int logicalIndex, const QRect& sectionRect ) const { int captionHeight = m_filterEditEnabled ? m_filterList[logicalIndex].m_captionSizeHint.height() : sectionRect.height(); return QRect(sectionRect.left(), sectionRect.top(), sectionRect.width(), captionHeight); } protected slots: void initializeFilterEditList() { if( !m_filterEditEnabled ) { for( int i=0; i<m_filterList.size(); i++ ) m_filterList[i].m_filterEdit->deleteLater(); m_filterList.clear(); } else { while( m_filterList.size() > count() && m_filterList.size() > 0 ) // count returns -1 if no model set... { m_filterList.back().m_filterEdit->hide(); m_filterList.back().m_filterEdit->deleteLater(); m_filterList.pop_back(); } while( m_filterList.size() < count() ) { m_filterList.append( SectionFilter() ); m_filterList.back().m_filterEdit = new QLineEdit(viewport()); m_filterList.back().m_filterEdit->setFrame(true); m_filterList.back().m_filterEdit->setObjectName(QString("FLE%1").arg(m_filterList.size()-1)); } } if( count() > 0 ) { // NOTE: works very well on Windows headerDataChanged(orientation(),0,count()-1); emit geometriesChanged(); //// WORKAROUND: force update of the header on Mac OS X //int size = sectionSize(0); //emit sectionResized(0, size, size); } //updateFilterState(); } private: struct SectionFilter { SectionFilter(): m_filterEdit(0), m_captionSizeHint() { } QLineEdit* m_filterEdit; mutable QSize m_captionSizeHint; }; QList<SectionFilter> m_filterList; bool m_filterEditEnabled; }; #include "main.moc" int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget w; w.setLayout(new QBoxLayout(QBoxLayout::TopToBottom)); QTableWidget* t = new QTableWidget(&w); QCheckBox* b = new QCheckBox("enable filter"); w.layout()->addWidget(t); w.layout()->addWidget(b); LineHeaderView* fhv = new LineHeaderView(t); t->setHorizontalHeader(fhv); QObject::connect(b, SIGNAL(toggled(bool)), fhv, SLOT(setFilterEditEnabled(bool))); t->setColumnCount(5); t->setRowCount(3); QStringList sL; for( int i=0; i<t->columnCount(); i++ ) { sL << QString("Column #%1").arg(i+1); } t->setHorizontalHeaderLabels(sL); w.show(); return a.exec(); }