Added slide animation for chats filter switching.

This commit is contained in:
23rd
2026-09-10 12:24:07 +03:00
committed by John Preston
parent 5d431ead94
commit f5f460da36
5 changed files with 111 additions and 13 deletions

View File

@@ -212,6 +212,7 @@ dialogsSpeakingDenominator: 8.;
dialogsImportantBarHeight: 37px;
dialogsWidthDuration: universalDuration;
dialogsFilterSwipeSlideDuration: 140;
dialogsTextWidthMin: 150px;
dialogsTextPalette: TextPalette(defaultTextPalette) {

View File

@@ -579,11 +579,6 @@ InnerWidget::InnerWidget(
update(next);
}, lifetime());
_controller->activeChatsFilter(
) | rpl::on_next([=](FilterId filterId) {
switchToFilter(filterId);
}, lifetime());
_controller->window().widget()->globalForceClicks(
) | rpl::on_next([=](QPoint globalPosition) {
processGlobalForceClick(globalPosition);
@@ -5596,8 +5591,8 @@ void InnerWidget::switchToFilter(FilterId filterId) {
const auto skip = found
// Don't save a scroll state for very flexible chat filters.
&& (filterIt->flags() & (Data::ChatFilter::Flag::NoRead));
if (!skip) {
restoreChatsFilterScrollState(filterId);
if (skip || !restoreChatsFilterScrollState(filterId)) {
jumpToTop();
}
}
}
@@ -5610,11 +5605,13 @@ void InnerWidget::saveChatsFilterScrollState(FilterId filterId) {
_chatsFilterScrollStates[filterId] = -y();
}
void InnerWidget::restoreChatsFilterScrollState(FilterId filterId) {
bool InnerWidget::restoreChatsFilterScrollState(FilterId filterId) {
const auto it = _chatsFilterScrollStates.find(filterId);
if (it != end(_chatsFilterScrollStates)) {
_mustScrollTo.fire({ std::max(it->second, 0), -1 });
if (it == end(_chatsFilterScrollStates)) {
return false;
}
_mustScrollTo.fire({ std::max(it->second, 0), -1 });
return true;
}
QImage *InnerWidget::cacheChatsFilterTag(

View File

@@ -147,6 +147,7 @@ public:
void peerSearchReceived(Api::PeerSearchResult result);
[[nodiscard]] FilterId filterId() const;
void switchToFilter(FilterId filterId);
void clearSelection();
@@ -340,7 +341,6 @@ private:
void refreshWithCollapsedRows(bool toTop = false);
bool needCollapsedRowsRefresh() const;
bool chooseCollapsedRow(Qt::KeyboardModifiers modifiers);
void switchToFilter(FilterId filterId);
bool chooseHashtag();
ChosenRow computeChosenRow() const;
bool isRowActive(not_null<Row*> row, const RowDescriptor &entry) const;
@@ -609,7 +609,7 @@ private:
[[nodiscard]] bool hasChatTypeFilter() const;
void saveChatsFilterScrollState(FilterId filterId);
void restoreChatsFilterScrollState(FilterId filterId);
bool restoreChatsFilterScrollState(FilterId filterId);
[[nodiscard]] not_null<Ui::QuickActionContext*> ensureQuickAction(
int64 key);

View File

@@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/vertical_layout.h"
#include "ui/effects/radial_animation.h"
#include "ui/effects/ripple_animation.h"
#include "ui/effects/slide_animation.h"
#include "ui/chat/requests_bar.h"
#include "ui/chat/group_call_bar.h"
#include "ui/chat/more_chats_bar.h"
@@ -447,6 +448,10 @@ Widget::Widget(
_childListPeerId.value(),
_childListShown.value(),
makeChildListShown)));
controller->activeChatsFilter(
) | rpl::on_next([=](FilterId id) {
switchToChatsFilter(id);
}, lifetime());
rpl::combine(
_scroll->heightValue(),
_topBarSuggestionHeightChanged.events_starting_with(0)
@@ -953,7 +958,9 @@ void Widget::setupSwipeBack() {
if (CheckAndJumpToNearChatsFilter(controller(), next, false)) {
return Ui::Controls::DefaultSwipeBackHandlerFinishData([=] {
_swipeBackData = {};
_chatsFilterSwipeSwitch = true;
CheckAndJumpToNearChatsFilter(controller(), next, true);
_chatsFilterSwipeSwitch = false;
});
}
}
@@ -2052,9 +2059,10 @@ void Widget::toggleFiltersMenu(bool enabled) {
_chatFilters.get(),
&session(),
[this](FilterId id) {
_scroll->scrollToY(0);
if (controller()->activeChatsFilterCurrent() != id) {
controller()->setActiveChatsFilter(id);
} else {
_scroll->scrollToY(0);
}
},
Window::GifPauseReason::Any,
@@ -2246,6 +2254,7 @@ void Widget::changeOpenedSubsection(
if (isHidden()) {
animated = anim::type::instant;
}
_chatsFilterSlideCanvas = nullptr;
auto oldContentCache = QPixmap();
const auto showDirection = fromRight
? Window::SlideDirection::FromRight
@@ -2519,6 +2528,84 @@ void Widget::showSearchInTopBar(anim::type animated) {
updateForceDisplayWide();
}
void Widget::switchToChatsFilter(FilterId id) {
const auto was = _inner->filterId();
const auto animated = (was != id)
&& !isHidden()
&& !_showAnimation
&& (_chatsFilterSwipeSwitch
|| (_chatFilters && !_chatFilters->isHidden()));
if (!animated) {
_inner->switchToFilter(id);
return;
}
const auto &list = session().data().chatsFilters().list();
const auto indexOf = [&](FilterId filterId) {
return int(ranges::find(list, filterId, &Data::ChatFilter::id)
- begin(list));
};
const auto slideLeft = (indexOf(id) < indexOf(was));
const auto duration = _chatsFilterSwipeSwitch
? st::dialogsFilterSwipeSlideDuration
: st::slideDuration;
_chatsFilterSlideCanvas = nullptr;
auto wasCache = grabForChatsFilterSlide();
_inner->switchToFilter(id);
if (_inner->filterId() == was) {
return;
}
startChatsFilterSlide(
std::move(wasCache),
grabForChatsFilterSlide(),
slideLeft,
duration);
}
QPixmap Widget::grabForChatsFilterSlide() {
const auto hidden = _scrollToTop->isHidden();
if (!hidden) {
_scrollToTop->hide();
}
auto result = Ui::GrabOpaque(
_scroll.data(),
_scroll->rect(),
st::dialogsBg->c);
if (!hidden) {
_scrollToTop->show();
}
return result;
}
void Widget::startChatsFilterSlide(
QPixmap wasCache,
QPixmap nowCache,
bool slideLeft,
crl::time duration) {
_chatsFilterSlideCanvas = std::make_unique<Ui::RpWidget>(this);
const auto canvas = _chatsFilterSlideCanvas.get();
canvas->setAttribute(Qt::WA_TransparentForMouseEvents);
canvas->setAttribute(Qt::WA_OpaquePaintEvent);
canvas->setGeometry(_scroll->geometry());
const auto animation
= canvas->lifetime().make_state<Ui::SlideAnimation>();
animation->setSnapshots(std::move(wasCache), std::move(nowCache));
canvas->paintOn([=](QPainter &p) {
p.fillRect(canvas->rect(), st::dialogsBg);
animation->paintFrame(p, 0, 0, canvas->width());
});
canvas->show();
if (_connecting) {
_connecting->raise();
}
animation->start(slideLeft, [=] {
if (animation->animating()) {
canvas->update();
} else {
_chatsFilterSlideCanvas = nullptr;
}
}, duration);
}
QPixmap Widget::grabForFolderSlideAnimation() {
const auto hidden = _scrollToTop->isHidden();
if (!hidden) {
@@ -2832,6 +2919,7 @@ void Widget::showAnimated(
Window::SlideDirection direction,
const Window::SectionSlideParams &params) {
_showAnimation = nullptr;
_chatsFilterSlideCanvas = nullptr;
auto oldContentCache = params.oldContentCache;
showFast();
@@ -4607,6 +4695,9 @@ void Widget::updateControlsGeometry() {
const auto scrollHeight = height() - scrollTop - bottomSkip;
const auto wasScrollHeight = _scroll->height();
_scroll->setGeometry(0, scrollTop, scrollWidth, scrollHeight);
if (_chatsFilterSlideCanvas) {
_chatsFilterSlideCanvas->setGeometry(_scroll->geometry());
}
if (scrollHeight != wasScrollHeight) {
controller()->floatPlayerAreaUpdated();
}

View File

@@ -212,6 +212,13 @@ private:
void setupConnectingWidget();
void setupMainMenuToggle();
void setupMoreChatsBar();
void switchToChatsFilter(FilterId id);
[[nodiscard]] QPixmap grabForChatsFilterSlide();
void startChatsFilterSlide(
QPixmap wasCache,
QPixmap nowCache,
bool slideLeft,
crl::time duration);
void setupDownloadBar();
void setupShortcuts();
void setupStories();
@@ -428,6 +435,7 @@ private:
bool _searchQueryFromArchive = true;
Ui::Controls::SwipeBackResult _swipeBackData;
bool _chatsFilterSwipeSwitch = false;
bool _swipeBackMirrored = false;
bool _swipeBackIconMirrored = false;
@@ -448,6 +456,7 @@ private:
rpl::variable<float64> _childListShown;
rpl::variable<PeerId> _childListPeerId;
std::unique_ptr<Ui::RpWidget> _hideChildListCanvas;
std::unique_ptr<Ui::RpWidget> _chatsFilterSlideCanvas;
};