Skip to content

Commit 9ede9f3

Browse files
committed
Added custom scrollbar for WaveFormView
1 parent 2e0e954 commit 9ede9f3

11 files changed

+552
-102
lines changed

SoundGenerator.pro

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ SOURCES += \
6767
UI/WaveFormView.cpp \
6868
LoopableBuffer.cpp \
6969
ActionCycle.cpp \
70-
UI/TimeRuler.cpp
70+
UI/TimeRuler.cpp \
71+
UI/WaveFormScrollBar.cpp
7172

7273
HEADERS += \
7374
Components/AddComponent.h \
@@ -110,7 +111,8 @@ HEADERS += \
110111
UI/WaveFormView.h \
111112
LoopableBuffer.h \
112113
ActionCycle.h \
113-
UI/TimeRuler.h
114+
UI/TimeRuler.h \
115+
UI/WaveFormScrollBar.h
114116

115117
FORMS += \
116118
UI/mainwindow.ui

UI/MainWindow.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,10 @@ MainWindow::MainWindow(QWidget* _parent)
6060
ui->waveFormView->setSignal(&m_signal);
6161
connect(ui->waveFormView, &WaveFormView::zoomChanged, this, &MainWindow::onWaveFormViewZoomChanged);
6262
connect(ui->waveFormScrollBar, &QScrollBar::valueChanged, this, &MainWindow::onScrollbarValueChanged);
63-
connect(ui->timeRuler, &TimeRuler::onTimeSelected, ui->waveFormView, &WaveFormView::setCursorTime);
64-
connect(ui->timeRuler, &TimeRuler::onTimeSelected, &m_signal, &Signal::setCursorTime);
63+
connect(ui->timeRuler, &TimeRuler::onTimeSelected, this, &MainWindow::onTimeCursorChanged);
64+
connect(ui->waveFormScrollBar, &WaveFormScrollBar::pageStepChanged, this, &MainWindow::onScrollbarValueChanged);
65+
ui->waveFormScrollBar->setMaximum(5000);
66+
ui->waveFormScrollBar->setPageStep(1000);
6567

6668
m_scene = new NodalScene(ui->nodalView);
6769
m_scene->setBackgroundBrush(Qt::black);
@@ -567,12 +569,22 @@ void MainWindow::onScrollbarValueChanged()
567569

568570
// update wave form view
569571
ui->waveFormView->setSampleOffset(offset);
572+
ui->waveFormView->setNbSampleViewed(viewSize);
570573

571574
// update time ruler
572575
qreal sampleDuration = 1.0 / static_cast<qreal>(m_signal.getSampleRate());
573576
ui->timeRuler->setTimeWindow(offset * sampleDuration, (offset + viewSize) * sampleDuration);
574577
}
575578

579+
void MainWindow::onTimeCursorChanged(qreal _newTime)
580+
{
581+
m_signal.setCursorTime(_newTime);
582+
583+
int cursorSample = qRound(_newTime * m_signal.getSampleRate());
584+
ui->waveFormView->setCursorSample(cursorSample);
585+
ui->waveFormScrollBar->setCursorValue(cursorSample);
586+
}
587+
576588
void MainWindow::createActions()
577589
{
578590

UI/MainWindow.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public slots:
7979

8080
void onWaveFormViewZoomChanged();
8181
void onScrollbarValueChanged();
82+
void onTimeCursorChanged(qreal _newTime);
8283

8384
private:
8485
void createActions();

UI/TimeRuler.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ void TimeRuler::paintEvent(QPaintEvent *_event)
9393

9494
int cursorRadius = 2;
9595
int height = size().height() - 1;
96-
int x = qRound(Utils::MapValue(m_currentTime, m_startTime, m_endTime, 0.0, size().width())) - CURSOR_HALF_WIDTH;
96+
int x = qRound(Utils::MapValue(m_currentTime, m_startTime, m_endTime, 0.0, static_cast<qreal>(size().width()))) - CURSOR_HALF_WIDTH;
9797

9898
QPainterPath cursor;
9999
cursor.moveTo(x, cursorRadius);
@@ -112,7 +112,7 @@ void TimeRuler::mousePressEvent(QMouseEvent *_event)
112112
if(_event->button() == Qt::MouseButton::LeftButton)
113113
{
114114
m_draggingCursor = true;
115-
int cursorPos = qRound(Utils::MapValue(m_currentTime, m_startTime, m_endTime, 0.0, size().width()));
115+
int cursorPos = qRound(Utils::MapValue(m_currentTime, m_startTime, m_endTime, 0.0, static_cast<qreal>(size().width())));
116116
int mousePos = _event->pos().x();
117117
m_cursorOffset = qAbs(cursorPos - mousePos) < CURSOR_HALF_WIDTH ? cursorPos - mousePos : 0;
118118
updateCursor(mousePos);
@@ -140,7 +140,7 @@ void TimeRuler::mouseReleaseEvent(QMouseEvent *_event)
140140
void TimeRuler::updateCursor(int _mousePosX)
141141
{
142142
qreal value = static_cast<qreal>(_mousePosX + m_cursorOffset);
143-
m_currentTime = Utils::MapValue(value, 0, static_cast<qreal>(size().width()), m_startTime, m_endTime);
143+
m_currentTime = Utils::MapValue(value, 0.0, static_cast<qreal>(size().width()), m_startTime, m_endTime);
144144
update();
145145
emit onTimeSelected(m_currentTime);
146146
}

UI/WaveFormScrollBar.cpp

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
/*
2+
* Copyright 2020 Benoit Pelletier
3+
*
4+
* This file is part of Sound Generator.
5+
*
6+
* Sound Generator is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Sound Generator is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Sound Generator. If not, see <https://www.gnu.org/licenses/>.
18+
*
19+
*/
20+
21+
#include "WaveFormScrollBar.h"
22+
#include <QPainter>
23+
#include <QtWidgets>
24+
#include <QMouseEvent>
25+
#include "Utils.h"
26+
27+
WaveFormScrollBar::WaveFormScrollBar(QWidget* _parent)
28+
: QAbstractSlider(_parent)
29+
{
30+
installEventFilter(this);
31+
}
32+
33+
void WaveFormScrollBar::setCursorValue(int _value)
34+
{
35+
m_cursorValue = _value;
36+
repaint();
37+
}
38+
39+
void WaveFormScrollBar::paintEvent(QPaintEvent *_event)
40+
{
41+
Q_UNUSED(_event);
42+
43+
int radius = 5;
44+
int minX, maxX;
45+
getScreenStartAndWidth(minX, maxX);
46+
maxX += minX - HANDLE_WIDTH;
47+
minX += HANDLE_WIDTH;
48+
49+
QPainter painter(this);
50+
painter.setRenderHint(QPainter::Antialiasing);
51+
52+
QPen pen(QColor(140, 140, 140));
53+
pen.setCosmetic(true);
54+
painter.setPen(pen);
55+
56+
painter.drawRoundedRect(0, 0, size().width(), size().height(), 6, 6);
57+
58+
QBrush brush(QColor(0, 0, 0));
59+
60+
pen.setColor(QColor(200, 200, 200));
61+
painter.setPen(pen);
62+
63+
brush.setColor(getColor(m_dragZone == SliderZone::NONE && m_hoverZone == SliderZone::DEFAULT, m_dragZone == SliderZone::DEFAULT));
64+
painter.setBrush(brush);
65+
painter.drawRect(minX, 0, maxX - minX, size().height());
66+
67+
QPainterPath leftHandle;
68+
leftHandle.moveTo(minX, 0);
69+
leftHandle.lineTo(minX, size().height());
70+
leftHandle.arcTo(minX - HANDLE_WIDTH, size().height() - 2*radius, 2*radius, 2*radius, -90, -90);
71+
leftHandle.arcTo(minX - HANDLE_WIDTH, 0, 2*radius, 2*radius, 180, -90);
72+
leftHandle.closeSubpath();
73+
74+
brush.setColor(getColor(m_dragZone == SliderZone::NONE && m_hoverZone == SliderZone::MINIMUM, m_dragZone == SliderZone::MINIMUM));
75+
painter.setBrush(brush);
76+
painter.drawPath(leftHandle);
77+
78+
QPainterPath rightHandle;
79+
rightHandle.moveTo(maxX, 0);
80+
rightHandle.lineTo(maxX, size().height());
81+
rightHandle.arcTo(maxX + HANDLE_WIDTH - 2*radius, size().height() - 2*radius, 2*radius, 2*radius, -90, 90);
82+
rightHandle.arcTo(maxX + HANDLE_WIDTH - 2*radius, 0, 2*radius, 2*radius, 0, 90);
83+
rightHandle.closeSubpath();
84+
85+
brush.setColor(getColor(m_dragZone == SliderZone::NONE && m_hoverZone == SliderZone::MAXIMUM, m_dragZone == SliderZone::MAXIMUM));
86+
painter.setBrush(brush);
87+
painter.drawPath(rightHandle);
88+
89+
int cursorX = mapToScreen(m_cursorValue);
90+
pen.setColor(QColor("yellow"));
91+
painter.setPen(pen);
92+
painter.drawLine(cursorX, 0, cursorX, size().height());
93+
}
94+
95+
// Called only when mouse button is pressed inside and dragged
96+
void WaveFormScrollBar::mouseMoveEvent(QMouseEvent *_event)
97+
{
98+
int newValue = 0;
99+
int newPageStep = 0;
100+
int mouseValue = m_valueStart + mapToValue(_event->pos().x() - m_mouseStart);
101+
102+
switch(m_dragZone)
103+
{
104+
case SliderZone::DEFAULT:
105+
newValue = mouseValue;
106+
setValue(newValue);
107+
break;
108+
case SliderZone::MINIMUM:
109+
newValue = Utils::Clamp(mouseValue, minimum(), value() + pageStep() - 1);
110+
newPageStep = value() + pageStep() - newValue;
111+
setMaximum(maximum() + pageStep() - newPageStep);
112+
setPageStep(newPageStep);
113+
setValue(newValue);
114+
break;
115+
case SliderZone::MAXIMUM:
116+
newPageStep = Utils::Clamp(mouseValue, value() + 1, maximum() + pageStep()) - value();
117+
setMaximum(maximum() + pageStep() - newPageStep);
118+
setPageStep(newPageStep);
119+
emit pageStepChanged(newPageStep);
120+
break;
121+
default:
122+
break;
123+
}
124+
}
125+
126+
void WaveFormScrollBar::mousePressEvent(QMouseEvent *_event)
127+
{
128+
int mousePos = _event->pos().x();
129+
130+
SliderZone zone = getSliderZone(mousePos);
131+
if(zone == SliderZone::PAGEDOWN)
132+
{
133+
setValue(value() - pageStep());
134+
updateSliderState(mousePos);
135+
}
136+
else if(zone == SliderZone::PAGEUP)
137+
{
138+
setValue(value() + pageStep());
139+
updateSliderState(mousePos);
140+
}
141+
else if(zone != m_dragZone)
142+
{
143+
m_dragZone = zone;
144+
m_mouseStart = mousePos;
145+
m_valueStart = value() + (zone == SliderZone::MAXIMUM ? pageStep() : 0);
146+
updateSliderState(mousePos);
147+
repaint();
148+
}
149+
}
150+
151+
void WaveFormScrollBar::mouseReleaseEvent(QMouseEvent *_event)
152+
{
153+
m_dragZone = SliderZone::NONE;
154+
updateSliderState(_event->pos().x());
155+
repaint();
156+
}
157+
158+
void WaveFormScrollBar::mouseEnterEvent(QHoverEvent *_event)
159+
{
160+
updateSliderState(_event->pos().x());
161+
}
162+
163+
void WaveFormScrollBar::mouseLeaveEvent(QHoverEvent *_event)
164+
{
165+
updateSliderState(_event->pos().x());
166+
}
167+
168+
void WaveFormScrollBar::mouseHoverMoveEvent(QHoverEvent *_event)
169+
{
170+
updateSliderState(_event->pos().x());
171+
}
172+
173+
bool WaveFormScrollBar::eventFilter(QObject* _object, QEvent* _event)
174+
{
175+
Q_UNUSED(_object)
176+
177+
switch(_event->type())
178+
{
179+
case QEvent::HoverEnter:
180+
mouseEnterEvent(static_cast<QHoverEvent*>(_event));
181+
break;
182+
case QEvent::HoverLeave:
183+
mouseLeaveEvent(static_cast<QHoverEvent*>(_event));
184+
break;
185+
case QEvent::HoverMove:
186+
mouseHoverMoveEvent(static_cast<QHoverEvent*>(_event));
187+
break;
188+
default:
189+
break;
190+
}
191+
return false;
192+
}
193+
194+
int WaveFormScrollBar::mapToScreen(int _value)
195+
{
196+
return Utils::MapValue(_value, minimum(), maximum() + pageStep(), 0, size().width());
197+
}
198+
199+
int WaveFormScrollBar::mapToValue(int _screen)
200+
{
201+
return Utils::MapValue(_screen, 0, size().width(), minimum(), maximum() + pageStep());
202+
}
203+
204+
void WaveFormScrollBar::updateSliderState(int _mousePos)
205+
{
206+
SliderZone hoverMode = getSliderZone(_mousePos);
207+
if(hoverMode != m_hoverZone)
208+
{
209+
m_hoverZone = hoverMode;
210+
repaint();
211+
}
212+
213+
if(m_dragZone == SliderZone::NONE && m_hoverZone == SliderZone::DEFAULT)
214+
{
215+
setCursor(Qt::OpenHandCursor);
216+
}
217+
else if (m_dragZone == SliderZone::DEFAULT)
218+
{
219+
setCursor(Qt::ClosedHandCursor);
220+
}
221+
else if(m_dragZone == SliderZone::MINIMUM || m_dragZone == SliderZone::MAXIMUM
222+
|| m_hoverZone == SliderZone::MINIMUM || m_hoverZone == SliderZone::MAXIMUM)
223+
{
224+
setCursor(Qt::SizeHorCursor);
225+
}
226+
else
227+
{
228+
setCursor(Qt::ArrowCursor);
229+
}
230+
}
231+
232+
QColor WaveFormScrollBar::getColor(bool _hovered, bool _pressed)
233+
{
234+
return _pressed ? PRESSED_COLOR : (_hovered ? HOVERED_COLOR : NORMAL_COLOR);
235+
}
236+
237+
WaveFormScrollBar::SliderZone WaveFormScrollBar::getSliderZone(int _x)
238+
{
239+
int min, max;
240+
getScreenStartAndWidth(min, max);
241+
max += min;
242+
243+
if(_x >= min && _x <= min + HANDLE_WIDTH)
244+
return SliderZone::MINIMUM;
245+
if(_x >= max - HANDLE_WIDTH && _x <= max)
246+
return SliderZone::MAXIMUM;
247+
if(_x > min && _x < max)
248+
return SliderZone::DEFAULT;
249+
if(_x >= minimum() && _x < min)
250+
return SliderZone::PAGEDOWN;
251+
if(_x > max && _x <= maximum())
252+
return SliderZone::PAGEUP;
253+
return SliderZone::NONE;
254+
}
255+
256+
void WaveFormScrollBar::getScreenStartAndWidth(int& _start, int& _width)
257+
{
258+
int targetWidth = mapToScreen(pageStep());
259+
int minWidth = 2 * HANDLE_WIDTH + SLIDER_MIN_WIDTH;
260+
if(targetWidth < minWidth)
261+
{
262+
qreal percentage = (value() - minimum()) / static_cast<qreal>(maximum() - minimum());
263+
int offset = qRound((minWidth - targetWidth) * percentage);
264+
_start = mapToScreen(value()) - offset;
265+
_width = minWidth;
266+
267+
qDebug() << QString("start: %1 | percentage: %2 | %3/%4 | %5/%6")
268+
.arg(_start)
269+
.arg(percentage)
270+
.arg(value())
271+
.arg(maximum())
272+
.arg(offset)
273+
.arg(minWidth);
274+
}
275+
else
276+
{
277+
_start = mapToScreen(value());
278+
_width = targetWidth;
279+
}
280+
281+
}

0 commit comments

Comments
 (0)