mirror of
https://github.com/LMMS/lmms.git
synced 2026-05-19 20:26:24 -04:00
New loop marker shortcuts, attempt 2 (#6382)
Co-authored-by: Dominic Clark <mrdomclark@gmail.com>
This commit is contained in:
BIN
data/themes/classic/cursor_select_left.png
Normal file
BIN
data/themes/classic/cursor_select_left.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 B |
BIN
data/themes/classic/cursor_select_right.png
Normal file
BIN
data/themes/classic/cursor_select_right.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 193 B |
@@ -663,15 +663,25 @@ lmms--gui--TimeLineWidget {
|
||||
background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #8796a7, stop: 1.0 #3e454e );
|
||||
|
||||
qproperty-inactiveLoopColor: rgba( 52, 63, 53, 64 );
|
||||
qproperty-inactiveLoopColor: rgba( 52, 63, 53, 64 );
|
||||
qproperty-inactiveLoopBrush: rgba( 255, 255, 255, 32 );
|
||||
qproperty-inactiveLoopInnerColor: rgba( 255, 255, 255, 32 );
|
||||
qproperty-inactiveLoopHandleColor: rgba( 192, 192, 192, 100 );
|
||||
qproperty-activeLoopColor: rgba( 52, 63, 53, 255 );
|
||||
qproperty-activeLoopBrush: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #378d59, stop: 1.0 #297e36 );
|
||||
qproperty-activeLoopInnerColor: rgba( 74, 155, 100, 255 );
|
||||
qproperty-activeLoopHandleColor: rgba( 192, 192, 192, 200 );
|
||||
|
||||
/* Width of loop marker handles (when handle mode is active) */
|
||||
qproperty-loopHandleWidth: 8;
|
||||
|
||||
qproperty-barLineColor: rgb( 192, 192, 192 );
|
||||
qproperty-barNumberColor: rgb( 192, 192, 192 );
|
||||
|
||||
/* Cursor hotspots for loop marker adjustment */
|
||||
qproperty-mouseHotspotSelLeft: 0px 16px;
|
||||
qproperty-mouseHotspotSelRight: 32px 16px;
|
||||
}
|
||||
|
||||
QTreeView {
|
||||
|
||||
BIN
data/themes/default/cursor_select_left.png
Normal file
BIN
data/themes/default/cursor_select_left.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 B |
BIN
data/themes/default/cursor_select_right.png
Normal file
BIN
data/themes/default/cursor_select_right.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 193 B |
@@ -704,23 +704,32 @@ lmms--gui--TimeLineWidget {
|
||||
/* Properties for the loop indicator rectangle in inactive state:
|
||||
- LoopColor: Color of the outermost border
|
||||
- LoopBrush: Brush to paint the main portion of the rectangle
|
||||
- LoopInnerColor: Color used to paint the inlayed border */
|
||||
- LoopInnerColor: Color used to paint the inlayed border
|
||||
- LoopHandleColor: Color used to paint loop marker handles */
|
||||
qproperty-inactiveLoopColor: #3B424A;
|
||||
qproperty-inactiveLoopBrush: #3B424A;
|
||||
qproperty-inactiveLoopInnerColor: #3B424A;
|
||||
qproperty-inactiveLoopHandleColor: rgba( 192, 192, 192, 100 );
|
||||
|
||||
/* Properties for the loop indicator rectangle in active state.
|
||||
See above for detailed description. */
|
||||
qproperty-activeLoopColor: #21A14F;
|
||||
qproperty-activeLoopBrush: #21A14F;
|
||||
qproperty-activeLoopInnerColor: #21A14F;
|
||||
qproperty-activeLoopHandleColor: rgba( 192, 192, 192, 200 );
|
||||
|
||||
/* Vertical padding for the loop indicator rectangle.
|
||||
A value of zero draws the rectangle at the full height of the widget. */
|
||||
qproperty-loopRectangleVerticalPadding: 1;
|
||||
/* Width of loop marker handles (when handle mode is active) */
|
||||
qproperty-loopHandleWidth: 8;
|
||||
|
||||
qproperty-barLineColor: rgb( 192, 192, 192 );
|
||||
qproperty-barNumberColor: rgb( 192, 192, 192 );
|
||||
|
||||
/* Cursor hotspots for loop marker adjustment */
|
||||
qproperty-mouseHotspotSelLeft: 0px 16px;
|
||||
qproperty-mouseHotspotSelRight: 32px 16px;
|
||||
}
|
||||
|
||||
lmms--gui--TrackContainerView QLabel
|
||||
|
||||
@@ -86,6 +86,7 @@ private slots:
|
||||
void toggleMMPZ(bool enabled);
|
||||
void toggleDisableBackup(bool enabled);
|
||||
void toggleOpenLastProject(bool enabled);
|
||||
void loopMarkerModeChanged();
|
||||
void setLanguage(int lang);
|
||||
|
||||
// Performance settings widget.
|
||||
@@ -147,6 +148,8 @@ private:
|
||||
bool m_MMPZ;
|
||||
bool m_disableBackup;
|
||||
bool m_openLastProject;
|
||||
QString m_loopMarkerMode;
|
||||
QComboBox* m_loopMarkerComboBox;
|
||||
QString m_lang;
|
||||
QStringList m_languages;
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#ifndef LMMS_GUI_TIMELINE_WIDGET_H
|
||||
#define LMMS_GUI_TIMELINE_WIDGET_H
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <QBrush>
|
||||
#include <QSize>
|
||||
#include <QWidget>
|
||||
|
||||
#include "Song.h"
|
||||
@@ -57,10 +61,15 @@ public:
|
||||
Q_PROPERTY( QColor inactiveLoopColor READ getInactiveLoopColor WRITE setInactiveLoopColor )
|
||||
Q_PROPERTY( QBrush inactiveLoopBrush READ getInactiveLoopBrush WRITE setInactiveLoopBrush )
|
||||
Q_PROPERTY( QColor inactiveLoopInnerColor READ getInactiveLoopInnerColor WRITE setInactiveLoopInnerColor )
|
||||
Q_PROPERTY(QColor inactiveLoopHandleColor MEMBER m_inactiveLoopHandleColor)
|
||||
Q_PROPERTY( QColor activeLoopColor READ getActiveLoopColor WRITE setActiveLoopColor )
|
||||
Q_PROPERTY( QBrush activeLoopBrush READ getActiveLoopBrush WRITE setActiveLoopBrush )
|
||||
Q_PROPERTY( QColor activeLoopInnerColor READ getActiveLoopInnerColor WRITE setActiveLoopInnerColor )
|
||||
Q_PROPERTY(QColor activeLoopHandleColor MEMBER m_activeLoopHandleColor)
|
||||
Q_PROPERTY( int loopRectangleVerticalPadding READ getLoopRectangleVerticalPadding WRITE setLoopRectangleVerticalPadding )
|
||||
Q_PROPERTY(int loopHandleWidth MEMBER m_loopHandleWidth)
|
||||
Q_PROPERTY(QSize mouseHotspotSelLeft READ mouseHotspotSelLeft WRITE setMouseHotspotSelLeft)
|
||||
Q_PROPERTY(QSize mouseHotspotSelRight READ mouseHotspotSelRight WRITE setMouseHotspotSelRight)
|
||||
|
||||
enum class AutoScrollState
|
||||
{
|
||||
@@ -99,6 +108,28 @@ public:
|
||||
inline int const & getLoopRectangleVerticalPadding() const { return m_loopRectangleVerticalPadding; }
|
||||
inline void setLoopRectangleVerticalPadding(int const & loopRectangleVerticalPadding) { m_loopRectangleVerticalPadding = loopRectangleVerticalPadding; }
|
||||
|
||||
auto mouseHotspotSelLeft() const -> QSize
|
||||
{
|
||||
const auto point = m_cursorSelectLeft.hotSpot();
|
||||
return QSize{point.x(), point.y()};
|
||||
}
|
||||
|
||||
void setMouseHotspotSelLeft(const QSize& s)
|
||||
{
|
||||
m_cursorSelectLeft = QCursor{m_cursorSelectLeft.pixmap(), s.width(), s.height()};
|
||||
}
|
||||
|
||||
auto mouseHotspotSelRight() const -> QSize
|
||||
{
|
||||
const auto point = m_cursorSelectRight.hotSpot();
|
||||
return QSize{point.x(), point.y()};
|
||||
}
|
||||
|
||||
void setMouseHotspotSelRight(const QSize& s)
|
||||
{
|
||||
m_cursorSelectRight = QCursor{m_cursorSelectRight.pixmap(), s.width(), s.height()};
|
||||
}
|
||||
|
||||
inline Song::PlayPos & pos()
|
||||
{
|
||||
return( m_pos );
|
||||
@@ -143,51 +174,64 @@ protected:
|
||||
void mousePressEvent( QMouseEvent * _me ) override;
|
||||
void mouseMoveEvent( QMouseEvent * _me ) override;
|
||||
void mouseReleaseEvent( QMouseEvent * _me ) override;
|
||||
|
||||
void contextMenuEvent(QContextMenuEvent* event) override;
|
||||
|
||||
private:
|
||||
QPixmap m_posMarkerPixmap = embed::getIconPixmap("playpos_marker");
|
||||
|
||||
QColor m_inactiveLoopColor;
|
||||
QBrush m_inactiveLoopBrush;
|
||||
QColor m_inactiveLoopInnerColor;
|
||||
|
||||
QColor m_activeLoopColor;
|
||||
QBrush m_activeLoopBrush;
|
||||
QColor m_activeLoopInnerColor;
|
||||
|
||||
int m_loopRectangleVerticalPadding;
|
||||
|
||||
QColor m_barLineColor;
|
||||
QColor m_barNumberColor;
|
||||
|
||||
AutoScrollState m_autoScroll;
|
||||
|
||||
bool m_changedPosition;
|
||||
|
||||
int m_xOffset;
|
||||
int m_posMarkerX;
|
||||
float m_ppb;
|
||||
float m_snapSize;
|
||||
Song::PlayPos & m_pos;
|
||||
Timeline* m_timeline;
|
||||
const TimePos & m_begin;
|
||||
const Song::PlayMode m_mode;
|
||||
|
||||
TextFloat * m_hint;
|
||||
int m_initalXSelect;
|
||||
|
||||
|
||||
enum class Action
|
||||
{
|
||||
NoAction,
|
||||
MovePositionMarker,
|
||||
MoveLoopBegin,
|
||||
MoveLoopEnd,
|
||||
MoveLoop,
|
||||
SelectSongClip,
|
||||
} m_action;
|
||||
};
|
||||
|
||||
int m_moveXOff;
|
||||
auto getClickedTime(int xPosition) const -> TimePos;
|
||||
auto getLoopAction(QMouseEvent* event) const -> Action;
|
||||
auto actionCursor(Action action) const -> QCursor;
|
||||
|
||||
QPixmap m_posMarkerPixmap = embed::getIconPixmap("playpos_marker");
|
||||
|
||||
QColor m_inactiveLoopColor = QColor{52, 63, 53, 64};
|
||||
QBrush m_inactiveLoopBrush = QColor{255, 255, 255, 32};
|
||||
QColor m_inactiveLoopInnerColor = QColor{255, 255, 255, 32};
|
||||
QColor m_inactiveLoopHandleColor = QColor{255, 255, 255, 32};
|
||||
|
||||
QColor m_activeLoopColor = QColor{52, 63, 53, 255};
|
||||
QBrush m_activeLoopBrush = QColor{55, 141, 89};
|
||||
QColor m_activeLoopInnerColor = QColor{74, 155, 100, 255};
|
||||
QColor m_activeLoopHandleColor = QColor{74, 155, 100, 255};
|
||||
|
||||
int m_loopRectangleVerticalPadding = 1;
|
||||
int m_loopHandleWidth = 5;
|
||||
|
||||
QColor m_barLineColor = QColor{192, 192, 192};
|
||||
QColor m_barNumberColor = m_barLineColor.darker(120);
|
||||
|
||||
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;
|
||||
|
||||
// Width of the unused region on the widget's left (above track labels or piano)
|
||||
int m_xOffset;
|
||||
float m_ppb;
|
||||
float m_snapSize = 1.f;
|
||||
Song::PlayPos & m_pos;
|
||||
Timeline* m_timeline;
|
||||
// Leftmost position visible in parent editor
|
||||
const TimePos & m_begin;
|
||||
const Song::PlayMode m_mode;
|
||||
// When in MoveLoop mode we need the initial positions. Storing only the latest
|
||||
// position allows for unquantized drag but fails when toggling quantization.
|
||||
std::array<TimePos, 2> m_oldLoopPos;
|
||||
TimePos m_dragStartPos;
|
||||
|
||||
TextFloat* m_hint = nullptr;
|
||||
int m_initalXSelect;
|
||||
|
||||
Action m_action = Action::NoAction;
|
||||
};
|
||||
|
||||
} // namespace lmms::gui
|
||||
|
||||
@@ -806,7 +806,7 @@ void SongEditor::updatePosition( const TimePos & t )
|
||||
m_scrollBack = false;
|
||||
}
|
||||
|
||||
const int x = m_timeLine->markerX(t) + 8;
|
||||
const int x = m_timeLine->markerX(t);
|
||||
if( x >= trackOpWidth + widgetWidth -1 )
|
||||
{
|
||||
m_positionLine->show();
|
||||
|
||||
@@ -27,11 +27,14 @@
|
||||
#include <cmath>
|
||||
|
||||
#include <QDomElement>
|
||||
#include <QGuiApplication>
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
#include <QToolBar>
|
||||
|
||||
#include "ConfigManager.h"
|
||||
#include "embed.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "NStateButton.h"
|
||||
@@ -47,35 +50,17 @@ namespace
|
||||
|
||||
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 ) ),
|
||||
m_inactiveLoopInnerColor( 255, 255, 255, 32 ),
|
||||
m_activeLoopColor( 52, 63, 53, 255 ),
|
||||
m_activeLoopBrush( QColor( 55, 141, 89 ) ),
|
||||
m_activeLoopInnerColor( 74, 155, 100, 255 ),
|
||||
m_loopRectangleVerticalPadding( 1 ),
|
||||
m_barLineColor( 192, 192, 192 ),
|
||||
m_barNumberColor( m_barLineColor.darker( 120 ) ),
|
||||
m_autoScroll( AutoScrollState::Enabled ),
|
||||
m_changedPosition( true ),
|
||||
m_xOffset( xoff ),
|
||||
m_posMarkerX( 0 ),
|
||||
m_ppb( ppb ),
|
||||
m_snapSize( 1.0 ),
|
||||
m_pos( pos ),
|
||||
QWidget{parent},
|
||||
m_xOffset{xoff},
|
||||
m_ppb{ppb},
|
||||
m_pos{pos},
|
||||
m_timeline{&timeline},
|
||||
m_begin( begin ),
|
||||
m_mode( mode ),
|
||||
m_hint( nullptr ),
|
||||
m_action( Action::NoAction ),
|
||||
m_moveXOff( 0 )
|
||||
m_begin{begin},
|
||||
m_mode{mode}
|
||||
{
|
||||
setAttribute( Qt::WA_OpaquePaintEvent, true );
|
||||
move( 0, yoff );
|
||||
|
||||
m_xOffset -= m_posMarkerPixmap.width() / 2;
|
||||
|
||||
setMouseTracking(true);
|
||||
|
||||
auto updateTimer = new QTimer(this);
|
||||
@@ -98,12 +83,8 @@ TimeLineWidget::~TimeLineWidget()
|
||||
void TimeLineWidget::setXOffset(const int x)
|
||||
{
|
||||
m_xOffset = x;
|
||||
m_xOffset -= m_posMarkerPixmap.width() / 2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TimeLineWidget::addToolButtons( QToolBar * _tool_bar )
|
||||
{
|
||||
auto autoScroll = new NStateButton(_tool_bar);
|
||||
@@ -151,15 +132,8 @@ void TimeLineWidget::addToolButtons( QToolBar * _tool_bar )
|
||||
|
||||
void TimeLineWidget::updatePosition()
|
||||
{
|
||||
const int new_x = markerX( m_pos );
|
||||
|
||||
if( new_x != m_posMarkerX )
|
||||
{
|
||||
m_posMarkerX = new_x;
|
||||
m_changedPosition = true;
|
||||
emit positionChanged( m_pos );
|
||||
update();
|
||||
}
|
||||
emit positionChanged(m_pos);
|
||||
update();
|
||||
}
|
||||
|
||||
void TimeLineWidget::toggleAutoScroll( int _n )
|
||||
@@ -175,19 +149,18 @@ void TimeLineWidget::paintEvent( QPaintEvent * )
|
||||
p.fillRect( 0, 0, width(), height(), p.background() );
|
||||
|
||||
// Clip so that we only draw everything starting from the offset
|
||||
const int leftMargin = m_xOffset + m_posMarkerPixmap.width() / 2;
|
||||
p.setClipRect(leftMargin, 0, width() - leftMargin, height() );
|
||||
p.setClipRect(m_xOffset, 0, width() - m_xOffset, height());
|
||||
|
||||
// Draw the loop rectangle
|
||||
int const & loopRectMargin = getLoopRectangleVerticalPadding();
|
||||
// Variables for the loop rectangle
|
||||
int const loopRectMargin = getLoopRectangleVerticalPadding();
|
||||
int const loopRectHeight = this->height() - 2 * loopRectMargin;
|
||||
int const loopStart = markerX(m_timeline->loopBegin()) + 8;
|
||||
int const loopEndR = markerX(m_timeline->loopEnd()) + 9;
|
||||
int const loopStart = markerX(m_timeline->loopBegin());
|
||||
int const loopEndR = markerX(m_timeline->loopEnd());
|
||||
int const loopRectWidth = loopEndR - loopStart;
|
||||
|
||||
bool const loopPointsActive = m_timeline->loopEnabled();
|
||||
|
||||
// Draw the main rectangle (inner fill only)
|
||||
// Draw the main loop rectangle (inner fill only)
|
||||
QRect outerRectangle( loopStart, loopRectMargin, loopRectWidth - 1, loopRectHeight - 1 );
|
||||
p.fillRect( outerRectangle, loopPointsActive ? getActiveLoopBrush() : getInactiveLoopBrush());
|
||||
|
||||
@@ -203,8 +176,7 @@ void TimeLineWidget::paintEvent( QPaintEvent * )
|
||||
QColor const & barNumberColor = getBarNumberColor();
|
||||
|
||||
bar_t barNumber = m_begin.getBar();
|
||||
int const x = m_xOffset + m_posMarkerPixmap.width() / 2
|
||||
- ((static_cast<int>(m_begin * m_ppb) / TimePos::ticksPerBar()) % static_cast<int>(m_ppb));
|
||||
int const x = m_xOffset - ((static_cast<int>(m_begin * m_ppb) / TimePos::ticksPerBar()) % static_cast<int>(m_ppb));
|
||||
|
||||
// Double the interval between bar numbers until they are far enough appart
|
||||
int barLabelInterval = 1;
|
||||
@@ -225,83 +197,139 @@ void TimeLineWidget::paintEvent( QPaintEvent * )
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the main rectangle (outer border)
|
||||
// Draw the loop rectangle's outer outline
|
||||
p.setPen( loopPointsActive ? getActiveLoopColor() : getInactiveLoopColor() );
|
||||
p.setBrush( Qt::NoBrush );
|
||||
p.drawRect( outerRectangle );
|
||||
|
||||
// Draw the inner border outline (no fill)
|
||||
// Draw the loop rectangle's inner outline
|
||||
QRect innerRectangle = outerRectangle.adjusted( 1, 1, -1, -1 );
|
||||
p.setPen( loopPointsActive ? getActiveLoopInnerColor() : getInactiveLoopInnerColor() );
|
||||
p.setBrush( Qt::NoBrush );
|
||||
p.drawRect( innerRectangle );
|
||||
|
||||
// Draw loop handles if necessary
|
||||
const auto handleMode = ConfigManager::inst()->value("app", "loopmarkermode") == "handles";
|
||||
if (handleMode && underMouse() && QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier))
|
||||
{
|
||||
const auto handleWidth = std::min(m_loopHandleWidth, loopRectWidth / 2 - 1);
|
||||
const auto leftHandle = QRectF(loopStart - .5, loopRectMargin - .5, handleWidth, loopRectHeight);
|
||||
const auto rightHandle = QRectF(loopEndR - handleWidth - .5, loopRectMargin - .5, handleWidth, loopRectHeight);
|
||||
const auto color = loopPointsActive ? m_activeLoopHandleColor : m_inactiveLoopHandleColor;
|
||||
|
||||
p.fillRect(leftHandle, color);
|
||||
p.fillRect(rightHandle, color);
|
||||
}
|
||||
|
||||
// Only draw the position marker if the position line is in view
|
||||
if (m_posMarkerX >= m_xOffset && m_posMarkerX < width() - m_posMarkerPixmap.width() / 2)
|
||||
if (markerX(m_pos) >= m_xOffset && markerX(m_pos) < width() - m_posMarkerPixmap.width() / 2)
|
||||
{
|
||||
// Let the position marker extrude to the left
|
||||
p.setClipping(false);
|
||||
p.setOpacity(0.6);
|
||||
p.drawPixmap(m_posMarkerX, height() - m_posMarkerPixmap.height(), m_posMarkerPixmap);
|
||||
p.drawPixmap(markerX(m_pos) - (m_posMarkerPixmap.width() / 2),
|
||||
height() - m_posMarkerPixmap.height(), m_posMarkerPixmap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TimeLineWidget::mousePressEvent( QMouseEvent* event )
|
||||
auto TimeLineWidget::getClickedTime(const int xPosition) const -> TimePos
|
||||
{
|
||||
if( event->x() < m_xOffset )
|
||||
// How far into the timeline we clicked, measuring pixels from the leftmost part of the editor
|
||||
const auto pixelDelta = std::max(xPosition - m_xOffset, 0);
|
||||
return m_begin + static_cast<int>(pixelDelta * TimePos::ticksPerBar() / m_ppb);
|
||||
}
|
||||
|
||||
auto TimeLineWidget::getLoopAction(QMouseEvent* event) const -> TimeLineWidget::Action
|
||||
{
|
||||
const auto mode = ConfigManager::inst()->value("app", "loopmarkermode");
|
||||
const auto xPos = event->x();
|
||||
const auto button = event->button();
|
||||
|
||||
if (mode == "handles")
|
||||
{
|
||||
return;
|
||||
// Loop start and end pos, or closest edge of screen if loop extends off it
|
||||
const auto leftMost = std::max(markerX(m_timeline->loopBegin()), m_xOffset);
|
||||
const auto rightMost = std::min(markerX(m_timeline->loopEnd()), width());
|
||||
// Distance from click to handle, positive aimed towards center of loop
|
||||
const auto deltaLeft = xPos - leftMost;
|
||||
const auto deltaRight = rightMost - xPos;
|
||||
|
||||
if (deltaLeft < 0 || deltaRight < 0) { return Action::NoAction; } // Clicked outside loop
|
||||
else if (deltaLeft <= m_loopHandleWidth && deltaLeft < deltaRight) { return Action::MoveLoopBegin; }
|
||||
else if (deltaRight <= m_loopHandleWidth) { return Action::MoveLoopEnd; }
|
||||
else { return Action::MoveLoop; }
|
||||
}
|
||||
if( event->button() == Qt::LeftButton && !(event->modifiers() & Qt::ShiftModifier) )
|
||||
else if (mode == "closest")
|
||||
{
|
||||
m_action = Action::MovePositionMarker;
|
||||
if (event->x() - m_xOffset < m_posMarkerPixmap.width())
|
||||
const TimePos loopMid = (m_timeline->loopBegin() + m_timeline->loopEnd()) / 2;
|
||||
return getClickedTime(xPos) < loopMid ? Action::MoveLoopBegin : Action::MoveLoopEnd;
|
||||
}
|
||||
else // Default to dual-button mode
|
||||
{
|
||||
if (button == Qt::LeftButton) { return Action::MoveLoopBegin; }
|
||||
else if (button == Qt::RightButton) { return Action::MoveLoopEnd; }
|
||||
return Action::NoAction;
|
||||
}
|
||||
}
|
||||
|
||||
auto TimeLineWidget::actionCursor(Action action) const -> QCursor
|
||||
{
|
||||
switch (action) {
|
||||
case Action::MoveLoop: return Qt::SizeHorCursor;
|
||||
case Action::MoveLoopBegin: return m_cursorSelectLeft;
|
||||
case Action::MoveLoopEnd: return m_cursorSelectRight;
|
||||
// Fall back to normal cursor if no action or action cursor not specified
|
||||
default: return Qt::ArrowCursor;
|
||||
}
|
||||
}
|
||||
|
||||
void TimeLineWidget::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event->x() < m_xOffset) { return; }
|
||||
|
||||
const auto shift = event->modifiers() & Qt::ShiftModifier;
|
||||
const auto ctrl = event->modifiers() & Qt::ControlModifier;
|
||||
|
||||
if (shift) // loop marker manipulation
|
||||
{
|
||||
m_action = getLoopAction(event);
|
||||
setCursor(actionCursor(m_action));
|
||||
|
||||
if (m_action == Action::MoveLoop)
|
||||
{
|
||||
m_moveXOff = event->x() - m_xOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_moveXOff = m_posMarkerPixmap.width() / 2;
|
||||
m_dragStartPos = getClickedTime(event->x());
|
||||
m_oldLoopPos = {m_timeline->loopBegin(), m_timeline->loopEnd()};
|
||||
}
|
||||
}
|
||||
else if( event->button() == Qt::LeftButton && (event->modifiers() & Qt::ShiftModifier) )
|
||||
else if (event->button() == Qt::LeftButton && ctrl) // selection
|
||||
{
|
||||
m_action = Action::SelectSongClip;
|
||||
m_initalXSelect = event->x();
|
||||
}
|
||||
else if( event->button() == Qt::RightButton )
|
||||
else if (event->button() == Qt::LeftButton && !ctrl) // move playhead
|
||||
{
|
||||
m_moveXOff = m_posMarkerPixmap.width() / 2;
|
||||
|
||||
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;
|
||||
m_action = Action::MovePositionMarker;
|
||||
}
|
||||
|
||||
if( m_action == Action::MoveLoopBegin || m_action == Action::MoveLoopEnd )
|
||||
if (m_action == Action::MoveLoopBegin || m_action == Action::MoveLoopEnd)
|
||||
{
|
||||
delete m_hint;
|
||||
m_hint = TextFloat::displayMessage( tr( "Hint" ),
|
||||
tr( "Press <%1> to disable magnetic loop points." ).arg(UI_CTRL_KEY),
|
||||
embed::getIconPixmap( "hint" ), 0 );
|
||||
m_hint = TextFloat::displayMessage(tr("Hint"),
|
||||
tr("Press <%1> to disable magnetic loop points.").arg(UI_CTRL_KEY),
|
||||
embed::getIconPixmap("hint"), 0);
|
||||
}
|
||||
mouseMoveEvent( event );
|
||||
|
||||
setContextMenuPolicy(m_action == Action::NoAction ? Qt::DefaultContextMenu : Qt::PreventContextMenu);
|
||||
|
||||
mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TimeLineWidget::mouseMoveEvent( QMouseEvent* event )
|
||||
{
|
||||
parentWidget()->update(); // essential for widgets that this timeline had taken their mouse move event from.
|
||||
|
||||
const auto cursorXOffset = std::max(event->x() - m_xOffset - m_moveXOff, 0);
|
||||
TimePos timeAtCursor = m_begin + static_cast<int>(cursorXOffset * TimePos::ticksPerBar() / m_ppb);
|
||||
auto timeAtCursor = getClickedTime(event->x());
|
||||
const auto control = event->modifiers() & Qt::ControlModifier;
|
||||
|
||||
switch( m_action )
|
||||
{
|
||||
@@ -324,7 +352,6 @@ void TimeLineWidget::mouseMoveEvent( QMouseEvent* event )
|
||||
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
|
||||
@@ -349,18 +376,35 @@ void TimeLineWidget::mouseMoveEvent( QMouseEvent* event )
|
||||
update();
|
||||
break;
|
||||
}
|
||||
case Action::SelectSongClip:
|
||||
case Action::MoveLoop:
|
||||
{
|
||||
const TimePos dragDelta = timeAtCursor - m_dragStartPos;
|
||||
auto loopPos = m_oldLoopPos;
|
||||
for (auto& point : loopPos)
|
||||
{
|
||||
point += dragDelta;
|
||||
if (!control) { point = point.quantize(m_snapSize); }
|
||||
}
|
||||
m_timeline->setLoopPoints(loopPos[0], loopPos[1]);
|
||||
break;
|
||||
}
|
||||
case Action::SelectSongClip:
|
||||
emit regionSelectedFromPixels( m_initalXSelect , event->x() );
|
||||
break;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (event->buttons() == Qt::NoButton)
|
||||
{
|
||||
setCursor(QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)
|
||||
? actionCursor(getLoopAction(event))
|
||||
: Qt::ArrowCursor
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TimeLineWidget::mouseReleaseEvent( QMouseEvent* event )
|
||||
{
|
||||
delete m_hint;
|
||||
@@ -369,5 +413,45 @@ void TimeLineWidget::mouseReleaseEvent( QMouseEvent* event )
|
||||
m_action = Action::NoAction;
|
||||
}
|
||||
|
||||
void TimeLineWidget::contextMenuEvent(QContextMenuEvent* event)
|
||||
{
|
||||
if (event->x() < m_xOffset) { return; }
|
||||
|
||||
auto menu = QMenu{};
|
||||
|
||||
menu.addAction(tr("Set loop begin here"), [this, event] {
|
||||
auto begin = getClickedTime(event->x());
|
||||
const auto end = m_timeline->loopEnd();
|
||||
if (!QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) { begin = begin.quantize(m_snapSize); }
|
||||
if (begin == end) { m_timeline->setLoopEnd(end + m_snapSize * TimePos::ticksPerBar()); }
|
||||
m_timeline->setLoopBegin(begin);
|
||||
update();
|
||||
});
|
||||
menu.addAction(tr("Set loop end here"), [this, event] {
|
||||
const auto begin = m_timeline->loopBegin();
|
||||
auto end = getClickedTime(event->x());
|
||||
if (!QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) { end = end.quantize(m_snapSize); }
|
||||
if (begin == end) { m_timeline->setLoopBegin(begin - m_snapSize * TimePos::ticksPerBar()); }
|
||||
m_timeline->setLoopEnd(end);
|
||||
update();
|
||||
});
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
const auto loopMenu = menu.addMenu(tr("Loop edit mode (hold shift)"));
|
||||
const auto loopMode = ConfigManager::inst()->value("app", "loopmarkermode", "dual");
|
||||
const auto addLoopModeAction = [loopMenu, &loopMode](QString text, QString mode) {
|
||||
const auto action = loopMenu->addAction(text, [mode] {
|
||||
ConfigManager::inst()->setValue("app", "loopmarkermode", mode);
|
||||
});
|
||||
action->setCheckable(true);
|
||||
if (loopMode == mode) { action->setChecked(true); }
|
||||
};
|
||||
addLoopModeAction(tr("Dual-button"), "dual");
|
||||
addLoopModeAction(tr("Grab closest"), "closest");
|
||||
addLoopModeAction(tr("Handles"), "handles");
|
||||
|
||||
menu.exec(event->globalPos());
|
||||
}
|
||||
|
||||
} // namespace lmms::gui
|
||||
|
||||
@@ -120,6 +120,7 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
"app", "disablebackup").toInt()),
|
||||
m_openLastProject(ConfigManager::inst()->value(
|
||||
"app", "openlastproject").toInt()),
|
||||
m_loopMarkerMode{ConfigManager::inst()->value("app", "loopmarkermode", "dual")},
|
||||
m_lang(ConfigManager::inst()->value(
|
||||
"app", "language")),
|
||||
m_saveInterval( ConfigManager::inst()->value(
|
||||
@@ -255,6 +256,19 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
|
||||
addCheckBox(tr("Show warning when deleting a mixer channel that is in use"), guiGroupBox, guiGroupLayout,
|
||||
m_mixerChannelDeletionWarning, SLOT(toggleMixerChannelDeletionWarning(bool)), false);
|
||||
|
||||
m_loopMarkerComboBox = new QComboBox{guiGroupBox};
|
||||
|
||||
m_loopMarkerComboBox->addItem(tr("Dual-button"), "dual");
|
||||
m_loopMarkerComboBox->addItem(tr("Grab closest"), "closest");
|
||||
m_loopMarkerComboBox->addItem(tr("Handles"), "handles");
|
||||
|
||||
m_loopMarkerComboBox->setCurrentIndex(m_loopMarkerComboBox->findData(m_loopMarkerMode));
|
||||
connect(m_loopMarkerComboBox, qOverload<int>(&QComboBox::currentIndexChanged),
|
||||
this, &SetupDialog::loopMarkerModeChanged);
|
||||
|
||||
guiGroupLayout->addWidget(new QLabel{tr("Loop edit mode"), guiGroupBox});
|
||||
guiGroupLayout->addWidget(m_loopMarkerComboBox);
|
||||
|
||||
generalControlsLayout->addWidget(guiGroupBox);
|
||||
|
||||
generalControlsLayout->addSpacing(10);
|
||||
@@ -922,6 +936,7 @@ void SetupDialog::accept()
|
||||
QString::number(!m_disableBackup));
|
||||
ConfigManager::inst()->setValue("app", "openlastproject",
|
||||
QString::number(m_openLastProject));
|
||||
ConfigManager::inst()->setValue("app", "loopmarkermode", m_loopMarkerMode);
|
||||
ConfigManager::inst()->setValue("app", "language", m_lang);
|
||||
ConfigManager::inst()->setValue("ui", "saveinterval",
|
||||
QString::number(m_saveInterval));
|
||||
@@ -1061,6 +1076,12 @@ void SetupDialog::toggleOpenLastProject(bool enabled)
|
||||
}
|
||||
|
||||
|
||||
void SetupDialog::loopMarkerModeChanged()
|
||||
{
|
||||
m_loopMarkerMode = m_loopMarkerComboBox->currentData().toString();
|
||||
}
|
||||
|
||||
|
||||
void SetupDialog::setLanguage(int lang)
|
||||
{
|
||||
m_lang = m_languages[lang];
|
||||
|
||||
Reference in New Issue
Block a user