Add splitting (and resizing) to all types of clips (#7477)
Allow for splitting and resizing all types of clips (automation, MIDI, sample, pattern, etc) using the knife tool in the Song Editor. --------- Co-authored-by: szeli1 <143485814+szeli1@users.noreply.github.com> Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
This commit is contained in:
@@ -68,7 +68,6 @@ public:
|
||||
using TimemapIterator = timeMap::const_iterator;
|
||||
|
||||
AutomationClip( AutomationTrack * _auto_track );
|
||||
AutomationClip( const AutomationClip & _clip_to_copy );
|
||||
~AutomationClip() override = default;
|
||||
|
||||
bool addObject( AutomatableModel * _obj, bool _search_dup = true );
|
||||
@@ -90,7 +89,7 @@ public:
|
||||
void setTension( QString _new_tension );
|
||||
|
||||
TimePos timeMapLength() const;
|
||||
void updateLength();
|
||||
void updateLength() override;
|
||||
|
||||
TimePos putValue(
|
||||
const TimePos & time,
|
||||
@@ -196,12 +195,22 @@ public:
|
||||
static int quantization() { return s_quantization; }
|
||||
static void setQuantization(int q) { s_quantization = q; }
|
||||
|
||||
AutomationClip* clone() override
|
||||
{
|
||||
return new AutomationClip(*this);
|
||||
}
|
||||
|
||||
void clearObjects() { m_objects.clear(); }
|
||||
|
||||
public slots:
|
||||
void clear();
|
||||
void objectDestroyed( lmms::jo_id_t );
|
||||
void flipY( int min, int max );
|
||||
void flipY();
|
||||
void flipX( int length = -1 );
|
||||
void flipX(int start = -1, int end = -1);
|
||||
|
||||
protected:
|
||||
AutomationClip( const AutomationClip & _clip_to_copy );
|
||||
|
||||
private:
|
||||
void cleanObjects();
|
||||
|
||||
@@ -75,6 +75,8 @@ private:
|
||||
|
||||
QStaticText m_staticTextName;
|
||||
void scaleTimemapToFit( float oldMin, float oldMax );
|
||||
|
||||
bool isResizableBeforeStart() override { return false; }
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ class AutomationEditor : public QWidget, public JournallingObject
|
||||
Q_PROPERTY(QColor ghostNoteColor MEMBER m_ghostNoteColor)
|
||||
Q_PROPERTY(QColor detuningNoteColor MEMBER m_detuningNoteColor)
|
||||
Q_PROPERTY(QColor ghostSampleColor MEMBER m_ghostSampleColor)
|
||||
Q_PROPERTY(QColor outOfBoundsShade MEMBER m_outOfBoundsShade)
|
||||
public:
|
||||
void setCurrentClip(AutomationClip * new_clip);
|
||||
void setGhostMidiClip(MidiClip* newMidiClip);
|
||||
@@ -291,6 +292,7 @@ private:
|
||||
QColor m_ghostNoteColor;
|
||||
QColor m_detuningNoteColor;
|
||||
QColor m_ghostSampleColor;
|
||||
QColor m_outOfBoundsShade;
|
||||
|
||||
SampleThumbnail m_sampleThumbnail;
|
||||
|
||||
|
||||
@@ -100,12 +100,28 @@ public:
|
||||
* resized by clicking and dragging its edge.
|
||||
*
|
||||
*/
|
||||
inline void setAutoResize( const bool r )
|
||||
inline void setResizable( const bool r )
|
||||
{
|
||||
m_resizable = r;
|
||||
}
|
||||
|
||||
inline const bool getResizable() const
|
||||
{
|
||||
return m_resizable;
|
||||
}
|
||||
|
||||
/*! \brief Set whether a clip has been resized yet by the user or the knife tool.
|
||||
*
|
||||
* If a clip has been resized previously, it will not automatically
|
||||
* resize when editing it.
|
||||
*
|
||||
*/
|
||||
void setAutoResize(const bool r)
|
||||
{
|
||||
m_autoResize = r;
|
||||
}
|
||||
|
||||
inline const bool getAutoResize() const
|
||||
bool getAutoResize() const
|
||||
{
|
||||
return m_autoResize;
|
||||
}
|
||||
@@ -115,6 +131,7 @@ public:
|
||||
|
||||
virtual void movePosition( const TimePos & pos );
|
||||
virtual void changeLength( const TimePos & length );
|
||||
virtual void updateLength() {};
|
||||
|
||||
virtual gui::ClipView * createView( gui::TrackView * tv ) = 0;
|
||||
|
||||
@@ -137,6 +154,12 @@ public:
|
||||
// Will copy the state of a clip to another clip
|
||||
static void copyStateTo( Clip *src, Clip *dst );
|
||||
|
||||
/**
|
||||
* Creates a copy of this clip
|
||||
* @return pointer to the new clip object
|
||||
*/
|
||||
virtual Clip* clone() = 0;
|
||||
|
||||
public slots:
|
||||
void toggleMute();
|
||||
|
||||
@@ -147,6 +170,8 @@ signals:
|
||||
void destroyedClip();
|
||||
void colorChanged();
|
||||
|
||||
protected:
|
||||
Clip(const Clip& other);
|
||||
|
||||
private:
|
||||
Track * m_track;
|
||||
@@ -158,7 +183,8 @@ private:
|
||||
|
||||
BoolModel m_mutedModel;
|
||||
BoolModel m_soloModel;
|
||||
bool m_autoResize;
|
||||
bool m_resizable = true;
|
||||
bool m_autoResize = true;
|
||||
|
||||
bool m_selectViewOnCreate;
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ class ClipView : public selectableObject, public ModelView
|
||||
Q_PROPERTY( QColor textShadowColor READ textShadowColor WRITE setTextShadowColor )
|
||||
Q_PROPERTY( QColor patternClipBackground READ patternClipBackground WRITE setPatternClipBackground )
|
||||
Q_PROPERTY( bool gradient READ gradient WRITE setGradient )
|
||||
Q_PROPERTY(QColor markerColor READ markerColor WRITE setMarkerColor)
|
||||
// We have to use a QSize here because using QPoint isn't supported.
|
||||
// width -> x, height -> y
|
||||
Q_PROPERTY( QSize mouseHotspotHand MEMBER m_mouseHotspotHand )
|
||||
@@ -94,6 +95,7 @@ public:
|
||||
QColor textBackgroundColor() const;
|
||||
QColor textShadowColor() const;
|
||||
QColor patternClipBackground() const;
|
||||
QColor markerColor() const;
|
||||
bool gradient() const;
|
||||
void setMutedColor( const QColor & c );
|
||||
void setMutedBackgroundColor( const QColor & c );
|
||||
@@ -103,6 +105,7 @@ public:
|
||||
void setTextShadowColor( const QColor & c );
|
||||
void setPatternClipBackground(const QColor& c);
|
||||
void setGradient( const bool & b );
|
||||
void setMarkerColor(const QColor& c);
|
||||
|
||||
// access needsUpdate member variable
|
||||
bool needsUpdate();
|
||||
@@ -121,10 +124,8 @@ public:
|
||||
// some metadata to be written to the clipboard.
|
||||
static void remove( QVector<ClipView *> clipvs );
|
||||
static void toggleMute( QVector<ClipView *> clipvs );
|
||||
static void mergeClips(QVector<ClipView*> clipvs);
|
||||
|
||||
// Returns true if selection can be merged and false if not
|
||||
static bool canMergeSelection(QVector<ClipView*> clipvs);
|
||||
void toggleSelectedAutoResize();
|
||||
|
||||
QColor getColorForDisplay( QColor );
|
||||
|
||||
@@ -147,8 +148,7 @@ protected:
|
||||
Cut,
|
||||
Copy,
|
||||
Paste,
|
||||
Mute,
|
||||
Merge
|
||||
Mute
|
||||
};
|
||||
|
||||
TrackView * m_trackView;
|
||||
@@ -176,7 +176,7 @@ protected:
|
||||
}
|
||||
|
||||
bool unquantizedModHeld( QMouseEvent * me );
|
||||
TimePos quantizeSplitPos( TimePos, bool shiftMode );
|
||||
TimePos quantizeSplitPos(TimePos);
|
||||
|
||||
float pixelsPerBar();
|
||||
|
||||
@@ -224,6 +224,7 @@ private:
|
||||
QColor m_textShadowColor;
|
||||
QColor m_patternClipBackground;
|
||||
bool m_gradient;
|
||||
QColor m_markerColor;
|
||||
QSize m_mouseHotspotHand; // QSize must be used because QPoint
|
||||
QSize m_mouseHotspotKnife; // isn't supported by property system
|
||||
QCursor m_cursorHand;
|
||||
@@ -244,8 +245,24 @@ private:
|
||||
TimePos draggedClipPos( QMouseEvent * me );
|
||||
int knifeMarkerPos( QMouseEvent * me );
|
||||
void setColor(const std::optional<QColor>& color);
|
||||
//! Return true iff the clip could be split. Currently only implemented for samples
|
||||
virtual bool splitClip( const TimePos pos ){ return false; };
|
||||
|
||||
//! Returns whether the user can left-resize this clip so that the start of the clip bounds is before the start of the clip content.
|
||||
virtual bool isResizableBeforeStart() { return true; };
|
||||
/**
|
||||
* Split this Clip into two clips
|
||||
* @param pos the position of the split, relative to the start of the clip
|
||||
* @return true if the clip could be split
|
||||
*/
|
||||
bool splitClip(const TimePos pos);
|
||||
/**
|
||||
* Destructively split this Clip into two clips. If the clip type does not implement this feature, it will default to normal splitting.
|
||||
* @param pos the position of the split, relative to the start of the clip
|
||||
* @return true if the clip could be split
|
||||
*/
|
||||
virtual bool destructiveSplitClip(const TimePos pos)
|
||||
{
|
||||
return splitClip(pos);
|
||||
}
|
||||
void updateCursor(QMouseEvent * me);
|
||||
} ;
|
||||
|
||||
|
||||
@@ -39,6 +39,10 @@ public:
|
||||
InlineAutomation()
|
||||
{
|
||||
}
|
||||
DetuningHelper(const DetuningHelper& _copy) :
|
||||
InlineAutomation(_copy)
|
||||
{
|
||||
}
|
||||
|
||||
~DetuningHelper() override = default;
|
||||
|
||||
|
||||
@@ -32,22 +32,24 @@
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class InlineAutomation : public FloatModel, public sharedObject
|
||||
class InlineAutomation : public FloatModel
|
||||
{
|
||||
public:
|
||||
InlineAutomation() :
|
||||
FloatModel(),
|
||||
sharedObject(),
|
||||
m_autoClip( nullptr )
|
||||
FloatModel()
|
||||
{
|
||||
}
|
||||
|
||||
InlineAutomation(const InlineAutomation& _copy) :
|
||||
FloatModel(_copy.value(), _copy.minValue(), _copy.maxValue(), _copy.step<float>()),
|
||||
m_autoClip(_copy.m_autoClip->clone())
|
||||
{
|
||||
m_autoClip->clearObjects();
|
||||
m_autoClip->addObject(this);
|
||||
}
|
||||
|
||||
~InlineAutomation() override
|
||||
{
|
||||
if( m_autoClip )
|
||||
{
|
||||
delete m_autoClip;
|
||||
}
|
||||
}
|
||||
|
||||
virtual float defaultValue() const = 0;
|
||||
@@ -81,10 +83,10 @@ public:
|
||||
{
|
||||
if( m_autoClip == nullptr )
|
||||
{
|
||||
m_autoClip = new AutomationClip( nullptr );
|
||||
m_autoClip = std::make_unique<AutomationClip>(nullptr);
|
||||
m_autoClip->addObject( this );
|
||||
}
|
||||
return m_autoClip;
|
||||
return m_autoClip.get();
|
||||
}
|
||||
|
||||
void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
|
||||
@@ -92,7 +94,7 @@ public:
|
||||
|
||||
|
||||
private:
|
||||
AutomationClip * m_autoClip;
|
||||
std::unique_ptr<AutomationClip> m_autoClip;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -53,12 +53,11 @@ public:
|
||||
} ;
|
||||
|
||||
MidiClip( InstrumentTrack* instrumentTrack );
|
||||
MidiClip( const MidiClip& other );
|
||||
~MidiClip() override;
|
||||
|
||||
void init();
|
||||
|
||||
void updateLength();
|
||||
void updateLength() override;
|
||||
|
||||
// note management
|
||||
Note * addNote( const Note & _new_note, const bool _quant_pos = true );
|
||||
@@ -117,6 +116,11 @@ public:
|
||||
|
||||
gui::ClipView * createView( gui::TrackView * _tv ) override;
|
||||
|
||||
MidiClip* clone() override
|
||||
{
|
||||
return new MidiClip(*this);
|
||||
}
|
||||
|
||||
|
||||
using Model::dataChanged;
|
||||
|
||||
@@ -127,6 +131,7 @@ public slots:
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
MidiClip( const MidiClip& other );
|
||||
void updatePatternTrack();
|
||||
|
||||
protected slots:
|
||||
|
||||
@@ -63,6 +63,11 @@ public:
|
||||
QColor const & getMutedNoteBorderColor() const { return m_mutedNoteBorderColor; }
|
||||
void setMutedNoteBorderColor(QColor const & color) { m_mutedNoteBorderColor = color; }
|
||||
|
||||
// Returns true if selection can be merged and false if not
|
||||
static bool canMergeSelection(QVector<ClipView*> clipvs);
|
||||
static void mergeClips(QVector<ClipView*> clipvs);
|
||||
static void bulkClearNotesOutOfBounds(QVector<ClipView*> clipvs);
|
||||
|
||||
public slots:
|
||||
lmms::MidiClip* getMidiClip();
|
||||
void update() override;
|
||||
@@ -76,6 +81,7 @@ protected slots:
|
||||
void resetName();
|
||||
void changeName();
|
||||
void transposeSelection();
|
||||
void clearNotesOutOfBounds();
|
||||
|
||||
|
||||
protected:
|
||||
@@ -103,6 +109,10 @@ private:
|
||||
QStaticText m_staticTextName;
|
||||
|
||||
bool m_legacySEPattern;
|
||||
|
||||
bool isResizableBeforeStart() override { return false; }
|
||||
|
||||
bool destructiveSplitClip(const TimePos pos) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#ifndef LMMS_NOTE_H
|
||||
#define LMMS_NOTE_H
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
@@ -107,6 +108,8 @@ public:
|
||||
Note( const Note & note );
|
||||
~Note() override;
|
||||
|
||||
Note& operator=(const Note& note);
|
||||
|
||||
// Note types
|
||||
enum class Type
|
||||
{
|
||||
@@ -236,7 +239,7 @@ public:
|
||||
|
||||
DetuningHelper * detuning() const
|
||||
{
|
||||
return m_detuning;
|
||||
return m_detuning.get();
|
||||
}
|
||||
bool hasDetuningInfo() const;
|
||||
bool withinRange(int tickStart, int tickEnd) const;
|
||||
@@ -262,7 +265,7 @@ private:
|
||||
panning_t m_panning;
|
||||
TimePos m_length;
|
||||
TimePos m_pos;
|
||||
DetuningHelper * m_detuning;
|
||||
std::unique_ptr<DetuningHelper> m_detuning;
|
||||
|
||||
Type m_type = Type::Regular;
|
||||
};
|
||||
|
||||
@@ -51,6 +51,11 @@ public:
|
||||
|
||||
gui::ClipView * createView( gui::TrackView * _tv ) override;
|
||||
|
||||
PatternClip* clone() override
|
||||
{
|
||||
return new PatternClip(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class PatternClipView;
|
||||
} ;
|
||||
|
||||
@@ -90,6 +90,7 @@ class PianoRoll : public QWidget
|
||||
Q_PROPERTY(int ghostNoteOpacity MEMBER m_ghostNoteOpacity)
|
||||
Q_PROPERTY(bool ghostNoteBorders MEMBER m_ghostNoteBorders)
|
||||
Q_PROPERTY(QColor backgroundShade MEMBER m_backgroundShade)
|
||||
Q_PROPERTY(QColor outOfBoundsShade MEMBER m_outOfBoundsShade)
|
||||
|
||||
/* white key properties */
|
||||
Q_PROPERTY(int whiteKeyWidth MEMBER m_whiteKeyWidth)
|
||||
@@ -516,6 +517,7 @@ private:
|
||||
bool m_noteBorders;
|
||||
bool m_ghostNoteBorders;
|
||||
QColor m_backgroundShade;
|
||||
QColor m_outOfBoundsShade;
|
||||
/* white key properties */
|
||||
int m_whiteKeyWidth;
|
||||
QColor m_whiteKeyActiveTextColor;
|
||||
|
||||
@@ -49,7 +49,6 @@ class SampleClip : public Clip
|
||||
public:
|
||||
SampleClip(Track* track, Sample sample, bool isPlaying);
|
||||
SampleClip(Track* track);
|
||||
SampleClip( const SampleClip& orig );
|
||||
~SampleClip() override;
|
||||
|
||||
SampleClip& operator=( const SampleClip& that ) = delete;
|
||||
@@ -81,6 +80,11 @@ public:
|
||||
void setIsPlaying(bool isPlaying);
|
||||
void setSampleBuffer(std::shared_ptr<const SampleBuffer> sb);
|
||||
|
||||
SampleClip* clone() override
|
||||
{
|
||||
return new SampleClip(*this);
|
||||
}
|
||||
|
||||
public slots:
|
||||
void setSampleFile(const QString& sf);
|
||||
void updateLength();
|
||||
@@ -88,6 +92,8 @@ public slots:
|
||||
void playbackPositionChanged();
|
||||
void updateTrackClips();
|
||||
|
||||
protected:
|
||||
SampleClip( const SampleClip& orig );
|
||||
|
||||
private:
|
||||
Sample m_sample;
|
||||
|
||||
@@ -68,7 +68,6 @@ private:
|
||||
SampleThumbnail m_sampleThumbnail;
|
||||
QPixmap m_paintPixmap;
|
||||
long m_paintPixmapXPosition;
|
||||
bool splitClip( const TimePos pos ) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user