Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Style QtComboBox, QListViewItem - Set text padding

Tags:

css

qt

Info: Qt 4.8, Qt Designer

I'm struggling to set the padding of the text items on a combobox drop down. My understanding is the whole thing is the QComboBox widget, and the drop down part is a QListView. But I can't work out what css selector to use to change the style of the text items in the drop down list.

I want the hover item's background color to extend all the way left, instead of leaving a 5px margin. The text itself is lined up fine, just that white gap on the left is bugging me.

Also I would be great if I could remove the dotted border around the highlighted item.

enter image description here

I guess I'm just not using the correct item selector. I've tried QListView::item, and QListViewItem. Here is my CSS applied to the combobox.

QComboBox{
border:                 none;
background-color:   rgb(87, 96, 134);
color:                      rgb(255, 255, 255);
font-weight:            bold;
padding:                    5px 

}

QComboBox::drop-down{
    border:                 none;
    background-color:   rgb(87, 96, 134);
    color:                      rgb(255, 255, 255);
    font-weight:            bold;
    padding:                    0px;
}

QComboBox::down-arrow{
    image:                      url(:/icons/combobox_down_arrow.png);
    padding-right:          5px;
}

QListView{
    border:                 none;
    color:                      rgb(87, 96, 134);
    background-color:   rgb(255, 255, 255);
    font-weight:            bold;
    selection-background-color: rgb(47, 175, 178);
    show-decoration-selected: 1;
    margin-left:                -10px;
    padding-left    :           15px;
}

QListView::item:hover{

    background-color:   rgb(47, 175, 178);
    border:                 none;
}

Thoughts?

like image 562
Oliver Avatar asked Oct 29 '15 13:10

Oliver


1 Answers

It is possible to remove the dotter border using only CSS by setting undocumented property outline:

QComboBox QAbstractItemView {
    outline: none;
}

QListView also can be used in this selector. QAbstractItemView is used in this example, since it is a base class of QListView. Note that this property is set not per item but for the external container of items.

There are other ways to remove the dotted border using coding, for example QT - CSS: decoration on focus


It looks that it is needed to change the code to have better flexibility with padding. QComboBox uses its own private implementation of QAbstractItemDelegate based on QItemDelegate. However, other controls use QStyledItemDelegate (QStyledItemDelegate vs. QItemDelegate). That is why CSS selectors for items (QListView::item) does not have an effect on QComboBox items.

There is the following comment with explanation in the source code:

Note that this class is intentionally not using QStyledItemDelegate
Vista does not use the new theme for combo boxes and there might be other side effects from using the new class

If the above comment is not a problem, it is possible to set QStyledItemDelegate to QComboBox objects:

QComboBox *combobox = new QComboBox;
combobox->setItemDelegate(new QStyledItemDelegate(combobox));

Now, the property selection-background-color is not needed anymore. It is possible to customize ::item:

QComboBox QAbstractItemView::item {
    border: none;
    padding-left: 5px;
}

QComboBox QAbstractItemView::item:selected {
    background: rgb(47, 175, 178);
    padding-left: 5px;
}

Note that to set padding it is also needed to provide, for example, at least border or background property together with padding. Otherwise the padding property is not taken into account.

The selector ::item:selected is used instead of :hover, since items can be also selected by keyboard.


Although it is documented that it is possible to set custom view for QComboBox it is not so simple. QComboBox can also have separator items. The above solution works fine if there are no separator items in QComboBox. To handle separators the item delegate should also know about them.

It is possible to subclass QStyledItemDelegate and copy required functions from Qt private implementation of QComboBoxDelegate. That solution is not very nice. It may be non-portable with new Qt versions. The implementation of QComboBoxDelegate in Qt5 is not compatible with Qt4. However, Qt5 can work with Qt4 implementation, so this class can be taken from Qt4. The QItemDelegate base class is replaced by QStyledItemDelegate:

class ComboBoxDelegateStyled : public QStyledItemDelegate
{ 
    Q_OBJECT
public:
    ComboBoxDelegateStyled(QObject *parent, QComboBox *cmb) :
        QStyledItemDelegate(parent), mCombo(cmb) {}

    static bool isSeparator(const QModelIndex &index) {
        return index.data(Qt::AccessibleDescriptionRole).toString() == QLatin1String("separator");
    }

protected:
    void paint(QPainter *painter,
               const QStyleOptionViewItem &option,
               const QModelIndex &index) const {
        if (isSeparator(index)) {
            QRect rect = option.rect;
            if (const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3*>(&option))
                if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(v3->widget))
                    rect.setWidth(view->viewport()->width());
            QStyleOption opt;
            opt.rect = rect;
            mCombo->style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, painter, mCombo);
        } else {
            QStyledItemDelegate::paint(painter, option, index);
        }
    }

    QSize sizeHint(const QStyleOptionViewItem &option,
                   const QModelIndex &index) const {
        if (isSeparator(index)) {
            int pm = mCombo->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, mCombo);
            return QSize(pm, pm);
        }
        return QStyledItemDelegate::sizeHint(option, index);
    }
private:
    QComboBox *mCombo;
};

It makes sense to subclass QComboBox to use ComboBoxDelegateStyled:

class ComboBoxStyled : public QComboBox
{
public:
    explicit ComboBoxStyled(QWidget *parent = 0) : QComboBox(parent) {
        setItemDelegate(new ComboBoxDelegateStyled(this, this));
    }
};

Now the class ComboBoxStyled can be used instead of QComboBox. It supports combo box separator drawing and also it supports CSS for ::item.

Similar solution for customizing QComboBox separator: how to add stylesheet for separator in QCombobox


PyQt

The above behavior is valid for PyQt. It is possible to remove dotted border using outline and to set styled item delegate to customize CSS ::item:

styledComboBox = QtGui.QComboBox()
delegate = QtGui.QStyledItemDelegate()
styledComboBox.setItemDelegate(delegate)

In such case the combo box separator is displayed as a regular item without text. It is also possible to create custom delegate to handle separators.

like image 153
Orest Hera Avatar answered Nov 10 '22 20:11

Orest Hera