300 lines
8.2 KiB
C++
300 lines
8.2 KiB
C++
/*
|
|
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 "chat_helpers/compose/compose_show.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 "media/stories/media_stories_view.h"
|
|
#include "ui/effects/ripple_animation.h"
|
|
#include "ui/layers/box_content.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)
|
|
, _sourcePeer(_story->repost()
|
|
? _story->repostSourcePeer()
|
|
: _story->owner().peer(
|
|
_story->channelPosts().front().itemId.peer).get()) {
|
|
Expects(_story->repost() || !_story->channelPosts().empty());
|
|
|
|
if (!_story->repost()) {
|
|
_link = MakeChannelPostHandler(
|
|
&_story->session(),
|
|
_story->channelPosts().front().itemId);
|
|
}
|
|
|
|
_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();
|
|
}
|
|
if (_loading) {
|
|
return;
|
|
}
|
|
const auto simple = _text.isEmpty();
|
|
if (simple) {
|
|
y += st::normalFont->height;
|
|
}
|
|
const auto w = _lastWidth = std::min(int(_maxWidth), availableWidth);
|
|
const auto h = height() - (simple ? st::normalFont->height : 0);
|
|
const auto rect = QRect(x, y, w, h);
|
|
const auto backgroundEmojiId = (!simple && _sourcePeer)
|
|
? _sourcePeer->backgroundEmojiId()
|
|
: DocumentId();
|
|
const auto cache = &_quoteCache;
|
|
const auto "eSt = simple
|
|
? st::storiesRepostSimpleStyle
|
|
: 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();
|
|
}
|
|
}
|
|
|
|
if (w > st::historyReplyPadding.left()) {
|
|
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);
|
|
if (!simple) {
|
|
const auto textLeft = x + st::historyReplyPadding.left();
|
|
const auto textTop = y
|
|
+ st::historyReplyPadding.top()
|
|
+ st::semiboldFont->height;
|
|
_text.draw(p, {
|
|
.position = { textLeft, textTop },
|
|
.availableWidth = textw,
|
|
.palette = &st::mediaviewTextPalette,
|
|
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
|
.pausedEmoji = On(PowerSaving::kEmojiChat),
|
|
.pausedSpoiler = On(PowerSaving::kChatSpoiler),
|
|
.elisionLines = 1,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RepostClickHandler RepostView::lookupHandler(QPoint position) {
|
|
if (_loading) {
|
|
return {};
|
|
}
|
|
const auto simple = _text.isEmpty();
|
|
const auto w = _lastWidth;
|
|
const auto skip = simple ? st::normalFont->height : 0;
|
|
const auto h = height() - skip;
|
|
const auto rect = QRect(0, skip, w, h);
|
|
if (!rect.contains(position)) {
|
|
return {};
|
|
} else if (!_link) {
|
|
_link = std::make_shared<LambdaClickHandler>(crl::guard(this, [=] {
|
|
const auto peer = _story->repostSourcePeer();
|
|
const auto owner = &_story->owner();
|
|
if (const auto id = peer ? _story->repostSourceId() : 0) {
|
|
const auto of = owner->stories().lookup({ peer->id, id });
|
|
if (of) {
|
|
using namespace Data;
|
|
_controller->show(*of, { StoriesContextSingle() });
|
|
} else {
|
|
_controller->uiShow()->show(PrepareShortInfoBox(peer));
|
|
}
|
|
} else {
|
|
_controller->uiShow()->showToast(
|
|
tr::lng_forwarded_story_expired(tr::now));
|
|
}
|
|
}));
|
|
}
|
|
_lastPosition = position;
|
|
return { _link, this };
|
|
}
|
|
|
|
PeerData *RepostView::fromPeer() const {
|
|
return _sourcePeer;
|
|
}
|
|
|
|
QString RepostView::fromName() const {
|
|
return _sourcePeer ? _sourcePeer->name() : _story->repostSourceName();
|
|
}
|
|
|
|
void RepostView::recountDimensions() {
|
|
const auto name = _sourcePeer
|
|
? _sourcePeer->name()
|
|
: _story->repostSourceName();
|
|
const auto owner = &_story->owner();
|
|
const auto repostId = _story->repost() ? _story->repostSourceId() : 0;
|
|
|
|
const auto colorIndexPlusOne = _sourcePeer
|
|
? (_sourcePeer->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 unavailable = false;
|
|
if (_sourcePeer && repostId) {
|
|
const auto senderId = _sourcePeer->id;
|
|
const auto of = owner->stories().lookup({ senderId, repostId });
|
|
unavailable = !of && (of.error() == Data::NoStory::Deleted);
|
|
if (of) {
|
|
text = (*of)->caption();
|
|
} else if (!unavailable) {
|
|
const auto done = crl::guard(this, [=] {
|
|
_maxWidth = 0;
|
|
_controller->repaint();
|
|
});
|
|
owner->stories().resolve({ _sourcePeer->id, repostId }, done);
|
|
}
|
|
}
|
|
|
|
auto nameFull = TextWithEntities();
|
|
nameFull.append(HistoryView::Reply::PeerEmoji(owner, _sourcePeer));
|
|
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 = _text.isEmpty()
|
|
? 0
|
|
: std::min(_text.maxWidth(), st::maxSignatureSize);
|
|
_maxWidth = std::max(nameMaxWidth, optimalTextWidth);
|
|
_maxWidth = st::historyReplyPadding.left()
|
|
+ _maxWidth
|
|
+ st::historyReplyPadding.right();
|
|
}
|
|
|
|
void RepostView::clickHandlerPressedChanged(
|
|
const ClickHandlerPtr &action,
|
|
bool pressed) {
|
|
if (action == _link) {
|
|
if (pressed) {
|
|
const auto simple = _text.isEmpty();
|
|
const auto skip = simple ? st::normalFont->height : 0;
|
|
if (!_ripple) {
|
|
const auto h = height() - skip;
|
|
_ripple = std::make_unique<Ui::RippleAnimation>(
|
|
st::defaultRippleAnimation,
|
|
Ui::RippleAnimation::RoundRectMask(
|
|
QSize(_lastWidth, h),
|
|
(simple
|
|
? st::storiesRepostSimpleStyle
|
|
: st::messageQuoteStyle).radius),
|
|
[=] { _controller->repaint(); });
|
|
}
|
|
_ripple->add(_lastPosition - QPoint(0, skip));
|
|
} else if (_ripple) {
|
|
_ripple->lastStop();
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace Media::Stories
|