diff --git a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp index e8a8b9d59..ed8a77787 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp @@ -48,7 +48,8 @@ class DefaultIconEmoji final : public Ui::Text::CustomEmoji { public: DefaultIconEmoji( rpl::producer value, - Fn repaint); + Fn repaint, + Data::CustomEmojiSizeTag tag); int width() override; QString entityData() override; @@ -61,14 +62,17 @@ public: private: DefaultIcon _icon = {}; QImage _image; + Data::CustomEmojiSizeTag _tag = {}; rpl::lifetime _lifetime; }; DefaultIconEmoji::DefaultIconEmoji( - rpl::producer value, - Fn repaint) { + rpl::producer value, + Fn repaint, + Data::CustomEmojiSizeTag tag) +: _tag(tag) { std::move(value) | rpl::start_with_next([=](DefaultIcon value) { _icon = value; _image = QImage(); @@ -85,19 +89,22 @@ QString DefaultIconEmoji::entityData() { } void DefaultIconEmoji::paint(QPainter &p, const Context &context) { + const auto &st = (_tag == Data::CustomEmojiSizeTag::Normal) + ? st::normalForumTopicIcon + : st::defaultForumTopicIcon; if (_image.isNull()) { _image = Data::IsForumGeneralIconTitle(_icon.title) ? Data::ForumTopicGeneralIconFrame( - st::defaultForumTopicIcon.size, + st.size, Data::ParseForumGeneralIconColor(_icon.colorId)) - : Data::ForumTopicIconFrame( - _icon.colorId, - _icon.title, - st::defaultForumTopicIcon); + : Data::ForumTopicIconFrame(_icon.colorId, _icon.title, st); } - const auto esize = Ui::Emoji::GetSizeLarge() / style::DevicePixelRatio(); + const auto full = (_tag == Data::CustomEmojiSizeTag::Normal) + ? Ui::Emoji::GetSizeNormal() + : Ui::Emoji::GetSizeLarge(); + const auto esize = full / style::DevicePixelRatio(); const auto customSize = Ui::Text::AdjustCustomEmojiSize(esize); - const auto skip = (customSize - st::defaultForumTopicIcon.size) / 2; + const auto skip = (customSize - st.size) / 2; p.drawImage(context.position + QPoint(skip, skip), _image); } @@ -262,7 +269,8 @@ struct IconSelector { if (id == kDefaultIconId) { return std::make_unique( rpl::duplicate(defaultIcon), - std::move(repaint)); + std::move(repaint), + tag); } return manager->create(id, std::move(repaint), tag); }; @@ -576,8 +584,10 @@ void EditForumTopicBox( std::unique_ptr MakeTopicIconEmoji( Data::TopicIconDescriptor descriptor, - Fn repaint) { + Fn repaint, + Data::CustomEmojiSizeTag tag) { return std::make_unique( rpl::single(descriptor), - std::move(repaint)); + std::move(repaint), + tag); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.h b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.h index 620ecebaf..81758e84c 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.h @@ -13,6 +13,7 @@ class History; namespace Data { struct TopicIconDescriptor; +enum class CustomEmojiSizeTag : uchar; } // namespace Data namespace Window { @@ -32,4 +33,5 @@ void EditForumTopicBox( [[nodiscard]] std::unique_ptr MakeTopicIconEmoji( Data::TopicIconDescriptor descriptor, - Fn repaint); + Fn repaint, + Data::CustomEmojiSizeTag tag); diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp index 7f110b6b1..c7eea5d48 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp @@ -542,7 +542,7 @@ std::unique_ptr CustomEmojiManager::create( const auto size = EmojiSizeFromTag(tag) / ratio; return userpic(data, std::move(update), size); } else if (const auto parsed = Data::ParseTopicIconEmojiEntity(data)) { - return MakeTopicIconEmoji(parsed, std::move(update)); + return MakeTopicIconEmoji(parsed, std::move(update), tag); } const auto parsed = ParseCustomEmojiData(data); return parsed diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 63c85b638..caa3526e2 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -46,6 +46,11 @@ defaultForumTopicIcon: ForumTopicIcon { font: font(bold 11px); textTop: 2px; } +normalForumTopicIcon: ForumTopicIcon { + size: 19px; + font: font(bold 10px); + textTop: 2px; +} largeForumTopicIcon: ForumTopicIcon { size: 26px; font: font(bold 13px); diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 5d8cdc063..db8724a61 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -47,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "base/event_filter.h" #include "core/application.h" +#include "core/ui_integration.h" #include "core/update_checker.h" #include "core/shortcuts.h" #include "boxes/peer_list_box.h" @@ -1230,9 +1231,17 @@ void Widget::updateSearchTabs() { } return; } else if (!_searchTabs) { + const auto savedSession = &session(); + const auto markedTextContext = [=](Fn repaint) { + return Core::MarkedTextContext{ + .session = savedSession, + .customEmojiRepaint = std::move(repaint), + }; + }; _searchTabs = std::make_unique( this, - _searchState.tab); + _searchState.tab, + std::move(markedTextContext)); _searchTabs->setVisible(!_showAnimation); _searchTabs->tabChanges( ) | rpl::filter([=](ChatSearchTab tab) { @@ -1250,16 +1259,19 @@ void Widget::updateSearchTabs() { : _openedForum ? _openedForum->channel().get() : nullptr; - const auto topicShortLabel = topic - ? Ui::Text::SingleCustomEmoji(Data::TopicIconEmojiEntity({ + const auto topicShortLabel = !topic + ? TextWithEntities() + : topic->iconId() + ? Ui::Text::SingleCustomEmoji( + Data::SerializeCustomEmojiId(topic->iconId())) + : Ui::Text::SingleCustomEmoji(Data::TopicIconEmojiEntity({ .title = (topic->isGeneral() ? Data::ForumGeneralIconTitle() : topic->title()), .colorId = (topic->isGeneral() ? Data::ForumGeneralIconColor(st::windowSubTextFg->c) : topic->colorId()), - })) - : TextWithEntities(); + })); const auto peerShortLabel = peer ? Ui::Text::SingleCustomEmoji( session().data().customEmojiManager().peerUserpicEmojiData( diff --git a/Telegram/SourceFiles/dialogs/ui/chat_search_tabs.cpp b/Telegram/SourceFiles/dialogs/ui/chat_search_tabs.cpp index 504321fab..a8615ae11 100644 --- a/Telegram/SourceFiles/dialogs/ui/chat_search_tabs.cpp +++ b/Telegram/SourceFiles/dialogs/ui/chat_search_tabs.cpp @@ -97,31 +97,19 @@ bool IsHashtagSearchQuery(const QString &query) { return true; } -ChatSearchTabs::ChatSearchTabs(QWidget *parent, ChatSearchTab active) +ChatSearchTabs::ChatSearchTabs( + QWidget *parent, + ChatSearchTab active, + Fn)> markedTextContext) : RpWidget(parent) , _tabs(std::make_unique(this, st::dialogsSearchTabs)) , _shadow(std::make_unique(this)) +, _markedTextContext(std::move(markedTextContext)) , _active(active) { - for (const auto tab : { - ChatSearchTab::ThisTopic, - ChatSearchTab::ThisPeer, - ChatSearchTab::MyMessages, - ChatSearchTab::PublicPosts, - }) { - _list.push_back({ tab, TabLabel(tab) }); - } _tabs->move(st::dialogsSearchTabsPadding, 0); _tabs->sectionActivated( ) | rpl::start_with_next([=](int index) { - for (const auto &tab : _list) { - if (tab.shortLabel.empty()) { - continue; - } else if (!index) { - _active = tab.value; - return; - } - --index; - } + _active = _list[index].value; }, lifetime()); } @@ -131,40 +119,73 @@ void ChatSearchTabs::setTabShortLabels( std::vector labels, ChatSearchTab active, ChatSearchPeerTabType peerTabType) { - for (const auto &label : labels) { - const auto i = ranges::find(_list, label.tab, &Tab::value); - Assert(i != end(_list)); - i->shortLabel = std::move(label.label); - if (i->value == ChatSearchTab::ThisPeer) { - i->label = TabLabel(label.tab, peerTabType); + const auto &st = st::dialogsSearchTabs; + const auto &font = st.labelStyle.font; + _list.clear(); + _list.reserve(labels.size()); + + auto widthTotal = 0; + for (const auto tab : { + ChatSearchTab::ThisTopic, + ChatSearchTab::ThisPeer, + ChatSearchTab::MyMessages, + ChatSearchTab::PublicPosts, + }) { + const auto i = ranges::find(labels, tab, &ShortLabel::tab); + if (i != end(labels) && !i->label.empty()) { + const auto label = TabLabel(tab, peerTabType); + const auto widthFull = font->width(label) + st.strictSkip; + _list.push_back({ + .value = tab, + .label = label, + .shortLabel = i->label, + .widthFull = widthFull, + }); + widthTotal += widthFull; } } - refreshTabs(active); + const auto widthSingleEmoji = st::emojiSize + st.strictSkip; + for (const auto tab : { + ChatSearchTab::PublicPosts, + ChatSearchTab::ThisTopic, + ChatSearchTab::ThisPeer, + ChatSearchTab::MyMessages, + }) { + const auto i = ranges::find(_list, tab, &Tab::value); + if (i != end(_list)) { + i->widthThresholdForShort = widthTotal; + widthTotal -= i->widthFull; + widthTotal += widthSingleEmoji; + } + } + refillTabs(active, width()); } rpl::producer ChatSearchTabs::tabChanges() const { return _active.changes(); } -void ChatSearchTabs::refreshTabs(ChatSearchTab active) { - auto index = 0; - auto labels = std::vector(); +void ChatSearchTabs::refillTabs( + ChatSearchTab active, + int newWidth) { + auto labels = std::vector(); + const auto available = newWidth - 2 * st::dialogsSearchTabsPadding; for (const auto &tab : _list) { - if (tab.value == active) { - index = int(labels.size()); - Assert(!tab.shortLabel.empty()); - labels.push_back(tab.label); - } else if (!tab.shortLabel.empty()) { - labels.push_back(tab.label); - } + auto label = (available < tab.widthThresholdForShort) + ? tab.shortLabel + : TextWithEntities{ tab.label }; + labels.push_back(std::move(label)); } - _tabs->setSections(labels); - _tabs->setActiveSectionFast(index); - resizeToWidth(width()); + _tabs->setSections(labels, _markedTextContext([=] { update(); })); + + const auto i = ranges::find(_list, active, &Tab::value); + Assert(i != end(_list)); + _tabs->setActiveSectionFast(i - begin(_list)); + _tabs->resizeToWidth(newWidth); } int ChatSearchTabs::resizeGetHeight(int newWidth) { - _tabs->resizeToWidth(newWidth); + refillTabs(_active.current(), newWidth); _shadow->setGeometry( 0, _tabs->y() + _tabs->height() - st::lineWidth, diff --git a/Telegram/SourceFiles/dialogs/ui/chat_search_tabs.h b/Telegram/SourceFiles/dialogs/ui/chat_search_tabs.h index ba87f9f98..720bf5734 100644 --- a/Telegram/SourceFiles/dialogs/ui/chat_search_tabs.h +++ b/Telegram/SourceFiles/dialogs/ui/chat_search_tabs.h @@ -34,7 +34,10 @@ enum class ChatSearchPeerTabType : uchar { class ChatSearchTabs final : public Ui::RpWidget { public: - ChatSearchTabs(QWidget *parent, ChatSearchTab active); + ChatSearchTabs( + QWidget *parent, + ChatSearchTab active, + Fn)> markedTextContext); ~ChatSearchTabs(); // A [custom] emoji to use when there is not enough space for text. @@ -55,14 +58,18 @@ private: ChatSearchTab value = {}; QString label; TextWithEntities shortLabel; + int widthFull = 0; + int widthThresholdForShort = 0; }; void refreshTabs(ChatSearchTab active); + void refillTabs(ChatSearchTab active, int newWidth); int resizeGetHeight(int newWidth) override; void paintEvent(QPaintEvent *e) override; const std::unique_ptr _tabs; const std::unique_ptr _shadow; + const Fn)> _markedTextContext; std::vector _list; rpl::variable _active; diff --git a/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp b/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp index e78de050a..53dc4e3dd 100644 --- a/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp +++ b/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp @@ -66,6 +66,13 @@ void DiscreteSlider::addSection(const QString &label) { resizeToWidth(width()); } +void DiscreteSlider::addSection( + const TextWithEntities &label, + const std::any &context) { + _sections.push_back(Section(label, getLabelStyle(), context)); + resizeToWidth(width()); +} + void DiscreteSlider::setSections(const std::vector &labels) { Assert(!labels.empty()); @@ -73,6 +80,22 @@ void DiscreteSlider::setSections(const std::vector &labels) { for (const auto &label : labels) { _sections.push_back(Section(label, getLabelStyle())); } + refresh(); +} + +void DiscreteSlider::setSections( + const std::vector &labels, + const std::any &context) { + Assert(!labels.empty()); + + _sections.clear(); + for (const auto &label : labels) { + _sections.push_back(Section(label, getLabelStyle(), context)); + } + refresh(); +} + +void DiscreteSlider::refresh() { stopAnimation(); if (_activeIndex >= _sections.size()) { _activeIndex = 0; @@ -182,6 +205,13 @@ DiscreteSlider::Section::Section( : label(st, label) { } +DiscreteSlider::Section::Section( + const TextWithEntities &label, + const style::TextStyle &st, + const std::any &context) { + this->label.setMarkedText(st, label, kMarkupTextOptions, context); +} + SettingsSlider::SettingsSlider( QWidget *parent, const style::SettingsSlider &st) diff --git a/Telegram/SourceFiles/ui/widgets/discrete_sliders.h b/Telegram/SourceFiles/ui/widgets/discrete_sliders.h index 38cdb473d..0cb930bf1 100644 --- a/Telegram/SourceFiles/ui/widgets/discrete_sliders.h +++ b/Telegram/SourceFiles/ui/widgets/discrete_sliders.h @@ -22,7 +22,13 @@ public: ~DiscreteSlider(); void addSection(const QString &label); + void addSection( + const TextWithEntities &label, + const std::any &context = {}); void setSections(const std::vector &labels); + void setSections( + const std::vector &labels, + const std::any &context = {}); int activeSection() const { return _activeIndex; } @@ -44,6 +50,10 @@ protected: struct Section { Section(const QString &label, const style::TextStyle &st); + Section( + const TextWithEntities &label, + const style::TextStyle &st, + const std::any &context); int left = 0; int width = 0; @@ -75,6 +85,7 @@ protected: _a_left.stop(); _a_width.stop(); } + void refresh(); void setSelectOnPress(bool selectOnPress);