From ec95ef1ba91956790dda3ed557dbcd00e10e9dd9 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 12 Jun 2016 18:13:23 +0200 Subject: [PATCH] Implements #2635: Draws a transparent rectangle in the loop area (plus CSS exposure) (#2657) * Draws a transparent rectangle in the loop area (plus CSS exposure) The color for the rectangle can be defined in the style CSS for the active and inactive case. The following properties of the TimeLineWidget are exposed through the CSS: - The color of the lines that are drawn for each bar - The color of the bar numbers - The font size (given in pt) - The minimum and the maximum height of the widget (given in em so that it scales with the font size). Set both to the same value to set a fixed size. - The background of the widget - A loop color: The color for the main rectangle's pen - A loop brush: The brush used to fill the main rectangle - An inner loop color: The color used for the pen that draws the inner border. - loopRectangleVerticalPadding: specifies the padding used for the loop indicator rectangle. The bar numbers are drawn conditionally like the bar lines. The numbers are drawn with a constant distance to the tact line. This gives a more consistent picture at different zoom levels and also fixes the broken look at very small zoom sizes like for example 12.5%. The bar numbers are drawn with hinting so that they show up less blurry on low DPI displays. Remove the pixmaps that have been used up to now to draw the loop boundaries. Removes the unused "TimeLine" from style.css. Document the style sheet properties for the loop indicator rectangle. --- data/themes/classic/loop_point_b.png | Bin 472 -> 0 bytes data/themes/classic/loop_point_e.png | Bin 474 -> 0 bytes data/themes/classic/style.css | 24 ++++++- data/themes/default/loop_point_b.png | Bin 291 -> 0 bytes data/themes/default/loop_point_e.png | Bin 299 -> 0 bytes data/themes/default/style.css | 32 ++++++++- include/TimeLineWidget.h | 53 +++++++++++++- src/gui/TimeLineWidget.cpp | 102 +++++++++++++++------------ 8 files changed, 159 insertions(+), 52 deletions(-) delete mode 100644 data/themes/classic/loop_point_b.png delete mode 100644 data/themes/classic/loop_point_e.png delete mode 100755 data/themes/default/loop_point_b.png delete mode 100755 data/themes/default/loop_point_e.png diff --git a/data/themes/classic/loop_point_b.png b/data/themes/classic/loop_point_b.png deleted file mode 100644 index bcd65ad0866ce8acb889c557997caf3255225b0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 472 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2?f#ZI0f96(URk z2Vq7hjoB4ILCF%=h?3y^w370~qEv?R@^Zb*yzJuS#DY}4{G#;P?`)(P7#Kr6T^vI+ zCbkCc^lAaUlw>+N^VlbESW!U>uUv4Ne{JB0a*)H|gmOboyKKx)9dGp@Az80xejCJ5_4*V{M!-X*hza1;vou&Os zJ65(k-*W(ID>c)YLV!}$0+mj8mQ&RNjWQubCbgNrPuEJ3Kxm){zUyJ2zL-$NTAba= z-<2=d~3w3|5SA5JXBtxgJO=E4H4w&Yf(R6e++_ zV6B6pfoAxw2bS8lk17kk?*snmAejmE2(l-^X7KTP`S>imeT>YCL?A@NA(*)D-n5Rp zckOrS^dX%-SkZ)filBz;=~nkFoKIxQG6$hzGRm6p?|J}0ZRgOdH?O?r8 z2Vq7hjoB4ILCF%=h?3y^w370~qEv?R@^Zb*yzJuS#DY}4{G#;P?`))iiWYdfIEH9U zoO{WTkI_-&*vJ3Ut4yVqZ|LOS(UdWBt%;1I%pu8)knXUUgRCjXHBD}GRj$faNjk2* z|H=2-v%;aO-+FZK{4w6r8XUc2eJ)!>)O_DX(-<5}?|#Y?Xl9tT=*{HQJLH=ggoK}5 zuuIS6VS3Tx7IAdZ>dKjsJX*~cAFiGLd)JGVhgQ6K=eOUXRb=8z<-2mP8@P1eW$s(a f&G2OY{c7gzoAeC|j;K@u-OAwU>gTe~DWM4f6m)JO diff --git a/data/themes/default/loop_point_e.png b/data/themes/default/loop_point_e.png deleted file mode 100755 index 3b7b10e066d5f6a54d1c06f00a63f95590586b47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2?f#ZI0f96(URk z2Vq7hjoB4ILCF%=h?3y^w370~qEv?R@^Zb*yzJuS#DY}4{G#;P?`))iidJ~KIEH9U zoO;oak1g-1D>k zSvz^AVCd4UnE{8>{Bob!3FSU}=J>@_L*Bn3)g69%u2xy m+P2@PEs;(TwE4Ndijgzk)VZtX`Fo(789ZJ6T-G@yGywpDr))<6 diff --git a/data/themes/default/style.css b/data/themes/default/style.css index bd52cfef2..b7751352d 100755 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -536,8 +536,36 @@ Fader { qproperty-peakRed: #660505; } -TimeLine { - font-size: 8px; +TimeLineWidget { + /* font-size only supports px and pt. */ + font-size: 7pt; + /* lengths also support em. This will make sure that the height + will always change in the same proportion as the font size + defined above. + If you want a fixed size set min and max to the same value. */ + min-height: 1.5em; + max-height: 1.5em; + + /* Properties for the loop indicator rectangle in inactive state: + - LoopColor: Color of the outermost border + - LoopBrush: Brush to paint the main portion of the rectangle + - LoopInnerColor: Color used to paint the inlayed border */ + qproperty-inactiveLoopColor: #3B424A; + qproperty-inactiveLoopBrush: #3B424A; + qproperty-inactiveLoopInnerColor: #3B424A; + + /* Properties for the loop indicator rectangle in active state. + See above for detailed description. */ + qproperty-activeLoopColor: #21A14F; + qproperty-activeLoopBrush: #21A14F; + qproperty-activeLoopInnerColor: #21A14F; + + /* Vertical padding for the loop indicator rectangle. + A value of zero draws the rectangle at the full height of the widget. */ + qproperty-loopRectangleVerticalPadding: 1; + + qproperty-barLineColor: rgb( 192, 192, 192 ); + qproperty-barNumberColor: rgb( 192, 192, 192 ); } QTreeView { diff --git a/include/TimeLineWidget.h b/include/TimeLineWidget.h index 6487c840a..0f8ac8350 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -42,6 +42,16 @@ class TimeLineWidget : public QWidget, public JournallingObject { Q_OBJECT public: + Q_PROPERTY( QColor barLineColor READ getBarLineColor WRITE setBarLineColor ) + Q_PROPERTY( QColor barNumberColor READ getBarNumberColor WRITE setBarNumberColor ) + Q_PROPERTY( QColor inactiveLoopColor READ getInactiveLoopColor WRITE setInactiveLoopColor ) + Q_PROPERTY( QBrush inactiveLoopBrush READ getInactiveLoopBrush WRITE setInactiveLoopBrush ) + Q_PROPERTY( QColor inactiveLoopInnerColor READ getInactiveLoopInnerColor WRITE setInactiveLoopInnerColor ) + Q_PROPERTY( QColor activeLoopColor READ getActiveLoopColor WRITE setActiveLoopColor ) + Q_PROPERTY( QBrush activeLoopBrush READ getActiveLoopBrush WRITE setActiveLoopBrush ) + Q_PROPERTY( QColor activeLoopInnerColor READ getActiveLoopInnerColor WRITE setActiveLoopInnerColor ) + Q_PROPERTY( int loopRectangleVerticalPadding READ getLoopRectangleVerticalPadding WRITE setLoopRectangleVerticalPadding ) + enum AutoScrollStates { AutoScrollEnabled, @@ -66,6 +76,33 @@ public: const MidiTime & begin, QWidget * parent ); virtual ~TimeLineWidget(); + inline QColor const & getBarLineColor() const { return m_barLineColor; } + inline void setBarLineColor(QColor const & tactLineColor) { m_barLineColor = tactLineColor; } + + inline QColor const & getBarNumberColor() const { return m_barNumberColor; } + inline void setBarNumberColor(QColor const & tactNumberColor) { m_barNumberColor = tactNumberColor; } + + inline QColor const & getInactiveLoopColor() const { return m_inactiveLoopColor; } + inline void setInactiveLoopColor(QColor const & inactiveLoopColor) { m_inactiveLoopColor = inactiveLoopColor; } + + inline QBrush const & getInactiveLoopBrush() const { return m_inactiveLoopBrush; } + inline void setInactiveLoopBrush(QBrush const & inactiveLoopBrush) { m_inactiveLoopBrush = inactiveLoopBrush; } + + inline QColor const & getInactiveLoopInnerColor() const { return m_inactiveLoopInnerColor; } + inline void setInactiveLoopInnerColor(QColor const & inactiveLoopInnerColor) { m_inactiveLoopInnerColor = inactiveLoopInnerColor; } + + inline QColor const & getActiveLoopColor() const { return m_activeLoopColor; } + inline void setActiveLoopColor(QColor const & activeLoopColor) { m_activeLoopColor = activeLoopColor; } + + inline QBrush const & getActiveLoopBrush() const { return m_activeLoopBrush; } + inline void setActiveLoopBrush(QBrush const & activeLoopBrush) { m_activeLoopBrush = activeLoopBrush; } + + inline QColor const & getActiveLoopInnerColor() const { return m_activeLoopInnerColor; } + inline void setActiveLoopInnerColor(QColor const & activeLoopInnerColor) { m_activeLoopInnerColor = activeLoopInnerColor; } + + inline int const & getLoopRectangleVerticalPadding() const { return m_loopRectangleVerticalPadding; } + inline void setLoopRectangleVerticalPadding(int const & loopRectangleVerticalPadding) { m_loopRectangleVerticalPadding = loopRectangleVerticalPadding; } + inline Song::PlayPos & pos() { return( m_pos ); @@ -155,9 +192,19 @@ protected: private: static QPixmap * s_posMarkerPixmap; - static QPixmap * s_loopPointBeginPixmap; - static QPixmap * s_loopPointEndPixmap; - static QPixmap * s_loopPointDisabledPixmap; + + QColor m_inactiveLoopColor; + QBrush m_inactiveLoopBrush; + QColor m_inactiveLoopInnerColor; + + QColor m_activeLoopColor; + QBrush m_activeLoopBrush; + QColor m_activeLoopInnerColor; + + int m_loopRectangleVerticalPadding; + + QColor m_barLineColor; + QColor m_barNumberColor; AutoScrollStates m_autoScroll; LoopPointStates m_loopPoints; diff --git a/src/gui/TimeLineWidget.cpp b/src/gui/TimeLineWidget.cpp index a367d2bcf..682fb56fd 100644 --- a/src/gui/TimeLineWidget.cpp +++ b/src/gui/TimeLineWidget.cpp @@ -48,13 +48,20 @@ QPixmap * TimeLineWidget::s_posMarkerPixmap = NULL; -QPixmap * TimeLineWidget::s_loopPointBeginPixmap = NULL; -QPixmap * TimeLineWidget::s_loopPointEndPixmap = NULL; TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppt, Song::PlayPos & pos, const MidiTime & begin, QWidget * parent ) : QWidget( parent ), + m_inactiveLoopColor( 52, 63, 53, 64 ), + m_inactiveLoopBrush( QColor( 255, 255, 255, 32 ) ), + m_inactiveLoopInnerColor( 255, 255, 255, 32 ), + m_activeLoopColor( 52, 63, 53, 255 ), + m_activeLoopBrush( QColor( 55, 141, 89 ) ), + m_activeLoopInnerColor( 74, 155, 100, 255 ), + m_loopRectangleVerticalPadding( 1 ), + m_barLineColor( 192, 192, 192 ), + m_barNumberColor( m_barLineColor.darker( 120 ) ), m_autoScroll( AutoScrollEnabled ), m_loopPoints( LoopPointsDisabled ), m_behaviourAtStop( BackToZero ), @@ -77,20 +84,9 @@ TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppt, s_posMarkerPixmap = new QPixmap( embed::getIconPixmap( "playpos_marker" ) ); } - if( s_loopPointBeginPixmap == NULL ) - { - s_loopPointBeginPixmap = new QPixmap( embed::getIconPixmap( - "loop_point_b" ) ); - } - if( s_loopPointEndPixmap == NULL ) - { - s_loopPointEndPixmap = new QPixmap( embed::getIconPixmap( - "loop_point_e" ) ); - } setAttribute( Qt::WA_OpaquePaintEvent, true ); move( 0, yoff ); - setFixedHeight( 18 ); m_xOffset -= s_posMarkerPixmap->width() / 2; @@ -224,55 +220,71 @@ void TimeLineWidget::paintEvent( QPaintEvent * ) { QPainter p( this ); - QColor bg_color = QApplication::palette().color( QPalette::Active, - QPalette::Background ); - QLinearGradient g( 0, 0, 0, height() ); - g.setColorAt( 0, bg_color.lighter( 150 ) ); - g.setColorAt( 1, bg_color.darker( 150 ) ); - p.fillRect( 0, 0, width(), height(), g ); + // Draw background + p.fillRect( 0, 0, width(), height(), p.background() ); + // Clip so that we only draw everything starting from the offset p.setClipRect( m_xOffset, 0, width() - m_xOffset, height() ); - p.setPen( QColor( 0, 0, 0 ) ); - p.setOpacity( loopPointsEnabled() ? 0.9 : 0.2 ); - p.drawPixmap( markerX( loopBegin() )+2, 2, *s_loopPointBeginPixmap ); - p.drawPixmap( markerX( loopEnd() )+2, 2, *s_loopPointEndPixmap ); - p.setOpacity( 1.0 ); + // Draw the loop rectangle + int const & loopRectMargin = getLoopRectangleVerticalPadding(); + int const loopRectHeight = this->height() - 2 * loopRectMargin; + int const loopStart = markerX( loopBegin() ) + 8; + int const loopEndR = markerX( loopEnd() ) + 9; + int const loopRectWidth = loopEndR - loopStart; + bool const loopPointsActive = loopPointsEnabled(); - tact_t tact_num = m_begin.getTact(); - int x = m_xOffset + s_posMarkerPixmap->width() / 2 - - ( ( static_cast( m_begin * m_ppt ) / MidiTime::ticksPerTact() ) % static_cast( m_ppt ) ); + // Draw the main rectangle (inner fill only) + QRect outerRectangle( loopStart, loopRectMargin, loopRectWidth - 1, loopRectHeight - 1 ); + p.fillRect( outerRectangle, loopPointsActive ? getActiveLoopBrush() : getInactiveLoopBrush()); - QColor lineColor( 192, 192, 192 ); - QColor tactColor( lineColor.darker( 120 ) ); - - // Set font to half of the widgets size (in pixels) + // Draw the bar lines and numbers + // Activate hinting on the font QFont font = p.font(); - font.setPixelSize( this->height() * 0.5 ); - p.setFont( font ); + font.setHintingPreference( QFont::PreferFullHinting ); + p.setFont(font); + int const fontAscent = p.fontMetrics().ascent(); + int const fontHeight = p.fontMetrics().height(); + + QColor const & barLineColor = getBarLineColor(); + QColor const & barNumberColor = getBarNumberColor(); + + tact_t barNumber = m_begin.getTact(); + int const x = m_xOffset + s_posMarkerPixmap->width() / 2 - + ( ( static_cast( m_begin * m_ppt ) / MidiTime::ticksPerTact() ) % static_cast( m_ppt ) ); for( int i = 0; x + i * m_ppt < width(); ++i ) { - const int cx = x + qRound( i * m_ppt ); - p.setPen( lineColor ); - p.drawLine( cx, 5, cx, height() - 6 ); - ++tact_num; - if( ( tact_num - 1 ) % + ++barNumber; + if( ( barNumber - 1 ) % qMax( 1, qRound( 1.0f / 3.0f * MidiTime::ticksPerTact() / m_ppt ) ) == 0 ) { - const QString s = QString::number( tact_num ); - p.setPen( tactColor ); - p.drawText( cx + qRound( ( m_ppt - p.fontMetrics(). - width( s ) ) / 2 ), - height() - p.fontMetrics().ascent() / 2, s ); + const int cx = x + qRound( i * m_ppt ); + p.setPen( barLineColor ); + p.drawLine( cx, 5, cx, height() - 6 ); + + const QString s = QString::number( barNumber ); + p.setPen( barNumberColor ); + p.drawText( cx + 5, ((height() - fontHeight) / 2) + fontAscent, s ); } } + // Draw the main rectangle (outer border) + p.setPen( loopPointsActive ? getActiveLoopColor() : getInactiveLoopColor() ); + p.setBrush( Qt::NoBrush ); + p.drawRect( outerRectangle ); + + // Draw the inner border outline (no fill) + QRect innerRectangle = outerRectangle.adjusted( 1, 1, -1, -1 ); + p.setPen( loopPointsActive ? getActiveLoopInnerColor() : getInactiveLoopInnerColor() ); + p.setBrush( Qt::NoBrush ); + p.drawRect( innerRectangle ); + + // Draw the position marker p.setOpacity( 0.6 ); - p.drawPixmap( m_posMarkerX, height() - s_posMarkerPixmap->height(), - *s_posMarkerPixmap ); + p.drawPixmap( m_posMarkerX, height() - s_posMarkerPixmap->height(), *s_posMarkerPixmap ); }