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. Improved slider code review
Forum Updated to NodeBB v4.3 + New Features

Improved slider code review

Scheduled Pinned Locked Moved Solved General and Desktop
8 Posts 2 Posters 2.3k 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.
  • UnitScanU Offline
    UnitScanU Offline
    UnitScan
    wrote on last edited by UnitScan
    #1

    By default, QSlider move his thumbtrack by a value belonging to the pageStep() prop on mouse click. To make thumbtrack jump directly at the mouse click point, we need to create a new class inherited by QSlider.

    Header file (.h): http://www.unitscan.org/qimprovedslider-h
    Source file (.cpp): http://www.unitscan.org/qimprovedslider-cpp

    When thumbtrack jump directly at mouse click, onClick(int value) signal are fired: you can bind function through connect(widget, signal, class, slot) method.

    This code works well, so is not a broken code. There is a way to improve this? Maybe trying to differentiate the direct jump from the slider handle dragging

    1 Reply Last reply
    0
    • Chris KawaC Offline
      Chris KawaC Offline
      Chris Kawa
      Lifetime Qt Champion
      wrote on last edited by Chris Kawa
      #2

      Haven't actually run the code, but here's my review just by the looks of it:
      The header:

      • It would be nice to provide the same set of constructors the base class has, i.e. add one with orientation parameter.
      • The only include you need in the header is for QSlider. Move all the others to the .cpp. For QMouseEvent it's enough to just put forward declaration in the header.
      • The whole point of include guards (the QIMPROVEDSLIDER_H macro) is that stuff doesn't get included twice. Move that to the top of your header and put includes under it.
      • onWHATEVER is a very unQtish naming convention. Signals mean something happened with the object, so instead of onClick() a better name would be clicked(). Make the signal const while you're at it, so you can emit it from const member functions if you need to.
      • Use override keyword when overriding (e.g. mousePressEvent). Saves you from typo errors misery.
      • If you don't need to do anything in the destructor don't implement it. There's no point to it.

      The implementation:

      • Stealing focus in an enter event is a very unpolite thing to do. Imagine someone types something in a text input widget and just moves the mouse out of the way over your widget. Now he can't type. Or someone just passes over your widget without any intention to use it and you steal the focus anyway. Very user unfriendly.
      • Similarly setSliderDown in an enter event is a very weird thing to do. Imagine a button that pressed itself when you hover over it. that's just totally unexpected.
      • A "click" is a mouse press and release. You should not emit clicked signal from a press event. Only from the release event on the condition that the press has also taken place over your widget. A user can press a button over your widget and move away while holding it. That also should not emit a clicked signal.
      UnitScanU 1 Reply Last reply
      2
      • Chris KawaC Chris Kawa

        Haven't actually run the code, but here's my review just by the looks of it:
        The header:

        • It would be nice to provide the same set of constructors the base class has, i.e. add one with orientation parameter.
        • The only include you need in the header is for QSlider. Move all the others to the .cpp. For QMouseEvent it's enough to just put forward declaration in the header.
        • The whole point of include guards (the QIMPROVEDSLIDER_H macro) is that stuff doesn't get included twice. Move that to the top of your header and put includes under it.
        • onWHATEVER is a very unQtish naming convention. Signals mean something happened with the object, so instead of onClick() a better name would be clicked(). Make the signal const while you're at it, so you can emit it from const member functions if you need to.
        • Use override keyword when overriding (e.g. mousePressEvent). Saves you from typo errors misery.
        • If you don't need to do anything in the destructor don't implement it. There's no point to it.

        The implementation:

        • Stealing focus in an enter event is a very unpolite thing to do. Imagine someone types something in a text input widget and just moves the mouse out of the way over your widget. Now he can't type. Or someone just passes over your widget without any intention to use it and you steal the focus anyway. Very user unfriendly.
        • Similarly setSliderDown in an enter event is a very weird thing to do. Imagine a button that pressed itself when you hover over it. that's just totally unexpected.
        • A "click" is a mouse press and release. You should not emit clicked signal from a press event. Only from the release event on the condition that the press has also taken place over your widget. A user can press a button over your widget and move away while holding it. That also should not emit a clicked signal.
        UnitScanU Offline
        UnitScanU Offline
        UnitScan
        wrote on last edited by
        #3

        @Chris-Kawa said in Improved slider code review:

        • A "click" is a mouse press and release. You should not emit clicked signal from a press event. Only from the release event on the condition that the press has also taken place over your widget. A user can press a button over your widget and move away while holding it. That also should not emit a clicked signal.

        So I should take a look to http://doc.qt.io/qt-5/qslider.html#mouseReleaseEvent?

        Chris KawaC 1 Reply Last reply
        0
        • UnitScanU UnitScan

          @Chris-Kawa said in Improved slider code review:

          • A "click" is a mouse press and release. You should not emit clicked signal from a press event. Only from the release event on the condition that the press has also taken place over your widget. A user can press a button over your widget and move away while holding it. That also should not emit a clicked signal.

          So I should take a look to http://doc.qt.io/qt-5/qslider.html#mouseReleaseEvent?

          Chris KawaC Offline
          Chris KawaC Offline
          Chris Kawa
          Lifetime Qt Champion
          wrote on last edited by
          #4

          Yup, make the press and release work together.

          1 Reply Last reply
          0
          • UnitScanU Offline
            UnitScanU Offline
            UnitScan
            wrote on last edited by UnitScan
            #5

            .h

            #include <QSlider>
            
            #ifndef QIMPROVEDSLIDER_H
            #define QIMPROVEDSLIDER_H
            
            class QImprovedSlider : public QSlider
            {
                Q_OBJECT
            protected:
                void mousePressEvent(QMouseEvent *event) override;
                void mouseReleaseEvent(QMouseEvent *event) override;
            public:
                explicit QImprovedSlider(QWidget *parent = 0);
            
            public slots:
            
            private:
            private slots:
            
            signals:
                void clicked(int value);
            
            };
            
            #endif // QIMPROVEDSLIDER_H
            

            .cpp

            void QImprovedSlider::mousePressEvent(QMouseEvent *event) {
                QStyleOptionSlider opt;
                initStyleOption(&opt);
                QRect sr = style()->subControlRect(QStyle::CC_Slider,
                                                   &opt,
                                                   QStyle::SC_SliderHandle,
                                                   this);
                if (event->button() == Qt::LeftButton &&
                        !sr.contains(event->pos())) {
                    int newVal;
                    if (orientation() == Qt::Vertical) {
                        double halfHandleHeight = (0.5 * sr.height()) + 0.5;
                        int adaptedPosY = height() - event->y();
                        if (adaptedPosY < halfHandleHeight) {
                            adaptedPosY = halfHandleHeight;
                        }
                        if (adaptedPosY > height() - halfHandleHeight) {
                            adaptedPosY = height() - halfHandleHeight;
                        }
                        double newHeight = (height() - halfHandleHeight) - halfHandleHeight;
                        double normalizedPosition = (adaptedPosY - halfHandleHeight)  / newHeight ;
                        newVal = minimum() + (maximum()-minimum()) * normalizedPosition;
                    }
                    else {
                        double halfHandleWidth = (0.5 * sr.width());
                        int adaptedPosX = event->x();
                        if (adaptedPosX < halfHandleWidth) {
                            adaptedPosX = halfHandleWidth;
                        }
                        if (adaptedPosX > width() - halfHandleWidth) {
                            adaptedPosX = width() - halfHandleWidth;
                        }
                        double newWidth = (width() - halfHandleWidth) - halfHandleWidth;
                        double normalizedPosition = (adaptedPosX - halfHandleWidth)  / newWidth ;
                        newVal = minimum() + ((maximum()-minimum()) * normalizedPosition);
                    }
                    if (invertedAppearance() == true) {
                        this->setValue( maximum() - newVal );
                        }
                    else {
                        this->setValue(newVal);
                        }
                    event->accept();
                    //QSlider::mouseReleaseEvent(QMouseEvent::MouseButtonRelease);
                }
                else {
                    QSlider::mousePressEvent(event);
                }
            }
            
            void QImprovedSlider::mouseReleaseEvent(QMouseEvent *event) {
                //press and release work together?
                setSliderDown(true);
                emit clicked(value());
                setSliderDown(false);
            }
            
            1 Reply Last reply
            0
            • Chris KawaC Offline
              Chris KawaC Offline
              Chris Kawa
              Lifetime Qt Champion
              wrote on last edited by Chris Kawa
              #6

              setSliderDown(true) means the handle is drawn as pressed. It should go in the press event. Calling it with true and false in the same function is pointless, as there is no event loop processing between them so no drawing.
              You should also call the base implementation (QSlider::mouseReleaseEvent(event)) in your override, because you might have just removed some built-in functionality.

              Other nitpicks:

              • as I mentioned earlier: includes go under the include guards:
              #ifndef QIMPROVEDSLIDER_H
              #define QIMPROVEDSLIDER_H
              #include <QSlider>
              
              • These do nothing if you don't put methods under them so just remove them:
              public slots:
              private:
              private slots:
              
              • As mentioned before, make the signal const. It's not mandatory but a good practice:
              signals:
                  void clicked(int value) const;
              
              1 Reply Last reply
              3
              • UnitScanU Offline
                UnitScanU Offline
                UnitScan
                wrote on last edited by
                #7
                void QImprovedSlider::mousePressEvent(QMouseEvent *event) {
                    QStyleOptionSlider opt;
                    initStyleOption(&opt);
                    QRect sr = style()->subControlRect(QStyle::CC_Slider,
                                                       &opt,
                                                       QStyle::SC_SliderHandle,
                                                       this);
                    if (event->button() == Qt::LeftButton &&
                            !sr.contains(event->pos())) {
                        int newVal;
                        if (orientation() == Qt::Vertical) {
                            double halfHandleHeight = (0.5 * sr.height()) + 0.5;
                            int adaptedPosY = height() - event->y();
                            if (adaptedPosY < halfHandleHeight) {
                                adaptedPosY = halfHandleHeight;
                            }
                            if (adaptedPosY > height() - halfHandleHeight) {
                                adaptedPosY = height() - halfHandleHeight;
                            }
                            double newHeight = (height() - halfHandleHeight) - halfHandleHeight;
                            double normalizedPosition = (adaptedPosY - halfHandleHeight)  / newHeight ;
                            newVal = minimum() + (maximum()-minimum()) * normalizedPosition;
                        }
                        else {
                            double halfHandleWidth = (0.5 * sr.width());
                            int adaptedPosX = event->x();
                            if (adaptedPosX < halfHandleWidth) {
                                adaptedPosX = halfHandleWidth;
                            }
                            if (adaptedPosX > width() - halfHandleWidth) {
                                adaptedPosX = width() - halfHandleWidth;
                            }
                            double newWidth = (width() - halfHandleWidth) - halfHandleWidth;
                            double normalizedPosition = (adaptedPosX - halfHandleWidth)  / newWidth ;
                            newVal = minimum() + ((maximum()-minimum()) * normalizedPosition);
                        }
                        if (invertedAppearance() == true) {
                            this->setValue( maximum() - newVal );
                        }
                        else {
                            this->setValue(newVal);
                        }
                        event->accept();
                        setSliderDown(true);
                    }
                    else {
                        QSlider::mousePressEvent(event);
                    }
                }
                
                void QImprovedSlider::mouseReleaseEvent(QMouseEvent *event) {
                    if (isSliderDown()) { emit clicked(value()); }
                    setSliderDown(false);
                    QSlider::mouseReleaseEvent(event);
                }
                
                1 Reply Last reply
                0
                • Chris KawaC Offline
                  Chris KawaC Offline
                  Chris Kawa
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  Right, so now I've actually run the code and it doesn't behave really well.
                  When you press and hold the mouse it doesn't really highligh the handle and you can't drag it around unless you release and press it again. That's not good.
                  All in all I would stop trying to control the slider state manually with setSliderDown and just let the default handlers do their work i.e.:

                  void QImprovedSlider::mouseReleaseEvent(QMouseEvent *event) {
                      if (isSliderDown()) //just take care of emitting signal and leave the rest to default event handler
                          emit clicked(value());
                  
                      QSlider::mouseReleaseEvent(event);
                  }
                  

                  same in the press event:

                      ...
                          else {
                              this->setValue(newVal);
                          }
                          //event->accept(); //don't try to control the state, just move to new position
                          //setSliderDown(true);
                      }
                      //else { //let the default handler do its work always
                      QSlider::mousePressEvent(event);
                      //}
                  }
                  

                  This seems to behave a lot nicer i.e. highlights correctly and lets you drag the handle immediately after the press without releasing first.

                  1 Reply Last reply
                  3

                  • Login

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