Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions include/AutomationEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,6 @@ protected slots:
QScrollBar * m_leftRightScroll;
QScrollBar * m_topBottomScroll;

void adjustLeftRightScoll(int value);

TimePos m_currentPosition;

Action m_action;
Expand All @@ -254,8 +252,11 @@ protected slots:
float m_drawLastLevel;
tick_t m_drawLastTick;

//! Pixels per bar
int m_ppb;
//! Pixels per step on the Y axis (when Y zoom is not Auto)
int m_y_delta;
//! True if Y zoom is Auto
bool m_y_auto;

// Time position (key) of automation node whose outValue is being dragged
Expand Down
2 changes: 0 additions & 2 deletions include/PianoRoll.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,6 @@ protected slots:
QScrollBar * m_leftRightScroll;
QScrollBar * m_topBottomScroll;

void adjustLeftRightScoll(int value);

TimePos m_currentPosition;
bool m_recording;
bool m_doAutoQuantization{false};
Expand Down
1 change: 1 addition & 0 deletions include/PianoView.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class PianoView : public QWidget, public ModelView
void focusOutEvent( QFocusEvent * _fe ) override;
void focusInEvent( QFocusEvent * fe ) override;
void resizeEvent( QResizeEvent * _event ) override;
void wheelEvent(QWheelEvent* event) override;


private:
Expand Down
139 changes: 139 additions & 0 deletions include/Scroll.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Scroll.h - calculate scroll distance
*
* Copyright (c) 2025 Alex <allejok96/gmail>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/

#ifndef SCROLL_H
#define SCROLL_H

#include "Flags.h"
#include "lmms_export.h"


class QWheelEvent;


namespace lmms::gui {


class LMMS_EXPORT Scroll
{
public:
//! QWheelEvent->angleDelta() that corresponds to a "wheel tick"
static constexpr float ANGLE_DELTA_PER_TICK = 120;
//! Default scroll speed for various editors
static constexpr int PIXELS_PER_STEP = 36;

enum class Flag {
NoFlag = 0x0,
Horizontal = 0x1,
//! Change any natural (inverted) scroll to regular scroll.
//! This is useful for widgets like faders, where you want up to be up.
//! Some operating systems does not support this.
DisableNaturalScrolling = 0x2,
//! Swap x/y scroll orientation when pressing Shift.
//! On Windows and Linux this flag will also let you use Alt to swap,
//! because that is the default behavior of Qt on those platforms,
//! so it will match QScrollBar and other built-in widgets.
SwapWithShiftOrAlt = 0x4,
};

using Flags = lmms::Flags<Flag>;


/*! \brief Scroll delta
*
* This class measures scroll delta in "wheel ticks",
* unlike QWheelEvent which measures scroll delta in 1/8ths of a degree.
*/
Scroll(QWheelEvent* event);


/*! \brief Return number of completely scrolled steps of some size
*
* The return value is positive when the wheel is rotated away from the hand.
*
* `stepsPerWheelTick` is the number of steps to count for every wheel tick.
* If set to 5 it will count a step whenever the wheel has moved 1/5 of a tick.
* It will always cound at least one step per wheel tick.
*
* --------------------------------------------------------------------------
*
* You should always use this function instead of the following:
* int steps = wheelEvent->angleDelta().y() / some_value
*
* This is because some trackpads and mice report much smaller chunks of angleDelta
* than the standard 120 (which is a "wheel tick"). In the worst case scenario
* the result will always be rounded down to 0. This function solves it by accumulating
* the angleDelta until a complete step is reached.
*
* Note: don't call this function if you intend to ignore() the event, as it
* may result in double-counting scroll delta.
*/
int getSteps(const float stepsPerWheelTick = 1.0, const Flags flags = Flag::NoFlag);

inline int getSteps(const Flags flags)
{
return getSteps(1.0, flags);
}


/*! \brief Return number of scrolled "wheel ticks"
*
* The value is positive when the wheel is rotated away from the hand.
*
* If you intend to use this in a calculation where the end result is an integer,
* you should probably use getSteps() instead to avoid rounding issues with
* smooth scrolling trackpads and mice.
*/
inline float getStepsFloat(const Flags flags)
{
return getAngleDelta(flags) / ANGLE_DELTA_PER_TICK;
}

//! \brief True when scrolling vertically
bool isVertical();

//! \brief True when scrolling horizontally
bool isHorizontal();

private:
int getAngleDelta(const Flags flags = Flag::NoFlag);
int calculateSteps(const int angleDelta, const float stepsPerTick, const bool horizontal);

QWheelEvent* m_event;

// These are used by calculateSteps() to accumulate partially scrolled steps
// They are shared across all widgets but that doesn't notisable affect the user experience
static float s_partialStepX;
static float s_partialStepY;

const float m_initialPartialStepX;
const float m_initialPartialStepY;
};

LMMS_DECLARE_OPERATORS_FOR_FLAGS(Scroll::Flag)


} // namespace lmms::gui

#endif
2 changes: 0 additions & 2 deletions include/SongEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,6 @@ private slots:

QScrollBar * m_leftRightScroll;

void adjustLeftRightScoll(int value);

LcdSpinBox * m_tempoSpinBox;

TimeLineWidget * m_timeLine;
Expand Down
10 changes: 9 additions & 1 deletion plugins/AudioFileProcessor/AudioFileProcessorWaveView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "ConfigManager.h"
#include "SampleThumbnail.h"
#include "FontHelper.h"
#include "Scroll.h"

#include <QPainter>
#include <QMouseEvent>
Expand Down Expand Up @@ -192,7 +193,14 @@ void AudioFileProcessorWaveView::mouseMoveEvent(QMouseEvent * me)

void AudioFileProcessorWaveView::wheelEvent(QWheelEvent * we)
{
zoom(we->angleDelta().y() > 0);
auto scroll = Scroll(we);
we->accept();

int scrolledSteps = scroll.getSteps();
if (scrolledSteps == 0) { return; }

bool zoomOut = scrolledSteps < 0;
zoom(zoomOut);
update();
}

Expand Down
8 changes: 6 additions & 2 deletions plugins/Compressor/CompressorControlDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "Knob.h"
#include "MainWindow.h"
#include "PixmapButton.h"
#include "Scroll.h"

namespace lmms::gui
{
Expand Down Expand Up @@ -645,9 +646,12 @@ void CompressorControlDialog::resizeEvent(QResizeEvent *event)

void CompressorControlDialog::wheelEvent(QWheelEvent * event)
{
auto scroll = Scroll(event);
event->accept();

const float temp = m_dbRange;
const float dbRangeNew = m_dbRange - copysignf(COMP_GRID_SPACING, event->angleDelta().y());
m_dbRange = round(qBound(COMP_GRID_SPACING, dbRangeNew, COMP_GRID_MAX) / COMP_GRID_SPACING) * COMP_GRID_SPACING;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since scroll.getSteps() returns an int I don't think we need to round to the nearest multiple of COMP_GRID_SPACING because the new value will always be a multiple of COMP_GRID_SPACING.

const float dbRangeNew = m_dbRange - scroll.getSteps() * COMP_GRID_SPACING;
m_dbRange = std::clamp(dbRangeNew, COMP_GRID_SPACING, COMP_GRID_MAX);

// Only reset view if the scolling had an effect
if (m_dbRange != temp)
Expand Down
5 changes: 3 additions & 2 deletions plugins/Eq/EqCurve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,8 +561,10 @@ void EqHandle::mouseReleaseEvent( QGraphicsSceneMouseEvent *event )

void EqHandle::wheelEvent( QGraphicsSceneWheelEvent *wevent )
{
wevent->accept();

float highestBandwich = m_type != EqHandleType::Para ? 10 : 4;
int numDegrees = wevent->delta() / 120;
float numDegrees = wevent->delta() / 120.f;
float numSteps = 0;
if( wevent->modifiers() == Qt::ControlModifier )
{
Expand All @@ -579,7 +581,6 @@ void EqHandle::wheelEvent( QGraphicsSceneWheelEvent *wevent )

emit positionChanged();
}
wevent->accept();
}


Expand Down
15 changes: 11 additions & 4 deletions plugins/Vectorscope/VectorView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "GuiApplication.h"
#include "FontHelper.h"
#include "MainWindow.h"
#include "Scroll.h"
#include "VecControls.h"

namespace lmms::gui
Expand Down Expand Up @@ -241,12 +242,18 @@ void VectorView::mouseDoubleClickEvent(QMouseEvent *event)
// Change zoom level using the mouse wheel
void VectorView::wheelEvent(QWheelEvent *event)
{
// Go through integers to avoid accumulating errors
auto scroll = Scroll(event);
event->accept();

// Increment 20% per mouse wheel step
const int increment = scroll.getSteps(20);

// Round zoom percentage to integers to avoid accumulating errors
const unsigned short old_zoom = round(100 * m_zoom);
// Min-max bounds are 20 and 1000 %, step for 15°-increment mouse wheel is 20 %
const unsigned short new_zoom = qBound(20, old_zoom + event->angleDelta().y() / 6, 1000);
// Min-max bounds are 20 and 1000 %
const unsigned short new_zoom = std::clamp(old_zoom + increment, 20, 1000);

m_zoom = new_zoom / 100.f;
event->accept();
m_zoomTimestamp = std::chrono::duration_cast<std::chrono::milliseconds>
(
std::chrono::high_resolution_clock::now().time_since_epoch()
Expand Down
1 change: 1 addition & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ SET(LMMS_SRCS
gui/SampleLoader.cpp
gui/SampleTrackWindow.cpp
gui/SampleThumbnail.cpp
gui/Scroll.cpp
gui/SendButtonIndicator.cpp
gui/SideBar.cpp
gui/SideBarWidget.cpp
Expand Down
Loading
Loading