What you are trying to do is a great example of what qt persistently annoys me - if there is any graphic effect that Qt designers did not think about, creating it themselves, itβs a pain, a constant struggle against Qt, and usually ends up which still refuses.
I suspect you are doing this with small screens in your mind (cell phones? Tablets?), So I think there is no other way to solve this problem.
What I'm trying here is hacked, but otherwise you may have to rewrite the entire scrollbar yourself to add these few missing details. My suggestion:
#ifndef MYSCROLLBAR_H #define MYSCROLLBAR_H #include <QScrollBar> class MyScrollBar : public QScrollBar { Q_OBJECT public: explicit MyScrollBar(QWidget *parent = 0); protected: void showEvent ( QShowEvent * event ); signals: public slots: void updateMask(); }; #endif // MYSCROLLBAR_H
And in myscrollbar.cpp
#include "myscrollbar.h" #include <QPaintEvent> #include <QRegion> #include <QStyleOptionSlider> MyScrollBar::MyScrollBar(QWidget *parent) : QScrollBar(parent) { connect(this, SIGNAL(valueChanged(int)), this, SLOT(updateMask())); } void MyScrollBar::updateMask(){ QStyleOptionSlider opt; initStyleOption(&opt); QRegion r(style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, this)); r+= style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarAddLine, this); r+= style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSubLine, this); setMask(r); } void MyScrollBar::showEvent ( QShowEvent * event ){ QScrollBar::showEvent(event); updateMask(); }
Such a scroller will be transparent (both visually and randomly) in any of these non-vital parts. It still creates some artifacts on the widgets below it - I think setMask() should never have been used like that. To reduce it, you can connect the valueChanged() signal to the update() slot of the view widget of your list. This was well reflected in my toy example, but if you add custom widgets to your list, it can become unbearable. It can also lead to performance issues with more complex applications - especially if you are writing for mobile platforms.
Alternatively, you can simply fork out the entire QScrollBar class and simply change its paintEvent to use fewer subControls than SC_All - with the optional setAttribute(Qt::WA_OpaquePaintEvent, false); constructor setAttribute(Qt::WA_OpaquePaintEvent, false); in the designer, it must provide visual transparency. Then you also have to drag the mouse events (if you donβt do anything important) to the list widget widget (again, there is a problem with custom widgets in the view).
Now it remains to write your own layout class (or just manually position it) that will put both the listview and the scroll bar on top of each other in the correct positions - QStackedLayout sounds good, but allows you to see only one layer at any time - obviously not what we are looking for.
The last step is to turn off the default default scrollbars and connect the default signals / slots (invisible) to the slots / signals of your scrollbar to achieve the effect of the actual scroll.
Soon this will require LOT coding. Are you sure such a simple effect is worth it?
** EDIT: **
I create a layout class for stacking widgets on top of each other - this question gave me the motivation to do this completely;)
#ifndef STACKLAYOUT_H #define STACKLAYOUT_H #include <QLayout> class StackLayout : public QLayout { Q_OBJECT public: StackLayout(); explicit StackLayout(QWidget *parent); ~StackLayout(); void addItem ( QLayoutItem * item ); int count () const; Qt::Orientations expandingDirections () const; bool hasHeightForWidth () const; int heightForWidth ( int w ) const; QLayoutItem * itemAt ( int index ) const; bool isEmpty () const; QSize maximumSize () const; int minimumHeightForWidth ( int w ) const; QSize minimumSize () const; void setGeometry ( const QRect & r ); QSize sizeHint () const; QLayoutItem * takeAt ( int index ); private: QList<QLayoutItem *> itemList; }; #endif // STACKLAYOUT_H
And the stacklayout.cpp file:
StackLayout::StackLayout() :QLayout() {} StackLayout::StackLayout(QWidget *parent) : QLayout(parent) { } StackLayout::~StackLayout(){ QLayoutItem *item; foreach (item, itemList){ delete item; } } void StackLayout::addItem ( QLayoutItem * item ){ itemList.append(item); } int StackLayout::count () const{ return itemList.count(); } Qt::Orientations StackLayout::expandingDirections () const{ Qt::Orientations result = 0; QLayoutItem *item; foreach (item, itemList){ result = result | item->expandingDirections(); } return result; } bool StackLayout::hasHeightForWidth () const{ QLayoutItem *item; foreach (item, itemList){ if (item->hasHeightForWidth()) return true; } return false; } int StackLayout::heightForWidth ( int w ) const{ int result = 0; QLayoutItem *item; foreach (item, itemList){ if (item->hasHeightForWidth()) result = qMax(result, item->heightForWidth(w)); } return result; } QLayoutItem * StackLayout::itemAt ( int index ) const{ if (index<itemList.count()) return itemList[index]; return 0; } bool StackLayout::isEmpty () const{ QLayoutItem *item; foreach (item, itemList){ if (!item->isEmpty()) return false; } return true; } QSize StackLayout::maximumSize () const{ QSize result=QLayout::maximumSize(); QLayoutItem *item; foreach (item, itemList){ result = result.boundedTo(item->maximumSize()); } return result; } int StackLayout::minimumHeightForWidth ( int w ) const{ int result = 0; QLayoutItem *item; foreach (item, itemList){ if (item->hasHeightForWidth()) result = qMax(result, item->minimumHeightForWidth(w)); } return result; } QSize StackLayout::minimumSize () const{ QSize result=QLayout::minimumSize(); QLayoutItem *item; foreach (item, itemList){ result = result.expandedTo(item->minimumSize()); } return result; } void StackLayout::setGeometry ( const QRect & r ){ QLayoutItem *item; foreach (item, itemList){ item->setGeometry(r); } } QSize StackLayout::sizeHint () const{ QSize result=QSize(0,0); QLayoutItem *item; foreach (item, itemList){ result = result.expandedTo(item->sizeHint()); } return result; } QLayoutItem * StackLayout::takeAt ( int index ){ if (index < itemList.count()) return itemList.takeAt(index); return 0; }
Assuming you already have a nice transparent scrollbar to insert it, you would do:
QWidget* w = new QWidget(); StackLayout* sl = new StackLayout(w); QListView* lv = new QListView(w); sl->addWidget(lv); QHBoxLayout* hbl = new QHBoxLayout(); sl->addItem(hbl); TransparentScrollBar* tsc = new TransparentScrollBar(w); hbl->addWidget(tsc,0); hbl->insertStretch(0,1);