elastic_scroll.h 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #pragma once
  8. #include "ui/widgets/scroll_area.h" // For helpers, like ScrollToRequest.
  9. #include "ui/effects/animations.h"
  10. #include "ui/rp_widget.h"
  11. #include "base/object_ptr.h"
  12. #include "base/timer.h"
  13. namespace style {
  14. struct ScrollArea;
  15. } // namespace style
  16. namespace st {
  17. extern const style::ScrollArea &defaultScrollArea;
  18. } // namespace st
  19. namespace Ui {
  20. struct ScrollState {
  21. int visibleFrom = 0;
  22. int visibleTill = 0;
  23. int fullSize = 0;
  24. friend inline constexpr auto operator<=>(
  25. const ScrollState &,
  26. const ScrollState &) = default;
  27. friend inline constexpr bool operator==(
  28. const ScrollState &,
  29. const ScrollState &) = default;
  30. };
  31. class ElasticScrollBar final : public RpWidget {
  32. public:
  33. ElasticScrollBar(
  34. QWidget *parent,
  35. const style::ScrollArea &st,
  36. Qt::Orientation orientation = Qt::Vertical);
  37. void updateState(ScrollState state);
  38. void toggle(bool shown, anim::type animated = anim::type::normal);
  39. [[nodiscard]] rpl::producer<int> visibleFromDragged() const;
  40. private:
  41. void paintEvent(QPaintEvent *e) override;
  42. void mousePressEvent(QMouseEvent *e) override;
  43. void mouseMoveEvent(QMouseEvent *e) override;
  44. void mouseReleaseEvent(QMouseEvent *e) override;
  45. void enterEventHook(QEnterEvent *e) override;
  46. void leaveEventHook(QEvent *e) override;
  47. void resizeEvent(QResizeEvent *e) override;
  48. bool eventHook(QEvent *e) override;
  49. [[nodiscard]] int scaleToBar(int change) const;
  50. [[nodiscard]] bool barHighlighted() const;
  51. void toggleOver(bool over, anim::type animated = anim::type::normal);
  52. void toggleOverBar(bool over, anim::type animated = anim::type::normal);
  53. void toggleDragging(
  54. bool dragging,
  55. anim::type animated = anim::type::normal);
  56. void startBarHighlightAnimation(bool wasHighlighted);
  57. void refreshGeometry();
  58. const style::ScrollArea &_st;
  59. Ui::Animations::Simple _shownAnimation;
  60. Ui::Animations::Simple _overAnimation;
  61. Ui::Animations::Simple _barHighlightAnimation;
  62. base::Timer _hideTimer;
  63. rpl::event_stream<int> _visibleFromDragged;
  64. int _dragOverscrollAccumulated = 0;
  65. QRect _area;
  66. QRect _bar;
  67. QPoint _dragPosition;
  68. ScrollState _state;
  69. bool _shown : 1 = false;
  70. bool _over : 1 = false;
  71. bool _overBar : 1 = false;
  72. bool _vertical : 1 = false;
  73. bool _dragging : 1 = false;
  74. };
  75. struct ElasticScrollPosition {
  76. int value = 0;
  77. int overscroll = 0;
  78. friend inline auto operator<=>(
  79. ElasticScrollPosition,
  80. ElasticScrollPosition) = default;
  81. friend inline bool operator==(
  82. ElasticScrollPosition,
  83. ElasticScrollPosition) = default;
  84. };
  85. enum class ElasticScrollMovement {
  86. None,
  87. Progress,
  88. Momentum,
  89. Returning,
  90. };
  91. class ElasticScroll final : public RpWidget {
  92. public:
  93. ElasticScroll(
  94. QWidget *parent,
  95. const style::ScrollArea &st = st::defaultScrollArea,
  96. Qt::Orientation orientation = Qt::Vertical);
  97. ~ElasticScroll();
  98. void setHandleTouch(bool handle);
  99. bool viewportEvent(QEvent *e);
  100. void keyPressEvent(QKeyEvent *e) override;
  101. QWidget *viewport() const; // Dummy.
  102. int scrollWidth() const;
  103. int scrollHeight() const;
  104. int scrollLeftMax() const;
  105. int scrollTopMax() const;
  106. int scrollLeft() const;
  107. int scrollTop() const;
  108. template <typename Widget>
  109. QPointer<Widget> setOwnedWidget(object_ptr<Widget> widget) {
  110. auto result = QPointer<Widget>(widget);
  111. doSetOwnedWidget(std::move(widget));
  112. return result;
  113. }
  114. template <typename Widget>
  115. object_ptr<Widget> takeWidget() {
  116. return object_ptr<Widget>::fromRaw(
  117. static_cast<Widget*>(doTakeWidget().release()));
  118. }
  119. void updateBars();
  120. auto scrollTopValue() const {
  121. return _scrollTopUpdated.events_starting_with(scrollTop());
  122. }
  123. auto scrollTopChanges() const {
  124. return _scrollTopUpdated.events();
  125. }
  126. void scrollTo(ScrollToRequest request);
  127. void scrollToWidget(not_null<QWidget*> widget);
  128. void scrollToY(int toTop, int toBottom = -1);
  129. void scrollTo(int toFrom, int toTill = -1);
  130. void disableScroll(bool dis);
  131. void innerResized();
  132. void setCustomWheelProcess(Fn<bool(not_null<QWheelEvent*>)> process) {
  133. _customWheelProcess = std::move(process);
  134. }
  135. void setCustomTouchProcess(Fn<bool(not_null<QTouchEvent*>)> process) {
  136. _customTouchProcess = std::move(process);
  137. }
  138. enum class OverscrollType : uchar {
  139. None,
  140. Virtual,
  141. Real,
  142. };
  143. void setOverscrollTypes(OverscrollType from, OverscrollType till);
  144. void setOverscrollDefaults(int from, int till, bool shift = false);
  145. void setOverscrollBg(QColor bg);
  146. [[nodiscard]] rpl::producer<> scrolls() const;
  147. [[nodiscard]] rpl::producer<> innerResizes() const;
  148. [[nodiscard]] rpl::producer<> geometryChanged() const;
  149. using Position = ElasticScrollPosition;
  150. [[nodiscard]] Position position() const;
  151. [[nodiscard]] rpl::producer<Position> positionValue() const;
  152. using Movement = ElasticScrollMovement;
  153. [[nodiscard]] Movement movement() const;
  154. [[nodiscard]] rpl::producer<Movement> movementValue() const;
  155. [[nodiscard]] rpl::producer<bool> touchMaybePressing() const;
  156. private:
  157. bool eventHook(QEvent *e) override;
  158. bool eventFilter(QObject *obj, QEvent *e) override;
  159. void resizeEvent(QResizeEvent *e) override;
  160. void moveEvent(QMoveEvent *e) override;
  161. void wheelEvent(QWheelEvent *e) override;
  162. void paintEvent(QPaintEvent *e) override;
  163. void enterEventHook(QEnterEvent *e) override;
  164. void leaveEventHook(QEvent *e) override;
  165. bool handleWheelEvent(not_null<QWheelEvent*> e, bool touch = false);
  166. void handleTouchEvent(QTouchEvent *e);
  167. void updateState();
  168. void setState(ScrollState state);
  169. [[nodiscard]] int willScrollTo(int position) const;
  170. void tryScrollTo(int position, bool synthMouseMove = true);
  171. void applyScrollTo(int position, bool synthMouseMove = true);
  172. void applyOverscroll(int overscroll);
  173. void doSetOwnedWidget(object_ptr<QWidget> widget);
  174. object_ptr<QWidget> doTakeWidget();
  175. bool filterOutTouchEvent(QEvent *e);
  176. void touchScrollTimer();
  177. void touchScrollUpdated();
  178. void sendWheelEvent(Qt::ScrollPhase phase, QPoint delta = {});
  179. void touchResetSpeed();
  180. void touchUpdateSpeed();
  181. void touchDeaccelerate(int32 elapsed);
  182. struct AccumulatedParts {
  183. int base = 0;
  184. int relative = 0;
  185. };
  186. [[nodiscard]] AccumulatedParts computeAccumulatedParts() const;
  187. [[nodiscard]] int currentOverscrollDefault() const;
  188. [[nodiscard]] int currentOverscrollDefaultAccumulated() const;
  189. void overscrollReturn();
  190. void overscrollReturnCancel();
  191. void overscrollCheckReturnFinish();
  192. bool overscrollFinish();
  193. void applyAccumulatedScroll();
  194. const style::ScrollArea &_st;
  195. std::unique_ptr<ElasticScrollBar> _bar;
  196. ScrollState _state;
  197. base::Timer _touchTimer;
  198. base::Timer _touchScrollTimer;
  199. QPoint _touchStart;
  200. QPoint _touchPreviousPosition;
  201. QPoint _touchPosition;
  202. QPoint _touchSpeed;
  203. crl::time _touchSpeedTime = 0;
  204. crl::time _touchAccelerationTime = 0;
  205. crl::time _touchTime = 0;
  206. crl::time _lastScroll = 0;
  207. rpl::variable<bool> _touchMaybePressing;
  208. TouchScrollState _touchScrollState = TouchScrollState::Manual;
  209. int _overscrollAccumulated = 0;
  210. int _ignoreMomentumFromOverscroll = 0;
  211. bool _touchDisabled : 1 = false;
  212. bool _touchScroll : 1 = false;
  213. bool _touchPress : 1 = false;
  214. bool _touchRightButton : 1 = false;
  215. bool _touchPreviousPositionValid : 1 = false;
  216. bool _touchWaitingAcceleration : 1 = false;
  217. bool _vertical : 1 = false;
  218. bool _widgetAcceptsTouch : 1 = false;
  219. bool _disabled : 1 = false;
  220. bool _dirtyState : 1 = false;
  221. bool _overscrollReturning : 1 = false;
  222. Fn<bool(not_null<QWheelEvent*>)> _customWheelProcess;
  223. Fn<bool(not_null<QTouchEvent*>)> _customTouchProcess;
  224. int _overscroll = 0;
  225. int _overscrollDefaultFrom = 0;
  226. int _overscrollDefaultTill = 0;
  227. OverscrollType _overscrollTypeFrom = OverscrollType::Real;
  228. OverscrollType _overscrollTypeTill = OverscrollType::Real;
  229. std::optional<QColor> _overscrollBg;
  230. Ui::Animations::Simple _overscrollReturnAnimation;
  231. rpl::variable<Position> _position;
  232. rpl::variable<Movement> _movement;
  233. object_ptr<QWidget> _widget = { nullptr };
  234. rpl::event_stream<int> _scrollTopUpdated;
  235. rpl::event_stream<> _scrolls;
  236. rpl::event_stream<> _innerResizes;
  237. rpl::event_stream<> _geometryChanged;
  238. };
  239. [[nodiscard]] int OverscrollFromAccumulated(int accumulated);
  240. [[nodiscard]] int OverscrollToAccumulated(int overscroll);
  241. } // namespace Ui