Browse Source

ENH: Add symmetric moves support for range sliders

When symmetricMoves is true, moving a handle will move the other handle
symmetrically, otherwise the handles are independent. False by default
Julien Finet 14 years ago
parent
commit
bc08554b0f

+ 16 - 1
Libs/Widgets/Testing/Cpp/ctkDoubleRangeSliderTest1.cpp

@@ -21,6 +21,7 @@
 // Qt includes
 #include <QApplication>
 #include <QString>
+#include <QTimer>
 
 // CTK includes
 #include "ctkDoubleRangeSlider.h"
@@ -293,6 +294,20 @@ int ctkDoubleRangeSliderTest1(int argc, char * argv [] )
   //             << slider.maximumPosition() << std::endl;
   //   return EXIT_FAILURE;
   //   }
+  slider.setSymmetricMoves(true);
+  
+  if (slider.symmetricMoves() != true)
+    {
+    std::cerr << "ctkDoubleRangeSlider::setSymmetricMoves failed" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  slider.show();
+
+  if (argc < 2 || QString(argv[1]) != "-I" )
+    {
+    QTimer::singleShot(200, &app, SLOT(quit()));
+    }
 
-  return EXIT_SUCCESS;
+  return app.exec();
 }

+ 17 - 1
Libs/Widgets/Testing/Cpp/ctkRangeSliderTest1.cpp

@@ -21,6 +21,7 @@
 // Qt includes
 #include <QApplication>
 #include <QString>
+#include <QTimer>
 
 // CTK includes
 #include "ctkRangeSlider.h"
@@ -249,5 +250,20 @@ int ctkRangeSliderTest1(int argc, char * argv [] )
     return EXIT_FAILURE;
     }
 
-  return EXIT_SUCCESS;
+  slider.setSymmetricMoves(true);
+  
+  if (slider.symmetricMoves() != true)
+    {
+    std::cerr << "ctkRangeSlider::setSymmetricMoves failed" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  slider.show();
+
+  if (argc < 2 || QString(argv[1]) != "-I" )
+    {
+    QTimer::singleShot(200, &app, SLOT(quit()));
+    }
+
+  return app.exec();
 }

+ 22 - 1
Libs/Widgets/Testing/Cpp/ctkRangeWidgetTest1.cpp

@@ -20,6 +20,7 @@
 
 // Qt includes
 #include <QApplication>
+#include <QTimer>
 
 // CTK includes
 #include "ctkRangeWidget.h"
@@ -129,6 +130,7 @@ int ctkRangeWidgetTest1(int argc, char * argv [] )
               << sliderSpinBox.maximum() << std::endl;
     return EXIT_FAILURE;
     }
+
   std::cout << "3" << std::endl;
   sliderSpinBox.setMaximumValue(77.777);
 
@@ -167,6 +169,7 @@ int ctkRangeWidgetTest1(int argc, char * argv [] )
               << sliderSpinBox.maximum() << std::endl;
     return EXIT_FAILURE;
     }
+
   std::cout << "6" << std::endl;
   //sliderSpinBox.reset();
   sliderSpinBox.setSpinBoxAlignment(Qt::AlignRight);
@@ -181,6 +184,7 @@ int ctkRangeWidgetTest1(int argc, char * argv [] )
               << sliderSpinBox.maximum() << std::endl;
     return EXIT_FAILURE;
     }
+
   std::cout << "7" << std::endl;
   sliderSpinBox.setAutoSpinBoxWidth(false);
 
@@ -194,6 +198,7 @@ int ctkRangeWidgetTest1(int argc, char * argv [] )
               << sliderSpinBox.maximum() << std::endl;
     return EXIT_FAILURE;
     }
+
   std::cout << "8" << std::endl;
   sliderSpinBox.setPrefix("$");
 
@@ -233,7 +238,23 @@ int ctkRangeWidgetTest1(int argc, char * argv [] )
     return EXIT_FAILURE;
     }
 
+  sliderSpinBox.setSymmetricMoves(true);
+  
+  if (sliderSpinBox.symmetricMoves() != true)
+    {
+    std::cerr << "ctkRangeWidget::setSymmetricMoves failed" << std::endl;
+    return EXIT_FAILURE;
+    }
   // FIXME check that the correct signals are sent.
-  return EXIT_SUCCESS;
+
+  sliderSpinBox.show();
+
+  if (argc < 2 || QString(argv[1]) != "-I" )
+    {
+    QTimer::singleShot(200, &app, SLOT(quit()));
+    }
+
+  return app.exec();
+
 }
 

+ 14 - 0
Libs/Widgets/ctkDoubleRangeSlider.cpp

@@ -496,6 +496,20 @@ Qt::Orientation ctkDoubleRangeSlider::orientation()const
 }
 
 // --------------------------------------------------------------------------
+bool ctkDoubleRangeSlider::symmetricMoves()const
+{
+  Q_D(const ctkDoubleRangeSlider);
+  return d->Slider->symmetricMoves();
+}
+
+// --------------------------------------------------------------------------
+void ctkDoubleRangeSlider::setSymmetricMoves(bool symmetry)
+{
+  Q_D(ctkDoubleRangeSlider);
+  d->Slider->setSymmetricMoves(symmetry);
+}
+
+// --------------------------------------------------------------------------
 void ctkDoubleRangeSlider::onMinValueChanged(int newValue)
 {
   Q_D(ctkDoubleRangeSlider);

+ 8 - 0
Libs/Widgets/ctkDoubleRangeSlider.h

@@ -49,6 +49,7 @@ class CTK_WIDGETS_EXPORT ctkDoubleRangeSlider : public QWidget
   Q_PROPERTY(bool tracking READ hasTracking WRITE setTracking)
   Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation)
   Q_PROPERTY(double tickInterval READ tickInterval WRITE setTickInterval)
+  Q_PROPERTY(bool symmetricMoves READ symmetricMoves WRITE setSymmetricMoves)
 public:
   // Superclass typedef
   typedef QWidget Superclass;
@@ -152,6 +153,13 @@ public:
   /// The orientation must be Qt::Vertical (the default) or Qt::Horizontal.
   Qt::Orientation orientation()const;
   void setOrientation(Qt::Orientation orientation);
+
+  ///
+  /// When symmetricMoves is true, moving a handle will move the other handle
+  /// symmetrically, otherwise the handles are independent. False by default
+  bool symmetricMoves()const; 
+  void setSymmetricMoves(bool symmetry);
+
 signals:
   ///
   /// This signal is emitted when the slider minimum value has changed, 

+ 46 - 24
Libs/Widgets/ctkRangeSlider.cpp

@@ -38,46 +38,40 @@ protected:
 public:
   ctkRangeSliderPrivate(ctkRangeSlider& object);
   void init();
-  
-  // Description:
-  // Copied verbatim from QSliderPrivate class (see QSlider.cpp)
+
+  /// Copied verbatim from QSliderPrivate class (see QSlider.cpp)
   int pixelPosToRangeValue(int pos) const;
   int pixelPosFromRangeValue(int val) const;
 
-  // Description:
-  // Draw the bottom and top sliders.
+  /// Draw the bottom and top sliders.
   void drawMinimumSlider( QStylePainter* painter ) const;
   void drawMaximumSlider( QStylePainter* painter ) const;
     
-  // Description:
-  // End points of the range on the Model
+  /// End points of the range on the Model
   int m_MaximumValue;
   int m_MinimumValue;
 
-  // Description:
-  // End points of the range on the GUI. This is synced with the model.
+  /// End points of the range on the GUI. This is synced with the model.
   int m_MaximumPosition;
   int m_MinimumPosition;
 
-  // Description:
-  // Controls selected ?
+  /// Controls selected ?
   QStyle::SubControl m_MinimumSliderSelected;
   QStyle::SubControl m_MaximumSliderSelected;
 
-  // Description:
-  // See QSliderPrivate::clickOffset. 
-  // Overrides this ivar
+  /// See QSliderPrivate::clickOffset. 
+  /// Overrides this ivar
   int m_SubclassClickOffset;
     
-  // Description:
-  // See QSliderPrivate::position
-  // Overrides this ivar.
+  /// See QSliderPrivate::position
+  /// Overrides this ivar.
   int m_SubclassPosition;
-
+  
+  /// Original width between the 2 bounds before any moves
   int m_SubclassWidth;
-  // Description:
-  // Boolean indicates the selected handle
-  //   True for the minimum range handle, false for the maximum range handle
+  
+  /// Boolean indicates the selected handle
+  ///   True for the minimum range handle, false for the maximum range handle
   enum Handle {
     NoHandle = 0x0,
     MinimumHandle = 0x1,
@@ -85,6 +79,10 @@ public:
   };
   Q_DECLARE_FLAGS(Handles, Handle);
   ctkRangeSliderPrivate::Handles m_SelectedHandles;
+
+  /// When symmetricMoves is true, moving a handle will move the other handle
+  /// symmetrically, otherwise the handles are independent.
+  bool m_SymmetricMoves;
 };
 
 // --------------------------------------------------------------------------
@@ -101,6 +99,7 @@ ctkRangeSliderPrivate::ctkRangeSliderPrivate(ctkRangeSlider& object)
   this->m_SubclassPosition = 0;
   this->m_SubclassWidth = 0;
   this->m_SelectedHandles = 0;
+  this->m_SymmetricMoves = false;
 }
 
 // --------------------------------------------------------------------------
@@ -429,6 +428,20 @@ void ctkRangeSlider::setPositions(int min, int max)
 }
 
 // --------------------------------------------------------------------------
+void ctkRangeSlider::setSymmetricMoves(bool symmetry)
+{
+  Q_D(ctkRangeSlider);
+  d->m_SymmetricMoves = symmetry;
+}
+
+// --------------------------------------------------------------------------
+bool ctkRangeSlider::symmetricMoves()const
+{
+  Q_D(const ctkRangeSlider);
+  return d->m_SymmetricMoves;
+}
+
+// --------------------------------------------------------------------------
 void ctkRangeSlider::onRangeChanged(int minimum, int maximum)
 {
   Q_UNUSED(minimum);
@@ -683,18 +696,27 @@ void ctkRangeSlider::mouseMoveEvent(QMouseEvent* mouseEvent)
       }
     }
 
+  // The lower/left slider is down
   if (d->m_SelectedHandles == ctkRangeSliderPrivate::MinimumHandle)
     {
-    this->setMinimumPosition(qMin(newPosition,d->m_MaximumPosition));
+    double newMinPos = qMin(newPosition,d->m_MaximumPosition);
+    this->setPositions(newMinPos, d->m_MaximumPosition +
+      (d->m_SymmetricMoves ? d->m_MinimumPosition - newMinPos : 0));
     }
+  // The upper/right slider is down
   else if (d->m_SelectedHandles == ctkRangeSliderPrivate::MaximumHandle)
     {
-    this->setMaximumPosition(qMax(d->m_MinimumPosition, newPosition));
+    double newMaxPos = qMax(d->m_MinimumPosition, newPosition);
+    this->setPositions(d->m_MinimumPosition -
+      (d->m_SymmetricMoves ? newMaxPos - d->m_MaximumPosition: 0),
+      newMaxPos);
     }
+  // Both handles are down (the user clicked in between the handles)
   else if (d->m_SelectedHandles & ctkRangeSliderPrivate::MinimumHandle && 
            d->m_SelectedHandles & ctkRangeSliderPrivate::MaximumHandle)
     {
-    this->setPositions(newPosition - d->m_SubclassWidth, newPosition + d->m_SubclassWidth );
+    this->setPositions(newPosition - d->m_SubclassWidth,
+                       newPosition + d->m_SubclassWidth );
     }
   mouseEvent->accept();
 }

+ 8 - 1
Libs/Widgets/ctkRangeSlider.h

@@ -53,6 +53,7 @@ class CTK_WIDGETS_EXPORT ctkRangeSlider : public QSlider
   Q_PROPERTY(int maximumValue READ maximumValue WRITE setMaximumValue)
   Q_PROPERTY(int minimumPosition READ minimumPosition WRITE setMinimumPosition)
   Q_PROPERTY(int maximumPosition READ maximumPosition WRITE setMaximumPosition)
+  Q_PROPERTY(bool symmetricMoves READ symmetricMoves WRITE setSymmetricMoves)
 
 public:
   // Superclass typedef
@@ -94,7 +95,13 @@ public:
   /// Utility function that set the minimum position and
   /// maximum position at once.
   void setPositions(int min, int max);
-
+  
+  ///
+  /// When symmetricMoves is true, moving a handle will move the other handle
+  /// symmetrically, otherwise the handles are independent. False by default
+  bool symmetricMoves()const; 
+  void setSymmetricMoves(bool symmetry);
+  
 signals:
   ///
   /// This signal is emitted when the slider minimum value has changed, 

+ 14 - 0
Libs/Widgets/ctkRangeWidget.cpp

@@ -656,6 +656,20 @@ void ctkRangeWidget::setAutoSpinBoxWidth(bool autoWidth)
   d->updateSpinBoxWidth();
 }
 
+// --------------------------------------------------------------------------
+bool ctkRangeWidget::symmetricMoves()const
+{
+  Q_D(const ctkRangeWidget);
+  return d->Slider->symmetricMoves();
+}
+
+// --------------------------------------------------------------------------
+void ctkRangeWidget::setSymmetricMoves(bool symmetry)
+{
+  Q_D(ctkRangeWidget);
+  d->Slider->setSymmetricMoves(symmetry);
+}
+
 // -------------------------------------------------------------------------
 ctkDoubleRangeSlider* ctkRangeWidget::slider()const
 {

+ 7 - 0
Libs/Widgets/ctkRangeWidget.h

@@ -52,6 +52,7 @@ class CTK_WIDGETS_EXPORT ctkRangeWidget : public QWidget
   Q_PROPERTY(Qt::Alignment spinBoxTextAlignment READ spinBoxTextAlignment WRITE setSpinBoxTextAlignment)
   Q_PROPERTY(Qt::Alignment spinBoxAlignment READ spinBoxAlignment WRITE setSpinBoxAlignment)
   Q_PROPERTY(bool tracking READ hasTracking WRITE setTracking)
+  Q_PROPERTY(bool symmetricMoves READ symmetricMoves WRITE setSymmetricMoves)
 
 public:
   /// Superclass typedef
@@ -170,6 +171,12 @@ public:
   // ctkRangeWidget siblings.
   bool isAutoSpinBoxWidth()const;
   void setAutoSpinBoxWidth(bool autoWidth);
+  
+  ///
+  /// When symmetricMoves is true, moving a handle will move the other handle
+  /// symmetrically, otherwise the handles are independent. False by default
+  bool symmetricMoves()const;
+  void setSymmetricMoves(bool symmetry);
 
 public slots:
   ///