Improve premium sticker sync / preview.
This commit is contained in:
parent
3b5ec78f4f
commit
ca731968ca
|
@ -272,6 +272,8 @@ PRIVATE
|
||||||
boxes/sessions_box.h
|
boxes/sessions_box.h
|
||||||
boxes/share_box.cpp
|
boxes/share_box.cpp
|
||||||
boxes/share_box.h
|
boxes/share_box.h
|
||||||
|
boxes/sticker_preview_box.cpp
|
||||||
|
boxes/sticker_preview_box.h
|
||||||
boxes/sticker_set_box.cpp
|
boxes/sticker_set_box.cpp
|
||||||
boxes/sticker_set_box.h
|
boxes/sticker_set_box.h
|
||||||
boxes/stickers_box.cpp
|
boxes/stickers_box.cpp
|
||||||
|
|
|
@ -224,6 +224,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
"lng_limits_increase" = "Increase Limit";
|
"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_flood_error" = "Too many tries. Please try again later.";
|
||||||
"lng_gif_error" = "An error has occurred while reading GIF animation :(";
|
"lng_gif_error" = "An error has occurred while reading GIF animation :(";
|
||||||
"lng_edit_error" = "You cannot edit this message";
|
"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;
|
messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;
|
||||||
messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity;
|
messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity;
|
||||||
messageEntitySpoiler#32ca960f offset:int length:int = MessageEntity;
|
messageEntitySpoiler#32ca960f offset:int length:int = MessageEntity;
|
||||||
|
messageEntityAnimatedEmoji#5eef0214 offset:int length:int = MessageEntity;
|
||||||
|
|
||||||
inputChannelEmpty#ee8c1e86 = InputChannel;
|
inputChannelEmpty#ee8c1e86 = InputChannel;
|
||||||
inputChannel#f35aec28 channel_id:long access_hash:long = InputChannel;
|
inputChannel#f35aec28 channel_id:long access_hash:long = InputChannel;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -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;
|
inlineRadialSize: 44px;
|
||||||
inlineFileSize: 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&) {
|
[](const MTPDmessageEntityBlockquote&) {
|
||||||
return Type::Blockquote; },
|
return Type::Blockquote; },
|
||||||
[](const MTPDmessageEntityBankCard&) { return Type::BankCard; },
|
[](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.text = mid(start, length);
|
||||||
part.additional = entity.match(
|
part.additional = entity.match(
|
||||||
[](const MTPDmessageEntityPre &data) {
|
[](const MTPDmessageEntityPre &data) {
|
||||||
|
|
|
@ -1606,6 +1606,10 @@ void HistoryWidget::toggleChooseChatTheme(not_null<PeerData*> peer) {
|
||||||
) | rpl::start_with_next(update, _chooseTheme->lifetime());
|
) | rpl::start_with_next(update, _chooseTheme->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ui::ChatTheme *HistoryWidget::customChatTheme() const {
|
||||||
|
return _list ? _list->theme().get() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void HistoryWidget::fieldChanged() {
|
void HistoryWidget::fieldChanged() {
|
||||||
const auto updateTyping = (_textUpdateEvents & TextUpdateEvent::SendTyping);
|
const auto updateTyping = (_textUpdateEvents & TextUpdateEvent::SendTyping);
|
||||||
|
|
||||||
|
@ -7821,7 +7825,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
|
||||||
Window::SectionWidget::PaintBackground(
|
Window::SectionWidget::PaintBackground(
|
||||||
controller(),
|
controller(),
|
||||||
_list ? _list->theme().get() : controller()->defaultChatTheme().get(),
|
controller()->currentChatTheme(),
|
||||||
this,
|
this,
|
||||||
e->rect());
|
e->rect());
|
||||||
|
|
||||||
|
|
|
@ -242,6 +242,7 @@ public:
|
||||||
void saveFieldToHistoryLocalDraft();
|
void saveFieldToHistoryLocalDraft();
|
||||||
|
|
||||||
void toggleChooseChatTheme(not_null<PeerData*> peer);
|
void toggleChooseChatTheme(not_null<PeerData*> peer);
|
||||||
|
[[nodiscard]] Ui::ChatTheme *customChatTheme() const;
|
||||||
|
|
||||||
void applyCloudDraft(History *history);
|
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()) {
|
if (const auto media = _media.get()) {
|
||||||
return media->externalLottieTill(frame);
|
return media->externalLottieTill(info);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ enum class InfoDisplayType : char;
|
||||||
struct StateRequest;
|
struct StateRequest;
|
||||||
struct TextState;
|
struct TextState;
|
||||||
class Media;
|
class Media;
|
||||||
|
struct ExternalLottieInfo;
|
||||||
|
|
||||||
using PaintContext = Ui::ChatPaintContext;
|
using PaintContext = Ui::ChatPaintContext;
|
||||||
|
|
||||||
|
@ -272,7 +273,7 @@ public:
|
||||||
void refreshDataId();
|
void refreshDataId();
|
||||||
|
|
||||||
void externalLottieProgressing(bool external) const;
|
void externalLottieProgressing(bool external) const;
|
||||||
bool externalLottieTill(int frame) const;
|
bool externalLottieTill(ExternalLottieInfo info) const;
|
||||||
|
|
||||||
QDateTime dateTime() const;
|
QDateTime dateTime() const;
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@ namespace HistoryView {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kEmojiMultiplier = 3;
|
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 kEmojiCachesCount = 4;
|
||||||
constexpr auto kPremiumCachesCount = 8;
|
constexpr auto kPremiumCachesCount = 8;
|
||||||
constexpr auto kMaxPlays = 5;
|
constexpr auto kMaxPlays = 5;
|
||||||
|
@ -277,7 +278,9 @@ QRect EmojiInteractions::computeRect(
|
||||||
const auto fullWidth = view->width();
|
const auto fullWidth = view->width();
|
||||||
const auto sticker = premium ? _premiumSize : _emojiSize;
|
const auto sticker = premium ? _premiumSize : _emojiSize;
|
||||||
const auto size = sizeFor(premium);
|
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 inner = view->innerGeometry();
|
||||||
const auto rightAligned = view->hasOutLayout()
|
const auto rightAligned = view->hasOutLayout()
|
||||||
&& !view->delegate()->elementIsChatWide();
|
&& !view->delegate()->elementIsChatWide();
|
||||||
|
@ -325,7 +328,11 @@ void EmojiInteractions::paint(QPainter &p) {
|
||||||
p.drawImage(
|
p.drawImage(
|
||||||
QRect(rect.topLeft(), frame.image.size() / factor),
|
QRect(rect.topLeft(), frame.image.size() / factor),
|
||||||
frame.image);
|
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();
|
play.lottie->markFrameShown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,11 @@ enum class MediaInBubbleState {
|
||||||
TimeId duration,
|
TimeId duration,
|
||||||
const QString &base);
|
const QString &base);
|
||||||
|
|
||||||
|
struct ExternalLottieInfo {
|
||||||
|
int frame = -1;
|
||||||
|
int count = -1;
|
||||||
|
};
|
||||||
|
|
||||||
class Media : public Object {
|
class Media : public Object {
|
||||||
public:
|
public:
|
||||||
explicit Media(not_null<Element*> parent) : _parent(parent) {
|
explicit Media(not_null<Element*> parent) : _parent(parent) {
|
||||||
|
@ -175,11 +180,11 @@ public:
|
||||||
|
|
||||||
virtual void externalLottieProgressing(bool external) {
|
virtual void externalLottieProgressing(bool external) {
|
||||||
}
|
}
|
||||||
virtual bool externalLottieTill(int frame) {
|
virtual bool externalLottieTill(ExternalLottieInfo info) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
virtual int externalLottieTillFrame() const {
|
virtual ExternalLottieInfo externalLottieInfo() const {
|
||||||
return -1;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] virtual QSize sizeForGroupingOptimal(int maxWidth) const {
|
[[nodiscard]] virtual QSize sizeForGroupingOptimal(int maxWidth) const {
|
||||||
|
|
|
@ -464,12 +464,12 @@ void UnwrappedMedia::externalLottieProgressing(bool external) {
|
||||||
_content->externalLottieProgressing(external);
|
_content->externalLottieProgressing(external);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UnwrappedMedia::externalLottieTill(int frame) {
|
bool UnwrappedMedia::externalLottieTill(ExternalLottieInfo info) {
|
||||||
return _content->externalLottieTill(frame);
|
return _content->externalLottieTill(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
int UnwrappedMedia::externalLottieTillFrame() const {
|
ExternalLottieInfo UnwrappedMedia::externalLottieInfo() const {
|
||||||
return _content->externalLottieTillFrame();
|
return _content->externalLottieInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
int UnwrappedMedia::calculateFullRight(const QRect &inner) const {
|
int UnwrappedMedia::calculateFullRight(const QRect &inner) const {
|
||||||
|
|
|
@ -43,11 +43,11 @@ public:
|
||||||
|
|
||||||
virtual void externalLottieProgressing(bool external) {
|
virtual void externalLottieProgressing(bool external) {
|
||||||
}
|
}
|
||||||
virtual bool externalLottieTill(int frame) {
|
virtual bool externalLottieTill(ExternalLottieInfo info) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
virtual int externalLottieTillFrame() const {
|
virtual ExternalLottieInfo externalLottieInfo() const {
|
||||||
return -1;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool hasHeavyPart() const {
|
virtual bool hasHeavyPart() const {
|
||||||
|
@ -103,8 +103,8 @@ public:
|
||||||
const Lottie::ColorReplacements *replacements) override;
|
const Lottie::ColorReplacements *replacements) override;
|
||||||
|
|
||||||
void externalLottieProgressing(bool external) override;
|
void externalLottieProgressing(bool external) override;
|
||||||
bool externalLottieTill(int frame) override;
|
bool externalLottieTill(ExternalLottieInfo info) override;
|
||||||
int externalLottieTillFrame() const override;
|
ExternalLottieInfo externalLottieInfo() const override;
|
||||||
|
|
||||||
bool hasHeavyPart() const override {
|
bool hasHeavyPart() const override {
|
||||||
return _content->hasHeavyPart();
|
return _content->hasHeavyPart();
|
||||||
|
|
|
@ -75,7 +75,7 @@ Sticker::Sticker(
|
||||||
if (const auto media = replacing ? replacing->media() : nullptr) {
|
if (const auto media = replacing ? replacing->media() : nullptr) {
|
||||||
_lottie = media->stickerTakeLottie(_data, _replacements);
|
_lottie = media->stickerTakeLottie(_data, _replacements);
|
||||||
if (_lottie) {
|
if (_lottie) {
|
||||||
_externalTillFrame = media->externalLottieTillFrame();
|
_externalInfo = media->externalLottieInfo();
|
||||||
if (_data->isPremiumSticker()
|
if (_data->isPremiumSticker()
|
||||||
&& !_premiumEffectPlayed) {
|
&& !_premiumEffectPlayed) {
|
||||||
_premiumEffectPlayed = true;
|
_premiumEffectPlayed = true;
|
||||||
|
@ -217,8 +217,8 @@ void Sticker::paintLottie(
|
||||||
const auto count = _lottie->information().framesCount;
|
const auto count = _lottie->information().framesCount;
|
||||||
_frameIndex = frame.index;
|
_frameIndex = frame.index;
|
||||||
_framesCount = count;
|
_framesCount = count;
|
||||||
const auto paused = (_externalTillFrame >= 0)
|
const auto paused = (_externalInfo.frame >= 0)
|
||||||
? (_frameIndex >= _externalTillFrame)
|
? (_frameIndex % _externalInfo.count >= _externalInfo.frame)
|
||||||
: _parent->delegate()->elementIsGifPaused();
|
: _parent->delegate()->elementIsGifPaused();
|
||||||
_nextLastDiceFrame = !paused
|
_nextLastDiceFrame = !paused
|
||||||
&& (_diceIndex > 0)
|
&& (_diceIndex > 0)
|
||||||
|
@ -230,7 +230,7 @@ void Sticker::paintLottie(
|
||||||
: (isEmojiSticker()
|
: (isEmojiSticker()
|
||||||
|| !Core::App().settings().loopAnimatedStickers());
|
|| !Core::App().settings().loopAnimatedStickers());
|
||||||
const auto lastDiceFrame = (_diceIndex > 0) && atTheEnd();
|
const auto lastDiceFrame = (_diceIndex > 0) && atTheEnd();
|
||||||
const auto switchToNext = (_externalTillFrame >= 0)
|
const auto switchToNext = (_externalInfo.frame >= 0)
|
||||||
|| !playOnce
|
|| !playOnce
|
||||||
|| (!lastDiceFrame && (_frameIndex != 0 || !_lottieOncePlayed));
|
|| (!lastDiceFrame && (_frameIndex != 0 || !_lottieOncePlayed));
|
||||||
if (!paused
|
if (!paused
|
||||||
|
@ -407,7 +407,7 @@ void Sticker::setupLottie() {
|
||||||
_dataMedia.get(),
|
_dataMedia.get(),
|
||||||
_replacements,
|
_replacements,
|
||||||
ChatHelpers::StickerLottieSize::MessageHistory,
|
ChatHelpers::StickerLottieSize::MessageHistory,
|
||||||
size() * cIntRetinaFactor(),
|
size() * style::DevicePixelRatio(),
|
||||||
Lottie::Quality::High);
|
Lottie::Quality::High);
|
||||||
if (_data->isPremiumSticker()
|
if (_data->isPremiumSticker()
|
||||||
&& !_premiumEffectPlayed) {
|
&& !_premiumEffectPlayed) {
|
||||||
|
@ -466,29 +466,32 @@ std::unique_ptr<Lottie::SinglePlayer> Sticker::stickerTakeLottie(
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sticker::externalLottieProgressing(bool external) {
|
void Sticker::externalLottieProgressing(bool external) {
|
||||||
_externalTillFrame = !external
|
_externalInfo = !external
|
||||||
? -1
|
? ExternalLottieInfo{}
|
||||||
: (_externalTillFrame > 0)
|
: (_externalInfo.frame > 0)
|
||||||
? _externalTillFrame
|
? _externalInfo
|
||||||
: 0;
|
: ExternalLottieInfo{ 0, 2 };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Sticker::externalLottieTill(int frame) {
|
bool Sticker::externalLottieTill(ExternalLottieInfo info) {
|
||||||
_externalTillFrame = (_externalTillFrame >= 0) ? frame : -1;
|
if (_externalInfo.frame >= 0) {
|
||||||
|
_externalInfo = info;
|
||||||
|
}
|
||||||
return markFramesTillExternal();
|
return markFramesTillExternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Sticker::externalLottieTillFrame() const {
|
ExternalLottieInfo Sticker::externalLottieInfo() const {
|
||||||
return _externalTillFrame;
|
return _externalInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Sticker::markFramesTillExternal() {
|
bool Sticker::markFramesTillExternal() {
|
||||||
if (_externalTillFrame < 0 || !_lottie) {
|
if (_externalInfo.frame < 0 || !_lottie) {
|
||||||
return true;
|
return true;
|
||||||
} else if (!_lottie->ready()) {
|
} else if (!_lottie->ready()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
while (_lottie->frameIndex() < _externalTillFrame) {
|
const auto till = _externalInfo.frame % _lottie->framesCount();
|
||||||
|
while (_lottie->frameIndex() < till) {
|
||||||
if (!_lottie->markFrameShown()) {
|
if (!_lottie->markFrameShown()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,8 +52,8 @@ public:
|
||||||
const Lottie::ColorReplacements *replacements) override;
|
const Lottie::ColorReplacements *replacements) override;
|
||||||
|
|
||||||
void externalLottieProgressing(bool external) override;
|
void externalLottieProgressing(bool external) override;
|
||||||
bool externalLottieTill(int frame) override;
|
bool externalLottieTill(ExternalLottieInfo info) override;
|
||||||
int externalLottieTillFrame() const override;
|
ExternalLottieInfo externalLottieInfo() const override;
|
||||||
|
|
||||||
bool hasHeavyPart() const override;
|
bool hasHeavyPart() const override;
|
||||||
void unloadHeavyPart() override;
|
void unloadHeavyPart() override;
|
||||||
|
@ -107,10 +107,10 @@ private:
|
||||||
QSize _size;
|
QSize _size;
|
||||||
QImage _lastDiceFrame;
|
QImage _lastDiceFrame;
|
||||||
QString _diceEmoji;
|
QString _diceEmoji;
|
||||||
|
ExternalLottieInfo _externalInfo;
|
||||||
int _diceIndex = -1;
|
int _diceIndex = -1;
|
||||||
mutable int _frameIndex = -1;
|
mutable int _frameIndex = -1;
|
||||||
mutable int _framesCount = -1;
|
mutable int _framesCount = -1;
|
||||||
int _externalTillFrame = -1;
|
|
||||||
mutable bool _lottieOncePlayed = false;
|
mutable bool _lottieOncePlayed = false;
|
||||||
mutable bool _premiumEffectPlayed = false;
|
mutable bool _premiumEffectPlayed = false;
|
||||||
mutable bool _nextLastDiceFrame = false;
|
mutable bool _nextLastDiceFrame = false;
|
||||||
|
|
|
@ -1476,10 +1476,14 @@ void MainWidget::ui_showPeerHistory(
|
||||||
floatPlayerCheckVisibility();
|
floatPlayerCheckVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
PeerData *MainWidget::peer() {
|
PeerData *MainWidget::peer() const {
|
||||||
return _history->peer();
|
return _history->peer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ui::ChatTheme *MainWidget::customChatTheme() const {
|
||||||
|
return _history->customChatTheme();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWidget::saveSectionInStack() {
|
void MainWidget::saveSectionInStack() {
|
||||||
if (_mainSection) {
|
if (_mainSection) {
|
||||||
if (auto memento = _mainSection->createMemento()) {
|
if (auto memento = _mainSection->createMemento()) {
|
||||||
|
|
|
@ -70,6 +70,7 @@ struct Content;
|
||||||
} // namespace Export
|
} // namespace Export
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
class ChatTheme;
|
||||||
class ConfirmBox;
|
class ConfirmBox;
|
||||||
class ResizeArea;
|
class ResizeArea;
|
||||||
class PlainShadow;
|
class PlainShadow;
|
||||||
|
@ -144,7 +145,8 @@ public:
|
||||||
void dialogsToUp();
|
void dialogsToUp();
|
||||||
void checkHistoryActivation();
|
void checkHistoryActivation();
|
||||||
|
|
||||||
PeerData *peer();
|
[[nodiscard]] PeerData *peer() const;
|
||||||
|
[[nodiscard]] Ui::ChatTheme *customChatTheme() const;
|
||||||
|
|
||||||
int backgroundFromY() const;
|
int backgroundFromY() const;
|
||||||
void showSection(
|
void showSection(
|
||||||
|
|
|
@ -51,7 +51,7 @@ constexpr auto kMinAcceptableContrast = 1.14;// 4.5;
|
||||||
return (doubled % 2) ? 0.5 : 1.;
|
return (doubled % 2) ? 0.5 : 1.;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] CacheBackgroundResult CacheBackground(
|
[[nodiscard]] CacheBackgroundResult CacheBackgroundByRequest(
|
||||||
const CacheBackgroundRequest &request) {
|
const CacheBackgroundRequest &request) {
|
||||||
Expects(!request.area.isEmpty());
|
Expects(!request.area.isEmpty());
|
||||||
|
|
||||||
|
@ -205,6 +205,11 @@ bool operator!=(
|
||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CacheBackgroundResult CacheBackground(
|
||||||
|
const CacheBackgroundRequest &request) {
|
||||||
|
return CacheBackgroundByRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
CachedBackground::CachedBackground(CacheBackgroundResult &&result)
|
CachedBackground::CachedBackground(CacheBackgroundResult &&result)
|
||||||
: pixmap(PixmapFromImage(std::move(result.image)))
|
: pixmap(PixmapFromImage(std::move(result.image)))
|
||||||
, area(result.area)
|
, area(result.area)
|
||||||
|
|
|
@ -86,6 +86,9 @@ struct CacheBackgroundResult {
|
||||||
bool waitingForNegativePattern = false;
|
bool waitingForNegativePattern = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] CacheBackgroundResult CacheBackground(
|
||||||
|
const CacheBackgroundRequest &request);
|
||||||
|
|
||||||
struct CachedBackground {
|
struct CachedBackground {
|
||||||
CachedBackground() = default;
|
CachedBackground() = default;
|
||||||
CachedBackground(CacheBackgroundResult &&result);
|
CachedBackground(CacheBackgroundResult &&result);
|
||||||
|
@ -174,6 +177,10 @@ public:
|
||||||
[[nodiscard]] rpl::producer<> repaintBackgroundRequests() const;
|
[[nodiscard]] rpl::producer<> repaintBackgroundRequests() const;
|
||||||
void rotateComplexGradientBackground();
|
void rotateComplexGradientBackground();
|
||||||
|
|
||||||
|
[[nodiscard]] CacheBackgroundRequest cacheBackgroundRequest(
|
||||||
|
QSize area,
|
||||||
|
int addRotation = 0) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void cacheBackground();
|
void cacheBackground();
|
||||||
void cacheBackgroundNow();
|
void cacheBackgroundNow();
|
||||||
|
@ -181,9 +188,6 @@ private:
|
||||||
const CacheBackgroundRequest &request,
|
const CacheBackgroundRequest &request,
|
||||||
Fn<void(CacheBackgroundResult&&)> done = nullptr);
|
Fn<void(CacheBackgroundResult&&)> done = nullptr);
|
||||||
void setCachedBackground(CacheBackgroundResult &&cached);
|
void setCachedBackground(CacheBackgroundResult &&cached);
|
||||||
[[nodiscard]] CacheBackgroundRequest cacheBackgroundRequest(
|
|
||||||
QSize area,
|
|
||||||
int addRotation = 0) const;
|
|
||||||
[[nodiscard]] bool readyForBackgroundRotation() const;
|
[[nodiscard]] bool readyForBackgroundRotation() const;
|
||||||
void generateNextBackgroundRotation();
|
void generateNextBackgroundRotation();
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/ui_utility.h"
|
#include "ui/ui_utility.h"
|
||||||
#include "ui/chat/chat_theme.h"
|
#include "ui/chat/chat_theme.h"
|
||||||
#include "ui/toasts/common_toasts.h"
|
#include "ui/toasts/common_toasts.h"
|
||||||
|
#include "boxes/sticker_preview_box.h"
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
@ -334,9 +335,7 @@ bool ShowSendPremiumError(
|
||||||
|| document->session().user()->isPremium()) {
|
|| document->session().user()->isPremium()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Ui::ShowMultilineToast({
|
ShowStickerPreviewBox(controller, document);
|
||||||
.text = { u"Premium sticker."_q },
|
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,9 @@ namespace Window {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kStickerPreviewEmojiLimit = 10;
|
constexpr auto kStickerPreviewEmojiLimit = 10;
|
||||||
constexpr auto kPremiumMultiplier = 2.25;
|
constexpr auto kPremiumShift = 0.082;
|
||||||
constexpr auto kPremiumDownscale = 1.5;
|
constexpr auto kPremiumMultiplier = 1.5;
|
||||||
|
constexpr auto kPremiumDownscale = 1.25;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -71,6 +72,10 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
|
||||||
: Lottie::Animation::FrameInfo();
|
: Lottie::Animation::FrameInfo();
|
||||||
const auto image = frame.image;
|
const auto image = frame.image;
|
||||||
const auto effectImage = effect.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 pixmap = image.isNull() ? currentImage() : QPixmap();
|
||||||
const auto size = image.isNull() ? pixmap.size() : image.size();
|
const auto size = image.isNull() ? pixmap.size() : image.size();
|
||||||
int w = size.width() / factor, h = size.height() / factor;
|
int w = size.width() / factor, h = size.height() / factor;
|
||||||
|
@ -111,10 +116,12 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
|
||||||
emojiLeft += _emojiSize + st::stickerEmojiSkip;
|
emojiLeft += _emojiSize + st::stickerEmojiSkip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!frame.image.isNull() && frame.index <= effect.index) {
|
if (!frame.image.isNull()
|
||||||
|
&& (!_effect || ((frame.index % effectsCount) <= effect.index))) {
|
||||||
_lottie->markFrameShown();
|
_lottie->markFrameShown();
|
||||||
}
|
}
|
||||||
if (!effect.image.isNull() && effect.index <= frame.index) {
|
if (!effect.image.isNull()
|
||||||
|
&& ((effect.index % framesCount) <= frame.index)) {
|
||||||
_effect->markFrameShown();
|
_effect->markFrameShown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +137,7 @@ QPoint MediaPreviewWidget::innerPosition(QSize size) const {
|
||||||
(height() - size.height()) / 2);
|
(height() - size.height()) / 2);
|
||||||
}
|
}
|
||||||
const auto outer = size * kPremiumMultiplier;
|
const auto outer = size * kPremiumMultiplier;
|
||||||
const auto shift = outer.width() / 40;
|
const auto shift = size.width() * kPremiumShift;
|
||||||
return outerPosition(size)
|
return outerPosition(size)
|
||||||
+ QPoint(
|
+ QPoint(
|
||||||
outer.width() - size.width() - shift,
|
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(
|
void SessionController::setChatStyleTheme(
|
||||||
const std::shared_ptr<Ui::ChatTheme> &theme) {
|
const std::shared_ptr<Ui::ChatTheme> &theme) {
|
||||||
if (_chatStyleTheme.lock() == theme) {
|
if (_chatStyleTheme.lock() == theme) {
|
||||||
|
|
|
@ -473,6 +473,7 @@ public:
|
||||||
void setChatStyleTheme(const std::shared_ptr<Ui::ChatTheme> &theme);
|
void setChatStyleTheme(const std::shared_ptr<Ui::ChatTheme> &theme);
|
||||||
void clearCachedChatThemes();
|
void clearCachedChatThemes();
|
||||||
void pushLastUsedChatTheme(const std::shared_ptr<Ui::ChatTheme> &theme);
|
void pushLastUsedChatTheme(const std::shared_ptr<Ui::ChatTheme> &theme);
|
||||||
|
[[nodiscard]] not_null<Ui::ChatTheme*> currentChatTheme() const;
|
||||||
|
|
||||||
void overridePeerTheme(
|
void overridePeerTheme(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit fb3bb0ef3d46e61debbe3b3dd996bd5050275ba3
|
Subproject commit b2fd42d374051d2ba8ff2eb180814f5ee2f99c8a
|
Loading…
Reference in New Issue