Animate search suggestions.

This commit is contained in:
John Preston 2024-04-12 19:18:07 +04:00
parent 19f5d95a3c
commit 0180fe9468
4 changed files with 122 additions and 21 deletions

View File

@ -1030,10 +1030,7 @@ void Widget::fullSearchRefreshOn(rpl::producer<> events) {
void Widget::updateControlsVisibility(bool fast) {
updateLoadMoreChatsVisibility();
_scroll->setVisible(!_suggestions);
if (_suggestions) {
_suggestions->show();
}
_scroll->setVisible(!_suggestions && _hidingSuggestions.empty());
updateStoriesVisibility();
if ((_openedFolder || _openedForum) && _searchHasFocus) {
setInnerFocus();
@ -1120,10 +1117,32 @@ void Widget::updateSuggestions(anim::type animated) {
const auto suggest = _searchHasFocus
&& !_searchInChat
&& (_inner->state() == WidgetState::Default);
if (anim::Disabled() || !session().data().chatsListLoaded()) {
animated = anim::type::instant;
}
if (!suggest && _suggestions) {
_suggestions = nullptr;
_scroll->show();
if (animated == anim::type::normal) {
startWidthAnimation();
_suggestions->hide(animated, [=, raw = _suggestions.get()] {
stopWidthAnimation();
_hidingSuggestions.erase(
ranges::remove(
_hidingSuggestions,
raw,
&std::unique_ptr<Suggestions>::get),
end(_hidingSuggestions));
updateControlsVisibility();
});
_hidingSuggestions.push_back(std::move(_suggestions));
} else {
_suggestions = nullptr;
_hidingSuggestions.clear();
_scroll->show();
}
} else if (suggest && !_suggestions) {
if (animated == anim::type::normal) {
startWidthAnimation();
}
_suggestions = std::make_unique<Suggestions>(
this,
controller(),
@ -1141,9 +1160,12 @@ void Widget::updateSuggestions(anim::type animated) {
}
}, _suggestions->lifetime());
_suggestions->show();
_scroll->hide();
updateControlsGeometry();
_suggestions->show(animated, [=] {
stopWidthAnimation();
});
_scroll->hide();
}
}
@ -1542,18 +1564,15 @@ void Widget::scrollToDefault(bool verytop) {
anim::sineInOut);
}
void Widget::startWidthAnimation() {
if (!_widthAnimationCache.isNull()) {
return;
}
[[nodiscard]] QPixmap Widget::grabNonNarrowScrollFrame() {
auto scrollGeometry = _scroll->geometry();
auto grabGeometry = QRect(
scrollGeometry.x(),
scrollGeometry.y(),
st::columnMinimalWidthLeft,
std::max(scrollGeometry.width(), st::columnMinimalWidthLeft),
scrollGeometry.height());
_scroll->setGeometry(grabGeometry);
_inner->resize(st::columnMinimalWidthLeft, _inner->height());
_inner->resize(grabGeometry.width(), _inner->height());
_inner->setNarrowRatio(0.);
Ui::SendPendingMoveResizeEvents(_scroll);
auto image = QImage(
@ -1565,11 +1584,18 @@ void Widget::startWidthAnimation() {
QPainter p(&image);
Ui::RenderWidget(p, _scroll);
}
_widthAnimationCache = Ui::PixmapFromImage(std::move(image));
if (scrollGeometry != grabGeometry) {
_scroll->setGeometry(scrollGeometry);
updateControlsGeometry();
}
return Ui::PixmapFromImage(std::move(image));
}
void Widget::startWidthAnimation() {
if (!_widthAnimationCache.isNull()) {
return;
}
_widthAnimationCache = grabNonNarrowScrollFrame();
_scroll->hide();
updateStoriesVisibility();
}
@ -1578,9 +1604,6 @@ void Widget::stopWidthAnimation() {
_widthAnimationCache = QPixmap();
if (!_showAnimation) {
_scroll->setVisible(!_suggestions);
if (_suggestions) {
_suggestions->show();
}
}
updateStoriesVisibility();
update();
@ -3226,7 +3249,15 @@ void Widget::paintEvent(QPaintEvent *e) {
auto belowTop = _scroll->y() + _scroll->height();
if (!_widthAnimationCache.isNull()) {
p.drawPixmapLeft(0, _scroll->y(), width(), _widthAnimationCache);
const auto suggestionsShown = _suggestions
? _suggestions->shownOpacity()
: !_hidingSuggestions.empty()
? _hidingSuggestions.back()->shownOpacity()
: 0.;
const auto suggestionsSkip = suggestionsShown
* (st::topPeers.height + st::searchedBarHeight);
const auto top = _scroll->y() + suggestionsSkip;
p.drawPixmapLeft(0, top, width(), _widthAnimationCache);
belowTop = _scroll->y()
+ (_widthAnimationCache.height() / style::DevicePixelRatio());
}
@ -3302,12 +3333,17 @@ bool Widget::cancelSearch() {
setFocus();
clearingInChat = true;
}
const auto clearSearchFocus = !_searchInChat && _searchHasFocus;
if (!_suggestions && clearSearchFocus) {
// Don't create suggestions in unfocus case.
setFocus();
}
_lastSearchPeer = nullptr;
_lastSearchId = _lastSearchMigratedId = 0;
_inner->clearFilter();
clearSearchField();
applySearchUpdate();
if (!_searchInChat && _searchHasFocus) {
if (_suggestions && clearSearchFocus) {
setFocus();
}
return clearingQuery || clearingInChat;

View File

@ -105,6 +105,7 @@ public:
void jumpToTop(bool belowPinned = false);
void raiseWithTooltip();
[[nodiscard]] QPixmap grabNonNarrowScrollFrame();
void startWidthAnimation();
void stopWidthAnimation();
@ -277,6 +278,7 @@ private:
object_ptr<Ui::ElasticScroll> _scroll;
QPointer<InnerWidget> _inner;
std::unique_ptr<Suggestions> _suggestions;
std::vector<std::unique_ptr<Suggestions>> _hidingSuggestions;
class BottomButton;
object_ptr<BottomButton> _updateTelegram = { nullptr };
object_ptr<BottomButton> _loadMoreChats = { nullptr };

View File

@ -545,8 +545,61 @@ void Suggestions::chooseRow() {
}
}
void Suggestions::show(anim::type animated, Fn<void()> finish) {
RpWidget::show();
_hidden = false;
if (animated == anim::type::instant) {
_shownAnimation.stop();
_scroll->show();
} else {
_shownAnimation.start([=] {
update();
if (!_shownAnimation.animating() && finish) {
finish();
_cache = QPixmap();
_scroll->show();
}
}, 0., 1., st::slideDuration, anim::easeOutQuint);
_cache = Ui::GrabWidget(_scroll.get());
_scroll->hide();
}
}
void Suggestions::hide(anim::type animated, Fn<void()> finish) {
_hidden = true;
if (isHidden()) {
return;
} else if (animated == anim::type::instant) {
RpWidget::hide();
} else {
_shownAnimation.start([=] {
update();
if (!_shownAnimation.animating() && finish) {
finish();
}
}, 1., 0., st::slideDuration, anim::easeOutQuint);
_cache = Ui::GrabWidget(_scroll.get());
_scroll->hide();
}
}
float64 Suggestions::shownOpacity() const {
return _shownAnimation.value(_hidden ? 0. : 1.);
}
void Suggestions::paintEvent(QPaintEvent *e) {
QPainter(this).fillRect(e->rect(), st::windowBg);
const auto opacity = shownOpacity();
auto color = st::windowBg->c;
color.setAlphaF(color.alphaF() * opacity);
auto p = QPainter(this);
p.fillRect(e->rect(), color);
if (_scroll->isHidden()) {
const auto slide = st::topPeers.height + st::searchedBarHeight;
p.setOpacity(opacity);
p.drawPixmap(0, (opacity - 1.) * slide, _cache);
}
}
void Suggestions::resizeEvent(QResizeEvent *e) {

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/object_ptr.h"
#include "dialogs/ui/top_peers_strip.h"
#include "ui/effects/animations.h"
#include "ui/rp_widget.h"
namespace Main {
@ -44,6 +45,10 @@ public:
void selectJump(Qt::Key direction, int pageSize = 0);
void chooseRow();
void show(anim::type animated, Fn<void()> finish);
void hide(anim::type animated, Fn<void()> finish);
[[nodiscard]] float64 shownOpacity() const;
[[nodiscard]] rpl::producer<not_null<PeerData*>> topPeerChosen() const {
return _topPeerChosen.events();
}
@ -81,6 +86,11 @@ private:
rpl::event_stream<not_null<PeerData*>> _topPeerChosen;
rpl::event_stream<not_null<PeerData*>> _recentPeerChosen;
Ui::Animations::Simple _shownAnimation;
Fn<void()> _showFinished;
bool _hidden = false;
QPixmap _cache;
};
[[nodiscard]] rpl::producer<TopPeersList> TopPeersContent(