| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911 |
- // This file is part of Desktop App Toolkit,
- // a set of libraries for developing nice desktop applications.
- //
- // For license and copyright information please follow this link:
- // https://github.com/desktop-app/legal/blob/master/LEGAL
- //
- #include "ui/widgets/scroll_area.h"
- #include "ui/painter.h"
- #include "ui/qt_weak_factory.h"
- #include "ui/ui_utility.h"
- #include "base/qt/qt_common_adapters.h"
- #include "base/debug_log.h"
- #include <QtWidgets/QScrollBar>
- #include <QtWidgets/QApplication>
- #include <QtGui/QGuiApplication>
- #include <QtGui/QWindow>
- namespace Ui {
- namespace {
- [[nodiscard]] int ComputeScrollTo(
- int toFrom,
- int toTill,
- int toMin,
- int toMax,
- int current,
- int size) {
- if (toFrom < toMin) {
- toFrom = toMin;
- } else if (toFrom > toMax) {
- toFrom = toMax;
- }
- const auto exact = (toTill < 0);
- const auto curBottom = current + size;
- auto scToFrom = toFrom;
- if (!exact && toFrom >= current) {
- if (toTill < toFrom) {
- toTill = toFrom;
- }
- if (toTill <= curBottom) {
- return current;
- }
- scToFrom = toTill - size;
- if (scToFrom > toFrom) {
- scToFrom = toFrom;
- }
- if (scToFrom == current) {
- return current;
- }
- } else {
- scToFrom = toFrom;
- }
- return scToFrom;
- }
- } // namespace
- // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
- ScrollShadow::ScrollShadow(ScrollArea *parent, const style::ScrollArea *st)
- : QWidget(parent)
- , _st(st) {
- Expects(_st != nullptr);
- Expects(_st->shColor.get() != nullptr);
- setVisible(false);
- }
- void ScrollShadow::paintEvent(QPaintEvent *e) {
- QPainter p(this);
- p.fillRect(rect(), _st->shColor);
- }
- void ScrollShadow::changeVisibility(bool shown) {
- setVisible(shown);
- }
- ScrollBar::ScrollBar(ScrollArea *parent, bool vert, const style::ScrollArea *st) : TWidget(parent)
- , _st(st)
- , _vertical(vert)
- , _hiding(_st->hiding != 0)
- , _connected(vert ? parent->verticalScrollBar() : parent->horizontalScrollBar())
- , _scrollMax(_connected->maximum())
- , _hideTimer([=] { hideTimer(); }) {
- recountSize();
- connect(_connected, &QAbstractSlider::valueChanged, [=] {
- area()->scrolled();
- updateBar();
- });
- connect(_connected, &QAbstractSlider::rangeChanged, [=] {
- area()->innerResized();
- updateBar();
- });
- updateBar();
- }
- void ScrollBar::recountSize() {
- setGeometry(_vertical
- ? QRect(
- style::RightToLeft() ? 0 : (area()->width() - _st->width),
- _st->deltat,
- _st->width,
- area()->height() - _st->deltat - _st->deltab)
- : QRect(
- _st->deltat,
- area()->height() - _st->width,
- area()->width() - _st->deltat - _st->deltab,
- _st->width));
- }
- void ScrollBar::updateBar(bool force) {
- QRect newBar;
- if (_connected->maximum() != _scrollMax) {
- const auto oldMax = _scrollMax;
- const auto newMax = _connected->maximum();
- _scrollMax = newMax;
- area()->rangeChanged(oldMax, newMax, _vertical);
- }
- if (_vertical) {
- const auto sh = area()->scrollHeight();
- const auto rh = height();
- auto h = sh ? int32((rh * int64(area()->height())) / sh) : 0;
- if (_st->barHidden
- || h >= rh
- || !area()->scrollTopMax()
- || rh < _st->minHeight) {
- if (!isHidden()) {
- hide();
- }
- const auto newTopSh = (_st->topsh < 0);
- const auto newBottomSh = (_st->bottomsh < 0);
- if (newTopSh != _topSh || force) {
- _shadowVisibilityChanged.fire({
- .type = ScrollShadow::Type::Top,
- .visible = (_topSh = newTopSh),
- });
- }
- if (newBottomSh != _bottomSh || force) {
- _shadowVisibilityChanged.fire({
- .type = ScrollShadow::Type::Bottom,
- .visible = (_bottomSh = newBottomSh),
- });
- }
- return;
- }
- if (h <= _st->minHeight) {
- h = _st->minHeight;
- }
- const auto stm = area()->scrollTopMax();
- const auto y = stm
- ? std::min(
- int32(((rh - h) * int64(area()->scrollTop())) / stm),
- rh - h)
- : 0;
- newBar = QRect(_st->deltax, y, width() - 2 * _st->deltax, h);
- } else {
- const auto sw = area()->scrollWidth();
- const auto rw = width();
- auto w = sw ? int32((rw * int64(area()->width())) / sw) : 0;
- if (_st->barHidden
- || w >= rw
- || !area()->scrollLeftMax()
- || rw < _st->minHeight) {
- if (!isHidden()) {
- hide();
- }
- return;
- }
- if (w <= _st->minHeight) {
- w = _st->minHeight;
- }
- const auto slm = area()->scrollLeftMax();
- const auto x = slm
- ? std::min(
- int32(((rw - w) * int64(area()->scrollLeft())) / slm),
- rw - w)
- : 0;
- newBar = QRect(x, _st->deltax, w, height() - 2 * _st->deltax);
- }
- if (newBar != _bar) {
- _bar = newBar;
- update();
- }
- if (_vertical) {
- const auto newTopSh = (_st->topsh < 0)
- || (area()->scrollTop() > _st->topsh);
- const auto newBottomSh = (_st->bottomsh < 0)
- || (area()->scrollTop()
- < area()->scrollTopMax() - _st->bottomsh);
- if (newTopSh != _topSh || force) {
- _shadowVisibilityChanged.fire({
- .type = ScrollShadow::Type::Top,
- .visible = (_topSh = newTopSh),
- });
- }
- if (newBottomSh != _bottomSh || force) {
- _shadowVisibilityChanged.fire({
- .type = ScrollShadow::Type::Bottom,
- .visible = (_bottomSh = newBottomSh),
- });
- }
- }
- if (isHidden()) show();
- }
- void ScrollBar::hideTimer() {
- if (!_hiding) {
- _hiding = true;
- _a_opacity.start([this] { update(); }, 1., 0., _st->duration);
- }
- }
- ScrollArea *ScrollBar::area() {
- return static_cast<ScrollArea*>(parentWidget());
- }
- void ScrollBar::setOver(bool over) {
- if (_over != over) {
- auto wasOver = (_over || _moving);
- _over = over;
- auto nowOver = (_over || _moving);
- if (wasOver != nowOver) {
- _a_over.start([this] { update(); }, nowOver ? 0. : 1., nowOver ? 1. : 0., _st->duration);
- }
- if (nowOver && _hiding) {
- _hiding = false;
- _a_opacity.start([this] { update(); }, 0., 1., _st->duration);
- }
- }
- }
- void ScrollBar::setOverBar(bool overbar) {
- if (_overbar != overbar) {
- auto wasBarOver = (_overbar || _moving);
- _overbar = overbar;
- auto nowBarOver = (_overbar || _moving);
- if (wasBarOver != nowBarOver) {
- _a_barOver.start([this] { update(); }, nowBarOver ? 0. : 1., nowBarOver ? 1. : 0., _st->duration);
- }
- }
- }
- void ScrollBar::setMoving(bool moving) {
- if (_moving != moving) {
- auto wasOver = (_over || _moving);
- auto wasBarOver = (_overbar || _moving);
- _moving = moving;
- auto nowBarOver = (_overbar || _moving);
- if (wasBarOver != nowBarOver) {
- _a_barOver.start([this] { update(); }, nowBarOver ? 0. : 1., nowBarOver ? 1. : 0., _st->duration);
- }
- auto nowOver = (_over || _moving);
- if (wasOver != nowOver) {
- _a_over.start([this] { update(); }, nowOver ? 0. : 1., nowOver ? 1. : 0., _st->duration);
- }
- if (!nowOver && _st->hiding && !_hiding) {
- _hideTimer.callOnce(_hideIn);
- }
- }
- }
- void ScrollBar::paintEvent(QPaintEvent *e) {
- if (!_bar.width() && !_bar.height()) {
- hide();
- return;
- }
- auto opacity = _a_opacity.value(_hiding ? 0. : 1.);
- if (opacity == 0.) return;
- QPainter p(this);
- auto deltal = _vertical ? _st->deltax : 0, deltar = _vertical ? _st->deltax : 0;
- auto deltat = _vertical ? 0 : _st->deltax, deltab = _vertical ? 0 : _st->deltax;
- p.setPen(Qt::NoPen);
- auto bg = anim::color(_st->bg, _st->bgOver, _a_over.value((_over || _moving) ? 1. : 0.));
- bg.setAlpha(anim::interpolate(0, bg.alpha(), opacity));
- auto bar = anim::color(_st->barBg, _st->barBgOver, _a_barOver.value((_overbar || _moving) ? 1. : 0.));
- bar.setAlpha(anim::interpolate(0, bar.alpha(), opacity));
- const auto outer = QRect(deltal, deltat, width() - deltal - deltar, height() - deltat - deltab);
- const auto radius = (_st->round < 0)
- ? (std::min(outer.width(), outer.height()) / 2.)
- : _st->round;
- if (radius) {
- PainterHighQualityEnabler hq(p);
- p.setBrush(bg);
- p.drawRoundedRect(outer, radius, radius);
- p.setBrush(bar);
- p.drawRoundedRect(_bar, radius, radius);
- } else {
- p.fillRect(outer, bg);
- p.fillRect(_bar, bar);
- }
- }
- void ScrollBar::hideTimeout(crl::time dt) {
- if (_hiding && dt > 0) {
- _hiding = false;
- _a_opacity.start([this] { update(); }, 0., 1., _st->duration);
- }
- _hideIn = dt;
- if (!_moving) {
- _hideTimer.callOnce(_hideIn);
- }
- }
- void ScrollBar::enterEventHook(QEnterEvent *e) {
- _hideTimer.cancel();
- setMouseTracking(true);
- setOver(true);
- }
- void ScrollBar::leaveEventHook(QEvent *e) {
- if (!_moving) {
- setMouseTracking(false);
- }
- setOver(false);
- setOverBar(false);
- if (_st->hiding && !_hiding) {
- _hideTimer.callOnce(_hideIn);
- }
- }
- void ScrollBar::mouseMoveEvent(QMouseEvent *e) {
- setOverBar(_bar.contains(e->pos()));
- if (_moving) {
- int delta = 0, barDelta = _vertical ? (area()->height() - _bar.height()) : (area()->width() - _bar.width());
- if (barDelta > 0) {
- QPoint d = (e->globalPos() - _dragStart);
- delta = int32((_vertical ? (d.y() * int64(area()->scrollTopMax())) : (d.x() * int64(area()->scrollLeftMax()))) / barDelta);
- }
- _connected->setValue(_startFrom + delta);
- }
- }
- void ScrollBar::mousePressEvent(QMouseEvent *e) {
- if (!width() || !height()) return;
- _dragStart = e->globalPos();
- area()->setMovingByScrollBar(true);
- setMoving(true);
- if (_overbar) {
- _startFrom = _connected->value();
- } else {
- int32 val = _vertical ? e->pos().y() : e->pos().x(), div = _vertical ? height() : width();
- val = (val <= _st->deltat) ? 0 : (val - _st->deltat);
- div = (div <= _st->deltat + _st->deltab) ? 1 : (div - _st->deltat - _st->deltab);
- _startFrom = _vertical ? int32((val * int64(area()->scrollTopMax())) / div) : ((val * int64(area()->scrollLeftMax())) / div);
- _connected->setValue(_startFrom);
- setOverBar(true);
- }
- }
- void ScrollBar::mouseReleaseEvent(QMouseEvent *e) {
- if (_moving) {
- area()->setMovingByScrollBar(false);
- setMoving(false);
- }
- if (!_over) {
- setMouseTracking(false);
- }
- }
- void ScrollBar::resizeEvent(QResizeEvent *e) {
- updateBar();
- }
- void ScrollBar::wheelEvent(QWheelEvent *e) {
- static_cast<ScrollArea*>(parentWidget())->viewportEvent(e);
- }
- auto ScrollBar::shadowVisibilityChanged() const
- -> rpl::producer<ScrollBar::ShadowVisibility> {
- return _shadowVisibilityChanged.events();
- }
- ScrollArea::ScrollArea(
- QWidget *parent,
- const style::ScrollArea &st,
- bool handleTouch)
- : Parent(parent)
- , _st(st)
- , _horizontalBar(this, false, &_st)
- , _verticalBar(this, true, &_st)
- , _topShadow(this, &_st)
- , _bottomShadow(this, &_st)
- , _touchEnabled(handleTouch) {
- setLayoutDirection(style::LayoutDirection());
- setFocusPolicy(Qt::NoFocus);
- _verticalBar->shadowVisibilityChanged(
- ) | rpl::start_with_next([=](const ScrollBar::ShadowVisibility &data) {
- ((data.type == ScrollShadow::Type::Top)
- ? _topShadow
- : _bottomShadow)->changeVisibility(data.visible);
- }, lifetime());
- _verticalBar->updateBar(true);
- verticalScrollBar()->setSingleStep(style::ConvertScale(verticalScrollBar()->singleStep()));
- horizontalScrollBar()->setSingleStep(style::ConvertScale(horizontalScrollBar()->singleStep()));
- setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- setFrameStyle(int(QFrame::NoFrame) | QFrame::Plain);
- viewport()->setAutoFillBackground(false);
- _horizontalValue = horizontalScrollBar()->value();
- _verticalValue = verticalScrollBar()->value();
- if (_touchEnabled) {
- viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
- _touchTimer.setCallback([=] { _touchRightButton = true; });
- _touchScrollTimer.setCallback([=] { touchScrollTimer(); });
- }
- }
- void ScrollArea::touchDeaccelerate(int32 elapsed) {
- int32 x = _touchSpeed.x();
- int32 y = _touchSpeed.y();
- _touchSpeed.setX((x == 0) ? x : (x > 0) ? qMax(0, x - elapsed) : qMin(0, x + elapsed));
- _touchSpeed.setY((y == 0) ? y : (y > 0) ? qMax(0, y - elapsed) : qMin(0, y + elapsed));
- }
- void ScrollArea::scrolled() {
- if (const auto inner = widget()) {
- SendPendingMoveResizeEvents(inner);
- }
- bool em = false;
- int horizontalValue = horizontalScrollBar()->value();
- int verticalValue = verticalScrollBar()->value();
- if (_horizontalValue != horizontalValue) {
- if (_disabled) {
- horizontalScrollBar()->setValue(_horizontalValue);
- } else {
- _horizontalValue = horizontalValue;
- if (_st.hiding) {
- _horizontalBar->hideTimeout(_st.hiding);
- }
- em = true;
- }
- }
- if (_verticalValue != verticalValue) {
- if (_disabled) {
- verticalScrollBar()->setValue(_verticalValue);
- } else {
- _verticalValue = verticalValue;
- if (_st.hiding) {
- _verticalBar->hideTimeout(_st.hiding);
- }
- em = true;
- _scrollTopUpdated.fire_copy(_verticalValue);
- }
- }
- if (em) {
- _scrolls.fire({});
- if (!_movingByScrollBar) {
- SendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton);
- }
- }
- }
- void ScrollArea::innerResized() {
- _innerResizes.fire({});
- }
- int ScrollArea::scrollWidth() const {
- QWidget *w(widget());
- return w ? qMax(w->width(), width()) : width();
- }
- int ScrollArea::scrollHeight() const {
- QWidget *w(widget());
- return w ? qMax(w->height(), height()) : height();
- }
- int ScrollArea::scrollLeftMax() const {
- return scrollWidth() - width();
- }
- int ScrollArea::scrollTopMax() const {
- return scrollHeight() - height();
- }
- int ScrollArea::scrollLeft() const {
- return _horizontalValue;
- }
- int ScrollArea::scrollTop() const {
- return _verticalValue;
- }
- void ScrollArea::touchScrollTimer() {
- auto nowTime = crl::now();
- if (_touchScrollState == TouchScrollState::Acceleration && _touchWaitingAcceleration && (nowTime - _touchAccelerationTime) > 40) {
- _touchScrollState = TouchScrollState::Manual;
- touchResetSpeed();
- } else if (_touchScrollState == TouchScrollState::Auto || _touchScrollState == TouchScrollState::Acceleration) {
- int32 elapsed = int32(nowTime - _touchTime);
- QPoint delta = _touchSpeed * elapsed / 1000;
- bool hasScrolled = touchScroll(delta);
- if (_touchSpeed.isNull() || !hasScrolled) {
- _touchScrollState = TouchScrollState::Manual;
- _touchScroll = false;
- _touchScrollTimer.cancel();
- } else {
- _touchTime = nowTime;
- }
- touchDeaccelerate(elapsed);
- }
- }
- void ScrollArea::touchUpdateSpeed() {
- const auto nowTime = crl::now();
- if (_touchPrevPosValid) {
- const int elapsed = nowTime - _touchSpeedTime;
- if (elapsed) {
- const QPoint newPixelDiff = (_touchPos - _touchPrevPos);
- const QPoint pixelsPerSecond = newPixelDiff * (1000 / elapsed);
- // fingers are inacurates, we ignore small changes to avoid stopping the autoscroll because
- // of a small horizontal offset when scrolling vertically
- const int newSpeedY = (qAbs(pixelsPerSecond.y()) > kFingerAccuracyThreshold) ? pixelsPerSecond.y() : 0;
- const int newSpeedX = (qAbs(pixelsPerSecond.x()) > kFingerAccuracyThreshold) ? pixelsPerSecond.x() : 0;
- if (_touchScrollState == TouchScrollState::Auto) {
- const int oldSpeedY = _touchSpeed.y();
- const int oldSpeedX = _touchSpeed.x();
- if ((oldSpeedY <= 0 && newSpeedY <= 0) || ((oldSpeedY >= 0 && newSpeedY >= 0)
- && (oldSpeedX <= 0 && newSpeedX <= 0)) || (oldSpeedX >= 0 && newSpeedX >= 0)) {
- _touchSpeed.setY(std::clamp((oldSpeedY + (newSpeedY / 4)), -kMaxScrollAccelerated, +kMaxScrollAccelerated));
- _touchSpeed.setX(std::clamp((oldSpeedX + (newSpeedX / 4)), -kMaxScrollAccelerated, +kMaxScrollAccelerated));
- } else {
- _touchSpeed = QPoint();
- }
- } else {
- // we average the speed to avoid strange effects with the last delta
- if (!_touchSpeed.isNull()) {
- _touchSpeed.setX(std::clamp((_touchSpeed.x() / 4) + (newSpeedX * 3 / 4), -kMaxScrollFlick, +kMaxScrollFlick));
- _touchSpeed.setY(std::clamp((_touchSpeed.y() / 4) + (newSpeedY * 3 / 4), -kMaxScrollFlick, +kMaxScrollFlick));
- } else {
- _touchSpeed = QPoint(newSpeedX, newSpeedY);
- }
- }
- }
- } else {
- _touchPrevPosValid = true;
- }
- _touchSpeedTime = nowTime;
- _touchPrevPos = _touchPos;
- }
- void ScrollArea::touchResetSpeed() {
- _touchSpeed = QPoint();
- _touchPrevPosValid = false;
- }
- bool ScrollArea::eventHook(QEvent *e) {
- const auto was = (e->type() == QEvent::LayoutRequest)
- ? verticalScrollBar()->minimum()
- : 0;
- const auto result = RpWidgetBase<QScrollArea>::eventHook(e);
- if (was) {
- // Because LayoutRequest resets custom-set minimum allowed value.
- verticalScrollBar()->setMinimum(was);
- }
- return result;
- }
- bool ScrollArea::eventFilter(QObject *obj, QEvent *e) {
- const auto result = QScrollArea::eventFilter(obj, e);
- return (obj == widget() && filterOutTouchEvent(e)) || result;
- }
- bool ScrollArea::viewportEvent(QEvent *e) {
- if (filterOutTouchEvent(e)) {
- return true;
- } else if (e->type() == QEvent::Wheel) {
- if (_customWheelProcess
- && _customWheelProcess(static_cast<QWheelEvent*>(e))) {
- return true;
- }
- }
- return QScrollArea::viewportEvent(e);
- }
- bool ScrollArea::filterOutTouchEvent(QEvent *e) {
- const auto type = e->type();
- if (type == QEvent::TouchBegin
- || type == QEvent::TouchUpdate
- || type == QEvent::TouchEnd
- || type == QEvent::TouchCancel) {
- const auto ev = static_cast<QTouchEvent*>(e);
- if (ev->device()->type() == base::TouchDevice::TouchScreen) {
- if (_customTouchProcess && _customTouchProcess(ev)) {
- return true;
- } else if (_touchEnabled) {
- touchEvent(ev);
- return true;
- }
- }
- }
- return false;
- }
- void ScrollArea::touchEvent(QTouchEvent *e) {
- if (!e->touchPoints().isEmpty()) {
- _touchPrevPos = _touchPos;
- _touchPos = e->touchPoints().cbegin()->screenPos().toPoint();
- }
- switch (e->type()) {
- case QEvent::TouchBegin: {
- if (_touchPress || e->touchPoints().isEmpty()) return;
- _touchPress = true;
- if (_touchScrollState == TouchScrollState::Auto) {
- _touchScrollState = TouchScrollState::Acceleration;
- _touchWaitingAcceleration = true;
- _touchMaybePressing = false;
- _touchAccelerationTime = crl::now();
- touchUpdateSpeed();
- _touchStart = _touchPos;
- } else {
- _touchScroll = false;
- _touchMaybePressing = true;
- _touchTimer.callOnce(QApplication::startDragTime());
- }
- _touchStart = _touchPrevPos = _touchPos;
- _touchRightButton = false;
- } break;
- case QEvent::TouchUpdate: {
- if (!_touchPress) return;
- if (!_touchScroll && (_touchPos - _touchStart).manhattanLength() >= QApplication::startDragDistance()) {
- _touchTimer.cancel();
- _touchScroll = true;
- _touchMaybePressing = false;
- touchUpdateSpeed();
- }
- if (_touchScroll) {
- if (_touchScrollState == TouchScrollState::Manual) {
- touchScrollUpdated(_touchPos);
- } else if (_touchScrollState == TouchScrollState::Acceleration) {
- touchUpdateSpeed();
- _touchAccelerationTime = crl::now();
- if (_touchSpeed.isNull()) {
- _touchScrollState = TouchScrollState::Manual;
- }
- }
- }
- } break;
- case QEvent::TouchEnd: {
- if (!_touchPress) return;
- _touchPress = false;
- auto weak = MakeWeak(this);
- if (_touchScroll) {
- if (_touchScrollState == TouchScrollState::Manual) {
- _touchScrollState = TouchScrollState::Auto;
- _touchPrevPosValid = false;
- _touchScrollTimer.callEach(15);
- _touchTime = crl::now();
- } else if (_touchScrollState == TouchScrollState::Auto) {
- _touchScrollState = TouchScrollState::Manual;
- _touchScroll = false;
- touchResetSpeed();
- } else if (_touchScrollState == TouchScrollState::Acceleration) {
- _touchScrollState = TouchScrollState::Auto;
- _touchWaitingAcceleration = false;
- _touchPrevPosValid = false;
- }
- } else if (window()) { // one short tap -- like left mouse click, one long tap -- like right mouse click
- Qt::MouseButton btn(_touchRightButton ? Qt::RightButton : Qt::LeftButton);
- if (weak) SendSynteticMouseEvent(this, QEvent::MouseMove, Qt::NoButton, _touchStart);
- if (weak) SendSynteticMouseEvent(this, QEvent::MouseButtonPress, btn, _touchStart);
- if (weak) SendSynteticMouseEvent(this, QEvent::MouseButtonRelease, btn, _touchStart);
- if (weak && _touchRightButton) {
- auto windowHandle = window()->windowHandle();
- auto localPoint = windowHandle->mapFromGlobal(_touchStart);
- QContextMenuEvent ev(QContextMenuEvent::Mouse, localPoint, _touchStart, QGuiApplication::keyboardModifiers());
- ev.setTimestamp(crl::now());
- QGuiApplication::sendEvent(windowHandle, &ev);
- }
- }
- if (weak) {
- _touchTimer.cancel();
- _touchRightButton = false;
- _touchMaybePressing = false;
- }
- } break;
- case QEvent::TouchCancel: {
- _touchPress = false;
- _touchScroll = false;
- _touchMaybePressing = false;
- _touchScrollState = TouchScrollState::Manual;
- _touchTimer.cancel();
- } break;
- }
- }
- void ScrollArea::touchScrollUpdated(const QPoint &screenPos) {
- _touchPos = screenPos;
- touchScroll(_touchPos - _touchPrevPos);
- touchUpdateSpeed();
- }
- void ScrollArea::disableScroll(bool dis) {
- _disabled = dis;
- if (_disabled && _st.hiding) {
- _horizontalBar->hideTimeout(0);
- _verticalBar->hideTimeout(0);
- }
- }
- void ScrollArea::scrollContentsBy(int dx, int dy) {
- if (_disabled) {
- return;
- }
- QScrollArea::scrollContentsBy(dx, dy);
- }
- bool ScrollArea::touchScroll(const QPoint &delta) {
- const auto top = scrollTop();
- const auto topMax = scrollTopMax();
- const auto left = scrollLeft();
- const auto leftMax = scrollLeftMax();
- const auto xAbs = qAbs(delta.x());
- const auto yAbs = qAbs(delta.y());
- const auto direction = (leftMax <= 0 || yAbs > xAbs)
- ? Qt::Vertical
- : Qt::Horizontal;
- const auto was = (direction == Qt::Vertical) ? top : left;
- const auto now = (direction == Qt::Vertical)
- ? std::clamp(top - delta.y(), 0, topMax)
- : std::clamp(left - delta.x(), 0, leftMax);
- if (now == was) {
- return false;
- } else if (direction == Qt::Vertical) {
- scrollToY(now);
- } else {
- horizontalScrollBar()->setValue(now);
- }
- return true;
- }
- void ScrollArea::resizeEvent(QResizeEvent *e) {
- QScrollArea::resizeEvent(e);
- _horizontalBar->recountSize();
- _verticalBar->recountSize();
- _topShadow->setGeometry(QRect(0, 0, width(), qAbs(_st.topsh)));
- _bottomShadow->setGeometry(QRect(0, height() - qAbs(_st.bottomsh), width(), qAbs(_st.bottomsh)));
- _geometryChanged.fire({});
- }
- void ScrollArea::moveEvent(QMoveEvent *e) {
- QScrollArea::moveEvent(e);
- _geometryChanged.fire({});
- }
- void ScrollArea::keyPressEvent(QKeyEvent *e) {
- if ((e->key() == Qt::Key_Up || e->key() == Qt::Key_Down)
- && (e->modifiers().testFlag(Qt::AltModifier)
- || e->modifiers().testFlag(Qt::ControlModifier))) {
- e->ignore();
- } else if(e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) {
- ((QObject*)widget())->event(e);
- } else {
- QScrollArea::keyPressEvent(e);
- }
- }
- void ScrollArea::enterEventHook(QEnterEvent *e) {
- if (_disabled) return;
- if (_st.hiding) {
- _horizontalBar->hideTimeout(_st.hiding);
- _verticalBar->hideTimeout(_st.hiding);
- }
- return QScrollArea::enterEvent(e);
- }
- void ScrollArea::leaveEventHook(QEvent *e) {
- if (_st.hiding) {
- _horizontalBar->hideTimeout(0);
- _verticalBar->hideTimeout(0);
- }
- return QScrollArea::leaveEvent(e);
- }
- void ScrollArea::scrollTo(ScrollToRequest request) {
- scrollToY(request.ymin, request.ymax);
- }
- void ScrollArea::scrollToWidget(not_null<QWidget*> widget) {
- if (auto local = this->widget()) {
- auto globalPosition = widget->mapToGlobal(QPoint(0, 0));
- auto localPosition = local->mapFromGlobal(globalPosition);
- auto localTop = localPosition.y();
- auto localBottom = localTop + widget->height();
- scrollToY(localTop, localBottom);
- }
- }
- int ScrollArea::computeScrollToX(int toLeft, int toRight) {
- if (const auto inner = widget()) {
- SendPendingMoveResizeEvents(inner);
- }
- SendPendingMoveResizeEvents(this);
- return ComputeScrollTo(
- toLeft,
- toRight,
- 0,
- scrollLeftMax(),
- scrollLeft(),
- width());
- }
- int ScrollArea::computeScrollToY(int toTop, int toBottom) {
- if (const auto inner = widget()) {
- SendPendingMoveResizeEvents(inner);
- }
- SendPendingMoveResizeEvents(this);
- return ComputeScrollTo(
- toTop,
- toBottom,
- 0,
- scrollTopMax(),
- scrollTop(),
- height());
- }
- void ScrollArea::scrollToX(int toLeft, int toRight) {
- horizontalScrollBar()->setValue(computeScrollToX(toLeft, toRight));
- }
- void ScrollArea::scrollToY(int toTop, int toBottom) {
- verticalScrollBar()->setValue(computeScrollToY(toTop, toBottom));
- }
- void ScrollArea::doSetOwnedWidget(object_ptr<QWidget> w) {
- if (widget() && _touchEnabled) {
- widget()->removeEventFilter(this);
- if (!_widgetAcceptsTouch) widget()->setAttribute(Qt::WA_AcceptTouchEvents, false);
- }
- _widget = std::move(w);
- QScrollArea::setWidget(_widget);
- if (_widget) {
- _widget->setAutoFillBackground(false);
- if (_touchEnabled) {
- _widget->installEventFilter(this);
- _widgetAcceptsTouch = _widget->testAttribute(Qt::WA_AcceptTouchEvents);
- _widget->setAttribute(Qt::WA_AcceptTouchEvents);
- }
- }
- }
- object_ptr<QWidget> ScrollArea::doTakeWidget() {
- QScrollArea::takeWidget();
- return std::move(_widget);
- }
- void ScrollArea::rangeChanged(int oldMax, int newMax, bool vertical) {
- }
- void ScrollArea::updateBars() {
- _horizontalBar->updateBar(true);
- _verticalBar->updateBar(true);
- }
- bool ScrollArea::focusNextPrevChild(bool next) {
- if (QWidget::focusNextPrevChild(next)) {
- // if (QWidget *fw = focusWidget())
- // ensureWidgetVisible(fw);
- return true;
- }
- return false;
- }
- void ScrollArea::setMovingByScrollBar(bool movingByScrollBar) {
- _movingByScrollBar = movingByScrollBar;
- }
- rpl::producer<> ScrollArea::scrolls() const {
- return _scrolls.events();
- }
- rpl::producer<> ScrollArea::innerResizes() const {
- return _innerResizes.events();
- }
- rpl::producer<> ScrollArea::geometryChanged() const {
- return _geometryChanged.events();
- }
- rpl::producer<bool> ScrollArea::touchMaybePressing() const {
- return _touchMaybePressing.value();
- }
- } // namespace Ui
|