diff --git a/slider.cpp b/slider.cpp index de5f2885..2ba6cbdf 100644 --- a/slider.cpp +++ b/slider.cpp @@ -1,335 +1,338 @@ /** * Copyright (c) 2003-2009 Mark Kretschmann * Copyright (c) 2005 Gabor Lehel * Copyright (c) 2008 Dan Meltzer * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "slider.h" #include "svghandler.h" #include #include #include #include #include #include #include #include #include Slider::Slider( Qt::Orientation orientation, uint max, QWidget *parent ) : QSlider( orientation, parent ) , m_sliding( false ) , m_outside( false ) , m_prevValue( 0 ) , m_needsResize( true ) { setMouseTracking( true ); setRange( 0, max ); setAttribute( Qt::WA_NoMousePropagation, true ); setAttribute( Qt::WA_Hover, true ); if ( orientation == Qt::Vertical ) { setInvertedAppearance( true ); setInvertedControls( true ); } } -QRect -Slider::sliderHandleRect( const QRect &slider, qreal percent ) const +QRectF +Slider::sliderHandleRect( const QRectF &slider, qreal percent ) const { - QRect rect; + QRectF rect; const bool inverse = ( orientation() == Qt::Horizontal ) ? ( invertedAppearance() != (layoutDirection() == Qt::RightToLeft) ) : ( !invertedAppearance() ); if(m_usingCustomStyle) + { rect = The::svgHandler()->sliderKnobRect( slider, percent, inverse ); + } else { if ( inverse ) percent = 1.0 - percent; const int handleSize = style()->pixelMetric( QStyle::PM_SliderControlThickness ); rect = QRect( 0, 0, handleSize, handleSize ); rect.moveTo( slider.x() + qRound( ( slider.width() - handleSize ) * percent ), slider.y() + 1 ); } return rect; } void Slider::wheelEvent( QWheelEvent *e ) { if( orientation() == Qt::Vertical ) { // Will be handled by the parent widget e->ignore(); return; } // Position Slider (horizontal) // only used for progress slider now! int step = e->delta() * 24; int nval = value() + step; nval = qMax(nval, minimum()); nval = qMin(nval, maximum()); QSlider::setValue( nval ); emit sliderReleased( value() ); } void Slider::mouseMoveEvent( QMouseEvent *e ) { if ( m_sliding ) { //feels better, but using set value of 20 is bad of course - QRect rect( -20, -20, width()+40, height()+40 ); + QRectF rect( -20, -20, width()+40, height()+40 ); if ( orientation() == Qt::Horizontal && !rect.contains( e->pos() ) ) { if ( !m_outside ) { QSlider::setValue( m_prevValue ); //if mouse released outside of slider, emit sliderMoved to previous value emit sliderMoved( m_prevValue ); } m_outside = true; } else { m_outside = false; slideEvent( e ); emit sliderMoved( value() ); } } else QSlider::mouseMoveEvent( e ); } void Slider::slideEvent( QMouseEvent *e ) { - QRect knob; + QRectF knob; if ( maximum() > minimum() ) knob = sliderHandleRect( rect(), ((qreal)value()) / ( maximum() - minimum() ) ); int position; int span; if( orientation() == Qt::Horizontal ) { position = e->pos().x() - knob.width() / 2; span = width() - knob.width(); } else { position = e->pos().y() - knob.height() / 2; span = height() - knob.height(); } const bool inverse = ( orientation() == Qt::Horizontal ) ? ( invertedAppearance() != (layoutDirection() == Qt::RightToLeft) ) : ( !invertedAppearance() ); const int val = QStyle::sliderValueFromPosition( minimum(), maximum(), position, span, inverse ); QSlider::setValue( val ); } void Slider::mousePressEvent( QMouseEvent *e ) { m_sliding = true; m_prevValue = value(); - QRect knob; + QRectF knob; if ( maximum() > minimum() ) knob = sliderHandleRect( rect(), ((qreal)value()) / ( maximum() - minimum() ) ); if ( !knob.contains( e->pos() ) ) mouseMoveEvent( e ); } void Slider::mouseReleaseEvent( QMouseEvent* ) { if( !m_outside && value() != m_prevValue ) emit sliderReleased( value() ); m_sliding = false; m_outside = false; } void Slider::setValue( int newValue ) { //don't adjust the slider while the user is dragging it! if ( !m_sliding || m_outside ) QSlider::setValue( newValue ); else m_prevValue = newValue; } void Slider::paintCustomSlider( QPainter *p ) { qreal percent = 0.0; if ( maximum() > minimum() ) percent = ((qreal)value()) / ( maximum() - minimum() ); QStyleOptionSlider opt; initStyleOption( &opt ); if ( m_sliding || ( underMouse() && sliderHandleRect( rect(), percent ).contains( mapFromGlobal(QCursor::pos()) ) ) ) { opt.activeSubControls |= QStyle::SC_SliderHandle; } + The::svgHandler()->setDevicePixelRatioF(devicePixelRatioF()); The::svgHandler()->paintCustomSlider( p, &opt, percent ); } ////////////////////////////////////////////////////////////////////////////////////////// /// CLASS VolumeSlider ////////////////////////////////////////////////////////////////////////////////////////// VolumeSlider::VolumeSlider( uint max, QWidget *parent, bool customStyle ) : Slider( customStyle ? Qt::Horizontal : Qt::Vertical, max, parent ) { m_usingCustomStyle = customStyle; setFocusPolicy( Qt::NoFocus ); setInvertedAppearance( false ); setInvertedControls( false ); connect( this, SIGNAL(sliderMoved(int)), this, SLOT(emitVolumeChanged(int)) ); connect( this, SIGNAL(sliderReleased(int)), this, SLOT(emitVolumeChanged(int)) ); } void VolumeSlider::mousePressEvent( QMouseEvent *e ) { if( e->button() != Qt::RightButton ) { Slider::mousePressEvent( e ); slideEvent( e ); } } void VolumeSlider::contextMenuEvent( QContextMenuEvent *e ) { QMenu menu; menu.setTitle( i18n( "Volume" ) ); menu.addAction( i18n( "100%" ) )->setData( 100 ); menu.addAction( i18n( "80%" ) )->setData( 80 ); menu.addAction( i18n( "60%" ) )->setData( 60 ); menu.addAction( i18n( "40%" ) )->setData( 40 ); menu.addAction( i18n( "20%" ) )->setData( 20 ); menu.addAction( i18n( "0%" ) )->setData( 0 ); QAction* a = menu.exec( mapToGlobal( e->pos() ) ); if( a ) { const int n = a->data().toInt(); if( n >= 0 ) { QSlider::setValue( n ); emit volumeChanged( float( n ) / float( maximum() ) ); } } } void VolumeSlider::wheelEvent( QWheelEvent *e ) { static const int volumeSensitivity = 30; const uint step = e->delta() / volumeSensitivity; QSlider::setValue( QSlider::value() + step ); emit volumeChanged( float( value() ) / float( maximum() ) ); } void VolumeSlider::paintEvent( QPaintEvent *event ) { if( m_usingCustomStyle ) { QPainter p( this ); paintCustomSlider( &p ); p.end(); return; } QSlider::paintEvent( event ); } void VolumeSlider::emitVolumeChanged( int value ) { emit volumeChanged( float( value ) / float( maximum() ) ); } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////// TIMESLIDER //////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// TimeSlider::TimeSlider( QWidget *parent ) : Slider( Qt::Horizontal, 0, parent ) , m_knobX( 0.0 ) { m_usingCustomStyle = true; setFocusPolicy( Qt::NoFocus ); } void TimeSlider::setSliderValue( int value ) { Slider::setValue( value ); } void TimeSlider::paintEvent( QPaintEvent *pe ) { QPainter p( this ); p.setClipRegion( pe->region() ); paintCustomSlider( &p ); p.end(); } void TimeSlider::sliderChange( SliderChange change ) { if ( change == SliderValueChange || change == SliderRangeChange ) { int oldKnobX = m_knobX; qreal percent = 0.0; if ( maximum() > minimum() ) percent = ((qreal)value()) / ( maximum() - minimum() ); - QRect knob = sliderHandleRect( rect(), percent ); + QRectF knob = sliderHandleRect( rect(), percent ); m_knobX = knob.x(); if (oldKnobX < m_knobX) update( oldKnobX, knob.y(), knob.right() + 1 - oldKnobX, knob.height() ); else if (oldKnobX > m_knobX) update( m_knobX, knob.y(), oldKnobX + knob.width(), knob.height() ); } else Slider::sliderChange( change ); // calls update() } void TimeSlider::mousePressEvent( QMouseEvent *event ) { // We should probably eat this event if we're not able to seek Slider::mousePressEvent( event ); } diff --git a/slider.h b/slider.h index d405f7e4..3ed5c139 100644 --- a/slider.h +++ b/slider.h @@ -1,124 +1,124 @@ /** * Copyright (c) 2003-2009 Mark Kretschmann * Copyright (c) 2005 Gabor Lehel * Copyright (c) 2008 Dan Meltzer * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef SLIDERWIDGET_H #define SLIDERWIDGET_H #include #include #include #include class QPalette; class QTimer; class Slider : public QSlider { Q_OBJECT public: explicit Slider( Qt::Orientation, uint max = 0, QWidget* parent = 0 ); virtual void setValue( int ); signals: //we emit this when the user has specifically changed the slider //so connect to it if valueChanged() is too generic //Qt also emits valueChanged( int ) void sliderReleased( int ); protected: virtual void wheelEvent( QWheelEvent* ) override; virtual void mouseMoveEvent( QMouseEvent* ) override; virtual void mouseReleaseEvent( QMouseEvent* ) override; virtual void mousePressEvent( QMouseEvent* ) override; virtual void slideEvent( QMouseEvent* ); - QRect sliderHandleRect( const QRect &slider, qreal percent ) const; + QRectF sliderHandleRect( const QRectF &slider, qreal percent ) const; void paintCustomSlider( QPainter *p ); bool m_sliding; bool m_usingCustomStyle; static const int s_borderWidth = 6; static const int s_borderHeight = 6; static const int s_sliderInsertX = 5; static const int s_sliderInsertY = 5; private: bool m_outside; int m_prevValue; bool m_needsResize; QPixmap m_topLeft; QPixmap m_topRight; QPixmap m_top; QPixmap m_bottomRight; QPixmap m_right; QPixmap m_bottomLeft; QPixmap m_bottom; QPixmap m_left; Q_DISABLE_COPY( Slider ) }; class VolumeSlider : public Slider { Q_OBJECT public: explicit VolumeSlider( uint max, QWidget *parent, bool customStyle = true ); // VolumePopupButton needs to access this virtual void wheelEvent( QWheelEvent *e ) override; protected: virtual void paintEvent( QPaintEvent* ) override; virtual void mousePressEvent( QMouseEvent* ) override; virtual void contextMenuEvent( QContextMenuEvent* ) override; signals: void volumeChanged( float ); private slots: void emitVolumeChanged( int ); private: Q_DISABLE_COPY( VolumeSlider ) }; class TimeSlider : public Slider { Q_OBJECT public: explicit TimeSlider( QWidget *parent ); void setSliderValue( int value ); protected: virtual void paintEvent( QPaintEvent* ) override; virtual void mousePressEvent( QMouseEvent* ) override; virtual void sliderChange( SliderChange change ) override; private: Q_DISABLE_COPY( TimeSlider ) int m_knobX; // The position of the current indicator. }; #endif diff --git a/svghandler.cpp b/svghandler.cpp index 3fddb43e..b8fbc458 100644 --- a/svghandler.cpp +++ b/svghandler.cpp @@ -1,221 +1,228 @@ /** * Copyright (c) 2008 Nikolaj Hald Nielsen * Copyright (c) 2008 Jeff Mitchell * Copyright (c) 2009 Mark Kretschmann * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "svghandler.h" #include "juk_debug.h" #include #include #include #include #include #include #include #include #include namespace The { static SvgHandler* s_SvgHandler_instance = 0; SvgHandler* svgHandler() { if( !s_SvgHandler_instance ) s_SvgHandler_instance = new SvgHandler(); return s_SvgHandler_instance; } } SvgHandler::SvgHandler( QObject* parent ) : QObject( parent ) , m_renderer( 0 ) , m_themeFile( "juk/pics/theme.svg" ) { } SvgHandler::~SvgHandler() { delete m_renderer; The::s_SvgHandler_instance = 0; } bool SvgHandler::loadSvg( const QString& name ) { const QString &svgFilename = QStandardPaths::locate( QStandardPaths::GenericDataLocation, name ); QSvgRenderer *renderer = new QSvgRenderer( svgFilename, this ); if ( !renderer->isValid() ) { delete renderer; return false; } QWriteLocker writeLocker( &m_lock ); if( m_renderer ) delete m_renderer; m_renderer = renderer; return true; } QSvgRenderer * SvgHandler::getRenderer() { QReadLocker readLocker( &m_lock ); if( ! m_renderer ) { readLocker.unlock(); if( !loadSvg( m_themeFile ) ) { QWriteLocker writeLocker( &m_lock ); m_renderer = new QSvgRenderer( this ); } readLocker.relock(); } return m_renderer; } QPixmap SvgHandler::renderSvg( const QString& keyname, int width, int height, const QString& element ) { QString key = keyname % QChar( ':' ) % QString::number( width ) % QChar( 'x' ) % QString::number( height ); QPixmap pixmap; QMap::const_iterator it = m_cache.constFind( key ); if( it != m_cache.constEnd() ) { pixmap = *it; } else { - pixmap = QPixmap( width, height ); + pixmap = QPixmap( width * dpr, height * dpr ); + pixmap.setDevicePixelRatio(dpr); pixmap.fill( Qt::transparent ); QReadLocker readLocker( &m_lock ); if( ! m_renderer ) { readLocker.unlock(); if( !loadSvg( m_themeFile ) ) { return pixmap; } readLocker.relock(); } QPainter pt( &pixmap ); if ( element.isEmpty() ) m_renderer->render( &pt, QRectF( 0, 0, width, height ) ); else { m_renderer->render( &pt, element, QRectF( 0, 0, width, height ) ); } m_cache.insert( key, pixmap ); } return pixmap; } void SvgHandler::reTint() { // The::svgTinter()->init(); if ( !loadSvg( m_themeFile )) qCDebug(JUK_LOG) << "Unable to load theme file: " << m_themeFile; emit retinted(); } QString SvgHandler::themeFile() { return m_themeFile; } -QRect SvgHandler::sliderKnobRect( const QRect &slider, qreal percent, bool inverse ) const +QRectF SvgHandler::sliderKnobRect( const QRectF &slider, qreal percent, bool inverse ) const { if ( inverse ) percent = 1.0 - percent; const int knobSize = slider.height() - 4; - QRect ret( 0, 0, knobSize, knobSize ); + QRectF ret( 0, 0, knobSize, knobSize ); ret.moveTo( slider.x() + qRound( ( slider.width() - knobSize ) * percent ), slider.y() + 1 ); return ret; } // Experimental, using a mockup from Nuno Pinheiro (new_slider_nuno) void SvgHandler::paintCustomSlider( QPainter *p, QStyleOptionSlider *slider, qreal percentage ) { - int sliderHeight = slider->rect.height() - 6; + qreal sliderHeight = slider->rect.height() - 6; const bool inverse = ( slider->orientation == Qt::Vertical ) ? slider->upsideDown : ( (slider->direction == Qt::RightToLeft) != slider->upsideDown ); - QRect knob = sliderKnobRect( slider->rect, percentage, inverse ); - QPoint pt = slider->rect.topLeft() + QPoint( 0, 2 ); + QRectF knob = sliderKnobRect( slider->rect, percentage, inverse ); + QPointF pt = slider->rect.topLeft() + QPointF( 0, 2 ); //debug() << "rel: " << knobRelPos << ", width: " << width << ", height:" << height << ", %: " << percentage; // Draw the slider background in 3 parts p->drawPixmap( pt, renderSvg( "progress_slider_left", sliderHeight, sliderHeight, "progress_slider_left" ) ); pt.rx() += sliderHeight; - QRect midRect(pt, QSize(slider->rect.width() - sliderHeight * 2, sliderHeight) ); + QRectF midRect(pt, QSize(slider->rect.width() - sliderHeight * 2, sliderHeight) ); p->drawTiledPixmap( midRect, renderSvg( "progress_slider_mid", 32, sliderHeight, "progress_slider_mid" ) ); pt = midRect.topRight() + QPoint( 1, 0 ); p->drawPixmap( pt, renderSvg( "progress_slider_right", sliderHeight, sliderHeight, "progress_slider_right" ) ); //draw the played background. - int playedBarHeight = sliderHeight - 6; + qreal playedBarHeight = sliderHeight - 6; + qreal min = 0; - int sizeOfLeftPlayed = qBound( 0, inverse ? slider->rect.right() - knob.right() + 2 : + qreal sizeOfLeftPlayed = qBound( min, inverse ? slider->rect.right() - knob.right() + 2 : knob.x() - 2, playedBarHeight ); if( sizeOfLeftPlayed > 0 ) { - QPoint tl, br; + QPointF tl, br; if ( inverse ) { - tl = knob.topRight() + QPoint( -5, 5 ); // 5px x padding to avoid a "gap" between it and the top and bottom of the round knob. - br = slider->rect.topRight() + QPoint( -3, 5 + playedBarHeight - 1 ); + tl = knob.topRight() + QPointF( -5, 5 ); // 5px x padding to avoid a "gap" between it and the top and bottom of the round knob. + br = slider->rect.topRight() + QPointF( -3, 5 + playedBarHeight - 1 ); QPixmap rightEnd = renderSvg( "progress_slider_played_right", playedBarHeight, playedBarHeight, "progress_slider_played_right" ); - p->drawPixmap( br.x() - rightEnd.width() + 1, tl.y(), rightEnd, qMax(0, rightEnd.width() - (sizeOfLeftPlayed + 3)), 0, sizeOfLeftPlayed + 3, playedBarHeight ); + p->drawPixmap( br.x() - rightEnd.width() + 1, tl.y(), rightEnd, qMax(qreal(0), rightEnd.width() - (sizeOfLeftPlayed + 3)), 0, sizeOfLeftPlayed + 3, playedBarHeight ); br.rx() -= playedBarHeight; } else { - tl = slider->rect.topLeft() + QPoint( 3, 5 ); - br = QPoint( knob.x() + 5, tl.y() + playedBarHeight - 1 ); + tl = slider->rect.topLeft() + QPointF( 3, 5 ); + br = QPointF( knob.x() + 5, tl.y() + playedBarHeight - 1 ); QPixmap leftEnd = renderSvg( "progress_slider_played_left", playedBarHeight, playedBarHeight, "progress_slider_played_left" ); p->drawPixmap( tl.x(), tl.y(), leftEnd, 0, 0, sizeOfLeftPlayed + 3, playedBarHeight ); tl.rx() += playedBarHeight; } if ( sizeOfLeftPlayed == playedBarHeight ) - p->drawTiledPixmap( QRect(tl, br), renderSvg( "progress_slider_played_mid", 32, playedBarHeight, "progress_slider_played_mid" ) ); + p->drawTiledPixmap( QRectF(tl, br), renderSvg( "progress_slider_played_mid", 32, playedBarHeight, "progress_slider_played_mid" ) ); } if ( slider->state & QStyle::State_Enabled ) { // Draw the knob (handle) const char *string = ( slider->activeSubControls & QStyle::SC_SliderHandle ) ? "slider_knob_200911_active" : "slider_knob_200911"; p->drawPixmap( knob.topLeft(), renderSvg( string, knob.width(), knob.height(), string ) ); } } + +void SvgHandler::setDevicePixelRatioF(qreal dpr) +{ + this->dpr = dpr; +} diff --git a/svghandler.h b/svghandler.h index 0a289c0a..ae4a69ab 100644 --- a/svghandler.h +++ b/svghandler.h @@ -1,111 +1,115 @@ /** * Copyright (c) 2008 Nikolaj Hald Nielsen * Copyright (c) 2008 Jeff Mitchell * Copyright (c) 2009 Mark Kretschmann * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef SVGHANDLER_H #define SVGHANDLER_H class QStyleOptionSlider; #include #include #include #include class SvgHandler; class QSvgRenderer; namespace The { SvgHandler* svgHandler(); } /** A class to abstract out some common operations of users of tinted svgs */ class SvgHandler : public QObject { Q_OBJECT friend SvgHandler* The::svgHandler(); public: ~SvgHandler(); QSvgRenderer* getRenderer(); /** * Overloaded function that uses the current theme * @param keyname the name of the key to save in the cache * @param width Width of the resulting pixmap * @param height Height of the resulting pixmap * @param element The theme element to render ( if none the entire svg is rendered ) * @return The svg element/file rendered into a pixmap */ QPixmap renderSvg( const QString& keyname, int width, int height, const QString& element = QString() ); /** * Paint a custom slider using the specified painter. The slider consists * of a background part, a "knob" that moves along it to show the current * position, and 2 end markers to clearly mark the ends of the slider. * The background part before the knob, is painted in a different color than the * part after (and under) the knob. * @param p The painter to use. * @param x The x position to begin painting at. * @param y The y position to begin painting at. * @param width The width of the slider to paint. * @param height The height of the slider. The background part does not scale in height, it will always be a relatively thin line, but the knob and end markers do. * @param percentage The percentange of the slider that the knob is positioned at. * @param active Specifies whether the slider should be painted "active" using the current palettes active colors, to specify that it currently has mouse focus or hover. */ void paintCustomSlider( QPainter *p, QStyleOptionSlider *slider, qreal percentage ); /** * Calculate the visual slider knob rect from its value, use it instead the QStyle functions * QStyle::sliderPositionFromValue() and QStyle::subControlRect(); */ - QRect sliderKnobRect( const QRect &slider, qreal percent, bool inverse ) const; + QRectF sliderKnobRect( const QRectF &slider, qreal percent, bool inverse ) const; /** * Get the path of the currently used svg theme file. * * @return the path of the currently used theme file. */ QString themeFile(); + + void setDevicePixelRatioF(qreal dpr); public slots: void reTint(); signals: void retinted(); private: SvgHandler( QObject* parent = 0 ); bool loadSvg( const QString& name ); QPixmap sliderHandle( const QColor &color, bool pressed, int size ); QMap m_cache; QSvgRenderer *m_renderer; QReadWriteLock m_lock; QString m_themeFile; + + qreal dpr = 1; }; #endif