mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
458 lines
11 KiB
C++
458 lines
11 KiB
C++
#include "widgets/Scrollbar.hpp"
|
|
|
|
#include "Application.hpp"
|
|
#include "singletons/Settings.hpp"
|
|
#include "singletons/Theme.hpp"
|
|
#include "singletons/WindowManager.hpp"
|
|
#include "widgets/helper/ChannelView.hpp"
|
|
|
|
#include <QDebug>
|
|
#include <QMouseEvent>
|
|
#include <QPainter>
|
|
#include <QTimer>
|
|
|
|
#include <cmath>
|
|
|
|
#define MIN_THUMB_HEIGHT 10
|
|
|
|
namespace chatterino {
|
|
|
|
Scrollbar::Scrollbar(ChannelView *parent)
|
|
: BaseWidget(parent)
|
|
, currentValueAnimation_(this, "currentValue_")
|
|
{
|
|
resize(int(16 * this->scale()), 100);
|
|
this->currentValueAnimation_.setDuration(150);
|
|
this->currentValueAnimation_.setEasingCurve(
|
|
QEasingCurve(QEasingCurve::OutCubic));
|
|
|
|
setMouseTracking(true);
|
|
}
|
|
|
|
void Scrollbar::addHighlight(ScrollbarHighlight highlight)
|
|
{
|
|
ScrollbarHighlight deleted;
|
|
this->highlights_.pushBack(highlight, deleted);
|
|
}
|
|
|
|
void Scrollbar::addHighlightsAtStart(
|
|
const std::vector<ScrollbarHighlight> &_highlights)
|
|
{
|
|
this->highlights_.pushFront(_highlights);
|
|
}
|
|
|
|
void Scrollbar::replaceHighlight(size_t index, ScrollbarHighlight replacement)
|
|
{
|
|
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()
|
|
{
|
|
if (!this->highlightsPaused_)
|
|
{
|
|
this->highlightSnapshot_ = this->highlights_.getSnapshot();
|
|
}
|
|
|
|
return this->highlightSnapshot_;
|
|
}
|
|
|
|
void Scrollbar::scrollToBottom(bool animate)
|
|
{
|
|
this->setDesiredValue(this->maximum_ - this->getLargeChange(), animate);
|
|
}
|
|
|
|
bool Scrollbar::isAtBottom() const
|
|
{
|
|
return this->atBottom_;
|
|
}
|
|
|
|
void Scrollbar::setMaximum(qreal value)
|
|
{
|
|
this->maximum_ = value;
|
|
|
|
updateScroll();
|
|
}
|
|
|
|
void Scrollbar::setMinimum(qreal value)
|
|
{
|
|
this->minimum_ = value;
|
|
|
|
updateScroll();
|
|
}
|
|
|
|
void Scrollbar::setLargeChange(qreal value)
|
|
{
|
|
this->largeChange_ = value;
|
|
|
|
updateScroll();
|
|
}
|
|
|
|
void Scrollbar::setSmallChange(qreal value)
|
|
{
|
|
this->smallChange_ = value;
|
|
|
|
updateScroll();
|
|
}
|
|
|
|
void Scrollbar::setDesiredValue(qreal value, bool animated)
|
|
{
|
|
animated &= getSettings()->enableSmoothScrolling;
|
|
value = std::max(this->minimum_,
|
|
std::min(this->maximum_ - this->largeChange_, value));
|
|
|
|
if (std::abs(this->desiredValue_ + this->smoothScrollingOffset_ - value) >
|
|
0.0001)
|
|
{
|
|
if (animated)
|
|
{
|
|
this->currentValueAnimation_.stop();
|
|
this->currentValueAnimation_.setStartValue(
|
|
this->currentValue_ + this->smoothScrollingOffset_);
|
|
|
|
// if (((this->getMaximum() - this->getLargeChange()) -
|
|
// value) <= 0.01) {
|
|
// value += 1;
|
|
// }
|
|
|
|
this->currentValueAnimation_.setEndValue(value);
|
|
this->smoothScrollingOffset_ = 0;
|
|
this->atBottom_ = ((this->getMaximum() - this->getLargeChange()) -
|
|
value) <= 0.0001;
|
|
this->currentValueAnimation_.start();
|
|
}
|
|
else
|
|
{
|
|
if (this->currentValueAnimation_.state() !=
|
|
QPropertyAnimation::Running)
|
|
{
|
|
this->smoothScrollingOffset_ = 0;
|
|
this->desiredValue_ = value;
|
|
this->currentValueAnimation_.stop();
|
|
this->atBottom_ =
|
|
((this->getMaximum() - this->getLargeChange()) - value) <=
|
|
0.0001;
|
|
setCurrentValue(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
this->smoothScrollingOffset_ = 0;
|
|
this->desiredValue_ = value;
|
|
|
|
this->desiredValueChanged_.invoke();
|
|
}
|
|
|
|
qreal Scrollbar::getMaximum() const
|
|
{
|
|
return this->maximum_;
|
|
}
|
|
|
|
qreal Scrollbar::getMinimum() const
|
|
{
|
|
return this->minimum_;
|
|
}
|
|
|
|
qreal Scrollbar::getLargeChange() const
|
|
{
|
|
return this->largeChange_;
|
|
}
|
|
|
|
qreal Scrollbar::getSmallChange() const
|
|
{
|
|
return this->smallChange_;
|
|
}
|
|
|
|
qreal Scrollbar::getDesiredValue() const
|
|
{
|
|
return this->desiredValue_ + this->smoothScrollingOffset_;
|
|
}
|
|
|
|
qreal Scrollbar::getCurrentValue() const
|
|
{
|
|
return this->currentValue_;
|
|
}
|
|
|
|
void Scrollbar::offset(qreal value)
|
|
{
|
|
if (this->currentValueAnimation_.state() == QPropertyAnimation::Running)
|
|
{
|
|
this->smoothScrollingOffset_ += value;
|
|
}
|
|
else
|
|
{
|
|
this->setDesiredValue(this->getDesiredValue() + value);
|
|
}
|
|
}
|
|
|
|
pajlada::Signals::NoArgSignal &Scrollbar::getCurrentValueChanged()
|
|
{
|
|
return this->currentValueChanged_;
|
|
}
|
|
|
|
pajlada::Signals::NoArgSignal &Scrollbar::getDesiredValueChanged()
|
|
{
|
|
return this->desiredValueChanged_;
|
|
}
|
|
|
|
void Scrollbar::setCurrentValue(qreal value)
|
|
{
|
|
value = std::max(this->minimum_,
|
|
std::min(this->maximum_ - this->largeChange_,
|
|
value + this->smoothScrollingOffset_));
|
|
|
|
if (std::abs(this->currentValue_ - value) > 0.0001)
|
|
{
|
|
this->currentValue_ = value;
|
|
|
|
this->updateScroll();
|
|
this->currentValueChanged_.invoke();
|
|
|
|
this->update();
|
|
}
|
|
}
|
|
|
|
void Scrollbar::printCurrentState(const QString &prefix) const
|
|
{
|
|
qDebug() << prefix //
|
|
<< "Current value: " << this->getCurrentValue() //
|
|
<< ". Maximum: " << this->getMaximum() //
|
|
<< ". Minimum: " << this->getMinimum() //
|
|
<< ". Large change: " << this->getLargeChange(); //
|
|
}
|
|
|
|
void Scrollbar::paintEvent(QPaintEvent *)
|
|
{
|
|
bool mouseOver = this->mouseOverIndex_ != -1;
|
|
int xOffset = mouseOver ? 0 : width() - int(4 * this->scale());
|
|
|
|
QPainter painter(this);
|
|
painter.fillRect(rect(), this->theme->scrollbars.background);
|
|
|
|
bool enableRedeemedHighlights = getSettings()->enableRedeemedHighlight;
|
|
|
|
// painter.fillRect(QRect(xOffset, 0, width(), this->buttonHeight),
|
|
// this->themeManager->ScrollbarArrow);
|
|
// painter.fillRect(QRect(xOffset, height() - this->buttonHeight,
|
|
// width(), this->buttonHeight),
|
|
// this->themeManager->ScrollbarArrow);
|
|
|
|
this->thumbRect_.setX(xOffset);
|
|
|
|
// mouse over thumb
|
|
if (this->mouseDownIndex_ == 2)
|
|
{
|
|
painter.fillRect(this->thumbRect_,
|
|
this->theme->scrollbars.thumbSelected);
|
|
}
|
|
// mouse not over thumb
|
|
else
|
|
{
|
|
painter.fillRect(this->thumbRect_, this->theme->scrollbars.thumb);
|
|
}
|
|
|
|
// draw highlights
|
|
auto snapshot = this->getHighlightSnapshot();
|
|
size_t snapshotLength = snapshot.size();
|
|
|
|
if (snapshotLength == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int w = this->width();
|
|
float y = 0;
|
|
float dY =
|
|
float(this->height()) / std::max<float>(1.0f, float(snapshotLength));
|
|
int highlightHeight =
|
|
int(std::ceil(std::max<float>(this->scale() * 2, dY)));
|
|
|
|
for (size_t i = 0; i < snapshotLength; i++)
|
|
{
|
|
ScrollbarHighlight const &highlight = snapshot[i];
|
|
|
|
if (!highlight.isNull())
|
|
{
|
|
if (!highlight.isRedeemedHighlight() || enableRedeemedHighlights)
|
|
{
|
|
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:;
|
|
}
|
|
}
|
|
}
|
|
|
|
y += dY;
|
|
}
|
|
}
|
|
|
|
void Scrollbar::resizeEvent(QResizeEvent *)
|
|
{
|
|
this->resize(int(16 * this->scale()), this->height());
|
|
}
|
|
|
|
void Scrollbar::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
if (this->mouseDownIndex_ == -1)
|
|
{
|
|
int y = event->pos().y();
|
|
|
|
auto oldIndex = this->mouseOverIndex_;
|
|
|
|
if (y < this->buttonHeight_)
|
|
{
|
|
this->mouseOverIndex_ = 0;
|
|
}
|
|
else if (y < this->thumbRect_.y())
|
|
{
|
|
this->mouseOverIndex_ = 1;
|
|
}
|
|
else if (this->thumbRect_.contains(2, y))
|
|
{
|
|
this->mouseOverIndex_ = 2;
|
|
}
|
|
else if (y < height() - this->buttonHeight_)
|
|
{
|
|
this->mouseOverIndex_ = 3;
|
|
}
|
|
else
|
|
{
|
|
this->mouseOverIndex_ = 4;
|
|
}
|
|
|
|
if (oldIndex != this->mouseOverIndex_)
|
|
{
|
|
update();
|
|
}
|
|
}
|
|
else if (this->mouseDownIndex_ == 2)
|
|
{
|
|
int delta = event->pos().y() - this->lastMousePosition_.y();
|
|
|
|
setDesiredValue(
|
|
this->desiredValue_ +
|
|
qreal(delta) /
|
|
std::max<qreal>(0.02, this->trackHeight_ * this->maximum_));
|
|
}
|
|
|
|
this->lastMousePosition_ = event->pos();
|
|
}
|
|
|
|
void Scrollbar::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
int y = event->pos().y();
|
|
|
|
if (y < this->buttonHeight_)
|
|
{
|
|
this->mouseDownIndex_ = 0;
|
|
}
|
|
else if (y < this->thumbRect_.y())
|
|
{
|
|
this->mouseDownIndex_ = 1;
|
|
}
|
|
else if (this->thumbRect_.contains(2, y))
|
|
{
|
|
this->mouseDownIndex_ = 2;
|
|
}
|
|
else if (y < height() - this->buttonHeight_)
|
|
{
|
|
this->mouseDownIndex_ = 3;
|
|
}
|
|
else
|
|
{
|
|
this->mouseDownIndex_ = 4;
|
|
}
|
|
}
|
|
|
|
void Scrollbar::mouseReleaseEvent(QMouseEvent *event)
|
|
{
|
|
int y = event->pos().y();
|
|
|
|
if (y < this->buttonHeight_)
|
|
{
|
|
if (this->mouseDownIndex_ == 0)
|
|
{
|
|
setDesiredValue(this->desiredValue_ - this->smallChange_, true);
|
|
}
|
|
}
|
|
else if (y < this->thumbRect_.y())
|
|
{
|
|
if (this->mouseDownIndex_ == 1)
|
|
{
|
|
setDesiredValue(this->desiredValue_ - this->smallChange_, true);
|
|
}
|
|
}
|
|
else if (this->thumbRect_.contains(2, y))
|
|
{
|
|
// do nothing
|
|
}
|
|
else if (y < height() - this->buttonHeight_)
|
|
{
|
|
if (this->mouseDownIndex_ == 3)
|
|
{
|
|
setDesiredValue(this->desiredValue_ + this->smallChange_, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this->mouseDownIndex_ == 4)
|
|
{
|
|
setDesiredValue(this->desiredValue_ + this->smallChange_, true);
|
|
}
|
|
}
|
|
|
|
this->mouseDownIndex_ = -1;
|
|
update();
|
|
}
|
|
|
|
void Scrollbar::leaveEvent(QEvent *)
|
|
{
|
|
this->mouseOverIndex_ = -1;
|
|
|
|
update();
|
|
}
|
|
|
|
void Scrollbar::updateScroll()
|
|
{
|
|
this->trackHeight_ = this->height() - this->buttonHeight_ -
|
|
this->buttonHeight_ - MIN_THUMB_HEIGHT - 1;
|
|
|
|
auto div = std::max<qreal>(0.01, this->maximum_ * this->trackHeight_);
|
|
|
|
this->thumbRect_ =
|
|
QRect(0, int(this->currentValue_ / div) + 1 + this->buttonHeight_,
|
|
this->width(), int(this->largeChange_ / div) + MIN_THUMB_HEIGHT);
|
|
|
|
this->update();
|
|
}
|
|
|
|
} // namespace chatterino
|