Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-5696

QViewHeader subclass does not repaint correctly on mac carbon

    XMLWordPrintable

Details

    • macOS

    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();
      }
      

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            sorvig Morten Sørvig
            dettman Dean Dettman (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes