4

So, I have a QComboBox.

enter image description here

If the currentText() is too long for the widget then I want to show an ellipsis.

Like this :

enter image description here

So :

void MyComboBox::paintEvent(QPaintEvent * )
{
      QStylePainter painter(this);
      QStyleOptionComboBox opt;
      initStyleOption(&opt);
      painter.drawComplexControl(QStyle::CC_ComboBox, opt);

      QRect rect = this->rect();
      //this is not ideal
      rect.setLeft(rect.left() + 7);
      rect.setRight(rect.width() - 15);
      //

      QTextOption option;
      option.setAlignment(Qt::AlignVCenter);

      QFontMetrics fontMetric(painter.font());
      const QString elidedText = QAbstractItemDelegate::elidedText(fontMetric, rect.width(), Qt::ElideRight, this->currentText());
      painter.drawText( rect, elidedText, option);
}

This is working flawlessy. The problem is the code in between the comments, because I am hardcoding the distances from the left and right border. It makes me cringe.

The result without that code is:

enter image description here

Does anybody know a more general way to do this, without hardcoding? Thank you

andrea.marangoni
  • 1,499
  • 8
  • 22

2 Answers2

3

Where the text should be drawn exactly depends on the used style. You can get information about (some of) the positioning of subelements with QStyle::subControlRect. The subcontrol that matches the combo box text best seems to be QStyle::SC_ComboBoxEditField, though if the item has an icon, this needs to be taken into account as well. If the items do not have icons, you can go with

  QRect textRect = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this);
  QFontMetrics fontMetric(painter.font());
  const QString elidedText = QAbstractItemDelegate::elidedText(fontMetric, textRect.width(), Qt::ElideRight, this->currentText());
  opt.currentText = elidedText;
  painter.drawControl(QStyle::CE_ComboBoxLabel, opt);

You might want to have a look at how e.g. QFusionStyle::drawControl works for details.

In general, if you want all your combo boxes to elide the text, you should consider implementing your own QProxyStyle and only override MyStyle::drawControl for QStyle::CE_ComboBoxLabel.

E4z9
  • 1,713
  • 9
  • 11
2

This is the solution I've been using:

void CustomComboBox::paintEvent(QPaintEvent * /*event*/)
{
    QStyleOptionComboBox opt;
    initStyleOption(&opt);

    QStylePainter p(this);
    p.drawComplexControl(QStyle::CC_ComboBox, opt);

    QRect textRect = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this);
    opt.currentText = p.fontMetrics().elidedText(opt.currentText, Qt::ElideRight, textRect.width());
    p.drawControl(QStyle::CE_ComboBoxLabel, opt);
}

This approach is very similar to a combination of your sample code and the snippet E4z9 suggested. I just thought I'd include the whole method for others coming here in the future.

Parker Coates
  • 8,520
  • 3
  • 31
  • 37
  • 1
    Tried this method, the debug output shows the correct initial text and it's correctly elided but in the UI is still shown the initial non-elided text. What is even stranger, if I comment out the whole method body content, the label is still drawn, nothing else, no button, no frame of the combobox, only the label with the non elided text. Am I missing something? – Damir Porobic May 24 '21 at 10:32
  • 1
    @DamirPorobic, I just checked and the implementation above is exactly what we have been shipping for a number of years without any reported issues. If you comment out the body of `paintEvent` nothing will be painted by the widget, meaning anything you do see painted has to be coming from some where else. – Parker Coates Oct 07 '21 at 13:06
  • This will change the text of the item in the popup, not the `LineEdit` text displaying when the popup is hidden. – W.Perrin Jan 13 '22 at 05:32
  • @W.Perrin, this is assuming a non-editable `QComboBox`. I can think of no sane way one could (or would want to) elide text inside a `QLineEdit`. – Parker Coates Jan 17 '22 at 13:11