Enhanced snapping in song editor (#4973)

* New default behavior: Preserves offsets when moving clips, resizes in fixed increments.

* Adds shift + drag: Snaps move start position (like current behavior) or end position (new),
based on which is closest to the real position. When moving a selection,
the grabbed clip snaps into position and the rest move relative to it.

* Adds alt + drag: Allows fine adjustment of a clip's position or size,
as an alternative to ctrl + drag.

* Adds a Q dropdown in the song editor to allow finer or coarser snapping (8 bars to 1/16th bar)

* Adds a proportional snap toggle. When enabled, snapping size/Q adjusts based on zoom,
and a label appears showing the current snap size. This is disabled by default.
This commit is contained in:
Spekular
2019-07-27 11:14:49 +02:00
committed by Hyunjin Song
parent 7492e53b02
commit 1c715bc335
10 changed files with 328 additions and 92 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

View File

@@ -63,7 +63,7 @@ public:
MidiTime( const tact_t tact, const tick_t ticks );
MidiTime( const tick_t ticks = 0 );
MidiTime toNearestTact() const;
MidiTime quantize(float) const;
MidiTime toAbsoluteTact() const;
MidiTime& operator+=( const MidiTime& time );
@@ -110,4 +110,3 @@ private:
#endif

View File

@@ -73,6 +73,9 @@ public:
void loadSettings( const QDomElement& element );
ComboBoxModel *zoomingModel() const;
ComboBoxModel *snappingModel() const;
float getSnapSize() const;
QString getSnapSizeString() const;
public slots:
void scrolled( int new_pos );
@@ -80,6 +83,7 @@ public slots:
void setEditMode( EditMode mode );
void setEditModeDraw();
void setEditModeSelect();
void toggleProportionalSnap();
void updatePosition( const MidiTime & t );
void updatePositionLine();
@@ -130,6 +134,8 @@ private:
positionLine * m_positionLine;
ComboBoxModel* m_zoomingModel;
ComboBoxModel* m_snappingModel;
bool m_proportionalSnap;
static const QVector<double> m_zoomLevels;
@@ -141,7 +147,6 @@ private:
EditMode m_ctrlMode; // mode they were in before they hit ctrl
friend class SongEditorWindow;
} ;
@@ -170,6 +175,8 @@ protected slots:
void lostFocus();
void adjustUiAfterProjectLoad();
void updateSnapLabel();
signals:
void playTriggered();
void resized();
@@ -181,6 +188,7 @@ private:
QAction* m_addBBTrackAction;
QAction* m_addSampleTrackAction;
QAction* m_addAutomationTrackAction;
QAction* m_setProportionalSnapAction;
ActionGroup * m_editModeGroup;
QAction* m_drawModeAction;
@@ -188,6 +196,8 @@ private:
QAction* m_crtlAction;
ComboBox * m_zoomingComboBox;
ComboBox * m_snappingComboBox;
QLabel* m_snapSizeLabel;
};
#endif

View File

@@ -236,7 +236,7 @@ public:
// access needsUpdate member variable
bool needsUpdate();
void setNeedsUpdate( bool b );
public slots:
virtual bool close();
void cut();
@@ -297,6 +297,9 @@ private:
Actions m_action;
QPoint m_initialMousePos;
QPoint m_initialMouseGlobalPos;
MidiTime m_initialTCOPos;
MidiTime m_initialTCOEnd;
QVector<MidiTime> m_initialOffsets;
TextFloat * m_hint;
@@ -311,14 +314,17 @@ private:
bool m_gradient;
bool m_needsUpdate;
inline void setInitialMousePos( QPoint pos )
inline void setInitialPos( QPoint pos )
{
m_initialMousePos = pos;
m_initialMouseGlobalPos = mapToGlobal( pos );
m_initialTCOPos = m_tco->startPosition();
m_initialTCOEnd = m_initialTCOPos + m_tco->length();
}
void setInitialOffsets();
bool mouseMovedDistance( QMouseEvent * me, int distance );
MidiTime draggedTCOPos( QMouseEvent * me );
} ;
@@ -564,13 +570,13 @@ public:
using Model::dataChanged;
inline int getHeight()
inline int getHeight()
{
return m_height >= MINIMAL_TRACK_HEIGHT
? m_height
? m_height
: DEFAULT_TRACK_HEIGHT;
}
inline void setHeight( int height )
inline void setHeight( int height )
{
m_height = height;
}

View File

@@ -267,6 +267,9 @@ TrackContentObjectView::TrackContentObjectView( TrackContentObject * tco,
m_action( NoAction ),
m_initialMousePos( QPoint( 0, 0 ) ),
m_initialMouseGlobalPos( QPoint( 0, 0 ) ),
m_initialTCOPos( MidiTime(0) ),
m_initialTCOEnd( MidiTime(0) ),
m_initialOffsets( QVector<MidiTime>() ),
m_hint( NULL ),
m_mutedColor( 0, 0, 0 ),
m_mutedBackgroundColor( 0, 0, 0 ),
@@ -524,7 +527,7 @@ void TrackContentObjectView::updatePosition()
void TrackContentObjectView::dragEnterEvent( QDragEnterEvent * dee )
{
TrackContentWidget * tcw = getTrackView()->getTrackContentWidget();
MidiTime tcoPos = MidiTime( m_tco->startPosition().getTact(), 0 );
MidiTime tcoPos = MidiTime( m_tco->startPosition() );
if( tcw->canPasteSelection( tcoPos, dee ) == false )
{
dee->ignore();
@@ -563,7 +566,7 @@ void TrackContentObjectView::dropEvent( QDropEvent * de )
if( m_trackView->trackContainerView()->allowRubberband() == true )
{
TrackContentWidget * tcw = getTrackView()->getTrackContentWidget();
MidiTime tcoPos = MidiTime( m_tco->startPosition().getTact(), 0 );
MidiTime tcoPos = MidiTime( m_tco->startPosition() );
if( tcw->pasteSelection( tcoPos, de ) == true )
{
de->accept();
@@ -711,7 +714,8 @@ void TrackContentObjectView::paintTextLabel(QString const & text, QPainter & pai
*/
void TrackContentObjectView::mousePressEvent( QMouseEvent * me )
{
setInitialMousePos( me->pos() );
setInitialPos( me->pos() );
setInitialOffsets();
if( !fixedTCOs() && me->button() == Qt::LeftButton )
{
if( me->modifiers() & Qt::ControlModifier )
@@ -725,7 +729,9 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me )
m_action = ToggleSelected;
}
}
else if( !me->modifiers() )
else if( !me->modifiers()
|| (me->modifiers() & Qt::AltModifier)
|| (me->modifiers() & Qt::ShiftModifier) )
{
if( isSelected() )
{
@@ -739,7 +745,8 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me )
// move or resize
m_tco->setJournalling( false );
setInitialMousePos( me->pos() );
setInitialPos( me->pos() );
setInitialOffsets();
SampleTCO * sTco = dynamic_cast<SampleTCO*>( m_tco );
if( me->x() < RESIZE_GRIP_WIDTH && sTco
@@ -889,76 +896,86 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me )
const float ppt = m_trackView->trackContainerView()->pixelsPerTact();
if( m_action == Move )
{
const int x = mapToParent( me->pos() ).x() - m_initialMousePos.x();
MidiTime t = qMax( 0, (int)
m_trackView->trackContainerView()->currentPosition()+
static_cast<int>( x * MidiTime::ticksPerTact() /
ppt ) );
if( ! ( me->modifiers() & Qt::ControlModifier )
&& me->button() == Qt::NoButton )
{
t = t.toNearestTact();
}
m_tco->movePosition( t );
MidiTime newPos = draggedTCOPos( me );
// Don't go left of bar zero
newPos = max( 0, newPos.getTicks() );
m_tco->movePosition( newPos );
m_trackView->getTrackContentWidget()->changePosition();
s_textFloat->setText( QString( "%1:%2" ).
arg( m_tco->startPosition().getTact() + 1 ).
arg( m_tco->startPosition().getTicks() %
arg( newPos.getTact() + 1 ).
arg( newPos.getTicks() %
MidiTime::ticksPerTact() ) );
s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2 ) );
}
else if( m_action == MoveSelection )
{
const int dx = me->x() - m_initialMousePos.x();
const bool snap = !(me->modifiers() & Qt::ControlModifier) &&
me->button() == Qt::NoButton;
// 1: Find the position we want to move the grabbed TCO to
MidiTime newPos = draggedTCOPos( me );
// 2: Handle moving the other selected TCOs the same distance
QVector<selectableObject *> so =
m_trackView->trackContainerView()->selectedObjects();
QVector<TrackContentObject *> tcos;
int smallestPos = 0;
MidiTime dtick = MidiTime( static_cast<int>( dx *
MidiTime::ticksPerTact() / ppt ) );
if( snap )
{
dtick = dtick.toNearestTact();
}
// find out smallest position of all selected objects for not
// moving an object before zero
QVector<TrackContentObject *> tcos; // List of selected clips
int leftmost = 0; // Leftmost clip's offset from grabbed clip
// Populate tcos, find leftmost
for( QVector<selectableObject *>::iterator it = so.begin();
it != so.end(); ++it )
{
TrackContentObjectView * tcov =
dynamic_cast<TrackContentObjectView *>( *it );
if( tcov == NULL )
{
continue;
}
TrackContentObject * tco = tcov->m_tco;
tcos.push_back( tco );
smallestPos = qMin<int>( smallestPos,
(int)tco->startPosition() + dtick );
}
dtick -= smallestPos;
if( snap )
{
dtick = dtick.toAbsoluteTact(); // round toward 0
if( tcov == NULL ) { continue; }
tcos.push_back( tcov->m_tco );
int index = std::distance( so.begin(), it );
leftmost = min (leftmost, m_initialOffsets[index].getTicks() );
}
// Make sure the leftmost clip doesn't get moved to a negative position
if ( newPos.getTicks() + leftmost < 0 ) { newPos = -leftmost; }
for( QVector<TrackContentObject *>::iterator it = tcos.begin();
it != tcos.end(); ++it )
{
( *it )->movePosition( ( *it )->startPosition() + dtick );
int index = std::distance( tcos.begin(), it );
( *it )->movePosition( newPos + m_initialOffsets[index] );
}
}
else if( m_action == Resize || m_action == ResizeLeft )
{
// If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize
const bool unquantized = (me->modifiers() & Qt::ControlModifier) || (me->modifiers() & Qt::AltModifier);
const float snapSize = gui->songEditor()->m_editor->getSnapSize();
// Length in ticks of one snap increment
const MidiTime snapLength = MidiTime( (int)(snapSize * MidiTime::ticksPerTact()) );
if( m_action == Resize )
{
MidiTime t = qMax( MidiTime::ticksPerTact() / 16, static_cast<int>( me->x() * MidiTime::ticksPerTact() / ppt ) );
if( ! ( me->modifiers() & Qt::ControlModifier ) && me->button() == Qt::NoButton )
{
t = qMax<int>( MidiTime::ticksPerTact(), t.toNearestTact() );
// The clip's new length
MidiTime l = static_cast<int>( me->x() * MidiTime::ticksPerTact() / ppt );
if ( unquantized )
{ // We want to preserve this adjusted offset,
// even if the user switches to snapping later
setInitialPos( m_initialMousePos );
// Don't resize to less than 1 tick
m_tco->changeLength( qMax<int>( 1, l ) );
}
else if ( me->modifiers() & Qt::ShiftModifier )
{ // If shift is held, quantize clip's end position
MidiTime end = MidiTime( m_initialTCOPos + l ).quantize( snapSize );
// The end position has to be after the clip's start
MidiTime min = m_initialTCOPos.quantize( snapSize );
if ( min <= m_initialTCOPos ) min += snapLength;
m_tco->changeLength( qMax<int>(min - m_initialTCOPos, end - m_initialTCOPos) );
}
else
{ // Otherwise, resize in fixed increments
MidiTime initialLength = m_initialTCOEnd - m_initialTCOPos;
MidiTime offset = MidiTime( l - initialLength ).quantize( snapSize );
// Don't resize to less than 1 tick
MidiTime min = MidiTime( initialLength % snapLength );
if (min < 1) min += snapLength;
m_tco->changeLength( qMax<int>( min, initialLength + offset) );
}
m_tco->changeLength( t );
}
else
{
@@ -969,15 +986,34 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me )
MidiTime t = qMax( 0, (int)
m_trackView->trackContainerView()->currentPosition()+
static_cast<int>( x * MidiTime::ticksPerTact() /
ppt ) );
if( ! ( me->modifiers() & Qt::ControlModifier )
&& me->button() == Qt::NoButton )
{
t = t.toNearestTact();
static_cast<int>( x * MidiTime::ticksPerTact() / ppt ) );
if( unquantized )
{ // We want to preserve this adjusted offset,
// even if the user switches to snapping later
setInitialPos( m_initialMousePos );
//Don't resize to less than 1 tick
t = qMin<int>( m_initialTCOEnd - 1, t);
}
else if( me->modifiers() & Qt::ShiftModifier )
{ // If shift is held, quantize clip's start position
// Don't let the start position move past the end position
MidiTime max = m_initialTCOEnd.quantize( snapSize );
if ( max >= m_initialTCOEnd ) max -= snapLength;
t = qMin<int>( max, t.quantize( snapSize ) );
}
else
{ // Otherwise, resize in fixed increments
// Don't resize to less than 1 tick
MidiTime initialLength = m_initialTCOEnd - m_initialTCOPos;
MidiTime minLength = MidiTime( initialLength % snapLength );
if (minLength < 1) minLength += snapLength;
MidiTime offset = MidiTime(t - m_initialTCOPos).quantize( snapSize );
t = qMin<int>( m_initialTCOEnd - minLength, m_initialTCOPos + offset );
}
MidiTime oldPos = m_tco->startPosition();
if( m_tco->length() + ( oldPos - t ) >= MidiTime::ticksPerTact() )
if( m_tco->length() + ( oldPos - t ) >= 1 )
{
m_tco->movePosition( t );
m_trackView->getTrackContentWidget()->changePosition();
@@ -1091,7 +1127,6 @@ void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme )
/*! \brief How many pixels a tact (bar) takes for this trackContentObjectView.
*
* \return the number of pixels per tact (bar).
@@ -1102,6 +1137,27 @@ float TrackContentObjectView::pixelsPerTact()
}
/*! \brief Save the offsets between all selected tracks and a clicked track */
void TrackContentObjectView::setInitialOffsets()
{
QVector<selectableObject *> so = m_trackView->trackContainerView()->selectedObjects();
QVector<MidiTime> offsets;
for( QVector<selectableObject *>::iterator it = so.begin();
it != so.end(); ++it )
{
TrackContentObjectView * tcov =
dynamic_cast<TrackContentObjectView *>( *it );
if( tcov == NULL )
{
continue;
}
offsets.push_back( tcov->m_tco->startPosition() - m_initialTCOPos );
}
m_initialOffsets = offsets;
}
/*! \brief Detect whether the mouse moved more than n pixels on screen.
@@ -1118,6 +1174,49 @@ bool TrackContentObjectView::mouseMovedDistance( QMouseEvent * me, int distance
/*! \brief Calculate the new position of a dragged TCO from a mouse event
*
*
* \param me The QMouseEvent
*/
MidiTime TrackContentObjectView::draggedTCOPos( QMouseEvent * me )
{
//Pixels per tact
const float ppt = m_trackView->trackContainerView()->pixelsPerTact();
// The pixel distance that the mouse has moved
const int mouseOff = mapToGlobal(me->pos()).x() - m_initialMouseGlobalPos.x();
MidiTime newPos = m_initialTCOPos + mouseOff * MidiTime::ticksPerTact() / ppt;
MidiTime offset = newPos - m_initialTCOPos;
// If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize
if ( me->button() != Qt::NoButton
|| (me->modifiers() & Qt::ControlModifier)
|| (me->modifiers() & Qt::AltModifier) )
{
// We want to preserve this adjusted offset,
// even if the user switches to snapping
setInitialPos( m_initialMousePos );
}
else if ( me->modifiers() & Qt::ShiftModifier )
{ // If shift is held, quantize position (Default in 1.2.0 and earlier)
// or end position, whichever is closest to the actual position
MidiTime startQ = newPos.quantize( gui->songEditor()->m_editor->getSnapSize() );
// Find start position that gives snapped clip end position
MidiTime endQ = ( newPos + m_tco->length() );
endQ = endQ.quantize( gui->songEditor()->m_editor->getSnapSize() );
endQ = endQ - m_tco->length();
// Select the position closest to actual position
if ( abs(newPos - startQ) < abs(newPos - endQ) ) newPos = startQ;
else newPos = endQ;
}
else
{ // Otherwise, quantize moved distance (preserves user offsets)
newPos = m_initialTCOPos + offset.quantize( gui->songEditor()->m_editor->getSnapSize() );
}
return newPos;
}
// ===========================================================================
// trackContentWidget
@@ -1496,7 +1595,6 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de )
int initialTrackIndex = tiAttr.value().toInt();
QDomAttr tcoPosAttr = metadata.attributeNode( "grabbedTCOPos" );
MidiTime grabbedTCOPos = tcoPosAttr.value().toInt();
MidiTime grabbedTCOTact = MidiTime( grabbedTCOPos.getTact(), 0 );
// Snap the mouse position to the beginning of the dropped tact, in ticks
const TrackContainer::TrackList tracks = getTrack()->trackContainer()->tracks();
@@ -1517,6 +1615,10 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de )
// TODO -- Need to draw the hovericon either way, or ghost the TCOs
// onto their final position.
// All patterns should be offset the same amount as the grabbed pattern
// The offset is quantized (rather than the positions) to preserve fine adjustments
int offset = MidiTime(tcoPos - grabbedTCOPos).quantize(gui->songEditor()->m_editor->getSnapSize());
for( int i = 0; i<tcoNodes.length(); i++ )
{
QDomElement outerTCOElement = tcoNodes.item( i ).toElement();
@@ -1526,13 +1628,11 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de )
int finalTrackIndex = trackIndex + ( currentTrackIndex - initialTrackIndex );
Track * t = tracks.at( finalTrackIndex );
// Compute the final position by moving the tco's pos by
// the number of tacts between the first TCO and the mouse drop TCO
MidiTime oldPos = tcoElement.attributeNode( "pos" ).value().toInt();
MidiTime offset = oldPos - MidiTime( oldPos.getTact(), 0 );
MidiTime oldTact = MidiTime( oldPos.getTact(), 0 );
MidiTime delta = offset + ( oldTact - grabbedTCOTact );
MidiTime pos = tcoPos + delta;
// The new position is the old position plus the offset.
MidiTime pos = tcoElement.attributeNode( "pos" ).value().toInt() + offset;
// If we land on ourselves, offset by one snap
MidiTime shift = MidiTime::ticksPerTact() * gui->songEditor()->m_editor->getSnapSize();
if (offset == 0) { pos += shift; }
TrackContentObject * tco = t->createTCO( pos );
tco->restoreState( tcoElement );
@@ -1562,7 +1662,7 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de )
*/
void TrackContentWidget::dropEvent( QDropEvent * de )
{
MidiTime tcoPos = MidiTime( getPosition( de->pos().x() ).getTact(), 0 );
MidiTime tcoPos = MidiTime( getPosition( de->pos().x() ) );
if( pasteSelection( tcoPos, de ) == true )
{
de->accept();

View File

@@ -63,13 +63,18 @@ MidiTime::MidiTime( const tick_t ticks ) :
{
}
MidiTime MidiTime::toNearestTact() const
MidiTime MidiTime::quantize(float bars) const
{
if( m_ticks % s_ticksPerTact >= s_ticksPerTact/2 )
{
return ( getTact() + 1 ) * s_ticksPerTact;
}
return getTact() * s_ticksPerTact;
//The intervals we should snap to, our new position should be a factor of this
int interval = s_ticksPerTact * bars;
//The lower position we could snap to
int lowPos = m_ticks / interval;
//Offset from the lower position
int offset = m_ticks % interval;
//1 if we should snap up, 0 if we shouldn't
int snapUp = offset / (interval / 2);
return (lowPos + snapUp) * interval;
}

View File

@@ -2,7 +2,7 @@
* TimeLineWidget.cpp - class timeLine, representing a time-line with position marker
*
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
@@ -384,14 +384,14 @@ void TimeLineWidget::mouseMoveEvent( QMouseEvent* event )
}
else
{
m_loopPos[i] = t.toNearestTact();
m_loopPos[i] = t.quantize(1.0);
}
// Catch begin == end
if( m_loopPos[0] == m_loopPos[1] )
{
// Note, swap 1 and 0 below and the behavior "skips" the other
// marking instead of pushing it.
if( m_action == MoveLoopBegin )
if( m_action == MoveLoopBegin )
m_loopPos[0] -= MidiTime::ticksPerTact();
else
m_loopPos[1] += MidiTime::ticksPerTact();

View File

@@ -76,11 +76,14 @@ SongEditor::SongEditor( Song * song ) :
TrackContainerView( song ),
m_song( song ),
m_zoomingModel(new ComboBoxModel()),
m_snappingModel(new ComboBoxModel()),
m_proportionalSnap( false ),
m_scrollBack( false ),
m_smoothScroll( ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt() ),
m_mode(DrawMode)
{
m_zoomingModel->setParent(this);
m_snappingModel->setParent(this);
// create time-line
m_widgetWidthTotal = ConfigManager::inst()->value( "ui",
"compacttrackbuttons" ).toInt()==1 ?
@@ -230,7 +233,7 @@ SongEditor::SongEditor( Song * song ) :
connect( m_song, SIGNAL( lengthChanged( int ) ),
this, SLOT( updateScrollBar( int ) ) );
// Set up zooming model
//Set up zooming model
for( float const & zoomLevel : m_zoomLevels )
{
m_zoomingModel->addItem( QString( "%1\%" ).arg( zoomLevel * 100 ) );
@@ -240,6 +243,24 @@ SongEditor::SongEditor( Song * song ) :
connect( m_zoomingModel, SIGNAL( dataChanged() ),
this, SLOT( zoomingChanged() ) );
//Set up snapping model, 2^i
for ( int i = 3; i >= -4; i-- )
{
if ( i > 0 )
{
m_snappingModel->addItem( QString( "%1 Bars").arg( 1 << i ) );
}
else if ( i == 0 )
{
m_snappingModel->addItem( "1 Bar" );
}
else
{
m_snappingModel->addItem( QString( "1/%1 Bar" ).arg( 1 << (-i) ) );
}
}
m_snappingModel->setInitValue( m_snappingModel->findText( "1 Bar" ) );
setFocusPolicy( Qt::StrongFocus );
setFocus();
}
@@ -264,6 +285,48 @@ void SongEditor::loadSettings( const QDomElement& element )
float SongEditor::getSnapSize() const
{
// 1 Bar is the third value in the snapping dropdown
int val = -m_snappingModel->value() + 3;
// If proportional snap is on, we snap to finer values when zoomed in
if (m_proportionalSnap)
{
val = val - m_zoomingModel->value() + 3;
}
val = max(val, -6); // -6 gives 1/64th bar snapping. Lower values cause crashing.
if ( val >= 0 ){
return 1 << val;
}
else {
return 1.0 / ( 1 << -val );
}
}
QString SongEditor::getSnapSizeString() const
{
int val = -m_snappingModel->value() + 3;
val = val - m_zoomingModel->value() + 3;
val = max(val, -6); // -6 gives 1/64th bar snapping. Lower values cause crashing.
if ( val >= 0 ){
int bars = 1 << val;
if ( bars == 1 ) { return QString("1 Bar"); }
else
{
return QString( "%1 Bars" ).arg(bars);
}
}
else {
int div = ( 1 << -val );
return QString( "1/%1 Bar" ).arg(div);
}
}
void SongEditor::setHighQuality( bool hq )
{
Engine::mixer()->changeQuality( Mixer::qualitySettings(
@@ -298,6 +361,11 @@ void SongEditor::setEditModeSelect()
setEditMode(SelectMode);
}
void SongEditor::toggleProportionalSnap()
{
m_proportionalSnap = !m_proportionalSnap;
}
@@ -653,10 +721,19 @@ ComboBoxModel *SongEditor::zoomingModel() const
ComboBoxModel *SongEditor::snappingModel() const
{
return m_snappingModel;
}
SongEditorWindow::SongEditorWindow(Song* song) :
Editor(Engine::mixer()->audioDev()->supportsCapture(), false),
m_editor(new SongEditor(song)),
m_crtlAction( NULL )
m_crtlAction( NULL ),
m_snapSizeLabel( new QLabel( m_toolBar ) )
{
setWindowTitle( tr( "Song-Editor" ) );
setWindowIcon( embed::getIconPixmap( "songeditor" ) );
@@ -718,23 +795,63 @@ SongEditorWindow::SongEditorWindow(Song* song) :
QLabel * zoom_lbl = new QLabel( m_toolBar );
zoom_lbl->setPixmap( embed::getIconPixmap( "zoom" ) );
// setup zooming-stuff
//Set up zooming-stuff
m_zoomingComboBox = new ComboBox( m_toolBar );
m_zoomingComboBox->setFixedSize( 80, 22 );
m_zoomingComboBox->move( 580, 4 );
m_zoomingComboBox->setModel(m_editor->m_zoomingModel);
m_zoomingComboBox->setToolTip(tr("Horizontal zooming"));
connect(m_editor->zoomingModel(), SIGNAL(dataChanged()), this, SLOT(updateSnapLabel()));
zoomToolBar->addWidget( zoom_lbl );
zoomToolBar->addWidget( m_zoomingComboBox );
DropToolBar *snapToolBar = addDropToolBarToTop(tr("Snap controls"));
QLabel * snap_lbl = new QLabel( m_toolBar );
snap_lbl->setPixmap( embed::getIconPixmap( "quantize" ) );
//Set up quantization/snapping selector
m_snappingComboBox = new ComboBox( m_toolBar );
m_snappingComboBox->setFixedSize( 80, 22 );
m_snappingComboBox->setModel(m_editor->m_snappingModel);
m_snappingComboBox->setToolTip(tr("Clip snapping size"));
connect(m_editor->snappingModel(), SIGNAL(dataChanged()), this, SLOT(updateSnapLabel()));
m_setProportionalSnapAction = new QAction(embed::getIconPixmap("proportional_snap"),
tr("Toggle proportional snap on/off"), this);
m_setProportionalSnapAction->setCheckable(true);
m_setProportionalSnapAction->setChecked(false);
connect(m_setProportionalSnapAction, SIGNAL(triggered()), m_editor, SLOT(toggleProportionalSnap()));
connect(m_setProportionalSnapAction, SIGNAL(triggered()), this, SLOT(updateSnapLabel()) );
snapToolBar->addWidget( snap_lbl );
snapToolBar->addWidget( m_snappingComboBox );
snapToolBar->addSeparator();
snapToolBar->addAction( m_setProportionalSnapAction );
snapToolBar->addSeparator();
snapToolBar->addWidget( m_snapSizeLabel );
connect(song, SIGNAL(projectLoaded()), this, SLOT(adjustUiAfterProjectLoad()));
connect(this, SIGNAL(resized()), m_editor, SLOT(updatePositionLine()));
}
QSize SongEditorWindow::sizeHint() const
{
return {600, 300};
return {720, 300};
}
void SongEditorWindow::updateSnapLabel(){
if (m_setProportionalSnapAction->isChecked())
{
m_snapSizeLabel->setText(QString("Snap: ") + m_editor->getSnapSizeString());
m_snappingComboBox->setToolTip(tr("Base snapping size"));
}
else
{
m_snappingComboBox->setToolTip(tr("Clip snapping size"));
m_snapSizeLabel->clear();
}
}

View File

@@ -529,7 +529,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe )
float nom = Engine::getSong()->getTimeSigModel().getNumerator();
float den = Engine::getSong()->getTimeSigModel().getDenominator();
float ticksPerTact = DefaultTicksPerTact * nom / den;
float offset = m_tco->startTimeOffset() / ticksPerTact * pixelsPerTact();
QRect r = QRect( TCO_BORDER_WIDTH + offset, spacing,
qMax( static_cast<int>( m_tco->sampleLength() * ppt / ticksPerTact ), 1 ), rect().bottom() - 2 * spacing );
@@ -931,7 +931,7 @@ void SampleTrackView::dropEvent(QDropEvent *de)
? MidiTime(0)
: MidiTime(((xPos - trackHeadWidth) / trackContainerView()->pixelsPerTact()
* MidiTime::ticksPerTact()) + trackContainerView()->currentPosition()
).toNearestTact();
).quantize(1.0);
SampleTCO * sTco = static_cast<SampleTCO*>(getTrack()->createTCO(tcoPos));
if (sTco) { sTco->setSampleFile(value); }
@@ -1192,4 +1192,3 @@ void SampleTrackWindow::loadSettings(const QDomElement& element)
m_stv->m_tlb->setChecked(true);
}
}