Search code examples
qtqwidgetqcheckbox

How to draw a Square between the text and the checkbox in QCheckBox


I need to customize the QCheckbox like this: add a square between the checkbox and the text Example:

So to do this, I inherit QCheckBox and override paintEvent(QPaintEvent*):

void customCheckBox::paintEvent(QPaintEvent*){
      QPainter p(this);
      QStyleOptionButton opt;
      initStyleOption(&opt);
      style()->drawControl(QStyle::CE_CheckBox,&opt,&p,this);

      QFontMetrics font= this->fontMetrics();// try to get pos by bounding rect of text, but it ain't work :p
      QRectF rec = font.boundingRect(this->text());
      p.fillRect(rect,QBrush(QColor(125,125,125,128)));
      p.drawRect(rect);

}

I have a problem that I don't know how the position to place the QRectF rec. And I can't set the size too small, eg: it will disappear when size of rec < QSize(10,10).

Any ideas are appreciated.

PS: I use OpenSUSE 13.1 with Qt 4.8.5


Solution

  • The main idea is to copy default implementation of checkbox drawing and modify it according to your needs. We get label rectangle in default implementation, so we just need to draw new element in this place and shift label to the right. Also we need to adjust size hint so that both new element and default content fit in minimal size.

    class CustomCheckBox : public QCheckBox {
      Q_OBJECT
    public:
      CustomCheckBox(QWidget* parent = 0) : QCheckBox(parent) {
        m_decoratorSize = QSize(16, 16);
        m_decoratorMargin = 2;
      }
    
      QSize minimumSizeHint() const {
        QSize result = QCheckBox::minimumSizeHint();
        result.setWidth(result.width() + m_decoratorSize.width() + m_decoratorMargin * 2);
        return result;
      }
    
    protected:
      void paintEvent(QPaintEvent*) {
        QPainter p(this);
        QStyleOptionButton opt;
        initStyleOption(&opt);
        QStyleOptionButton subopt = opt;
        subopt.rect = style()->subElementRect(QStyle::SE_CheckBoxIndicator, &opt, this);
        style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &subopt, &p, this);
        subopt.rect = style()->subElementRect(QStyle::SE_CheckBoxContents, &opt, this);
        p.fillRect(QRect(subopt.rect.topLeft() + QPoint(m_decoratorMargin, 0),
                         m_decoratorSize), QBrush(Qt::green));
        subopt.rect.translate(m_decoratorSize.width() + m_decoratorMargin * 2, 0);
        style()->drawControl(QStyle::CE_CheckBoxLabel, &subopt, &p, this);
        if (opt.state & QStyle::State_HasFocus) {
          QStyleOptionFocusRect fropt;
          fropt.rect = style()->subElementRect(QStyle::SE_CheckBoxFocusRect, &opt, this);
          fropt.rect.setRight(fropt.rect.right() + 
                              m_decoratorSize.width() + m_decoratorMargin * 2);
          style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fropt, &p, this);
        }
      }
    
    private:
      QSize m_decoratorSize;
      int m_decoratorMargin;
    };
    

    Note that this solution may be not portable because checkboxes are drawn with drastic differences on different platforms. I tested it on Windows only. I used default implementation provided by QCommonStyle.