How to change QComboBox's first item style when there is no selection

114 Views Asked by At

I have a QComboBox with no preselected item. When I first open it, the first item has a light blue color. After I move the cursor on the dropdown list, the hovered item gets dark blue, and there is no more light blue colored entry.

Why is the first item light blue, and how can I change its color using QSS?

enter image description here enter image description here

ui->cb1->addItems({"1111", "2222"});
ui->cb1->setEditable(true);
ui->cb1->lineEdit()->setPlaceholderText(tr("Please select"));
ui->cb1->setCurrentIndex(-1);
3

There are 3 best solutions below

0
musicamante On BEST ANSWER

The partially highlighted item you see is the current index of the popup (a QListView).

Qt item views have two similar but independent states: current items and selected items (see the table from the Model/View programming documentation).

The current item (or index) is primarily related to focus and editing, there can only be one current index for a view.

The selected index indicates a selection that could correspond to one, more, or no indexes.

Those two aspects are often commonly related: the current index is normally the selected index, or is part of a selection. In reality, the current index can also be outside the selection: this is similar to what happens when you deselect an icon from a group selection.

When an item view receives focus, and no current index has been explicitly set, it automatically tries to find the first available/selectable index in its model (using moveCursor()), and sets it as its current index, but without selecting it.

This is exactly what happens in a combo box when the popup is shown: if a valid index is set in the combo, the view will automatically make it the current and selected index, but if the combo index is -1 there is no valid index set, so it sets the first item as its current, without selecting it.

The color you're seeing is caused by your current style ("fusion"), which chooses to use a semi transparent color for the "focus frame" based on the palette Highlight role. Other styles may not draw anything at all.

If you want to change that color, you can use style sheets with the item:focus:!selected selector:

QComboBox QAbstractItemView::item:focus:!selected {
    background-color: orange;
}

Alternatively, if you want to completely avoid that behavior, just explicitly set the current index to the view to an invalid index right after setting the -1 index:

ui->cb1->setCurrentIndex(-1);
ui->cb1->view()->setCurrentIndex(QModelIndex());
0
Atmo On

Depending on what you want, different solutions can be used. Based on the appearance of your screenshots, you seem to be using the Fusion style.
Therefore, I have tested the below solutions in that same style.

Option 1: Remove this light-blue highlight completely.

The CSS you can apply to your QComboBox is set the outline property to none.
With that said, the outline property does not work perfectly in Qt, so a bit of styling is required to adjust the items from the drop-down:

QComboBox QAbstractItemView {
    outline:none;
}

QComboBox QAbstractItemView::item {
    border: none;
    color:black;
}

QComboBox QAbstractItemView::item:hover {
    color:white;
    background-color:darkgray;
}

Option 2: Change the selection background color and have a focus highlight that is in a matching light color

You can simply use selection-background-color/selection-color as was pointed out in the accepted answer linked in the comments (there):

QComboBox QAbstractItemView {
    selection-background-color: darkgray;
    selection-color: white;
} 

This option works, I think, only in style Fusion. To get the same result in a different style such as Windows, do it manually using option 3 below.

Option 3: Independently set a color for the focused item

For that, you can apply the style sheet below. Note that I made it red, but you can of course pick the same color or a manually-picked lighter color as when items are hovered.

QComboBox QAbstractItemView {
    outline:none;
}

QComboBox QAbstractItemView::item:focus {
    background-color:red;
}

QComboBox QAbstractItemView::item::hover,
QComboBox QAbstractItemView::item::selected {
    color:white;
    background-color:darkgray;
}
0
A.R.M On

This answer is only complementary to this, and this answer. It only explains the source of that partially-transparent highlight color, which is what appears as light blue.


What appears to be light blue is actually a QLinearGradient, and is specific to Fusion Style. It's used to fill the PE_FrameFocusRect, which is a generic focus indicator.

Show focus rect by changing focus through widgets using tab

From the source code of QFusionStyle, specifically in QFusionStyle::drawPrimitive, we can see how case PE_FrameFocusRect is handled:

case PE_FrameFocusRect:
    if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(option)) {
        //### check for d->alt_down
        if (!(fropt->state & State_KeyboardFocusChange))
            return;
        QRect rect = option->rect;
        painter->save();
        painter->setRenderHint(QPainter::Antialiasing, 
                               true);
        painter->translate(0.5, 
                           0.5);
        QColor fillcolor = highlightedOutline;
        fillcolor.setAlpha(80);
        painter->setPen(fillcolor.darker(
            120));
        fillcolor.setAlpha(30);
        QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
        gradient.setColorAt(0, fillcolor.lighter(
                                   160));
        gradient.setColorAt(1, 
                            fillcolor);
        painter->setBrush(gradient);
        painter->drawRoundedRect(option->rect.adjusted(0, 0, -1, -1), 1, 
                                 1);
        painter->restore();
    }
    break;

painter uses gradient as a brush, gradient uses fillcolor as its stops' color, and fillcolor is just highlightedOutline made partially transparent, which in turn, is just the highlight role color, and is obtained through QColor QFusionStylePrivate::highlightedOutline:

QColor highlightedOutline(const QPalette &pal) const {
    QColor highlightedOutline = highlight(pal).darker(125);
    if (highlightedOutline.value() > 160)
        highlightedOutline.setHsl(highlightedOutline.hue(), highlightedOutline.saturation(), 160);
    return highlightedOutline;
}

For comparison, Windows Style depends on the current background color, specifically the QStyleOptionFocusRect's, and if invalid, the painter's:

case PE_FrameFocusRect:
    if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(opt)) {
        //### check for d->alt_down
        if (!(fropt->state & State_KeyboardFocusChange) && !proxy()->styleHint(SH_UnderlineShortcut, opt))
            return;
        QRect r = opt->rect;
        p->save();
        p->setBackgroundMode(Qt::TransparentMode);
        QColor bg_col = fropt->backgroundColor;
        if (!bg_col.isValid())
            bg_col = p->background().color();
        // Create an "XOR" color.
        QColor patternCol((bg_col.red() ^ 0xff) & 0xff,
                          (bg_col.green() ^ 0xff) & 0xff,
                          (bg_col.blue() ^ 0xff) & 0xff);
        p->setBrush(QBrush(patternCol, Qt::Dense4Pattern));
        p->setBrushOrigin(r.topLeft());
        p->setPen(Qt::NoPen);
        p->drawRect(r.left(), r.top(), r.width(), 1);    // Top
        p->drawRect(r.left(), r.bottom(), r.width(), 1); // Bottom
        p->drawRect(r.left(), r.top(), 1, r.height());   // Left
        p->drawRect(r.right(), r.top(), 1, r.height());  // Right
        p->restore();
    }
    break;