diff --git a/src/libRender/CMakeLists.txt b/src/libRender/CMakeLists.txt index f32cea2200a598cdbff0f1ef845b182dbf33ca90..fd86e22f9e02b7c5f20361622c7c6bb604ee98b9 100644 --- a/src/libRender/CMakeLists.txt +++ b/src/libRender/CMakeLists.txt @@ -148,6 +148,7 @@ set(gui_sources RadiumNBR/Gui/CodeEditor/SyntaxRule.cpp RadiumNBR/Gui/CodeEditor/XmlHelper.cpp RadiumNBR/Gui/GlslEditor.cpp + RadiumNBR/Gui/PowerSlider.cpp ) set(gui_public_headers @@ -165,6 +166,7 @@ set(gui_public_headers RadiumNBR/Gui/CodeEditor/SyntaxRule.hpp RadiumNBR/Gui/CodeEditor/XmlHelper.hpp RadiumNBR/Gui/GlslEditor.hpp + RadiumNBR/Gui/PowerSlider.hpp ) set(gui_headers diff --git a/src/libRender/RadiumNBR/Gui/PowerSlider.cpp b/src/libRender/RadiumNBR/Gui/PowerSlider.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fc4f6f8a1f780ba9aab51e4704b0bc830e275d2d --- /dev/null +++ b/src/libRender/RadiumNBR/Gui/PowerSlider.cpp @@ -0,0 +1,155 @@ +#include "PowerSlider.hpp" + +#include <QHBoxLayout> +#include <cmath> +#include <qslider.h> +#include <qspinbox.h> + +#include <limits> +PowerSlider::PowerSlider( QWidget* parent, double alignmentValue ) : QWidget( parent ) { + // create widget + slider_ = new QSlider( Qt::Horizontal, this ); + spinBox_ = new QDoubleSpinBox( this ); + + // style question + spinBox_->setAlignment( Qt::AlignRight ); + spinBox_->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ); + + // minimum size for "nice" alignment + spinBox_->setRange( 0, alignmentValue ); + spinBox_->setMinimumSize( QSize( spinBox_->sizeHint().width(), 0 ) ); + + // update slider range + slider_->setRange( 0, 100000 ); + + // default value + setRange( 0.0, 1.0 ); + setSingleStep( 0.1 ); + + // connect spinBox_ and slider_ + connect( + spinBox_, SIGNAL( valueChanged( double ) ), SLOT( on_spinBox_valueChanged( double ) ) ); + connect( slider_, SIGNAL( valueChanged( int ) ), SLOT( on_slider_valueChanged( int ) ) ); + + // connect to external signal + connect( spinBox_, SIGNAL( valueChanged( double ) ), SIGNAL( valueChanged( double ) ) ); + + // layout in a hbox + QHBoxLayout* layout = new QHBoxLayout; + layout->setMargin( 0 ); + layout->setSpacing( 6 ); + layout->addWidget( slider_ ); + layout->addWidget( spinBox_ ); + setLayout( layout ); +} + +PowerSlider::~PowerSlider() { + disconnect( spinBox_, 0, 0, 0 ); + disconnect( slider_, 0, 0, 0 ); + disconnect( this, 0, 0, 0 ); + + delete spinBox_; + delete slider_; + delete layout(); +} + +void PowerSlider::on_slider_valueChanged( int val ) { + // compute corresponding value in spinBox_ space + double dval = spinBox_->minimum() + double( val - slider_->minimum() ) / + double( slider_->maximum() - slider_->minimum() ) * + ( spinBox_->maximum() - spinBox_->minimum() ); + + // update if needed + if ( fabs( dval - spinBox_->value() ) >= std::numeric_limits<float>::epsilon() ) + { spinBox_->setValue( dval ); } +} + +void PowerSlider::on_spinBox_valueChanged( double val ) { + // compute corresponding value in slider_ space + int ival = + int( ( ( val - spinBox_->minimum() ) / ( spinBox_->maximum() - spinBox_->minimum() ) * + ( slider_->maximum() - slider_->minimum() ) ) ); + + // update if needed + if ( ival != slider_->value() ) { slider_->setValue( ival ); } +} + +void PowerSlider::setRange( double min, double max ) { + int decimals = std::max( int( round( std::abs( log( min - floor( min ) ) / log( 10 ) ) ) ), + int( round( std::abs( log( max - floor( max ) ) / log( 10 ) ) ) ) ) + + 1; + spinBox_->setDecimals( decimals ); + spinBox_->setRange( min, max ); + + setSingleStep( std::min( spinBox_->singleStep(), ( max - min ) / 10.0 ) ); +} + +void PowerSlider::setMinimum( double min ) { + double max = spinBox_->maximum(); + if ( min > max ) { max = min; } + setRange( min, max ); +} + +void PowerSlider::setMaximum( double max ) { + double min = spinBox_->minimum(); + if ( max < min ) { min = max; } + setRange( min, max ); +} + +void PowerSlider::setSingleStep( double step ) { + + int decimals = int( round( std::abs( log( step - floor( step ) ) / log( 10 ) ) ) ) + 1; + spinBox_->setDecimals( decimals ); + + // set spinBox_ step + spinBox_->setSingleStep( step ); + + // set slider step with the same relative value + slider_->setSingleStep( int( step / ( spinBox_->maximum() - spinBox_->minimum() ) * + double( slider_->maximum() - slider_->minimum() ) ) ); +} + +void PowerSlider::setValue( double dval ) { + if ( fabs( dval - spinBox_->value() ) >= std::numeric_limits<float>::epsilon() ) + { spinBox_->setValue( dval ); } +} +#ifdef ENABLE_QDOM +QDomElement PowerSlider::domElement( const QString& name, QDomDocument& document ) const { + QDomElement de = document.createElement( name ); + de.setAttribute( "value", QString::number( spinBox_->value() ) ); + return de; +} + +void PowerSlider::initFromDOMElement( const QDomElement& e ) { + float value = spinBox_->minimum(); + if ( e.hasAttribute( "value" ) ) + { + const QString s = e.attribute( "value" ); + bool ok; + s.toFloat( &ok ); + if ( ok ) + value = s.toFloat(); + else + { // throw error ?? + } + } + else + { + // throw error ?? + } + setValue( value ); +} +#endif +double PowerSlider::value() { + return spinBox_->value(); +} + +double PowerSlider::minimum() { + return spinBox_->minimum(); +} +double PowerSlider::maximum() { + return spinBox_->maximum(); +} +double PowerSlider::singleStep() { + return spinBox_->singleStep(); +} diff --git a/src/libRender/RadiumNBR/Gui/PowerSlider.hpp b/src/libRender/RadiumNBR/Gui/PowerSlider.hpp new file mode 100644 index 0000000000000000000000000000000000000000..737003a56be0e288cf43ec0f921b7ac9d8eaa559 --- /dev/null +++ b/src/libRender/RadiumNBR/Gui/PowerSlider.hpp @@ -0,0 +1,55 @@ +#ifndef __POWERSLIDER_H__ +#define __POWERSLIDER_H__ + +#ifdef ENABLE_QDOM +# include <QDomElement> +#endif +#include <QWidget> + +#ifdef POWERSLIDER_DESIGNER_PLUGIN +# include <QtUiPlugin/QDesignerExportWidget> +#else +# define QDESIGNER_WIDGET_EXPORT +#endif + +class QSlider; +class QDoubleSpinBox; + +class QDESIGNER_WIDGET_EXPORT PowerSlider : public QWidget +{ + Q_OBJECT; + Q_PROPERTY( double Min READ minimum WRITE setMinimum ); + Q_PROPERTY( double Max READ maximum WRITE setMaximum ); + Q_PROPERTY( double Value READ value WRITE setValue ); + Q_PROPERTY( double SingleStep READ singleStep WRITE setSingleStep ); + + public: + PowerSlider( QWidget* parent = 0, double alignmentValue = 100.0 ); + ~PowerSlider(); +#ifdef ENABLE_QDOM + QDomElement domElement( const QString& name, QDomDocument& document ) const; + void initFromDOMElement( const QDomElement& element ); +#endif + double value(); + double minimum(); + double maximum(); + double singleStep(); + public slots: + void setSingleStep( double ); + void setRange( double, double ); + void setMinimum( double ); + void setMaximum( double ); + + void setValue( double ); + signals: + void valueChanged( double ); + + private: + QSlider* slider_; + QDoubleSpinBox* spinBox_; + private slots: + void on_slider_valueChanged( int ); + void on_spinBox_valueChanged( double ); +}; + +#endif // __POWERSLIDER_H__ diff --git a/src/libRender/RadiumNBR/Gui/RendererPanel.cpp b/src/libRender/RadiumNBR/Gui/RendererPanel.cpp index 36d7a71b271d5a616fac578c4c1ca741da35476f..7fb47934a865ea97775ce65186eeee304b68b398 100644 --- a/src/libRender/RadiumNBR/Gui/RendererPanel.cpp +++ b/src/libRender/RadiumNBR/Gui/RendererPanel.cpp @@ -1,6 +1,7 @@ #include <RadiumNBR/Gui/RendererPanel.hpp> #include <RadiumNBR/Gui/GlslEditor.hpp> +#include <RadiumNBR/Gui/PowerSlider.hpp> #include <QLabel> #include <QVBoxLayout> @@ -80,6 +81,24 @@ void RendererPanel::addSliderInput( const std::string& name, connect( inputField, &QSlider::valueChanged, std::move( callback ) ); m_currentLayout->addLayout( inputLayout ); } + +void RendererPanel::addPowerSliderInput( const std::string& name, + std::function<void( double )> callback, + double initial, + double min, + double max ) { + auto inputLayout = new QHBoxLayout(); + auto inputLabel = new QLabel( tr( name.c_str() ), this ); + auto inputField = new PowerSlider(); + inputField->setValue( initial ); + inputField->setRange( min, max ); + inputField->setSingleStep( 0.01 ); + inputLayout->addWidget( inputLabel ); + inputLayout->addWidget( inputField ); + connect( inputField, &PowerSlider::valueChanged, std::move( callback ) ); + m_currentLayout->addLayout( inputLayout ); +} + void RendererPanel::addColorInput( const std::string& name, const std::function<void( const Ra::Core::Utils::Color& clr )>& callback, @@ -205,10 +224,9 @@ void RendererPanel::addCodeEditor( const std::string& name, connect( buttonBox, &QDialogButtonBox::accepted, [callback, textEditor]() { callback( textEditor->toPlainText().toStdString() ); } ); - connect( - buttonBox, &QDialogButtonBox::rejected, [callback, &dialog, initialText]() mutable { - dialog->hide(); - } ); + connect( buttonBox, + &QDialogButtonBox::rejected, + [callback, &dialog, initialText]() mutable { dialog->hide(); } ); QVBoxLayout* mainLayout = new QVBoxLayout; mainLayout->addWidget( textEditor ); mainLayout->addWidget( buttonBox ); diff --git a/src/libRender/RadiumNBR/Gui/RendererPanel.hpp b/src/libRender/RadiumNBR/Gui/RendererPanel.hpp index 59962dc2913796fcf2e4a809ad236055a3adb74f..8a1f0fc9acae1311eeed2e4065602b38b3db6ee0 100644 --- a/src/libRender/RadiumNBR/Gui/RendererPanel.hpp +++ b/src/libRender/RadiumNBR/Gui/RendererPanel.hpp @@ -112,6 +112,21 @@ class NodeBasedRenderer_LIBRARY_API RendererPanel : public QFrame int min = 0, int max = 100 ); + /** Add an horizontal power slider input. + * This input will return a scalar value within given bounds. + * @param name The name of the scalar value + * @param callback The function to call when the state changed + * @param initial The initial value of the color + * @param min The min bound of the value + * @param max The max bound of the value + * @param dec The display precision (decimals) of the value + */ + void addPowerSliderInput( const std::string& name, + std::function<void( double )> callback, + double initial = 0, + double min = 0, + double max = 100 ); + /** Add a file dialog to the ui. * Allow the user to select a file to load according to given filters * @param name The name of the file property to expose