diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 919a540d7b..a0fd1245af 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -106,20 +106,23 @@ using ItemPreview = HistoryView::ItemPreview; [[nodiscard]] QString WithCaptionDialogsText( const QString &attachType, - const QString &caption) { + const QString &caption, + bool hasMiniImages) { if (caption.isEmpty()) { return textcmdLink(1, TextUtilities::Clean(attachType)); } - return tr::lng_dialogs_text_media( - tr::now, - lt_media_part, - textcmdLink(1, tr::lng_dialogs_text_media_wrapped( + return hasMiniImages + ? TextUtilities::Clean(caption) + : tr::lng_dialogs_text_media( tr::now, - lt_media, - TextUtilities::Clean(attachType))), - lt_caption, - TextUtilities::Clean(caption)); + lt_media_part, + textcmdLink(1, tr::lng_dialogs_text_media_wrapped( + tr::now, + lt_media, + TextUtilities::Clean(attachType))), + lt_caption, + TextUtilities::Clean(caption)); } [[nodiscard]] QString WithCaptionNotificationText( @@ -491,10 +494,9 @@ ItemPreview MediaPhoto::toPreview(ToPreviewOptions options) const { context = media; } } + const auto type = tr::lng_in_dlg_photo(tr::now); return { - .text = WithCaptionDialogsText( - tr::lng_in_dlg_photo(tr::now), - caption), + .text = WithCaptionDialogsText(type, caption, !images.empty()), .images = std::move(images), .loadingContext = std::move(context), }; @@ -706,7 +708,7 @@ ItemPreview MediaFile::toPreview(ToPreviewOptions options) const { } } return { - .text = WithCaptionDialogsText(type, caption), + .text = WithCaptionDialogsText(type, caption, !images.empty()), .images = std::move(images), .loadingContext = std::move(context), }; @@ -1029,10 +1031,9 @@ Data::CloudImage *MediaLocation::location() const { } ItemPreview MediaLocation::toPreview(ToPreviewOptions options) const { - // #TODO minis generate images - return { - .text = WithCaptionDialogsText(tr::lng_maps_point(tr::now), _title), - }; + const auto type = tr::lng_maps_point(tr::now); + const auto hasMiniImages = false; + return { .text = WithCaptionDialogsText(type, _title, hasMiniImages) }; } QString MediaLocation::notificationText() const { diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 00148aac41..3a39b00984 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -296,6 +296,7 @@ dialogsScamFont: font(9px semibold); dialogsScamSkip: 4px; dialogsScamRadius: 2px; +dialogsMiniPreviewTop: 1px; dialogsMiniPreview: 16px; dialogsMiniPreviewSkip: 2px; dialogsMiniPreviewRight: 3px; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp index 0b088188a9..244b548fc5 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp @@ -12,8 +12,61 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "ui/text/text_options.h" #include "ui/image/image.h" +#include "lang/lang_keys.h" #include "styles/style_dialogs.h" +namespace { + +template +struct TextWithTagOffset { + TextWithTagOffset(QString text) : text(text) { + } + static TextWithTagOffset FromString(const QString &text) { + return { text }; + } + + QString text; + int offset = -1; +}; + +} // namespace + +namespace Lang { + +template +struct ReplaceTag> { + static TextWithTagOffset Call( + TextWithTagOffset &&original, + ushort tag, + const TextWithTagOffset &replacement); +}; + +template +TextWithTagOffset ReplaceTag>::Call( + TextWithTagOffset &&original, + ushort tag, + const TextWithTagOffset &replacement) { + const auto replacementPosition = FindTagReplacementPosition( + original.text, + tag); + if (replacementPosition < 0) { + return std::move(original); + } + original.text = ReplaceTag::Replace( + std::move(original.text), + replacement.text, + replacementPosition); + if (tag == kTag) { + original.offset = replacementPosition; + } else if (original.offset > replacementPosition) { + constexpr auto kReplaceCommandLength = 4; + original.offset += replacement.text.size() - kReplaceCommandLength; + } + return std::move(original); +} + +} // namespace Lang + namespace Dialogs::Ui { namespace { @@ -52,9 +105,16 @@ void MessageView::paint( } if (_textCachedFor != item.get()) { auto preview = item->toPreview(options); + if (!preview.images.empty() && preview.imagesInTextPosition > 0) { + _senderCache.setText( + st::dialogsTextStyle, + preview.text.mid(0, preview.imagesInTextPosition).trimmed(), + DialogTextOptions()); + preview.text = preview.text.mid(preview.imagesInTextPosition); + } _textCache.setText( st::dialogsTextStyle, - preview.text, + preview.text.trimmed(), DialogTextOptions()); _textCachedFor = item; _imagesCache = std::move(preview.images); @@ -71,12 +131,34 @@ void MessageView::paint( _loadingContext = nullptr; } } + p.setTextPalette(active + ? st::dialogsTextPaletteActive + : selected + ? st::dialogsTextPaletteOver + : st::dialogsTextPalette); + p.setFont(st::dialogsTextFont); + p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg)); + const auto guard = gsl::finally([&] { + p.restoreTextPalette(); + }); + auto rect = geometry; + if (!_senderCache.isEmpty()) { + _senderCache.drawElided( + p, + rect.left(), + rect.top(), + rect.width(), + rect.height() / st::dialogsTextFont->height); + const auto skip = st::dialogsMiniPreviewSkip + + st::dialogsMiniPreviewRight; + rect.setLeft(rect.x() + _senderCache.maxWidth() + skip); + } for (const auto &image : _imagesCache) { if (rect.width() < st::dialogsMiniPreview) { break; } - p.drawImage(rect.topLeft(), image); + p.drawImage(rect.x(), rect.y() + st::dialogsMiniPreviewTop, image); rect.setLeft(rect.x() + st::dialogsMiniPreview + st::dialogsMiniPreviewSkip); @@ -87,20 +169,29 @@ void MessageView::paint( if (rect.isEmpty()) { return; } - p.setTextPalette(active - ? st::dialogsTextPaletteActive - : selected - ? st::dialogsTextPaletteOver - : st::dialogsTextPalette); - p.setFont(st::dialogsTextFont); - p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg)); _textCache.drawElided( p, rect.left(), rect.top(), rect.width(), rect.height() / st::dialogsTextFont->height); - p.restoreTextPalette(); +} + +HistoryView::ItemPreview PreviewWithSender( + HistoryView::ItemPreview &&preview, + const QString &sender) { + auto textWithOffset = tr::lng_dialogs_text_with_from( + tr::now, + lt_from_part, + sender, + lt_message, + std::move(preview.text), + TextWithTagOffset::FromString); + preview.text = std::move(textWithOffset.text); + preview.imagesInTextPosition = (textWithOffset.offset < 0) + ? 0 + : textWithOffset.offset + sender.size(); + return preview; } } // namespace Dialogs::Ui diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h index cdf12965e8..ed679f464e 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h @@ -48,10 +48,15 @@ private: struct LoadingContext; mutable const HistoryItem *_textCachedFor = nullptr; + mutable Ui::Text::String _senderCache; mutable Ui::Text::String _textCache; mutable std::vector _imagesCache; mutable std::unique_ptr _loadingContext; }; +[[nodiscard]] HistoryView::ItemPreview PreviewWithSender( + HistoryView::ItemPreview &&preview, + const QString &sender); + } // namespace Dialogs::Ui diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 48033b7fad..3015ba3438 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/crash_reports.h" #include "base/unixtime.h" #include "api/api_text_entities.h" +#include "dialogs/ui/dialogs_message_view.h" #include "data/data_scheduled_messages.h" // kScheduledUntilOnlineTimestamp #include "data/data_changes.h" #include "data/data_session.h" @@ -965,24 +966,19 @@ ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const { } return nullptr; }(); - if (sender) { - const auto fromText = sender->isSelf() - ? tr::lng_from_you(tr::now) - : sender->shortName(); - const auto fromWrapped = textcmdLink( - 1, - tr::lng_dialogs_text_from_wrapped( - tr::now, - lt_from, - TextUtilities::Clean(fromText))); - result.text = tr::lng_dialogs_text_with_from( - tr::now, - lt_from_part, - fromWrapped, - lt_message, - std::move(result.text)); + if (!sender) { + return result; } - return result; + const auto fromText = sender->isSelf() + ? tr::lng_from_you(tr::now) + : sender->shortName(); + const auto fromWrapped = textcmdLink( + 1, + tr::lng_dialogs_text_from_wrapped( + tr::now, + lt_from, + TextUtilities::Clean(fromText))); + return Dialogs::Ui::PreviewWithSender(std::move(result), fromWrapped); } Ui::Text::IsolatedEmoji HistoryItem::isolatedEmoji() const { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index ee6c5a1a4d..e8712127b8 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -66,6 +66,7 @@ struct ToPreviewOptions { struct ItemPreview { QString text; std::vector images; + int imagesInTextPosition = 0; std::any loadingContext; }; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 49fbb5bf22..cad1f08dea 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -6853,7 +6853,6 @@ void HistoryWidget::updateForwardingTexts() { } if (count < 2) { - // #TODO minis use images text = _toForward.items.front()->toPreview({ .hideSender = true, .hideCaption = !keepCaptions, diff --git a/Telegram/SourceFiles/history/view/history_view_send_action.cpp b/Telegram/SourceFiles/history/view/history_view_send_action.cpp index 16e3a54da7..78f522a74b 100644 --- a/Telegram/SourceFiles/history/view/history_view_send_action.cpp +++ b/Telegram/SourceFiles/history/view/history_view_send_action.cpp @@ -377,11 +377,14 @@ bool SendActionPainter::updateNeedsAnimating(crl::time now, bool force) { if (force || sendActionChanged || (sendActionResult && !anim::Disabled())) { + const auto height = std::max( + st::normalFont->height, + st::dialogsMiniPreviewTop + st::dialogsMiniPreview); _history->peer->owner().sendActionManager().updateAnimation({ _history, 0, _sendActionAnimation.width() + _animationLeft, - st::normalFont->height, + height, (force || sendActionChanged) }); }