mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Improve premium sticker sync / preview.
This commit is contained in:
parent
3b5ec78f4f
commit
ca731968ca
26 changed files with 492 additions and 54 deletions
|
@ -272,6 +272,8 @@ PRIVATE
|
|||
boxes/sessions_box.h
|
||||
boxes/share_box.cpp
|
||||
boxes/share_box.h
|
||||
boxes/sticker_preview_box.cpp
|
||||
boxes/sticker_preview_box.h
|
||||
boxes/sticker_set_box.cpp
|
||||
boxes/sticker_set_box.h
|
||||
boxes/stickers_box.cpp
|
||||
|
|
|
@ -224,6 +224,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
"lng_limits_increase" = "Increase Limit";
|
||||
|
||||
"lng_sticker_premium_about" = "Unlock this sticker and more by subscribing to\nTelegram Premium.";
|
||||
"lng_sticker_premium_button" = "Unlock Premium Stickers";
|
||||
|
||||
"lng_flood_error" = "Too many tries. Please try again later.";
|
||||
"lng_gif_error" = "An error has occurred while reading GIF animation :(";
|
||||
"lng_edit_error" = "You cannot edit this message";
|
||||
|
|
|
@ -627,6 +627,7 @@ messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity;
|
|||
messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;
|
||||
messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity;
|
||||
messageEntitySpoiler#32ca960f offset:int length:int = MessageEntity;
|
||||
messageEntityAnimatedEmoji#5eef0214 offset:int length:int = MessageEntity;
|
||||
|
||||
inputChannelEmpty#ee8c1e86 = InputChannel;
|
||||
inputChannel#f35aec28 channel_id:long access_hash:long = InputChannel;
|
||||
|
|
343
Telegram/SourceFiles/boxes/sticker_preview_box.cpp
Normal file
343
Telegram/SourceFiles/boxes/sticker_preview_box.cpp
Normal file
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
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 "boxes/sticker_preview_box.h"
|
||||
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "lottie/lottie_single_player.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kPremiumShift = 0.082;
|
||||
constexpr auto kPremiumMultiplier = 1.5;
|
||||
|
||||
struct Preload {
|
||||
not_null<DocumentData*> document;
|
||||
std::shared_ptr<Data::DocumentMedia> media;
|
||||
base::weak_ptr<Window::SessionController> controller;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::vector<Preload> &Preloads() {
|
||||
static auto result = std::vector<Preload>();
|
||||
return result;
|
||||
}
|
||||
|
||||
void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
|
||||
const auto origin = media->owner()->stickerSetOrigin();
|
||||
media->automaticLoad(origin, nullptr);
|
||||
media->videoThumbnailWanted(origin);
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> StickerPreview(
|
||||
QWidget *parent,
|
||||
const std::shared_ptr<Data::DocumentMedia> &media,
|
||||
const QImage &back,
|
||||
int size) {
|
||||
auto result = object_ptr<Ui::FixedHeightWidget>(parent, size);
|
||||
const auto raw = result.data();
|
||||
auto &lifetime = raw->lifetime();
|
||||
|
||||
struct State {
|
||||
std::unique_ptr<Lottie::SinglePlayer> lottie;
|
||||
std::unique_ptr<Lottie::SinglePlayer> effect;
|
||||
};
|
||||
const auto state = lifetime.make_state<State>();
|
||||
|
||||
const auto lottie = int(size / kPremiumMultiplier);
|
||||
const auto lottieSize = QSize(lottie, lottie);
|
||||
const auto effectSize = QSize(size, size);
|
||||
const auto createLottieIfReady = [=] {
|
||||
if (state->lottie) {
|
||||
return;
|
||||
}
|
||||
const auto document = media->owner();
|
||||
const auto sticker = document->sticker();
|
||||
if (!sticker || !sticker->isLottie() || !media->loaded()) {
|
||||
return;
|
||||
} else if (media->videoThumbnailContent().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto factor = style::DevicePixelRatio();
|
||||
state->lottie = std::make_unique<Lottie::SinglePlayer>(
|
||||
Lottie::ReadContent(media->bytes(), document->filepath()),
|
||||
Lottie::FrameRequest{ lottieSize * factor },
|
||||
Lottie::Quality::High);
|
||||
state->effect = std::make_unique<Lottie::SinglePlayer>(
|
||||
Lottie::ReadContent(media->videoThumbnailContent(), {}),
|
||||
Lottie::FrameRequest{ effectSize * factor },
|
||||
Lottie::Quality::High);
|
||||
|
||||
const auto update = [=] { raw->update(); };
|
||||
auto &lifetime = raw->lifetime();
|
||||
state->lottie->updates() | rpl::start_with_next(update, lifetime);
|
||||
state->effect->updates() | rpl::start_with_next(update, lifetime);
|
||||
};
|
||||
|
||||
raw->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
createLottieIfReady();
|
||||
|
||||
auto p = QPainter(raw);
|
||||
p.drawImage(0, 0, back);
|
||||
if (!state->lottie
|
||||
|| !state->lottie->ready()
|
||||
|| !state->effect->ready()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto factor = style::DevicePixelRatio();
|
||||
const auto frame = state->lottie->frameInfo({ lottieSize * factor });
|
||||
const auto effect = state->effect->frameInfo(
|
||||
{ effectSize * factor });
|
||||
const auto framesCount = !frame.image.isNull()
|
||||
? state->lottie->framesCount()
|
||||
: 1;
|
||||
const auto effectsCount = !effect.image.isNull()
|
||||
? state->effect->framesCount()
|
||||
: 1;
|
||||
|
||||
const auto left = effectSize.width()
|
||||
- int(lottieSize.width() * (1. + kPremiumShift));
|
||||
const auto top = (effectSize.height() - lottieSize.height()) / 2;
|
||||
p.drawImage(
|
||||
QRect(QPoint(left, top), lottieSize),
|
||||
state->lottie->frame());
|
||||
p.drawImage(raw->rect(), state->effect->frame());
|
||||
|
||||
if (!frame.image.isNull()
|
||||
&& ((frame.index % effectsCount) <= effect.index)) {
|
||||
state->lottie->markFrameShown();
|
||||
}
|
||||
if (!effect.image.isNull()
|
||||
&& ((effect.index % framesCount) <= frame.index)) {
|
||||
state->effect->markFrameShown();
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
class GradientButton final : public Ui::RippleButton {
|
||||
public:
|
||||
GradientButton(QWidget *widget, QGradientStops stops);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void validateBg();
|
||||
|
||||
QGradientStops _stops;
|
||||
QImage _bg;
|
||||
|
||||
};
|
||||
|
||||
GradientButton::GradientButton(QWidget *widget, QGradientStops stops)
|
||||
: RippleButton(widget, st::defaultRippleAnimation)
|
||||
, _stops(std::move(stops)) {
|
||||
}
|
||||
|
||||
void GradientButton::paintEvent(QPaintEvent *e) {
|
||||
QPainter p(this);
|
||||
|
||||
validateBg();
|
||||
p.drawImage(0, 0, _bg);
|
||||
const auto ripple = QColor(0, 0, 0, 36);
|
||||
paintRipple(p, 0, 0, &ripple);
|
||||
}
|
||||
|
||||
void GradientButton::validateBg() {
|
||||
const auto factor = devicePixelRatio();
|
||||
if (!_bg.isNull()
|
||||
&& (_bg.devicePixelRatio() == factor)
|
||||
&& (_bg.size() == size() * factor)) {
|
||||
return;
|
||||
}
|
||||
_bg = QImage(size() * factor, QImage::Format_ARGB32_Premultiplied);
|
||||
_bg.setDevicePixelRatio(factor);
|
||||
|
||||
auto p = QPainter(&_bg);
|
||||
auto gradient = QLinearGradient(QPointF(0, 0), QPointF(width(), 0));
|
||||
gradient.setStops(_stops);
|
||||
p.fillRect(rect(), gradient);
|
||||
p.end();
|
||||
|
||||
_bg = Images::Round(std::move(_bg), ImageRoundRadius::Large);
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::AbstractButton> CreateGradientButton(
|
||||
QWidget *parent,
|
||||
QGradientStops stops) {
|
||||
return object_ptr<GradientButton>(parent, std::move(stops));
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::AbstractButton> CreatePremiumButton(
|
||||
QWidget *parent) {
|
||||
return CreateGradientButton(parent, {
|
||||
{ 0., st::premiumButtonBg1->c },
|
||||
{ 0.6, st::premiumButtonBg2->c },
|
||||
{ 1., st::premiumButtonBg3->c },
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::AbstractButton> CreateUnlockButton(
|
||||
QWidget *parent,
|
||||
int width) {
|
||||
auto result = CreatePremiumButton(parent);
|
||||
const auto &st = st::premiumPreviewBox.button;
|
||||
result->resize(width, st.height);
|
||||
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
result.data(),
|
||||
tr::lng_sticker_premium_button(),
|
||||
st::premiumPreviewButtonLabel);
|
||||
rpl::combine(
|
||||
result->widthValue(),
|
||||
label->widthValue()
|
||||
) | rpl::start_with_next([=](int outer, int width) {
|
||||
label->moveToLeft(
|
||||
(outer - width) / 2,
|
||||
st::premiumPreviewBox.button.textTop,
|
||||
outer);
|
||||
}, label->lifetime());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void StickerBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
const std::shared_ptr<Data::DocumentMedia> &media,
|
||||
const QImage &back) {
|
||||
const auto size = st::boxWideWidth;
|
||||
box->setWidth(size);
|
||||
box->setNoContentMargin(true);
|
||||
box->addRow(StickerPreview(box, media, back, size), {});
|
||||
const auto padding = st::premiumPreviewAboutPadding;
|
||||
auto label = object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_sticker_premium_about(),
|
||||
st::premiumPreviewAbout);
|
||||
label->resizeToWidth(size - padding.left() - padding.right());
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
box,
|
||||
std::move(label)),
|
||||
padding);
|
||||
box->setStyle(st::premiumPreviewBox);
|
||||
const auto buttonPadding = st::premiumPreviewBox.buttonPadding;
|
||||
const auto width = size - buttonPadding.left() - buttonPadding.right();
|
||||
auto button = CreateUnlockButton(box, width);
|
||||
button->setClickedCallback([=] {
|
||||
controller->showSettings();
|
||||
});
|
||||
box->addButton(std::move(button));
|
||||
}
|
||||
|
||||
void Show(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const std::shared_ptr<Data::DocumentMedia> &media,
|
||||
QImage back) {
|
||||
controller->show(Box(StickerBox, controller, media, back));
|
||||
}
|
||||
|
||||
void Show(not_null<Window::SessionController*> controller, QImage back) {
|
||||
auto &list = Preloads();
|
||||
for (auto i = begin(list); i != end(list);) {
|
||||
const auto already = i->controller.get();
|
||||
if (!already) {
|
||||
i = list.erase(i);
|
||||
} else if (already == controller) {
|
||||
Show(controller, i->media, back);
|
||||
i = list.erase(i);
|
||||
return;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage SolidColorImage(QSize size, QColor color) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
auto result = QImage(size * ratio, QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(ratio);
|
||||
result.fill(color);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ShowStickerPreviewBox(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<DocumentData*> document) {
|
||||
auto &list = Preloads();
|
||||
for (auto i = begin(list); i != end(list);) {
|
||||
const auto already = i->controller.get();
|
||||
if (!already) {
|
||||
i = list.erase(i);
|
||||
} else if (already == controller) {
|
||||
if (i->document == document) {
|
||||
return;
|
||||
}
|
||||
i->document = document;
|
||||
i->media = document->createMediaView();
|
||||
PreloadSticker(i->media);
|
||||
return;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
const auto weak = base::make_weak(controller.get());
|
||||
list.push_back({
|
||||
.document = document,
|
||||
.media = document->createMediaView(),
|
||||
.controller = weak,
|
||||
});
|
||||
PreloadSticker(list.back().media);
|
||||
|
||||
const auto fill = QSize(st::boxWideWidth, st::boxWideWidth);
|
||||
const auto theme = controller->currentChatTheme();
|
||||
const auto color = theme->background().colorForFill;
|
||||
const auto area = QSize(fill.width(), fill.height() * 2);
|
||||
const auto request = theme->cacheBackgroundRequest(area);
|
||||
crl::async([=] {
|
||||
using Option = Images::Option;
|
||||
auto back = color
|
||||
? SolidColorImage(fill, *color)
|
||||
: request.background.waitingForNegativePattern()
|
||||
? SolidColorImage(fill, Qt::black)
|
||||
: Ui::CacheBackground(request).image;
|
||||
const auto factor = style::DevicePixelRatio();
|
||||
auto cropped = back.copy(QRect(
|
||||
QPoint(0, fill.height() * factor / 2),
|
||||
fill * factor));
|
||||
cropped.setDevicePixelRatio(factor);
|
||||
const auto options = Images::Options()
|
||||
| Option::RoundSkipBottomLeft
|
||||
| Option::RoundSkipBottomRight
|
||||
| Option::RoundLarge;
|
||||
const auto result = Images::Round(
|
||||
std::move(cropped),
|
||||
Images::CornersMask(st::boxRadius),
|
||||
RectPart::TopLeft | RectPart::TopRight);
|
||||
crl::on_main([=] {
|
||||
if (const auto strong = weak.get()) {
|
||||
Show(strong, result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
18
Telegram/SourceFiles/boxes/sticker_preview_box.h
Normal file
18
Telegram/SourceFiles/boxes/sticker_preview_box.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
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
|
||||
|
||||
class DocumentData;
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
void ShowStickerPreviewBox(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<DocumentData*> document);
|
|
@ -278,3 +278,23 @@ manageEmojiStatusTop: 25px;
|
|||
|
||||
inlineRadialSize: 44px;
|
||||
inlineFileSize: 44px;
|
||||
|
||||
premiumPreviewBox: Box(defaultBox) {
|
||||
buttonPadding: margins(18px, 18px, 18px, 18px);
|
||||
buttonHeight: 44px;
|
||||
button: RoundButton(defaultActiveButton) {
|
||||
height: 44px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
}
|
||||
}
|
||||
premiumPreviewAbout: FlatLabel(defaultFlatLabel) {
|
||||
minWidth: 240px;
|
||||
textFg: membersAboutLimitFg;
|
||||
align: align(top);
|
||||
}
|
||||
premiumPreviewAboutPadding: margins(18px, 23px, 18px, 8px);
|
||||
premiumPreviewButtonLabel: FlatLabel(defaultFlatLabel) {
|
||||
textFg: premiumButtonFg;
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
|
|
|
@ -154,7 +154,8 @@ std::vector<TextPart> ParseText(
|
|||
[](const MTPDmessageEntityBlockquote&) {
|
||||
return Type::Blockquote; },
|
||||
[](const MTPDmessageEntityBankCard&) { return Type::BankCard; },
|
||||
[](const MTPDmessageEntitySpoiler&) { return Type::Spoiler; });
|
||||
[](const MTPDmessageEntitySpoiler&) { return Type::Spoiler; },
|
||||
[](const MTPDmessageEntityAnimatedEmoji&) { return Type::Unknown; });
|
||||
part.text = mid(start, length);
|
||||
part.additional = entity.match(
|
||||
[](const MTPDmessageEntityPre &data) {
|
||||
|
|
|
@ -1606,6 +1606,10 @@ void HistoryWidget::toggleChooseChatTheme(not_null<PeerData*> peer) {
|
|||
) | rpl::start_with_next(update, _chooseTheme->lifetime());
|
||||
}
|
||||
|
||||
Ui::ChatTheme *HistoryWidget::customChatTheme() const {
|
||||
return _list ? _list->theme().get() : nullptr;
|
||||
}
|
||||
|
||||
void HistoryWidget::fieldChanged() {
|
||||
const auto updateTyping = (_textUpdateEvents & TextUpdateEvent::SendTyping);
|
||||
|
||||
|
@ -7821,7 +7825,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
|
|||
|
||||
Window::SectionWidget::PaintBackground(
|
||||
controller(),
|
||||
_list ? _list->theme().get() : controller()->defaultChatTheme().get(),
|
||||
controller()->currentChatTheme(),
|
||||
this,
|
||||
e->rect());
|
||||
|
||||
|
|
|
@ -242,6 +242,7 @@ public:
|
|||
void saveFieldToHistoryLocalDraft();
|
||||
|
||||
void toggleChooseChatTheme(not_null<PeerData*> peer);
|
||||
[[nodiscard]] Ui::ChatTheme *customChatTheme() const;
|
||||
|
||||
void applyCloudDraft(History *history);
|
||||
|
||||
|
|
|
@ -418,9 +418,9 @@ void Element::externalLottieProgressing(bool external) const {
|
|||
}
|
||||
}
|
||||
|
||||
bool Element::externalLottieTill(int frame) const {
|
||||
bool Element::externalLottieTill(ExternalLottieInfo info) const {
|
||||
if (const auto media = _media.get()) {
|
||||
return media->externalLottieTill(frame);
|
||||
return media->externalLottieTill(info);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ enum class InfoDisplayType : char;
|
|||
struct StateRequest;
|
||||
struct TextState;
|
||||
class Media;
|
||||
struct ExternalLottieInfo;
|
||||
|
||||
using PaintContext = Ui::ChatPaintContext;
|
||||
|
||||
|
@ -272,7 +273,7 @@ public:
|
|||
void refreshDataId();
|
||||
|
||||
void externalLottieProgressing(bool external) const;
|
||||
bool externalLottieTill(int frame) const;
|
||||
bool externalLottieTill(ExternalLottieInfo info) const;
|
||||
|
||||
QDateTime dateTime() const;
|
||||
|
||||
|
|
|
@ -25,7 +25,8 @@ namespace HistoryView {
|
|||
namespace {
|
||||
|
||||
constexpr auto kEmojiMultiplier = 3;
|
||||
constexpr auto kPremiumMultiplier = 2.25;
|
||||
constexpr auto kPremiumShift = 0.082;
|
||||
constexpr auto kPremiumMultiplier = 1.5;
|
||||
constexpr auto kEmojiCachesCount = 4;
|
||||
constexpr auto kPremiumCachesCount = 8;
|
||||
constexpr auto kMaxPlays = 5;
|
||||
|
@ -277,7 +278,9 @@ QRect EmojiInteractions::computeRect(
|
|||
const auto fullWidth = view->width();
|
||||
const auto sticker = premium ? _premiumSize : _emojiSize;
|
||||
const auto size = sizeFor(premium);
|
||||
const auto shift = size.width() / 40;
|
||||
const auto shift = premium
|
||||
? int(_premiumSize.width() * kPremiumShift)
|
||||
: (size.width() / 40);
|
||||
const auto inner = view->innerGeometry();
|
||||
const auto rightAligned = view->hasOutLayout()
|
||||
&& !view->delegate()->elementIsChatWide();
|
||||
|
@ -325,7 +328,11 @@ void EmojiInteractions::paint(QPainter &p) {
|
|||
p.drawImage(
|
||||
QRect(rect.topLeft(), frame.image.size() / factor),
|
||||
frame.image);
|
||||
if (!play.premium || play.view->externalLottieTill(frame.index)) {
|
||||
const auto info = HistoryView::ExternalLottieInfo{
|
||||
.frame = frame.index,
|
||||
.count = play.framesCount,
|
||||
};
|
||||
if (!play.premium || play.view->externalLottieTill(info)) {
|
||||
play.lottie->markFrameShown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,11 @@ enum class MediaInBubbleState {
|
|||
TimeId duration,
|
||||
const QString &base);
|
||||
|
||||
struct ExternalLottieInfo {
|
||||
int frame = -1;
|
||||
int count = -1;
|
||||
};
|
||||
|
||||
class Media : public Object {
|
||||
public:
|
||||
explicit Media(not_null<Element*> parent) : _parent(parent) {
|
||||
|
@ -175,11 +180,11 @@ public:
|
|||
|
||||
virtual void externalLottieProgressing(bool external) {
|
||||
}
|
||||
virtual bool externalLottieTill(int frame) {
|
||||
virtual bool externalLottieTill(ExternalLottieInfo info) {
|
||||
return true;
|
||||
}
|
||||
virtual int externalLottieTillFrame() const {
|
||||
return -1;
|
||||
virtual ExternalLottieInfo externalLottieInfo() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual QSize sizeForGroupingOptimal(int maxWidth) const {
|
||||
|
|
|
@ -464,12 +464,12 @@ void UnwrappedMedia::externalLottieProgressing(bool external) {
|
|||
_content->externalLottieProgressing(external);
|
||||
}
|
||||
|
||||
bool UnwrappedMedia::externalLottieTill(int frame) {
|
||||
return _content->externalLottieTill(frame);
|
||||
bool UnwrappedMedia::externalLottieTill(ExternalLottieInfo info) {
|
||||
return _content->externalLottieTill(info);
|
||||
}
|
||||
|
||||
int UnwrappedMedia::externalLottieTillFrame() const {
|
||||
return _content->externalLottieTillFrame();
|
||||
ExternalLottieInfo UnwrappedMedia::externalLottieInfo() const {
|
||||
return _content->externalLottieInfo();
|
||||
}
|
||||
|
||||
int UnwrappedMedia::calculateFullRight(const QRect &inner) const {
|
||||
|
|
|
@ -43,11 +43,11 @@ public:
|
|||
|
||||
virtual void externalLottieProgressing(bool external) {
|
||||
}
|
||||
virtual bool externalLottieTill(int frame) {
|
||||
virtual bool externalLottieTill(ExternalLottieInfo info) {
|
||||
return true;
|
||||
}
|
||||
virtual int externalLottieTillFrame() const {
|
||||
return -1;
|
||||
virtual ExternalLottieInfo externalLottieInfo() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual bool hasHeavyPart() const {
|
||||
|
@ -103,8 +103,8 @@ public:
|
|||
const Lottie::ColorReplacements *replacements) override;
|
||||
|
||||
void externalLottieProgressing(bool external) override;
|
||||
bool externalLottieTill(int frame) override;
|
||||
int externalLottieTillFrame() const override;
|
||||
bool externalLottieTill(ExternalLottieInfo info) override;
|
||||
ExternalLottieInfo externalLottieInfo() const override;
|
||||
|
||||
bool hasHeavyPart() const override {
|
||||
return _content->hasHeavyPart();
|
||||
|
|
|
@ -75,7 +75,7 @@ Sticker::Sticker(
|
|||
if (const auto media = replacing ? replacing->media() : nullptr) {
|
||||
_lottie = media->stickerTakeLottie(_data, _replacements);
|
||||
if (_lottie) {
|
||||
_externalTillFrame = media->externalLottieTillFrame();
|
||||
_externalInfo = media->externalLottieInfo();
|
||||
if (_data->isPremiumSticker()
|
||||
&& !_premiumEffectPlayed) {
|
||||
_premiumEffectPlayed = true;
|
||||
|
@ -217,8 +217,8 @@ void Sticker::paintLottie(
|
|||
const auto count = _lottie->information().framesCount;
|
||||
_frameIndex = frame.index;
|
||||
_framesCount = count;
|
||||
const auto paused = (_externalTillFrame >= 0)
|
||||
? (_frameIndex >= _externalTillFrame)
|
||||
const auto paused = (_externalInfo.frame >= 0)
|
||||
? (_frameIndex % _externalInfo.count >= _externalInfo.frame)
|
||||
: _parent->delegate()->elementIsGifPaused();
|
||||
_nextLastDiceFrame = !paused
|
||||
&& (_diceIndex > 0)
|
||||
|
@ -230,7 +230,7 @@ void Sticker::paintLottie(
|
|||
: (isEmojiSticker()
|
||||
|| !Core::App().settings().loopAnimatedStickers());
|
||||
const auto lastDiceFrame = (_diceIndex > 0) && atTheEnd();
|
||||
const auto switchToNext = (_externalTillFrame >= 0)
|
||||
const auto switchToNext = (_externalInfo.frame >= 0)
|
||||
|| !playOnce
|
||||
|| (!lastDiceFrame && (_frameIndex != 0 || !_lottieOncePlayed));
|
||||
if (!paused
|
||||
|
@ -407,7 +407,7 @@ void Sticker::setupLottie() {
|
|||
_dataMedia.get(),
|
||||
_replacements,
|
||||
ChatHelpers::StickerLottieSize::MessageHistory,
|
||||
size() * cIntRetinaFactor(),
|
||||
size() * style::DevicePixelRatio(),
|
||||
Lottie::Quality::High);
|
||||
if (_data->isPremiumSticker()
|
||||
&& !_premiumEffectPlayed) {
|
||||
|
@ -466,29 +466,32 @@ std::unique_ptr<Lottie::SinglePlayer> Sticker::stickerTakeLottie(
|
|||
}
|
||||
|
||||
void Sticker::externalLottieProgressing(bool external) {
|
||||
_externalTillFrame = !external
|
||||
? -1
|
||||
: (_externalTillFrame > 0)
|
||||
? _externalTillFrame
|
||||
: 0;
|
||||
_externalInfo = !external
|
||||
? ExternalLottieInfo{}
|
||||
: (_externalInfo.frame > 0)
|
||||
? _externalInfo
|
||||
: ExternalLottieInfo{ 0, 2 };
|
||||
}
|
||||
|
||||
bool Sticker::externalLottieTill(int frame) {
|
||||
_externalTillFrame = (_externalTillFrame >= 0) ? frame : -1;
|
||||
bool Sticker::externalLottieTill(ExternalLottieInfo info) {
|
||||
if (_externalInfo.frame >= 0) {
|
||||
_externalInfo = info;
|
||||
}
|
||||
return markFramesTillExternal();
|
||||
}
|
||||
|
||||
int Sticker::externalLottieTillFrame() const {
|
||||
return _externalTillFrame;
|
||||
ExternalLottieInfo Sticker::externalLottieInfo() const {
|
||||
return _externalInfo;
|
||||
}
|
||||
|
||||
bool Sticker::markFramesTillExternal() {
|
||||
if (_externalTillFrame < 0 || !_lottie) {
|
||||
if (_externalInfo.frame < 0 || !_lottie) {
|
||||
return true;
|
||||
} else if (!_lottie->ready()) {
|
||||
return false;
|
||||
}
|
||||
while (_lottie->frameIndex() < _externalTillFrame) {
|
||||
const auto till = _externalInfo.frame % _lottie->framesCount();
|
||||
while (_lottie->frameIndex() < till) {
|
||||
if (!_lottie->markFrameShown()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -52,8 +52,8 @@ public:
|
|||
const Lottie::ColorReplacements *replacements) override;
|
||||
|
||||
void externalLottieProgressing(bool external) override;
|
||||
bool externalLottieTill(int frame) override;
|
||||
int externalLottieTillFrame() const override;
|
||||
bool externalLottieTill(ExternalLottieInfo info) override;
|
||||
ExternalLottieInfo externalLottieInfo() const override;
|
||||
|
||||
bool hasHeavyPart() const override;
|
||||
void unloadHeavyPart() override;
|
||||
|
@ -107,10 +107,10 @@ private:
|
|||
QSize _size;
|
||||
QImage _lastDiceFrame;
|
||||
QString _diceEmoji;
|
||||
ExternalLottieInfo _externalInfo;
|
||||
int _diceIndex = -1;
|
||||
mutable int _frameIndex = -1;
|
||||
mutable int _framesCount = -1;
|
||||
int _externalTillFrame = -1;
|
||||
mutable bool _lottieOncePlayed = false;
|
||||
mutable bool _premiumEffectPlayed = false;
|
||||
mutable bool _nextLastDiceFrame = false;
|
||||
|
|
|
@ -1476,10 +1476,14 @@ void MainWidget::ui_showPeerHistory(
|
|||
floatPlayerCheckVisibility();
|
||||
}
|
||||
|
||||
PeerData *MainWidget::peer() {
|
||||
PeerData *MainWidget::peer() const {
|
||||
return _history->peer();
|
||||
}
|
||||
|
||||
Ui::ChatTheme *MainWidget::customChatTheme() const {
|
||||
return _history->customChatTheme();
|
||||
}
|
||||
|
||||
void MainWidget::saveSectionInStack() {
|
||||
if (_mainSection) {
|
||||
if (auto memento = _mainSection->createMemento()) {
|
||||
|
|
|
@ -70,6 +70,7 @@ struct Content;
|
|||
} // namespace Export
|
||||
|
||||
namespace Ui {
|
||||
class ChatTheme;
|
||||
class ConfirmBox;
|
||||
class ResizeArea;
|
||||
class PlainShadow;
|
||||
|
@ -144,7 +145,8 @@ public:
|
|||
void dialogsToUp();
|
||||
void checkHistoryActivation();
|
||||
|
||||
PeerData *peer();
|
||||
[[nodiscard]] PeerData *peer() const;
|
||||
[[nodiscard]] Ui::ChatTheme *customChatTheme() const;
|
||||
|
||||
int backgroundFromY() const;
|
||||
void showSection(
|
||||
|
|
|
@ -51,7 +51,7 @@ constexpr auto kMinAcceptableContrast = 1.14;// 4.5;
|
|||
return (doubled % 2) ? 0.5 : 1.;
|
||||
}
|
||||
|
||||
[[nodiscard]] CacheBackgroundResult CacheBackground(
|
||||
[[nodiscard]] CacheBackgroundResult CacheBackgroundByRequest(
|
||||
const CacheBackgroundRequest &request) {
|
||||
Expects(!request.area.isEmpty());
|
||||
|
||||
|
@ -205,6 +205,11 @@ bool operator!=(
|
|||
return !(a == b);
|
||||
}
|
||||
|
||||
CacheBackgroundResult CacheBackground(
|
||||
const CacheBackgroundRequest &request) {
|
||||
return CacheBackgroundByRequest(request);
|
||||
}
|
||||
|
||||
CachedBackground::CachedBackground(CacheBackgroundResult &&result)
|
||||
: pixmap(PixmapFromImage(std::move(result.image)))
|
||||
, area(result.area)
|
||||
|
|
|
@ -86,6 +86,9 @@ struct CacheBackgroundResult {
|
|||
bool waitingForNegativePattern = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] CacheBackgroundResult CacheBackground(
|
||||
const CacheBackgroundRequest &request);
|
||||
|
||||
struct CachedBackground {
|
||||
CachedBackground() = default;
|
||||
CachedBackground(CacheBackgroundResult &&result);
|
||||
|
@ -174,6 +177,10 @@ public:
|
|||
[[nodiscard]] rpl::producer<> repaintBackgroundRequests() const;
|
||||
void rotateComplexGradientBackground();
|
||||
|
||||
[[nodiscard]] CacheBackgroundRequest cacheBackgroundRequest(
|
||||
QSize area,
|
||||
int addRotation = 0) const;
|
||||
|
||||
private:
|
||||
void cacheBackground();
|
||||
void cacheBackgroundNow();
|
||||
|
@ -181,9 +188,6 @@ private:
|
|||
const CacheBackgroundRequest &request,
|
||||
Fn<void(CacheBackgroundResult&&)> done = nullptr);
|
||||
void setCachedBackground(CacheBackgroundResult &&cached);
|
||||
[[nodiscard]] CacheBackgroundRequest cacheBackgroundRequest(
|
||||
QSize area,
|
||||
int addRotation = 0) const;
|
||||
[[nodiscard]] bool readyForBackgroundRotation() const;
|
||||
void generateNextBackgroundRotation();
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/ui_utility.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "boxes/sticker_preview_box.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_document.h"
|
||||
|
@ -334,9 +335,7 @@ bool ShowSendPremiumError(
|
|||
|| document->session().user()->isPremium()) {
|
||||
return false;
|
||||
}
|
||||
Ui::ShowMultilineToast({
|
||||
.text = { u"Premium sticker."_q },
|
||||
});
|
||||
ShowStickerPreviewBox(controller, document);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,8 +26,9 @@ namespace Window {
|
|||
namespace {
|
||||
|
||||
constexpr auto kStickerPreviewEmojiLimit = 10;
|
||||
constexpr auto kPremiumMultiplier = 2.25;
|
||||
constexpr auto kPremiumDownscale = 1.5;
|
||||
constexpr auto kPremiumShift = 0.082;
|
||||
constexpr auto kPremiumMultiplier = 1.5;
|
||||
constexpr auto kPremiumDownscale = 1.25;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -71,6 +72,10 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
|
|||
: Lottie::Animation::FrameInfo();
|
||||
const auto image = frame.image;
|
||||
const auto effectImage = effect.image;
|
||||
const auto framesCount = !image.isNull() ? _lottie->framesCount() : 1;
|
||||
const auto effectsCount = !effectImage.isNull()
|
||||
? _effect->framesCount()
|
||||
: 1;
|
||||
const auto pixmap = image.isNull() ? currentImage() : QPixmap();
|
||||
const auto size = image.isNull() ? pixmap.size() : image.size();
|
||||
int w = size.width() / factor, h = size.height() / factor;
|
||||
|
@ -111,10 +116,12 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
|
|||
emojiLeft += _emojiSize + st::stickerEmojiSkip;
|
||||
}
|
||||
}
|
||||
if (!frame.image.isNull() && frame.index <= effect.index) {
|
||||
if (!frame.image.isNull()
|
||||
&& (!_effect || ((frame.index % effectsCount) <= effect.index))) {
|
||||
_lottie->markFrameShown();
|
||||
}
|
||||
if (!effect.image.isNull() && effect.index <= frame.index) {
|
||||
if (!effect.image.isNull()
|
||||
&& ((effect.index % framesCount) <= frame.index)) {
|
||||
_effect->markFrameShown();
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +137,7 @@ QPoint MediaPreviewWidget::innerPosition(QSize size) const {
|
|||
(height() - size.height()) / 2);
|
||||
}
|
||||
const auto outer = size * kPremiumMultiplier;
|
||||
const auto shift = outer.width() / 40;
|
||||
const auto shift = size.width() * kPremiumShift;
|
||||
return outerPosition(size)
|
||||
+ QPoint(
|
||||
outer.width() - size.width() - shift,
|
||||
|
|
|
@ -1654,6 +1654,13 @@ void SessionController::pushLastUsedChatTheme(
|
|||
}
|
||||
}
|
||||
|
||||
not_null<Ui::ChatTheme*> SessionController::currentChatTheme() const {
|
||||
if (const auto custom = content()->customChatTheme()) {
|
||||
return custom;
|
||||
}
|
||||
return defaultChatTheme().get();
|
||||
}
|
||||
|
||||
void SessionController::setChatStyleTheme(
|
||||
const std::shared_ptr<Ui::ChatTheme> &theme) {
|
||||
if (_chatStyleTheme.lock() == theme) {
|
||||
|
|
|
@ -473,6 +473,7 @@ public:
|
|||
void setChatStyleTheme(const std::shared_ptr<Ui::ChatTheme> &theme);
|
||||
void clearCachedChatThemes();
|
||||
void pushLastUsedChatTheme(const std::shared_ptr<Ui::ChatTheme> &theme);
|
||||
[[nodiscard]] not_null<Ui::ChatTheme*> currentChatTheme() const;
|
||||
|
||||
void overridePeerTheme(
|
||||
not_null<PeerData*> peer,
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit fb3bb0ef3d46e61debbe3b3dd996bd5050275ba3
|
||||
Subproject commit b2fd42d374051d2ba8ff2eb180814f5ee2f99c8a
|
Loading…
Add table
Reference in a new issue