mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Show repost info on story view.
This commit is contained in:
parent
23a0413113
commit
78897dd143
15 changed files with 540 additions and 24 deletions
|
@ -1034,6 +1034,8 @@ PRIVATE
|
||||||
media/stories/media_stories_recent_views.h
|
media/stories/media_stories_recent_views.h
|
||||||
media/stories/media_stories_reply.cpp
|
media/stories/media_stories_reply.cpp
|
||||||
media/stories/media_stories_reply.h
|
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.cpp
|
||||||
media/stories/media_stories_share.h
|
media/stories/media_stories_share.h
|
||||||
media/stories/media_stories_sibling.cpp
|
media/stories/media_stories_sibling.cpp
|
||||||
|
|
|
@ -105,6 +105,31 @@ using UpdateFlag = StoryUpdate::Flag;
|
||||||
return result;
|
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
|
} // namespace
|
||||||
|
|
||||||
class StoryPreload::LoadTask final : private Storage::DownloadMtprotoTask {
|
class StoryPreload::LoadTask final : private Storage::DownloadMtprotoTask {
|
||||||
|
@ -216,6 +241,9 @@ Story::Story(
|
||||||
TimeId now)
|
TimeId now)
|
||||||
: _id(id)
|
: _id(id)
|
||||||
, _peer(peer)
|
, _peer(peer)
|
||||||
|
, _repostSourcePeer(RepostSourcePeer(&peer->owner(), data))
|
||||||
|
, _repostSourceName(RepostSourceName(data))
|
||||||
|
, _repostSourceId(RepostSourceId(data))
|
||||||
, _date(data.vdate().v)
|
, _date(data.vdate().v)
|
||||||
, _expires(data.vexpire_date().v) {
|
, _expires(data.vexpire_date().v) {
|
||||||
applyFields(std::move(media), data, now, true);
|
applyFields(std::move(media), data, now, true);
|
||||||
|
@ -730,6 +758,22 @@ TimeId Story::lastUpdateTime() const {
|
||||||
return _lastUpdateTime;
|
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)
|
StoryPreload::StoryPreload(not_null<Story*> story, Fn<void()> done)
|
||||||
: _story(story)
|
: _story(story)
|
||||||
, _done(std::move(done)) {
|
, _done(std::move(done)) {
|
||||||
|
|
|
@ -181,6 +181,11 @@ public:
|
||||||
void applyViewsCounts(const MTPDstoryViews &data);
|
void applyViewsCounts(const MTPDstoryViews &data);
|
||||||
[[nodiscard]] TimeId lastUpdateTime() const;
|
[[nodiscard]] TimeId lastUpdateTime() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool repost() const;
|
||||||
|
[[nodiscard]] PeerData *repostSourcePeer() const;
|
||||||
|
[[nodiscard]] QString repostSourceName() const;
|
||||||
|
[[nodiscard]] StoryId repostSourceId() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ViewsCounts {
|
struct ViewsCounts {
|
||||||
int views = 0;
|
int views = 0;
|
||||||
|
@ -203,6 +208,9 @@ private:
|
||||||
|
|
||||||
const StoryId _id = 0;
|
const StoryId _id = 0;
|
||||||
const not_null<PeerData*> _peer;
|
const not_null<PeerData*> _peer;
|
||||||
|
PeerData * const _repostSourcePeer = nullptr;
|
||||||
|
const QString _repostSourceName;
|
||||||
|
const StoryId _repostSourceId = 0;
|
||||||
Data::ReactionId _sentReactionId;
|
Data::ReactionId _sentReactionId;
|
||||||
StoryMedia _media;
|
StoryMedia _media;
|
||||||
TextWithEntities _caption;
|
TextWithEntities _caption;
|
||||||
|
|
|
@ -38,30 +38,40 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kNonExpandedLinesLimit = 5;
|
constexpr auto kNonExpandedLinesLimit = 5;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void ValidateBackgroundEmoji(
|
void ValidateBackgroundEmoji(
|
||||||
DocumentId backgroundEmojiId,
|
DocumentId backgroundEmojiId,
|
||||||
not_null<Ui::BackgroundEmojiData*> data,
|
not_null<Ui::BackgroundEmojiData*> data,
|
||||||
not_null<Ui::BackgroundEmojiCache*> cache,
|
not_null<Ui::BackgroundEmojiCache*> cache,
|
||||||
not_null<Ui::Text::QuotePaintCache*> quote,
|
not_null<Ui::Text::QuotePaintCache*> quote,
|
||||||
not_null<const Element*> view) {
|
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 (data->firstFrameMask.isNull()) {
|
||||||
if (!cache->frames[0].isNull()) {
|
if (!cache->frames[0].isNull()) {
|
||||||
for (auto &frame : cache->frames) {
|
for (auto &frame : cache->frames) {
|
||||||
frame = QImage();
|
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()) {
|
if (!data->emoji->ready()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto tag = Data::CustomEmojiSizeTag::Isolated;
|
||||||
const auto size = Data::FrameSizeFromTag(tag);
|
const auto size = Data::FrameSizeFromTag(tag);
|
||||||
data->firstFrameMask = QImage(
|
data->firstFrameMask = QImage(
|
||||||
QSize(size, size),
|
QSize(size, size),
|
||||||
|
@ -115,8 +125,19 @@ void ValidateBackgroundEmoji(
|
||||||
cache->frames[2] = make(kSize3);
|
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(
|
void FillBackgroundEmoji(
|
||||||
Painter &p,
|
QPainter &p,
|
||||||
const QRect &rect,
|
const QRect &rect,
|
||||||
bool quote,
|
bool quote,
|
||||||
const Ui::BackgroundEmojiCache &cache) {
|
const Ui::BackgroundEmojiCache &cache) {
|
||||||
|
@ -157,8 +178,6 @@ void FillBackgroundEmoji(
|
||||||
p.setOpacity(1.);
|
p.setOpacity(1.);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
Reply::Reply()
|
Reply::Reply()
|
||||||
: _name(st::maxSignatureSize / 2)
|
: _name(st::maxSignatureSize / 2)
|
||||||
, _text(st::maxSignatureSize / 2) {
|
, _text(st::maxSignatureSize / 2) {
|
||||||
|
@ -799,6 +818,12 @@ void Reply::stopLastRipple() {
|
||||||
TextWithEntities Reply::PeerEmoji(
|
TextWithEntities Reply::PeerEmoji(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
PeerData *peer) {
|
PeerData *peer) {
|
||||||
|
return PeerEmoji(&history->owner(), peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextWithEntities Reply::PeerEmoji(
|
||||||
|
not_null<Data::Session*> owner,
|
||||||
|
PeerData *peer) {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
const auto icon = !peer
|
const auto icon = !peer
|
||||||
? pair(&st::historyReplyUser, st::historyReplyUserPadding)
|
? pair(&st::historyReplyUser, st::historyReplyUserPadding)
|
||||||
|
@ -807,7 +832,6 @@ TextWithEntities Reply::PeerEmoji(
|
||||||
: (peer->isChannel() || peer->isChat())
|
: (peer->isChannel() || peer->isChat())
|
||||||
? pair(&st::historyReplyGroup, st::historyReplyGroupPadding)
|
? pair(&st::historyReplyGroup, st::historyReplyGroupPadding)
|
||||||
: pair(&st::historyReplyUser, st::historyReplyUserPadding);
|
: pair(&st::historyReplyUser, st::historyReplyUserPadding);
|
||||||
const auto owner = &history->owner();
|
|
||||||
return Ui::Text::SingleCustomEmoji(
|
return Ui::Text::SingleCustomEmoji(
|
||||||
owner->customEmojiManager().registerInternalEmoji(
|
owner->customEmojiManager().registerInternalEmoji(
|
||||||
*icon.first,
|
*icon.first,
|
||||||
|
|
|
@ -9,12 +9,48 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class Session;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class SpoilerAnimation;
|
class SpoilerAnimation;
|
||||||
|
struct BackgroundEmojiData;
|
||||||
|
struct BackgroundEmojiCache;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Ui::Text {
|
||||||
|
class CustomEmoji;
|
||||||
|
struct QuotePaintCache;
|
||||||
|
} // namespace Ui::Text
|
||||||
|
|
||||||
namespace HistoryView {
|
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> {
|
class Reply final : public RuntimeComponent<Reply, Element> {
|
||||||
public:
|
public:
|
||||||
Reply();
|
Reply();
|
||||||
|
@ -66,6 +102,9 @@ public:
|
||||||
[[nodiscard]] static TextWithEntities PeerEmoji(
|
[[nodiscard]] static TextWithEntities PeerEmoji(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
PeerData *peer);
|
PeerData *peer);
|
||||||
|
[[nodiscard]] static TextWithEntities PeerEmoji(
|
||||||
|
not_null<Data::Session*> owner,
|
||||||
|
PeerData *peer);
|
||||||
[[nodiscard]] static TextWithEntities ComposePreviewName(
|
[[nodiscard]] static TextWithEntities ComposePreviewName(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
not_null<HistoryItem*> to,
|
not_null<HistoryItem*> to,
|
||||||
|
|
|
@ -26,7 +26,7 @@ CaptionFullView::CaptionFullView(not_null<Controller*> controller)
|
||||||
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
|
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
|
||||||
_scroll.get(),
|
_scroll.get(),
|
||||||
object_ptr<Ui::FlatLabel>(_scroll.get(), st::storiesCaptionFull),
|
object_ptr<Ui::FlatLabel>(_scroll.get(), st::storiesCaptionFull),
|
||||||
st::mediaviewCaptionPadding)))
|
st::mediaviewCaptionPadding + _controller->repostCaptionPadding())))
|
||||||
, _text(_wrap->entity()) {
|
, _text(_wrap->entity()) {
|
||||||
_text->setMarkedText(controller->captionText(), Core::MarkedTextContext{
|
_text->setMarkedText(controller->captionText(), Core::MarkedTextContext{
|
||||||
.session = &controller->uiShow()->session(),
|
.session = &controller->uiShow()->session(),
|
||||||
|
|
|
@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "media/stories/media_stories_reactions.h"
|
#include "media/stories/media_stories_reactions.h"
|
||||||
#include "media/stories/media_stories_recent_views.h"
|
#include "media/stories/media_stories_recent_views.h"
|
||||||
#include "media/stories/media_stories_reply.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_share.h"
|
||||||
#include "media/stories/media_stories_stealth.h"
|
#include "media/stories/media_stories_stealth.h"
|
||||||
#include "media/stories/media_stories_view.h"
|
#include "media/stories/media_stories_view.h"
|
||||||
|
@ -332,6 +333,8 @@ Controller::Controller(not_null<Delegate*> delegate)
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller::~Controller() {
|
Controller::~Controller() {
|
||||||
|
_captionFullView = nullptr;
|
||||||
|
_repostView = nullptr;
|
||||||
changeShown(nullptr);
|
changeShown(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,7 +589,34 @@ TextWithEntities Controller::captionText() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::skipCaption() 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() {
|
void Controller::toggleLiked() {
|
||||||
|
@ -837,6 +867,7 @@ void Controller::show(
|
||||||
}
|
}
|
||||||
|
|
||||||
captionClosed();
|
captionClosed();
|
||||||
|
_repostView = validateRepostView(story);
|
||||||
_captionText = story->caption();
|
_captionText = story->caption();
|
||||||
_contentFaded = false;
|
_contentFaded = false;
|
||||||
_contentFadeAnimation.stop();
|
_contentFadeAnimation.stop();
|
||||||
|
@ -1341,6 +1372,10 @@ void Controller::repaintSibling(not_null<Sibling*> sibling) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::repaint() {
|
||||||
|
_delegate->storiesRepaint();
|
||||||
|
}
|
||||||
|
|
||||||
SiblingView Controller::sibling(SiblingType type) const {
|
SiblingView Controller::sibling(SiblingType type) const {
|
||||||
const auto &pointer = (type == SiblingType::Left)
|
const auto &pointer = (type == SiblingType::Left)
|
||||||
? _siblingLeft
|
? _siblingLeft
|
||||||
|
@ -1428,6 +1463,13 @@ StoryId Controller::shownId(int index) const {
|
||||||
: StoryId();
|
: 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() {
|
void Controller::loadMoreToList() {
|
||||||
Expects(shown());
|
Expects(shown());
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,7 @@ struct SiblingView;
|
||||||
enum class SiblingType;
|
enum class SiblingType;
|
||||||
struct ContentLayout;
|
struct ContentLayout;
|
||||||
class CaptionFullView;
|
class CaptionFullView;
|
||||||
|
class RepostView;
|
||||||
enum class ReactionsMode;
|
enum class ReactionsMode;
|
||||||
class SuggestedReactionView;
|
class SuggestedReactionView;
|
||||||
|
|
||||||
|
@ -121,11 +122,15 @@ public:
|
||||||
[[nodiscard]] Data::FileOrigin fileOrigin() const;
|
[[nodiscard]] Data::FileOrigin fileOrigin() const;
|
||||||
[[nodiscard]] TextWithEntities captionText() const;
|
[[nodiscard]] TextWithEntities captionText() const;
|
||||||
[[nodiscard]] bool skipCaption() const;
|
[[nodiscard]] bool skipCaption() const;
|
||||||
|
[[nodiscard]] bool repost() const;
|
||||||
void toggleLiked();
|
void toggleLiked();
|
||||||
void showFullCaption();
|
void showFullCaption();
|
||||||
void captionClosing();
|
void captionClosing();
|
||||||
void captionClosed();
|
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]] std::shared_ptr<ChatHelpers::Show> uiShow() const;
|
||||||
[[nodiscard]] auto stickerOrEmojiChosen() const
|
[[nodiscard]] auto stickerOrEmojiChosen() const
|
||||||
-> rpl::producer<ChatHelpers::FileChosen>;
|
-> rpl::producer<ChatHelpers::FileChosen>;
|
||||||
|
@ -152,6 +157,7 @@ public:
|
||||||
void changeVolume(float64 volume);
|
void changeVolume(float64 volume);
|
||||||
void volumeChangeFinished();
|
void volumeChangeFinished();
|
||||||
|
|
||||||
|
void repaint();
|
||||||
void repaintSibling(not_null<Sibling*> sibling);
|
void repaintSibling(not_null<Sibling*> sibling);
|
||||||
[[nodiscard]] SiblingView sibling(SiblingType type) const;
|
[[nodiscard]] SiblingView sibling(SiblingType type) const;
|
||||||
|
|
||||||
|
@ -239,6 +245,8 @@ private:
|
||||||
[[nodiscard]] PeerData *shownPeer() const;
|
[[nodiscard]] PeerData *shownPeer() const;
|
||||||
[[nodiscard]] int shownCount() const;
|
[[nodiscard]] int shownCount() const;
|
||||||
[[nodiscard]] StoryId shownId(int index) 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 rebuildFromContext(not_null<PeerData*> peer, FullStoryId storyId);
|
||||||
void checkMoveByDelta();
|
void checkMoveByDelta();
|
||||||
void loadMoreToList();
|
void loadMoreToList();
|
||||||
|
@ -247,6 +255,7 @@ private:
|
||||||
const std::vector<Data::StoriesSourceInfo> &lists,
|
const std::vector<Data::StoriesSourceInfo> &lists,
|
||||||
int index);
|
int index);
|
||||||
|
|
||||||
|
[[nodiscard]] int repostSkipTop() const;
|
||||||
void updateAreas(Data::Story *story);
|
void updateAreas(Data::Story *story);
|
||||||
void reactionChosen(ReactionsMode mode, ChosenReaction chosen);
|
void reactionChosen(ReactionsMode mode, ChosenReaction chosen);
|
||||||
|
|
||||||
|
@ -263,6 +272,7 @@ private:
|
||||||
std::unique_ptr<Unsupported> _unsupported;
|
std::unique_ptr<Unsupported> _unsupported;
|
||||||
std::unique_ptr<PhotoPlayback> _photoPlayback;
|
std::unique_ptr<PhotoPlayback> _photoPlayback;
|
||||||
std::unique_ptr<CaptionFullView> _captionFullView;
|
std::unique_ptr<CaptionFullView> _captionFullView;
|
||||||
|
std::unique_ptr<RepostView> _repostView;
|
||||||
|
|
||||||
Ui::Animations::Simple _contentFadeAnimation;
|
Ui::Animations::Simple _contentFadeAnimation;
|
||||||
bool _contentFaded = false;
|
bool _contentFaded = false;
|
||||||
|
|
238
Telegram/SourceFiles/media/stories/media_stories_repost_view.cpp
Normal file
238
Telegram/SourceFiles/media/stories/media_stories_repost_view.cpp
Normal 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 "eSt = 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
|
|
@ -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
|
|
@ -146,6 +146,22 @@ bool View::skipCaption() const {
|
||||||
return _controller->skipCaption();
|
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() {
|
void View::showFullCaption() {
|
||||||
_controller->showFullCaption();
|
_controller->showFullCaption();
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,8 +78,12 @@ public:
|
||||||
[[nodiscard]] Data::FileOrigin fileOrigin() const;
|
[[nodiscard]] Data::FileOrigin fileOrigin() const;
|
||||||
[[nodiscard]] TextWithEntities captionText() const;
|
[[nodiscard]] TextWithEntities captionText() const;
|
||||||
[[nodiscard]] bool skipCaption() const;
|
[[nodiscard]] bool skipCaption() const;
|
||||||
|
[[nodiscard]] bool repost() const;
|
||||||
void showFullCaption();
|
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);
|
void updatePlayback(const Player::TrackState &state);
|
||||||
[[nodiscard]] ClickHandlerPtr lookupAreaHandler(QPoint point) const;
|
[[nodiscard]] ClickHandlerPtr lookupAreaHandler(QPoint point) const;
|
||||||
|
|
||||||
|
|
|
@ -1434,7 +1434,10 @@ void OverlayWidget::refreshCaptionGeometry() {
|
||||||
_captionShowMoreWidth = 0;
|
_captionShowMoreWidth = 0;
|
||||||
_captionSkipBlockWidth = 0;
|
_captionSkipBlockWidth = 0;
|
||||||
|
|
||||||
if (_caption.isEmpty()) {
|
const auto storiesCaptionWidth = _w
|
||||||
|
- st::mediaviewCaptionPadding.left()
|
||||||
|
- st::mediaviewCaptionPadding.right();
|
||||||
|
if (_caption.isEmpty() && (!_stories || !_stories->repost())) {
|
||||||
_captionRect = QRect();
|
_captionRect = QRect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1451,9 +1454,7 @@ void OverlayWidget::refreshCaptionGeometry() {
|
||||||
? _groupThumbsTop
|
? _groupThumbsTop
|
||||||
: height() - st::mediaviewCaptionMargin.height();
|
: height() - st::mediaviewCaptionMargin.height();
|
||||||
const auto captionWidth = _stories
|
const auto captionWidth = _stories
|
||||||
? (_w
|
? storiesCaptionWidth
|
||||||
- st::mediaviewCaptionPadding.left()
|
|
||||||
- st::mediaviewCaptionPadding.right())
|
|
||||||
: std::min(
|
: std::min(
|
||||||
(_groupThumbsAvailableWidth
|
(_groupThumbsAvailableWidth
|
||||||
- st::mediaviewCaptionPadding.left()
|
- st::mediaviewCaptionPadding.left()
|
||||||
|
@ -4612,8 +4613,7 @@ void OverlayWidget::paint(not_null<Renderer*> renderer) {
|
||||||
if (!_stories) {
|
if (!_stories) {
|
||||||
renderer->paintFooter(footerGeometry(), opacity);
|
renderer->paintFooter(footerGeometry(), opacity);
|
||||||
}
|
}
|
||||||
if (!_caption.isEmpty()
|
if (!(_stories ? _stories->skipCaption() : _caption.isEmpty())) {
|
||||||
&& (!_stories || !_stories->skipCaption())) {
|
|
||||||
renderer->paintCaption(captionGeometry(), opacity);
|
renderer->paintCaption(captionGeometry(), opacity);
|
||||||
}
|
}
|
||||||
if (_groupThumbs) {
|
if (_groupThumbs) {
|
||||||
|
@ -5020,8 +5020,13 @@ void OverlayWidget::paintCaptionContent(
|
||||||
QRect outer,
|
QRect outer,
|
||||||
QRect clip,
|
QRect clip,
|
||||||
float64 opacity) {
|
float64 opacity) {
|
||||||
const auto inner = outer.marginsRemoved(st::mediaviewCaptionPadding);
|
auto inner = outer.marginsRemoved(st::mediaviewCaptionPadding);
|
||||||
if (!_stories) {
|
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.setOpacity(opacity);
|
||||||
p.setBrush(st::mediaviewCaptionBg);
|
p.setBrush(st::mediaviewCaptionBg);
|
||||||
p.setPen(Qt::NoPen);
|
p.setPen(Qt::NoPen);
|
||||||
|
@ -5068,7 +5073,9 @@ void OverlayWidget::paintCaptionContent(
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect OverlayWidget::captionGeometry() const {
|
QRect OverlayWidget::captionGeometry() const {
|
||||||
return _captionRect.marginsAdded(st::mediaviewCaptionPadding);
|
return (_stories && _stories->repost())
|
||||||
|
? _stories->captionWithRepostGeometry(_captionRect)
|
||||||
|
: _captionRect.marginsAdded(st::mediaviewCaptionPadding);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::paintGroupThumbsContent(
|
void OverlayWidget::paintGroupThumbsContent(
|
||||||
|
@ -5710,6 +5717,8 @@ void OverlayWidget::updateOver(QPoint pos) {
|
||||||
lnk = ensureCaptionExpandLink();
|
lnk = ensureCaptionExpandLink();
|
||||||
}
|
}
|
||||||
lnkhost = this;
|
lnkhost = this;
|
||||||
|
} else if (_stories && captionGeometry().contains(pos)) {
|
||||||
|
//_stories->repostState();
|
||||||
} else if (_groupThumbs && _groupThumbsRect.contains(pos)) {
|
} else if (_groupThumbs && _groupThumbsRect.contains(pos)) {
|
||||||
const auto point = pos - QPoint(_groupThumbsLeft, _groupThumbsTop);
|
const auto point = pos - QPoint(_groupThumbsLeft, _groupThumbsTop);
|
||||||
lnk = _groupThumbs->getState(point);
|
lnk = _groupThumbs->getState(point);
|
||||||
|
|
|
@ -143,6 +143,21 @@ int BackgroundEmojiData::CacheIndex(
|
||||||
return (base * 2) + (selected ? 1 : 0);
|
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) {
|
ChatStyle::ChatStyle(rpl::producer<ColorIndicesCompressed> colorIndices) {
|
||||||
if (colorIndices) {
|
if (colorIndices) {
|
||||||
_colorIndicesLifetime = std::move(
|
_colorIndicesLifetime = std::move(
|
||||||
|
|
|
@ -236,12 +236,21 @@ struct ColorIndicesCompressed {
|
||||||
std::shared_ptr<std::array<ColorIndexData, kColorIndexCount>> colors;
|
std::shared_ptr<std::array<ColorIndexData, kColorIndexCount>> colors;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] int ColorPatternIndex(
|
||||||
|
const ColorIndicesCompressed &indices,
|
||||||
|
uint8 colorIndex,
|
||||||
|
bool dark);
|
||||||
|
|
||||||
struct ColorIndexValues {
|
struct ColorIndexValues {
|
||||||
std::array<QColor, kColorPatternsCount> outlines;
|
std::array<QColor, kColorPatternsCount> outlines;
|
||||||
QColor name;
|
QColor name;
|
||||||
QColor bg;
|
QColor bg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] ColorIndexValues SimpleColorIndexValues(
|
||||||
|
QColor color,
|
||||||
|
int patternIndex);
|
||||||
|
|
||||||
class ChatStyle final : public style::palette {
|
class ChatStyle final : public style::palette {
|
||||||
public:
|
public:
|
||||||
explicit ChatStyle(rpl::producer<ColorIndicesCompressed> colorIndices);
|
explicit ChatStyle(rpl::producer<ColorIndicesCompressed> colorIndices);
|
||||||
|
|
Loading…
Add table
Reference in a new issue