From ae3496d6a43b4d6d5e01c97291b0512c68645e7a Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 5 Oct 2022 11:42:44 +0400 Subject: [PATCH] Update API scheme on layer 148: Topic icons. --- Telegram/Resources/art/topic_icons/blue.svg | 18 +++ Telegram/Resources/art/topic_icons/gray.svg | 18 +++ Telegram/Resources/art/topic_icons/green.svg | 18 +++ Telegram/Resources/art/topic_icons/red.svg | 18 +++ Telegram/Resources/art/topic_icons/rose.svg | 18 +++ Telegram/Resources/art/topic_icons/violet.svg | 18 +++ Telegram/Resources/art/topic_icons/yellow.svg | 18 +++ Telegram/Resources/qrc/telegram/telegram.qrc | 7 + Telegram/Resources/tl/api.tl | 12 +- .../boxes/peers/edit_forum_topic_box.cpp | 105 ++++++-------- .../SourceFiles/data/data_cloud_themes.cpp | 6 +- Telegram/SourceFiles/data/data_forum.cpp | 8 +- Telegram/SourceFiles/data/data_forum.h | 2 + .../SourceFiles/data/data_forum_topic.cpp | 134 +++++++++++++++++- Telegram/SourceFiles/data/data_forum_topic.h | 19 +++ Telegram/SourceFiles/data/data_histories.cpp | 25 ++-- .../SourceFiles/data/data_replies_list.cpp | 2 +- Telegram/SourceFiles/data/data_session.cpp | 30 ++-- Telegram/SourceFiles/data/data_wall_paper.cpp | 45 +++--- Telegram/SourceFiles/data/data_wall_paper.h | 15 +- Telegram/SourceFiles/dialogs/dialogs.style | 17 +++ .../export/data/export_data_types.cpp | 15 +- .../export/data/export_data_types.h | 10 +- .../export/output/export_output_html.cpp | 23 +-- .../export/output/export_output_json.cpp | 15 +- Telegram/SourceFiles/history/history.cpp | 16 +-- .../SourceFiles/history/history_service.cpp | 50 +++---- .../history/view/history_view_element.cpp | 4 +- .../SourceFiles/ui/color_int_conversion.cpp | 25 ++++ .../SourceFiles/ui/color_int_conversion.h | 16 +++ Telegram/cmake/td_ui.cmake | 2 + 31 files changed, 525 insertions(+), 204 deletions(-) create mode 100644 Telegram/Resources/art/topic_icons/blue.svg create mode 100644 Telegram/Resources/art/topic_icons/gray.svg create mode 100644 Telegram/Resources/art/topic_icons/green.svg create mode 100644 Telegram/Resources/art/topic_icons/red.svg create mode 100644 Telegram/Resources/art/topic_icons/rose.svg create mode 100644 Telegram/Resources/art/topic_icons/violet.svg create mode 100644 Telegram/Resources/art/topic_icons/yellow.svg create mode 100644 Telegram/SourceFiles/ui/color_int_conversion.cpp create mode 100644 Telegram/SourceFiles/ui/color_int_conversion.h diff --git a/Telegram/Resources/art/topic_icons/blue.svg b/Telegram/Resources/art/topic_icons/blue.svg new file mode 100644 index 000000000..eb2a99bfc --- /dev/null +++ b/Telegram/Resources/art/topic_icons/blue.svg @@ -0,0 +1,18 @@ + + + topic blue + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Telegram/Resources/art/topic_icons/gray.svg b/Telegram/Resources/art/topic_icons/gray.svg new file mode 100644 index 000000000..b4db98c3f --- /dev/null +++ b/Telegram/Resources/art/topic_icons/gray.svg @@ -0,0 +1,18 @@ + + + topic gray + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Telegram/Resources/art/topic_icons/green.svg b/Telegram/Resources/art/topic_icons/green.svg new file mode 100644 index 000000000..9140843ed --- /dev/null +++ b/Telegram/Resources/art/topic_icons/green.svg @@ -0,0 +1,18 @@ + + + topic green + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Telegram/Resources/art/topic_icons/red.svg b/Telegram/Resources/art/topic_icons/red.svg new file mode 100644 index 000000000..8b7293c8c --- /dev/null +++ b/Telegram/Resources/art/topic_icons/red.svg @@ -0,0 +1,18 @@ + + + topic red + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Telegram/Resources/art/topic_icons/rose.svg b/Telegram/Resources/art/topic_icons/rose.svg new file mode 100644 index 000000000..a730c0ea7 --- /dev/null +++ b/Telegram/Resources/art/topic_icons/rose.svg @@ -0,0 +1,18 @@ + + + topic rose + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Telegram/Resources/art/topic_icons/violet.svg b/Telegram/Resources/art/topic_icons/violet.svg new file mode 100644 index 000000000..a54dfc5d2 --- /dev/null +++ b/Telegram/Resources/art/topic_icons/violet.svg @@ -0,0 +1,18 @@ + + + topic violet + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Telegram/Resources/art/topic_icons/yellow.svg b/Telegram/Resources/art/topic_icons/yellow.svg new file mode 100644 index 000000000..c478ddfbe --- /dev/null +++ b/Telegram/Resources/art/topic_icons/yellow.svg @@ -0,0 +1,18 @@ + + + topic yellow + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc index bfcf00c99..859714567 100644 --- a/Telegram/Resources/qrc/telegram/telegram.qrc +++ b/Telegram/Resources/qrc/telegram/telegram.qrc @@ -28,6 +28,13 @@ ../../icons/settings/dino.svg ../../icons/settings/star.svg ../../icons/settings/starmini.svg + ../../art/topic_icons/blue.svg + ../../art/topic_icons/yellow.svg + ../../art/topic_icons/violet.svg + ../../art/topic_icons/green.svg + ../../art/topic_icons/rose.svg + ../../art/topic_icons/red.svg + ../../art/topic_icons/gray.svg ../../icons/calls/hands.lottie diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 01abd6f35..9897977ab 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -192,9 +192,8 @@ messageActionChatJoinedByRequest#ebbca3cb = MessageAction; messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction; messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction; messageActionGiftPremium#aba0f5c6 currency:string amount:long months:int = MessageAction; -messageActionTopicCreate#6fa796ac flags:# title:string icon_emoji_id:flags.0?long = MessageAction; -messageActionTopicEditTitle#ce0b23cd title:string = MessageAction; -messageActionTopicEditIcon#820a6e2b emoji_document_id:long = MessageAction; +messageActionTopicCreate#d999256 flags:# title:string icon_color:int icon_emoji_id:flags.0?long = MessageAction; +messageActionTopicEdit#b117a9f5 flags:# title:flags.0?string icon_emoji_id:flags.1?long = MessageAction; dialog#a8edd0f5 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog; dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog; @@ -1455,7 +1454,7 @@ stickerKeyword#fcfeb29c document_id:long keyword:Vector = StickerKeyword username#b4073647 flags:# editable:flags.0?true active:flags.1?true username:string = Username; -forumTopic#2f3e1a06 flags:# id:int date:int title:string icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int = ForumTopic; +forumTopic#18a9a864 flags:# my:flags.1?true id:int date:int title:string icon_color:int icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int from_id:Peer = ForumTopic; messages.forumTopics#367617d3 flags:# order_by_create_date:flags.0?true count:int topics:Vector messages:Vector chats:Vector users:Vector pts:int = messages.ForumTopics; @@ -1862,11 +1861,10 @@ channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates; channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector = Bool; channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool; channels.toggleForum#a4298b29 channel:InputChannel enabled:Bool = Updates; -channels.createForumTopic#60bf3bc9 flags:# channel:InputChannel title:string icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates; +channels.createForumTopic#f40c0224 flags:# channel:InputChannel title:string icon_color:flags.0?int icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates; channels.getForumTopics#de560d1 flags:# channel:InputChannel q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics; channels.getForumTopicsByID#b0831eb9 channel:InputChannel topics:Vector = messages.ForumTopics; -channels.editForumTitle#8881b9b1 channel:InputChannel topic_id:int title:string = Updates; -channels.editForumIcon#55cea2dd channel:InputChannel topic_id:int emoji_document_id:long = Updates; +channels.editForumTopic#8a7f464b flags:# channel:InputChannel topic_id:int title:flags.0?string icon_emoji_id:flags.1?long = Updates; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; diff --git a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp index 756767b5d..1e533cd60 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp @@ -9,11 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/input_fields.h" #include "ui/abstract_button.h" +#include "ui/color_int_conversion.h" #include "data/data_channel.h" #include "data/data_forum.h" #include "data/data_forum_topic.h" #include "data/data_session.h" #include "data/stickers/data_custom_emoji.h" +#include "base/random.h" #include "main/main_session.h" #include "history/history.h" #include "history/view/history_view_replies_section.h" @@ -22,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "settings/settings_common.h" #include "apiwrap.h" -#include "styles/style_chat_helpers.h" +#include "styles/style_dialogs.h" namespace { @@ -34,34 +36,39 @@ namespace { [[nodiscard]] rpl::producer EditIconButton( not_null controller, not_null parent, - DocumentId id) { + int32 colorId, + DocumentId iconId) { using namespace Info::Profile; struct State { - rpl::variable id; + rpl::variable iconId; EmojiStatusPanel panel; std::unique_ptr chosen; + QImage empty; }; const auto tag = Data::CustomEmojiManager::SizeTag::Large; const auto size = EditIconSize(); const auto result = Ui::CreateChild(parent.get()); const auto state = result->lifetime().make_state(); - state->id.value( - ) | rpl::start_with_next([=](DocumentId id) { + state->empty = Data::ForumTopicIconBackground( + colorId, + st::largeForumTopicIcon.size); + state->iconId.value( + ) | rpl::start_with_next([=](DocumentId iconId) { const auto owner = &controller->session().data(); - state->chosen = id + state->chosen = iconId ? owner->customEmojiManager().create( - id, + iconId, [=] { result->update(); }, tag) : nullptr; result->update(); }, result->lifetime()); - state->id = id; + state->iconId = iconId; state->panel.setChooseFilter([=](DocumentId) { return true; }); state->panel.setChooseCallback([=](DocumentId id) { - state->id = id; + state->iconId = id; }); result->resize(size, size); result->paintRequest( @@ -78,14 +85,14 @@ namespace { if (state->chosen) { state->chosen->paint(p, args); } else { - // #TODO forum - st::stickersPremium.paint(p, 0, 0, result->width()); + const auto skip = (size - st::largeForumTopicIcon.size) / 2; + p.drawImage(skip, skip, state->empty); } }, result->lifetime()); result->setClickedCallback([=] { state->panel.show(controller, result, tag); }); - return state->id.value(); + return state->iconId.value(); } } // namespace @@ -110,11 +117,16 @@ void EditForumTopicBox( box->setTitle(rpl::single(creating ? u"New topic"_q : u"Edit topic"_q)); struct State { + int32 colorId = 0; DocumentId iconId = 0; - mtpRequestId titleRequestId = 0; - mtpRequestId iconRequestId = 0; + mtpRequestId requestId = 0; }; const auto state = box->lifetime().make_state(); + const auto &colors = Data::ForumTopicColorIds(); + state->iconId = topic ? topic->iconId() : 0; + state->colorId = topic + ? topic->colorId() + : colors[base::RandomIndex(colors.size())]; // #TODO forum lang and design Settings::AddSubsectionTitle( box->verticalLayout(), @@ -124,7 +136,8 @@ void EditForumTopicBox( EditIconButton( controller, badgeWrap, - topic ? topic->iconId() : 0 + state->colorId, + state->iconId ) | rpl::start_with_next([=](DocumentId id) { state->iconId = id; }, box->lifetime()); @@ -154,6 +167,7 @@ void EditForumTopicBox( forum, channel->forum()->reserveCreatingId( title->getLastText().trimmed(), + state->colorId, state->iconId)), Window::SectionShow::Way::ClearStack); }; @@ -166,59 +180,32 @@ void EditForumTopicBox( if (!topic) { box->closeBox(); return; - } - const auto api = &forum->session().api(); - if (state->titleRequestId <= 0) { - if (title->getLastText().trimmed().isEmpty()) { - title->showError(); - return; - } else if (parent->creating(rootId)) { - topic->applyTitle(title->getLastText().trimmed()); - } else { - const auto done = [=] { - state->titleRequestId = 0; - if (!state->iconRequestId) { - box->closeBox(); - } - }; - state->titleRequestId = api->request(MTPchannels_EditForumTitle( - topic->channel()->inputChannel, - MTP_int(rootId), - MTP_string(title->getLastText().trimmed()) - )).done([=](const MTPUpdates &result) { - api->applyUpdates(result); - done(); - }).fail([=](const MTP::Error &error) { - if (error.type() == u"TOPIC_NOT_MODIFIED") { - done(); - } else { - state->titleRequestId = -1; - } - }).send(); - } - } - if (parent->creating(rootId)) { + } else if (state->requestId > 0) { + return; + } else if (title->getLastText().trimmed().isEmpty()) { + title->showError(); + return; + } else if (parent->creating(rootId)) { + topic->applyTitle(title->getLastText().trimmed()); + topic->applyColorId(state->colorId); topic->applyIconId(state->iconId); - box->closeBox(); - } else if (state->iconRequestId <= 0) { - const auto done = [=] { - state->iconRequestId = 0; - if (!state->titleRequestId) { - box->closeBox(); - } - }; - state->iconRequestId = api->request(MTPchannels_EditForumIcon( + } else { + using Flag = MTPchannels_EditForumTopic::Flag; + const auto api = &forum->session().api(); + state->requestId = api->request(MTPchannels_EditForumTopic( + MTP_flags(Flag::f_title | Flag::f_icon_emoji_id), topic->channel()->inputChannel, MTP_int(rootId), + MTP_string(title->getLastText().trimmed()), MTP_long(state->iconId) )).done([=](const MTPUpdates &result) { api->applyUpdates(result); - done(); + box->closeBox(); }).fail([=](const MTP::Error &error) { if (error.type() == u"TOPIC_NOT_MODIFIED") { - done(); + box->closeBox(); } else { - state->iconRequestId = -1; + state->requestId = -1; } }).send(); } diff --git a/Telegram/SourceFiles/data/data_cloud_themes.cpp b/Telegram/SourceFiles/data/data_cloud_themes.cpp index a215d36fb..8d00aba8c 100644 --- a/Telegram/SourceFiles/data/data_cloud_themes.cpp +++ b/Telegram/SourceFiles/data/data_cloud_themes.cpp @@ -49,7 +49,7 @@ CloudTheme CloudTheme::Parse( settings.match([&](const MTPDthemeSettings &data) { if (const auto colors = data.vmessage_colors()) { for (const auto &color : colors->v) { - result.push_back(ColorFromSerialized(color)); + result.push_back(Ui::ColorFromSerialized(color)); } } }); @@ -57,12 +57,12 @@ CloudTheme CloudTheme::Parse( }; const auto accentColor = [&](const MTPThemeSettings &settings) { return settings.match([&](const MTPDthemeSettings &data) { - return ColorFromSerialized(data.vaccent_color().v); + return Ui::ColorFromSerialized(data.vaccent_color()); }); }; const auto outgoingAccentColor = [&](const MTPThemeSettings &settings) { return settings.match([&](const MTPDthemeSettings &data) { - return MaybeColorFromSerialized(data.voutbox_accent_color()); + return Ui::MaybeColorFromSerialized(data.voutbox_accent_color()); }); }; const auto basedOnDark = [&](const MTPThemeSettings &settings) { diff --git a/Telegram/SourceFiles/data/data_forum.cpp b/Telegram/SourceFiles/data/data_forum.cpp index e6df600f9..31f67f6fb 100644 --- a/Telegram/SourceFiles/data/data_forum.cpp +++ b/Telegram/SourceFiles/data/data_forum.cpp @@ -26,6 +26,7 @@ namespace { constexpr auto kTopicsFirstLoad = 20; constexpr auto kTopicsPerPage = 500; +constexpr auto kGeneralColorId = 0xA9A9A9; } // namespace @@ -123,6 +124,7 @@ void Forum::applyReceivedTopics( void Forum::applyTopicAdded( MsgId rootId, const QString &title, + int32 colorId, DocumentId iconId) { const auto i = _topics.find(rootId); const auto raw = (i != end(_topics)) @@ -132,6 +134,7 @@ void Forum::applyTopicAdded( std::make_unique(_history, rootId) ).first->second.get(); raw->applyTitle(title); + raw->applyColorId(colorId); raw->applyIconId(iconId); if (!creating(rootId)) { raw->addToChatList(FilterId(), topicsList()); @@ -147,10 +150,11 @@ void Forum::applyTopicRemoved(MsgId rootId) { MsgId Forum::reserveCreatingId( const QString &title, + int32 colorId, DocumentId iconId) { const auto result = _history->owner().nextLocalMessageId(); _creatingRootIds.emplace(result); - applyTopicAdded(result, title, iconId); + applyTopicAdded(result, title, colorId, iconId); return result; } @@ -200,7 +204,7 @@ ForumTopic *Forum::topicFor(MsgId rootId) { } } else { // #TODO forum lang - applyTopicAdded(rootId, "General! Created.", DocumentId(0)); + applyTopicAdded(rootId, "General! Created.", kGeneralColorId, 0); return _topics.find(rootId)->second.get(); } return nullptr; diff --git a/Telegram/SourceFiles/data/data_forum.h b/Telegram/SourceFiles/data/data_forum.h index bc422d9f8..f9d9d1c34 100644 --- a/Telegram/SourceFiles/data/data_forum.h +++ b/Telegram/SourceFiles/data/data_forum.h @@ -34,6 +34,7 @@ public: void applyTopicAdded( MsgId rootId, const QString &title, + int32 colorId, DocumentId iconId); void applyTopicRemoved(MsgId rootId); void applyTopicCreated(MsgId rootId, MsgId realId); @@ -44,6 +45,7 @@ public: [[nodiscard]] MsgId reserveCreatingId( const QString &title, + int32 colorId, DocumentId iconId); void discardCreatingId(MsgId rootId); [[nodiscard]] bool creating(MsgId rootId) const; diff --git a/Telegram/SourceFiles/data/data_forum_topic.cpp b/Telegram/SourceFiles/data/data_forum_topic.cpp index 22110b362..3a4086cf4 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.cpp +++ b/Telegram/SourceFiles/data/data_forum_topic.cpp @@ -18,11 +18,110 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "ui/painter.h" +#include "ui/color_int_conversion.h" #include "styles/style_dialogs.h" #include "styles/style_chat_helpers.h" +#include + namespace Data { +const base::flat_map &ForumTopicIcons() { + static const auto Result = base::flat_map{ + { 0x6FB9F0, u"blue"_q }, + { 0xFFD67E, u"yellow"_q }, + { 0xCB86DB, u"violet"_q }, + { 0x8EEE98, u"green"_q }, + { 0xFF93B2, u"rose"_q }, + { 0xFB6F5F, u"red"_q }, + }; + return Result; +} + +const std::vector &ForumTopicColorIds() { + static const auto Result = ForumTopicIcons( + ) | ranges::views::transform([](const auto &pair) { + return pair.first; + }) | ranges::to_vector; + return Result; +} + +const QString &ForumTopicDefaultIcon() { + static const auto Result = u"gray"_q; + return Result; +} + +const QString &ForumTopicIcon(int32 colorId) { + const auto &icons = ForumTopicIcons(); + const auto i = icons.find(colorId); + return (i != end(icons)) ? i->second : ForumTopicDefaultIcon(); +} + +QString ForumTopicIconPath(const QString &name) { + return u":/gui/topic_icons/%1.svg"_q.arg(name); +} + +QImage ForumTopicIconBackground(int32 colorId, int size) { + const auto ratio = style::DevicePixelRatio(); + auto svg = QSvgRenderer(ForumTopicIconPath(ForumTopicIcon(colorId))); + auto result = QImage( + QSize(size, size) * ratio, + QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(ratio); + result.fill(Qt::transparent); + + auto p = QPainter(&result); + svg.render(&p, QRect(0, 0, size, size)); + p.end(); + + return result; +} + +QString ExtractNonEmojiLetter(const QString &title) { + const auto begin = title.data(); + const auto end = begin + title.size(); + for (auto ch = begin; ch != end;) { + auto length = 0; + if (Ui::Emoji::Find(ch, end, &length)) { + ch += length; + continue; + } + uint ucs4 = ch->unicode(); + length = 1; + if (QChar::isHighSurrogate(ucs4) && ch + 1 != end) { + ushort low = ch[1].unicode(); + if (QChar::isLowSurrogate(low)) { + ucs4 = QChar::surrogateToUcs4(ucs4, low); + length = 2; + } + } + if (!QChar::isLetterOrNumber(ucs4)) { + ch += length; + continue; + } + return QString(ch, length); + } + return QString(); +} + +QImage ForumTopicIconFrame( + int32 colorId, + const QString &title, + const style::ForumTopicIcon &st) { + auto background = ForumTopicIconBackground(colorId, st.size); + + if (const auto one = ExtractNonEmojiLetter(title); !one.isEmpty()) { + auto p = QPainter(&background); + p.setPen(Qt::white); + p.drawText( + QRect(0, st.textTop, st.size, st.font->height * 2), + one, + style::al_top); + } + + return background; +} + ForumTopic::ForumTopic(not_null history, MsgId rootId) : Entry(&history->owner(), Type::ForumTopic) , _history(history) @@ -62,6 +161,7 @@ void ForumTopic::applyTopic(const MTPForumTopic &topic) { } else { applyIconId(0); } + applyColorId(data.vicon_color().v); const auto pinned = _list->pinned(); #if 0 // #TODO forum pinned @@ -218,19 +318,29 @@ void ForumTopic::paintUserpic( std::shared_ptr &view, const Dialogs::Ui::PaintContext &context) const { const auto &st = context.st; + const auto position = QPoint(st->padding.left(), st->padding.top()); if (_icon) { _icon->paint(p, { .preview = st::windowBgOver->c, .now = context.now, - .position = QPoint(st->padding.left(), st->padding.top()), + .position = position, .paused = context.paused, }); } else { - // #TODO forum - st::stickersPremium.paint( - p, - QPoint(st->padding.left(), st->padding.top()), - st->padding.left() * 2 + st::stickersPremium.width()); + validateDefaultIcon(); + const auto size = st::defaultForumTopicIcon.size; + const auto esize = st::emojiSize; + const auto shift = (esize - size) / 2; + p.drawImage(position + QPoint(shift, shift), _defaultIcon); + } +} + +void ForumTopic::validateDefaultIcon() const { + if (_defaultIcon.isNull()) { + _defaultIcon = ForumTopicIconFrame( + _colorId, + _title, + st::defaultForumTopicIcon); } } @@ -291,6 +401,7 @@ void ForumTopic::applyTitle(const QString &title) { } _title = isGeneral() ? "General! Topic." : title; // #TODO forum lang ++_titleVersion; + _defaultIcon = QImage(); indexTitleParts(); updateChatListEntry(); } @@ -308,10 +419,21 @@ void ForumTopic::applyIconId(DocumentId iconId) { [=] { updateChatListEntry(); }, Data::CustomEmojiManager::SizeTag::Normal) : nullptr; + if (iconId) { + _defaultIcon = QImage(); + } } updateChatListEntry(); } +int32 ForumTopic::colorId() const { + return _colorId; +} + +void ForumTopic::applyColorId(int32 colorId) { + _colorId = colorId; +} + void ForumTopic::applyItemAdded(not_null item) { setLastMessage(item); } diff --git a/Telegram/SourceFiles/data/data_forum_topic.h b/Telegram/SourceFiles/data/data_forum_topic.h index cf15a8e23..927af3234 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.h +++ b/Telegram/SourceFiles/data/data_forum_topic.h @@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class ChannelData; +namespace style { +struct ForumTopicIcon; +} // namespace style + namespace Dialogs { class MainList; } // namespace Dialogs @@ -25,6 +29,16 @@ namespace Data { class Session; class Forum; +[[nodiscard]] const base::flat_map &ForumTopicIcons(); +[[nodiscard]] const std::vector &ForumTopicColorIds(); +[[nodiscard]] const QString &ForumTopicIcon(int32 colorId); +[[nodiscard]] QString ForumTopicIconPath(const QString &name); +[[nodiscard]] QImage ForumTopicIconBackground(int32 colorId, int size); +[[nodiscard]] QImage ForumTopicIconFrame( + int32 colorId, + const QString &title, + const style::ForumTopicIcon &st); + class ForumTopic final : public Dialogs::Entry { public: static constexpr auto kGeneralId = 1; @@ -72,6 +86,8 @@ public: void applyTitle(const QString &title); [[nodiscard]] DocumentId iconId() const; void applyIconId(DocumentId iconId); + [[nodiscard]] int32 colorId() const; + void applyColorId(int32 colorId); void applyItemAdded(not_null item); void applyItemRemoved(MsgId id); @@ -95,6 +111,7 @@ public: private: void indexTitleParts(); + void validateDefaultIcon() const; void applyTopicTopMessage(MsgId topMessageId); void applyTopicFields( int unreadCount, @@ -119,8 +136,10 @@ private: base::flat_set _titleWords; base::flat_set _titleFirstLetters; int _titleVersion = 0; + int32 _colorId = 0; std::unique_ptr _icon; + mutable QImage _defaultIcon; // on-demand std::optional _inboxReadBefore; std::optional _outboxReadBefore; diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index 042084999..22732f631 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -860,18 +860,15 @@ void Histories::sendCreateTopicRequest( const auto api = &session().api(); using Flag = MTPchannels_CreateForumTopic::Flag; api->request(MTPchannels_CreateForumTopic( - MTP_flags(topic->iconId() ? Flag::f_icon_emoji_id : Flag(0)), + MTP_flags(Flag::f_icon_color + | (topic->iconId() ? Flag::f_icon_emoji_id : Flag(0))), history->peer->asChannel()->inputChannel, MTP_string(topic->title()), + MTP_int(topic->colorId()), MTP_long(topic->iconId()), MTP_long(randomId), MTPInputPeer() // send_as )).done([=](const MTPUpdates &result) { - //AssertIsDebug(); - //const auto id = result.c_updates().vupdates().v.front().c_updateMessageID().vrandom_id().v; - //session().data().registerMessageRandomId( - // id, - // { history->peer->id, rootId }); api->applyUpdates(result, randomId); }).fail([=](const MTP::Error &error) { api->sendMessageFail(error, history->peer, randomId); @@ -952,14 +949,14 @@ void Histories::checkTopicCreated(FullMsgId rootId, MsgId realId) { const auto history = _owner->history(rootId.peer); for (auto &entry : scheduled) { _creatingTopicRequests.erase(entry.requestId); - //AssertIsDebug(); - sendPreparedMessage( - history, - realId, - entry.randomId, - std::move(entry.message), - std::move(entry.done), - std::move(entry.fail)); + AssertIsDebug(); + //sendPreparedMessage( + // history, + // realId, + // entry.randomId, + // std::move(entry.message), + // std::move(entry.done), + // std::move(entry.fail)); } for (const auto &item : history->clientSideMessages()) { const auto replace = [&](MsgId nowId) { diff --git a/Telegram/SourceFiles/data/data_replies_list.cpp b/Telegram/SourceFiles/data/data_replies_list.cpp index cc963704f..e1869a5ea 100644 --- a/Telegram/SourceFiles/data/data_replies_list.cpp +++ b/Telegram/SourceFiles/data/data_replies_list.cpp @@ -260,7 +260,7 @@ void RepliesList::injectRootMessage(not_null viewer) { return; } const auto root = lookupRoot(); - if (!root) { + if (!root || root->topicRootId() != Data::ForumTopic::kGeneralId) { return; } injectRootDivider(root, slice); diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 0a21fadcd..5e6be8a52 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -307,6 +307,20 @@ void Session::clear() { // Optimization: clear notifications before destroying items. Core::App().notifications().clearFromSession(_session); + // We must clear all forums before clearing customEmojiManager. + // Because in Data::ForumTopic an Ui::Text::CustomEmoji is cached. + auto forums = base::flat_set>(); + for (const auto &[peerId, peer] : _peers) { + if (const auto channel = peer->asChannel()) { + if (channel->isForum()) { + forums.emplace(channel); + } + } + } + for (const auto &channel : forums) { + channel->setFlags(channel->flags() & ~ChannelDataFlag::Forum); + } + _sendActionManager->clear(); _histories->unloadAll(); @@ -1244,21 +1258,7 @@ void Session::setupUserIsContactViewer() { }, _lifetime); } -Session::~Session() { - // We must clear all forums before clearing customEmojiManager. - // Because in Data::ForumTopic an Ui::Text::CustomEmoji is cached. - auto forums = base::flat_set>(); - for (const auto &[peerId, peer] : _peers) { - if (const auto channel = peer->asChannel()) { - if (channel->isForum()) { - forums.emplace(channel); - } - } - } - for (const auto &channel : forums) { - channel->setFlags(channel->flags() & ~ChannelDataFlag::Forum); - } -} +Session::~Session() = default; template void Session::enumerateItemViews( diff --git a/Telegram/SourceFiles/data/data_wall_paper.cpp b/Telegram/SourceFiles/data/data_wall_paper.cpp index 353ca70fd..5b4f06781 100644 --- a/Telegram/SourceFiles/data/data_wall_paper.cpp +++ b/Telegram/SourceFiles/data/data_wall_paper.cpp @@ -12,9 +12,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "storage/serialize_common.h" #include "ui/chat/chat_theme.h" +#include "ui/color_int_conversion.h" #include "core/application.h" #include "main/main_session.h" +namespace Ui { + +QColor ColorFromSerialized(MTPint serialized) { + return ColorFromSerialized(serialized.v); +} + +std::optional MaybeColorFromSerialized( + const tl::conditional &mtp) { + return mtp ? ColorFromSerialized(*mtp) : std::optional(); +} + +} // namespace Ui + namespace Data { namespace { @@ -38,6 +52,8 @@ constexpr auto kIncorrectDefaultBackground = FromLegacyBackgroundId(105); constexpr auto kVersionTag = qint32(0x7FFFFFFF); constexpr auto kVersion = 1; +using Ui::MaybeColorFromSerialized; + [[nodiscard]] quint32 SerializeColor(const QColor &color) { return (quint32(std::clamp(color.red(), 0, 255)) << 16) | (quint32(std::clamp(color.green(), 0, 255)) << 8) @@ -57,7 +73,8 @@ constexpr auto kVersion = 1; } result.reserve(4); result.push_back(*c1); - const auto c2 = MaybeColorFromSerialized(data.vsecond_background_color()); + const auto c2 = MaybeColorFromSerialized( + data.vsecond_background_color()); if (!c2) { return result; } @@ -67,7 +84,8 @@ constexpr auto kVersion = 1; return result; } result.push_back(*c3); - const auto c4 = MaybeColorFromSerialized(data.vfourth_background_color()); + const auto c4 = MaybeColorFromSerialized( + data.vfourth_background_color()); if (!c4) { return result; } @@ -712,29 +730,6 @@ QImage GenerateDitheredGradient(const Data::WallPaper &paper) { paper.gradientRotation()); } -QColor ColorFromSerialized(quint32 serialized) { - return QColor( - int((serialized >> 16) & 0xFFU), - int((serialized >> 8) & 0xFFU), - int(serialized & 0xFFU)); -} - -QColor ColorFromSerialized(MTPint serialized) { - return ColorFromSerialized(serialized.v); -} - -std::optional MaybeColorFromSerialized( - quint32 serialized) { - return (serialized == quint32(-1)) - ? std::nullopt - : std::make_optional(ColorFromSerialized(serialized)); -} - -std::optional MaybeColorFromSerialized( - const tl::conditional &mtp) { - return mtp ? std::make_optional(ColorFromSerialized(*mtp)) : std::nullopt; -} - namespace details { WallPaper UninitializedWallPaper() { diff --git a/Telegram/SourceFiles/data/data_wall_paper.h b/Telegram/SourceFiles/data/data_wall_paper.h index ee4975278..df38478ee 100644 --- a/Telegram/SourceFiles/data/data_wall_paper.h +++ b/Telegram/SourceFiles/data/data_wall_paper.h @@ -15,6 +15,14 @@ namespace Main { class Session; } // namespace Main +namespace Ui { + +[[nodiscard]] QColor ColorFromSerialized(MTPint serialized); +[[nodiscard]] std::optional MaybeColorFromSerialized( + const tl::conditional &mtp); + +} // namespace Ui + namespace Data { struct FileOrigin; @@ -126,13 +134,6 @@ private: [[nodiscard]] QImage GenerateDitheredGradient(const WallPaper &paper); -[[nodiscard]] QColor ColorFromSerialized(quint32 serialized); -[[nodiscard]] QColor ColorFromSerialized(MTPint serialized); -[[nodiscard]] std::optional MaybeColorFromSerialized( - quint32 serialized); -[[nodiscard]] std::optional MaybeColorFromSerialized( - const tl::conditional &mtp); - namespace details { [[nodiscard]] WallPaper UninitializedWallPaper(); diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index ddfd5f2f3..2376099dd 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -19,6 +19,23 @@ DialogRow { textTop: pixels; } +ForumTopicIcon { + size: pixels; + font: font; + textTop: pixels; +} + +defaultForumTopicIcon: ForumTopicIcon { + size: 21px; + font: font(bold 12px); + textTop: 3px; +} +largeForumTopicIcon: ForumTopicIcon { + size: 26px; + font: font(bold 13px); + textTop: 4px; +} + dialogsUnreadFont: font(12px bold); dialogsUnreadHeight: 19px; dialogsUnreadPadding: 5px; diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index e86bf5b4b..13bc81aa8 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -1156,13 +1156,14 @@ ServiceAction ParseServiceAction( auto content = ActionTopicCreate(); content.title = ParseString(data.vtitle()); result.content = content; - }, [&](const MTPDmessageActionTopicEditTitle &data) { - auto content = ActionTopicEditTitle(); - content.title = ParseString(data.vtitle()); - result.content = content; - }, [&](const MTPDmessageActionTopicEditIcon &data) { - auto content = ActionTopicEditIcon(); - content.emojiDocumentId = data.vemoji_document_id().v; + }, [&](const MTPDmessageActionTopicEdit &data) { + auto content = ActionTopicEdit(); + if (const auto title = data.vtitle()) { + content.title = ParseString(*title); + } + if (const auto icon = data.vicon_emoji_id()) { + content.iconEmojiId = icon->v; + } result.content = content; }, [](const MTPDmessageActionEmpty &data) {}); return result; diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index e6c9c1f4c..565ea302f 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -503,12 +503,9 @@ struct ActionTopicCreate { Utf8String title; }; -struct ActionTopicEditTitle { +struct ActionTopicEdit { Utf8String title; -}; - -struct ActionTopicEditIcon { - uint64 emojiDocumentId = 0; + std::optional iconEmojiId = 0; }; struct ServiceAction { @@ -545,8 +542,7 @@ struct ServiceAction { ActionWebViewDataSent, ActionGiftPremium, ActionTopicCreate, - ActionTopicEditTitle, - ActionTopicEditIcon> content; + ActionTopicEdit> content; }; ServiceAction ParseServiceAction( diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp index 05c442e37..020b65c7c 100644 --- a/Telegram/SourceFiles/export/output/export_output_html.cpp +++ b/Telegram/SourceFiles/export/output/export_output_html.cpp @@ -1150,16 +1150,19 @@ auto HtmlWriter::Wrap::pushMessage( + " created topic «" + SerializeString(data.title) + "»"; - }, [&](const ActionTopicEditTitle &data) { - return serviceFrom - + " changed topic title to «" - + SerializeString(data.title) - + "»"; - }, [&](const ActionTopicEditIcon &data) { - return serviceFrom - + " changed topic icon to «" - + QString::number(data.emojiDocumentId).toUtf8() - + "»"; + }, [&](const ActionTopicEdit &data) { + auto parts = QList(); + if (!data.title.isEmpty()) { + parts.push_back("title to «" + + SerializeString(data.title) + + "»"); + } + if (data.iconEmojiId) { + parts.push_back("icon to «" + + QString::number(*data.iconEmojiId).toUtf8() + + "»"); + } + return serviceFrom + " changed topic " + parts.join(','); }, [](v::null_t) { return QByteArray(); }); if (!serviceText.isEmpty()) { diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp index 98c9ae9e2..874c7b703 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.cpp +++ b/Telegram/SourceFiles/export/output/export_output_json.cpp @@ -564,14 +564,15 @@ QByteArray SerializeMessage( pushActor(); pushAction("topic_created"); push("title", data.title); - }, [&](const ActionTopicEditTitle &data) { + }, [&](const ActionTopicEdit &data) { pushActor(); - pushAction("topic_title_edit"); - push("title", data.title); - }, [&](const ActionTopicEditIcon &data) { - pushActor(); - pushAction("topic_icon_edit"); - push("emoji_document_id", data.emojiDocumentId); + pushAction("topic_edit"); + if (!data.title.isEmpty()) { + push("new_title", data.title); + } + if (data.iconEmojiId) { + push("new_icon_emoji_id", *data.iconEmojiId); + } }, [](v::null_t) {}); if (v::is_null(message.action.content)) { diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 5ccb0253a..647d27662 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -1080,18 +1080,18 @@ void History::applyServiceChanges( forum->applyTopicAdded( item->id, qs(data.vtitle()), + data.vicon_color().v, data.vicon_emoji_id().value_or(DocumentId())); } - }, [&](const MTPDmessageActionTopicEditTitle &data) { + }, [&](const MTPDmessageActionTopicEdit &data) { if (const auto forum = peer->forum()) { if (const auto topic = forum->topicFor(item)) { - topic->applyTitle(qs(data.vtitle())); - } - } - }, [&](const MTPDmessageActionTopicEditIcon &data) { - if (const auto forum = peer->forum()) { - if (const auto topic = forum->topicFor(item)) { - topic->applyIconId(data.vemoji_document_id().v); + if (const auto &title = data.vtitle()) { + topic->applyTitle(qs(*title)); + } + if (const auto icon = data.vicon_emoji_id()) { + topic->applyIconId(icon->v); + } } } }, [](const auto &) { diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index d0a0b9853..12cac02fa 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -643,26 +643,24 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { return result; }; - auto prepareTopicEditTitle = [&](const MTPDmessageActionTopicEditTitle &action) { + auto prepareTopicEdit = [&](const MTPDmessageActionTopicEdit &action) { auto result = PreparedText{}; // #TODO forum lang - result.text = { "topic edited: " + qs(action.vtitle()) }; - return result; - }; - - auto prepareTopicEditIcon = [&](const MTPDmessageActionTopicEditIcon &action) { - auto result = PreparedText{}; - result.text = { "topic icon: " }; // #TODO forum lang - result.text.append(TextWithEntities{ - "@", - { EntityInText( - EntityType::CustomEmoji, - 0, - 1, - Data::SerializeCustomEmojiId({ - .id = action.vemoji_document_id().v })) }, - }); - + result.text = { "topic edited: " }; // #TODO forum lang + if (const auto icon = action.vicon_emoji_id()) { + result.text.append(TextWithEntities{ + "@", + { EntityInText( + EntityType::CustomEmoji, + 0, + 1, + Data::SerializeCustomEmojiId({ .id = icon->v })) + }, + }); + } + if (const auto &title = action.vtitle()) { + result.text.append(qs(*title)); + } return result; }; @@ -737,10 +735,8 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { return prepareGiftPremium(data); }, [&](const MTPDmessageActionTopicCreate &data) { return prepareTopicCreate(data); - }, [&](const MTPDmessageActionTopicEditTitle &data) { - return prepareTopicEditTitle(data); - }, [&](const MTPDmessageActionTopicEditIcon &data) { - return prepareTopicEditIcon(data); + }, [&](const MTPDmessageActionTopicEdit &data) { + return prepareTopicEdit(data); }, [&](const MTPDmessageActionWebViewDataSentMe &data) { LOG(("API Error: messageActionWebViewDataSentMe received.")); return PreparedText{ @@ -1312,8 +1308,10 @@ MsgId HistoryService::replyToTop() const { MsgId HistoryService::topicRootId() const { if (const auto data = GetDependentData() - ; data && data->topicPost) { + ; data && data->topicPost && data->topId) { return data->topId; + } else if (const auto topic = Get()) { + return id; } return Data::ForumTopic::kGeneralId; } @@ -1455,9 +1453,9 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) { if (type == mtpc_messageActionPinMessage) { UpdateComponents(HistoryServicePinned::Bit()); } else if (type == mtpc_messageActionTopicCreate - || type == mtpc_messageActionTopicEditTitle - || type == mtpc_messageActionTopicEditIcon) { + || type == mtpc_messageActionTopicEdit) { UpdateComponents(HistoryServiceTopicInfo::Bit()); + Get()->topicPost = true; } else if (type == mtpc_messageActionSetChatTheme) { setupChatThemeChange(); } else if (type == mtpc_messageActionSetMessagesTTL) { @@ -1548,6 +1546,8 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) { dependent->msgId = data.vreply_to_msg_id().v; dependent->topId = data.vreply_to_top_id().value_or( data.vreply_to_msg_id().v); + dependent->topicPost = data.is_forum_topic() + || Has(); if (!updateDependent()) { RequestDependentMessageData( this, diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 2ea9c8a7d..2b4aac4eb 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -756,10 +756,12 @@ bool Element::computeIsAttachToPrevious(not_null previous) { const auto item = data(); if (!Has() && !Has()) { const auto prev = previous->data(); + const auto previousMarkup = prev->inlineReplyMarkup(); const auto possible = (std::abs(prev->date() - item->date()) < kAttachMessageToPreviousSecondsDelta) && mayBeAttached(this) - && mayBeAttached(previous); + && mayBeAttached(previous) + && (!previousMarkup || previousMarkup->hiddenBy(prev->media())); if (possible) { const auto forwarded = item->Get(); const auto prevForwarded = prev->Get(); diff --git a/Telegram/SourceFiles/ui/color_int_conversion.cpp b/Telegram/SourceFiles/ui/color_int_conversion.cpp new file mode 100644 index 000000000..a1b0bcb45 --- /dev/null +++ b/Telegram/SourceFiles/ui/color_int_conversion.cpp @@ -0,0 +1,25 @@ +/* +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 "ui/color_int_conversion.h" + +namespace Ui { + +QColor ColorFromSerialized(quint32 serialized) { + return QColor( + int((serialized >> 16) & 0xFFU), + int((serialized >> 8) & 0xFFU), + int(serialized & 0xFFU)); +} + +std::optional MaybeColorFromSerialized(quint32 serialized) { + return (serialized == quint32(-1)) + ? std::nullopt + : std::make_optional(ColorFromSerialized(serialized)); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/color_int_conversion.h b/Telegram/SourceFiles/ui/color_int_conversion.h new file mode 100644 index 000000000..ed2bb6a18 --- /dev/null +++ b/Telegram/SourceFiles/ui/color_int_conversion.h @@ -0,0 +1,16 @@ +/* +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 + +namespace Ui { + +[[nodiscard]] QColor ColorFromSerialized(quint32 serialized); +[[nodiscard]] std::optional MaybeColorFromSerialized( + quint32 serialized); + +} // namespace Ui diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 48c00977d..3bd8eadf9 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -266,6 +266,8 @@ PRIVATE ui/cached_round_corners.h ui/color_contrast.cpp ui/color_contrast.h + ui/color_int_conversion.cpp + ui/color_int_conversion.h ui/grouped_layout.cpp ui/grouped_layout.h ui/widgets/fields/special_fields.cpp