Change color of placeholder text in QLineEdit
Asked Answered
B

7

7

When I set the placeholder text with QLineEdit::setPlaceholderText(), it appears gray.

enter image description here

Is there any way to change the color to something else, for example red?

Brockbrocken answered 1/12, 2014 at 8:14 Comment(3)
You need to implement your own placeholder painting function. You may look throught source of Qt how it works. It's very simple.Yalta
Here you can see how it is handled in the QLineEdit's source code. Basically it just takes the text color and reduces the alpha.Metz
I edited the question to make it more general and understandable.Mochun
C
3

You can't, at least with the current QLineEdit code.

As you can see from the source code, the placeholder text is simply taking the foreground brush of the palette and making it partially transparent, see QLineEdit::paintEvent:

if (d->shouldShowPlaceholderText()) {
    if (!d->placeholderText.isEmpty()) {
        QColor col = pal.text().color();
        col.setAlpha(128);
        QPen oldpen = p.pen();
        p.setPen(col);
        QRect ph = lineRect.adjusted(minLB, 0, 0, 0);
        QString elidedText = fm.elidedText(d->placeholderText, Qt::ElideRight, ph.width());
        p.drawText(ph, va, elidedText);
        p.setPen(oldpen);
    }
}

You can work with upstream into a more general solution, though. In particular I one would expect that color to be added to the palette, or in general provided by the current QStyle (for instance as a style hint).

Contentment answered 22/1, 2015 at 17:27 Comment(0)
E
7

You'll have to subclass QLineEdit and paint your own placeholder in the paintEvent().

class CustomColorPlaceholderLineEdit : public QLineEdit
{
public:
    CustomColorPlaceholderLineEdit(QWidget * parent = 0) : QLineEdit(parent) { color = QColor(0,0,0,128); }
    void setCustomPlaceholderText(const QString &text) { this->mText = text; }
    const QString &customPlaceholderText() const { return mText; }
    void setCustomPlaceholderColor(const QColor &color) { this->color = color; }
    const QColor &customPlaceholderColor() const { return color; }
    void paintEvent(QPaintEvent *event) {
        QLineEdit::paintEvent(event);
        if (!hasFocus() && text().isEmpty() && !mText.isEmpty()) {
            // QLineEdit's own placeholder clashes with ours.
            Q_ASSERT(placeholderText().isEmpty());
            QPainter p(this);
            p.setPen(color);
            QFontMetrics fm = fontMetrics();
            int minLB = qMax(0, -fm.minLeftBearing());
            QRect lineRect = this->rect();
            QRect ph = lineRect.adjusted(minLB + 3, 0, 0, 0);
            QString elidedText = fm.elidedText(mText, Qt::ElideRight, ph.width());
            p.drawText(ph, Qt::AlignVCenter, elidedText);
        }
    }
private:
    QString mText;
    QColor color;
};
Elevenses answered 22/1, 2015 at 19:31 Comment(4)
I would not recommend going this way. It's an extremely fragile hack, as you're basically overpainting over the existing placeholder text. What if Qt decides to fine-tune the style and move stuff around by a pixel or so? What if the condition that checks if the placeholder should be painted changes? (In particular, the one in this code is wrong.)Contentment
@Contentment Qt standart placeholder text does not exist. I'm drawing custom text over QLineEdit under certain conditions. And you can change the condition to whatever you want.Elevenses
@Contentment I have an idea - you could just change the text color in the onFocus() event - change it to the custom color on focus leave, and change it back on focus enter.Loadstone
And the "whatever you want" is what I question. The condition is already broken as it doesn't match the same condition under which Qt draws its own. Sure, that could be solved by using private headers. But again, if Qt decides to change the behaviour somehow (for instance, just like you're doing, by modifying the margins) you'll end up with broken rendering. It's a hack. Not a workaround. Not a solution.Contentment
J
7

There is another a bit hacky but simple and reliable way.

connect(lineEdit, &QLineEdit::textChanged, this, &YourClass::updateLineEditStyleSheet);

void YourLineEdit::updateLineEditStyleSheet()
{
    if (lineEdit->text().isEmpty()) {
        lineEdit->setStyleSheet("#lineEdit { color: lightGray;"); // Set your color but remember that Qt will reduce alpha
    } else {
        lineEdit->setStyleSheet("#lineEdit { color: black;"); // usual color
    }
}

also you can use this way to derived from QLineEdit class

Jewfish answered 29/12, 2016 at 0:2 Comment(0)
D
5

If you want to use QSS instead of QPalette, try the following:

setStyleSheet("QLineEdit{"
              "    color: red;" //TEXT COLOR
              "}"
              "QLineEdit[text=\"\"]{"
              "    color: gray;" //TEXTHOLDER COLOR
              "}");
connect(ui->lineEdit, &QLineEdit::textChanged, [=]{ style()->polish(ui->lineEdit); });

You can change the color, but bare in mind there is an alpha factor set in the placeholder from the source code (as mentioned in another comment) that cannot be removed. Therefore you will always see the placeholder darker (no white possible with this option).

Duplet answered 26/2, 2019 at 13:57 Comment(1)
Would've been great if this worked. But it also changes the color to gray when there is text inside of the QLineEdit. At least it does in Qt 5.15Ratify
M
3

If you want to change placeholder text color for a QLineEdit you have to customize the component's QPalette object.

QPalette p = lineEdit->palette();
p.setColor(QPalette::Mid, Qt::red); // assuming Mid is the color you want to change.
lineEdit->setPalette(p);

I don't recall exactly which QPalette::ColorRole is appropriate for changing QLineEdit's placeholder text color though.

Mochun answered 1/12, 2014 at 10:12 Comment(3)
This code doesn't work - I tested it, text color stays gray. I've started a bounty on this question.Loadstone
In Qt 5.12, there is a QPalette::PlaceholderText which works for this purpose, and avoids the 0.5 opacity problem.Mita
@AlexKritchevsky tnx QPalette::PlaceholderText works for me.Strength
C
3

You can't, at least with the current QLineEdit code.

As you can see from the source code, the placeholder text is simply taking the foreground brush of the palette and making it partially transparent, see QLineEdit::paintEvent:

if (d->shouldShowPlaceholderText()) {
    if (!d->placeholderText.isEmpty()) {
        QColor col = pal.text().color();
        col.setAlpha(128);
        QPen oldpen = p.pen();
        p.setPen(col);
        QRect ph = lineRect.adjusted(minLB, 0, 0, 0);
        QString elidedText = fm.elidedText(d->placeholderText, Qt::ElideRight, ph.width());
        p.drawText(ph, va, elidedText);
        p.setPen(oldpen);
    }
}

You can work with upstream into a more general solution, though. In particular I one would expect that color to be added to the palette, or in general provided by the current QStyle (for instance as a style hint).

Contentment answered 22/1, 2015 at 17:27 Comment(0)
O
0

@Meefte solution is quite good given the situation that Qt gives placeholder the same color as for the text, except it adds 50% opacity. So, there is little choice to set placeholder color to be different than the text. However, even this solution could be improved by making sure that you would not need to set some other variable than the default one Qt provides you.

The need to use default placeholderText() might arise from the situation when you have lots of QLineEdit controls which are already promoted to some control overriding QLineEdit behavior, and placeholderText() is already set through code or through Qt Creator, i.e. it would be a bit painful to introduce another dynamic property. However, if you did not promote to some child control, then it would be a necessity to do so in order to use such solution.

class CustomColorPlaceholderLineEdit : public QLineEdit
{
public:
    CustomColorPlaceholderLineEdit(QWidget * parent = 0) : QLineEdit(parent) { color = QColor(0,0,0,128); }

    const QString &customPlaceholderText() const { return mText; }

    void setCustomPlaceholderColor(const QColor &color) { this->color = color; }

    const QColor &customPlaceholderColor() const { return color; }

    void paintEvent(QPaintEvent *event)
    {
        if(color.isValid() && text().isEmpty() && (!placeholderText().isEmpty() || !mText.isEmpty()))
        {
            if(!placeholderText().isEmpty())
            {
                // In this way, placeholderText() is taken into local variable 'mText' care. Whenever placeholderText() will change, there it will be taken care of.
                mText = placeholderText();

                // This will ensure Qt will not draw placeholder for us.
                setPlaceholderText("");
            }

            // By this, we make sure Qt will paint QLineEdit default parts properly.
            QLineEdit::paintEvent(e);

            // And now @Meefte code is reused here.
            QPainter p(this);
            p.setPen(color);
            QFontMetrics fm = fontMetrics();
            int minLB = qMax(0, -fm.minLeftBearing());
            QRect lineRect = this->rect();
            QRect ph = lineRect.adjusted(minLB + 3, 0, 0, 0);
            QString elidedText = fm.elidedText(mText, Qt::ElideRight, ph.width());
            p.drawText(ph, Qt::AlignVCenter, elidedText);
            return; // No need to paint again.
        }

        // Default Qt's painting behavior for QLineEdit.
        QLineEdit::paintEvent(e);
    }
private:
    QString mText;
    QColor color;
};
Obstacle answered 10/10, 2017 at 14:59 Comment(0)
T
0

QT still has this problem) I solved it like this:

bool CustomLineEdit::event(QEvent *event)
{
    bool eventResult = QLineEdit::event(event);

    if (event->type() == QEvent::StyleChange) {
         QPalette pal = palette();
         pal.setColor(QPalette::PlaceholderText, Qt::red);
        setPalette(pal);
    }
    return eventResult;
}
Thallium answered 25/11, 2021 at 16:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.