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