// 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/ui_utility.h" #include "base/qt/qt_common_adapters.h" #include "base/debug_log.h" #include #include #include #include namespace Ui { // 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) { setVisible(false); Assert(_st != nullptr); Assert(_st->shColor.v() != nullptr); } 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) { int32 oldMax = _scrollMax, newMax = _connected->maximum(); _scrollMax = newMax; area()->rangeChanged(oldMax, newMax, _vertical); } if (_vertical) { int sh = area()->scrollHeight(), rh = height(), h = sh ? int32((rh * int64(area()->height())) / sh) : 0; if (h >= rh || !area()->scrollTopMax() || rh < _st->minHeight) { if (!isHidden()) hide(); bool newTopSh = (_st->topsh < 0), 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; int stm = area()->scrollTopMax(), y = stm ? int32(((rh - h) * int64(area()->scrollTop())) / stm) : 0; if (y > rh - h) y = rh - h; newBar = QRect(_st->deltax, y, width() - 2 * _st->deltax, h); } else { int sw = area()->scrollWidth(), rw = width(), w = sw ? int32((rw * int64(area()->width())) / sw) : 0; if (w >= rw || !area()->scrollLeftMax() || rw < _st->minHeight) { if (!isHidden()) hide(); return; } if (w <= _st->minHeight) w = _st->minHeight; int slm = area()->scrollLeftMax(), x = slm ? int32(((rw - w) * int64(area()->scrollLeft())) / slm) : 0; if (x > rw - w) x = rw - w; newBar = QRect(x, _st->deltax, w, height() - 2 * _st->deltax); } if (newBar != _bar) { _bar = newBar; update(); } if (_vertical) { bool newTopSh = (_st->topsh < 0) || (area()->scrollTop() > _st->topsh), 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(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(parentWidget())->viewportEvent(e); } auto ScrollBar::shadowVisibilityChanged() const -> rpl::producer { 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::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(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(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; _touchAccelerationTime = crl::now(); touchUpdateSpeed(); _touchStart = _touchPos; } else { _touchScroll = false; _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; 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; } } break; case QEvent::TouchCancel: { _touchPress = false; _touchScroll = 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 scTop = scrollTop(); const auto scMax = scrollTopMax(); const auto scNew = std::clamp(scTop - delta.y(), 0, scMax); if (scNew == scTop) { return false; } scrollToY(scNew); 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 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); } } void ScrollArea::scrollToY(int toTop, int toBottom) { if (const auto inner = widget()) { SendPendingMoveResizeEvents(inner); } SendPendingMoveResizeEvents(this); int toMin = 0, toMax = scrollTopMax(); if (toTop < toMin) { toTop = toMin; } else if (toTop > toMax) { toTop = toMax; } bool exact = (toBottom < 0); int curTop = scrollTop(), curHeight = height(), curBottom = curTop + curHeight, scToTop = toTop; if (!exact && toTop >= curTop) { if (toBottom < toTop) toBottom = toTop; if (toBottom <= curBottom) return; scToTop = toBottom - curHeight; if (scToTop > toTop) scToTop = toTop; if (scToTop == curTop) return; } else { scToTop = toTop; } verticalScrollBar()->setValue(scToTop); } void ScrollArea::doSetOwnedWidget(object_ptr 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 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(); } } // namespace Ui