// 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 // #pragma once #include "ui/widgets/scroll_area.h" // For helpers, like ScrollToRequest. #include "ui/effects/animations.h" #include "ui/rp_widget.h" #include "base/object_ptr.h" #include "base/timer.h" namespace style { struct ScrollArea; } // namespace style namespace st { extern const style::ScrollArea &defaultScrollArea; } // namespace st namespace Ui { inline constexpr auto kPixelToAngleDelta = 2; struct ScrollState { int visibleFrom = 0; int visibleTill = 0; int fullSize = 0; friend inline constexpr auto operator<=>( const ScrollState &, const ScrollState &) = default; friend inline constexpr bool operator==( const ScrollState &, const ScrollState &) = default; }; class ElasticScrollBar final : public RpWidget { public: ElasticScrollBar( QWidget *parent, const style::ScrollArea &st, Qt::Orientation orientation = Qt::Vertical); void updateState(ScrollState state); void toggle(bool shown, anim::type animated = anim::type::normal); [[nodiscard]] rpl::producer visibleFromDragged() const; private: void paintEvent(QPaintEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void enterEventHook(QEnterEvent *e) override; void leaveEventHook(QEvent *e) override; void resizeEvent(QResizeEvent *e) override; bool eventHook(QEvent *e) override; [[nodiscard]] int scaleToBar(int change) const; [[nodiscard]] bool barHighlighted() const; void toggleOver(bool over, anim::type animated = anim::type::normal); void toggleOverBar(bool over, anim::type animated = anim::type::normal); void toggleDragging( bool dragging, anim::type animated = anim::type::normal); void startBarHighlightAnimation(bool wasHighlighted); void refreshGeometry(); const style::ScrollArea &_st; Ui::Animations::Simple _shownAnimation; Ui::Animations::Simple _overAnimation; Ui::Animations::Simple _barHighlightAnimation; base::Timer _hideTimer; rpl::event_stream _visibleFromDragged; int _dragOverscrollAccumulated = 0; QRect _area; QRect _bar; QPoint _dragPosition; ScrollState _state; bool _shown : 1 = false; bool _over : 1 = false; bool _overBar : 1 = false; bool _vertical : 1 = false; bool _dragging : 1 = false; }; struct ElasticScrollPosition { int value = 0; int overscroll = 0; friend inline auto operator<=>( ElasticScrollPosition, ElasticScrollPosition) = default; friend inline bool operator==( ElasticScrollPosition, ElasticScrollPosition) = default; }; enum class ElasticScrollMovement { None, Progress, Momentum, Returning, }; class ElasticScroll final : public RpWidget { public: ElasticScroll( QWidget *parent, const style::ScrollArea &st = st::defaultScrollArea, Qt::Orientation orientation = Qt::Vertical); ~ElasticScroll(); void setHandleTouch(bool handle); bool viewportEvent(QEvent *e); int scrollWidth() const; int scrollHeight() const; int scrollLeftMax() const; int scrollTopMax() const; int scrollLeft() const; int scrollTop() const; template QPointer setOwnedWidget(object_ptr widget) { auto result = QPointer(widget); doSetOwnedWidget(std::move(widget)); return result; } template object_ptr takeWidget() { return object_ptr::fromRaw( static_cast(doTakeWidget().release())); } void updateBars(); auto scrollTopValue() const { return _scrollTopUpdated.events_starting_with(scrollTop()); } auto scrollTopChanges() const { return _scrollTopUpdated.events(); } void scrollTo(ScrollToRequest request); void scrollToWidget(not_null widget); void scrollToY(int toTop, int toBottom = -1); void scrollTo(int toFrom, int toTill = -1); void disableScroll(bool dis); void innerResized(); void setCustomWheelProcess(Fn)> process) { _customWheelProcess = std::move(process); } void setCustomTouchProcess(Fn)> process) { _customTouchProcess = std::move(process); } enum class OverscrollType : uchar { None, Virtual, Real, }; void setOverscrollTypes(OverscrollType from, OverscrollType till); void setOverscrollDefaults(int from, int till, bool shift = false); void setOverscrollBg(QColor bg); [[nodiscard]] rpl::producer<> scrolls() const; [[nodiscard]] rpl::producer<> innerResizes() const; [[nodiscard]] rpl::producer<> geometryChanged() const; using Position = ElasticScrollPosition; [[nodiscard]] Position position() const; [[nodiscard]] rpl::producer positionValue() const; using Movement = ElasticScrollMovement; [[nodiscard]] Movement movement() const; [[nodiscard]] rpl::producer movementValue() const; private: bool eventHook(QEvent *e) override; bool eventFilter(QObject *obj, QEvent *e) override; void resizeEvent(QResizeEvent *e) override; void moveEvent(QMoveEvent *e) override; void wheelEvent(QWheelEvent *e) override; void paintEvent(QPaintEvent *e) override; void enterEventHook(QEnterEvent *e) override; void leaveEventHook(QEvent *e) override; void keyPressEvent(QKeyEvent *e) override; bool handleWheelEvent(not_null e, bool touch = false); void handleTouchEvent(QTouchEvent *e); void updateState(); void setState(ScrollState state); [[nodiscard]] int willScrollTo(int position) const; void tryScrollTo(int position, bool synthMouseMove = true); void applyScrollTo(int position, bool synthMouseMove = true); void applyOverscroll(int overscroll); void doSetOwnedWidget(object_ptr widget); object_ptr doTakeWidget(); bool filterOutTouchEvent(QEvent *e); void touchScrollTimer(); void touchScrollUpdated(); void sendWheelEvent(Qt::ScrollPhase phase, QPoint delta = {}); void touchResetSpeed(); void touchUpdateSpeed(); void touchDeaccelerate(int32 elapsed); struct AccumulatedParts { int base = 0; int relative = 0; }; [[nodiscard]] AccumulatedParts computeAccumulatedParts() const; [[nodiscard]] int currentOverscrollDefault() const; [[nodiscard]] int currentOverscrollDefaultAccumulated() const; void overscrollReturn(); void overscrollReturnCancel(); void overscrollCheckReturnFinish(); bool overscrollFinish(); void applyAccumulatedScroll(); const style::ScrollArea &_st; std::unique_ptr _bar; ScrollState _state; base::Timer _touchTimer; base::Timer _touchScrollTimer; QPoint _touchStart; QPoint _touchPreviousPosition; QPoint _touchPosition; QPoint _touchSpeed; crl::time _touchSpeedTime = 0; crl::time _touchAccelerationTime = 0; crl::time _touchTime = 0; crl::time _lastScroll = 0; TouchScrollState _touchScrollState = TouchScrollState::Manual; int _overscrollAccumulated = 0; int _ignoreMomentumFromOverscroll = 0; bool _touchDisabled : 1 = false; bool _touchScroll : 1 = false; bool _touchPress : 1 = false; bool _touchRightButton : 1 = false; bool _touchPreviousPositionValid : 1 = false; bool _touchWaitingAcceleration : 1 = false; bool _vertical : 1 = false; bool _widgetAcceptsTouch : 1 = false; bool _disabled : 1 = false; bool _dirtyState : 1 = false; bool _overscrollReturning : 1 = false; Fn)> _customWheelProcess; Fn)> _customTouchProcess; int _overscroll = 0; int _overscrollDefaultFrom = 0; int _overscrollDefaultTill = 0; OverscrollType _overscrollTypeFrom = OverscrollType::Real; OverscrollType _overscrollTypeTill = OverscrollType::Real; std::optional _overscrollBg; Ui::Animations::Simple _overscrollReturnAnimation; rpl::variable _position; rpl::variable _movement; object_ptr _widget = { nullptr }; rpl::event_stream _scrollTopUpdated; rpl::event_stream<> _scrolls; rpl::event_stream<> _innerResizes; rpl::event_stream<> _geometryChanged; }; [[nodiscard]] QPoint ScrollDelta(not_null e, bool touch = false); } // namespace Ui