diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 7b30ec996..8c31ef74e 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -405,6 +405,7 @@ public: struct SendActionAnimationUpdate { not_null history; + int left = 0; int width = 0; int height = 0; bool textUpdated = false; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index d974eadaa..bb173417c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -171,6 +171,7 @@ InnerWidget::InnerWidget( const Data::Session::SendActionAnimationUpdate &update) { using RowPainter = Layout::RowPainter; const auto updateRect = RowPainter::sendActionAnimationRect( + update.left, update.width, update.height, width(), diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 0f6da8538..82f3d649a 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -943,11 +943,24 @@ void RowPainter::paint( paintCounterCallback); } -QRect RowPainter::sendActionAnimationRect(int animationWidth, int animationHeight, int fullWidth, bool textUpdated) { - auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; - auto namewidth = fullWidth - nameleft - st::dialogsPadding.x(); - auto texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; - return QRect(nameleft, texttop, textUpdated ? namewidth : animationWidth, animationHeight); +QRect RowPainter::sendActionAnimationRect( + int animationLeft, + int animationWidth, + int 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( diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.h b/Telegram/SourceFiles/dialogs/dialogs_layout.h index 4fb7027fe..f3f7a4ea1 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.h +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.h @@ -39,6 +39,7 @@ public: crl::time ms, bool displayUnreadInfo); static QRect sendActionAnimationRect( + int animationLeft, int animationWidth, int animationHeight, int fullWidth, diff --git a/Telegram/SourceFiles/history/view/history_view_send_action.cpp b/Telegram/SourceFiles/history/view/history_view_send_action.cpp index 4a0641c4d..2c9440358 100644 --- a/Telegram/SourceFiles/history/view/history_view_send_action.cpp +++ b/Telegram/SourceFiles/history/view/history_view_send_action.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "main/main_session.h" #include "history/history.h" +#include "lang/lang_instance.h" // Instance::supportChoosingStickerReplacement #include "lang/lang_keys.h" #include "ui/effects/animations.h" #include "ui/text/text_options.h" @@ -39,6 +40,7 @@ constexpr auto kStatusShowClientsideSpeaking = 6 * crl::time(1000); SendActionPainter::SendActionPainter(not_null history) : _history(history) , _weak(&_history->session()) +, _st(st::dialogsTextStyle) , _sendActionText(st::dialogsTextWidthMin) { } @@ -130,16 +132,29 @@ bool SendActionPainter::paint( style::color color, crl::time ms) { if (_sendActionAnimation) { + const auto animationWidth = _sendActionAnimation.width(); + const auto extraAnimationWidth = _animationLeft + ? animationWidth * 2 + : 0; + const auto left = + (availableWidth < _animationLeft + extraAnimationWidth) + ? 0 + : _animationLeft; _sendActionAnimation.paint( p, color, - x, + left + x, y + st::normalFont->ascent, outerWidth, ms); - auto animationWidth = _sendActionAnimation.width(); - x += animationWidth; - availableWidth -= animationWidth; + // availableWidth should be the same + // if an animation is in the middle of text. + if (!left) { + x += animationWidth; + availableWidth -= _animationLeft + ? extraAnimationWidth + : animationWidth; + } p.setPen(color); _sendActionText.drawElided(p, x, y, availableWidth); return true; @@ -270,6 +285,30 @@ bool SendActionPainter::updateNeedsAnimating(crl::time now, bool force) { _history->peer->isUser() ? QString() : user->firstName); if (!newTypingString.isEmpty()) { _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; } } @@ -327,6 +366,7 @@ bool SendActionPainter::updateNeedsAnimating(crl::time now, bool force) { || (sendActionResult && !anim::Disabled())) { _history->peer->owner().updateSendActionAnimation({ _history, + _animationLeft, _sendActionAnimation.width(), st::normalFont->height, (force || sendActionChanged) diff --git a/Telegram/SourceFiles/history/view/history_view_send_action.h b/Telegram/SourceFiles/history/view/history_view_send_action.h index 4eb559809..249af8995 100644 --- a/Telegram/SourceFiles/history/view/history_view_send_action.h +++ b/Telegram/SourceFiles/history/view/history_view_send_action.h @@ -54,6 +54,7 @@ public: private: const not_null _history; const base::weak_ptr _weak; + const style::TextStyle &_st; base::flat_map, crl::time> _typing; base::flat_map, crl::time> _speaking; base::flat_map, Api::SendProgress> _sendActions; @@ -62,6 +63,9 @@ private: Ui::SendActionAnimation _sendActionAnimation; Ui::SendActionAnimation _speakingAnimation; + int _animationLeft = 0; + int _spacesCount = 0; + }; } // namespace HistoryView diff --git a/Telegram/SourceFiles/lang/lang_instance.cpp b/Telegram/SourceFiles/lang/lang_instance.cpp index ab32ffeae..6701f8aa0 100644 --- a/Telegram/SourceFiles/lang/lang_instance.cpp +++ b/Telegram/SourceFiles/lang/lang_instance.cpp @@ -303,6 +303,7 @@ void Instance::reset(const Language &data) { _values[i] = GetOriginalValue(ushort(i)); } ranges::fill(_nonDefaultSet, 0); + updateSupportChoosingStickerReplacement(); _idChanges.fire_copy(_id); } @@ -548,6 +549,7 @@ void Instance::fillFromSerialized( applyValue(nonDefaultStrings[i], nonDefaultStrings[i + 1]); } updatePluralRules(); + updateSupportChoosingStickerReplacement(); _idChanges.fire_copy(_id); } @@ -572,6 +574,7 @@ void Instance::fillFromCustomContent( _pluralId = PluralCodeForCustom(absolutePath, relativePath); _name = _nativeName = QString(); loadFromCustomContent(absolutePath, relativePath, content); + updateSupportChoosingStickerReplacement(); _idChanges.fire_copy(_id); } @@ -602,6 +605,20 @@ bool Instance::loadFromCustomFile(const QString &filePath) { 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. // It is called for all key-value pairs in string. // ResetCallback takes one QByteArray: key. diff --git a/Telegram/SourceFiles/lang/lang_instance.h b/Telegram/SourceFiles/lang/lang_instance.h index cb563b7b1..961f17df9 100644 --- a/Telegram/SourceFiles/lang/lang_instance.h +++ b/Telegram/SourceFiles/lang/lang_instance.h @@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Lang { +inline constexpr auto kChoosingStickerReplacement = "oo"_cs; + struct Language { QString id; QString pluralId; @@ -74,6 +76,8 @@ public: QByteArray serialize() const; void fillFromSerialized(const QByteArray &data, int dataAppVersion); + bool supportChoosingStickerReplacement() const; + void applyDifference( Pack pack, const MTPDlangPackDifference &difference); @@ -120,6 +124,7 @@ private: const QString &relativePath, const QByteArray &content); void updatePluralRules(); + void updateSupportChoosingStickerReplacement(); Instance *_derived = nullptr; @@ -132,6 +137,8 @@ private: int _version = 0; rpl::event_stream<> _updated; + bool _supportChoosingStickerReplacement; + mutable QString _systemLanguage; std::vector _values;