mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Implement background emoji selector.
This commit is contained in:
parent
bcdb1bdfd2
commit
cb6698cf4a
17 changed files with 445 additions and 118 deletions
|
@ -790,6 +790,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_settings_color_about" = "You can choose a color to tint your name, the links you send, and replies to your messages.";
|
"lng_settings_color_about" = "You can choose a color to tint your name, the links you send, and replies to your messages.";
|
||||||
"lng_settings_color_about_channel" = "You can choose a color to tint your channel's name, the links it sends, and replies to its messages.";
|
"lng_settings_color_about_channel" = "You can choose a color to tint your channel's name, the links it sends, and replies to its messages.";
|
||||||
"lng_settings_color_emoji" = "Add icons to replies";
|
"lng_settings_color_emoji" = "Add icons to replies";
|
||||||
|
"lng_settings_color_emoji_remove" = "Remove icon";
|
||||||
"lng_settings_color_emoji_off" = "Off";
|
"lng_settings_color_emoji_off" = "Off";
|
||||||
"lng_settings_color_emoji_about" = "Make replies to your messages stand out by adding custom patterns to them.";
|
"lng_settings_color_emoji_about" = "Make replies to your messages stand out by adding custom patterns to them.";
|
||||||
"lng_settings_color_emoji_about_channel" = "Make replies to your channel's messages stand out by adding custom patterns to them.";
|
"lng_settings_color_emoji_about_channel" = "Make replies to your channel's messages stand out by adding custom patterns to them.";
|
||||||
|
|
|
@ -510,40 +510,57 @@ void PeerPhoto::requestUserPhotos(
|
||||||
_userPhotosRequests.emplace(user, requestId);
|
_userPhotosRequests.emplace(user, requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto PeerPhoto::emojiList(EmojiListType type) -> EmojiListData & {
|
||||||
|
switch (type) {
|
||||||
|
case EmojiListType::Profile: return _profileEmojiList;
|
||||||
|
case EmojiListType::Group: return _groupEmojiList;
|
||||||
|
case EmojiListType::Background: return _backgroundEmojiList;
|
||||||
|
}
|
||||||
|
Unexpected("Type in PeerPhoto::emojiList.");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PeerPhoto::emojiList(EmojiListType type) const
|
||||||
|
-> const EmojiListData & {
|
||||||
|
return const_cast<PeerPhoto*>(this)->emojiList(type);
|
||||||
|
}
|
||||||
|
|
||||||
void PeerPhoto::requestEmojiList(EmojiListType type) {
|
void PeerPhoto::requestEmojiList(EmojiListType type) {
|
||||||
if (_requestIdEmojiList) {
|
auto &list = emojiList(type);
|
||||||
|
if (list.requestId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto isGroup = (type == EmojiListType::Group);
|
const auto send = [&](auto &&request) {
|
||||||
const auto d = [=](const MTPEmojiList &result) {
|
return _api.request(
|
||||||
_requestIdEmojiList = 0;
|
std::move(request)
|
||||||
result.match([](const MTPDemojiListNotModified &data) {
|
).done([=](const MTPEmojiList &result) {
|
||||||
}, [&](const MTPDemojiList &data) {
|
auto &list = emojiList(type);
|
||||||
auto &list = isGroup ? _profileEmojiList : _groupEmojiList;
|
list.requestId = 0;
|
||||||
list = ranges::views::all(
|
result.match([](const MTPDemojiListNotModified &data) {
|
||||||
data.vdocument_id().v
|
}, [&](const MTPDemojiList &data) {
|
||||||
) | ranges::views::transform(&MTPlong::v) | ranges::to_vector;
|
list.list = ranges::views::all(
|
||||||
});
|
data.vdocument_id().v
|
||||||
|
) | ranges::views::transform(
|
||||||
|
&MTPlong::v
|
||||||
|
) | ranges::to_vector;
|
||||||
|
});
|
||||||
|
}).fail([=] {
|
||||||
|
emojiList(type).requestId = 0;
|
||||||
|
}).send();
|
||||||
};
|
};
|
||||||
const auto f = [=] { _requestIdEmojiList = 0; };
|
list.requestId = (type == EmojiListType::Profile)
|
||||||
_requestIdEmojiList = isGroup
|
? send(MTPaccount_GetDefaultProfilePhotoEmojis())
|
||||||
? _api.request(
|
: (type == EmojiListType::Group)
|
||||||
MTPaccount_GetDefaultGroupPhotoEmojis()
|
? send(MTPaccount_GetDefaultGroupPhotoEmojis())
|
||||||
).done(d).fail(f).send()
|
: send(MTPaccount_GetDefaultBackgroundEmojis());
|
||||||
: _api.request(
|
|
||||||
MTPaccount_GetDefaultProfilePhotoEmojis()
|
|
||||||
).done(d).fail(f).send();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<PeerPhoto::EmojiList> PeerPhoto::emojiListValue(
|
rpl::producer<PeerPhoto::EmojiList> PeerPhoto::emojiListValue(
|
||||||
EmojiListType type) {
|
EmojiListType type) {
|
||||||
auto &list = (type == EmojiListType::Group)
|
auto &list = emojiList(type);
|
||||||
? _profileEmojiList
|
if (list.list.current().empty() && !list.requestId) {
|
||||||
: _groupEmojiList;
|
|
||||||
if (list.current().empty() && !_requestIdEmojiList) {
|
|
||||||
requestEmojiList(type);
|
requestEmojiList(type);
|
||||||
}
|
}
|
||||||
return list.value();
|
return list.list.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-personal photo in case a personal photo is set.
|
// Non-personal photo in case a personal photo is set.
|
||||||
|
|
|
@ -31,6 +31,7 @@ public:
|
||||||
enum class EmojiListType {
|
enum class EmojiListType {
|
||||||
Profile,
|
Profile,
|
||||||
Group,
|
Group,
|
||||||
|
Background,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UserPhoto {
|
struct UserPhoto {
|
||||||
|
@ -73,6 +74,10 @@ private:
|
||||||
Suggestion,
|
Suggestion,
|
||||||
Fallback,
|
Fallback,
|
||||||
};
|
};
|
||||||
|
struct EmojiListData {
|
||||||
|
rpl::variable<EmojiList> list;
|
||||||
|
mtpRequestId requestId = 0;
|
||||||
|
};
|
||||||
|
|
||||||
void ready(
|
void ready(
|
||||||
const FullMsgId &msgId,
|
const FullMsgId &msgId,
|
||||||
|
@ -84,6 +89,9 @@ private:
|
||||||
UploadType type,
|
UploadType type,
|
||||||
Fn<void()> done);
|
Fn<void()> done);
|
||||||
|
|
||||||
|
[[nodiscard]] EmojiListData &emojiList(EmojiListType type);
|
||||||
|
[[nodiscard]] const EmojiListData &emojiList(EmojiListType type) const;
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
MTP::Sender _api;
|
MTP::Sender _api;
|
||||||
|
|
||||||
|
@ -101,9 +109,9 @@ private:
|
||||||
not_null<UserData*>,
|
not_null<UserData*>,
|
||||||
not_null<PhotoData*>> _nonPersonalPhotos;
|
not_null<PhotoData*>> _nonPersonalPhotos;
|
||||||
|
|
||||||
mtpRequestId _requestIdEmojiList = 0;
|
EmojiListData _profileEmojiList;
|
||||||
rpl::variable<EmojiList> _profileEmojiList;
|
EmojiListData _groupEmojiList;
|
||||||
rpl::variable<EmojiList> _groupEmojiList;
|
EmojiListData _backgroundEmojiList;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "chat_helpers/compose/compose_show.h"
|
#include "chat_helpers/compose/compose_show.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_web_page.h"
|
#include "data/data_web_page.h"
|
||||||
|
@ -19,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "info/boosts/info_boosts_widget.h"
|
#include "info/boosts/info_boosts_widget.h"
|
||||||
|
#include "info/profile/info_profile_emoji_status_panel.h"
|
||||||
#include "info/info_memento.h"
|
#include "info/info_memento.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_account.h"
|
#include "main/main_account.h"
|
||||||
|
@ -105,7 +107,8 @@ public:
|
||||||
std::shared_ptr<Ui::ChatStyle> style,
|
std::shared_ptr<Ui::ChatStyle> style,
|
||||||
std::shared_ptr<Ui::ChatTheme> theme,
|
std::shared_ptr<Ui::ChatTheme> theme,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
rpl::producer<uint8> colorIndexValue);
|
rpl::producer<uint8> colorIndexValue,
|
||||||
|
rpl::producer<DocumentId> backgroundEmojiId);
|
||||||
~PreviewWrap();
|
~PreviewWrap();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -253,7 +256,8 @@ PreviewWrap::PreviewWrap(
|
||||||
std::shared_ptr<Ui::ChatStyle> style,
|
std::shared_ptr<Ui::ChatStyle> style,
|
||||||
std::shared_ptr<Ui::ChatTheme> theme,
|
std::shared_ptr<Ui::ChatTheme> theme,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
rpl::producer<uint8> colorIndexValue)
|
rpl::producer<uint8> colorIndexValue,
|
||||||
|
rpl::producer<DocumentId> backgroundEmojiId)
|
||||||
: RpWidget(box)
|
: RpWidget(box)
|
||||||
, _box(box)
|
, _box(box)
|
||||||
, _peer(peer)
|
, _peer(peer)
|
||||||
|
@ -329,6 +333,10 @@ PreviewWrap::PreviewWrap(
|
||||||
_fake->changeColorIndex(index);
|
_fake->changeColorIndex(index);
|
||||||
update();
|
update();
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
std::move(backgroundEmojiId) | rpl::start_with_next([=](DocumentId id) {
|
||||||
|
_fake->changeBackgroundEmojiId(id);
|
||||||
|
update();
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
const auto session = &_history->session();
|
const auto session = &_history->session();
|
||||||
session->data().viewRepaintRequest(
|
session->data().viewRepaintRequest(
|
||||||
|
@ -423,22 +431,28 @@ HistoryView::Context PreviewDelegate::elementContext() {
|
||||||
void Set(
|
void Set(
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
uint8 colorIndex) {
|
uint8 colorIndex,
|
||||||
const auto was = peer->colorIndex();
|
DocumentId backgroundEmojiId) {
|
||||||
peer->changeColorIndex(colorIndex);
|
const auto wasIndex = peer->colorIndex();
|
||||||
peer->session().changes().peerUpdated(
|
const auto wasEmojiId = peer->backgroundEmojiId();
|
||||||
peer,
|
|
||||||
Data::PeerUpdate::Flag::Color);
|
const auto setLocal = [=](uint8 index, DocumentId emojiId) {
|
||||||
|
using UpdateFlag = Data::PeerUpdate::Flag;
|
||||||
|
peer->changeColorIndex(index);
|
||||||
|
peer->changeBackgroundEmojiId(emojiId);
|
||||||
|
peer->session().changes().peerUpdated(
|
||||||
|
peer,
|
||||||
|
UpdateFlag::Color | UpdateFlag::BackgroundEmoji);
|
||||||
|
};
|
||||||
|
setLocal(colorIndex, backgroundEmojiId);
|
||||||
|
|
||||||
const auto done = [=] {
|
const auto done = [=] {
|
||||||
show->showToast(peer->isSelf()
|
show->showToast(peer->isSelf()
|
||||||
? tr::lng_settings_color_changed(tr::now)
|
? tr::lng_settings_color_changed(tr::now)
|
||||||
: tr::lng_settings_color_changed_channel(tr::now));
|
: tr::lng_settings_color_changed_channel(tr::now));
|
||||||
};
|
};
|
||||||
const auto fail = [=](const MTP::Error &error) {
|
const auto fail = [=](const MTP::Error &error) {
|
||||||
peer->changeColorIndex(was);
|
setLocal(wasIndex, wasEmojiId);
|
||||||
peer->session().changes().peerUpdated(
|
|
||||||
peer,
|
|
||||||
Data::PeerUpdate::Flag::Color);
|
|
||||||
show->showToast(error.type());
|
show->showToast(error.type());
|
||||||
};
|
};
|
||||||
const auto send = [&](auto &&request) {
|
const auto send = [&](auto &&request) {
|
||||||
|
@ -451,14 +465,14 @@ void Set(
|
||||||
MTP_flags(
|
MTP_flags(
|
||||||
MTPaccount_UpdateColor::Flag::f_background_emoji_id),
|
MTPaccount_UpdateColor::Flag::f_background_emoji_id),
|
||||||
MTP_int(colorIndex),
|
MTP_int(colorIndex),
|
||||||
MTP_long(peer->backgroundEmojiId())));
|
MTP_long(backgroundEmojiId)));
|
||||||
} else if (const auto channel = peer->asChannel()) {
|
} else if (const auto channel = peer->asChannel()) {
|
||||||
send(MTPchannels_UpdateColor(
|
send(MTPchannels_UpdateColor(
|
||||||
MTP_flags(
|
MTP_flags(
|
||||||
MTPchannels_UpdateColor::Flag::f_background_emoji_id),
|
MTPchannels_UpdateColor::Flag::f_background_emoji_id),
|
||||||
channel->inputChannel,
|
channel->inputChannel,
|
||||||
MTP_int(colorIndex),
|
MTP_int(colorIndex),
|
||||||
MTP_long(peer->backgroundEmojiId())));
|
MTP_long(backgroundEmojiId)));
|
||||||
} else {
|
} else {
|
||||||
Unexpected("Invalid peer type in Set(colorIndex).");
|
Unexpected("Invalid peer type in Set(colorIndex).");
|
||||||
}
|
}
|
||||||
|
@ -468,10 +482,12 @@ void Apply(
|
||||||
std::shared_ptr<ChatHelpers::Show> show,
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
uint8 colorIndex,
|
uint8 colorIndex,
|
||||||
|
DocumentId backgroundEmojiId,
|
||||||
Fn<void()> close,
|
Fn<void()> close,
|
||||||
Fn<void()> cancel) {
|
Fn<void()> cancel) {
|
||||||
const auto session = &peer->session();
|
const auto session = &peer->session();
|
||||||
if (peer->colorIndex() == colorIndex) {
|
if (peer->colorIndex() == colorIndex
|
||||||
|
&& peer->backgroundEmojiId() == backgroundEmojiId) {
|
||||||
close();
|
close();
|
||||||
} else if (peer->isSelf() && !session->premium()) {
|
} else if (peer->isSelf() && !session->premium()) {
|
||||||
Settings::ShowPremiumPromoToast(
|
Settings::ShowPremiumPromoToast(
|
||||||
|
@ -486,7 +502,7 @@ void Apply(
|
||||||
u"name_color"_q);
|
u"name_color"_q);
|
||||||
cancel();
|
cancel();
|
||||||
} else if (peer->isSelf()) {
|
} else if (peer->isSelf()) {
|
||||||
Set(show, peer, colorIndex);
|
Set(show, peer, colorIndex, backgroundEmojiId);
|
||||||
close();
|
close();
|
||||||
} else {
|
} else {
|
||||||
session->api().request(MTPpremium_GetBoostsStatus(
|
session->api().request(MTPpremium_GetBoostsStatus(
|
||||||
|
@ -497,7 +513,7 @@ void Apply(
|
||||||
"channel_color_level_min",
|
"channel_color_level_min",
|
||||||
5);
|
5);
|
||||||
if (data.vlevel().v >= required) {
|
if (data.vlevel().v >= required) {
|
||||||
Set(show, peer, colorIndex);
|
Set(show, peer, colorIndex, backgroundEmojiId);
|
||||||
close();
|
close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -635,6 +651,121 @@ int ColorSelector::resizeGetHeight(int newWidth) {
|
||||||
return (top - skip) + ((count % columns) ? (isize + skip) : 0);
|
return (top - skip) + ((count % columns) ? (isize + skip) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] object_ptr<Ui::SettingsButton> CreateEmojiIconButton(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
std::shared_ptr<ChatHelpers::Show> show,
|
||||||
|
std::shared_ptr<Ui::ChatStyle> style,
|
||||||
|
rpl::producer<uint8> colorIndexValue,
|
||||||
|
rpl::producer<DocumentId> emojiIdValue,
|
||||||
|
Fn<void(DocumentId)> emojiIdChosen) {
|
||||||
|
const auto &basicSt = st::settingsButtonNoIcon;
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
const auto added = st::normalFont->spacew;
|
||||||
|
const auto emojiSize = Data::FrameSizeFromTag({}) / ratio;
|
||||||
|
const auto noneWidth = added
|
||||||
|
+ st::normalFont->width(tr::lng_settings_color_emoji_off(tr::now));
|
||||||
|
const auto emojiWidth = added + emojiSize;
|
||||||
|
const auto rightPadding = std::max(noneWidth, emojiWidth)
|
||||||
|
+ basicSt.padding.right();
|
||||||
|
const auto st = parent->lifetime().make_state<style::SettingsButton>(
|
||||||
|
basicSt);
|
||||||
|
st->padding.setRight(rightPadding);
|
||||||
|
auto result = CreateButton(
|
||||||
|
parent,
|
||||||
|
tr::lng_settings_color_emoji(),
|
||||||
|
*st,
|
||||||
|
{});
|
||||||
|
const auto raw = result.data();
|
||||||
|
|
||||||
|
const auto right = Ui::CreateChild<Ui::RpWidget>(raw);
|
||||||
|
right->show();
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
Info::Profile::EmojiStatusPanel panel;
|
||||||
|
std::unique_ptr<Ui::Text::CustomEmoji> emoji;
|
||||||
|
DocumentId emojiId = 0;
|
||||||
|
uint8 index = 0;
|
||||||
|
};
|
||||||
|
const auto state = right->lifetime().make_state<State>();
|
||||||
|
state->panel.backgroundEmojiChosen(
|
||||||
|
) | rpl::start_with_next(emojiIdChosen, raw->lifetime());
|
||||||
|
|
||||||
|
std::move(colorIndexValue) | rpl::start_with_next([=](uint8 index) {
|
||||||
|
state->index = index;
|
||||||
|
if (state->emoji) {
|
||||||
|
right->update();
|
||||||
|
}
|
||||||
|
}, right->lifetime());
|
||||||
|
|
||||||
|
const auto session = &show->session();
|
||||||
|
std::move(emojiIdValue) | rpl::start_with_next([=](DocumentId emojiId) {
|
||||||
|
state->emojiId = emojiId;
|
||||||
|
state->emoji = emojiId
|
||||||
|
? session->data().customEmojiManager().create(
|
||||||
|
emojiId,
|
||||||
|
[=] { right->update(); })
|
||||||
|
: nullptr;
|
||||||
|
right->resize(
|
||||||
|
(emojiId ? emojiWidth : noneWidth) + added,
|
||||||
|
right->height());
|
||||||
|
right->update();
|
||||||
|
}, right->lifetime());
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
raw->sizeValue(),
|
||||||
|
right->widthValue()
|
||||||
|
) | rpl::start_with_next([=](QSize outer, int width) {
|
||||||
|
right->resize(width, outer.height());
|
||||||
|
const auto skip = st::settingsButton.padding.right();
|
||||||
|
right->moveToRight(skip - added, 0, outer.width());
|
||||||
|
}, right->lifetime());
|
||||||
|
|
||||||
|
right->paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
if (state->panel.paintBadgeFrame(right)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto p = QPainter(right);
|
||||||
|
const auto height = right->height();
|
||||||
|
if (state->emoji) {
|
||||||
|
const auto colors = style->coloredValues(false, state->index);
|
||||||
|
state->emoji->paint(p, {
|
||||||
|
.textColor = colors.name,
|
||||||
|
.position = QPoint(added, (height - emojiSize) / 2),
|
||||||
|
.internal = {
|
||||||
|
.forceFirstFrame = true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const auto &font = st::normalFont;
|
||||||
|
p.setFont(font);
|
||||||
|
p.setPen(style->windowActiveTextFg());
|
||||||
|
p.drawText(
|
||||||
|
QPoint(added, (height - font->height) / 2 + font->ascent),
|
||||||
|
tr::lng_settings_color_emoji_off(tr::now));
|
||||||
|
}
|
||||||
|
}, right->lifetime());
|
||||||
|
|
||||||
|
raw->setClickedCallback([=] {
|
||||||
|
const auto customTextColor = [=] {
|
||||||
|
return style->coloredValues(false, state->index).name;
|
||||||
|
};
|
||||||
|
const auto controller = show->resolveWindow(
|
||||||
|
ChatHelpers::WindowUsage::PremiumPromo);
|
||||||
|
if (controller) {
|
||||||
|
state->panel.show({
|
||||||
|
.controller = controller,
|
||||||
|
.button = right,
|
||||||
|
.currentBackgroundEmojiId = state->emojiId,
|
||||||
|
.customTextColor = customTextColor,
|
||||||
|
.backgroundEmojiMode = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void EditPeerColorBox(
|
void EditPeerColorBox(
|
||||||
|
@ -648,18 +779,21 @@ void EditPeerColorBox(
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
rpl::variable<uint8> index;
|
rpl::variable<uint8> index;
|
||||||
|
rpl::variable<DocumentId> emojiId;
|
||||||
bool changing = false;
|
bool changing = false;
|
||||||
bool applying = false;
|
bool applying = false;
|
||||||
};
|
};
|
||||||
const auto state = box->lifetime().make_state<State>();
|
const auto state = box->lifetime().make_state<State>();
|
||||||
state->index = peer->colorIndex();
|
state->index = peer->colorIndex();
|
||||||
|
state->emojiId = peer->backgroundEmojiId();
|
||||||
|
|
||||||
box->addRow(object_ptr<PreviewWrap>(
|
box->addRow(object_ptr<PreviewWrap>(
|
||||||
box,
|
box,
|
||||||
style,
|
style,
|
||||||
theme,
|
theme,
|
||||||
peer,
|
peer,
|
||||||
state->index.value()
|
state->index.value(),
|
||||||
|
state->emojiId.value()
|
||||||
), {});
|
), {});
|
||||||
|
|
||||||
const auto appConfig = &peer->session().account().appConfig();
|
const auto appConfig = &peer->session().account().appConfig();
|
||||||
|
@ -691,12 +825,29 @@ void EditPeerColorBox(
|
||||||
? tr::lng_settings_color_about()
|
? tr::lng_settings_color_about()
|
||||||
: tr::lng_settings_color_about_channel());
|
: tr::lng_settings_color_about_channel());
|
||||||
|
|
||||||
|
AddSkip(container, st::settingsColorSampleSkip);
|
||||||
|
|
||||||
|
container->add(CreateEmojiIconButton(
|
||||||
|
container,
|
||||||
|
show,
|
||||||
|
style,
|
||||||
|
state->index.value(),
|
||||||
|
state->emojiId.value(),
|
||||||
|
[=](DocumentId id) { state->emojiId = id; }));
|
||||||
|
|
||||||
|
AddSkip(container, st::settingsColorSampleSkip);
|
||||||
|
AddDividerText(container, peer->isSelf()
|
||||||
|
? tr::lng_settings_color_emoji_about()
|
||||||
|
: tr::lng_settings_color_emoji_about_channel());
|
||||||
|
|
||||||
box->addButton(tr::lng_settings_apply(), [=] {
|
box->addButton(tr::lng_settings_apply(), [=] {
|
||||||
if (state->applying) {
|
if (state->applying) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state->applying = true;
|
state->applying = true;
|
||||||
Apply(show, peer, state->index.current(), crl::guard(box, [=] {
|
const auto index = state->index.current();
|
||||||
|
const auto emojiId = state->emojiId.current();
|
||||||
|
Apply(show, peer, index, emojiId, crl::guard(box, [=] {
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
}), crl::guard(box, [=] {
|
}), crl::guard(box, [=] {
|
||||||
state->applying = false;
|
state->applying = false;
|
||||||
|
|
|
@ -662,6 +662,9 @@ statusEmojiPan: EmojiPan(defaultEmojiPan) {
|
||||||
fadeLeft: icon {{ "fade_horizontal-flip_horizontal", windowBg }};
|
fadeLeft: icon {{ "fade_horizontal-flip_horizontal", windowBg }};
|
||||||
fadeRight: icon {{ "fade_horizontal", windowBg }};
|
fadeRight: icon {{ "fade_horizontal", windowBg }};
|
||||||
}
|
}
|
||||||
|
backgroundEmojiPan: EmojiPan(defaultEmojiPan) {
|
||||||
|
padding: margins(7px, 7px, 4px, 0px);
|
||||||
|
}
|
||||||
|
|
||||||
inlineBotsScroll: ScrollArea(defaultSolidScroll) {
|
inlineBotsScroll: ScrollArea(defaultSolidScroll) {
|
||||||
deltat: stickerPanPadding;
|
deltat: stickerPanPadding;
|
||||||
|
|
|
@ -467,6 +467,7 @@ EmojiListWidget::EmojiListWidget(
|
||||||
, _localSetsManager(
|
, _localSetsManager(
|
||||||
std::make_unique<LocalStickersManager>(&session()))
|
std::make_unique<LocalStickersManager>(&session()))
|
||||||
, _customRecentFactory(std::move(descriptor.customRecentFactory))
|
, _customRecentFactory(std::move(descriptor.customRecentFactory))
|
||||||
|
, _customTextColor(std::move(descriptor.customTextColor))
|
||||||
, _overBg(st::emojiPanRadius, st().overBg)
|
, _overBg(st::emojiPanRadius, st().overBg)
|
||||||
, _collapsedBg(st::emojiPanExpand.height / 2, st().headerFg)
|
, _collapsedBg(st::emojiPanExpand.height / 2, st().headerFg)
|
||||||
, _picker(this, st())
|
, _picker(this, st())
|
||||||
|
@ -476,7 +477,7 @@ EmojiListWidget::EmojiListWidget(
|
||||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_mode != Mode::RecentReactions) {
|
if (_mode != Mode::RecentReactions && _mode != Mode::BackgroundEmoji) {
|
||||||
setupSearch();
|
setupSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -791,10 +792,12 @@ object_ptr<TabbedSelector::InnerFooter> EmojiListWidget::createFooter() {
|
||||||
};
|
};
|
||||||
auto result = object_ptr<StickersListFooter>(FooterDescriptor{
|
auto result = object_ptr<StickersListFooter>(FooterDescriptor{
|
||||||
.session = &session(),
|
.session = &session(),
|
||||||
|
.customTextColor = _customTextColor,
|
||||||
.paused = footerPaused,
|
.paused = footerPaused,
|
||||||
.parent = this,
|
.parent = this,
|
||||||
.st = &st(),
|
.st = &st(),
|
||||||
.features = { .stickersSettings = false },
|
.features = { .stickersSettings = false },
|
||||||
|
.forceFirstFrame = (_mode == Mode::BackgroundEmoji),
|
||||||
});
|
});
|
||||||
_footer = result;
|
_footer = result;
|
||||||
|
|
||||||
|
@ -1027,6 +1030,14 @@ void EmojiListWidget::fillRecentFrom(const std::vector<DocumentId> &list) {
|
||||||
if (!id && _mode == Mode::EmojiStatus) {
|
if (!id && _mode == Mode::EmojiStatus) {
|
||||||
const auto star = QString::fromUtf8("\xe2\xad\x90\xef\xb8\x8f");
|
const auto star = QString::fromUtf8("\xe2\xad\x90\xef\xb8\x8f");
|
||||||
_recent.push_back({ .id = { Ui::Emoji::Find(star) } });
|
_recent.push_back({ .id = { Ui::Emoji::Find(star) } });
|
||||||
|
} else if (!id && _mode == Mode::BackgroundEmoji) {
|
||||||
|
const auto fakeId = DocumentId(5246772116543512028ULL);
|
||||||
|
const auto no = QString::fromUtf8("\xe2\x9b\x94\xef\xb8\x8f");
|
||||||
|
_recent.push_back({
|
||||||
|
.custom = resolveCustomRecent(fakeId),
|
||||||
|
.id = { Ui::Emoji::Find(no) },
|
||||||
|
});
|
||||||
|
_recentCustomIds.emplace(fakeId);
|
||||||
} else {
|
} else {
|
||||||
_recent.push_back({
|
_recent.push_back({
|
||||||
.custom = resolveCustomRecent(id),
|
.custom = resolveCustomRecent(id),
|
||||||
|
@ -1188,7 +1199,9 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
|
||||||
void EmojiListWidget::validateEmojiPaintContext(
|
void EmojiListWidget::validateEmojiPaintContext(
|
||||||
const ExpandingContext &context) {
|
const ExpandingContext &context) {
|
||||||
auto value = Ui::Text::CustomEmojiPaintContext{
|
auto value = Ui::Text::CustomEmojiPaintContext{
|
||||||
.textColor = (_mode == Mode::EmojiStatus
|
.textColor = (_customTextColor
|
||||||
|
? _customTextColor()
|
||||||
|
: (_mode == Mode::EmojiStatus)
|
||||||
? anim::color(
|
? anim::color(
|
||||||
st::stickerPanPremium1,
|
st::stickerPanPremium1,
|
||||||
st::stickerPanPremium2,
|
st::stickerPanPremium2,
|
||||||
|
@ -1199,6 +1212,7 @@ void EmojiListWidget::validateEmojiPaintContext(
|
||||||
.scale = context.progress,
|
.scale = context.progress,
|
||||||
.paused = On(powerSavingFlag()) || paused(),
|
.paused = On(powerSavingFlag()) || paused(),
|
||||||
.scaled = context.expanding,
|
.scaled = context.expanding,
|
||||||
|
.internal = { .forceFirstFrame = (_mode == Mode::BackgroundEmoji) },
|
||||||
};
|
};
|
||||||
if (!_emojiPaintContext) {
|
if (!_emojiPaintContext) {
|
||||||
_emojiPaintContext = std::make_unique<
|
_emojiPaintContext = std::make_unique<
|
||||||
|
@ -1384,7 +1398,17 @@ void EmojiListWidget::drawRecent(
|
||||||
_emojiPaintContext->position = position
|
_emojiPaintContext->position = position
|
||||||
+ _innerPosition
|
+ _innerPosition
|
||||||
+ _customPosition;
|
+ _customPosition;
|
||||||
|
const auto isResetIcon = (_mode == Mode::BackgroundEmoji)
|
||||||
|
&& v::is<EmojiPtr>(recent.id.data);
|
||||||
|
if (isResetIcon) {
|
||||||
|
_emojiPaintContext->textColor = st::windowSubTextFg->c;
|
||||||
|
}
|
||||||
custom->paint(p, *_emojiPaintContext);
|
custom->paint(p, *_emojiPaintContext);
|
||||||
|
if (isResetIcon) {
|
||||||
|
_emojiPaintContext->textColor = _customTextColor
|
||||||
|
? _customTextColor()
|
||||||
|
: st().textFg->c;
|
||||||
|
}
|
||||||
} else if (const auto emoji = std::get_if<EmojiPtr>(&recent.id.data)) {
|
} else if (const auto emoji = std::get_if<EmojiPtr>(&recent.id.data)) {
|
||||||
if (_mode == Mode::EmojiStatus) {
|
if (_mode == Mode::EmojiStatus) {
|
||||||
position += QPoint(
|
position += QPoint(
|
||||||
|
@ -1629,6 +1653,9 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
case Mode::TopicIcon:
|
case Mode::TopicIcon:
|
||||||
Settings::ShowPremium(resolved, u"forum_topic_icon"_q);
|
Settings::ShowPremium(resolved, u"forum_topic_icon"_q);
|
||||||
break;
|
break;
|
||||||
|
case Mode::BackgroundEmoji:
|
||||||
|
Settings::ShowPremium(resolved, u"name_color"_q);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1995,7 +2022,10 @@ void EmojiListWidget::refreshCustom() {
|
||||||
const auto &sets = owner->stickers().sets();
|
const auto &sets = owner->stickers().sets();
|
||||||
const auto push = [&](uint64 setId, bool installed) {
|
const auto push = [&](uint64 setId, bool installed) {
|
||||||
auto it = sets.find(setId);
|
auto it = sets.find(setId);
|
||||||
if (it == sets.cend() || it->second->stickers.isEmpty()) {
|
if (it == sets.cend()
|
||||||
|
|| it->second->stickers.isEmpty()
|
||||||
|
|| (_mode == Mode::BackgroundEmoji
|
||||||
|
&& !it->second->textColor())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto canRemove = !!(it->second->flags
|
const auto canRemove = !!(it->second->flags
|
||||||
|
|
|
@ -74,11 +74,13 @@ enum class EmojiListMode {
|
||||||
FullReactions,
|
FullReactions,
|
||||||
RecentReactions,
|
RecentReactions,
|
||||||
UserpicBuilder,
|
UserpicBuilder,
|
||||||
|
BackgroundEmoji,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EmojiListDescriptor {
|
struct EmojiListDescriptor {
|
||||||
std::shared_ptr<Show> show;
|
std::shared_ptr<Show> show;
|
||||||
EmojiListMode mode = EmojiListMode::Full;
|
EmojiListMode mode = EmojiListMode::Full;
|
||||||
|
Fn<QColor()> customTextColor;
|
||||||
Fn<bool()> paused;
|
Fn<bool()> paused;
|
||||||
std::vector<DocumentId> customRecentList;
|
std::vector<DocumentId> customRecentList;
|
||||||
Fn<std::unique_ptr<Ui::Text::CustomEmoji>(
|
Fn<std::unique_ptr<Ui::Text::CustomEmoji>(
|
||||||
|
@ -386,6 +388,7 @@ private:
|
||||||
base::flat_map<
|
base::flat_map<
|
||||||
DocumentId,
|
DocumentId,
|
||||||
std::unique_ptr<Ui::Text::CustomEmoji>> _customRecent;
|
std::unique_ptr<Ui::Text::CustomEmoji>> _customRecent;
|
||||||
|
Fn<QColor()> _customTextColor;
|
||||||
int _customSingleSize = 0;
|
int _customSingleSize = 0;
|
||||||
bool _allowWithoutPremium = false;
|
bool _allowWithoutPremium = false;
|
||||||
Ui::RoundRect _overBg;
|
Ui::RoundRect _overBg;
|
||||||
|
|
|
@ -291,12 +291,14 @@ StickersListFooter::StickersListFooter(Descriptor &&descriptor)
|
||||||
descriptor.parent,
|
descriptor.parent,
|
||||||
descriptor.st ? *descriptor.st : st::defaultEmojiPan)
|
descriptor.st ? *descriptor.st : st::defaultEmojiPan)
|
||||||
, _session(descriptor.session)
|
, _session(descriptor.session)
|
||||||
, _paused(descriptor.paused)
|
, _customTextColor(std::move(descriptor.customTextColor))
|
||||||
|
, _paused(std::move(descriptor.paused))
|
||||||
, _features(descriptor.features)
|
, _features(descriptor.features)
|
||||||
, _iconState([=] { update(); })
|
, _iconState([=] { update(); })
|
||||||
, _subiconState([=] { update(); })
|
, _subiconState([=] { update(); })
|
||||||
, _selectionBg(st::emojiPanRadius, st().categoriesBgOver)
|
, _selectionBg(st::emojiPanRadius, st().categoriesBgOver)
|
||||||
, _subselectionBg(st().iconArea / 2, st().categoriesBgOver) {
|
, _subselectionBg(st().iconArea / 2, st().categoriesBgOver)
|
||||||
|
, _forceFirstFrame(descriptor.forceFirstFrame) {
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
|
|
||||||
_iconsLeft = st().iconSkip
|
_iconsLeft = st().iconSkip
|
||||||
|
@ -1345,13 +1347,16 @@ void StickersListFooter::paintSetIconToCache(
|
||||||
const auto y = (st().footer - icon.pixh) / 2;
|
const auto y = (st().footer - icon.pixh) / 2;
|
||||||
if (icon.custom) {
|
if (icon.custom) {
|
||||||
icon.custom->paint(p, Ui::Text::CustomEmoji::Context{
|
icon.custom->paint(p, Ui::Text::CustomEmoji::Context{
|
||||||
.textColor = st().textFg->c,
|
.textColor = (_customTextColor
|
||||||
|
? _customTextColor()
|
||||||
|
: st().textFg->c),
|
||||||
.size = QSize(icon.pixw, icon.pixh),
|
.size = QSize(icon.pixw, icon.pixh),
|
||||||
.now = now,
|
.now = now,
|
||||||
.scale = context.progress,
|
.scale = context.progress,
|
||||||
.position = { x, y },
|
.position = { x, y },
|
||||||
.paused = paused,
|
.paused = paused,
|
||||||
.scaled = context.expanding,
|
.scaled = context.expanding,
|
||||||
|
.internal = { .forceFirstFrame = _forceFirstFrame },
|
||||||
});
|
});
|
||||||
} else if (icon.lottie && icon.lottie->ready()) {
|
} else if (icon.lottie && icon.lottie->ready()) {
|
||||||
const auto frame = icon.lottie->frame();
|
const auto frame = icon.lottie->frame();
|
||||||
|
@ -1428,11 +1433,13 @@ void StickersListFooter::paintSetIconToCache(
|
||||||
return icons[index];
|
return icons[index];
|
||||||
};
|
};
|
||||||
const auto paintOne = [&](int left, const style::icon *icon) {
|
const auto paintOne = [&](int left, const style::icon *icon) {
|
||||||
icon->paint(
|
left += (_singleWidth - icon->width()) / 2;
|
||||||
p,
|
const auto top = (st().footer - icon->height()) / 2;
|
||||||
left + (_singleWidth - icon->width()) / 2,
|
if (_customTextColor) {
|
||||||
(st().footer - icon->height()) / 2,
|
icon->paint(p, left, top, width(), _customTextColor());
|
||||||
width());
|
} else {
|
||||||
|
icon->paint(p, left, top, width());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if (_icons[info.index].setId == AllEmojiSectionSetId()
|
if (_icons[info.index].setId == AllEmojiSectionSetId()
|
||||||
&& info.width > _singleWidth) {
|
&& info.width > _singleWidth) {
|
||||||
|
|
|
@ -115,10 +115,12 @@ class StickersListFooter final : public TabbedSelector::InnerFooter {
|
||||||
public:
|
public:
|
||||||
struct Descriptor {
|
struct Descriptor {
|
||||||
not_null<Main::Session*> session;
|
not_null<Main::Session*> session;
|
||||||
|
Fn<QColor()> customTextColor;
|
||||||
Fn<bool()> paused;
|
Fn<bool()> paused;
|
||||||
not_null<RpWidget*> parent;
|
not_null<RpWidget*> parent;
|
||||||
const style::EmojiPan *st = nullptr;
|
const style::EmojiPan *st = nullptr;
|
||||||
ComposeFeatures features;
|
ComposeFeatures features;
|
||||||
|
bool forceFirstFrame = false;
|
||||||
};
|
};
|
||||||
explicit StickersListFooter(Descriptor &&descriptor);
|
explicit StickersListFooter(Descriptor &&descriptor);
|
||||||
|
|
||||||
|
@ -269,6 +271,7 @@ private:
|
||||||
void clipCallback(Media::Clip::Notification notification, uint64 setId);
|
void clipCallback(Media::Clip::Notification notification, uint64 setId);
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
|
const Fn<QColor()> _customTextColor;
|
||||||
const Fn<bool()> _paused;
|
const Fn<bool()> _paused;
|
||||||
const ComposeFeatures _features;
|
const ComposeFeatures _features;
|
||||||
|
|
||||||
|
@ -303,6 +306,7 @@ private:
|
||||||
int _subiconsWidth = 0;
|
int _subiconsWidth = 0;
|
||||||
bool _subiconsExpanded = false;
|
bool _subiconsExpanded = false;
|
||||||
bool _repaintScheduled = false;
|
bool _repaintScheduled = false;
|
||||||
|
bool _forceFirstFrame = false;
|
||||||
|
|
||||||
rpl::event_stream<> _openSettingsRequests;
|
rpl::event_stream<> _openSettingsRequests;
|
||||||
rpl::event_stream<uint64> _setChosen;
|
rpl::event_stream<uint64> _setChosen;
|
||||||
|
|
|
@ -331,7 +331,7 @@ TabbedSelector::TabbedSelector(
|
||||||
Mode mode)
|
Mode mode)
|
||||||
: TabbedSelector(parent, {
|
: TabbedSelector(parent, {
|
||||||
.show = std::move(show),
|
.show = std::move(show),
|
||||||
.st = (mode == Mode::EmojiStatus
|
.st = ((mode == Mode::EmojiStatus || mode == Mode::BackgroundEmoji)
|
||||||
? st::statusEmojiPan
|
? st::statusEmojiPan
|
||||||
: st::defaultEmojiPan),
|
: st::defaultEmojiPan),
|
||||||
.level = level,
|
.level = level,
|
||||||
|
@ -347,6 +347,7 @@ TabbedSelector::TabbedSelector(
|
||||||
, _features(descriptor.features)
|
, _features(descriptor.features)
|
||||||
, _show(std::move(descriptor.show))
|
, _show(std::move(descriptor.show))
|
||||||
, _level(descriptor.level)
|
, _level(descriptor.level)
|
||||||
|
, _customTextColor(std::move(descriptor.customTextColor))
|
||||||
, _mode(descriptor.mode)
|
, _mode(descriptor.mode)
|
||||||
, _panelRounding(Ui::PrepareCornerPixmaps(st::emojiPanRadius, _st.bg))
|
, _panelRounding(Ui::PrepareCornerPixmaps(st::emojiPanRadius, _st.bg))
|
||||||
, _categoriesRounding(
|
, _categoriesRounding(
|
||||||
|
@ -512,7 +513,10 @@ TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type, int index) {
|
||||||
.show = _show,
|
.show = _show,
|
||||||
.mode = (_mode == Mode::EmojiStatus
|
.mode = (_mode == Mode::EmojiStatus
|
||||||
? EmojiMode::EmojiStatus
|
? EmojiMode::EmojiStatus
|
||||||
|
: _mode == Mode::BackgroundEmoji
|
||||||
|
? EmojiMode::BackgroundEmoji
|
||||||
: EmojiMode::Full),
|
: EmojiMode::Full),
|
||||||
|
.customTextColor = _customTextColor,
|
||||||
.paused = paused,
|
.paused = paused,
|
||||||
.st = &_st,
|
.st = &_st,
|
||||||
.features = _features,
|
.features = _features,
|
||||||
|
|
|
@ -81,6 +81,7 @@ enum class TabbedSelectorMode {
|
||||||
EmojiOnly,
|
EmojiOnly,
|
||||||
MediaEditor,
|
MediaEditor,
|
||||||
EmojiStatus,
|
EmojiStatus,
|
||||||
|
BackgroundEmoji,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TabbedSelectorDescriptor {
|
struct TabbedSelectorDescriptor {
|
||||||
|
@ -88,6 +89,7 @@ struct TabbedSelectorDescriptor {
|
||||||
const style::EmojiPan &st;
|
const style::EmojiPan &st;
|
||||||
PauseReason level = {};
|
PauseReason level = {};
|
||||||
TabbedSelectorMode mode = TabbedSelectorMode::Full;
|
TabbedSelectorMode mode = TabbedSelectorMode::Full;
|
||||||
|
Fn<QColor()> customTextColor;
|
||||||
ComposeFeatures features;
|
ComposeFeatures features;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -272,6 +274,7 @@ private:
|
||||||
const ComposeFeatures _features;
|
const ComposeFeatures _features;
|
||||||
const std::shared_ptr<Show> _show;
|
const std::shared_ptr<Show> _show;
|
||||||
const PauseReason _level = {};
|
const PauseReason _level = {};
|
||||||
|
const Fn<QColor()> _customTextColor;
|
||||||
|
|
||||||
Mode _mode = Mode::Full;
|
Mode _mode = Mode::Full;
|
||||||
int _roundRadius = 0;
|
int _roundRadius = 0;
|
||||||
|
|
|
@ -53,7 +53,8 @@ StickersSetFlags ParseStickersSetFlags(const MTPDstickerSet &data) {
|
||||||
| (data.is_masks() ? Flag::Masks : Flag())
|
| (data.is_masks() ? Flag::Masks : Flag())
|
||||||
| (data.is_emojis() ? Flag::Emoji : Flag())
|
| (data.is_emojis() ? Flag::Emoji : Flag())
|
||||||
| (data.vinstalled_date() ? Flag::Installed : Flag())
|
| (data.vinstalled_date() ? Flag::Installed : Flag())
|
||||||
| (data.is_videos() ? Flag::Webm : Flag());
|
| (data.is_videos() ? Flag::Webm : Flag())
|
||||||
|
| (data.is_text_color() ? Flag::TextColor : Flag());
|
||||||
}
|
}
|
||||||
|
|
||||||
StickersSet::StickersSet(
|
StickersSet::StickersSet(
|
||||||
|
@ -108,6 +109,10 @@ StickersType StickersSet::type() const {
|
||||||
: StickersType::Stickers;
|
: StickersType::Stickers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool StickersSet::textColor() const {
|
||||||
|
return flags & StickersSetFlag::TextColor;
|
||||||
|
}
|
||||||
|
|
||||||
void StickersSet::setThumbnail(const ImageWithLocation &data) {
|
void StickersSet::setThumbnail(const ImageWithLocation &data) {
|
||||||
Data::UpdateCloudFile(
|
Data::UpdateCloudFile(
|
||||||
_thumbnail,
|
_thumbnail,
|
||||||
|
|
|
@ -57,6 +57,7 @@ enum class StickersSetFlag {
|
||||||
Special = (1 << 7),
|
Special = (1 << 7),
|
||||||
Webm = (1 << 8),
|
Webm = (1 << 8),
|
||||||
Emoji = (1 << 9),
|
Emoji = (1 << 9),
|
||||||
|
TextColor = (1 << 10),
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(StickersSetFlag) { return true; };
|
inline constexpr bool is_flag_type(StickersSetFlag) { return true; };
|
||||||
using StickersSetFlags = base::flags<StickersSetFlag>;
|
using StickersSetFlags = base::flags<StickersSetFlag>;
|
||||||
|
@ -84,6 +85,7 @@ public:
|
||||||
[[nodiscard]] MTPInputStickerSet mtpInput() const;
|
[[nodiscard]] MTPInputStickerSet mtpInput() const;
|
||||||
[[nodiscard]] StickerSetIdentifier identifier() const;
|
[[nodiscard]] StickerSetIdentifier identifier() const;
|
||||||
[[nodiscard]] StickersType type() const;
|
[[nodiscard]] StickersType type() const;
|
||||||
|
[[nodiscard]] bool textColor() const;
|
||||||
|
|
||||||
void setThumbnail(const ImageWithLocation &data);
|
void setThumbnail(const ImageWithLocation &data);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "info/profile/info_profile_emoji_status_panel.h"
|
#include "info/profile/info_profile_emoji_status_panel.h"
|
||||||
|
|
||||||
|
#include "api/api_peer_photo.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
@ -66,26 +68,17 @@ void EmojiStatusPanel::show(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
not_null<QWidget*> button,
|
not_null<QWidget*> button,
|
||||||
Data::CustomEmojiSizeTag animationSizeTag) {
|
Data::CustomEmojiSizeTag animationSizeTag) {
|
||||||
const auto self = controller->session().user();
|
show({
|
||||||
const auto &statuses = controller->session().data().emojiStatuses();
|
.controller = controller,
|
||||||
const auto &recent = statuses.list(Data::EmojiStatuses::Type::Recent);
|
.button = button,
|
||||||
const auto &other = statuses.list(Data::EmojiStatuses::Type::Default);
|
.animationSizeTag = animationSizeTag,
|
||||||
auto list = statuses.list(Data::EmojiStatuses::Type::Colored);
|
});
|
||||||
list.insert(begin(list), 0);
|
}
|
||||||
if (list.size() > kLimitFirstRow) {
|
|
||||||
list.erase(begin(list) + kLimitFirstRow, end(list));
|
void EmojiStatusPanel::show(Descriptor &&descriptor) {
|
||||||
}
|
const auto controller = descriptor.controller;
|
||||||
list.reserve(list.size() + recent.size() + other.size() + 1);
|
|
||||||
for (const auto &id : ranges::views::concat(recent, other)) {
|
|
||||||
if (!ranges::contains(list, id)) {
|
|
||||||
list.push_back(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!ranges::contains(list, self->emojiStatusId())) {
|
|
||||||
list.push_back(self->emojiStatusId());
|
|
||||||
}
|
|
||||||
if (!_panel) {
|
if (!_panel) {
|
||||||
create(controller);
|
create(descriptor);
|
||||||
|
|
||||||
_panel->shownValue(
|
_panel->shownValue(
|
||||||
) | rpl::filter([=] {
|
) | rpl::filter([=] {
|
||||||
|
@ -98,23 +91,67 @@ void EmojiStatusPanel::show(
|
||||||
}
|
}
|
||||||
}, _panel->lifetime());
|
}, _panel->lifetime());
|
||||||
}
|
}
|
||||||
|
const auto button = descriptor.button;
|
||||||
if (const auto previous = _panelButton.data()) {
|
if (const auto previous = _panelButton.data()) {
|
||||||
if (previous != button) {
|
if (previous != button) {
|
||||||
previous->removeEventFilter(_panel.get());
|
previous->removeEventFilter(_panel.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_panelButton = button;
|
_panelButton = button;
|
||||||
_animationSizeTag = animationSizeTag;
|
_animationSizeTag = descriptor.animationSizeTag;
|
||||||
_panel->selector()->provideRecentEmoji(list);
|
auto list = std::vector<DocumentId>();
|
||||||
|
if (descriptor.backgroundEmojiMode) {
|
||||||
|
controller->session().api().peerPhoto().emojiListValue(
|
||||||
|
Api::PeerPhoto::EmojiListType::Background
|
||||||
|
) | rpl::start_with_next([=](std::vector<DocumentId> &&list) {
|
||||||
|
list.insert(begin(list), 0);
|
||||||
|
if (const auto now = descriptor.currentBackgroundEmojiId) {
|
||||||
|
if (!ranges::contains(list, now)) {
|
||||||
|
list.push_back(now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_panel->selector()->provideRecentEmoji(list);
|
||||||
|
}, _panel->lifetime());
|
||||||
|
} else {
|
||||||
|
const auto self = controller->session().user();
|
||||||
|
const auto &statuses = controller->session().data().emojiStatuses();
|
||||||
|
const auto &recent = statuses.list(Data::EmojiStatuses::Type::Recent);
|
||||||
|
const auto &other = statuses.list(Data::EmojiStatuses::Type::Default);
|
||||||
|
auto list = statuses.list(Data::EmojiStatuses::Type::Colored);
|
||||||
|
list.insert(begin(list), 0);
|
||||||
|
if (list.size() > kLimitFirstRow) {
|
||||||
|
list.erase(begin(list) + kLimitFirstRow, end(list));
|
||||||
|
}
|
||||||
|
list.reserve(list.size() + recent.size() + other.size() + 1);
|
||||||
|
for (const auto &id : ranges::views::concat(recent, other)) {
|
||||||
|
if (!ranges::contains(list, id)) {
|
||||||
|
list.push_back(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ranges::contains(list, self->emojiStatusId())) {
|
||||||
|
list.push_back(self->emojiStatusId());
|
||||||
|
}
|
||||||
|
_panel->selector()->provideRecentEmoji(list);
|
||||||
|
}
|
||||||
const auto parent = _panel->parentWidget();
|
const auto parent = _panel->parentWidget();
|
||||||
const auto global = button->mapToGlobal(QPoint());
|
const auto global = button->mapToGlobal(QPoint());
|
||||||
const auto local = parent->mapFromGlobal(global);
|
const auto local = parent->mapFromGlobal(global);
|
||||||
_panel->moveTopRight(
|
if (descriptor.backgroundEmojiMode) {
|
||||||
local.y() + button->height() - (st::normalFont->height / 2),
|
_panel->moveBottomRight(
|
||||||
local.x() + button->width() * 3);
|
local.y() + (st::normalFont->height / 2),
|
||||||
|
local.x() + button->width() * 3);
|
||||||
|
} else {
|
||||||
|
_panel->moveTopRight(
|
||||||
|
local.y() + button->height() - (st::normalFont->height / 2),
|
||||||
|
local.x() + button->width() * 3);
|
||||||
|
}
|
||||||
_panel->toggleAnimated();
|
_panel->toggleAnimated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmojiStatusPanel::repaint() {
|
||||||
|
_panel->selector()->update();
|
||||||
|
}
|
||||||
|
|
||||||
bool EmojiStatusPanel::paintBadgeFrame(not_null<Ui::RpWidget*> widget) {
|
bool EmojiStatusPanel::paintBadgeFrame(not_null<Ui::RpWidget*> widget) {
|
||||||
if (!_animation) {
|
if (!_animation) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -125,19 +162,31 @@ bool EmojiStatusPanel::paintBadgeFrame(not_null<Ui::RpWidget*> widget) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiStatusPanel::create(
|
void EmojiStatusPanel::create(const Descriptor &descriptor) {
|
||||||
not_null<Window::SessionController*> controller) {
|
|
||||||
using Selector = ChatHelpers::TabbedSelector;
|
using Selector = ChatHelpers::TabbedSelector;
|
||||||
|
using Descriptor = ChatHelpers::TabbedSelectorDescriptor;
|
||||||
|
using Mode = ChatHelpers::TabbedSelector::Mode;
|
||||||
|
const auto controller = descriptor.controller;
|
||||||
const auto body = controller->window().widget()->bodyWidget();
|
const auto body = controller->window().widget()->bodyWidget();
|
||||||
_panel = base::make_unique_q<ChatHelpers::TabbedPanel>(
|
_panel = base::make_unique_q<ChatHelpers::TabbedPanel>(
|
||||||
body,
|
body,
|
||||||
controller,
|
controller,
|
||||||
object_ptr<Selector>(
|
object_ptr<Selector>(
|
||||||
nullptr,
|
nullptr,
|
||||||
controller->uiShow(),
|
Descriptor{
|
||||||
Window::GifPauseReason::Layer,
|
.show = controller->uiShow(),
|
||||||
ChatHelpers::TabbedSelector::Mode::EmojiStatus));
|
.st = (descriptor.backgroundEmojiMode
|
||||||
_panel->setDropDown(true);
|
? st::backgroundEmojiPan
|
||||||
|
: st::statusEmojiPan),
|
||||||
|
.level = Window::GifPauseReason::Layer,
|
||||||
|
.mode = (descriptor.backgroundEmojiMode
|
||||||
|
? Mode::BackgroundEmoji
|
||||||
|
: Mode::EmojiStatus),
|
||||||
|
.customTextColor = descriptor.customTextColor,
|
||||||
|
}));
|
||||||
|
_customTextColor = descriptor.customTextColor;
|
||||||
|
_backgroundEmojiMode = descriptor.backgroundEmojiMode;
|
||||||
|
_panel->setDropDown(!_backgroundEmojiMode);
|
||||||
_panel->setDesiredHeightValues(
|
_panel->setDesiredHeightValues(
|
||||||
1.,
|
1.,
|
||||||
st::emojiPanMinHeight / 2,
|
st::emojiPanMinHeight / 2,
|
||||||
|
@ -169,34 +218,46 @@ void EmojiStatusPanel::create(
|
||||||
return Chosen{ .animation = data.messageSendingFrom };
|
return Chosen{ .animation = data.messageSendingFrom };
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto weak = Ui::MakeWeak(_panel.get());
|
if (descriptor.backgroundEmojiMode) {
|
||||||
const auto accept = [=](Chosen chosen) {
|
rpl::merge(
|
||||||
Expects(chosen.until != Selector::kPickCustomTimeId);
|
std::move(statusChosen),
|
||||||
|
std::move(emojiChosen)
|
||||||
// From PickUntilBox is called after EmojiStatusPanel is destroyed!
|
) | rpl::start_with_next([=](const Chosen &chosen) {
|
||||||
const auto owner = &controller->session().data();
|
const auto owner = &controller->session().data();
|
||||||
if (weak) {
|
|
||||||
startAnimation(owner, body, chosen.id, chosen.animation);
|
startAnimation(owner, body, chosen.id, chosen.animation);
|
||||||
}
|
_backgroundEmojiChosen.fire_copy(chosen.id);
|
||||||
owner->emojiStatuses().set(chosen.id, chosen.until);
|
_panel->hideAnimated();
|
||||||
};
|
}, _panel->lifetime());
|
||||||
|
} else {
|
||||||
|
const auto weak = Ui::MakeWeak(_panel.get());
|
||||||
|
const auto accept = [=](Chosen chosen) {
|
||||||
|
Expects(chosen.until != Selector::kPickCustomTimeId);
|
||||||
|
|
||||||
rpl::merge(
|
// PickUntilBox calls this after EmojiStatusPanel is destroyed!
|
||||||
std::move(statusChosen),
|
const auto owner = &controller->session().data();
|
||||||
std::move(emojiChosen)
|
if (weak) {
|
||||||
) | rpl::filter([=](const Chosen &chosen) {
|
startAnimation(owner, body, chosen.id, chosen.animation);
|
||||||
return filter(controller, chosen.id);
|
}
|
||||||
}) | rpl::start_with_next([=](const Chosen &chosen) {
|
owner->emojiStatuses().set(chosen.id, chosen.until);
|
||||||
if (chosen.until == Selector::kPickCustomTimeId) {
|
};
|
||||||
_panel->hideAnimated();
|
|
||||||
controller->show(Box(PickUntilBox, [=](TimeId seconds) {
|
rpl::merge(
|
||||||
accept({ chosen.id, base::unixtime::now() + seconds });
|
std::move(statusChosen),
|
||||||
}));
|
std::move(emojiChosen)
|
||||||
} else {
|
) | rpl::filter([=](const Chosen &chosen) {
|
||||||
accept(chosen);
|
return filter(controller, chosen.id);
|
||||||
_panel->hideAnimated();
|
}) | rpl::start_with_next([=](const Chosen &chosen) {
|
||||||
}
|
if (chosen.until == Selector::kPickCustomTimeId) {
|
||||||
}, _panel->lifetime());
|
_panel->hideAnimated();
|
||||||
|
controller->show(Box(PickUntilBox, [=](TimeId seconds) {
|
||||||
|
accept({ chosen.id, base::unixtime::now() + seconds });
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
accept(chosen);
|
||||||
|
_panel->hideAnimated();
|
||||||
|
}
|
||||||
|
}, _panel->lifetime());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmojiStatusPanel::filter(
|
bool EmojiStatusPanel::filter(
|
||||||
|
@ -223,13 +284,17 @@ void EmojiStatusPanel::startAnimation(
|
||||||
.id = { { statusId } },
|
.id = { { statusId } },
|
||||||
.flyIcon = from.frame,
|
.flyIcon = from.frame,
|
||||||
.flyFrom = body->mapFromGlobal(from.globalStartGeometry),
|
.flyFrom = body->mapFromGlobal(from.globalStartGeometry),
|
||||||
|
.forceFirstFrame = _backgroundEmojiMode,
|
||||||
};
|
};
|
||||||
|
const auto color = _customTextColor
|
||||||
|
? _customTextColor
|
||||||
|
: [] { return st::profileVerifiedCheckBg->c; };
|
||||||
_animation = std::make_unique<Ui::EmojiFlyAnimation>(
|
_animation = std::make_unique<Ui::EmojiFlyAnimation>(
|
||||||
body,
|
body,
|
||||||
&owner->reactions(),
|
&owner->reactions(),
|
||||||
std::move(args),
|
std::move(args),
|
||||||
[=] { _animation->repaint(); },
|
[=] { _animation->repaint(); },
|
||||||
[] { return st::profileVerifiedCheckBg->c; },
|
_customTextColor,
|
||||||
_animationSizeTag);
|
_animationSizeTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,10 +47,25 @@ public:
|
||||||
not_null<QWidget*> button,
|
not_null<QWidget*> button,
|
||||||
Data::CustomEmojiSizeTag animationSizeTag = {});
|
Data::CustomEmojiSizeTag animationSizeTag = {});
|
||||||
|
|
||||||
|
struct Descriptor {
|
||||||
|
not_null<Window::SessionController*> controller;
|
||||||
|
not_null<QWidget*> button;
|
||||||
|
Data::CustomEmojiSizeTag animationSizeTag = {};
|
||||||
|
DocumentId currentBackgroundEmojiId = 0;
|
||||||
|
Fn<QColor()> customTextColor;
|
||||||
|
bool backgroundEmojiMode = false;
|
||||||
|
};
|
||||||
|
void show(Descriptor &&descriptor);
|
||||||
|
void repaint();
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<DocumentId> backgroundEmojiChosen() const {
|
||||||
|
return _backgroundEmojiChosen.events();
|
||||||
|
}
|
||||||
|
|
||||||
bool paintBadgeFrame(not_null<Ui::RpWidget*> widget);
|
bool paintBadgeFrame(not_null<Ui::RpWidget*> widget);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void create(not_null<Window::SessionController*> controller);
|
void create(const Descriptor &descriptor);
|
||||||
[[nodiscard]] bool filter(
|
[[nodiscard]] bool filter(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
DocumentId chosenId) const;
|
DocumentId chosenId) const;
|
||||||
|
@ -62,10 +77,13 @@ private:
|
||||||
Ui::MessageSendingAnimationFrom from);
|
Ui::MessageSendingAnimationFrom from);
|
||||||
|
|
||||||
base::unique_qptr<ChatHelpers::TabbedPanel> _panel;
|
base::unique_qptr<ChatHelpers::TabbedPanel> _panel;
|
||||||
|
Fn<QColor()> _customTextColor;
|
||||||
Fn<bool(DocumentId)> _chooseFilter;
|
Fn<bool(DocumentId)> _chooseFilter;
|
||||||
QPointer<QWidget> _panelButton;
|
QPointer<QWidget> _panelButton;
|
||||||
std::unique_ptr<Ui::EmojiFlyAnimation> _animation;
|
std::unique_ptr<Ui::EmojiFlyAnimation> _animation;
|
||||||
|
rpl::event_stream<DocumentId> _backgroundEmojiChosen;
|
||||||
Data::CustomEmojiSizeTag _animationSizeTag = {};
|
Data::CustomEmojiSizeTag _animationSizeTag = {};
|
||||||
|
bool _backgroundEmojiMode = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,8 @@ ReactionFlyAnimation::ReactionFlyAnimation(
|
||||||
, _repaint(std::move(repaint))
|
, _repaint(std::move(repaint))
|
||||||
, _flyFrom(args.flyFrom)
|
, _flyFrom(args.flyFrom)
|
||||||
, _scaleOutDuration(args.scaleOutDuration)
|
, _scaleOutDuration(args.scaleOutDuration)
|
||||||
, _scaleOutTarget(args.scaleOutTarget) {
|
, _scaleOutTarget(args.scaleOutTarget)
|
||||||
|
, _forceFirstFrame(args.forceFirstFrame) {
|
||||||
const auto &list = owner->list(::Data::Reactions::Type::All);
|
const auto &list = owner->list(::Data::Reactions::Type::All);
|
||||||
auto centerIcon = (DocumentData*)nullptr;
|
auto centerIcon = (DocumentData*)nullptr;
|
||||||
auto aroundAnimation = (DocumentData*)nullptr;
|
auto aroundAnimation = (DocumentData*)nullptr;
|
||||||
|
@ -251,6 +252,7 @@ void ReactionFlyAnimation::paintCenterFrame(
|
||||||
target.x() + (target.width() - _customSize) / 2,
|
target.x() + (target.width() - _customSize) / 2,
|
||||||
target.y() + (target.height() - _customSize) / 2),
|
target.y() + (target.height() - _customSize) / 2),
|
||||||
.scaled = scaled,
|
.scaled = scaled,
|
||||||
|
.internal = { .forceFirstFrame = _forceFirstFrame },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,6 +280,7 @@ void ReactionFlyAnimation::paintMiniCopies(
|
||||||
.size = size,
|
.size = size,
|
||||||
.now = now,
|
.now = now,
|
||||||
.scaled = true,
|
.scaled = true,
|
||||||
|
.internal = { .forceFirstFrame = _forceFirstFrame },
|
||||||
};
|
};
|
||||||
for (const auto &mini : _miniCopies) {
|
for (const auto &mini : _miniCopies) {
|
||||||
if (progress >= mini.duration) {
|
if (progress >= mini.duration) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ struct ReactionFlyAnimationArgs {
|
||||||
float64 scaleOutTarget = 0.;
|
float64 scaleOutTarget = 0.;
|
||||||
float64 miniCopyMultiplier = 1.;
|
float64 miniCopyMultiplier = 1.;
|
||||||
bool effectOnly = false;
|
bool effectOnly = false;
|
||||||
|
bool forceFirstFrame = false;
|
||||||
|
|
||||||
[[nodiscard]] ReactionFlyAnimationArgs translated(QPoint point) const;
|
[[nodiscard]] ReactionFlyAnimationArgs translated(QPoint point) const;
|
||||||
};
|
};
|
||||||
|
@ -42,6 +43,7 @@ struct ReactionFlyCenter {
|
||||||
float64 centerSizeMultiplier = 0.;
|
float64 centerSizeMultiplier = 0.;
|
||||||
int customSize = 0;
|
int customSize = 0;
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
bool forceFirstFrame = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ReactionFlyAnimation final {
|
class ReactionFlyAnimation final {
|
||||||
|
@ -121,6 +123,7 @@ private:
|
||||||
crl::time _scaleOutDuration = 0;
|
crl::time _scaleOutDuration = 0;
|
||||||
float64 _scaleOutTarget = 0.;
|
float64 _scaleOutTarget = 0.;
|
||||||
bool _noEffectScaleStarted = false;
|
bool _noEffectScaleStarted = false;
|
||||||
|
bool _forceFirstFrame = false;
|
||||||
bool _effectOnly = false;
|
bool _effectOnly = false;
|
||||||
bool _valid = false;
|
bool _valid = false;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue