2
votes

EDIT: Updated with working example and example screenshots of current and desired behavior.

Description

I'm trying to get a multiline QLabel to have these behaviors:

  1. It should expand to fill available width.
  2. If its text is longer than it can show, it should show all it can (expanding to its max height) and elide the last line.
  3. If its text is not the longest it can show, it should show all text, but shrink its height to fit the text.

The label is in a layout. Maybe I'm thinking about this wrong, like maybe instead of a label it should be a text area wrapped in something else?

Anyway, I've found solutions that cover one or two of these behaviors, but not all three. I've tried tweaking Qt's ElidedLabel, but I can't quite figure out how to tweak it to achieve all three.

I've considered overriding sizeHint(), but I'm not Qt-savvy enough to know if that's the right way to do this. I feel like maybe the right combination of layoutSizeConstraint on the layout and size policies on the ElidedLabel might make this possible, but I'm not sure.

I've slightly modified Qt's ElidedLabel example code so that it doesn't take its content in its constructor.

Example code

https://gist.github.com/jahabrewer/bd0c79ac2255953eeadf87c9767ce693

Screenshots

Current behavior

Left column has text short enough that the ElidedLabel should reduce its height and cede that vertical space to the QLabel underneath it. Right column has text long enough that it's elided, which is correct/desired.

current behavior

Desired behavior

(to be clear, I want a single configuration that will produce behavior like the left column when text is short and like the right column when text is long)

desired behavior

1
It'd help to create a MCVE (add main function creating labels, or better yet self-contained main.cpp with everything), and adding screenshots of the wrong behavior wouldn't hurt either.hyde
A QLabel with the property WordWrap to true isn't enough?Dimitry Ernot
@RomhaKorev QLabel with WordWrap doesn't elide.Jay
Right. My bad... The text in your label will change at runtime or not? Because it will be more tricky to adjust the layout (the label will be resized but not the layout item)Dimitry Ernot

1 Answers

1
votes

You have to constraint the height of your label to the minimum needed to draw the text. You can get this height by using the QFontMetrics class and the method QFontMetrics::boundingRect:

QFontMetrics const fontMetrics(font());
QRect const r = fontMetrics.boundingRect(
            QRect(QPoint(0, 0), size()),
            Qt::TextWordWrap | Qt::ElideRight,
            content
            );
qDebug() << "Needed Height:" << r.height();

First, define the method QWidget::sizeHint to force your label to have a size:

virtual QSize sizeHint() const override
{
    QFontMetrics const fontMetrics(font());
    QRect const r = fontMetrics.boundingRect(
            QRect(QPoint(0, 0), size()),
            Qt::TextWordWrap | Qt::ElideRight,
            content
            );
    return QSize(width(), r.height());
}

We want to shrink the height, only. That's why we will not use the width returned by the font metrics

Now, we will use the resize events to check if we can shrink the height:

So, we can override the QWidget::resizeEvent method:

virtual void resizeEvent(QResizeEvent* event) override
{
    QFrame::resizeEvent(event); // Process the event. The label is now resized
    QSize const size = sizeHint();
    if (size.height() < height()) // Shrink the height if needed
        resize(QSize(width(), size.height()));
}

If the text has to change during the runtime, we have to readjust the size:

void setText(const QString &newText)
{
   content = newText;
   update();
   adjustSize(); // Will resize the label
}

The code I used for the tests:

QWidget* w = new QWidget();
QVBoxLayout* l = new QVBoxLayout(w);

QString const lorem("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
ElidedLabel* label = new ElidedLabel(lorem);
label->setFrameShape(QFrame::Box); // To see its bounds

l->addWidget(label);
l->addWidget(new QLabel("Text Label"));

QTimer::singleShot(3000, [=]() { label->setText(lorem.left(100)); });

w->show();