Added bounce animation on finish of replying with left swipe.

This commit is contained in:
23rd 2024-09-03 10:19:41 +03:00 committed by John Preston
parent 7f70ee1227
commit 6129e5a1cf
3 changed files with 56 additions and 10 deletions

View file

@ -27,6 +27,7 @@ void SetupSwipeHandler(
const auto threshold = style::ConvertFloatScale(kThresholdWidth); const auto threshold = style::ConvertFloatScale(kThresholdWidth);
struct State { struct State {
base::unique_qptr<QObject> filter; base::unique_qptr<QObject> filter;
Ui::Animations::Simple animationReach;
Ui::Animations::Simple animationEnd; Ui::Animations::Simple animationEnd;
SwipeHandlerFinishData finishByTopData; SwipeHandlerFinishData finishByTopData;
std::optional<Qt::Orientation> orientation; std::optional<Qt::Orientation> orientation;
@ -41,6 +42,7 @@ void SetupSwipeHandler(
const auto updateRatio = [=](float64 ratio) { const auto updateRatio = [=](float64 ratio) {
update({ update({
.ratio = std::clamp(ratio, 0., 1.), .ratio = std::clamp(ratio, 0., 1.),
.reachRatio = state->animationReach.value(0.),
.translation = (-std::clamp(ratio, 0., 1.5) * threshold), .translation = (-std::clamp(ratio, 0., 1.5) * threshold),
.msgBareId = state->finishByTopData.msgBareId, .msgBareId = state->finishByTopData.msgBareId,
.cursorTop = state->cursorTop, .cursorTop = state->cursorTop,
@ -82,6 +84,9 @@ void SetupSwipeHandler(
scroll->scrolls() | rpl::start_with_next([=] { scroll->scrolls() | rpl::start_with_next([=] {
processEnd(nullptr); processEnd(nullptr);
}, state->lifetime); }, state->lifetime);
const auto animationReachCallback = [=] {
updateRatio((state->startAt - state->lastAt).x() / threshold);
};
const auto filter = [=](not_null<QEvent*> e) { const auto filter = [=](not_null<QEvent*> e) {
if (e->type() == QEvent::Leave && state->orientation) { if (e->type() == QEvent::Leave && state->orientation) {
processEnd(nullptr); processEnd(nullptr);
@ -124,8 +129,15 @@ void SetupSwipeHandler(
const auto ratio = delta.x() / threshold; const auto ratio = delta.x() / threshold;
updateRatio(ratio); updateRatio(ratio);
constexpr auto kResetReachedOn = 0.95; constexpr auto kResetReachedOn = 0.95;
constexpr auto kBounceDuration = crl::time(500);
if (!state->reached && ratio >= 1.) { if (!state->reached && ratio >= 1.) {
state->reached = true; state->reached = true;
state->animationReach.stop();
state->animationReach.start(
animationReachCallback,
0.,
1.,
kBounceDuration);
base::Platform::Haptic(); base::Platform::Haptic();
} else if (state->reached } else if (state->reached
&& ratio < kResetReachedOn) { && ratio < kResetReachedOn) {

View file

@ -11,6 +11,7 @@ namespace HistoryView {
struct ChatPaintGestureHorizontalData { struct ChatPaintGestureHorizontalData {
float64 ratio = 0.; float64 ratio = 0.;
float64 reachRatio = 0.;
float64 translation = 0.; float64 translation = 0.;
int64 msgBareId = 0; int64 msgBareId = 0;
int cursorTop = 0; int cursorTop = 0;

View file

@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "payments/payments_reaction_process.h" // TryAddingPaidReaction. #include "payments/payments_reaction_process.h" // TryAddingPaidReaction.
#include "ui/text/text_options.h" #include "ui/text/text_options.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "window/themes/window_theme.h" // IsNightMode.
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
@ -1491,18 +1492,31 @@ void Message::draw(Painter &p, const PaintContext &context) const {
p.translate(-context.gestureHorizontal.translation, 0); p.translate(-context.gestureHorizontal.translation, 0);
constexpr auto kShiftRatio = 1.5; constexpr auto kShiftRatio = 1.5;
constexpr auto kBouncePart = 0.25;
constexpr auto kStrokeWidth = 2.;
constexpr auto kWaveWidth = 10.;
const auto reachRatio = context.gestureHorizontal.reachRatio;
const auto size = st::historyFastShareSize; const auto size = st::historyFastShareSize;
const auto rect = QRect( const auto rect = QRectF(
width() - (size * kShiftRatio) * context.gestureHorizontal.ratio, width() - (size * kShiftRatio) * context.gestureHorizontal.ratio,
g.y() + (g.height() - size) / 2, g.y() + (g.height() - size) / 2,
size, size,
size); size);
const auto center = rect::center(rect); const auto center = rect::center(rect);
const auto spanAngle = -context.gestureHorizontal.ratio const auto spanAngle = context.gestureHorizontal.ratio
* arc::kFullLength; * arc::kFullLength;
const auto strokeWidth = style::ConvertFloatScale(2.); const auto strokeWidth = style::ConvertFloatScale(kStrokeWidth);
auto pen = QPen(context.st->msgServiceBg());
pen.setWidthF(strokeWidth); const auto reachScale = std::clamp(
(reachRatio > kBouncePart)
? (kBouncePart * 2 - reachRatio)
: reachRatio,
0.,
1.);
auto pen = Window::Theme::IsNightMode()
? QPen(anim::with_alpha(context.st->msgServiceFg()->c, 0.3))
: QPen(context.st->msgServiceBg());
pen.setWidthF(strokeWidth - (1. * (reachScale / kBouncePart)));
const auto arcRect = rect - Margins(strokeWidth); const auto arcRect = rect - Margins(strokeWidth);
p.save(); p.save();
{ {
@ -1510,15 +1524,34 @@ void Message::draw(Painter &p, const PaintContext &context) const {
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.setBrush(context.st->msgServiceBg()); p.setBrush(context.st->msgServiceBg());
p.setOpacity(context.gestureHorizontal.ratio); p.setOpacity(context.gestureHorizontal.ratio);
p.translate(center);
if (reachScale) {
p.scale(-(1. + 1. * reachScale), (1. + 1. * reachScale));
} else {
p.scale(-1., 1.);
}
p.translate(-center);
// All the next draws are mirrored.
p.drawEllipse(rect); p.drawEllipse(rect);
context.st->historyFastShareIcon().paintInCenter(
p,
QRect(
base::SafeRound(rect.x()),
base::SafeRound(rect.y()),
base::SafeRound(rect.width()),
base::SafeRound(rect.height())));
p.setPen(pen); p.setPen(pen);
p.setBrush(Qt::NoBrush); p.setBrush(Qt::NoBrush);
p.drawArc(arcRect, arc::kQuarterLength, spanAngle); p.drawArc(arcRect, arc::kQuarterLength, spanAngle);
p.drawArc(arcRect, arc::kQuarterLength, spanAngle); // p.drawArc(arcRect, arc::kQuarterLength, spanAngle);
p.translate(center); if (reachRatio) {
p.scale(-1., 1.); const auto w = style::ConvertFloatScale(kWaveWidth);
p.translate(-center); p.setOpacity(context.gestureHorizontal.ratio - reachRatio);
context.st->historyFastShareIcon().paintInCenter(p, rect); p.drawArc(
arcRect + Margins(reachRatio * reachRatio * w),
arc::kQuarterLength,
spanAngle);
}
} }
p.restore(); p.restore();
} }