mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 02:01:40 -05:00
Nice animations when selecting shared media items.
This commit is contained in:
parent
9dc39cb758
commit
0ced28f991
11 changed files with 442 additions and 189 deletions
|
@ -534,7 +534,12 @@ void HistoryTopBarWidget::showSelected(SelectedState state) {
|
|||
setCursor(hasSelected ? style::cur_default : style::cur_pointer);
|
||||
|
||||
updateMembersShowArea();
|
||||
_selectedShown.start([this] { selectedShowCallback(); }, hasSelected ? 0. : 1., hasSelected ? 1. : 0., st::topBarSlideDuration, anim::easeOutCirc);
|
||||
_selectedShown.start(
|
||||
[this] { selectedShowCallback(); },
|
||||
hasSelected ? 0. : 1.,
|
||||
hasSelected ? 1. : 0.,
|
||||
st::slideWrapDuration,
|
||||
anim::easeOutCirc);
|
||||
} else {
|
||||
updateControlsGeometry();
|
||||
}
|
||||
|
|
|
@ -25,10 +25,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "lang/lang_keys.h"
|
||||
#include "info/info_wrap_widget.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "ui/effects/numbers_animation.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "mainwidget.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
|
@ -44,7 +44,7 @@ TopBarOverride::TopBarOverride(
|
|||
, _items(std::move(items))
|
||||
, _canDelete(computeCanDelete())
|
||||
, _cancel(this, _st.mediaCancel)
|
||||
, _text(this, generateText(), Ui::FlatLabel::InitType::Simple, _st.title)
|
||||
, _text(this, _st.title, _st.titlePosition.y(), generateText())
|
||||
, _forward(this, _st.mediaForward)
|
||||
, _delete(this, _st.mediaDelete) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
|
@ -55,17 +55,18 @@ TopBarOverride::TopBarOverride(
|
|||
_delete->addClickHandler([this] { performDelete(); });
|
||||
}
|
||||
|
||||
QString TopBarOverride::generateText() const {
|
||||
Ui::StringWithNumbers TopBarOverride::generateText() const {
|
||||
using Data = Ui::StringWithNumbers;
|
||||
using Type = Storage::SharedMediaType;
|
||||
auto phrase = [&] {
|
||||
switch (_items.type) {
|
||||
case Type::Photo: return lng_media_selected_photo;
|
||||
case Type::Video: return lng_media_selected_video;
|
||||
case Type::File: return lng_media_selected_file;
|
||||
case Type::MusicFile: return lng_media_selected_song;
|
||||
case Type::Link: return lng_media_selected_link;
|
||||
case Type::VoiceFile: return lng_media_selected_audio;
|
||||
// case Type::RoundFile: return lng_media_selected_round;
|
||||
case Type::Photo: return lng_media_selected_photo__generic<Data>;
|
||||
case Type::Video: return lng_media_selected_video__generic<Data>;
|
||||
case Type::File: return lng_media_selected_file__generic<Data>;
|
||||
case Type::MusicFile: return lng_media_selected_song__generic<Data>;
|
||||
case Type::Link: return lng_media_selected_link__generic<Data>;
|
||||
case Type::VoiceFile: return lng_media_selected_audio__generic<Data>;
|
||||
// case Type::RoundFile: return lng_media_selected_round__generic<Data>;
|
||||
}
|
||||
Unexpected("Type in TopBarOverride::generateText()");
|
||||
}();
|
||||
|
@ -82,7 +83,7 @@ void TopBarOverride::setItems(SelectedItems &&items) {
|
|||
_items = std::move(items);
|
||||
_canDelete = computeCanDelete();
|
||||
|
||||
_text->setText(generateText());
|
||||
_text->setValue(generateText());
|
||||
updateControlsVisibility();
|
||||
updateControlsGeometry(width());
|
||||
}
|
||||
|
@ -110,8 +111,14 @@ void TopBarOverride::updateControlsGeometry(int newWidth) {
|
|||
right += _delete->width();
|
||||
}
|
||||
_forward->moveToRight(right, 0, newWidth);
|
||||
_cancel->moveToLeft(0, 0);
|
||||
_text->moveToLeft(_cancel->width(), _st.titlePosition.y());
|
||||
right += _forward->width();
|
||||
|
||||
auto left = 0;
|
||||
_cancel->moveToLeft(left, 0);
|
||||
left += _cancel->width();
|
||||
|
||||
const auto availableWidth = newWidth - left - right;
|
||||
_text->setGeometryToLeft(left, 0, availableWidth, _st.height, newWidth);
|
||||
}
|
||||
|
||||
void TopBarOverride::updateControlsVisibility() {
|
||||
|
|
|
@ -30,7 +30,8 @@ struct InfoTopBar;
|
|||
|
||||
namespace Ui {
|
||||
class IconButton;
|
||||
class FlatLabel;
|
||||
class LabelWithNumbers;
|
||||
struct StringWithNumbers;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Info {
|
||||
|
@ -54,7 +55,7 @@ protected:
|
|||
private:
|
||||
void updateControlsVisibility();
|
||||
void updateControlsGeometry(int newWidth);
|
||||
QString generateText() const;
|
||||
Ui::StringWithNumbers generateText() const;
|
||||
[[nodiscard]] bool computeCanDelete() const;
|
||||
[[nodiscard]] SelectedItemSet collectItems() const;
|
||||
|
||||
|
@ -65,7 +66,7 @@ private:
|
|||
SelectedItems _items;
|
||||
bool _canDelete = false;
|
||||
object_ptr<Ui::IconButton> _cancel;
|
||||
object_ptr<Ui::FlatLabel> _text;
|
||||
object_ptr<Ui::LabelWithNumbers> _text;
|
||||
object_ptr<Ui::IconButton> _forward;
|
||||
object_ptr<Ui::IconButton> _delete;
|
||||
rpl::event_stream<> _correctionCancelRequests;
|
||||
|
|
|
@ -46,7 +46,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "lang/lang_keys.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_profile.h"
|
||||
#include "styles/style_window.h"
|
||||
|
||||
namespace Info {
|
||||
namespace {
|
||||
|
@ -449,7 +448,7 @@ void WrapWidget::toggleTopBarOverride(bool shown) {
|
|||
[this] { topBarOverrideStep(); },
|
||||
_topBarOverrideShown ? 0. : 1.,
|
||||
_topBarOverrideShown ? 1. : 0.,
|
||||
st::topBarSlideDuration,
|
||||
st::slideWrapDuration,
|
||||
anim::easeOutCirc);
|
||||
}
|
||||
|
||||
|
|
261
Telegram/SourceFiles/ui/effects/numbers_animation.cpp
Normal file
261
Telegram/SourceFiles/ui/effects/numbers_animation.cpp
Normal file
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "ui/effects/numbers_animation.h"
|
||||
|
||||
#include "lang/lang_tag.h"
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
NumbersAnimation::NumbersAnimation(
|
||||
const style::font &font,
|
||||
base::lambda<void()> animationCallback)
|
||||
: _font(font)
|
||||
, _animationCallback(std::move(animationCallback)) {
|
||||
for (auto ch = '0'; ch != '9'; ++ch) {
|
||||
accumulate_max(_digitWidth, _font->m.width(ch));
|
||||
}
|
||||
}
|
||||
|
||||
void NumbersAnimation::setText(const QString &text, int value) {
|
||||
if (_a_ready.animating(getms())) {
|
||||
_delayedText = text;
|
||||
_delayedValue = value;
|
||||
} else {
|
||||
realSetText(text, value);
|
||||
}
|
||||
}
|
||||
|
||||
void NumbersAnimation::animationCallback() {
|
||||
if (_animationCallback) {
|
||||
_animationCallback();
|
||||
}
|
||||
if (_widthChangedCallback) {
|
||||
_widthChangedCallback();
|
||||
}
|
||||
if (!_a_ready.animating()) {
|
||||
if (!_delayedText.isEmpty()) {
|
||||
setText(_delayedText, _delayedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NumbersAnimation::realSetText(QString text, int value) {
|
||||
_delayedText = QString();
|
||||
_delayedValue = 0;
|
||||
|
||||
_growing = (value > _value);
|
||||
_value = value;
|
||||
|
||||
auto newSize = text.size();
|
||||
while (_digits.size() < newSize) {
|
||||
_digits.push_front(Digit());
|
||||
}
|
||||
while (_digits.size() > newSize && !_digits.front().to.unicode()) {
|
||||
_digits.pop_front();
|
||||
}
|
||||
auto oldSize = _digits.size();
|
||||
auto animating = false;
|
||||
for (auto i = 0, size = _digits.size(); i != size; ++i) {
|
||||
auto &digit = _digits[i];
|
||||
digit.from = digit.to;
|
||||
digit.fromWidth = digit.toWidth;
|
||||
digit.to = (newSize + i < size) ? QChar(0) : text[newSize + i - size];
|
||||
digit.toWidth = digit.to.unicode() ? _font->m.width(digit.to) : 0;
|
||||
if (digit.from != digit.to) {
|
||||
animating = true;
|
||||
}
|
||||
if (!digit.from.unicode()) {
|
||||
--oldSize;
|
||||
}
|
||||
}
|
||||
_fromWidth = oldSize * _digitWidth;
|
||||
_toWidth = newSize * _digitWidth;
|
||||
if (animating) {
|
||||
_a_ready.start(
|
||||
[this] { animationCallback(); },
|
||||
0.,
|
||||
1.,
|
||||
st::slideWrapDuration);
|
||||
}
|
||||
}
|
||||
|
||||
int NumbersAnimation::countWidth() const {
|
||||
return anim::interpolate(
|
||||
_fromWidth,
|
||||
_toWidth,
|
||||
anim::easeOutCirc(1., _a_ready.current(1.)));
|
||||
}
|
||||
|
||||
void NumbersAnimation::stepAnimation(TimeMs ms) {
|
||||
_a_ready.step(ms);
|
||||
}
|
||||
|
||||
void NumbersAnimation::finishAnimating() {
|
||||
auto width = countWidth();
|
||||
_a_ready.finish();
|
||||
if (_widthChangedCallback && countWidth() != width) {
|
||||
_widthChangedCallback();
|
||||
}
|
||||
if (!_delayedText.isEmpty()) {
|
||||
setText(_delayedText, _delayedValue);
|
||||
}
|
||||
}
|
||||
|
||||
void NumbersAnimation::paint(Painter &p, int x, int y, int outerWidth) {
|
||||
auto digitsCount = _digits.size();
|
||||
if (!digitsCount) return;
|
||||
|
||||
auto progress = anim::easeOutCirc(1., _a_ready.current(1.));
|
||||
auto width = anim::interpolate(_fromWidth, _toWidth, progress);
|
||||
|
||||
QString singleChar('0');
|
||||
if (rtl()) x = outerWidth - x - width;
|
||||
x += width - _digits.size() * _digitWidth;
|
||||
auto fromTop = anim::interpolate(0, _font->height, progress) * (_growing ? 1 : -1);
|
||||
auto toTop = anim::interpolate(_font->height, 0, progress) * (_growing ? -1 : 1);
|
||||
for (auto i = 0; i != digitsCount; ++i) {
|
||||
auto &digit = _digits[i];
|
||||
auto from = digit.from;
|
||||
auto to = digit.to;
|
||||
if (from == to) {
|
||||
p.setOpacity(1.);
|
||||
singleChar[0] = from;
|
||||
p.drawText(x + (_digitWidth - digit.fromWidth) / 2, y + _font->ascent, singleChar);
|
||||
} else {
|
||||
if (from.unicode()) {
|
||||
p.setOpacity(1. - progress);
|
||||
singleChar[0] = from;
|
||||
p.drawText(x + (_digitWidth - digit.fromWidth) / 2, y + fromTop + _font->ascent, singleChar);
|
||||
}
|
||||
if (to.unicode()) {
|
||||
p.setOpacity(progress);
|
||||
singleChar[0] = to;
|
||||
p.drawText(x + (_digitWidth - digit.toWidth) / 2, y + toTop + _font->ascent, singleChar);
|
||||
}
|
||||
}
|
||||
x += _digitWidth;
|
||||
}
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
|
||||
LabelWithNumbers::LabelWithNumbers(
|
||||
QWidget *parent,
|
||||
const style::FlatLabel &st,
|
||||
int textTop,
|
||||
const StringWithNumbers &value)
|
||||
: RpWidget(parent)
|
||||
, _st(st)
|
||||
, _textTop(textTop)
|
||||
, _before(GetBefore(value))
|
||||
, _after(GetAfter(value))
|
||||
, _numbers(_st.style.font, [this] { update(); })
|
||||
, _beforeWidth(_st.style.font->width(_before))
|
||||
, _afterWidth(st.style.font->width(_after)) {
|
||||
Expects((value.offset < 0) == (value.length == 0));
|
||||
|
||||
const auto numbers = GetNumbers(value);
|
||||
_numbers.setText(numbers, numbers.toInt());
|
||||
_numbers.finishAnimating();
|
||||
}
|
||||
|
||||
QString LabelWithNumbers::GetBefore(const StringWithNumbers &value) {
|
||||
return value.text.mid(0, value.offset);
|
||||
}
|
||||
|
||||
QString LabelWithNumbers::GetAfter(const StringWithNumbers &value) {
|
||||
return (value.offset >= 0)
|
||||
? value.text.mid(value.offset + value.length)
|
||||
: QString();
|
||||
}
|
||||
|
||||
QString LabelWithNumbers::GetNumbers(const StringWithNumbers &value) {
|
||||
return (value.offset >= 0)
|
||||
? value.text.mid(value.offset, value.length)
|
||||
: QString();
|
||||
}
|
||||
|
||||
void LabelWithNumbers::setValue(const StringWithNumbers &value) {
|
||||
_before = GetBefore(value);
|
||||
_after = GetAfter(value);
|
||||
const auto numbers = GetNumbers(value);
|
||||
_numbers.setText(numbers, numbers.toInt());
|
||||
|
||||
const auto oldBeforeWidth = std::exchange(
|
||||
_beforeWidth,
|
||||
_st.style.font->width(_before));
|
||||
_beforeWidthAnimation.start(
|
||||
[this] { update(); },
|
||||
oldBeforeWidth,
|
||||
_beforeWidth,
|
||||
st::slideWrapDuration,
|
||||
anim::easeOutCirc);
|
||||
|
||||
_afterWidth = _st.style.font->width(_after);
|
||||
}
|
||||
|
||||
void LabelWithNumbers::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
const auto ms = getms();
|
||||
const auto beforeWidth = _beforeWidthAnimation.current(ms, _beforeWidth);
|
||||
_numbers.stepAnimation(ms);
|
||||
|
||||
p.setFont(_st.style.font);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.setPen(_st.textFg);
|
||||
auto left = 0;
|
||||
const auto outerWidth = width();
|
||||
|
||||
p.setClipRect(0, 0, left + beforeWidth, height());
|
||||
p.drawTextLeft(left, _textTop, outerWidth, _before, _beforeWidth);
|
||||
left += beforeWidth;
|
||||
p.setClipping(false);
|
||||
|
||||
_numbers.paint(p, left, _textTop, outerWidth);
|
||||
left += _numbers.countWidth();
|
||||
|
||||
const auto availableWidth = outerWidth - left;
|
||||
const auto text = (availableWidth < _afterWidth)
|
||||
? _st.style.font->elided(_after, availableWidth)
|
||||
: _after;
|
||||
const auto textWidth = (availableWidth < _afterWidth) ? -1 : _afterWidth;
|
||||
p.drawTextLeft(left, _textTop, outerWidth, text, textWidth);
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
namespace Lang {
|
||||
|
||||
Ui::StringWithNumbers ReplaceTag<Ui::StringWithNumbers>::Call(
|
||||
Ui::StringWithNumbers &&original,
|
||||
ushort tag,
|
||||
const Ui::StringWithNumbers &replacement) {
|
||||
original.offset = FindTagReplacementPosition(original.text, tag);
|
||||
original.text = ReplaceTag<QString>::Call(
|
||||
std::move(original.text),
|
||||
tag,
|
||||
replacement.text);
|
||||
original.length = replacement.text.size();
|
||||
return std::move(original);
|
||||
}
|
||||
|
||||
} // namespace Lang
|
138
Telegram/SourceFiles/ui/effects/numbers_animation.h
Normal file
138
Telegram/SourceFiles/ui/effects/numbers_animation.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
namespace style {
|
||||
struct FlatLabel;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class NumbersAnimation {
|
||||
public:
|
||||
NumbersAnimation(
|
||||
const style::font &font,
|
||||
base::lambda<void()> animationCallback);
|
||||
|
||||
void setWidthChangedCallback(base::lambda<void()> callback) {
|
||||
_widthChangedCallback = std::move(callback);
|
||||
}
|
||||
void setText(const QString &text, int value);
|
||||
void stepAnimation(TimeMs ms);
|
||||
void finishAnimating();
|
||||
|
||||
void paint(Painter &p, int x, int y, int outerWidth);
|
||||
int countWidth() const;
|
||||
|
||||
private:
|
||||
struct Digit {
|
||||
QChar from = 0;
|
||||
QChar to = 0;
|
||||
int fromWidth = 0;
|
||||
int toWidth = 0;
|
||||
};
|
||||
|
||||
void animationCallback();
|
||||
void realSetText(QString text, int value);
|
||||
|
||||
const style::font &_font;
|
||||
|
||||
QList<Digit> _digits;
|
||||
int _digitWidth = 0;
|
||||
|
||||
int _fromWidth = 0;
|
||||
int _toWidth = 0;
|
||||
|
||||
Animation _a_ready;
|
||||
QString _delayedText;
|
||||
int _delayedValue = 0;
|
||||
|
||||
int _value = 0;
|
||||
bool _growing = false;
|
||||
|
||||
base::lambda<void()> _animationCallback;
|
||||
base::lambda<void()> _widthChangedCallback;
|
||||
|
||||
};
|
||||
|
||||
struct StringWithNumbers {
|
||||
QString text;
|
||||
int offset = -1;
|
||||
int length = 0;
|
||||
};
|
||||
|
||||
class LabelWithNumbers : public Ui::RpWidget {
|
||||
public:
|
||||
LabelWithNumbers(
|
||||
QWidget *parent,
|
||||
const style::FlatLabel &st,
|
||||
int textTop,
|
||||
const StringWithNumbers &value);
|
||||
|
||||
void setValue(const StringWithNumbers &value);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
static QString GetBefore(const StringWithNumbers &value);
|
||||
static QString GetAfter(const StringWithNumbers &value);
|
||||
static QString GetNumbers(const StringWithNumbers &value);
|
||||
|
||||
const style::FlatLabel &_st;
|
||||
int _textTop;
|
||||
QString _before;
|
||||
QString _after;
|
||||
NumbersAnimation _numbers;
|
||||
int _beforeWidth = 0;
|
||||
int _afterWidth = 0;
|
||||
Animation _beforeWidthAnimation;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
namespace Lang {
|
||||
|
||||
template <typename ResultString>
|
||||
struct StartReplacements;
|
||||
|
||||
template <>
|
||||
struct StartReplacements<Ui::StringWithNumbers> {
|
||||
static inline Ui::StringWithNumbers Call(QString &&langString) {
|
||||
return { std::move(langString) };
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ResultString>
|
||||
struct ReplaceTag;
|
||||
|
||||
template <>
|
||||
struct ReplaceTag<Ui::StringWithNumbers> {
|
||||
static Ui::StringWithNumbers Call(
|
||||
Ui::StringWithNumbers &&original,
|
||||
ushort tag,
|
||||
const Ui::StringWithNumbers &replacement);
|
||||
};
|
||||
|
||||
} // namespace Lang
|
|
@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/effects/cross_animation.h"
|
||||
#include "ui/effects/numbers_animation.h"
|
||||
#include "lang/lang_instance.h"
|
||||
|
||||
namespace Ui {
|
||||
|
@ -192,168 +193,6 @@ void FlatButton::paintEvent(QPaintEvent *e) {
|
|||
p.drawText(r, _text, style::al_top);
|
||||
}
|
||||
|
||||
class RoundButton::Numbers {
|
||||
public:
|
||||
Numbers(const style::RoundButton &st, base::lambda<void()> animationCallback);
|
||||
|
||||
void setWidthChangedCallback(base::lambda<void()> callback) {
|
||||
_widthChangedCallback = std::move(callback);
|
||||
}
|
||||
void setText(const QString &text, int value);
|
||||
void stepAnimation(TimeMs ms);
|
||||
void finishAnimating();
|
||||
|
||||
void paint(Painter &p, int x, int y, int outerWidth);
|
||||
int countWidth() const;
|
||||
|
||||
private:
|
||||
struct Digit {
|
||||
QChar from = 0;
|
||||
QChar to = 0;
|
||||
int fromWidth = 0;
|
||||
int toWidth = 0;
|
||||
};
|
||||
|
||||
void animationCallback();
|
||||
void realSetText(QString text, int value);
|
||||
|
||||
const style::RoundButton &_st;
|
||||
|
||||
QList<Digit> _digits;
|
||||
int _digitWidth = 0;
|
||||
|
||||
int _fromWidth = 0;
|
||||
int _toWidth = 0;
|
||||
|
||||
Animation _a_ready;
|
||||
QString _delayedText;
|
||||
int _delayedValue = 0;
|
||||
|
||||
int _value = 0;
|
||||
bool _growing = false;
|
||||
|
||||
base::lambda<void()> _animationCallback;
|
||||
base::lambda<void()> _widthChangedCallback;
|
||||
|
||||
};
|
||||
|
||||
RoundButton::Numbers::Numbers(const style::RoundButton &st, base::lambda<void()> animationCallback)
|
||||
: _st(st)
|
||||
, _animationCallback(std::move(animationCallback)) {
|
||||
for (auto ch = '0'; ch != '9'; ++ch) {
|
||||
accumulate_max(_digitWidth, _st.font->m.width(ch));
|
||||
}
|
||||
}
|
||||
|
||||
void RoundButton::Numbers::setText(const QString &text, int value) {
|
||||
if (_a_ready.animating(getms())) {
|
||||
_delayedText = text;
|
||||
_delayedValue = value;
|
||||
} else {
|
||||
realSetText(text, value);
|
||||
}
|
||||
}
|
||||
|
||||
void RoundButton::Numbers::animationCallback() {
|
||||
if (_animationCallback) {
|
||||
_animationCallback();
|
||||
}
|
||||
if (_widthChangedCallback) {
|
||||
_widthChangedCallback();
|
||||
}
|
||||
if (!_a_ready.animating()) {
|
||||
if (!_delayedText.isEmpty()) {
|
||||
setText(_delayedText, _delayedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RoundButton::Numbers::realSetText(QString text, int value) {
|
||||
_delayedText = QString();
|
||||
_delayedValue = 0;
|
||||
|
||||
_growing = (value > _value);
|
||||
_value = value;
|
||||
|
||||
auto newSize = text.size();
|
||||
while (_digits.size() < newSize) {
|
||||
_digits.push_front(Digit());
|
||||
}
|
||||
while (_digits.size() > newSize && !_digits.front().to.unicode()) {
|
||||
_digits.pop_front();
|
||||
}
|
||||
auto oldSize = _digits.size();
|
||||
auto animating = false;
|
||||
for (auto i = 0, size = _digits.size(); i != size; ++i) {
|
||||
auto &digit = _digits[i];
|
||||
digit.from = digit.to;
|
||||
digit.fromWidth = digit.toWidth;
|
||||
digit.to = (newSize + i < size) ? QChar(0) : text[newSize + i - size];
|
||||
digit.toWidth = digit.to.unicode() ? _st.font->m.width(digit.to) : 0;
|
||||
if (digit.from != digit.to) {
|
||||
animating = true;
|
||||
}
|
||||
if (!digit.from.unicode()) {
|
||||
--oldSize;
|
||||
}
|
||||
}
|
||||
_fromWidth = oldSize * _digitWidth;
|
||||
_toWidth = newSize * _digitWidth;
|
||||
if (animating) {
|
||||
_a_ready.start([this] { animationCallback(); }, 0., 1., _st.numbersDuration);
|
||||
}
|
||||
}
|
||||
|
||||
int RoundButton::Numbers::countWidth() const {
|
||||
return anim::interpolate(_fromWidth, _toWidth, anim::easeOutCirc(1., _a_ready.current(1.)));
|
||||
}
|
||||
|
||||
void RoundButton::Numbers::stepAnimation(TimeMs ms) {
|
||||
_a_ready.step(ms);
|
||||
}
|
||||
|
||||
void RoundButton::Numbers::finishAnimating() {
|
||||
auto width = countWidth();
|
||||
_a_ready.finish();
|
||||
if (_widthChangedCallback && countWidth() != width) {
|
||||
_widthChangedCallback();
|
||||
}
|
||||
if (!_delayedText.isEmpty()) {
|
||||
setText(_delayedText, _delayedValue);
|
||||
}
|
||||
}
|
||||
|
||||
void RoundButton::Numbers::paint(Painter &p, int x, int y, int outerWidth) {
|
||||
auto digitsCount = _digits.size();
|
||||
if (!digitsCount) return;
|
||||
|
||||
auto progress = anim::easeOutCirc(1., _a_ready.current(1.));
|
||||
auto width = anim::interpolate(_fromWidth, _toWidth, progress);
|
||||
|
||||
QString singleChar('0');
|
||||
if (rtl()) x = outerWidth - x - width;
|
||||
x += width - _digits.size() * _digitWidth;
|
||||
auto fromTop = anim::interpolate(0, _st.font->height, progress) * (_growing ? 1 : -1);
|
||||
auto toTop = anim::interpolate(_st.font->height, 0, progress) * (_growing ? -1 : 1);
|
||||
for (auto i = 0; i != digitsCount; ++i) {
|
||||
auto &digit = _digits[i];
|
||||
auto from = digit.from;
|
||||
auto to = digit.to;
|
||||
if (from.unicode()) {
|
||||
p.setOpacity(1. - progress);
|
||||
singleChar[0] = from;
|
||||
p.drawText(x + (_digitWidth - digit.fromWidth) / 2, y + fromTop + _st.font->ascent, singleChar);
|
||||
}
|
||||
if (to.unicode()) {
|
||||
p.setOpacity(progress);
|
||||
singleChar[0] = to;
|
||||
p.drawText(x + (_digitWidth - digit.toWidth) / 2, y + toTop + _st.font->ascent, singleChar);
|
||||
}
|
||||
x += _digitWidth;
|
||||
}
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
|
||||
RoundButton::RoundButton(QWidget *parent, base::lambda<QString()> textFactory, const style::RoundButton &st) : RippleButton(parent, st.ripple)
|
||||
, _textFactory(std::move(textFactory))
|
||||
, _st(st) {
|
||||
|
@ -376,7 +215,9 @@ void RoundButton::setNumbersText(const QString &numbersText, int numbers) {
|
|||
_numbers.reset();
|
||||
} else {
|
||||
if (!_numbers) {
|
||||
_numbers = std::make_unique<Numbers>(_st, [this] { numbersAnimationCallback(); });
|
||||
_numbers = std::make_unique<NumbersAnimation>(_st.font, [this] {
|
||||
numbersAnimationCallback();
|
||||
});
|
||||
}
|
||||
_numbers->setText(numbersText, numbers);
|
||||
}
|
||||
|
@ -385,7 +226,9 @@ void RoundButton::setNumbersText(const QString &numbersText, int numbers) {
|
|||
|
||||
void RoundButton::setWidthChangedCallback(base::lambda<void()> callback) {
|
||||
if (!_numbers) {
|
||||
_numbers = std::make_unique<Numbers>(_st, [this] { numbersAnimationCallback(); });
|
||||
_numbers = std::make_unique<NumbersAnimation>(_st.font, [this] {
|
||||
numbersAnimationCallback();
|
||||
});
|
||||
}
|
||||
_numbers->setWidthChangedCallback(std::move(callback));
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
namespace Ui {
|
||||
|
||||
class RippleAnimation;
|
||||
class NumbersAnimation;
|
||||
|
||||
class LinkButton : public AbstractButton {
|
||||
public:
|
||||
|
@ -152,8 +153,7 @@ private:
|
|||
base::lambda<QString()> _textFactory;
|
||||
int _textWidth;
|
||||
|
||||
class Numbers;
|
||||
std::unique_ptr<Numbers> _numbers;
|
||||
std::unique_ptr<NumbersAnimation> _numbers;
|
||||
|
||||
int _fullWidthOverride = 0;
|
||||
|
||||
|
|
|
@ -77,7 +77,6 @@ RoundButton {
|
|||
numbersTextFg: color;
|
||||
numbersTextFgOver: color;
|
||||
numbersSkip: pixels;
|
||||
numbersDuration: int;
|
||||
|
||||
width: pixels;
|
||||
height: pixels;
|
||||
|
@ -564,7 +563,6 @@ defaultActiveButton: RoundButton {
|
|||
textBgOver: activeButtonBgOver;
|
||||
|
||||
numbersSkip: 7px;
|
||||
numbersDuration: 200;
|
||||
|
||||
width: -34px;
|
||||
height: 34px;
|
||||
|
|
|
@ -304,7 +304,6 @@ topBarInfoButton: UserpicButton(defaultUserpicButton) {
|
|||
photoSize: 42px;
|
||||
photoPosition: point(2px, -1px);
|
||||
}
|
||||
topBarSlideDuration: 200;
|
||||
|
||||
themeEditorSampleSize: size(90px, 51px);
|
||||
themeEditorMargin: margins(17px, 10px, 17px, 10px);
|
||||
|
|
|
@ -510,6 +510,8 @@
|
|||
<(src_loc)/ui/effects/cross_animation.h
|
||||
<(src_loc)/ui/effects/fade_animation.cpp
|
||||
<(src_loc)/ui/effects/fade_animation.h
|
||||
<(src_loc)/ui/effects/numbers_animation.cpp
|
||||
<(src_loc)/ui/effects/numbers_animation.h
|
||||
<(src_loc)/ui/effects/panel_animation.cpp
|
||||
<(src_loc)/ui/effects/panel_animation.h
|
||||
<(src_loc)/ui/effects/radial_animation.cpp
|
||||
|
|
Loading…
Add table
Reference in a new issue