feat: smooth scroll

This commit is contained in:
bleizix 2025-07-28 17:45:28 +05:00
parent 1642734548
commit e6b0c61d78
22 changed files with 262 additions and 0 deletions

View file

@ -6886,6 +6886,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"ayu_DisableAds" = "Disable Ads";
"ayu_DisableStories" = "Disable Stories";
"ayu_DisableCustomBackgrounds" = "Disable Custom Backgrounds";
"ayu_SmoothScroll" = "Smooth Scroll";
"ayu_DisableNotificationsDelay" = "Disable Notify Delay";
"ayu_LocalPremium" = "Local Telegram Premium";
"ayu_DisplayGhostStatus" = "Display Ghost Mode Status";

View file

@ -32,6 +32,7 @@ void initUiSettings() {
AyuUiSettings::setMonoFont(settings.monoFont);
AyuUiSettings::setWideMultiplier(settings.wideMultiplier);
AyuUiSettings::setSmoothScroll(settings.smoothScroll);
}
void initDatabase() {

View file

@ -18,6 +18,7 @@
#include <fstream>
#include "ayu_worker.h"
#include "ayu/ayu_ui_settings.h"
#include "window/window_controller.h"
using json = nlohmann::json;
@ -229,6 +230,7 @@ AyuGramSettings::AyuGramSettings() {
increaseWebviewHeight = false;
increaseWebviewWidth = false;
smoothScroll = true;
disableNotificationsDelay = false;
localPremium = false;
showChannelReactions = true;
@ -410,6 +412,10 @@ void set_increaseWebviewHeight(bool val) {
void set_increaseWebviewWidth(bool val) {
settings->increaseWebviewWidth = val;
}
void set_smoothScroll(bool val) {
settings->smoothScroll = val;
AyuUiSettings::setSmoothScroll(val);
}
void set_disableNotificationsDelay(bool val) {
settings->disableNotificationsDelay = val;

View file

@ -74,6 +74,7 @@ public:
bool increaseWebviewHeight;
bool increaseWebviewWidth;
bool smoothScroll;
bool disableNotificationsDelay;
bool localPremium;
bool showChannelReactions;
@ -158,6 +159,7 @@ void set_spoofWebviewAsAndroid(bool val);
void set_increaseWebviewHeight(bool val);
void set_increaseWebviewWidth(bool val);
void set_smoothScroll(bool val);
void set_disableNotificationsDelay(bool val);
void set_localPremium(bool val);
void set_hideChannelReactions(bool val);
@ -232,6 +234,7 @@ inline void to_json(nlohmann::json &nlohmann_json_j, const AyuGramSettings &nloh
NLOHMANN_JSON_TO(spoofWebviewAsAndroid)
NLOHMANN_JSON_TO(increaseWebviewHeight)
NLOHMANN_JSON_TO(increaseWebviewWidth)
NLOHMANN_JSON_TO(smoothScroll)
NLOHMANN_JSON_TO(disableNotificationsDelay)
NLOHMANN_JSON_TO(localPremium)
NLOHMANN_JSON_TO(appIcon)
@ -297,6 +300,7 @@ inline void from_json(const nlohmann::json &nlohmann_json_j, AyuGramSettings &nl
NLOHMANN_JSON_FROM_WITH_DEFAULT(spoofWebviewAsAndroid)
NLOHMANN_JSON_FROM_WITH_DEFAULT(increaseWebviewHeight)
NLOHMANN_JSON_FROM_WITH_DEFAULT(increaseWebviewWidth)
NLOHMANN_JSON_FROM_WITH_DEFAULT(smoothScroll)
NLOHMANN_JSON_FROM_WITH_DEFAULT(disableNotificationsDelay)
NLOHMANN_JSON_FROM_WITH_DEFAULT(localPremium)
NLOHMANN_JSON_FROM_WITH_DEFAULT(appIcon)

View file

@ -167,6 +167,11 @@ Widget::Widget(
_inner->scrollToSignal(
) | rpl::start_with_next([=](int top)
{
// AyuGram smooth scroll
_scroll->stopSmoothScroll();
// AyuGram smooth scroll
_scroll->scrollToY(top);
},
lifetime());

View file

@ -789,6 +789,27 @@ void SetupQoLToggles(not_null<Ui::VerticalLayout*> container) {
AddDivider(container);
AddSkip(container);
AddButtonWithIcon(
container,
tr::ayu_SmoothScroll(),
st::settingsButtonNoIcon
)->toggleOn(
rpl::single(settings->smoothScroll)
)->toggledValue(
) | rpl::filter(
[=](bool enabled)
{
return (enabled != settings->smoothScroll);
}) | start_with_next(
[=](bool enabled)
{
AyuSettings::set_smoothScroll(enabled);
AyuSettings::save();
},
container->lifetime());
AddButtonWithIcon(
container,
tr::ayu_DisableNotificationsDelay(),

View file

@ -33,6 +33,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtWidgets/QApplication>
// AyuGram includes
#include "ayu/smooth_scroll/smooth_scroll.h"
#include "ui/ui_utility.h"
namespace ChatHelpers {
namespace {
@ -948,6 +953,36 @@ void StickersListFooter::scrollByWheelEvent(
? e->pixelDelta().y()
: e->angleDelta().y());
const auto use = [&](ScrollState &state) {
// AyuGram smooth scroll
if (state.dragging) {
SmoothScroll::Scroller::stop(state.scrollAnimation, state.scrollTo);
return;
}
const auto scrollDelta = Ui::ScrollDelta(e);
const auto scroll = (std::abs(scrollDelta.y()) >= std::abs(scrollDelta.x()))
? scrollDelta.y()
: scrollDelta.x();
if (SmoothScroll::Scroller::handleScroll(
-scroll,
state.scrollAnimation,
state.scrollTo,
{
.getScroll = [&]{return qRound(state.x.current());},
.setScroll = [&](int v){
state.x = anim::value(v);
update();
},
.getNewTarget = [&](int t){return std::clamp(t, 0, state.max);}
},
anim::easeOutCubic,
st::slideWrapDuration
)) {
return;
}
// AyuGram smooth scroll
const auto now = qRound(state.x.current());
const auto used = now - delta;
const auto next = std::clamp(used, 0, state.max);

View file

@ -199,6 +199,11 @@ private:
anim::value selectionWidth;
crl::time animationStart = 0;
Ui::Animations::Basic animation;
// AyuGram smooth scroll
Ui::Animations::Simple scrollAnimation;
float64 scrollTo = -1.;
};
struct ExpandingContext {
QRect clip;

View file

@ -63,6 +63,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_menu_icons.h"
#include "styles/style_window.h"
// AyuGram includes
#include <QScrollBar>
namespace Dialogs {
namespace {
@ -1349,6 +1354,31 @@ void Suggestions::setupTabs() {
if (std::abs(pixelDelta.x()) + std::abs(angleDelta.x())) {
return false;
}
// AyuGram smooth scroll
const auto delta = Ui::ScrollDelta(e);
const auto bar = _tabsScroll->horizontalScrollBar();
if (SmoothScroll::Scroller::handleScroll(
-delta.y(),
_tabsWheelAnimation,
_tabsWheelScrollTo,
{
.getScroll = [bar]
{
return bar->value();
},
.setScroll = [bar](int v)
{
bar->setValue(v);
},
.getNewTarget = [bar](int t)
{
return std::clamp(t, bar->minimum(), bar->maximum());
}
})) {
return true;
}
// AyuGram smooth scroll
const auto y = pixelDelta.y() ? pixelDelta.y() : angleDelta.y();
_tabsScroll->scrollToX(_tabsScroll->scrollLeft() - y);
return true;

View file

@ -112,6 +112,10 @@ public:
class ObjectListController;
// AyuGram smooth scroll
Ui::Animations::Simple _tabsWheelAnimation;
float64 _tabsWheelScrollTo = -1.;
private:
using MediaType = Storage::SharedMediaType;
enum class Tab : uchar {

View file

@ -326,6 +326,11 @@ Widget::Widget(
}, lifetime());
_inner->scrollToSignal(
) | rpl::start_with_next([=](int top) {
// AyuGram smooth scroll
_scroll->stopSmoothScroll();
// AyuGram smooth scroll
_scroll->scrollToY(top);
}, lifetime());

View file

@ -7142,6 +7142,18 @@ void HistoryWidget::updateHistoryGeometry(
updateListSize();
_updateHistoryGeometryRequired = false;
// AyuGram smooth scroll
// workaround:
// stop smooth scroll to prevent the view from
// jumping to the top when older messages are loaded
// scroll position isn't correctly calculated on resize
_scroll->stopSmoothScroll();
// AyuGram smooth scroll
auto newScrollTop = 0;
if (initial) {
newScrollTop = countInitialScrollTop();

View file

@ -571,6 +571,7 @@ bool Item::listScrollTo(int top, bool syntetic) {
updateInnerVisibleArea();
return false;
}
_scroll->stopSmoothScroll();
_scroll->scrollToY(top);
return true;
}

View file

@ -2912,6 +2912,11 @@ bool ChatWidget::listScrollTo(int top, bool syntetic) {
const auto scrolled = (_scroll->scrollTop() != top);
_synteticScrollEvent = syntetic;
if (scrolled) {
// AyuGram smooth scroll
_scroll->stopSmoothScroll();
// AyuGram smooth scroll
_scroll->scrollToY(top);
} else if (syntetic) {
updateInnerVisibleArea();

View file

@ -26,6 +26,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat_helpers.h"
#include "styles/style_menu_icons.h"
// AyuGram includes
#include "ayu/smooth_scroll/smooth_scroll.h"
namespace HistoryView::Reactions {
namespace {
@ -124,6 +128,35 @@ bool Button::consumeWheelEvent(not_null<QWheelEvent*> e) {
if (horizontal) {
return false;
}
// AyuGram smooth scroll
const auto scrollDelta = Ui::ScrollDelta(e) * (expandUp() ? -1. : 1.);
if (SmoothScroll::Scroller::handleScroll(
-scrollDelta.y(),
_scrollAnimation,
_scrollTarget,
{
.getScroll = [this]
{
return int(base::SafeRound(_scroll));
},
.setScroll = [this](int value)
{
_scroll = value;
_update(_geometry);
},
.getNewTarget = [=](int target)
{
return std::clamp(target, 0, scrollMax);
}
},
anim::easeOutCubic,
kCollapseDuration)) {
e->accept();
return true;
}
// AyuGram smooth scroll
const auto between = st::reactionCornerSkip;
const auto oneHeight = (st::reactionCornerSize.height() + between);
const auto max = oneHeight * kMaxReactionsScrollAtOnce;
@ -209,6 +242,14 @@ void Button::updateGeometry(Fn<void(QRect)> update) {
)) - _collapsed.height();
if (!added && _state != State::Inside) {
_scroll = 0;
// AyuGram smooth scroll
if (_scrollAnimation.animating()) {
_scrollAnimation.stop();
}
_scrollTarget = -1.;
// AyuGram smooth scroll
}
const auto geometry = _collapsed.marginsAdded({
0,

View file

@ -131,6 +131,11 @@ private:
base::Timer _hideTimer;
std::optional<QPoint> _lastGlobalPosition;
// AyuGram smooth scroll
Ui::Animations::Simple _scrollAnimation;
float64 _scrollTarget = -1.;
};
class Manager final : public base::has_weak_ptr {

View file

@ -295,6 +295,10 @@ void ContentWidget::scrollTopRestore(int scrollTop) {
void ContentWidget::scrollTo(const Ui::ScrollToRequest &request) {
_scroll->scrollTo(request);
}
// AyuGram smooth scroll
void ContentWidget::stopSmoothScroll() {
_scroll->stopSmoothScroll();
}
bool ContentWidget::floatPlayerHandleWheelEvent(QEvent *e) {
return _scroll->viewportEvent(e);

View file

@ -169,6 +169,8 @@ protected:
void setViewport(rpl::producer<not_null<QEvent*>> &&events) const;
// AyuGram smooth scroll
void stopSmoothScroll();
private:
RpWidget *doSetInnerWidget(object_ptr<RpWidget> inner);
void updateControlsGeometry();

View file

@ -136,6 +136,10 @@ Widget::Widget(QWidget *parent, not_null<Controller*> controller)
_inner->setScrollHeightValue(scrollHeightValue());
_inner->scrollToRequests(
) | rpl::start_with_next([this](Ui::ScrollToRequest request) {
// AyuGram smooth scroll
stopSmoothScroll();
// AyuGram smooth scroll
scrollTo(request);
}, _inner->lifetime());
}

View file

@ -19,6 +19,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtWidgets/QApplication>
// AyuGram includes
#include "ayu/smooth_scroll/smooth_scroll.h"
namespace Ui {
namespace {
@ -45,6 +48,11 @@ public:
[[nodiscard]] rpl::producer<int> moveRequests() const;
// AyuGram smooth scroll
bool isDragging() const {
return _dragging;
}
private:
struct Button {
EmojiGroup group;
@ -375,6 +383,38 @@ void SearchWithGroups::initGroups() {
widget->moveRequests(
) | rpl::start_with_next([=](int delta) {
// AyuGram smooth scroll
if (widget->isDragging()) {
SmoothScroll::Scroller::stop(
_groupsScrollAnimation,
_groupsScrollTo);
moveGroupsBy(width(), delta);
return;
}
if (SmoothScroll::Scroller::handleScroll(
delta,
_groupsScrollAnimation,
_groupsScrollTo,
{
.getScroll = [=] {
return _groups->x();
},
.setScroll = [=](int value) {
moveGroupsTo(width(), value);
},
.getNewTarget = [=](int target) {
return clampGroupsLeft(width(), target);
}
},
anim::easeOutCubic,
st::slideWrapDuration
)) {
return;
}
// AyuGram smooth scroll
moveGroupsBy(width(), delta);
}, lifetime());

View file

@ -114,6 +114,10 @@ private:
base::Timer _debounceTimer;
bool _inited = false;
// AyuGram smooth scroll
Animations::Simple _groupsScrollAnimation;
float64 _groupsScrollTo = -1.;
};
class TabbedSearch final {

View file

@ -53,6 +53,11 @@ struct State final {
std::unique_ptr<Ui::ChatsFiltersTabsReorder> reorder;
bool ignoreRefresh = false;
// AyuGram smooth scroll
Animations::Simple scrollAnimation;
float64 scrollTo = -1.;
};
void ShowMenu(
@ -303,6 +308,28 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
if (std::abs(pixelDelta.x()) + std::abs(angleDelta.x())) {
return false;
}
// AyuGram smooth scroll
const auto delta = ScrollDelta(e);
if (SmoothScroll::Scroller::handleScroll(
-delta.y(),
state->scrollAnimation,
state->scrollTo,
{
.getScroll = [=] { return scroll->horizontalScrollBar()->value(); },
.setScroll = [=](int v) { scroll->horizontalScrollBar()->setValue(v); },
.getNewTarget = [bar = scroll->horizontalScrollBar()](int t)
{
return std::clamp(t, bar->minimum(), bar->maximum());
}
})
) {
return true;
}
// AyuGram smooth scroll
const auto bar = scroll->horizontalScrollBar();
const auto y = pixelDelta.y() ? pixelDelta.y() : angleDelta.y();
bar->setValue(bar->value() - y);