Show repost info on story view.

This commit is contained in:
John Preston 2023-11-24 13:02:03 +04:00
parent 23a0413113
commit 78897dd143
15 changed files with 540 additions and 24 deletions

View File

@ -1034,6 +1034,8 @@ PRIVATE
media/stories/media_stories_recent_views.h
media/stories/media_stories_reply.cpp
media/stories/media_stories_reply.h
media/stories/media_stories_repost_view.cpp
media/stories/media_stories_repost_view.h
media/stories/media_stories_share.cpp
media/stories/media_stories_share.h
media/stories/media_stories_sibling.cpp

View File

@ -105,6 +105,31 @@ using UpdateFlag = StoryUpdate::Flag;
return result;
}
[[nodiscard]] PeerData *RepostSourcePeer(
not_null<Session*> owner,
const MTPDstoryItem &data) {
if (const auto forwarded = data.vfwd_from()) {
if (const auto from = forwarded->data().vfrom()) {
return owner->peer(peerFromMTP(*from));
}
}
return nullptr;
}
[[nodiscard]] QString RepostSourceName(const MTPDstoryItem &data) {
if (const auto forwarded = data.vfwd_from()) {
return qs(forwarded->data().vfrom_name().value_or_empty());
}
return {};
}
[[nodiscard]] StoryId RepostSourceId(const MTPDstoryItem &data) {
if (const auto forwarded = data.vfwd_from()) {
return forwarded->data().vstory_id().value_or_empty();
}
return {};
}
} // namespace
class StoryPreload::LoadTask final : private Storage::DownloadMtprotoTask {
@ -216,6 +241,9 @@ Story::Story(
TimeId now)
: _id(id)
, _peer(peer)
, _repostSourcePeer(RepostSourcePeer(&peer->owner(), data))
, _repostSourceName(RepostSourceName(data))
, _repostSourceId(RepostSourceId(data))
, _date(data.vdate().v)
, _expires(data.vexpire_date().v) {
applyFields(std::move(media), data, now, true);
@ -730,6 +758,22 @@ TimeId Story::lastUpdateTime() const {
return _lastUpdateTime;
}
bool Story::repost() const {
return _repostSourcePeer || !_repostSourceName.isEmpty();
}
PeerData *Story::repostSourcePeer() const {
return _repostSourcePeer;
}
QString Story::repostSourceName() const {
return _repostSourceName;
}
StoryId Story::repostSourceId() const {
return _repostSourceId;
}
StoryPreload::StoryPreload(not_null<Story*> story, Fn<void()> done)
: _story(story)
, _done(std::move(done)) {

View File

@ -181,6 +181,11 @@ public:
void applyViewsCounts(const MTPDstoryViews &data);
[[nodiscard]] TimeId lastUpdateTime() const;
[[nodiscard]] bool repost() const;
[[nodiscard]] PeerData *repostSourcePeer() const;
[[nodiscard]] QString repostSourceName() const;
[[nodiscard]] StoryId repostSourceId() const;
private:
struct ViewsCounts {
int views = 0;
@ -203,6 +208,9 @@ private:
const StoryId _id = 0;
const not_null<PeerData*> _peer;
PeerData * const _repostSourcePeer = nullptr;
const QString _repostSourceName;
const StoryId _repostSourceId = 0;
Data::ReactionId _sentReactionId;
StoryMedia _media;
TextWithEntities _caption;

View File

@ -38,30 +38,40 @@ namespace {
constexpr auto kNonExpandedLinesLimit = 5;
} // namespace
void ValidateBackgroundEmoji(
DocumentId backgroundEmojiId,
not_null<Ui::BackgroundEmojiData*> data,
not_null<Ui::BackgroundEmojiCache*> cache,
not_null<Ui::Text::QuotePaintCache*> quote,
not_null<const Element*> view) {
if (data->firstFrameMask.isNull() && !data->emoji) {
data->emoji = CreateBackgroundEmojiInstance(
&view->history()->owner(),
backgroundEmojiId,
crl::guard(view, [=] { view->repaint(); }));
}
ValidateBackgroundEmoji(backgroundEmojiId, data, cache, quote);
}
void ValidateBackgroundEmoji(
DocumentId backgroundEmojiId,
not_null<Ui::BackgroundEmojiData*> data,
not_null<Ui::BackgroundEmojiCache*> cache,
not_null<Ui::Text::QuotePaintCache*> quote) {
Expects(!data->firstFrameMask.isNull() || data->emoji != nullptr);
if (data->firstFrameMask.isNull()) {
if (!cache->frames[0].isNull()) {
for (auto &frame : cache->frames) {
frame = QImage();
}
}
const auto tag = Data::CustomEmojiSizeTag::Isolated;
if (!data->emoji) {
const auto repaint = crl::guard(view, [=] { view->repaint(); });
const auto owner = &view->history()->owner();
data->emoji = owner->customEmojiManager().create(
backgroundEmojiId,
repaint,
tag);
}
if (!data->emoji->ready()) {
return;
}
const auto tag = Data::CustomEmojiSizeTag::Isolated;
const auto size = Data::FrameSizeFromTag(tag);
data->firstFrameMask = QImage(
QSize(size, size),
@ -115,8 +125,19 @@ void ValidateBackgroundEmoji(
cache->frames[2] = make(kSize3);
}
auto CreateBackgroundEmojiInstance(
not_null<Data::Session*> owner,
DocumentId backgroundEmojiId,
Fn<void()> repaint)
-> std::unique_ptr<Ui::Text::CustomEmoji> {
return owner->customEmojiManager().create(
backgroundEmojiId,
repaint,
Data::CustomEmojiSizeTag::Isolated);
}
void FillBackgroundEmoji(
Painter &p,
QPainter &p,
const QRect &rect,
bool quote,
const Ui::BackgroundEmojiCache &cache) {
@ -157,8 +178,6 @@ void FillBackgroundEmoji(
p.setOpacity(1.);
}
} // namespace
Reply::Reply()
: _name(st::maxSignatureSize / 2)
, _text(st::maxSignatureSize / 2) {
@ -799,6 +818,12 @@ void Reply::stopLastRipple() {
TextWithEntities Reply::PeerEmoji(
not_null<History*> history,
PeerData *peer) {
return PeerEmoji(&history->owner(), peer);
}
TextWithEntities Reply::PeerEmoji(
not_null<Data::Session*> owner,
PeerData *peer) {
using namespace std;
const auto icon = !peer
? pair(&st::historyReplyUser, st::historyReplyUserPadding)
@ -807,7 +832,6 @@ TextWithEntities Reply::PeerEmoji(
: (peer->isChannel() || peer->isChat())
? pair(&st::historyReplyGroup, st::historyReplyGroupPadding)
: pair(&st::historyReplyUser, st::historyReplyUserPadding);
const auto owner = &history->owner();
return Ui::Text::SingleCustomEmoji(
owner->customEmojiManager().registerInternalEmoji(
*icon.first,

View File

@ -9,12 +9,48 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h"
namespace Data {
class Session;
} // namespace Data
namespace Ui {
class SpoilerAnimation;
struct BackgroundEmojiData;
struct BackgroundEmojiCache;
} // namespace Ui
namespace Ui::Text {
class CustomEmoji;
struct QuotePaintCache;
} // namespace Ui::Text
namespace HistoryView {
void ValidateBackgroundEmoji(
DocumentId backgroundEmojiId,
not_null<Ui::BackgroundEmojiData*> data,
not_null<Ui::BackgroundEmojiCache*> cache,
not_null<Ui::Text::QuotePaintCache*> quote,
not_null<const Element*> view);
// For this one data->firstFrameMask or data->emoji must be already set.
void ValidateBackgroundEmoji(
DocumentId backgroundEmojiId,
not_null<Ui::BackgroundEmojiData*> data,
not_null<Ui::BackgroundEmojiCache*> cache,
not_null<Ui::Text::QuotePaintCache*> quote);
[[nodiscard]] auto CreateBackgroundEmojiInstance(
not_null<Data::Session*> owner,
DocumentId backgroundEmojiId,
Fn<void()> repaint)
-> std::unique_ptr<Ui::Text::CustomEmoji>;
void FillBackgroundEmoji(
QPainter &p,
const QRect &rect,
bool quote,
const Ui::BackgroundEmojiCache &cache);
class Reply final : public RuntimeComponent<Reply, Element> {
public:
Reply();
@ -66,6 +102,9 @@ public:
[[nodiscard]] static TextWithEntities PeerEmoji(
not_null<History*> history,
PeerData *peer);
[[nodiscard]] static TextWithEntities PeerEmoji(
not_null<Data::Session*> owner,
PeerData *peer);
[[nodiscard]] static TextWithEntities ComposePreviewName(
not_null<History*> history,
not_null<HistoryItem*> to,

View File

@ -26,7 +26,7 @@ CaptionFullView::CaptionFullView(not_null<Controller*> controller)
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
_scroll.get(),
object_ptr<Ui::FlatLabel>(_scroll.get(), st::storiesCaptionFull),
st::mediaviewCaptionPadding)))
st::mediaviewCaptionPadding + _controller->repostCaptionPadding())))
, _text(_wrap->entity()) {
_text->setMarkedText(controller->captionText(), Core::MarkedTextContext{
.session = &controller->uiShow()->session(),

View File

@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/stories/media_stories_reactions.h"
#include "media/stories/media_stories_recent_views.h"
#include "media/stories/media_stories_reply.h"
#include "media/stories/media_stories_repost_view.h"
#include "media/stories/media_stories_share.h"
#include "media/stories/media_stories_stealth.h"
#include "media/stories/media_stories_view.h"
@ -332,6 +333,8 @@ Controller::Controller(not_null<Delegate*> delegate)
}
Controller::~Controller() {
_captionFullView = nullptr;
_repostView = nullptr;
changeShown(nullptr);
}
@ -586,7 +589,34 @@ TextWithEntities Controller::captionText() const {
}
bool Controller::skipCaption() const {
return _captionFullView != nullptr;
return (_captionFullView != nullptr)
|| (_captionText.empty() && !repost());
}
bool Controller::repost() const {
return _repostView != nullptr;
}
int Controller::repostSkipTop() const {
return _repostView
? (_repostView->height()
+ (_captionText.empty() ? 0 : st::mediaviewTextSkip))
: 0;
}
QRect Controller::captionWithRepostGeometry(QRect caption) const {
return caption.marginsAdded(st::mediaviewCaptionPadding).marginsAdded(
{ 0, repostSkipTop(), 0, 0 });
}
void Controller::drawRepostInfo(
Painter &p,
int x,
int y,
int availableWidth) const {
Expects(_repostView != nullptr);
_repostView->draw(p, x, y - repostSkipTop(), availableWidth);
}
void Controller::toggleLiked() {
@ -837,6 +867,7 @@ void Controller::show(
}
captionClosed();
_repostView = validateRepostView(story);
_captionText = story->caption();
_contentFaded = false;
_contentFadeAnimation.stop();
@ -1341,6 +1372,10 @@ void Controller::repaintSibling(not_null<Sibling*> sibling) {
}
}
void Controller::repaint() {
_delegate->storiesRepaint();
}
SiblingView Controller::sibling(SiblingType type) const {
const auto &pointer = (type == SiblingType::Left)
? _siblingLeft
@ -1428,6 +1463,13 @@ StoryId Controller::shownId(int index) const {
: StoryId();
}
std::unique_ptr<RepostView> Controller::validateRepostView(
not_null<Data::Story*> story) {
return story->repost()
? std::make_unique<RepostView>(this, story)
: nullptr;
}
void Controller::loadMoreToList() {
Expects(shown());

View File

@ -67,6 +67,7 @@ struct SiblingView;
enum class SiblingType;
struct ContentLayout;
class CaptionFullView;
class RepostView;
enum class ReactionsMode;
class SuggestedReactionView;
@ -121,11 +122,15 @@ public:
[[nodiscard]] Data::FileOrigin fileOrigin() const;
[[nodiscard]] TextWithEntities captionText() const;
[[nodiscard]] bool skipCaption() const;
[[nodiscard]] bool repost() const;
void toggleLiked();
void showFullCaption();
void captionClosing();
void captionClosed();
[[nodiscard]] QRect captionWithRepostGeometry(QRect caption) const;
void drawRepostInfo(Painter &p, int x, int y, int availableWidth) const;
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const;
[[nodiscard]] auto stickerOrEmojiChosen() const
-> rpl::producer<ChatHelpers::FileChosen>;
@ -152,6 +157,7 @@ public:
void changeVolume(float64 volume);
void volumeChangeFinished();
void repaint();
void repaintSibling(not_null<Sibling*> sibling);
[[nodiscard]] SiblingView sibling(SiblingType type) const;
@ -239,6 +245,8 @@ private:
[[nodiscard]] PeerData *shownPeer() const;
[[nodiscard]] int shownCount() const;
[[nodiscard]] StoryId shownId(int index) const;
[[nodiscard]] std::unique_ptr<RepostView> validateRepostView(
not_null<Data::Story*> story);
void rebuildFromContext(not_null<PeerData*> peer, FullStoryId storyId);
void checkMoveByDelta();
void loadMoreToList();
@ -247,6 +255,7 @@ private:
const std::vector<Data::StoriesSourceInfo> &lists,
int index);
[[nodiscard]] int repostSkipTop() const;
void updateAreas(Data::Story *story);
void reactionChosen(ReactionsMode mode, ChosenReaction chosen);
@ -263,6 +272,7 @@ private:
std::unique_ptr<Unsupported> _unsupported;
std::unique_ptr<PhotoPlayback> _photoPlayback;
std::unique_ptr<CaptionFullView> _captionFullView;
std::unique_ptr<RepostView> _repostView;
Ui::Animations::Simple _contentFadeAnimation;
bool _contentFaded = false;

View File

@ -0,0 +1,238 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "media/stories/media_stories_repost_view.h"
#include "core/ui_integration.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/data_stories.h"
#include "history/view/history_view_reply.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "media/stories/media_stories_controller.h"
#include "ui/effects/ripple_animation.h"
#include "ui/text/text_custom_emoji.h"
#include "ui/text/text_options.h"
#include "ui/painter.h"
#include "ui/power_saving.h"
#include "styles/style_chat.h"
#include "styles/style_media_view.h"
namespace Media::Stories {
RepostView::RepostView(
not_null<Controller*> controller,
not_null<Data::Story*> story)
: _controller(controller)
, _story(story) {
Expects(_story->repost());
_story->session().colorIndicesValue(
) | rpl::start_with_next([=](Ui::ColorIndicesCompressed &&indices) {
_colorIndices = std::move(indices);
if (_maxWidth) {
_controller->repaint();
}
}, _lifetime);
}
RepostView::~RepostView() = default;
int RepostView::height() const {
return st::historyReplyPadding.top()
+ st::semiboldFont->height
+ st::normalFont->height
+ st::historyReplyPadding.bottom();
}
void RepostView::draw(Painter &p, int x, int y, int availableWidth) {
if (!_maxWidth) {
recountDimensions();
}
const auto w = std::min(_maxWidth, availableWidth);
const auto rect = QRect(x, y, w, height());
const auto colorPeer = _story->repostSourcePeer();
const auto backgroundEmojiId = colorPeer
? colorPeer->backgroundEmojiId()
: DocumentId();
const auto cache = &_quoteCache;
const auto &quoteSt = st::messageQuoteStyle;
const auto backgroundEmoji = backgroundEmojiId
? &_backgroundEmojiData
: nullptr;
const auto backgroundEmojiCache = backgroundEmoji
? &backgroundEmoji->caches[0]
: nullptr;
auto rippleColor = cache->bg;
cache->bg = QColor(0, 0, 0, 64);
Ui::Text::ValidateQuotePaintCache(*cache, quoteSt);
Ui::Text::FillQuotePaint(p, rect, *cache, quoteSt);
if (backgroundEmoji) {
using namespace HistoryView;
if (backgroundEmoji->firstFrameMask.isNull()
&& !backgroundEmoji->emoji) {
backgroundEmoji->emoji = CreateBackgroundEmojiInstance(
&_story->owner(),
backgroundEmojiId,
crl::guard(this, [=] { _controller->repaint(); }));
}
ValidateBackgroundEmoji(
backgroundEmojiId,
backgroundEmoji,
backgroundEmojiCache,
cache);
if (!backgroundEmojiCache->frames[0].isNull()) {
const auto hasQuoteIcon = false;
FillBackgroundEmoji(
p,
rect,
hasQuoteIcon,
*backgroundEmojiCache);
}
}
cache->bg = rippleColor;
if (_ripple) {
_ripple->paint(p, x, y, w, &rippleColor);
if (_ripple->empty()) {
_ripple.reset();
}
}
const auto pausedSpoiler = On(PowerSaving::kChatSpoiler);
auto textLeft = x + st::historyReplyPadding.left();
auto textTop = y
+ st::historyReplyPadding.top()
+ st::semiboldFont->height;
if (w > st::historyReplyPadding.left()) {
if (_stateText.isEmpty()) {
const auto textw = w
- st::historyReplyPadding.left()
- st::historyReplyPadding.right();
const auto namew = textw;
if (namew > 0) {
p.setPen(cache->icon);
_name.drawLeftElided(
p,
x + st::historyReplyPadding.left(),
y + st::historyReplyPadding.top(),
namew,
w + 2 * x);
_text.draw(p, {
.position = { textLeft, textTop },
.availableWidth = w,
.palette = &st::mediaviewTextPalette,
.spoiler = Ui::Text::DefaultSpoilerCache(),
.pausedEmoji = On(PowerSaving::kEmojiChat),
.pausedSpoiler = On(PowerSaving::kChatSpoiler),
.elisionLines = 1,
});
}
} else {
p.setFont(st::msgDateFont);
p.setPen(cache->icon);
p.drawTextLeft(
textLeft,
(y
+ st::historyReplyPadding.top()
+ (st::msgDateFont->height / 2)),
w + 2 * x,
st::msgDateFont->elided(
_stateText,
x + w - textLeft - st::historyReplyPadding.right()));
}
}
}
void RepostView::recountDimensions() {
const auto sender = _story->repostSourcePeer();
const auto name = sender ? sender->name() : _story->repostSourceName();
const auto owner = &_story->owner();
const auto repostId = _story->repostSourceId();
const auto colorIndexPlusOne = sender
? (sender->colorIndex() + 1)
: 1;
const auto dark = true;
const auto colorPattern = colorIndexPlusOne
? Ui::ColorPatternIndex(_colorIndices, colorIndexPlusOne - 1, dark)
: 0;
Assert(colorPattern < Ui::Text::kMaxQuoteOutlines);
const auto values = Ui::SimpleColorIndexValues(
QColor(255, 255, 255),
colorPattern);
_quoteCache.bg = values.bg;
_quoteCache.outlines = values.outlines;
_quoteCache.icon = values.name;
auto text = TextWithEntities();
auto displaying = true;
auto unavailable = false;
if (sender && repostId) {
const auto of = owner->stories().lookup({ sender->id, repostId });
displaying = of.has_value();
unavailable = !displaying && (of.error() == Data::NoStory::Deleted);
if (displaying) {
text = (*of)->caption();
} else if (!unavailable) {
const auto done = crl::guard(this, [=] {
_maxWidth = 0;
_controller->repaint();
});
owner->stories().resolve({ sender->id, repostId }, done);
}
}
if (displaying && !unavailable && text.empty()) {
text = { tr::lng_in_dlg_story(tr::now) };
}
auto nameFull = TextWithEntities();
nameFull.append(HistoryView::Reply::PeerEmoji(owner, sender));
nameFull.append(name);
auto context = Core::MarkedTextContext{
.session = &_story->session(),
.customEmojiRepaint = [] {},
.customEmojiLoopLimit = 1,
};
_name.setMarkedText(
st::semiboldTextStyle,
nameFull,
Ui::NameTextOptions(),
context);
context.customEmojiRepaint = crl::guard(this, [=] {
_controller->repaint();
}),
_text.setMarkedText(
st::defaultTextStyle,
text,
Ui::DialogTextOptions(),
context);
const auto nameMaxWidth = _name.maxWidth();
const auto optimalTextWidth = std::min(
_text.maxWidth(),
st::maxSignatureSize);
_maxWidth = std::max(nameMaxWidth, optimalTextWidth);
if (!displaying) {
_stateText = !unavailable
? tr::lng_profile_loading(tr::now)
: tr::lng_deleted_story(tr::now);
const auto phraseWidth = st::msgDateFont->width(_stateText);
_maxWidth = unavailable
? phraseWidth
: std::max(_maxWidth, phraseWidth);
} else {
_stateText = QString();
}
_maxWidth = st::historyReplyPadding.left()
+ _maxWidth
+ st::historyReplyPadding.right();
}
} // namespace Media::Stories

View File

@ -0,0 +1,56 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/weak_ptr.h"
#include "ui/chat/chat_style.h"
class Painter;
namespace Data {
class Story;
} // namespace Data
namespace Ui {
class RippleAnimation;
} // namespace Ui
namespace Media::Stories {
class Controller;
class RepostView final : public base::has_weak_ptr {
public:
RepostView(
not_null<Controller*> controller,
not_null<Data::Story*> story);
~RepostView();
[[nodiscard]] int height() const;
void draw(Painter &p, int x, int y, int availableWidth);
private:
void recountDimensions();
const not_null<Controller*> _controller;
const not_null<Data::Story*> _story;
std::unique_ptr<Ui::RippleAnimation> _ripple;
Ui::Text::String _name;
Ui::Text::String _text;
Ui::Text::QuotePaintCache _quoteCache;
Ui::BackgroundEmojiData _backgroundEmojiData;
QString _stateText;
Ui::ColorIndicesCompressed _colorIndices;
int _maxWidth = 0;
rpl::lifetime _lifetime;
};
} // namespace Media::Stories

View File

@ -146,6 +146,22 @@ bool View::skipCaption() const {
return _controller->skipCaption();
}
bool View::repost() const {
return _controller->repost();
}
QRect View::captionWithRepostGeometry(QRect caption) const {
return _controller->captionWithRepostGeometry(caption);
}
void View::drawRepostInfo(
Painter &p,
int x,
int y,
int availableWidth) const {
_controller->drawRepostInfo(p, x, y, availableWidth);
}
void View::showFullCaption() {
_controller->showFullCaption();
}

View File

@ -78,8 +78,12 @@ public:
[[nodiscard]] Data::FileOrigin fileOrigin() const;
[[nodiscard]] TextWithEntities captionText() const;
[[nodiscard]] bool skipCaption() const;
[[nodiscard]] bool repost() const;
void showFullCaption();
[[nodiscard]] QRect captionWithRepostGeometry(QRect caption) const;
void drawRepostInfo(Painter &p, int x, int y, int availableWidth) const;
void updatePlayback(const Player::TrackState &state);
[[nodiscard]] ClickHandlerPtr lookupAreaHandler(QPoint point) const;

View File

@ -1434,7 +1434,10 @@ void OverlayWidget::refreshCaptionGeometry() {
_captionShowMoreWidth = 0;
_captionSkipBlockWidth = 0;
if (_caption.isEmpty()) {
const auto storiesCaptionWidth = _w
- st::mediaviewCaptionPadding.left()
- st::mediaviewCaptionPadding.right();
if (_caption.isEmpty() && (!_stories || !_stories->repost())) {
_captionRect = QRect();
return;
}
@ -1451,9 +1454,7 @@ void OverlayWidget::refreshCaptionGeometry() {
? _groupThumbsTop
: height() - st::mediaviewCaptionMargin.height();
const auto captionWidth = _stories
? (_w
- st::mediaviewCaptionPadding.left()
- st::mediaviewCaptionPadding.right())
? storiesCaptionWidth
: std::min(
(_groupThumbsAvailableWidth
- st::mediaviewCaptionPadding.left()
@ -4612,8 +4613,7 @@ void OverlayWidget::paint(not_null<Renderer*> renderer) {
if (!_stories) {
renderer->paintFooter(footerGeometry(), opacity);
}
if (!_caption.isEmpty()
&& (!_stories || !_stories->skipCaption())) {
if (!(_stories ? _stories->skipCaption() : _caption.isEmpty())) {
renderer->paintCaption(captionGeometry(), opacity);
}
if (_groupThumbs) {
@ -5020,8 +5020,13 @@ void OverlayWidget::paintCaptionContent(
QRect outer,
QRect clip,
float64 opacity) {
const auto inner = outer.marginsRemoved(st::mediaviewCaptionPadding);
if (!_stories) {
auto inner = outer.marginsRemoved(st::mediaviewCaptionPadding);
inner.setTop(inner.top() + inner.height() - _captionRect.height());
if (_stories) {
if (_stories->repost()) {
_stories->drawRepostInfo(p, inner.x(), inner.y(), inner.width());
}
} else {
p.setOpacity(opacity);
p.setBrush(st::mediaviewCaptionBg);
p.setPen(Qt::NoPen);
@ -5068,7 +5073,9 @@ void OverlayWidget::paintCaptionContent(
}
QRect OverlayWidget::captionGeometry() const {
return _captionRect.marginsAdded(st::mediaviewCaptionPadding);
return (_stories && _stories->repost())
? _stories->captionWithRepostGeometry(_captionRect)
: _captionRect.marginsAdded(st::mediaviewCaptionPadding);
}
void OverlayWidget::paintGroupThumbsContent(
@ -5710,6 +5717,8 @@ void OverlayWidget::updateOver(QPoint pos) {
lnk = ensureCaptionExpandLink();
}
lnkhost = this;
} else if (_stories && captionGeometry().contains(pos)) {
//_stories->repostState();
} else if (_groupThumbs && _groupThumbsRect.contains(pos)) {
const auto point = pos - QPoint(_groupThumbsLeft, _groupThumbsTop);
lnk = _groupThumbs->getState(point);

View File

@ -143,6 +143,21 @@ int BackgroundEmojiData::CacheIndex(
return (base * 2) + (selected ? 1 : 0);
};
int ColorPatternIndex(
const ColorIndicesCompressed &indices,
uint8 colorIndex,
bool dark) {
Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
if (!indices.colors
|| colorIndex < kSimpleColorIndexCount) {
return 0;
}
auto &data = (*indices.colors)[colorIndex];
auto &colors = dark ? data.dark : data.light;
return colors[2] ? 2 : colors[1] ? 1 : 0;
}
ChatStyle::ChatStyle(rpl::producer<ColorIndicesCompressed> colorIndices) {
if (colorIndices) {
_colorIndicesLifetime = std::move(

View File

@ -236,12 +236,21 @@ struct ColorIndicesCompressed {
std::shared_ptr<std::array<ColorIndexData, kColorIndexCount>> colors;
};
[[nodiscard]] int ColorPatternIndex(
const ColorIndicesCompressed &indices,
uint8 colorIndex,
bool dark);
struct ColorIndexValues {
std::array<QColor, kColorPatternsCount> outlines;
QColor name;
QColor bg;
};
[[nodiscard]] ColorIndexValues SimpleColorIndexValues(
QColor color,
int patternIndex);
class ChatStyle final : public style::palette {
public:
explicit ChatStyle(rpl::producer<ColorIndicesCompressed> colorIndices);