Added animation of send action for stickers to middle of text.

This commit is contained in:
23rd 2021-08-30 20:38:11 +03:00
parent 8c17e3e578
commit 436212bb88
8 changed files with 93 additions and 9 deletions

View file

@ -405,6 +405,7 @@ public:
struct SendActionAnimationUpdate { struct SendActionAnimationUpdate {
not_null<History*> history; not_null<History*> history;
int left = 0;
int width = 0; int width = 0;
int height = 0; int height = 0;
bool textUpdated = false; bool textUpdated = false;

View file

@ -171,6 +171,7 @@ InnerWidget::InnerWidget(
const Data::Session::SendActionAnimationUpdate &update) { const Data::Session::SendActionAnimationUpdate &update) {
using RowPainter = Layout::RowPainter; using RowPainter = Layout::RowPainter;
const auto updateRect = RowPainter::sendActionAnimationRect( const auto updateRect = RowPainter::sendActionAnimationRect(
update.left,
update.width, update.width,
update.height, update.height,
width(), width(),

View file

@ -943,11 +943,24 @@ void RowPainter::paint(
paintCounterCallback); paintCounterCallback);
} }
QRect RowPainter::sendActionAnimationRect(int animationWidth, int animationHeight, int fullWidth, bool textUpdated) { QRect RowPainter::sendActionAnimationRect(
auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; int animationLeft,
auto namewidth = fullWidth - nameleft - st::dialogsPadding.x(); int animationWidth,
auto texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; int animationHeight,
return QRect(nameleft, texttop, textUpdated ? namewidth : animationWidth, animationHeight); int fullWidth,
bool textUpdated) {
const auto nameleft = st::dialogsPadding.x()
+ st::dialogsPhotoSize
+ st::dialogsPhotoPadding;
const auto namewidth = fullWidth - nameleft - st::dialogsPadding.x();
const auto texttop = st::dialogsPadding.y()
+ st::msgNameFont->height
+ st::dialogsSkip;
return QRect(
nameleft + (textUpdated ? 0 : animationLeft),
texttop,
textUpdated ? namewidth : animationWidth,
animationHeight);
} }
void PaintCollapsedRow( void PaintCollapsedRow(

View file

@ -39,6 +39,7 @@ public:
crl::time ms, crl::time ms,
bool displayUnreadInfo); bool displayUnreadInfo);
static QRect sendActionAnimationRect( static QRect sendActionAnimationRect(
int animationLeft,
int animationWidth, int animationWidth,
int animationHeight, int animationHeight,
int fullWidth, int fullWidth,

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "history/history.h" #include "history/history.h"
#include "lang/lang_instance.h" // Instance::supportChoosingStickerReplacement
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/text/text_options.h" #include "ui/text/text_options.h"
@ -39,6 +40,7 @@ constexpr auto kStatusShowClientsideSpeaking = 6 * crl::time(1000);
SendActionPainter::SendActionPainter(not_null<History*> history) SendActionPainter::SendActionPainter(not_null<History*> history)
: _history(history) : _history(history)
, _weak(&_history->session()) , _weak(&_history->session())
, _st(st::dialogsTextStyle)
, _sendActionText(st::dialogsTextWidthMin) { , _sendActionText(st::dialogsTextWidthMin) {
} }
@ -130,16 +132,29 @@ bool SendActionPainter::paint(
style::color color, style::color color,
crl::time ms) { crl::time ms) {
if (_sendActionAnimation) { if (_sendActionAnimation) {
const auto animationWidth = _sendActionAnimation.width();
const auto extraAnimationWidth = _animationLeft
? animationWidth * 2
: 0;
const auto left =
(availableWidth < _animationLeft + extraAnimationWidth)
? 0
: _animationLeft;
_sendActionAnimation.paint( _sendActionAnimation.paint(
p, p,
color, color,
x, left + x,
y + st::normalFont->ascent, y + st::normalFont->ascent,
outerWidth, outerWidth,
ms); ms);
auto animationWidth = _sendActionAnimation.width(); // availableWidth should be the same
x += animationWidth; // if an animation is in the middle of text.
availableWidth -= animationWidth; if (!left) {
x += animationWidth;
availableWidth -= _animationLeft
? extraAnimationWidth
: animationWidth;
}
p.setPen(color); p.setPen(color);
_sendActionText.drawElided(p, x, y, availableWidth); _sendActionText.drawElided(p, x, y, availableWidth);
return true; return true;
@ -270,6 +285,30 @@ bool SendActionPainter::updateNeedsAnimating(crl::time now, bool force) {
_history->peer->isUser() ? QString() : user->firstName); _history->peer->isUser() ? QString() : user->firstName);
if (!newTypingString.isEmpty()) { if (!newTypingString.isEmpty()) {
_sendActionAnimation.start(action.type); _sendActionAnimation.start(action.type);
// Add an animation to the middle of text.
using namespace Lang;
if (GetInstance().supportChoosingStickerReplacement()
&& (action.type == Type::ChooseSticker)) {
const auto index = newTypingString.lastIndexOf(
Lang::kChoosingStickerReplacement.utf8());
_animationLeft = (index == -1)
? 0
: _st.font->width(newTypingString, 0, index);
if (!_spacesCount) {
_spacesCount = std::ceil(
_sendActionAnimation.width()
/ _st.font->spacew);
}
newTypingString = newTypingString.replace(
Lang::kChoosingStickerReplacement.utf8(),
QString().fill(' ', _spacesCount));
} else {
_animationLeft = 0;
}
break; break;
} }
} }
@ -327,6 +366,7 @@ bool SendActionPainter::updateNeedsAnimating(crl::time now, bool force) {
|| (sendActionResult && !anim::Disabled())) { || (sendActionResult && !anim::Disabled())) {
_history->peer->owner().updateSendActionAnimation({ _history->peer->owner().updateSendActionAnimation({
_history, _history,
_animationLeft,
_sendActionAnimation.width(), _sendActionAnimation.width(),
st::normalFont->height, st::normalFont->height,
(force || sendActionChanged) (force || sendActionChanged)

View file

@ -54,6 +54,7 @@ public:
private: private:
const not_null<History*> _history; const not_null<History*> _history;
const base::weak_ptr<Main::Session> _weak; const base::weak_ptr<Main::Session> _weak;
const style::TextStyle &_st;
base::flat_map<not_null<UserData*>, crl::time> _typing; base::flat_map<not_null<UserData*>, crl::time> _typing;
base::flat_map<not_null<UserData*>, crl::time> _speaking; base::flat_map<not_null<UserData*>, crl::time> _speaking;
base::flat_map<not_null<UserData*>, Api::SendProgress> _sendActions; base::flat_map<not_null<UserData*>, Api::SendProgress> _sendActions;
@ -62,6 +63,9 @@ private:
Ui::SendActionAnimation _sendActionAnimation; Ui::SendActionAnimation _sendActionAnimation;
Ui::SendActionAnimation _speakingAnimation; Ui::SendActionAnimation _speakingAnimation;
int _animationLeft = 0;
int _spacesCount = 0;
}; };
} // namespace HistoryView } // namespace HistoryView

View file

@ -303,6 +303,7 @@ void Instance::reset(const Language &data) {
_values[i] = GetOriginalValue(ushort(i)); _values[i] = GetOriginalValue(ushort(i));
} }
ranges::fill(_nonDefaultSet, 0); ranges::fill(_nonDefaultSet, 0);
updateSupportChoosingStickerReplacement();
_idChanges.fire_copy(_id); _idChanges.fire_copy(_id);
} }
@ -548,6 +549,7 @@ void Instance::fillFromSerialized(
applyValue(nonDefaultStrings[i], nonDefaultStrings[i + 1]); applyValue(nonDefaultStrings[i], nonDefaultStrings[i + 1]);
} }
updatePluralRules(); updatePluralRules();
updateSupportChoosingStickerReplacement();
_idChanges.fire_copy(_id); _idChanges.fire_copy(_id);
} }
@ -572,6 +574,7 @@ void Instance::fillFromCustomContent(
_pluralId = PluralCodeForCustom(absolutePath, relativePath); _pluralId = PluralCodeForCustom(absolutePath, relativePath);
_name = _nativeName = QString(); _name = _nativeName = QString();
loadFromCustomContent(absolutePath, relativePath, content); loadFromCustomContent(absolutePath, relativePath, content);
updateSupportChoosingStickerReplacement();
_idChanges.fire_copy(_id); _idChanges.fire_copy(_id);
} }
@ -602,6 +605,20 @@ bool Instance::loadFromCustomFile(const QString &filePath) {
return false; return false;
} }
void Instance::updateSupportChoosingStickerReplacement() {
// A language changing in the runtime is not supported.
const auto phrase = tr::lng_send_action_choose_sticker(tr::now);
const auto first = phrase.indexOf(kChoosingStickerReplacement.utf8());
const auto last = phrase.lastIndexOf(kChoosingStickerReplacement.utf8());
_supportChoosingStickerReplacement = (first == last)
? (first != -1)
: false;
}
bool Instance::supportChoosingStickerReplacement() const {
return _supportChoosingStickerReplacement;
}
// SetCallback takes two QByteArrays: key, value. // SetCallback takes two QByteArrays: key, value.
// It is called for all key-value pairs in string. // It is called for all key-value pairs in string.
// ResetCallback takes one QByteArray: key. // ResetCallback takes one QByteArray: key.

View file

@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Lang { namespace Lang {
inline constexpr auto kChoosingStickerReplacement = "oo"_cs;
struct Language { struct Language {
QString id; QString id;
QString pluralId; QString pluralId;
@ -74,6 +76,8 @@ public:
QByteArray serialize() const; QByteArray serialize() const;
void fillFromSerialized(const QByteArray &data, int dataAppVersion); void fillFromSerialized(const QByteArray &data, int dataAppVersion);
bool supportChoosingStickerReplacement() const;
void applyDifference( void applyDifference(
Pack pack, Pack pack,
const MTPDlangPackDifference &difference); const MTPDlangPackDifference &difference);
@ -120,6 +124,7 @@ private:
const QString &relativePath, const QString &relativePath,
const QByteArray &content); const QByteArray &content);
void updatePluralRules(); void updatePluralRules();
void updateSupportChoosingStickerReplacement();
Instance *_derived = nullptr; Instance *_derived = nullptr;
@ -132,6 +137,8 @@ private:
int _version = 0; int _version = 0;
rpl::event_stream<> _updated; rpl::event_stream<> _updated;
bool _supportChoosingStickerReplacement;
mutable QString _systemLanguage; mutable QString _systemLanguage;
std::vector<QString> _values; std::vector<QString> _values;