mirror-chatterino2/src/widgets/Scrollbar.cpp

460 lines
11 KiB
C++
Raw Normal View History

2018-06-26 14:09:39 +02:00
#include "widgets/Scrollbar.hpp"
2018-06-26 14:09:39 +02:00
#include "Application.hpp"
#include "singletons/Settings.hpp"
2018-06-28 20:03:04 +02:00
#include "singletons/Theme.hpp"
#include "singletons/WindowManager.hpp"
2018-06-26 14:09:39 +02:00
#include "widgets/helper/ChannelView.hpp"
2017-01-03 21:19:33 +01:00
2017-06-06 17:18:23 +02:00
#include <QDebug>
2017-01-26 04:26:40 +01:00
#include <QMouseEvent>
2017-01-18 04:52:47 +01:00
#include <QPainter>
#include <QTimer>
#include <cmath>
2017-01-18 04:52:47 +01:00
2017-01-18 04:33:30 +01:00
#define MIN_THUMB_HEIGHT 10
2017-04-14 17:52:22 +02:00
namespace chatterino {
2017-01-18 21:30:23 +01:00
2018-01-06 03:48:56 +01:00
Scrollbar::Scrollbar(ChannelView *parent)
: BaseWidget(parent)
2018-06-19 20:34:50 +02:00
, currentValueAnimation_(this, "currentValue_")
2017-04-12 17:46:44 +02:00
{
2018-11-21 21:37:41 +01:00
resize(int(16 * this->scale()), 100);
2018-06-13 13:27:10 +02:00
this->currentValueAnimation_.setDuration(150);
2018-08-06 21:17:03 +02:00
this->currentValueAnimation_.setEasingCurve(
QEasingCurve(QEasingCurve::OutCubic));
2017-04-12 17:46:44 +02:00
setMouseTracking(true);
2017-01-03 21:19:33 +01:00
}
2018-01-06 03:48:56 +01:00
void Scrollbar::addHighlight(ScrollbarHighlight highlight)
2017-01-03 21:19:33 +01:00
{
2018-01-06 03:48:56 +01:00
ScrollbarHighlight deleted;
2018-06-13 13:27:10 +02:00
this->highlights_.pushBack(highlight, deleted);
2017-01-03 21:19:33 +01:00
}
2018-08-06 21:17:03 +02:00
void Scrollbar::addHighlightsAtStart(
const std::vector<ScrollbarHighlight> &_highlights)
2017-01-03 21:19:33 +01:00
{
2018-06-13 13:27:10 +02:00
this->highlights_.pushFront(_highlights);
2017-01-03 21:19:33 +01:00
}
2018-01-06 03:48:56 +01:00
void Scrollbar::replaceHighlight(size_t index, ScrollbarHighlight replacement)
2017-01-03 21:19:33 +01:00
{
2018-06-13 13:27:10 +02:00
this->highlights_.replaceItem(index, replacement);
}
void Scrollbar::pauseHighlights()
{
this->highlightsPaused_ = true;
}
void Scrollbar::unpauseHighlights()
{
this->highlightsPaused_ = false;
}
void Scrollbar::clearHighlights()
{
this->highlights_.clear();
}
LimitedQueueSnapshot<ScrollbarHighlight> Scrollbar::getHighlightSnapshot()
2018-06-13 13:27:10 +02:00
{
2018-10-21 13:43:02 +02:00
if (!this->highlightsPaused_)
{
2018-06-13 13:27:10 +02:00
this->highlightSnapshot_ = this->highlights_.getSnapshot();
}
return this->highlightSnapshot_;
2017-04-12 17:46:44 +02:00
}
2018-01-06 03:48:56 +01:00
void Scrollbar::scrollToBottom(bool animate)
2017-06-06 17:18:23 +02:00
{
2018-06-13 13:27:10 +02:00
this->setDesiredValue(this->maximum_ - this->getLargeChange(), animate);
2017-06-06 17:18:23 +02:00
}
2018-01-06 03:48:56 +01:00
bool Scrollbar::isAtBottom() const
2017-06-06 17:18:23 +02:00
{
2018-06-13 13:27:10 +02:00
return this->atBottom_;
2017-06-06 17:18:23 +02:00
}
2018-01-06 03:48:56 +01:00
void Scrollbar::setMaximum(qreal value)
2017-04-12 17:46:44 +02:00
{
2018-06-13 13:27:10 +02:00
this->maximum_ = value;
2017-04-12 17:46:44 +02:00
updateScroll();
}
2018-01-06 03:48:56 +01:00
void Scrollbar::setMinimum(qreal value)
2017-04-12 17:46:44 +02:00
{
2018-06-13 13:27:10 +02:00
this->minimum_ = value;
2017-04-12 17:46:44 +02:00
updateScroll();
}
2018-01-06 03:48:56 +01:00
void Scrollbar::setLargeChange(qreal value)
2017-04-12 17:46:44 +02:00
{
2018-06-13 13:27:10 +02:00
this->largeChange_ = value;
2017-04-12 17:46:44 +02:00
updateScroll();
}
2018-01-06 03:48:56 +01:00
void Scrollbar::setSmallChange(qreal value)
2017-04-12 17:46:44 +02:00
{
2018-06-13 13:27:10 +02:00
this->smallChange_ = value;
2017-04-12 17:46:44 +02:00
updateScroll();
}
2018-01-06 03:48:56 +01:00
void Scrollbar::setDesiredValue(qreal value, bool animated)
2017-04-12 17:46:44 +02:00
{
animated &= getSettings()->enableSmoothScrolling;
2018-08-06 21:17:03 +02:00
value = std::max(this->minimum_,
std::min(this->maximum_ - this->largeChange_, value));
2017-04-12 17:46:44 +02:00
2018-08-06 21:17:03 +02:00
if (std::abs(this->desiredValue_ + this->smoothScrollingOffset_ - value) >
2018-10-21 13:43:02 +02:00
0.0001)
{
if (animated)
{
2018-06-13 13:27:10 +02:00
this->currentValueAnimation_.stop();
2018-08-06 21:17:03 +02:00
this->currentValueAnimation_.setStartValue(
this->currentValue_ + this->smoothScrollingOffset_);
2017-04-12 17:46:44 +02:00
2018-08-06 21:17:03 +02:00
// if (((this->getMaximum() - this->getLargeChange()) -
// value) <= 0.01) {
2017-12-18 22:13:46 +01:00
// value += 1;
// }
2018-06-19 20:34:50 +02:00
2018-06-13 13:27:10 +02:00
this->currentValueAnimation_.setEndValue(value);
this->smoothScrollingOffset_ = 0;
2018-08-06 21:17:03 +02:00
this->atBottom_ = ((this->getMaximum() - this->getLargeChange()) -
value) <= 0.0001;
2018-06-13 13:27:10 +02:00
this->currentValueAnimation_.start();
2018-10-21 13:43:02 +02:00
}
else
{
2018-08-06 21:17:03 +02:00
if (this->currentValueAnimation_.state() !=
2018-10-21 13:43:02 +02:00
QPropertyAnimation::Running)
{
2018-06-13 13:27:10 +02:00
this->smoothScrollingOffset_ = 0;
this->desiredValue_ = value;
this->currentValueAnimation_.stop();
2018-08-06 21:17:03 +02:00
this->atBottom_ =
((this->getMaximum() - this->getLargeChange()) - value) <=
0.0001;
2017-04-12 17:46:44 +02:00
setCurrentValue(value);
}
}
2017-01-03 21:19:33 +01:00
}
2018-06-13 13:27:10 +02:00
this->smoothScrollingOffset_ = 0;
this->desiredValue_ = value;
this->desiredValueChanged_.invoke();
2017-04-12 17:46:44 +02:00
}
2018-01-06 03:48:56 +01:00
qreal Scrollbar::getMaximum() const
2017-04-12 17:46:44 +02:00
{
2018-06-13 13:27:10 +02:00
return this->maximum_;
2017-04-12 17:46:44 +02:00
}
2018-01-06 03:48:56 +01:00
qreal Scrollbar::getMinimum() const
2017-04-12 17:46:44 +02:00
{
2018-06-13 13:27:10 +02:00
return this->minimum_;
2017-04-12 17:46:44 +02:00
}
2018-01-06 03:48:56 +01:00
qreal Scrollbar::getLargeChange() const
2017-04-12 17:46:44 +02:00
{
2018-06-13 13:27:10 +02:00
return this->largeChange_;
2017-04-12 17:46:44 +02:00
}
2018-01-06 03:48:56 +01:00
qreal Scrollbar::getSmallChange() const
2017-04-12 17:46:44 +02:00
{
2018-06-13 13:27:10 +02:00
return this->smallChange_;
2017-04-12 17:46:44 +02:00
}
2018-01-06 03:48:56 +01:00
qreal Scrollbar::getDesiredValue() const
2017-04-12 17:46:44 +02:00
{
2018-06-13 13:27:10 +02:00
return this->desiredValue_ + this->smoothScrollingOffset_;
2017-04-12 17:46:44 +02:00
}
2018-01-06 03:48:56 +01:00
qreal Scrollbar::getCurrentValue() const
2017-04-12 17:46:44 +02:00
{
2018-06-13 13:27:10 +02:00
return this->currentValue_;
2017-04-12 17:46:44 +02:00
}
2018-01-06 03:48:56 +01:00
void Scrollbar::offset(qreal value)
{
2018-10-21 13:43:02 +02:00
if (this->currentValueAnimation_.state() == QPropertyAnimation::Running)
{
2018-06-13 13:27:10 +02:00
this->smoothScrollingOffset_ += value;
2018-10-21 13:43:02 +02:00
}
else
{
this->setDesiredValue(this->getDesiredValue() + value);
}
}
pajlada::Signals::NoArgSignal &Scrollbar::getCurrentValueChanged()
2017-04-12 17:46:44 +02:00
{
2018-06-13 13:27:10 +02:00
return this->currentValueChanged_;
}
pajlada::Signals::NoArgSignal &Scrollbar::getDesiredValueChanged()
{
return this->desiredValueChanged_;
2017-04-12 17:46:44 +02:00
}
2018-01-06 03:48:56 +01:00
void Scrollbar::setCurrentValue(qreal value)
2017-04-12 17:46:44 +02:00
{
2018-08-06 21:17:03 +02:00
value = std::max(this->minimum_,
std::min(this->maximum_ - this->largeChange_,
value + this->smoothScrollingOffset_));
2017-04-12 17:46:44 +02:00
2018-10-21 13:43:02 +02:00
if (std::abs(this->currentValue_ - value) > 0.0001)
{
2018-06-13 13:27:10 +02:00
this->currentValue_ = value;
2017-04-12 17:46:44 +02:00
this->updateScroll();
2018-06-13 13:27:10 +02:00
this->currentValueChanged_.invoke();
2017-04-12 17:46:44 +02:00
this->update();
2017-04-12 17:46:44 +02:00
}
2017-01-03 21:19:33 +01:00
}
2018-01-06 03:48:56 +01:00
void Scrollbar::printCurrentState(const QString &prefix) const
2017-06-06 17:18:23 +02:00
{
qDebug() << prefix //
<< "Current value: " << this->getCurrentValue() //
<< ". Maximum: " << this->getMaximum() //
<< ". Minimum: " << this->getMinimum() //
<< ". Large change: " << this->getLargeChange(); //
}
2018-01-06 03:48:56 +01:00
void Scrollbar::paintEvent(QPaintEvent *)
2017-01-03 21:19:33 +01:00
{
2018-06-13 13:27:10 +02:00
bool mouseOver = this->mouseOverIndex_ != -1;
2018-11-21 21:37:41 +01:00
int xOffset = mouseOver ? 0 : width() - int(4 * this->scale());
2017-01-03 21:19:33 +01:00
QPainter painter(this);
2018-07-06 17:11:37 +02:00
painter.fillRect(rect(), this->theme->scrollbars.background);
2017-01-03 21:19:33 +01:00
bool enableRedeemedHighlights = getSettings()->enableRedeemedHighlight;
2018-01-02 02:15:11 +01:00
// painter.fillRect(QRect(xOffset, 0, width(), this->buttonHeight),
// this->themeManager->ScrollbarArrow);
2018-08-06 21:17:03 +02:00
// painter.fillRect(QRect(xOffset, height() - this->buttonHeight,
// width(), this->buttonHeight),
// this->themeManager->ScrollbarArrow);
2017-08-12 12:09:26 +02:00
2018-06-13 13:27:10 +02:00
this->thumbRect_.setX(xOffset);
2017-08-12 12:09:26 +02:00
// mouse over thumb
2018-10-21 13:43:02 +02:00
if (this->mouseDownIndex_ == 2)
{
2018-08-06 21:17:03 +02:00
painter.fillRect(this->thumbRect_,
this->theme->scrollbars.thumbSelected);
2017-08-12 12:09:26 +02:00
}
// mouse not over thumb
2018-10-21 13:43:02 +02:00
else
{
2018-07-06 17:11:37 +02:00
painter.fillRect(this->thumbRect_, this->theme->scrollbars.thumb);
2017-08-12 12:09:26 +02:00
}
2017-01-26 04:26:40 +01:00
2018-01-06 03:48:56 +01:00
// draw highlights
2018-06-13 13:27:10 +02:00
auto snapshot = this->getHighlightSnapshot();
2018-11-03 21:26:57 +01:00
size_t snapshotLength = snapshot.size();
2018-01-06 20:57:01 +01:00
2018-10-21 13:43:02 +02:00
if (snapshotLength == 0)
{
2018-01-06 20:57:01 +01:00
return;
}
2018-01-06 03:48:56 +01:00
int w = this->width();
float y = 0;
2020-04-21 20:57:16 +02:00
float dY = float(this->height()) / float(snapshotLength);
int highlightHeight =
int(std::ceil(std::max<float>(this->scale() * 2, dY)));
2018-01-06 03:48:56 +01:00
2018-10-21 13:43:02 +02:00
for (size_t i = 0; i < snapshotLength; i++)
{
2018-01-06 03:48:56 +01:00
ScrollbarHighlight const &highlight = snapshot[i];
2018-10-21 13:43:02 +02:00
if (!highlight.isNull())
{
2020-04-15 23:25:57 +02:00
if (!highlight.isRedeemedHighlight() || enableRedeemedHighlights)
{
2020-04-15 23:25:57 +02:00
QColor color = highlight.getColor();
color.setAlpha(255);
switch (highlight.getStyle())
{
case ScrollbarHighlight::Default: {
painter.fillRect(w / 8 * 3, int(y), w / 4,
highlightHeight, color);
}
break;
case ScrollbarHighlight::Line: {
painter.fillRect(0, int(y), w, 1, color);
}
break;
case ScrollbarHighlight::None:;
2018-10-21 13:43:02 +02:00
}
2018-01-06 03:48:56 +01:00
}
}
2017-01-03 21:19:33 +01:00
2018-01-06 03:48:56 +01:00
y += dY;
}
2017-01-18 04:33:30 +01:00
}
2018-01-06 03:48:56 +01:00
void Scrollbar::resizeEvent(QResizeEvent *)
2017-12-26 16:54:39 +01:00
{
2018-11-21 21:37:41 +01:00
this->resize(int(16 * this->scale()), this->height());
2017-12-26 16:54:39 +01:00
}
2018-01-06 03:48:56 +01:00
void Scrollbar::mouseMoveEvent(QMouseEvent *event)
2017-01-26 04:26:40 +01:00
{
2018-10-21 13:43:02 +02:00
if (this->mouseDownIndex_ == -1)
{
2017-01-26 04:26:40 +01:00
int y = event->pos().y();
2018-06-13 13:27:10 +02:00
auto oldIndex = this->mouseOverIndex_;
2017-01-26 04:26:40 +01:00
2018-10-21 13:43:02 +02:00
if (y < this->buttonHeight_)
{
2018-06-13 13:27:10 +02:00
this->mouseOverIndex_ = 0;
2018-10-21 13:43:02 +02:00
}
else if (y < this->thumbRect_.y())
{
2018-06-13 13:27:10 +02:00
this->mouseOverIndex_ = 1;
2018-10-21 13:43:02 +02:00
}
else if (this->thumbRect_.contains(2, y))
{
2018-06-13 13:27:10 +02:00
this->mouseOverIndex_ = 2;
2018-10-21 13:43:02 +02:00
}
else if (y < height() - this->buttonHeight_)
{
2018-06-13 13:27:10 +02:00
this->mouseOverIndex_ = 3;
2018-10-21 13:43:02 +02:00
}
else
{
2018-06-13 13:27:10 +02:00
this->mouseOverIndex_ = 4;
2017-01-26 04:26:40 +01:00
}
2018-10-21 13:43:02 +02:00
if (oldIndex != this->mouseOverIndex_)
{
2017-04-12 17:46:44 +02:00
update();
2017-01-26 04:26:40 +01:00
}
2018-10-21 13:43:02 +02:00
}
else if (this->mouseDownIndex_ == 2)
{
2018-06-13 13:27:10 +02:00
int delta = event->pos().y() - this->lastMousePosition_.y();
2017-01-26 04:26:40 +01:00
2020-05-07 00:21:08 +02:00
setDesiredValue(
this->desiredValue_ +
(qreal(delta) / std::max<qreal>(0.00000002, this->trackHeight_)) *
this->maximum_);
2017-01-26 04:26:40 +01:00
}
2018-06-13 13:27:10 +02:00
this->lastMousePosition_ = event->pos();
2017-01-26 04:26:40 +01:00
}
2018-01-06 03:48:56 +01:00
void Scrollbar::mousePressEvent(QMouseEvent *event)
2017-01-26 04:26:40 +01:00
{
int y = event->pos().y();
2018-10-21 13:43:02 +02:00
if (y < this->buttonHeight_)
{
2018-06-13 13:27:10 +02:00
this->mouseDownIndex_ = 0;
2018-10-21 13:43:02 +02:00
}
else if (y < this->thumbRect_.y())
{
2018-06-13 13:27:10 +02:00
this->mouseDownIndex_ = 1;
2018-10-21 13:43:02 +02:00
}
else if (this->thumbRect_.contains(2, y))
{
2018-06-13 13:27:10 +02:00
this->mouseDownIndex_ = 2;
2018-10-21 13:43:02 +02:00
}
else if (y < height() - this->buttonHeight_)
{
2018-06-13 13:27:10 +02:00
this->mouseDownIndex_ = 3;
2018-10-21 13:43:02 +02:00
}
else
{
2018-06-13 13:27:10 +02:00
this->mouseDownIndex_ = 4;
2017-01-26 04:26:40 +01:00
}
}
2018-01-06 03:48:56 +01:00
void Scrollbar::mouseReleaseEvent(QMouseEvent *event)
2017-01-26 04:26:40 +01:00
{
int y = event->pos().y();
2018-10-21 13:43:02 +02:00
if (y < this->buttonHeight_)
{
if (this->mouseDownIndex_ == 0)
{
2018-06-13 13:27:10 +02:00
setDesiredValue(this->desiredValue_ - this->smallChange_, true);
2017-01-26 04:26:40 +01:00
}
2018-10-21 13:43:02 +02:00
}
else if (y < this->thumbRect_.y())
{
if (this->mouseDownIndex_ == 1)
{
2018-06-13 13:27:10 +02:00
setDesiredValue(this->desiredValue_ - this->smallChange_, true);
2017-01-26 04:26:40 +01:00
}
2018-10-21 13:43:02 +02:00
}
else if (this->thumbRect_.contains(2, y))
{
2017-01-26 04:26:40 +01:00
// do nothing
2018-10-21 13:43:02 +02:00
}
else if (y < height() - this->buttonHeight_)
{
if (this->mouseDownIndex_ == 3)
{
2018-06-13 13:27:10 +02:00
setDesiredValue(this->desiredValue_ + this->smallChange_, true);
2017-01-26 04:26:40 +01:00
}
2018-10-21 13:43:02 +02:00
}
else
{
if (this->mouseDownIndex_ == 4)
{
2018-06-13 13:27:10 +02:00
setDesiredValue(this->desiredValue_ + this->smallChange_, true);
2017-01-26 04:26:40 +01:00
}
}
2018-06-13 13:27:10 +02:00
this->mouseDownIndex_ = -1;
2017-01-26 07:10:46 +01:00
update();
2017-01-26 04:26:40 +01:00
}
2018-01-06 03:48:56 +01:00
void Scrollbar::leaveEvent(QEvent *)
2017-01-26 04:26:40 +01:00
{
2018-06-13 13:27:10 +02:00
this->mouseOverIndex_ = -1;
2017-01-26 04:26:40 +01:00
2017-01-26 07:10:46 +01:00
update();
2017-01-26 04:26:40 +01:00
}
2018-01-06 03:48:56 +01:00
void Scrollbar::updateScroll()
2017-01-18 04:33:30 +01:00
{
2018-08-06 21:17:03 +02:00
this->trackHeight_ = this->height() - this->buttonHeight_ -
this->buttonHeight_ - MIN_THUMB_HEIGHT - 1;
2017-01-18 04:33:30 +01:00
2020-04-21 20:57:16 +02:00
auto div = std::max<qreal>(0.0000001, this->maximum_);
2020-04-21 20:57:16 +02:00
this->thumbRect_ = QRect(
0,
int(this->currentValue_ / div * this->trackHeight_) + 1 +
this->buttonHeight_,
this->width(),
int(this->largeChange_ / div * this->trackHeight_) + MIN_THUMB_HEIGHT);
2017-01-18 04:33:30 +01:00
2018-06-13 13:27:10 +02:00
this->update();
2017-01-03 21:19:33 +01:00
}
2017-06-06 17:18:23 +02:00
} // namespace chatterino