How to customize a QScrollBar

68 views Asked by At

This image shows the kind of scrollbar I want in my Qt application:

Wanted result

And this is my attempt, which isn't close to the requirement:

QString sbStyle = QString(
    "QScrollBar:vertical {"
    "border: none;"
    "background: #111;"
    "width: 10px;"
    "}"
    "QScrollBar::handle:vertical {"
    "background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #ddd, stop:1 #111);"
    "min-height: 10px;"
    "}"
    "QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {"
    "border: none;"
    "background: #111;"
    "}"
);
1

There are 1 answers

2
musicamante On

Styling scroll bars (and complex widgets in general) requires some patience and proper studying of the documentation and the examples.

Most importantly, complex widgets have a fundamental requirement, as explained in the documentation:

Note: With complex widgets such as QComboBox and QScrollBar, if one property or sub-control is customized, all the other properties or sub-controls must be customized as well.

The most important issue with your attempt is that you didn't leave space for the buttons and did not set any stylesheet for them.

The space for the button is set by using proper margins (top and bottom for vertical scroll bars).

The arrow buttons can be styled using images, but for simple triangle shapes a smart CSS trick can be used: use borders.

The idea is to have an 0-sized object with borders around it; since only the borders are visible, the result is a rectangle composed of triangles, and the border width is actually the height of each triangle (the distance between the side and the center):

0-sized rectangle with borders

Then you only draw the border that is opposed to the arrow direction; if you want to show an up arrow, draw the bottom border with a different color:

up arrow using the bottom border

Note tat in order to properly place those arrows without unnecessary margin (like the top "border" of the up arrow), some adjustments must be done using top or bottom properties.

Finally, if you need the dot-pattern background, that has to be done through an image (a simple 2x2 image will suffice).

Here is the resulting stylesheet:

    QScrollBar:vertical {
        border: none;
        background: #111;
        width: 8px;
        margin: 6px 0 6px 0;
    }
    QScrollBar::handle:vertical {
        background: qlineargradient(
            x1:0, y1:0, x2:1, y2:0, 
            stop:0 #fff, stop:1 #111
        );
        min-height: 8px;
        border: 2px solid;
        border-left-color: #ddd;
        border-top-color: #ddd;
        border-right-color: #444;
        border-bottom-color: #222;
    }
    QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
        border: none;
        background: #111;
        subcontrol-origin: margin;
        height: 6px;
    }
    QScrollBar::add-line:vertical {
        subcontrol-position: bottom;
    }
    QScrollBar::sub-line:vertical {
        subcontrol-position: top;
    }
    QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical {
        border: 4px solid #111;
        width: 0px;
        height: 0px;
    }
    QScrollBar::up-arrow:vertical {
        top: -2px;
        border-bottom-color: gray;
    }
    QScrollBar::down-arrow:vertical {
        bottom: -2px;
        border-top-color: gray;
    }
    QScrollBar::up-arrow:vertical:hover {
        border-bottom-color: lightgray;
    }
    QScrollBar::down-arrow:vertical:hover {
        border-top-color: lightgray
    }
    QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
        background: url("pattern.png");
    }
    QScrollBar::add-page:vertical {
        background-position: bottom; /* prevent background "scrolling" */
    }

And here is the result:

Screenshot of the result

Zoomed in to see more details:

Zoomed screenshot