/* * (c) LSIIT, UMR CNRS/UdS * Authors: F. Larue. * * See licence.txt for additional information. */ #include "GradientWidget.h" #include GradientWidget::GradientWidget( QWidget *parent ) : QWidget(parent) { setMinimumSize( 100, 20 ); m_BelowCursor = -1; m_SelectedStop = -1; setMouseTracking( true ); setFocusPolicy( Qt::ClickFocus ); } GradientWidget::GradientWidget( const QColor& start, const QColor& end, QWidget *parent ) : GradientWidget(parent) { addStop( start, 0.0f ); addStop( end , 1.0f ); } GradientWidget::GradientWidget( const QList& colors, const QList& values, QWidget *parent ) : GradientWidget(parent) { setStops( colors, values ); } void GradientWidget::resizeEvent( QResizeEvent *evt ) { for( auto &s : m_Stops ) s.updateX( width()-1 ); } void GradientWidget::paintEvent( QPaintEvent *evt ) { if( !isValid() ) return; const int w = width(); const int h = height(); QColor color = this->palette().windowText().color(); QPainter painter; painter.begin( this ); // Display gradients between every stop pair. painter.setPen( Qt::NoPen ); if( m_Stops.front().value() != 0.0f ) { painter.setBrush( QBrush(m_Stops.front().color()) ); painter.drawRect( 0, 3, m_Stops.front().x(), h-6 ); } if( m_Stops.back().value() != 1.0f ) { painter.setBrush( QBrush(m_Stops.back().color()) ); painter.drawRect( m_Stops.back().x(), 3, w-m_Stops.back().x(), h-6 ); } for( int i=0; i= 0 ) { QString text = m_KeyboardInputValue.isEmpty()? QString::number(m_Stops[m_SelectedStop].value()) : m_KeyboardInputValue; QFontMetrics fm( font() ); int textW = fm.width( text ); int textX = m_Stops[m_SelectedStop].x() + 4; int textY = ((h+fm.height()) >> 1) - fm.descent(); if( textX+textW >= w ) textX -= textW + 7; painter.setOpacity( 0.5f ); painter.setPen( QPen(QColor(255,255,255),1.0f) ); painter.drawText( textX-1, textY , text ); painter.drawText( textX , textY-1, text ); painter.drawText( textX , textY+1, text ); painter.drawText( textX+1, textY , text ); painter.setOpacity( 1.0f ); painter.setPen( QPen(color,1.0f) ); painter.drawText( textX, textY, text ); } painter.end(); } int GradientWidget::getInterval( float value ) const { int beg = 0, end = m_Stops.size(); while( end != beg ) { int p = beg + (end-beg)/2; if( value <= m_Stops[p].value() ) end = p; else beg = p+1; } return end; } QColor GradientWidget::getColor( int interval, float value ) const { assert( isValid() ); if( interval == 0 ) return m_Stops.front().color(); else if( interval == m_Stops.size() ) return m_Stops.back().color(); else { const Stop &s0 = m_Stops[interval-1]; const Stop &s1 = m_Stops[interval ]; float alpha = (value - s0.value()) / (s1.value() - s0.value()); const QColor &c0 = s0.color(); const QColor &c1 = s1.color(); float r = c0.redF () + alpha*(c1.redF () - c0.redF ()); float g = c0.greenF() + alpha*(c1.greenF() - c0.greenF()); float b = c0.blueF () + alpha*(c1.blueF () - c0.blueF ()); return QColor::fromRgbF( r, g, b ); } } float GradientWidget::clampValueToStopInterval( int n, float value ) const { value = std::max( 0.0f, std::min(value,1.0f) ); if( n > 0 && value < m_Stops[n-1].value() ) value = m_Stops[n-1].value(); else if( n < m_Stops.size()-1 && value > m_Stops[n+1].value() ) value = m_Stops[n+1].value(); return value; } void GradientWidget::addStop( const QColor& color, float value ) { Stop stop( color, value ); stop.updateX( width()-1 ); m_Stops.insert( getInterval(value), stop ); emit gradientModified(); update(); } void GradientWidget::setStops( const QList& colors, const QList& values ) { assert( colors.size() == values.size() ); assert( !colors.empty() ); m_Stops.clear(); auto c = colors.begin(); auto v = values.begin(); while( c != colors.end() ) { addStop( *c, *v ); ++ c; ++ v; } } QString GradientWidget::getShaderFunctionBodyGLSL( int beg, int end, const QString& indent ) const { QString res; if( end != beg ) { int p = beg + (end-beg)/2; res += indent + "if( value <= " + QString::number( m_Stops[p].value() ) + " )\n"; res += getShaderFunctionBodyGLSL( beg, p, indent+" " ); res += indent + "else\n"; res += getShaderFunctionBodyGLSL( p+1, end, indent+" " ); } else { if( beg == 0 ) { QColor c = m_Stops.front().color(); res = indent + "return vec3(" + QString::number(c.redF()) + "," + QString::number(c.greenF()) + "," + QString::number(c.blueF()) + ");\n"; } else if( beg == m_Stops.size() ) { QColor c = m_Stops.back().color(); res = indent + "return vec3(" + QString::number(c.redF()) + "," + QString::number(c.greenF()) + "," + QString::number(c.blueF()) + ");\n"; } else { const Stop &s0 = m_Stops[beg-1]; const Stop &s1 = m_Stops[beg ]; const QColor &c0 = s0.color(); const QColor &c1 = s1.color(); res += indent + "return mix( "; res += "vec3(" + QString::number(c0.redF()) + "," + QString::number(c0.greenF()) + "," + QString::number(c0.blueF()) + "), "; res += "vec3(" + QString::number(c1.redF()) + "," + QString::number(c1.greenF()) + "," + QString::number(c1.blueF()) + "), "; res += "(value-" + QString::number(s0.value()) + ")*" + QString::number(1.0f / (s1.value() - s0.value())); res += " );\n"; } } return res; } QString GradientWidget::getShaderFunctionGLSL( const QString& funcName ) const { QString func = "vec3 " + funcName + "( float value )\n" "{\n" + getShaderFunctionBodyGLSL( 0, m_Stops.size(), " " ) + "}\n"; return func; } void GradientWidget::mouseDoubleClickEvent( QMouseEvent *evt ) { if( m_SelectedStop >= 0 ) { Stop &stop = m_Stops[m_SelectedStop]; QColor color = QColorDialog::getColor( stop.color(), this, "Select gradient stop color" ); if( color.isValid() && color != stop.color() ) { stop.setColor( color ); emit gradientModified(); update(); } } else { float value = float(evt->x()) / (width()-1); int interval = getInterval( value ); Stop stop( getColor(interval,value), value ); stop.updateX( width()-1 ); m_Stops.insert( interval, stop ); emit gradientModified(); update(); } } void GradientWidget::mousePressEvent( QMouseEvent *evt ) { m_SelectedStop = m_BelowCursor; update(); } void GradientWidget::mouseMoveEvent( QMouseEvent *evt ) { if( (evt->buttons() & Qt::LeftButton) && m_SelectedStop >= 0 ) { float value = clampValueToStopInterval( m_SelectedStop, float(evt->x()) / (width()-1) ); if( value != m_Stops[m_SelectedStop].value() ) { m_Stops[m_SelectedStop].setValue( value ); m_Stops[m_SelectedStop].updateX( width()-1 ); emit gradientModified(); update(); } } else { int belowCursor = -1; for( int i=0; ix() >= m_Stops[i].x()-2 && evt->x() <= m_Stops[i].x()+2 ) { belowCursor = i; break; } if( belowCursor != m_BelowCursor ) { m_BelowCursor = belowCursor; setCursor( belowCursor<0? Qt::ArrowCursor : Qt::PointingHandCursor ); } } } void GradientWidget::keyPressEvent( QKeyEvent *evt ) { if( m_SelectedStop < 0 ) { m_KeyboardInputValue = QString(); QWidget::keyPressEvent( evt ); return; } switch( evt->key() ) { case Qt::Key_0: case Qt::Key_1: case Qt::Key_2: case Qt::Key_3: case Qt::Key_4: case Qt::Key_5: case Qt::Key_6: case Qt::Key_7: case Qt::Key_8: case Qt::Key_9: case Qt::Key_Period: { bool ok; QString tmp = m_KeyboardInputValue + (char) evt->key(); if( tmp == "." ) tmp = "0."; float value = tmp.toFloat( &ok ); if( ok ) { if( value >= 2.0f ) { m_KeyboardInputValue = "0." + tmp; update(); } else if( value <= 1.0f ) { m_KeyboardInputValue = tmp; update(); } } break; } case Qt::Key_Backspace: { m_KeyboardInputValue = m_KeyboardInputValue.left( m_KeyboardInputValue.size()-1 ); update(); break; } case Qt::Key_Delete: { if( m_Stops.size() > 1 ) { m_Stops.remove( m_SelectedStop ); m_SelectedStop = -1; m_KeyboardInputValue = QString(); emit gradientModified(); update(); } break; } case Qt::Key_Enter: case Qt::Key_Return: { if( m_KeyboardInputValue.isEmpty() ) break; float value = clampValueToStopInterval( m_SelectedStop, m_KeyboardInputValue.toFloat() ); m_KeyboardInputValue = QString(); if( value != m_Stops[m_SelectedStop].value() ) { m_Stops[m_SelectedStop].setValue( value ); m_Stops[m_SelectedStop].updateX( width()-1 ); emit gradientModified(); } update(); break; } default: QWidget::keyPressEvent( evt ); } } void GradientWidget::leaveEvent( QEvent *evt ) { if( m_SelectedStop >= 0 ) { m_SelectedStop = -1; update(); } }