280 lines
8 KiB
C++
280 lines
8 KiB
C++
// 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 {
|
|
|
|
constexpr auto kPixelToAngleDelta = 10;
|
|
|
|
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<int> 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<int> _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);
|
|
|
|
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 <typename Widget>
|
|
QPointer<Widget> setOwnedWidget(object_ptr<Widget> widget) {
|
|
auto result = QPointer<Widget>(widget);
|
|
doSetOwnedWidget(std::move(widget));
|
|
return result;
|
|
}
|
|
template <typename Widget>
|
|
object_ptr<Widget> takeWidget() {
|
|
return object_ptr<Widget>::fromRaw(
|
|
static_cast<Widget*>(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<QWidget*> widget);
|
|
void scrollToY(int toTop, int toBottom = -1);
|
|
void scrollTo(int toFrom, int toTill = -1);
|
|
void disableScroll(bool dis);
|
|
void innerResized();
|
|
|
|
void setCustomWheelProcess(Fn<bool(not_null<QWheelEvent*>)> process) {
|
|
_customWheelProcess = std::move(process);
|
|
}
|
|
void setCustomTouchProcess(Fn<bool(not_null<QTouchEvent*>)> 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<Position> positionValue() const;
|
|
|
|
using Movement = ElasticScrollMovement;
|
|
[[nodiscard]] Movement movement() const;
|
|
[[nodiscard]] rpl::producer<Movement> 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<QWheelEvent*> 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<QWidget> widget);
|
|
object_ptr<QWidget> 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<ElasticScrollBar> _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<bool(not_null<QWheelEvent*>)> _customWheelProcess;
|
|
Fn<bool(not_null<QTouchEvent*>)> _customTouchProcess;
|
|
int _overscroll = 0;
|
|
int _overscrollDefaultFrom = 0;
|
|
int _overscrollDefaultTill = 0;
|
|
OverscrollType _overscrollTypeFrom = OverscrollType::Real;
|
|
OverscrollType _overscrollTypeTill = OverscrollType::Real;
|
|
std::optional<QColor> _overscrollBg;
|
|
Ui::Animations::Simple _overscrollReturnAnimation;
|
|
rpl::variable<Position> _position;
|
|
rpl::variable<Movement> _movement;
|
|
|
|
object_ptr<QWidget> _widget = { nullptr };
|
|
|
|
rpl::event_stream<int> _scrollTopUpdated;
|
|
rpl::event_stream<> _scrolls;
|
|
rpl::event_stream<> _innerResizes;
|
|
rpl::event_stream<> _geometryChanged;
|
|
|
|
};
|
|
|
|
[[nodiscard]] QPoint ScrollDelta(not_null<QWheelEvent*> e);
|
|
|
|
} // namespace Ui
|