Add Continuous Auto-Scrolling (#7396)

* Initial Commit

* Refactor code and add new icons

* Fix logical error

* Add smooth scrolling to Song Editor

* Remove unused variable

* Fix scrolling speed and scrollbar width

* Remove QDebug and re-add a newline I deleted in an unrelated file

* Forgot to add files to commit

* Remove unused variable

* Fix Styling

* Fix Styling Again

* Fix Styling Again

* Fix Styling Again

* Add icons for classic theme

* Accidentally committed varying scroll speed with zoom -- removing

* Change abs to std::abs

Co-authored-by: saker <sakertooth@gmail.com>

* Change qMax to std::max and use static_cast

Co-authored-by: saker <sakertooth@gmail.com>

* Simplify stepped auto scrolling

Co-authored-by: saker <sakertooth@gmail.com>

* Remove unnecessary parentheses

* Remove return statement causing the play head line to stop

* Add specific tooltips to auto scrolling button states

* Remove `== true` from SongEditor.cpp

Co-authored-by: saker <sakertooth@gmail.com>

* Make tooltips translatable

Co-authored-by: Dominic Clark <mrdomclark@gmail.com>

* Fix zooming position calculation

* Rename bars to ticks

* Fix rubberband rect size

---------

Co-authored-by: saker <sakertooth@gmail.com>
Co-authored-by: Dominic Clark <mrdomclark@gmail.com>
This commit is contained in:
regulus79
2024-08-04 04:30:42 -04:00
committed by GitHub
parent 1c865843f7
commit ce17c95636
9 changed files with 59 additions and 47 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -73,7 +73,8 @@ public:
enum class AutoScrollState
{
Enabled,
Stepped,
Continuous,
Disabled
};
@@ -212,7 +213,7 @@ private:
QCursor m_cursorSelectLeft = QCursor{embed::getIconPixmap("cursor_select_left"), 0, 16};
QCursor m_cursorSelectRight = QCursor{embed::getIconPixmap("cursor_select_right"), 32, 16};
AutoScrollState m_autoScroll = AutoScrollState::Enabled;
AutoScrollState m_autoScroll = AutoScrollState::Stepped;
// Width of the unused region on the widget's left (above track labels or piano)
int m_xOffset;

View File

@@ -4062,7 +4062,7 @@ void PianoRoll::stop()
{
Engine::getSong()->stop();
m_recording = false;
m_scrollBack = ( m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Enabled );
m_scrollBack = m_timeLine->autoScroll() != TimeLineWidget::AutoScrollState::Disabled;
}
@@ -4463,30 +4463,36 @@ bool PianoRoll::deleteSelectedNotes()
void PianoRoll::autoScroll( const TimePos & t )
{
const int w = width() - m_whiteKeyWidth;
if( t > m_currentPosition + w * TimePos::ticksPerBar() / m_ppb )
if (m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Stepped)
{
m_leftRightScroll->setValue( t.getBar() * TimePos::ticksPerBar() );
if (t > m_currentPosition + w * TimePos::ticksPerBar() / m_ppb)
{
m_leftRightScroll->setValue(t.getBar() * TimePos::ticksPerBar());
}
else if (t < m_currentPosition)
{
TimePos t2 = std::max(t - w * TimePos::ticksPerBar() *
TimePos::ticksPerBar() / m_ppb, static_cast<tick_t>(0));
m_leftRightScroll->setValue(t2.getBar() * TimePos::ticksPerBar());
}
}
else if( t < m_currentPosition )
else if (m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Continuous)
{
TimePos t2 = qMax( t - w * TimePos::ticksPerBar() *
TimePos::ticksPerBar() / m_ppb, (tick_t) 0 );
m_leftRightScroll->setValue( t2.getBar() * TimePos::ticksPerBar() );
m_leftRightScroll->setValue(std::max(t.getTicks() - w * TimePos::ticksPerBar() / m_ppb / 2, 0));
}
m_scrollBack = false;
}
void PianoRoll::updatePosition( const TimePos & t )
void PianoRoll::updatePosition(const TimePos & t)
{
if( ( Engine::getSong()->isPlaying()
if ((Engine::getSong()->isPlaying()
&& Engine::getSong()->playMode() == Song::PlayMode::MidiClip
&& m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Enabled
) || m_scrollBack )
&& m_timeLine->autoScroll() != TimeLineWidget::AutoScrollState::Disabled
) || m_scrollBack)
{
autoScroll( t );
autoScroll(t);
}
// ticks relative to m_currentPosition
// < 0 = outside viewport left

View File

@@ -231,10 +231,10 @@ SongEditor::SongEditor( Song * song ) :
static_cast<QVBoxLayout *>( layout() )->insertWidget( 0, m_timeLine );
m_leftRightScroll = new QScrollBar( Qt::Horizontal, this );
m_leftRightScroll->setMinimum( 0 );
m_leftRightScroll->setMaximum( 0 );
m_leftRightScroll->setSingleStep( 1 );
m_leftRightScroll->setPageStep( 20 );
m_leftRightScroll->setMinimum(0);
m_leftRightScroll->setMaximum(0);
m_leftRightScroll->setSingleStep(1);
m_leftRightScroll->setPageStep(20 * TimePos::ticksPerBar());
static_cast<QVBoxLayout *>( layout() )->addWidget( m_leftRightScroll );
connect( m_leftRightScroll, SIGNAL(valueChanged(int)),
this, SLOT(scrolled(int)));
@@ -325,7 +325,7 @@ QString SongEditor::getSnapSizeString() const
void SongEditor::scrolled( int new_pos )
{
update();
emit positionChanged( m_currentPosition = TimePos( new_pos, 0 ) );
emit positionChanged(m_currentPosition = TimePos(new_pos));
}
@@ -384,7 +384,7 @@ void SongEditor::updateRubberband()
}
//take care of the scrollbar position
int hs = (m_leftRightScroll->value() - m_scrollPos.x()) * pixelsPerBar();
int hs = (m_leftRightScroll->value() - m_scrollPos.x()) * pixelsPerBar() / TimePos::ticksPerBar();
int vs = contentWidget()->verticalScrollBar()->value() - m_scrollPos.y();
//the adjusted origin point
@@ -522,8 +522,8 @@ void SongEditor::wheelEvent( QWheelEvent * we )
if ((we->modifiers() & Qt::ControlModifier) && (position(we).x() > m_trackHeadWidth))
{
int x = position(we).x() - m_trackHeadWidth;
// bar based on the mouse x-position where the scroll wheel was used
int bar = x / pixelsPerBar();
// tick based on the mouse x-position where the scroll wheel was used
int tick = x / pixelsPerBar() * TimePos::ticksPerBar();
// move zoom slider (pixelsPerBar will change automatically)
int step = we->modifiers() & Qt::ShiftModifier ? 1 : 5;
@@ -531,9 +531,9 @@ void SongEditor::wheelEvent( QWheelEvent * we )
int direction = (we->angleDelta().y() + we->angleDelta().x()) > 0 ? 1 : -1;
m_zoomingModel->incValue(step * direction);
// scroll to zooming around cursor's bar
int newBar = static_cast<int>(x / pixelsPerBar());
m_leftRightScroll->setValue(m_leftRightScroll->value() + bar - newBar);
// scroll to zooming around cursor's tick
int newTick = static_cast<int>(x / pixelsPerBar() * TimePos::ticksPerBar());
m_leftRightScroll->setValue(m_leftRightScroll->value() + tick - newTick);
// update timeline
m_timeLine->setPixelsPerBar(pixelsPerBar());
@@ -542,15 +542,15 @@ void SongEditor::wheelEvent( QWheelEvent * we )
}
// FIXME: Reconsider if determining orientation is necessary in Qt6.
else if(abs(we->angleDelta().x()) > abs(we->angleDelta().y())) // scrolling is horizontal
else if (std::abs(we->angleDelta().x()) > std::abs(we->angleDelta().y())) // scrolling is horizontal
{
m_leftRightScroll->setValue(m_leftRightScroll->value() -
we->angleDelta().x() /30);
m_leftRightScroll->setValue(m_leftRightScroll->value()
- we->angleDelta().x());
}
else if(we->modifiers() & Qt::ShiftModifier)
else if (we->modifiers() & Qt::ShiftModifier)
{
m_leftRightScroll->setValue(m_leftRightScroll->value() -
we->angleDelta().y() / 30);
m_leftRightScroll->setValue(m_leftRightScroll->value()
- we->angleDelta().y());
}
else
{
@@ -711,9 +711,9 @@ void SongEditor::hideMasterPitchFloat( void )
void SongEditor::updateScrollBar( int len )
void SongEditor::updateScrollBar(int len)
{
m_leftRightScroll->setMaximum( len );
m_leftRightScroll->setMaximum(len * TimePos::ticksPerBar());
}
@@ -756,22 +756,25 @@ void SongEditor::updatePosition( const TimePos & t )
const auto widgetWidth = compactTrackButtons ? DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT : DEFAULT_SETTINGS_WIDGET_WIDTH;
const auto trackOpWidth = compactTrackButtons ? TRACK_OP_WIDTH_COMPACT : TRACK_OP_WIDTH;
if( ( m_song->isPlaying() && m_song->m_playMode == Song::PlayMode::Song
&& m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Enabled) ||
m_scrollBack == true )
if ((m_song->isPlaying() && m_song->m_playMode == Song::PlayMode::Song)
|| m_scrollBack)
{
m_smoothScroll = ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt();
const int w = width() - widgetWidth
- trackOpWidth
- contentWidget()->verticalScrollBar()->width(); // width of right scrollbar
if( t > m_currentPosition + w * TimePos::ticksPerBar() /
pixelsPerBar() )
if (m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Stepped)
{
animateScroll( m_leftRightScroll, t.getBar(), m_smoothScroll );
const auto nextPosition = m_currentPosition + w * TimePos::ticksPerBar() / pixelsPerBar();
if (t > nextPosition || t < m_currentPosition)
{
animateScroll(m_leftRightScroll, t.getTicks(), m_smoothScroll);
}
}
else if( t < m_currentPosition )
else if (m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Continuous)
{
animateScroll( m_leftRightScroll, t.getBar(), m_smoothScroll );
animateScroll(m_leftRightScroll, std::max(t.getTicks() - w * TimePos::ticksPerBar() / pixelsPerBar() / 2, 0.0f), m_smoothScroll);
}
m_scrollBack = false;
}

View File

@@ -88,9 +88,10 @@ void TimeLineWidget::setXOffset(const int x)
void TimeLineWidget::addToolButtons( QToolBar * _tool_bar )
{
auto autoScroll = new NStateButton(_tool_bar);
autoScroll->setGeneralToolTip( tr( "Auto scrolling" ) );
autoScroll->addState( embed::getIconPixmap( "autoscroll_on" ) );
autoScroll->addState( embed::getIconPixmap( "autoscroll_off" ) );
autoScroll->setGeneralToolTip(tr("Auto scrolling"));
autoScroll->addState(embed::getIconPixmap("autoscroll_stepped_on"), tr("Stepped auto scrolling"));
autoScroll->addState(embed::getIconPixmap("autoscroll_continuous_on"), tr("Continuous auto scrolling"));
autoScroll->addState(embed::getIconPixmap("autoscroll_off"), tr("Auto scrolling disabled"));
connect( autoScroll, SIGNAL(changedState(int)), this,
SLOT(toggleAutoScroll(int)));

View File

@@ -292,6 +292,7 @@ void TrackContentWidget::changePosition( const TimePos & newPos )
setUpdatesEnabled( true );
// redraw background
updateBackground();
// update();
}
@@ -628,8 +629,8 @@ void TrackContentWidget::paintEvent( QPaintEvent * pe )
// Don't draw background on Pattern Editor
if (m_trackView->trackContainerView() != getGUI()->patternEditor()->m_editor)
{
p.drawTiledPixmap( rect(), m_background, QPoint(
tcv->currentPosition().getBar() * ppb, 0 ) );
p.drawTiledPixmap(rect(), m_background, QPoint(
tcv->currentPosition().getTicks() * ppb / TimePos::ticksPerBar(), 0));
}
}