Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to create an opaque scrollbar

How to create an opaque scrollbar

Scheduled Pinned Locked Moved Solved General and Desktop
4 Posts 2 Posters 651 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K Offline
    K Offline
    Kerndog73
    wrote on last edited by Kerndog73
    #1

    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 use QAbstractScrollArea 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.

    0_1559434488768_Screen Shot 2019-06-02 at 09.43.54.png

    I want to create opaque scrollbars that don't cover the contents of the scroll area. How can I do this?

    1 Reply Last reply
    0
    • K Offline
      K Offline
      Kerndog73
      wrote on last edited by
      #2

      I really feel like this is should be really easy to do. I shouldn't have to inherit from QAbstractScrollArea and do a whole lot of math. In the next couple of days, that's what I'll probably have to do unless someone comes up with a better solution.

      J.HilkJ 1 Reply Last reply
      0
      • K Kerndog73

        I really feel like this is should be really easy to do. I shouldn't have to inherit from QAbstractScrollArea and do a whole lot of math. In the next couple of days, that's what I'll probably have to do unless someone comes up with a better solution.

        J.HilkJ Online
        J.HilkJ Online
        J.Hilk
        Moderators
        wrote on last edited by
        #3

        @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


        Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


        Q: What's that?
        A: It's blue light.
        Q: What does it do?
        A: It turns blue.

        K 1 Reply Last reply
        0
        • J.HilkJ J.Hilk

          @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

          K Offline
          K Offline
          Kerndog73
          wrote on last edited by Kerndog73
          #4

          @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 at adjustMargins).

          #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!

          1 Reply Last reply
          2

          • Login

          • Login or register to search.
          • First post
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • Users
          • Groups
          • Search
          • Get Qt Extensions
          • Unsolved