How to create an opaque scrollbar
-
Native macOS scrollbars are translucent and cover part of the viewport. I want to create custom opaque scrollbars. The problem is that they cover part of the viewport because they're expected to be translucent. I'm currently using
QScrollArea
. I'm not sure but I might need to useQAbstractScrollArea
and do some stuff. I'm not really sure how to achieve what I want to achieve.This is an example that shows scrollbars covering the viewport.
#include <QtGui/qevent.h> #include <QtGui/qpainter.h> #include <QtWidgets/qscrollbar.h> #include <QtWidgets/qscrollarea.h> #include <QtWidgets/qmainwindow.h> #include <QtWidgets/qapplication.h> class MyScrollBar final : public QScrollBar { public: MyScrollBar(Qt::Orientation orient, QWidget *parent) : QScrollBar{orient, parent} { setStyleSheet("width: 8px; height: 8px"); } private: int pagePixels(const int length) const { return (length * pageStep()) / (maximum() - minimum() + pageStep()); } int valuePixels(const int length) const { if (minimum() == maximum()) { return 0; } else { return (length - pagePixels(length)) * value() / (maximum() - minimum()); } } void paintEvent(QPaintEvent *) override { QPainter painter{this}; painter.fillRect(rect(), QColor{255, 255, 255, 127}); if (orientation() == Qt::Horizontal) { painter.fillRect(QRect{ valuePixels(width()), 0, pagePixels(width()), height() }, QColor{127, 127, 127, 127}); } else if (orientation() == Qt::Vertical) { painter.fillRect(QRect{ 0, valuePixels(height()), width(), pagePixels(height()) }, QColor{127, 127, 127, 127}); } else { Q_UNREACHABLE(); } } }; class MyCorner final : public QWidget { public: explicit MyCorner(QWidget *parent) : QWidget{parent} {} private: void paintEvent(QPaintEvent *) override { QPainter painter{this}; painter.fillRect(rect(), QColor{255, 255, 255, 127}); } }; class MyContents final : public QWidget { public: explicit MyContents(QWidget *parent) : QWidget{parent} { setFixedSize(100, 100); setFocusPolicy(Qt::StrongFocus); } private: int scale = 1; void paintEvent(QPaintEvent *) override { QPainter painter{this}; painter.setPen(Qt::NoPen); painter.setBrush(Qt::darkGray); painter.drawRect(rect()); painter.setPen(QPen{Qt::red, 4.0}); painter.setBrush(Qt::NoBrush); painter.drawRect(2, 2, width() - 4, height() - 4); } void keyPressEvent(QKeyEvent *event) override { if (event->key() == Qt::Key_Z) { scale++; } else if (event->key() == Qt::Key_X) { scale = std::max(scale - 1, 1); } setFixedSize(100 * scale, 100 * scale); } }; int main(int argc, char **argv) { QApplication app{argc, argv}; QMainWindow window; window.setMinimumSize(200, 200); QScrollArea area{&window}; area.setAlignment(Qt::AlignCenter); area.setVerticalScrollBar(new MyScrollBar{Qt::Vertical, &area}); area.setHorizontalScrollBar(new MyScrollBar{Qt::Horizontal, &area}); area.setCornerWidget(new MyCorner{&area}); area.setWidget(new MyContents{&area}); window.setCentralWidget(&area); window.show(); return app.exec(); }
If you zoom in (by pressing Z) and scroll to the bottom-right corner, the red border will be covered by the scroll bars. I made the scroll bars translucent in this example so that you can see that the viewport is being covered.
I want to create opaque scrollbars that don't cover the contents of the scroll area. How can I do this?
-
-
@Kerndog73
I don't think, I'm much help here, but have you tried setting a viewport margin (of the size of your scrollbars) ?https://doc.qt.io/qt-5/qabstractscrollarea.html#setViewportMargins
-
@J.Hilk Sorry for the late reply (I didn't get an email notification). I think that's actually exactly what I was looking for! I've updated my example using
setViewportMargins
(have a look atadjustMargins
).#include <QtGui/qevent.h> #include <QtGui/qpainter.h> #include <QtWidgets/qscrollbar.h> #include <QtWidgets/qscrollarea.h> #include <QtWidgets/qmainwindow.h> #include <QtWidgets/qapplication.h> class MyScrollBar final : public QScrollBar { public: MyScrollBar(Qt::Orientation orient, QWidget *parent) : QScrollBar{orient, parent} { setStyleSheet("width: 8px; height: 8px"); } private: int pagePixels(const int length) const { return (length * pageStep()) / (maximum() - minimum() + pageStep()); } int valuePixels(const int length) const { if (minimum() == maximum()) { return 0; } else { return (length - pagePixels(length)) * value() / (maximum() - minimum()); } } void paintEvent(QPaintEvent *) override { QPainter painter{this}; painter.fillRect(rect(), QColor{255, 0, 255, 127}); if (orientation() == Qt::Horizontal) { painter.fillRect(QRect{ valuePixels(width()), 0, pagePixels(width()), height() }, QColor{0, 127, 127, 127}); } else if (orientation() == Qt::Vertical) { painter.fillRect(QRect{ 0, valuePixels(height()), width(), pagePixels(height()) }, QColor{0, 127, 127, 127}); } else { Q_UNREACHABLE(); } } }; class MyCorner final : public QWidget { public: explicit MyCorner(QWidget *parent) : QWidget{parent} {} private: void paintEvent(QPaintEvent *) override { QPainter painter{this}; painter.fillRect(rect(), QColor{255, 0, 255, 127}); } }; class MyContents final : public QWidget { Q_OBJECT public: explicit MyContents(QWidget *parent) : QWidget{parent} { setFixedSize(100, 100); setFocusPolicy(Qt::StrongFocus); } Q_SIGNALS: void resized(); private: int scale = 1; void paintEvent(QPaintEvent *) override { QPainter painter{this}; painter.setPen(Qt::NoPen); painter.setBrush(Qt::darkGray); painter.drawRect(rect()); painter.setPen(QPen{Qt::red, 4.0}); painter.setBrush(Qt::NoBrush); painter.drawRect(2, 2, width() - 4, height() - 4); } void keyPressEvent(QKeyEvent *event) override { if (event->key() == Qt::Key_Z) { scale++; } else if (event->key() == Qt::Key_X) { scale = std::max(scale - 1, 1); } setFixedSize(100 * scale, 100 * scale); } void resizeEvent(QResizeEvent *) override { Q_EMIT resized(); } }; class MyArea final : public QScrollArea { public: explicit MyArea(QWidget *parent) : QScrollArea{parent} { setAlignment(Qt::AlignCenter); setVerticalScrollBar(new MyScrollBar{Qt::Vertical, this}); setHorizontalScrollBar(new MyScrollBar{Qt::Horizontal, this}); setCornerWidget(new MyCorner{this}); auto *contents = new MyContents{this}; connect(contents, &MyContents::resized, this, &MyArea::adjustMargins); setWidget(contents); } private: void adjustMargins() { const QMargins margins = viewportMargins(); const int right = height() < widget()->height() + margins.bottom() ? 8 : 0; const int bottom = width() < widget()->width() + margins.right() ? 8 : 0; setViewportMargins(0, 0, right, bottom); } void resizeEvent(QResizeEvent *event) override { adjustMargins(); QScrollArea::resizeEvent(event); } }; int main(int argc, char **argv) { QApplication app{argc, argv}; QMainWindow window; window.setMinimumSize(200, 200); MyArea area{&window}; window.setCentralWidget(&area); window.show(); return app.exec(); }
The math inside
adjustMargins
is a bit funny and I'm not sure if it's right but it seems to work!