New loop marker shortcuts, attempt 2 (#6382)

Co-authored-by: Dominic Clark <mrdomclark@gmail.com>
This commit is contained in:
Spekular
2023-12-25 17:26:35 +01:00
committed by GitHub
parent ce722dd6b6
commit 4eba656bd3
11 changed files with 298 additions and 127 deletions

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

View File

@@ -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 {

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -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];