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);
|
setCursor(hasSelected ? style::cur_default : style::cur_pointer);
|
||||||
|
|
||||||
updateMembersShowArea();
|
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 {
|
} else {
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "info/info_wrap_widget.h"
|
#include "info/info_wrap_widget.h"
|
||||||
#include "storage/storage_shared_media.h"
|
#include "storage/storage_shared_media.h"
|
||||||
|
#include "ui/effects/numbers_animation.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/labels.h"
|
|
||||||
#include "ui/wrap/fade_wrap.h"
|
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
|
#include "ui/wrap/fade_wrap.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "boxes/peer_list_controllers.h"
|
#include "boxes/peer_list_controllers.h"
|
||||||
|
@ -44,7 +44,7 @@ TopBarOverride::TopBarOverride(
|
||||||
, _items(std::move(items))
|
, _items(std::move(items))
|
||||||
, _canDelete(computeCanDelete())
|
, _canDelete(computeCanDelete())
|
||||||
, _cancel(this, _st.mediaCancel)
|
, _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)
|
, _forward(this, _st.mediaForward)
|
||||||
, _delete(this, _st.mediaDelete) {
|
, _delete(this, _st.mediaDelete) {
|
||||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
|
@ -55,17 +55,18 @@ TopBarOverride::TopBarOverride(
|
||||||
_delete->addClickHandler([this] { performDelete(); });
|
_delete->addClickHandler([this] { performDelete(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TopBarOverride::generateText() const {
|
Ui::StringWithNumbers TopBarOverride::generateText() const {
|
||||||
|
using Data = Ui::StringWithNumbers;
|
||||||
using Type = Storage::SharedMediaType;
|
using Type = Storage::SharedMediaType;
|
||||||
auto phrase = [&] {
|
auto phrase = [&] {
|
||||||
switch (_items.type) {
|
switch (_items.type) {
|
||||||
case Type::Photo: return lng_media_selected_photo;
|
case Type::Photo: return lng_media_selected_photo__generic<Data>;
|
||||||
case Type::Video: return lng_media_selected_video;
|
case Type::Video: return lng_media_selected_video__generic<Data>;
|
||||||
case Type::File: return lng_media_selected_file;
|
case Type::File: return lng_media_selected_file__generic<Data>;
|
||||||
case Type::MusicFile: return lng_media_selected_song;
|
case Type::MusicFile: return lng_media_selected_song__generic<Data>;
|
||||||
case Type::Link: return lng_media_selected_link;
|
case Type::Link: return lng_media_selected_link__generic<Data>;
|
||||||
case Type::VoiceFile: return lng_media_selected_audio;
|
case Type::VoiceFile: return lng_media_selected_audio__generic<Data>;
|
||||||
// case Type::RoundFile: return lng_media_selected_round;
|
// case Type::RoundFile: return lng_media_selected_round__generic<Data>;
|
||||||
}
|
}
|
||||||
Unexpected("Type in TopBarOverride::generateText()");
|
Unexpected("Type in TopBarOverride::generateText()");
|
||||||
}();
|
}();
|
||||||
|
@ -82,7 +83,7 @@ void TopBarOverride::setItems(SelectedItems &&items) {
|
||||||
_items = std::move(items);
|
_items = std::move(items);
|
||||||
_canDelete = computeCanDelete();
|
_canDelete = computeCanDelete();
|
||||||
|
|
||||||
_text->setText(generateText());
|
_text->setValue(generateText());
|
||||||
updateControlsVisibility();
|
updateControlsVisibility();
|
||||||
updateControlsGeometry(width());
|
updateControlsGeometry(width());
|
||||||
}
|
}
|
||||||
|
@ -110,8 +111,14 @@ void TopBarOverride::updateControlsGeometry(int newWidth) {
|
||||||
right += _delete->width();
|
right += _delete->width();
|
||||||
}
|
}
|
||||||
_forward->moveToRight(right, 0, newWidth);
|
_forward->moveToRight(right, 0, newWidth);
|
||||||
_cancel->moveToLeft(0, 0);
|
right += _forward->width();
|
||||||
_text->moveToLeft(_cancel->width(), _st.titlePosition.y());
|
|
||||||
|
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() {
|
void TopBarOverride::updateControlsVisibility() {
|
||||||
|
|
|
@ -30,7 +30,8 @@ struct InfoTopBar;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class IconButton;
|
class IconButton;
|
||||||
class FlatLabel;
|
class LabelWithNumbers;
|
||||||
|
struct StringWithNumbers;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Info {
|
namespace Info {
|
||||||
|
@ -54,7 +55,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
void updateControlsVisibility();
|
void updateControlsVisibility();
|
||||||
void updateControlsGeometry(int newWidth);
|
void updateControlsGeometry(int newWidth);
|
||||||
QString generateText() const;
|
Ui::StringWithNumbers generateText() const;
|
||||||
[[nodiscard]] bool computeCanDelete() const;
|
[[nodiscard]] bool computeCanDelete() const;
|
||||||
[[nodiscard]] SelectedItemSet collectItems() const;
|
[[nodiscard]] SelectedItemSet collectItems() const;
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ private:
|
||||||
SelectedItems _items;
|
SelectedItems _items;
|
||||||
bool _canDelete = false;
|
bool _canDelete = false;
|
||||||
object_ptr<Ui::IconButton> _cancel;
|
object_ptr<Ui::IconButton> _cancel;
|
||||||
object_ptr<Ui::FlatLabel> _text;
|
object_ptr<Ui::LabelWithNumbers> _text;
|
||||||
object_ptr<Ui::IconButton> _forward;
|
object_ptr<Ui::IconButton> _forward;
|
||||||
object_ptr<Ui::IconButton> _delete;
|
object_ptr<Ui::IconButton> _delete;
|
||||||
rpl::event_stream<> _correctionCancelRequests;
|
rpl::event_stream<> _correctionCancelRequests;
|
||||||
|
|
|
@ -46,7 +46,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
#include "styles/style_profile.h"
|
#include "styles/style_profile.h"
|
||||||
#include "styles/style_window.h"
|
|
||||||
|
|
||||||
namespace Info {
|
namespace Info {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -449,7 +448,7 @@ void WrapWidget::toggleTopBarOverride(bool shown) {
|
||||||
[this] { topBarOverrideStep(); },
|
[this] { topBarOverrideStep(); },
|
||||||
_topBarOverrideShown ? 0. : 1.,
|
_topBarOverrideShown ? 0. : 1.,
|
||||||
_topBarOverrideShown ? 1. : 0.,
|
_topBarOverrideShown ? 1. : 0.,
|
||||||
st::topBarSlideDuration,
|
st::slideWrapDuration,
|
||||||
anim::easeOutCirc);
|
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/ripple_animation.h"
|
||||||
#include "ui/effects/cross_animation.h"
|
#include "ui/effects/cross_animation.h"
|
||||||
|
#include "ui/effects/numbers_animation.h"
|
||||||
#include "lang/lang_instance.h"
|
#include "lang/lang_instance.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
@ -192,168 +193,6 @@ void FlatButton::paintEvent(QPaintEvent *e) {
|
||||||
p.drawText(r, _text, style::al_top);
|
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)
|
RoundButton::RoundButton(QWidget *parent, base::lambda<QString()> textFactory, const style::RoundButton &st) : RippleButton(parent, st.ripple)
|
||||||
, _textFactory(std::move(textFactory))
|
, _textFactory(std::move(textFactory))
|
||||||
, _st(st) {
|
, _st(st) {
|
||||||
|
@ -376,7 +215,9 @@ void RoundButton::setNumbersText(const QString &numbersText, int numbers) {
|
||||||
_numbers.reset();
|
_numbers.reset();
|
||||||
} else {
|
} else {
|
||||||
if (!_numbers) {
|
if (!_numbers) {
|
||||||
_numbers = std::make_unique<Numbers>(_st, [this] { numbersAnimationCallback(); });
|
_numbers = std::make_unique<NumbersAnimation>(_st.font, [this] {
|
||||||
|
numbersAnimationCallback();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_numbers->setText(numbersText, numbers);
|
_numbers->setText(numbersText, numbers);
|
||||||
}
|
}
|
||||||
|
@ -385,7 +226,9 @@ void RoundButton::setNumbersText(const QString &numbersText, int numbers) {
|
||||||
|
|
||||||
void RoundButton::setWidthChangedCallback(base::lambda<void()> callback) {
|
void RoundButton::setWidthChangedCallback(base::lambda<void()> callback) {
|
||||||
if (!_numbers) {
|
if (!_numbers) {
|
||||||
_numbers = std::make_unique<Numbers>(_st, [this] { numbersAnimationCallback(); });
|
_numbers = std::make_unique<NumbersAnimation>(_st.font, [this] {
|
||||||
|
numbersAnimationCallback();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_numbers->setWidthChangedCallback(std::move(callback));
|
_numbers->setWidthChangedCallback(std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
||||||
class RippleAnimation;
|
class RippleAnimation;
|
||||||
|
class NumbersAnimation;
|
||||||
|
|
||||||
class LinkButton : public AbstractButton {
|
class LinkButton : public AbstractButton {
|
||||||
public:
|
public:
|
||||||
|
@ -152,8 +153,7 @@ private:
|
||||||
base::lambda<QString()> _textFactory;
|
base::lambda<QString()> _textFactory;
|
||||||
int _textWidth;
|
int _textWidth;
|
||||||
|
|
||||||
class Numbers;
|
std::unique_ptr<NumbersAnimation> _numbers;
|
||||||
std::unique_ptr<Numbers> _numbers;
|
|
||||||
|
|
||||||
int _fullWidthOverride = 0;
|
int _fullWidthOverride = 0;
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,6 @@ RoundButton {
|
||||||
numbersTextFg: color;
|
numbersTextFg: color;
|
||||||
numbersTextFgOver: color;
|
numbersTextFgOver: color;
|
||||||
numbersSkip: pixels;
|
numbersSkip: pixels;
|
||||||
numbersDuration: int;
|
|
||||||
|
|
||||||
width: pixels;
|
width: pixels;
|
||||||
height: pixels;
|
height: pixels;
|
||||||
|
@ -564,7 +563,6 @@ defaultActiveButton: RoundButton {
|
||||||
textBgOver: activeButtonBgOver;
|
textBgOver: activeButtonBgOver;
|
||||||
|
|
||||||
numbersSkip: 7px;
|
numbersSkip: 7px;
|
||||||
numbersDuration: 200;
|
|
||||||
|
|
||||||
width: -34px;
|
width: -34px;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
|
|
|
@ -304,7 +304,6 @@ topBarInfoButton: UserpicButton(defaultUserpicButton) {
|
||||||
photoSize: 42px;
|
photoSize: 42px;
|
||||||
photoPosition: point(2px, -1px);
|
photoPosition: point(2px, -1px);
|
||||||
}
|
}
|
||||||
topBarSlideDuration: 200;
|
|
||||||
|
|
||||||
themeEditorSampleSize: size(90px, 51px);
|
themeEditorSampleSize: size(90px, 51px);
|
||||||
themeEditorMargin: margins(17px, 10px, 17px, 10px);
|
themeEditorMargin: margins(17px, 10px, 17px, 10px);
|
||||||
|
|
|
@ -510,6 +510,8 @@
|
||||||
<(src_loc)/ui/effects/cross_animation.h
|
<(src_loc)/ui/effects/cross_animation.h
|
||||||
<(src_loc)/ui/effects/fade_animation.cpp
|
<(src_loc)/ui/effects/fade_animation.cpp
|
||||||
<(src_loc)/ui/effects/fade_animation.h
|
<(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.cpp
|
||||||
<(src_loc)/ui/effects/panel_animation.h
|
<(src_loc)/ui/effects/panel_animation.h
|
||||||
<(src_loc)/ui/effects/radial_animation.cpp
|
<(src_loc)/ui/effects/radial_animation.cpp
|
||||||
|
|
Loading…
Add table
Reference in a new issue