Split TimeLineWidget into core and GUI parts (#7004)
This commit is contained in:
@@ -248,7 +248,6 @@ private slots:
|
||||
void onExportProject();
|
||||
void onExportProjectTracks();
|
||||
void onImportProject();
|
||||
void onSongStopped();
|
||||
void onSongModified();
|
||||
void onProjectFileNameChanged();
|
||||
|
||||
|
||||
@@ -55,25 +55,20 @@ public:
|
||||
|
||||
|
||||
public slots:
|
||||
void changeState( int _n );
|
||||
|
||||
void changeState(int state);
|
||||
|
||||
signals:
|
||||
void changedState( int _n );
|
||||
|
||||
void changedState(int state);
|
||||
|
||||
protected:
|
||||
void mousePressEvent( QMouseEvent * _me ) override;
|
||||
|
||||
void mousePressEvent(QMouseEvent* me) override;
|
||||
|
||||
private:
|
||||
QVector<QPair<QPixmap, QString> > m_states;
|
||||
QVector<QPair<QPixmap, QString>> m_states;
|
||||
QString m_generalToolTip;
|
||||
|
||||
int m_curState;
|
||||
|
||||
} ;
|
||||
|
||||
};
|
||||
|
||||
} // namespace lmms::gui
|
||||
|
||||
|
||||
@@ -25,16 +25,18 @@
|
||||
#ifndef LMMS_SONG_H
|
||||
#define LMMS_SONG_H
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
#include "TrackContainer.h"
|
||||
#include "AudioEngine.h"
|
||||
#include "Controller.h"
|
||||
#include "lmms_constants.h"
|
||||
#include "MeterModel.h"
|
||||
#include "Timeline.h"
|
||||
#include "TrackContainer.h"
|
||||
#include "VstSyncController.h"
|
||||
|
||||
namespace lmms
|
||||
@@ -105,7 +107,6 @@ public:
|
||||
public:
|
||||
PlayPos( const int abs = 0 ) :
|
||||
TimePos( abs ),
|
||||
m_timeLine( nullptr ),
|
||||
m_currentFrame( 0.0f )
|
||||
{
|
||||
}
|
||||
@@ -125,13 +126,11 @@ public:
|
||||
{
|
||||
return m_jumped;
|
||||
}
|
||||
gui::TimeLineWidget * m_timeLine;
|
||||
|
||||
private:
|
||||
float m_currentFrame;
|
||||
bool m_jumped;
|
||||
|
||||
} ;
|
||||
};
|
||||
|
||||
void processNextBuffer();
|
||||
|
||||
@@ -274,6 +273,11 @@ public:
|
||||
return getPlayPos(m_playMode);
|
||||
}
|
||||
|
||||
auto getTimeline(PlayMode mode) -> Timeline& { return m_timelines[static_cast<std::size_t>(mode)]; }
|
||||
auto getTimeline(PlayMode mode) const -> const Timeline& { return m_timelines[static_cast<std::size_t>(mode)]; }
|
||||
auto getTimeline() -> Timeline& { return getTimeline(m_playMode); }
|
||||
auto getTimeline() const -> const Timeline& { return getTimeline(m_playMode); }
|
||||
|
||||
void updateLength();
|
||||
bar_t length() const
|
||||
{
|
||||
@@ -402,7 +406,7 @@ private slots:
|
||||
|
||||
void masterVolumeChanged();
|
||||
|
||||
void savePos();
|
||||
void savePlayStartPosition();
|
||||
|
||||
void updateFramesPerTick();
|
||||
|
||||
@@ -481,6 +485,8 @@ private:
|
||||
|
||||
QHash<QString, int> m_errors;
|
||||
|
||||
std::array<Timeline, PlayModeCount> m_timelines;
|
||||
|
||||
PlayMode m_playMode;
|
||||
PlayPos m_playPos[PlayModeCount];
|
||||
bar_t m_length;
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
class QPixmap;
|
||||
class QToolBar;
|
||||
|
||||
namespace lmms {
|
||||
|
||||
class Timeline;
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
@@ -42,7 +48,7 @@ class TextFloat;
|
||||
class SongEditor;
|
||||
|
||||
|
||||
class TimeLineWidget : public QWidget, public JournallingObject
|
||||
class TimeLineWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -60,24 +66,10 @@ public:
|
||||
{
|
||||
Enabled,
|
||||
Disabled
|
||||
} ;
|
||||
};
|
||||
|
||||
enum class LoopPointState
|
||||
{
|
||||
Disabled,
|
||||
Enabled
|
||||
} ;
|
||||
|
||||
enum class BehaviourAtStopState
|
||||
{
|
||||
BackToZero,
|
||||
BackToStart,
|
||||
KeepStopPosition
|
||||
} ;
|
||||
|
||||
|
||||
TimeLineWidget(int xoff, int yoff, float ppb, Song::PlayPos & pos,
|
||||
const TimePos & begin, Song::PlayMode mode, QWidget * parent);
|
||||
TimeLineWidget(int xoff, int yoff, float ppb, Song::PlayPos& pos, Timeline& timeline,
|
||||
const TimePos& begin, Song::PlayMode mode, QWidget* parent);
|
||||
~TimeLineWidget() override;
|
||||
|
||||
inline QColor const & getBarLineColor() const { return m_barLineColor; }
|
||||
@@ -117,42 +109,6 @@ public:
|
||||
return m_autoScroll;
|
||||
}
|
||||
|
||||
BehaviourAtStopState behaviourAtStop() const
|
||||
{
|
||||
return m_behaviourAtStop;
|
||||
}
|
||||
|
||||
void setBehaviourAtStop (int state)
|
||||
{
|
||||
emit loadBehaviourAtStop (state);
|
||||
}
|
||||
|
||||
bool loopPointsEnabled() const
|
||||
{
|
||||
return m_loopPoints == LoopPointState::Enabled;
|
||||
}
|
||||
|
||||
inline const TimePos & loopBegin() const
|
||||
{
|
||||
return ( m_loopPos[0] < m_loopPos[1] ) ?
|
||||
m_loopPos[0] : m_loopPos[1];
|
||||
}
|
||||
|
||||
inline const TimePos & loopEnd() const
|
||||
{
|
||||
return ( m_loopPos[0] > m_loopPos[1] ) ?
|
||||
m_loopPos[0] : m_loopPos[1];
|
||||
}
|
||||
|
||||
inline void savePos( const TimePos & pos )
|
||||
{
|
||||
m_savedPos = pos;
|
||||
}
|
||||
inline const TimePos & savedPos() const
|
||||
{
|
||||
return m_savedPos;
|
||||
}
|
||||
|
||||
inline void setPixelsPerBar( float ppb )
|
||||
{
|
||||
m_ppb = ppb;
|
||||
@@ -163,14 +119,6 @@ public:
|
||||
|
||||
void addToolButtons(QToolBar* _tool_bar );
|
||||
|
||||
|
||||
void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
|
||||
void loadSettings( const QDomElement & _this ) override;
|
||||
inline QString nodeName() const override
|
||||
{
|
||||
return "timeline";
|
||||
}
|
||||
|
||||
inline int markerX( const TimePos & _t ) const
|
||||
{
|
||||
return m_xOffset + static_cast<int>( ( _t - m_begin ) *
|
||||
@@ -178,25 +126,17 @@ public:
|
||||
}
|
||||
|
||||
signals:
|
||||
|
||||
void positionChanged(const lmms::TimePos& postion);
|
||||
void regionSelectedFromPixels( int, int );
|
||||
void selectionFinished();
|
||||
|
||||
|
||||
public slots:
|
||||
void updatePosition( const lmms::TimePos & );
|
||||
void updatePosition()
|
||||
{
|
||||
updatePosition( TimePos() );
|
||||
}
|
||||
void updatePosition();
|
||||
void setSnapSize( const float snapSize )
|
||||
{
|
||||
m_snapSize = snapSize;
|
||||
}
|
||||
void toggleAutoScroll( int _n );
|
||||
void toggleLoopPoints( int _n );
|
||||
void toggleBehaviourAtStop( int _n );
|
||||
|
||||
|
||||
protected:
|
||||
void paintEvent( QPaintEvent * _pe ) override;
|
||||
@@ -222,8 +162,6 @@ private:
|
||||
QColor m_barNumberColor;
|
||||
|
||||
AutoScrollState m_autoScroll;
|
||||
LoopPointState m_loopPoints;
|
||||
BehaviourAtStopState m_behaviourAtStop;
|
||||
|
||||
bool m_changedPosition;
|
||||
|
||||
@@ -232,12 +170,9 @@ private:
|
||||
float m_ppb;
|
||||
float m_snapSize;
|
||||
Song::PlayPos & m_pos;
|
||||
Timeline* m_timeline;
|
||||
const TimePos & m_begin;
|
||||
const Song::PlayMode m_mode;
|
||||
TimePos m_loopPos[2];
|
||||
|
||||
TimePos m_savedPos;
|
||||
|
||||
|
||||
TextFloat * m_hint;
|
||||
int m_initalXSelect;
|
||||
@@ -253,17 +188,7 @@ private:
|
||||
} m_action;
|
||||
|
||||
int m_moveXOff;
|
||||
|
||||
|
||||
signals:
|
||||
void positionChanged( const lmms::TimePos & _t );
|
||||
void loopPointStateLoaded( int _n );
|
||||
void positionMarkerMoved();
|
||||
void loadBehaviourAtStop( int _n );
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace lmms::gui
|
||||
|
||||
|
||||
82
include/Timeline.h
Normal file
82
include/Timeline.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Timeline.h
|
||||
*
|
||||
* Copyright (c) 2023 Dominic Clark
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef LMMS_TIMELINE_H
|
||||
#define LMMS_TIMELINE_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "JournallingObject.h"
|
||||
#include "TimePos.h"
|
||||
|
||||
namespace lmms {
|
||||
|
||||
class Timeline : public QObject, public JournallingObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class StopBehaviour
|
||||
{
|
||||
BackToZero,
|
||||
BackToStart,
|
||||
KeepPosition
|
||||
};
|
||||
|
||||
auto loopBegin() const -> TimePos { return m_loopBegin; }
|
||||
auto loopEnd() const -> TimePos { return m_loopEnd; }
|
||||
auto loopEnabled() const -> bool { return m_loopEnabled; }
|
||||
|
||||
void setLoopBegin(TimePos begin);
|
||||
void setLoopEnd(TimePos end);
|
||||
void setLoopPoints(TimePos begin, TimePos end);
|
||||
void setLoopEnabled(bool enabled);
|
||||
|
||||
auto playStartPosition() const -> TimePos { return m_playStartPosition; }
|
||||
auto stopBehaviour() const -> StopBehaviour { return m_stopBehaviour; }
|
||||
|
||||
void setPlayStartPosition(TimePos position) { m_playStartPosition = position; }
|
||||
void setStopBehaviour(StopBehaviour behaviour);
|
||||
|
||||
auto nodeName() const -> QString override { return "timeline"; }
|
||||
|
||||
signals:
|
||||
void loopEnabledChanged(bool enabled);
|
||||
void stopBehaviourChanged(lmms::Timeline::StopBehaviour behaviour);
|
||||
|
||||
protected:
|
||||
void saveSettings(QDomDocument& doc, QDomElement& element) override;
|
||||
void loadSettings(const QDomElement& element) override;
|
||||
|
||||
private:
|
||||
TimePos m_loopBegin = TimePos{0};
|
||||
TimePos m_loopEnd = TimePos{DefaultTicksPerBar};
|
||||
bool m_loopEnabled = false;
|
||||
|
||||
StopBehaviour m_stopBehaviour = StopBehaviour::BackToStart;
|
||||
TimePos m_playStartPosition = TimePos{-1};
|
||||
};
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_TIMELINE_H
|
||||
@@ -74,6 +74,7 @@ set(LMMS_SRCS
|
||||
core/SerializingObject.cpp
|
||||
core/Song.cpp
|
||||
core/TempoSyncKnobModel.cpp
|
||||
core/Timeline.cpp
|
||||
core/TimePos.cpp
|
||||
core/ToolPlugin.cpp
|
||||
core/Track.cpp
|
||||
|
||||
@@ -52,16 +52,10 @@ SampleClip::SampleClip( Track * _track ) :
|
||||
connect( Engine::getSong(), SIGNAL(timeSignatureChanged(int,int)),
|
||||
this, SLOT(updateLength()));
|
||||
|
||||
//care about positionmarker
|
||||
gui::TimeLineWidget* timeLine = Engine::getSong()->getPlayPos( Song::PlayMode::Song ).m_timeLine;
|
||||
if( timeLine )
|
||||
{
|
||||
connect( timeLine, SIGNAL(positionMarkerMoved()), this, SLOT(playbackPositionChanged()));
|
||||
}
|
||||
//playbutton clicked or space key / on Export Song set isPlaying to false
|
||||
connect( Engine::getSong(), SIGNAL(playbackStateChanged()),
|
||||
this, SLOT(playbackPositionChanged()), Qt::DirectConnection );
|
||||
//care about loops
|
||||
//care about loops and jumps
|
||||
connect( Engine::getSong(), SIGNAL(updateSampleTracks()),
|
||||
this, SLOT(playbackPositionChanged()), Qt::DirectConnection );
|
||||
//care about mute Clips
|
||||
|
||||
@@ -184,14 +184,9 @@ void Song::setTimeSignature()
|
||||
|
||||
|
||||
|
||||
void Song::savePos()
|
||||
void Song::savePlayStartPosition()
|
||||
{
|
||||
gui::TimeLineWidget* tl = getPlayPos().m_timeLine;
|
||||
|
||||
if( tl != nullptr )
|
||||
{
|
||||
tl->savePos( getPlayPos() );
|
||||
}
|
||||
getTimeline().setPlayStartPosition(getPlayPos());
|
||||
}
|
||||
|
||||
|
||||
@@ -258,16 +253,17 @@ void Song::processNextBuffer()
|
||||
return false;
|
||||
};
|
||||
|
||||
const auto timeline = getPlayPos().m_timeLine;
|
||||
const auto loopEnabled = !m_exporting && timeline && timeline->loopPointsEnabled();
|
||||
const auto& timeline = getTimeline();
|
||||
const auto loopEnabled = !m_exporting && timeline.loopEnabled();
|
||||
|
||||
// Ensure playback begins within the loop if it is enabled
|
||||
if (loopEnabled) { enforceLoop(timeline->loopBegin(), timeline->loopEnd()); }
|
||||
if (loopEnabled) { enforceLoop(timeline.loopBegin(), timeline.loopEnd()); }
|
||||
|
||||
// Inform VST plugins if the user moved the play head
|
||||
// Inform VST plugins and sample tracks if the user moved the play head
|
||||
if (getPlayPos().jumped())
|
||||
{
|
||||
m_vstSyncController.setPlaybackJumped(true);
|
||||
emit updateSampleTracks();
|
||||
getPlayPos().setJumped(false);
|
||||
}
|
||||
|
||||
@@ -301,13 +297,13 @@ void Song::processNextBuffer()
|
||||
}
|
||||
|
||||
// Handle loop points, and inform VST plugins of the loop status
|
||||
if (loopEnabled || (m_loopRenderRemaining > 1 && getPlayPos() >= timeline->loopBegin()))
|
||||
if (loopEnabled || (m_loopRenderRemaining > 1 && getPlayPos() >= timeline.loopBegin()))
|
||||
{
|
||||
m_vstSyncController.startCycle(
|
||||
timeline->loopBegin().getTicks(), timeline->loopEnd().getTicks());
|
||||
timeline.loopBegin().getTicks(), timeline.loopEnd().getTicks());
|
||||
|
||||
// Loop if necessary, and decrement the remaining loops if we did
|
||||
if (enforceLoop(timeline->loopBegin(), timeline->loopEnd())
|
||||
if (enforceLoop(timeline.loopBegin(), timeline.loopEnd())
|
||||
&& m_loopRenderRemaining > 1)
|
||||
{
|
||||
m_loopRenderRemaining--;
|
||||
@@ -492,7 +488,7 @@ void Song::playSong()
|
||||
|
||||
m_vstSyncController.setPlaybackState( true );
|
||||
|
||||
savePos();
|
||||
savePlayStartPosition();
|
||||
|
||||
emit playbackStateChanged();
|
||||
}
|
||||
@@ -531,7 +527,7 @@ void Song::playPattern()
|
||||
|
||||
m_vstSyncController.setPlaybackState( true );
|
||||
|
||||
savePos();
|
||||
savePlayStartPosition();
|
||||
|
||||
emit playbackStateChanged();
|
||||
}
|
||||
@@ -556,7 +552,7 @@ void Song::playMidiClip( const MidiClip* midiClipToPlay, bool loop )
|
||||
m_paused = false;
|
||||
}
|
||||
|
||||
savePos();
|
||||
savePlayStartPosition();
|
||||
|
||||
emit playbackStateChanged();
|
||||
}
|
||||
@@ -644,40 +640,32 @@ void Song::stop()
|
||||
// To avoid race conditions with the processing threads
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
|
||||
TimeLineWidget * tl = getPlayPos().m_timeLine;
|
||||
auto& timeline = getTimeline();
|
||||
m_paused = false;
|
||||
m_recording = true;
|
||||
|
||||
if( tl )
|
||||
{
|
||||
switch( tl->behaviourAtStop() )
|
||||
{
|
||||
case TimeLineWidget::BehaviourAtStopState::BackToZero:
|
||||
getPlayPos().setTicks(0);
|
||||
m_elapsedMilliSeconds[static_cast<std::size_t>(m_playMode)] = 0;
|
||||
break;
|
||||
|
||||
case TimeLineWidget::BehaviourAtStopState::BackToStart:
|
||||
if( tl->savedPos() >= 0 )
|
||||
{
|
||||
getPlayPos().setTicks(tl->savedPos().getTicks());
|
||||
setToTime(tl->savedPos());
|
||||
|
||||
tl->savePos( -1 );
|
||||
}
|
||||
break;
|
||||
|
||||
case TimeLineWidget::BehaviourAtStopState::KeepStopPosition:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
getPlayPos().setTicks( 0 );
|
||||
m_elapsedMilliSeconds[static_cast<std::size_t>(m_playMode)] = 0;
|
||||
}
|
||||
m_playing = false;
|
||||
|
||||
switch (timeline.stopBehaviour())
|
||||
{
|
||||
case Timeline::StopBehaviour::BackToZero:
|
||||
getPlayPos().setTicks(0);
|
||||
m_elapsedMilliSeconds[static_cast<std::size_t>(m_playMode)] = 0;
|
||||
break;
|
||||
|
||||
case Timeline::StopBehaviour::BackToStart:
|
||||
if (timeline.playStartPosition() >= 0)
|
||||
{
|
||||
getPlayPos().setTicks(timeline.playStartPosition().getTicks());
|
||||
setToTime(timeline.playStartPosition());
|
||||
|
||||
timeline.setPlayStartPosition(-1);
|
||||
}
|
||||
break;
|
||||
|
||||
case Timeline::StopBehaviour::KeepPosition:
|
||||
break;
|
||||
}
|
||||
|
||||
m_elapsedMilliSeconds[static_cast<std::size_t>(PlayMode::None)] = m_elapsedMilliSeconds[static_cast<std::size_t>(m_playMode)];
|
||||
getPlayPos(PlayMode::None).setTicks(getPlayPos().getTicks());
|
||||
|
||||
@@ -719,37 +707,35 @@ void Song::startExport()
|
||||
m_exporting = true;
|
||||
updateLength();
|
||||
|
||||
const auto& timeline = getTimeline(PlayMode::Song);
|
||||
|
||||
if (m_renderBetweenMarkers)
|
||||
{
|
||||
m_exportSongBegin = m_exportLoopBegin = getPlayPos(PlayMode::Song).m_timeLine->loopBegin();
|
||||
m_exportSongEnd = m_exportLoopEnd = getPlayPos(PlayMode::Song).m_timeLine->loopEnd();
|
||||
m_exportSongBegin = m_exportLoopBegin = timeline.loopBegin();
|
||||
m_exportSongEnd = m_exportLoopEnd = timeline.loopEnd();
|
||||
|
||||
getPlayPos(PlayMode::Song).setTicks( getPlayPos(PlayMode::Song).m_timeLine->loopBegin().getTicks() );
|
||||
getPlayPos(PlayMode::Song).setTicks(timeline.loopBegin().getTicks());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_exportSongEnd = TimePos(m_length, 0);
|
||||
|
||||
// Handle potentially ridiculous loop points gracefully.
|
||||
if (m_loopRenderCount > 1 && getPlayPos(PlayMode::Song).m_timeLine->loopEnd() > m_exportSongEnd)
|
||||
if (m_loopRenderCount > 1 && timeline.loopEnd() > m_exportSongEnd)
|
||||
{
|
||||
m_exportSongEnd = getPlayPos(PlayMode::Song).m_timeLine->loopEnd();
|
||||
m_exportSongEnd = timeline.loopEnd();
|
||||
}
|
||||
|
||||
if (!m_exportLoop)
|
||||
m_exportSongEnd += TimePos(1,0);
|
||||
|
||||
m_exportSongBegin = TimePos(0,0);
|
||||
// FIXME: remove this check once we load timeline in headless mode
|
||||
if (getPlayPos(PlayMode::Song).m_timeLine)
|
||||
{
|
||||
m_exportLoopBegin = getPlayPos(PlayMode::Song).m_timeLine->loopBegin() < m_exportSongEnd &&
|
||||
getPlayPos(PlayMode::Song).m_timeLine->loopEnd() <= m_exportSongEnd ?
|
||||
getPlayPos(PlayMode::Song).m_timeLine->loopBegin() : TimePos(0,0);
|
||||
m_exportLoopEnd = getPlayPos(PlayMode::Song).m_timeLine->loopBegin() < m_exportSongEnd &&
|
||||
getPlayPos(PlayMode::Song).m_timeLine->loopEnd() <= m_exportSongEnd ?
|
||||
getPlayPos(PlayMode::Song).m_timeLine->loopEnd() : TimePos(0,0);
|
||||
}
|
||||
m_exportLoopBegin = timeline.loopBegin() < m_exportSongEnd && timeline.loopEnd() <= m_exportSongEnd
|
||||
? timeline.loopBegin()
|
||||
: TimePos{0};
|
||||
m_exportLoopEnd = timeline.loopBegin() < m_exportSongEnd && timeline.loopEnd() <= m_exportSongEnd
|
||||
? timeline.loopEnd()
|
||||
: TimePos{0};
|
||||
|
||||
getPlayPos(PlayMode::Song).setTicks( 0 );
|
||||
}
|
||||
@@ -1080,11 +1066,7 @@ void Song::loadProject( const QString & fileName )
|
||||
m_masterVolumeModel.loadSettings( dataFile.head(), "mastervol" );
|
||||
m_masterPitchModel.loadSettings( dataFile.head(), "masterpitch" );
|
||||
|
||||
if( getPlayPos(PlayMode::Song).m_timeLine )
|
||||
{
|
||||
// reset loop-point-state
|
||||
getPlayPos(PlayMode::Song).m_timeLine->toggleLoopPoints( 0 );
|
||||
}
|
||||
getTimeline(PlayMode::Song).setLoopEnabled(false);
|
||||
|
||||
if( !dataFile.content().firstChildElement( "track" ).isNull() )
|
||||
{
|
||||
@@ -1167,9 +1149,9 @@ void Song::loadProject( const QString & fileName )
|
||||
{
|
||||
getGUI()->getProjectNotes()->SerializingObject::restoreState( node.toElement() );
|
||||
}
|
||||
else if( node.nodeName() == getPlayPos(PlayMode::Song).m_timeLine->nodeName() )
|
||||
else if (node.nodeName() == getTimeline(PlayMode::Song).nodeName())
|
||||
{
|
||||
getPlayPos(PlayMode::Song).m_timeLine->restoreState( node.toElement() );
|
||||
getTimeline(PlayMode::Song).restoreState(node.toElement());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1253,7 +1235,7 @@ bool Song::saveProjectFile(const QString & filename, bool withResources)
|
||||
getGUI()->pianoRoll()->saveState( dataFile, dataFile.content() );
|
||||
getGUI()->automationEditor()->m_editor->saveState( dataFile, dataFile.content() );
|
||||
getGUI()->getProjectNotes()->SerializingObject::saveState( dataFile, dataFile.content() );
|
||||
getPlayPos(PlayMode::Song).m_timeLine->saveState( dataFile, dataFile.content() );
|
||||
getTimeline(PlayMode::Song).saveState(dataFile, dataFile.content());
|
||||
}
|
||||
|
||||
saveControllerStates( dataFile, dataFile.content() );
|
||||
|
||||
83
src/core/Timeline.cpp
Normal file
83
src/core/Timeline.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Timeline.cpp
|
||||
*
|
||||
* Copyright (c) 2023 Dominic Clark
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* 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 (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "Timeline.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <tuple>
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <QDomElement>
|
||||
|
||||
namespace lmms {
|
||||
|
||||
void Timeline::setLoopBegin(TimePos begin)
|
||||
{
|
||||
std::tie(m_loopBegin, m_loopEnd) = std::minmax(begin, TimePos{m_loopEnd});
|
||||
}
|
||||
|
||||
void Timeline::setLoopEnd(TimePos end)
|
||||
{
|
||||
std::tie(m_loopBegin, m_loopEnd) = std::minmax(TimePos{m_loopBegin}, end);
|
||||
}
|
||||
|
||||
void Timeline::setLoopPoints(TimePos begin, TimePos end)
|
||||
{
|
||||
std::tie(m_loopBegin, m_loopEnd) = std::minmax(begin, end);
|
||||
}
|
||||
|
||||
void Timeline::setLoopEnabled(bool enabled)
|
||||
{
|
||||
if (enabled != m_loopEnabled) {
|
||||
m_loopEnabled = enabled;
|
||||
emit loopEnabledChanged(m_loopEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
void Timeline::setStopBehaviour(StopBehaviour behaviour)
|
||||
{
|
||||
if (behaviour != m_stopBehaviour) {
|
||||
m_stopBehaviour = behaviour;
|
||||
emit stopBehaviourChanged(m_stopBehaviour);
|
||||
}
|
||||
}
|
||||
|
||||
void Timeline::saveSettings(QDomDocument& doc, QDomElement& element)
|
||||
{
|
||||
element.setAttribute("lp0pos", static_cast<int>(loopBegin()));
|
||||
element.setAttribute("lp1pos", static_cast<int>(loopEnd()));
|
||||
element.setAttribute("lpstate", static_cast<int>(loopEnabled()));
|
||||
element.setAttribute("stopbehaviour", static_cast<int>(stopBehaviour()));
|
||||
}
|
||||
|
||||
void Timeline::loadSettings(const QDomElement& element)
|
||||
{
|
||||
setLoopPoints(
|
||||
static_cast<TimePos>(element.attribute("lp0pos").toInt()),
|
||||
static_cast<TimePos>(element.attribute("lp1pos").toInt())
|
||||
);
|
||||
setLoopEnabled(static_cast<bool>(element.attribute("lpstate").toInt()));
|
||||
setStopBehaviour(static_cast<StopBehaviour>(element.attribute("stopbehaviour", "1").toInt()));
|
||||
}
|
||||
|
||||
} // namespace lmms
|
||||
@@ -226,8 +226,6 @@ MainWindow::MainWindow() :
|
||||
connect( Engine::getSong(), SIGNAL(playbackStateChanged()),
|
||||
this, SLOT(updatePlayPauseIcons()));
|
||||
|
||||
connect(Engine::getSong(), SIGNAL(stopped()), SLOT(onSongStopped()));
|
||||
|
||||
connect(Engine::getSong(), SIGNAL(modified()), SLOT(onSongModified()));
|
||||
connect(Engine::getSong(), SIGNAL(projectFileNameChanged()), SLOT(onProjectFileNameChanged()));
|
||||
|
||||
@@ -1606,42 +1604,6 @@ void MainWindow::onImportProject()
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onSongStopped()
|
||||
{
|
||||
Song * song = Engine::getSong();
|
||||
Song::PlayPos const & playPos = song->getPlayPos();
|
||||
|
||||
TimeLineWidget * tl = playPos.m_timeLine;
|
||||
|
||||
if( tl )
|
||||
{
|
||||
SongEditorWindow* songEditor = getGUI()->songEditor();
|
||||
switch( tl->behaviourAtStop() )
|
||||
{
|
||||
case TimeLineWidget::BehaviourAtStopState::BackToZero:
|
||||
if( songEditor && ( tl->autoScroll() == TimeLineWidget::AutoScrollState::Enabled ) )
|
||||
{
|
||||
songEditor->m_editor->updatePosition(0);
|
||||
}
|
||||
break;
|
||||
|
||||
case TimeLineWidget::BehaviourAtStopState::BackToStart:
|
||||
if( tl->savedPos() >= 0 )
|
||||
{
|
||||
if(songEditor && ( tl->autoScroll() == TimeLineWidget::AutoScrollState::Enabled ) )
|
||||
{
|
||||
songEditor->m_editor->updatePosition( TimePos(tl->savedPos().getTicks() ) );
|
||||
}
|
||||
tl->savePos( -1 );
|
||||
}
|
||||
break;
|
||||
|
||||
case TimeLineWidget::BehaviourAtStopState::KeepStopPosition:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onSongModified()
|
||||
{
|
||||
// Only update the window title if the code is executed from the GUI main thread.
|
||||
|
||||
@@ -132,13 +132,12 @@ AutomationEditor::AutomationEditor() :
|
||||
m_quantizeModel.setValue( m_quantizeModel.findText( "1/8" ) );
|
||||
|
||||
// add time-line
|
||||
m_timeLine = new TimeLineWidget( VALUES_WIDTH, 0, m_ppb,
|
||||
Engine::getSong()->getPlayPos(
|
||||
Song::PlayMode::AutomationClip ),
|
||||
m_currentPosition,
|
||||
Song::PlayMode::AutomationClip, this );
|
||||
connect( this, SIGNAL( positionChanged( const lmms::TimePos& ) ),
|
||||
m_timeLine, SLOT( updatePosition( const lmms::TimePos& ) ) );
|
||||
m_timeLine = new TimeLineWidget(VALUES_WIDTH, 0, m_ppb,
|
||||
Engine::getSong()->getPlayPos(Song::PlayMode::AutomationClip),
|
||||
Engine::getSong()->getTimeline(Song::PlayMode::AutomationClip),
|
||||
m_currentPosition, Song::PlayMode::AutomationClip, this
|
||||
);
|
||||
connect(this, &AutomationEditor::positionChanged, m_timeLine, &TimeLineWidget::updatePosition);
|
||||
connect( m_timeLine, SIGNAL( positionChanged( const lmms::TimePos& ) ),
|
||||
this, SLOT( updatePosition( const lmms::TimePos& ) ) );
|
||||
|
||||
@@ -1602,11 +1601,7 @@ void AutomationEditor::resizeEvent(QResizeEvent * re)
|
||||
}
|
||||
centerTopBottomScroll();
|
||||
|
||||
if( Engine::getSong() )
|
||||
{
|
||||
Engine::getSong()->getPlayPos( Song::PlayMode::AutomationClip
|
||||
).m_timeLine->setFixedWidth( width() );
|
||||
}
|
||||
m_timeLine->setFixedWidth(width());
|
||||
|
||||
updateTopBottomLevels();
|
||||
update();
|
||||
|
||||
@@ -265,12 +265,11 @@ PianoRoll::PianoRoll() :
|
||||
|
||||
// add time-line
|
||||
m_timeLine = new TimeLineWidget(m_whiteKeyWidth, 0, m_ppb,
|
||||
Engine::getSong()->getPlayPos(
|
||||
Song::PlayMode::MidiClip ),
|
||||
m_currentPosition,
|
||||
Song::PlayMode::MidiClip, this );
|
||||
connect( this, SIGNAL( positionChanged( const lmms::TimePos& ) ),
|
||||
m_timeLine, SLOT( updatePosition( const lmms::TimePos& ) ) );
|
||||
Engine::getSong()->getPlayPos(Song::PlayMode::MidiClip),
|
||||
Engine::getSong()->getTimeline(Song::PlayMode::MidiClip),
|
||||
m_currentPosition, Song::PlayMode::MidiClip, this
|
||||
);
|
||||
connect(this, &PianoRoll::positionChanged, m_timeLine, &TimeLineWidget::updatePosition);
|
||||
connect( m_timeLine, SIGNAL( positionChanged( const lmms::TimePos& ) ),
|
||||
this, SLOT( updatePosition( const lmms::TimePos& ) ) );
|
||||
|
||||
@@ -282,10 +281,7 @@ PianoRoll::PianoRoll() :
|
||||
this, SLOT( updatePositionStepRecording( const lmms::TimePos& ) ) );
|
||||
|
||||
// update timeline when in record-accompany mode
|
||||
connect( Engine::getSong()->getPlayPos( Song::PlayMode::Song ).m_timeLine,
|
||||
SIGNAL( positionChanged( const lmms::TimePos& ) ),
|
||||
this,
|
||||
SLOT( updatePositionAccompany( const lmms::TimePos& ) ) );
|
||||
connect(m_timeLine, &TimeLineWidget::positionChanged, this, &PianoRoll::updatePositionAccompany);
|
||||
// TODO
|
||||
/* connect( engine::getSong()->getPlayPos( Song::PlayMode::Pattern ).m_timeLine,
|
||||
SIGNAL( positionChanged( const lmms::TimePos& ) ),
|
||||
@@ -3734,8 +3730,7 @@ void PianoRoll::resizeEvent(QResizeEvent* re)
|
||||
{
|
||||
updatePositionLineHeight();
|
||||
updateScrollbars();
|
||||
Engine::getSong()->getPlayPos(Song::PlayMode::MidiClip)
|
||||
.m_timeLine->setFixedWidth(width());
|
||||
m_timeLine->setFixedWidth(width());
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -5167,7 +5162,8 @@ void PianoRollWindow::saveSettings( QDomDocument & doc, QDomElement & de )
|
||||
de.appendChild(markedSemiTonesRoot);
|
||||
}
|
||||
|
||||
de.setAttribute("stopbehaviour", static_cast<int>(m_editor->m_timeLine->behaviourAtStop()));
|
||||
de.setAttribute("stopbehaviour", static_cast<int>(
|
||||
Engine::getSong()->getTimeline(Song::PlayMode::MidiClip).stopBehaviour()));
|
||||
|
||||
MainWindow::saveWidgetState( this, de );
|
||||
}
|
||||
@@ -5182,7 +5178,8 @@ void PianoRollWindow::loadSettings( const QDomElement & de )
|
||||
|
||||
MainWindow::restoreWidgetState( this, de );
|
||||
|
||||
m_editor->m_timeLine->setBehaviourAtStop(de.attribute("stopbehaviour").toInt());
|
||||
Engine::getSong()->getTimeline(Song::PlayMode::MidiClip).setStopBehaviour(
|
||||
static_cast<Timeline::StopBehaviour>(de.attribute("stopbehaviour").toInt()));
|
||||
|
||||
// update margins here because we're later in the startup process
|
||||
// We can't earlier because everything is still starting with the
|
||||
|
||||
@@ -97,14 +97,12 @@ SongEditor::SongEditor( Song * song ) :
|
||||
m_zoomingModel->setParent(this);
|
||||
m_snappingModel->setParent(this);
|
||||
|
||||
m_timeLine = new TimeLineWidget( m_trackHeadWidth, 32,
|
||||
pixelsPerBar(),
|
||||
m_song->getPlayPos(Song::PlayMode::Song),
|
||||
m_currentPosition,
|
||||
Song::PlayMode::Song, this );
|
||||
connect( this, SIGNAL( positionChanged( const lmms::TimePos& ) ),
|
||||
m_song->getPlayPos(Song::PlayMode::Song).m_timeLine,
|
||||
SLOT( updatePosition( const lmms::TimePos& ) ) );
|
||||
m_timeLine = new TimeLineWidget(m_trackHeadWidth, 32, pixelsPerBar(),
|
||||
m_song->getPlayPos(Song::PlayMode::Song),
|
||||
m_song->getTimeline(Song::PlayMode::Song),
|
||||
m_currentPosition, Song::PlayMode::Song, this
|
||||
);
|
||||
connect(this, &TrackContainerView::positionChanged, m_timeLine, &TimeLineWidget::updatePosition);
|
||||
connect( m_timeLine, SIGNAL( positionChanged( const lmms::TimePos& ) ),
|
||||
this, SLOT( updatePosition( const lmms::TimePos& ) ) );
|
||||
connect( m_timeLine, SIGNAL(regionSelectedFromPixels(int,int)),
|
||||
@@ -560,7 +558,7 @@ void SongEditor::wheelEvent( QWheelEvent * we )
|
||||
m_leftRightScroll->setValue(m_leftRightScroll->value() + bar - newBar);
|
||||
|
||||
// update timeline
|
||||
m_song->getPlayPos(Song::PlayMode::Song).m_timeLine->setPixelsPerBar(pixelsPerBar());
|
||||
m_timeLine->setPixelsPerBar(pixelsPerBar());
|
||||
// and make sure, all Clip's are resized and relocated
|
||||
realignTracks();
|
||||
}
|
||||
@@ -808,8 +806,7 @@ void SongEditor::updatePosition( const TimePos & t )
|
||||
m_scrollBack = false;
|
||||
}
|
||||
|
||||
const int x = m_song->getPlayPos(Song::PlayMode::Song).m_timeLine->
|
||||
markerX( t ) + 8;
|
||||
const int x = m_timeLine->markerX(t) + 8;
|
||||
if( x >= trackOpWidth + widgetWidth -1 )
|
||||
{
|
||||
m_positionLine->show();
|
||||
@@ -872,7 +869,7 @@ void SongEditor::zoomingChanged()
|
||||
int ppb = calculatePixelsPerBar();
|
||||
setPixelsPerBar(ppb);
|
||||
|
||||
m_song->getPlayPos(Song::PlayMode::Song).m_timeLine->setPixelsPerBar(ppb);
|
||||
m_timeLine->setPixelsPerBar(ppb);
|
||||
realignTracks();
|
||||
updateRubberband();
|
||||
m_timeLine->setSnapSize(getSnapSize());
|
||||
|
||||
@@ -45,9 +45,8 @@ namespace
|
||||
constexpr int MIN_BAR_LABEL_DISTANCE = 35;
|
||||
}
|
||||
|
||||
TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppb,
|
||||
Song::PlayPos & pos, const TimePos & begin, Song::PlayMode mode,
|
||||
QWidget * parent ) :
|
||||
TimeLineWidget::TimeLineWidget(const int xoff, const int yoff, const float ppb, Song::PlayPos& pos, Timeline& timeline,
|
||||
const TimePos& begin, Song::PlayMode mode, QWidget* parent) :
|
||||
QWidget( parent ),
|
||||
m_inactiveLoopColor( 52, 63, 53, 64 ),
|
||||
m_inactiveLoopBrush( QColor( 255, 255, 255, 32 ) ),
|
||||
@@ -59,35 +58,28 @@ TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppb,
|
||||
m_barLineColor( 192, 192, 192 ),
|
||||
m_barNumberColor( m_barLineColor.darker( 120 ) ),
|
||||
m_autoScroll( AutoScrollState::Enabled ),
|
||||
m_loopPoints( LoopPointState::Disabled ),
|
||||
m_behaviourAtStop( BehaviourAtStopState::BackToZero ),
|
||||
m_changedPosition( true ),
|
||||
m_xOffset( xoff ),
|
||||
m_posMarkerX( 0 ),
|
||||
m_ppb( ppb ),
|
||||
m_snapSize( 1.0 ),
|
||||
m_pos( pos ),
|
||||
m_timeline{&timeline},
|
||||
m_begin( begin ),
|
||||
m_mode( mode ),
|
||||
m_savedPos( -1 ),
|
||||
m_hint( nullptr ),
|
||||
m_action( Action::NoAction ),
|
||||
m_moveXOff( 0 )
|
||||
{
|
||||
m_loopPos[0] = 0;
|
||||
m_loopPos[1] = DefaultTicksPerBar;
|
||||
|
||||
setAttribute( Qt::WA_OpaquePaintEvent, true );
|
||||
move( 0, yoff );
|
||||
|
||||
m_xOffset -= m_posMarkerPixmap.width() / 2;
|
||||
|
||||
setMouseTracking(true);
|
||||
m_pos.m_timeLine = this;
|
||||
|
||||
auto updateTimer = new QTimer(this);
|
||||
connect( updateTimer, SIGNAL(timeout()),
|
||||
this, SLOT(updatePosition()));
|
||||
connect(updateTimer, &QTimer::timeout, this, &TimeLineWidget::updatePosition);
|
||||
updateTimer->start( 1000 / 60 ); // 60 fps
|
||||
connect( Engine::getSong(), SIGNAL(timeSignatureChanged(int,int)),
|
||||
this, SLOT(update()));
|
||||
@@ -98,10 +90,6 @@ TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppb,
|
||||
|
||||
TimeLineWidget::~TimeLineWidget()
|
||||
{
|
||||
if( getGUI()->songEditor() )
|
||||
{
|
||||
m_pos.m_timeLine = nullptr;
|
||||
}
|
||||
delete m_hint;
|
||||
}
|
||||
|
||||
@@ -129,10 +117,10 @@ void TimeLineWidget::addToolButtons( QToolBar * _tool_bar )
|
||||
loopPoints->setGeneralToolTip( tr( "Loop points" ) );
|
||||
loopPoints->addState( embed::getIconPixmap( "loop_points_off" ) );
|
||||
loopPoints->addState( embed::getIconPixmap( "loop_points_on" ) );
|
||||
connect( loopPoints, SIGNAL(changedState(int)), this,
|
||||
SLOT(toggleLoopPoints(int)));
|
||||
connect( this, SIGNAL(loopPointStateLoaded(int)), loopPoints,
|
||||
SLOT(changeState(int)));
|
||||
connect(loopPoints, &NStateButton::changedState, m_timeline, &Timeline::setLoopEnabled);
|
||||
connect(m_timeline, &Timeline::loopEnabledChanged, loopPoints, &NStateButton::changeState);
|
||||
connect(m_timeline, &Timeline::loopEnabledChanged, this, static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
loopPoints->changeState(static_cast<int>(m_timeline->loopEnabled()));
|
||||
|
||||
auto behaviourAtStop = new NStateButton(_tool_bar);
|
||||
behaviourAtStop->addState( embed::getIconPixmap( "back_to_zero" ),
|
||||
@@ -144,50 +132,24 @@ void TimeLineWidget::addToolButtons( QToolBar * _tool_bar )
|
||||
"started" ) );
|
||||
behaviourAtStop->addState( embed::getIconPixmap( "keep_stop_position" ),
|
||||
tr( "After stopping keep position" ) );
|
||||
connect( behaviourAtStop, SIGNAL(changedState(int)), this,
|
||||
SLOT(toggleBehaviourAtStop(int)));
|
||||
connect( this, SIGNAL(loadBehaviourAtStop(int)), behaviourAtStop,
|
||||
SLOT(changeState(int)));
|
||||
behaviourAtStop->changeState( static_cast<int>(BehaviourAtStopState::BackToStart) );
|
||||
connect(behaviourAtStop, &NStateButton::changedState, m_timeline,
|
||||
[timeline = m_timeline](int value) {
|
||||
timeline->setStopBehaviour(static_cast<Timeline::StopBehaviour>(value));
|
||||
}
|
||||
);
|
||||
connect(m_timeline, &Timeline::stopBehaviourChanged, behaviourAtStop,
|
||||
[button = behaviourAtStop](Timeline::StopBehaviour value) {
|
||||
button->changeState(static_cast<int>(value));
|
||||
}
|
||||
);
|
||||
behaviourAtStop->changeState(static_cast<int>(m_timeline->stopBehaviour()));
|
||||
|
||||
_tool_bar->addWidget( autoScroll );
|
||||
_tool_bar->addWidget( loopPoints );
|
||||
_tool_bar->addWidget( behaviourAtStop );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TimeLineWidget::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
{
|
||||
_this.setAttribute( "lp0pos", (int) loopBegin() );
|
||||
_this.setAttribute( "lp1pos", (int) loopEnd() );
|
||||
_this.setAttribute( "lpstate", static_cast<int>(m_loopPoints) );
|
||||
_this.setAttribute( "stopbehaviour", static_cast<int>(m_behaviourAtStop) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TimeLineWidget::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
m_loopPos[0] = _this.attribute( "lp0pos" ).toInt();
|
||||
m_loopPos[1] = _this.attribute( "lp1pos" ).toInt();
|
||||
m_loopPoints = static_cast<LoopPointState>(
|
||||
_this.attribute( "lpstate" ).toInt() );
|
||||
update();
|
||||
emit loopPointStateLoaded( static_cast<int>(m_loopPoints) );
|
||||
|
||||
if( _this.hasAttribute( "stopbehaviour" ) )
|
||||
{
|
||||
emit loadBehaviourAtStop( _this.attribute( "stopbehaviour" ).toInt() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TimeLineWidget::updatePosition( const TimePos & )
|
||||
void TimeLineWidget::updatePosition()
|
||||
{
|
||||
const int new_x = markerX( m_pos );
|
||||
|
||||
@@ -200,34 +162,11 @@ void TimeLineWidget::updatePosition( const TimePos & )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TimeLineWidget::toggleAutoScroll( int _n )
|
||||
{
|
||||
m_autoScroll = static_cast<AutoScrollState>( _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TimeLineWidget::toggleLoopPoints( int _n )
|
||||
{
|
||||
m_loopPoints = static_cast<LoopPointState>( _n );
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TimeLineWidget::toggleBehaviourAtStop( int _n )
|
||||
{
|
||||
m_behaviourAtStop = static_cast<BehaviourAtStopState>( _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TimeLineWidget::paintEvent( QPaintEvent * )
|
||||
{
|
||||
QPainter p( this );
|
||||
@@ -242,11 +181,11 @@ void TimeLineWidget::paintEvent( QPaintEvent * )
|
||||
// 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 loopStart = markerX(m_timeline->loopBegin()) + 8;
|
||||
int const loopEndR = markerX(m_timeline->loopEnd()) + 9;
|
||||
int const loopRectWidth = loopEndR - loopStart;
|
||||
|
||||
bool const loopPointsActive = loopPointsEnabled();
|
||||
bool const loopPointsActive = m_timeline->loopEnabled();
|
||||
|
||||
// Draw the main rectangle (inner fill only)
|
||||
QRect outerRectangle( loopStart, loopRectMargin, loopRectWidth - 1, loopRectHeight - 1 );
|
||||
@@ -336,12 +275,12 @@ void TimeLineWidget::mousePressEvent( QMouseEvent* event )
|
||||
else if( event->button() == Qt::RightButton )
|
||||
{
|
||||
m_moveXOff = m_posMarkerPixmap.width() / 2;
|
||||
const TimePos t = m_begin + static_cast<int>( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * TimePos::ticksPerBar() / m_ppb );
|
||||
const TimePos loopMid = ( m_loopPos[0] + m_loopPos[1] ) / 2;
|
||||
|
||||
m_action = t < loopMid ? Action::MoveLoopBegin : Action::MoveLoopEnd;
|
||||
std::sort(std::begin(m_loopPos), std::end(m_loopPos));
|
||||
m_loopPos[( m_action == Action::MoveLoopBegin ) ? 0 : 1] = t;
|
||||
const auto cursorXOffset = std::max(event->x() - m_xOffset - m_moveXOff, 0);
|
||||
const TimePos timeAtCursor = m_begin + static_cast<int>(cursorXOffset * TimePos::ticksPerBar() / m_ppb);
|
||||
const TimePos loopMid = (m_timeline->loopBegin() + m_timeline->loopEnd()) / 2;
|
||||
|
||||
m_action = timeAtCursor < loopMid ? Action::MoveLoopBegin : Action::MoveLoopEnd;
|
||||
}
|
||||
|
||||
if( m_action == Action::MoveLoopBegin || m_action == Action::MoveLoopEnd )
|
||||
@@ -360,49 +299,53 @@ void TimeLineWidget::mousePressEvent( QMouseEvent* event )
|
||||
void TimeLineWidget::mouseMoveEvent( QMouseEvent* event )
|
||||
{
|
||||
parentWidget()->update(); // essential for widgets that this timeline had taken their mouse move event from.
|
||||
const TimePos t = m_begin + static_cast<int>( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * TimePos::ticksPerBar() / m_ppb );
|
||||
|
||||
const auto cursorXOffset = std::max(event->x() - m_xOffset - m_moveXOff, 0);
|
||||
TimePos timeAtCursor = m_begin + static_cast<int>(cursorXOffset * TimePos::ticksPerBar() / m_ppb);
|
||||
|
||||
switch( m_action )
|
||||
{
|
||||
case Action::MovePositionMarker:
|
||||
m_pos.setTicks(t.getTicks());
|
||||
Engine::getSong()->setToTime(t, m_mode);
|
||||
m_pos.setTicks(timeAtCursor.getTicks());
|
||||
Engine::getSong()->setToTime(timeAtCursor, m_mode);
|
||||
if (!( Engine::getSong()->isPlaying()))
|
||||
{
|
||||
//Song::PlayMode::None is used when nothing is being played.
|
||||
Engine::getSong()->setToTime(t, Song::PlayMode::None);
|
||||
Engine::getSong()->setToTime(timeAtCursor, Song::PlayMode::None);
|
||||
}
|
||||
m_pos.setCurrentFrame( 0 );
|
||||
m_pos.setJumped( true );
|
||||
updatePosition();
|
||||
positionMarkerMoved();
|
||||
break;
|
||||
|
||||
case Action::MoveLoopBegin:
|
||||
case Action::MoveLoopEnd:
|
||||
{
|
||||
const int i = m_action == Action::MoveLoopBegin ? 0 : 1;
|
||||
const auto otherPoint = m_action == Action::MoveLoopBegin
|
||||
? m_timeline->loopEnd()
|
||||
: m_timeline->loopBegin();
|
||||
const bool control = event->modifiers() & Qt::ControlModifier;
|
||||
if (control)
|
||||
{
|
||||
// no ctrl-press-hint when having ctrl pressed
|
||||
delete m_hint;
|
||||
m_hint = nullptr;
|
||||
m_loopPos[i] = t;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_loopPos[i] = t.quantize(m_snapSize);
|
||||
timeAtCursor = timeAtCursor.quantize(m_snapSize);
|
||||
}
|
||||
// Catch begin == end
|
||||
if (m_loopPos[0] == m_loopPos[1])
|
||||
if (timeAtCursor == otherPoint)
|
||||
{
|
||||
const int offset = control ? 1 : m_snapSize * TimePos::ticksPerBar();
|
||||
// Note, swap 1 and 0 below and the behavior "skips" the other
|
||||
// marking instead of pushing it.
|
||||
if (m_action == Action::MoveLoopBegin) { m_loopPos[0] -= offset; }
|
||||
else { m_loopPos[1] += offset; }
|
||||
if (m_action == Action::MoveLoopBegin) { timeAtCursor -= offset; }
|
||||
else { timeAtCursor += offset; }
|
||||
}
|
||||
// Update m_action so we still move the correct point even if it is
|
||||
// dragged past the other.
|
||||
m_action = timeAtCursor < otherPoint ? Action::MoveLoopBegin : Action::MoveLoopEnd;
|
||||
m_timeline->setLoopPoints(timeAtCursor, otherPoint);
|
||||
update();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -67,34 +67,27 @@ void NStateButton::addState( const QPixmap & _pm, const QString & _tooltip )
|
||||
|
||||
|
||||
|
||||
void NStateButton::changeState( int _n )
|
||||
void NStateButton::changeState(int state)
|
||||
{
|
||||
if( _n >= 0 && _n < (int) m_states.size() )
|
||||
if (state >= 0 && state < m_states.size() && state != m_curState)
|
||||
{
|
||||
m_curState = _n;
|
||||
m_curState = state;
|
||||
|
||||
const QString & _tooltip =
|
||||
( m_states[m_curState].second != "" ) ?
|
||||
m_states[m_curState].second :
|
||||
m_generalToolTip;
|
||||
setToolTip(_tooltip);
|
||||
const auto& [icon, tooltip] = m_states[m_curState];
|
||||
setToolTip(tooltip.isEmpty() ? m_generalToolTip : tooltip);
|
||||
setIcon(icon);
|
||||
|
||||
setIcon( m_states[m_curState].first );
|
||||
|
||||
emit changedState( m_curState );
|
||||
emit changedState(m_curState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void NStateButton::mousePressEvent( QMouseEvent * _me )
|
||||
void NStateButton::mousePressEvent(QMouseEvent* me)
|
||||
{
|
||||
if( _me->button() == Qt::LeftButton && m_states.size() )
|
||||
if (me->button() == Qt::LeftButton && !m_states.empty())
|
||||
{
|
||||
changeState( ( ++m_curState ) % m_states.size() );
|
||||
changeState((m_curState + 1) % m_states.size());
|
||||
}
|
||||
ToolButton::mousePressEvent( _me );
|
||||
ToolButton::mousePressEvent(me);
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms::gui
|
||||
} // namespace lmms::gui
|
||||
|
||||
Reference in New Issue
Block a user