Add progress animation to GIFs search.

Also display "no results" phrase.
This commit is contained in:
John Preston 2017-04-04 16:19:49 +03:00
parent 2028116e22
commit 03a59b04be
8 changed files with 191 additions and 13 deletions

View File

@ -270,6 +270,7 @@ contactsSearchCancel: CrossButton {
crossPosition: point(4px, 4px); crossPosition: point(4px, 4px);
duration: 150; duration: 150;
loadingPeriod: 1000;
ripple: RippleAnimation(defaultRippleAnimation) { ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver; color: windowBgOver;
} }

View File

@ -137,6 +137,7 @@ dialogsCancelSearch: CrossButton {
crossPosition: point(0px, 0px); crossPosition: point(0px, 0px);
duration: 150; duration: 150;
loadingPeriod: 1000;
ripple: emptyRippleAnimation; ripple: emptyRippleAnimation;
} }

View File

@ -48,6 +48,9 @@ public:
void stealFocus(); void stealFocus();
void returnFocus(); void returnFocus();
void setLoading(bool loading) {
_cancel->setLoadingAnimation(loading);
}
protected: protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
@ -181,6 +184,7 @@ GifsListWidget::~GifsListWidget() {
} }
void GifsListWidget::cancelGifsSearch() { void GifsListWidget::cancelGifsSearch() {
_footer->setLoading(false);
if (_inlineRequestId) { if (_inlineRequestId) {
request(_inlineRequestId).cancel(); request(_inlineRequestId).cancel();
_inlineRequestId = 0; _inlineRequestId = 0;
@ -192,6 +196,7 @@ void GifsListWidget::cancelGifsSearch() {
} }
void GifsListWidget::inlineResultsDone(const MTPmessages_BotResults &result) { void GifsListWidget::inlineResultsDone(const MTPmessages_BotResults &result) {
_footer->setLoading(false);
_inlineRequestId = 0; _inlineRequestId = 0;
auto it = _inlineCache.find(_inlineQuery); auto it = _inlineCache.find(_inlineQuery);
@ -754,9 +759,7 @@ bool GifsListWidget::refreshInlineRows(int32 *added) {
auto it = _inlineCache.find(_inlineQuery); auto it = _inlineCache.find(_inlineQuery);
const InlineCacheEntry *entry = nullptr; const InlineCacheEntry *entry = nullptr;
if (it != _inlineCache.cend()) { if (it != _inlineCache.cend()) {
if (!it->second->results.empty()) { entry = it->second.get();
entry = it->second.get();
}
_inlineNextOffset = it->second->nextOffset; _inlineNextOffset = it->second->nextOffset;
} }
auto result = refreshInlineRows(entry, false); auto result = refreshInlineRows(entry, false);
@ -780,6 +783,7 @@ void GifsListWidget::searchForGifs(const QString &query) {
} }
if (_inlineQuery != query) { if (_inlineQuery != query) {
_footer->setLoading(false);
if (_inlineRequestId) { if (_inlineRequestId) {
request(_inlineRequestId).cancel(); request(_inlineRequestId).cancel();
_inlineRequestId = 0; _inlineRequestId = 0;
@ -813,8 +817,10 @@ void GifsListWidget::sendInlineRequest() {
if (_inlineRequestId || !_inlineQueryPeer || _inlineNextQuery.isEmpty()) { if (_inlineRequestId || !_inlineQueryPeer || _inlineNextQuery.isEmpty()) {
return; return;
} }
if (!_searchBot) { if (!_searchBot) {
// Wait for the bot being resolved. // Wait for the bot being resolved.
_footer->setLoading(true);
_inlineRequestTimer.start(kSearchRequestDelay); _inlineRequestTimer.start(kSearchRequestDelay);
return; return;
} }
@ -825,13 +831,18 @@ void GifsListWidget::sendInlineRequest() {
auto it = _inlineCache.find(_inlineQuery); auto it = _inlineCache.find(_inlineQuery);
if (it != _inlineCache.cend()) { if (it != _inlineCache.cend()) {
nextOffset = it->second->nextOffset; nextOffset = it->second->nextOffset;
if (nextOffset.isEmpty()) return; if (nextOffset.isEmpty()) {
_footer->setLoading(false);
return;
}
} }
_footer->setLoading(true);
_inlineRequestId = request(MTPmessages_GetInlineBotResults(MTP_flags(0), _searchBot->inputUser, _inlineQueryPeer->input, MTPInputGeoPoint(), MTP_string(_inlineQuery), MTP_string(nextOffset))).done([this](const MTPmessages_BotResults &result, mtpRequestId requestId) { _inlineRequestId = request(MTPmessages_GetInlineBotResults(MTP_flags(0), _searchBot->inputUser, _inlineQueryPeer->input, MTPInputGeoPoint(), MTP_string(_inlineQuery), MTP_string(nextOffset))).done([this](const MTPmessages_BotResults &result, mtpRequestId requestId) {
inlineResultsDone(result); inlineResultsDone(result);
}).fail([this](const RPCError &error) { }).fail([this](const RPCError &error) {
// show error? // show error?
_footer->setLoading(false);
_inlineRequestId = 0; _inlineRequestId = 0;
}).handleAllErrors().send(); }).handleAllErrors().send();
} }

View File

@ -21,19 +21,100 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/effects/cross_animation.h" #include "ui/effects/cross_animation.h"
namespace Ui { namespace Ui {
namespace {
void CrossAnimation::paint(Painter &p, const style::CrossAnimation &st, style::color color, int x, int y, int outerWidth, float64 shown) { constexpr auto kPointCount = 12;
//
// 1 3
// X X X X
// X X X X
// 0 X X 4
// X X X X
// X 2 X
// X X
// X X
// 11 5
// X X
// X X
// X 8 X
// X X X X
// 10 X X 6
// X X X X
// X X X X
// 9 7
//
void transformLoadingCross(float64 loading, std::array<QPointF, kPointCount> &points, int &paintPointsCount) {
auto moveTo = [](QPointF &point, QPointF &to, float64 ratio) {
point = point * (1. - ratio) + to * ratio;
};
auto moveFrom = [](QPointF &point, QPointF &from, float64 ratio) {
point = from * (1. - ratio) + point * ratio;
};
auto paintPoints = [&points, &paintPointsCount](std::initializer_list<int> &&indices) {
auto index = 0;
for (auto paintIndex : indices) {
points[index++] = points[paintIndex];
}
paintPointsCount = indices.size();
};
if (loading < 0.125) {
auto ratio = loading / 0.125;
moveTo(points[6], points[5], ratio);
moveTo(points[7], points[8], ratio);
} else if (loading < 0.25) {
auto ratio = (loading - 0.125) / 0.125;
moveTo(points[9], points[8], ratio);
moveTo(points[10], points[11], ratio);
paintPoints({ 0, 1, 2, 3, 4, 9, 10, 11 });
} else if (loading < 0.375) {
auto ratio = (loading - 0.25) / 0.125;
moveTo(points[0], points[11], ratio);
moveTo(points[1], points[2], ratio);
paintPoints({ 0, 1, 2, 3, 4, 8 });
} else if (loading < 0.5) {
auto ratio = (loading - 0.375) / 0.125;
moveTo(points[8], points[4], ratio);
moveTo(points[11], points[3], ratio);
paintPoints({ 3, 4, 8, 11 });
} else if (loading < 0.625) {
auto ratio = (loading - 0.5) / 0.125;
moveFrom(points[8], points[4], ratio);
moveFrom(points[11], points[3], ratio);
paintPoints({ 3, 4, 8, 11 });
} else if (loading < 0.75) {
auto ratio = (loading - 0.625) / 0.125;
moveFrom(points[6], points[5], ratio);
moveFrom(points[7], points[8], ratio);
paintPoints({ 3, 4, 5, 6, 7, 11 });
} else if (loading < 0.875) {
auto ratio = (loading - 0.75) / 0.125;
moveFrom(points[9], points[8], ratio);
moveFrom(points[10], points[11], ratio);
paintPoints({ 3, 4, 5, 6, 7, 8, 9, 10 });
} else {
auto ratio = (loading - 0.875) / 0.125;
moveFrom(points[0], points[11], ratio);
moveFrom(points[1], points[2], ratio);
}
}
} // namespace
void CrossAnimation::paint(Painter &p, const style::CrossAnimation &st, style::color color, int x, int y, int outerWidth, float64 shown, float64 loading) {
PainterHighQualityEnabler hq(p); PainterHighQualityEnabler hq(p);
auto deleteScale = shown + st.minScale * (1. - shown);
auto deleteSkip = deleteScale * st.skip + (1. - deleteScale) * (st.size / 2);
auto sqrt2 = sqrt(2.); auto sqrt2 = sqrt(2.);
auto deleteScale = shown + st.minScale * (1. - shown);
auto deleteSkip = (deleteScale * st.skip) + (1. - deleteScale) * (st.size / 2);
auto deleteLeft = rtlpoint(x + deleteSkip, 0, outerWidth).x() + 0.; auto deleteLeft = rtlpoint(x + deleteSkip, 0, outerWidth).x() + 0.;
auto deleteTop = y + deleteSkip + 0.; auto deleteTop = y + deleteSkip + 0.;
auto deleteWidth = st.size - 2 * deleteSkip; auto deleteWidth = st.size - 2 * deleteSkip;
auto deleteHeight = st.size - 2 * deleteSkip; auto deleteHeight = st.size - 2 * deleteSkip;
auto deleteStroke = st.stroke / sqrt2; auto deleteStroke = st.stroke / sqrt2;
QPointF pathDelete[] = { std::array<QPointF, kPointCount> pathDelete = { {
{ deleteLeft, deleteTop + deleteStroke }, { deleteLeft, deleteTop + deleteStroke },
{ deleteLeft + deleteStroke, deleteTop }, { deleteLeft + deleteStroke, deleteTop },
{ deleteLeft + (deleteWidth / 2.), deleteTop + (deleteHeight / 2.) - deleteStroke }, { deleteLeft + (deleteWidth / 2.), deleteTop + (deleteHeight / 2.) - deleteStroke },
@ -46,7 +127,17 @@ void CrossAnimation::paint(Painter &p, const style::CrossAnimation &st, style::c
{ deleteLeft + deleteStroke, deleteTop + deleteHeight }, { deleteLeft + deleteStroke, deleteTop + deleteHeight },
{ deleteLeft, deleteTop + deleteHeight - deleteStroke }, { deleteLeft, deleteTop + deleteHeight - deleteStroke },
{ deleteLeft + (deleteWidth / 2.) - deleteStroke, deleteTop + (deleteHeight / 2.) }, { deleteLeft + (deleteWidth / 2.) - deleteStroke, deleteTop + (deleteHeight / 2.) },
}; } };
auto pathDeleteSize = kPointCount;
auto loadingArcLength = 0;
if (loading > 0.) {
transformLoadingCross(loading, pathDelete, pathDeleteSize);
auto loadingArc = (loading >= 0.5) ? (loading - 1.) : loading;
loadingArcLength = qRound(-loadingArc * 2 * FullArcLength);
}
if (shown < 1.) { if (shown < 1.) {
auto alpha = -(shown - 1.) * M_PI_2; auto alpha = -(shown - 1.) * M_PI_2;
auto cosalpha = cos(alpha); auto cosalpha = cos(alpha);
@ -62,10 +153,30 @@ void CrossAnimation::paint(Painter &p, const style::CrossAnimation &st, style::c
} }
QPainterPath path; QPainterPath path;
path.moveTo(pathDelete[0]); path.moveTo(pathDelete[0]);
for (int i = 1; i != base::array_size(pathDelete); ++i) { for (int i = 1; i != pathDeleteSize; ++i) {
path.lineTo(pathDelete[i]); path.lineTo(pathDelete[i]);
} }
path.lineTo(pathDelete[0]);
p.fillPath(path, color); p.fillPath(path, color);
if (loadingArcLength != 0) {
auto loadingArcStart = FullArcLength / 8;
auto roundSkip = (st.size * (1 - sqrt2) + 2 * sqrt2 * deleteSkip + st.stroke) / 2;
auto roundPart = QRectF(x + roundSkip, y + roundSkip, st.size - 2 * roundSkip, st.size - 2 * roundSkip);
if (shown < 1.) {
loadingArcStart -= qRound(-(shown - 1.) * FullArcLength / 4.);
}
p.setBrush(Qt::NoBrush);
auto pen = color->p;
pen.setWidthF(st.stroke);
pen.setCapStyle(Qt::RoundCap);
p.setPen(pen);
if (loadingArcLength < 0) {
loadingArcStart += loadingArcLength;
loadingArcLength = -loadingArcLength;
}
p.drawArc(roundPart, loadingArcStart, loadingArcLength);
}
} }
} // namespace Ui } // namespace Ui

View File

@ -26,7 +26,7 @@ namespace Ui {
class CrossAnimation { class CrossAnimation {
public: public:
static void paint(Painter &p, const style::CrossAnimation &st, style::color color, int x, int y, int outerWidth, float64 shown); static void paint(Painter &p, const style::CrossAnimation &st, style::color color, int x, int y, int outerWidth, float64 shown, float64 loading = 0.);
}; };

View File

@ -625,12 +625,22 @@ void LeftOutlineButton::paintEvent(QPaintEvent *e) {
} }
CrossButton::CrossButton(QWidget *parent, const style::CrossButton &st) : RippleButton(parent, st.ripple) CrossButton::CrossButton(QWidget *parent, const style::CrossButton &st) : RippleButton(parent, st.ripple)
, _st(st) { , _st(st)
, _a_loading(animation(this, &CrossButton::step_loading)) {
resize(_st.width, _st.height); resize(_st.width, _st.height);
setCursor(style::cur_pointer); setCursor(style::cur_pointer);
hide(); hide();
} }
void CrossButton::step_loading(TimeMs ms, bool timer) {
if (stopLoadingAnimation(ms)) {
_a_loading.stop();
}
if (timer) {
update();
}
}
void CrossButton::toggleAnimated(bool visible) { void CrossButton::toggleAnimated(bool visible) {
if (_shown == visible) { if (_shown == visible) {
return; return;
@ -659,7 +669,43 @@ void CrossButton::paintEvent(QPaintEvent *e) {
paintRipple(p, _st.crossPosition.x(), _st.crossPosition.y(), ms); paintRipple(p, _st.crossPosition.x(), _st.crossPosition.y(), ms);
CrossAnimation::paint(p, _st.cross, over ? _st.crossFgOver : _st.crossFg, _st.crossPosition.x(), _st.crossPosition.y(), width(), shown); auto loading = 0.;
if (_a_loading.animating()) {
if (stopLoadingAnimation(ms)) {
_a_loading.stop();
} else {
loading = ((ms - _loadingStartMs) % _st.loadingPeriod) / float64(_st.loadingPeriod);
}
}
CrossAnimation::paint(p, _st.cross, over ? _st.crossFgOver : _st.crossFg, _st.crossPosition.x(), _st.crossPosition.y(), width(), shown, loading);
}
bool CrossButton::stopLoadingAnimation(TimeMs ms) {
if (!_loadingStopMs) {
return false;
}
auto stopPeriod = (_loadingStopMs - _loadingStartMs) / _st.loadingPeriod;
auto currentPeriod = (ms - _loadingStartMs) / _st.loadingPeriod;
if (currentPeriod != stopPeriod) {
t_assert(currentPeriod > stopPeriod);
return true;
}
return false;
}
void CrossButton::setLoadingAnimation(bool enabled) {
if (enabled) {
_loadingStopMs = 0;
if (!_a_loading.animating()) {
_loadingStartMs = getms();
_a_loading.start();
}
} else if (_a_loading.animating()) {
_loadingStopMs = getms();
if (!((_loadingStopMs - _loadingStartMs) % _st.loadingPeriod)) {
_a_loading.stop();
}
}
} }
void CrossButton::onStateChanged(State was, StateChangeSource source) { void CrossButton::onStateChanged(State was, StateChangeSource source) {

View File

@ -233,6 +233,7 @@ public:
bool isShown() const { bool isShown() const {
return _shown; return _shown;
} }
void setLoadingAnimation(bool enabled);
protected: protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
@ -243,6 +244,8 @@ protected:
QPoint prepareRippleStartPosition() const override; QPoint prepareRippleStartPosition() const override;
private: private:
void step_loading(TimeMs ms, bool timer);
bool stopLoadingAnimation(TimeMs ms);
void animationCallback(); void animationCallback();
const style::CrossButton &_st; const style::CrossButton &_st;
@ -250,6 +253,10 @@ private:
bool _shown = false; bool _shown = false;
Animation _a_show; Animation _a_show;
TimeMs _loadingStartMs = 0;
TimeMs _loadingStopMs = 0;
BasicAnimation _a_loading;
}; };
} // namespace Ui } // namespace Ui

View File

@ -315,6 +315,7 @@ CrossButton {
crossFgOver:color; crossFgOver:color;
crossPosition: point; crossPosition: point;
duration: int; duration: int;
loadingPeriod: int;
ripple: RippleAnimation; ripple: RippleAnimation;
} }