Allow disabling animations in folder emoji.

This commit is contained in:
John Preston 2024-12-31 15:41:13 +04:00
parent c810005f86
commit 06341efe0d
17 changed files with 235 additions and 73 deletions

View file

@ -5346,6 +5346,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_filters_edit" = "Edit Folder";
"lng_filters_setup_menu" = "Edit Folders";
"lng_filters_new_name" = "Folder name";
"lng_filters_enable_animations" = "Enable animations";
"lng_filters_disable_animations" = "Disable animations";
"lng_filters_add_chats" = "Add Chats";
"lng_filters_remove_chats" = "Add Chats to Exclude";
"lng_filters_include" = "Included chats";

View file

@ -52,7 +52,7 @@ public:
ToggleChatsController(
not_null<Window::SessionController*> window,
ToggleAction action,
TextWithEntities title,
Data::ChatFilterTitle title,
std::vector<not_null<PeerData*>> chats,
std::vector<not_null<PeerData*>> additional);
@ -78,7 +78,6 @@ private:
Ui::RpWidget *_addedBottomWidget = nullptr;
ToggleAction _action = ToggleAction::Adding;
TextWithEntities _filterTitle;
base::flat_set<not_null<PeerData*>> _checkable;
std::vector<not_null<PeerData*>> _chats;
std::vector<not_null<PeerData*>> _additional;
@ -141,7 +140,7 @@ void InitFilterLinkHeader(
not_null<PeerListBox*> box,
Fn<void(int minHeight, int maxHeight, int addedTopHeight)> adjust,
Ui::FilterLinkHeaderType type,
TextWithEntities title,
Data::ChatFilterTitle title,
QString iconEmoji,
rpl::producer<int> count,
bool horizontalFilters) {
@ -149,18 +148,20 @@ void InitFilterLinkHeader(
Ui::LookupFilterIconByEmoji(
iconEmoji
).value_or(Ui::FilterIcon::Custom)).active;
const auto isStatic = title.isStatic;
const auto makeContext = [=](Fn<void()> repaint) {
return Core::MarkedTextContext{
.session = &box->peerListUiShow()->session(),
.customEmojiRepaint = std::move(repaint),
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
auto header = Ui::MakeFilterLinkHeader(box, {
.type = type,
.title = TitleText(type)(tr::now),
.about = AboutText(type, title),
.about = AboutText(type, title.text),
.makeAboutContext = makeContext,
.folderTitle = title,
.folderTitle = title.text,
.folderIcon = icon,
.badge = (type == Ui::FilterLinkHeaderType::AddingChats
? std::move(count)
@ -258,12 +259,11 @@ void ImportInvite(
ToggleChatsController::ToggleChatsController(
not_null<Window::SessionController*> window,
ToggleAction action,
TextWithEntities title,
Data::ChatFilterTitle title,
std::vector<not_null<PeerData*>> chats,
std::vector<not_null<PeerData*>> additional)
: _window(window)
, _action(action)
, _filterTitle(title)
, _chats(std::move(chats))
, _additional(std::move(additional)) {
setStyleOverrides(&st::filterLinkChatsList);
@ -539,7 +539,7 @@ void ShowImportError(
void ShowImportToast(
base::weak_ptr<Window::SessionController> weak,
TextWithEntities title,
Data::ChatFilterTitle title,
Ui::FilterLinkHeaderType type,
int added) {
const auto strong = weak.get();
@ -551,7 +551,7 @@ void ShowImportToast(
? tr::lng_filters_added_title
: tr::lng_filters_updated_title;
auto text = Ui::Text::Wrapped(
phrase(tr::now, lt_folder, title, Ui::Text::WithEntities),
phrase(tr::now, lt_folder, title.text, Ui::Text::WithEntities),
EntityType::Bold);
if (added > 0) {
const auto phrase = created
@ -559,10 +559,12 @@ void ShowImportToast(
: tr::lng_filters_updated_also;
text.append('\n').append(phrase(tr::now, lt_count, added));
}
const auto isStatic = title.isStatic;
const auto makeContext = [=](not_null<QWidget*> widget) {
return Core::MarkedTextContext{
.session = &strong->session(),
.customEmojiRepaint = [=] { widget->update(); },
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
strong->showToast({
@ -595,7 +597,7 @@ void ProcessFilterInvite(
base::weak_ptr<Window::SessionController> weak,
const QString &slug,
FilterId filterId,
TextWithEntities title,
Data::ChatFilterTitle title,
QString iconEmoji,
std::vector<not_null<PeerData*>> peers,
std::vector<not_null<PeerData*>> already) {
@ -637,16 +639,18 @@ void ProcessFilterInvite(
raw->setRealContentHeight(box->heightValue());
const auto isStatic = title.isStatic;
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = &strong->session(),
.customEmojiRepaint = update,
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
auto owned = Ui::FilterLinkProcessButton(
box,
type,
title,
title.text,
makeContext,
std::move(badge));
@ -748,7 +752,7 @@ void CheckFilterInvite(
if (!strong) {
return;
}
auto title = TextWithEntities();
auto title = Data::ChatFilterTitle();
auto iconEmoji = QString();
auto filterId = FilterId();
auto peers = std::vector<not_null<PeerData*>>();
@ -767,7 +771,8 @@ void CheckFilterInvite(
return result;
};
result.match([&](const MTPDchatlists_chatlistInvite &data) {
title = ParseTextWithEntities(session, data.vtitle());
title.text = ParseTextWithEntities(session, data.vtitle());
title.isStatic = data.is_title_noanimate();
iconEmoji = data.vemoticon().value_or_empty();
peers = parseList(data.vpeers());
}, [&](const MTPDchatlists_chatlistInviteAlready &data) {
@ -832,7 +837,7 @@ void ProcessFilterUpdate(
void ProcessFilterRemove(
base::weak_ptr<Window::SessionController> weak,
TextWithEntities title,
Data::ChatFilterTitle title,
QString iconEmoji,
std::vector<not_null<PeerData*>> all,
std::vector<not_null<PeerData*>> suggest,
@ -867,16 +872,18 @@ void ProcessFilterRemove(
raw->adjust(min, max, addedTop);
}, type, title, iconEmoji, rpl::single(0), horizontalFilters);
const auto isStatic = title.isStatic;
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = &strong->session(),
.customEmojiRepaint = update,
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
auto owned = Ui::FilterLinkProcessButton(
box,
type,
title,
title.text,
makeContext,
std::move(badge));

View file

@ -17,6 +17,7 @@ class SessionController;
namespace Data {
class ChatFilter;
struct ChatFilterTitle;
} // namespace Data
namespace Api {
@ -36,7 +37,7 @@ void ProcessFilterUpdate(
void ProcessFilterRemove(
base::weak_ptr<Window::SessionController> weak,
TextWithEntities title,
Data::ChatFilterTitle title,
QString iconEmoji,
std::vector<not_null<PeerData*>> all,
std::vector<not_null<PeerData*>> suggest,

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/filters/edit_filter_box.h"
#include "boxes/premium_limits_box.h"
#include "core/application.h" // primaryWindow
#include "core/ui_integration.h"
#include "data/data_chat_filters.h"
#include "data/data_premium_limits.h"
#include "data/data_session.h"
@ -22,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/text/text_utilities.h" // Ui::Text::Bold
#include "ui/toast/toast.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/menu/menu_action.h"
#include "ui/widgets/popup_menu.h"
@ -169,15 +171,26 @@ void ChangeFilterById(
)).done([=, chat = history->peer->name(), name = filter.title()] {
const auto account = not_null(&history->session().account());
if (const auto controller = Core::App().windowFor(account)) {
controller->showToast((add
? tr::lng_filters_toast_add
: tr::lng_filters_toast_remove)(
tr::now,
lt_chat,
Ui::Text::Bold(chat),
lt_folder,
Ui::Text::Wrapped(name, EntityType::Bold),
Ui::Text::WithEntities));
const auto isStatic = name.isStatic;
const auto textContext = [=](not_null<QWidget*> widget) {
return Core::MarkedTextContext{
.session = &history->session(),
.customEmojiRepaint = [=] { widget->update(); },
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
controller->showToast({
.text = (add
? tr::lng_filters_toast_add
: tr::lng_filters_toast_remove)(
tr::now,
lt_chat,
Ui::Text::Bold(chat),
lt_folder,
Ui::Text::Wrapped(name.text, EntityType::Bold),
Ui::Text::WithEntities),
.textContext = textContext,
});
}
}).fail([=](const MTP::Error &error) {
LOG(("API Error: failed to %1 a dialog to a folder. %2")
@ -279,7 +292,7 @@ void FillChooseFilterMenu(
menu->st().menu,
Ui::Menu::CreateAction(
menu.get(), // todo filter emoji
Ui::Text::FixAmpersandInAction(filter.title().text),
Ui::Text::FixAmpersandInAction(filter.title().text.text),
std::move(callback)),
contains ? &st::mediaPlayerMenuCheck : nullptr,
contains ? &st::mediaPlayerMenuCheck : nullptr);

View file

@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/filter_icons.h"
#include "ui/layers/generic_box.h"
#include "ui/painter.h"
#include "ui/power_saving.h"
#include "ui/vertical_list.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/fields/input_field.h"
@ -352,6 +353,8 @@ void EditFilterBox(
rpl::variable<bool> hasLinks;
rpl::variable<bool> chatlist;
rpl::variable<bool> creating;
rpl::variable<TextWithEntities> title;
rpl::variable<bool> staticTitle;
rpl::variable<int> colorIndex;
};
const auto owner = &window->session().data();
@ -359,6 +362,8 @@ void EditFilterBox(
.rules = filter,
.chatlist = filter.chatlist(),
.creating = filter.title().empty(),
.title = filter.titleText(),
.staticTitle = filter.staticTitle(),
});
state->colorIndex = filter.colorIndex().value_or(kNoTag);
state->links = owner->chatsFilters().chatlistLinks(filter.id()),
@ -404,7 +409,7 @@ void EditFilterBox(
}, box->lifetime());
const auto content = box->verticalLayout();
const auto current = filter.title();
const auto current = state->title.current();
const auto name = content->add(
object_ptr<Ui::InputField>(
box,
@ -422,6 +427,44 @@ void EditFilterBox(
const auto nameEditing = box->lifetime().make_state<NameEditing>(
NameEditing{ name });
const auto staticTitle = Ui::CreateChild<Ui::LinkButton>(
name,
QString());
staticTitle->setClickedCallback([=] {
state->staticTitle = !state->staticTitle.current();
});
state->staticTitle.value() | rpl::start_with_next([=](bool value) {
staticTitle->setText(value
? tr::lng_filters_enable_animations(tr::now)
: tr::lng_filters_disable_animations(tr::now));
const auto paused = [=] {
using namespace Window;
return window->isGifPausedAtLeastFor(GifPauseReason::Layer);
};
name->setCustomTextContext([=](Fn<void()> repaint) {
return std::any(Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = std::move(repaint),
.customEmojiLoopLimit = value ? -1 : 0,
});
}, [paused] {
return On(PowerSaving::kEmojiChat) || paused();
}, [paused] {
return On(PowerSaving::kChatSpoiler) || paused();
});
name->update();
}, staticTitle->lifetime());
rpl::combine(
staticTitle->widthValue(),
name->widthValue()
) | rpl::start_with_next([=](int inner, int outer) {
staticTitle->moveToRight(
st::windowFilterStaticTitlePosition.x(),
st::windowFilterStaticTitlePosition.y(),
outer);
}, staticTitle->lifetime());
state->creating.value(
) | rpl::filter(!_1) | rpl::start_with_next([=] {
nameEditing->custom = true;
@ -432,7 +475,13 @@ void EditFilterBox(
if (!nameEditing->settingDefault) {
nameEditing->custom = true;
}
auto entered = name->getTextWithTags();
state->title = TextWithEntities{
std::move(entered.text),
TextUtilities::ConvertTextTagsToEntities(entered.tags),
};
}, name->lifetime());
const auto updateDefaultTitle = [=](const Data::ChatFilter &filter) {
if (nameEditing->custom) {
return;
@ -444,13 +493,11 @@ void EditFilterBox(
nameEditing->settingDefault = false;
}
};
const auto nameWithEntities = [=](bool upper = false) {
const auto entered = name->getTextWithTags();
return TextWithEntities{
(upper ? entered.text.toUpper() : entered.text),
TextUtilities::ConvertTextTagsToEntities(entered.tags),
};
};
state->title.value(
) | rpl::start_with_next([=](const TextWithEntities &value) {
staticTitle->setVisible(!value.entities.isEmpty());
}, staticTitle->lifetime());
const auto outer = box->getDelegate()->outerContainer();
CreateIconSelector(
@ -595,10 +642,16 @@ void EditFilterBox(
const auto palette = [](int i) {
return Ui::EmptyUserpic::UserpicColor(i).color2;
};
name->changes() | rpl::start_with_next([=] {
const auto upperTitle = [=] {
auto value = state->title.current();
value.text = value.text.toUpper();
return value;
};
state->title.changes(
) | rpl::start_with_next([=] {
tag->context.color = palette(state->colorIndex.current())->c;
tag->frame = Ui::ChatsFilterTag(
nameWithEntities(true),
upperTitle(),
tag->context);
preview->update();
}, preview->lifetime());
@ -615,7 +668,7 @@ void EditFilterBox(
if (progress == 1) {
tag->context.color = color->c;
tag->frame = Ui::ChatsFilterTag(
nameWithEntities(true),
upperTitle(),
tag->context);
if (i == kNoTag) {
tag->alpha = 0.;
@ -641,7 +694,7 @@ void EditFilterBox(
buttons[now]->setSelectedProgress(progress);
tag->context.color = anim::color(c1, c2, progress);
tag->frame = Ui::ChatsFilterTag(
nameWithEntities(true),
upperTitle(),
tag->context);
tag->alpha = anim::interpolateF(a1, a2, progress);
preview->update();
@ -689,7 +742,9 @@ void EditFilterBox(
}
const auto collect = [=]() -> std::optional<Data::ChatFilter> {
const auto title = nameWithEntities();
auto title = state->title.current();
const auto staticTitle = !title.entities.isEmpty()
&& state->staticTitle.current();
const auto rules = data->current();
if (title.empty()) {
name->showError();
@ -708,7 +763,9 @@ void EditFilterBox(
const auto colorIndex = (rawColorIndex >= kNoTag
? std::nullopt
: std::make_optional(rawColorIndex));
return rules.withTitle(title).withColorIndex(colorIndex);
return rules.withTitle(
{ std::move(title), staticTitle }
).withColorIndex(colorIndex);
};
Ui::AddSubsectionTitle(

View file

@ -151,7 +151,10 @@ ExceptionRow::ExceptionRow(
if (!filters.empty()) {
filters.append(u", "_q);
}
filters.append(filter.title());
auto title = filter.title();
filters.append(title.isStatic
? Data::ForceCustomEmojiStatic(std::move(title.text))
: std::move(title.text));
}
}
if (!filters.empty()) {

View file

@ -483,7 +483,7 @@ private:
const not_null<Window::SessionController*> _window;
InviteLinkData _data;
TextWithEntities _filterTitle;
Data::ChatFilterTitle _filterTitle;
base::flat_set<not_null<History*>> _filterChats;
base::flat_map<not_null<PeerData*>, QString> _denied;
rpl::variable<base::flat_set<not_null<PeerData*>>> _selected;
@ -536,10 +536,12 @@ void LinkController::addHeader(not_null<Ui::VerticalLayout*> container) {
}, verticalLayout->lifetime());
verticalLayout->add(std::move(icon.widget));
const auto isStatic = _filterTitle.isStatic;
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = &_window->session(),
.customEmojiRepaint = update,
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
verticalLayout->add(
@ -552,7 +554,7 @@ void LinkController::addHeader(not_null<Ui::VerticalLayout*> container) {
: tr::lng_filters_link_share_about(
lt_folder,
rpl::single(Ui::Text::Wrapped(
_filterTitle,
_filterTitle.text,
EntityType::Bold)),
Ui::Text::WithEntities)),
st::settingsFilterDividerLabel,

View file

@ -286,6 +286,9 @@ std::unique_ptr<Ui::Text::CustomEmoji> UiIntegration::createCustomEmoji(
return std::make_unique<Ui::Text::LimitedLoopsEmoji>(
std::move(result),
my->customEmojiLoopLimit);
} else if (my->customEmojiLoopLimit) {
return std::make_unique<Ui::Text::FirstFrameEmoji>(
std::move(result));
}
return result;
}

View file

@ -40,9 +40,22 @@ constexpr auto kLoadExceptionsPerRequest = 100;
} // namespace
TextWithEntities ForceCustomEmojiStatic(TextWithEntities text) {
for (auto &entity : text.entities) {
if (entity.type() == EntityType::CustomEmoji) {
entity = EntityInText(
EntityType::CustomEmoji,
entity.offset(),
entity.length(),
u"force-static:"_q + entity.data());
}
}
return text;
}
ChatFilter::ChatFilter(
FilterId id,
TextWithEntities title,
ChatFilterTitle title,
QString iconEmoji,
std::optional<uint8> colorIndex,
Flags flags,
@ -50,13 +63,15 @@ ChatFilter::ChatFilter(
std::vector<not_null<History*>> pinned,
base::flat_set<not_null<History*>> never)
: _id(id)
, _title(std::move(title))
, _title(std::move(title.text))
, _iconEmoji(std::move(iconEmoji))
, _colorIndex(colorIndex)
, _always(std::move(always))
, _pinned(std::move(pinned))
, _never(std::move(never))
, _flags(flags) {
, _flags(title.isStatic
? (flags | Flag::StaticTitle)
: (flags & ~Flag::StaticTitle)) {
}
ChatFilter ChatFilter::FromTL(
@ -70,7 +85,8 @@ ChatFilter ChatFilter::FromTL(
| (data.is_bots() ? Flag::Bots : Flag(0))
| (data.is_exclude_muted() ? Flag::NoMuted : Flag(0))
| (data.is_exclude_read() ? Flag::NoRead : Flag(0))
| (data.is_exclude_archived() ? Flag::NoArchived : Flag(0));
| (data.is_exclude_archived() ? Flag::NoArchived : Flag(0))
| (data.is_title_noanimate() ? Flag::StaticTitle : Flag(0));
auto &&to_histories = ranges::views::transform([&](
const MTPInputPeer &input) {
const auto peer = Data::PeerFromInputMTP(owner, input);
@ -96,7 +112,10 @@ ChatFilter ChatFilter::FromTL(
};
return ChatFilter(
data.vid().v,
Api::ParseTextWithEntities(&owner->session(), data.vtitle()),
{
Api::ParseTextWithEntities(&owner->session(), data.vtitle()),
data.is_title_noanimate(),
},
qs(data.vemoticon().value_or_empty()),
data.vcolor()
? std::make_optional(data.vcolor()->v)
@ -105,7 +124,7 @@ ChatFilter ChatFilter::FromTL(
std::move(list),
std::move(pinned),
{ never.begin(), never.end() });
}, [](const MTPDdialogFilterDefault &d) {
}, [](const MTPDdialogFilterDefault &) {
return ChatFilter();
}, [&](const MTPDdialogFilterChatlist &data) {
auto &&to_histories = ranges::views::transform([&](
@ -144,13 +163,17 @@ ChatFilter ChatFilter::FromTL(
};
return ChatFilter(
data.vid().v,
Api::ParseTextWithEntities(&owner->session(), data.vtitle()),
{
Api::ParseTextWithEntities(&owner->session(), data.vtitle()),
data.is_title_noanimate(),
},
qs(data.vemoticon().value_or_empty()),
data.vcolor()
? std::make_optional(data.vcolor()->v)
: std::nullopt,
(Flag::Chatlist
| (data.is_has_my_invites() ? Flag::HasMyLinks : Flag())),
| (data.is_has_my_invites() ? Flag::HasMyLinks : Flag())
| (data.is_title_noanimate() ? Flag::StaticTitle : Flag(0))),
std::move(list),
std::move(pinned),
{});
@ -163,9 +186,14 @@ ChatFilter ChatFilter::withId(FilterId id) const {
return result;
}
ChatFilter ChatFilter::withTitle(TextWithEntities title) const {
ChatFilter ChatFilter::withTitle(ChatFilterTitle title) const {
auto result = *this;
result._title = std::move(title);
result._title = std::move(title.text);
if (title.isStatic) {
result._flags |= Flag::StaticTitle;
} else {
result._flags &= ~Flag::StaticTitle;
}
return result;
}
@ -219,7 +247,8 @@ MTPDialogFilter ChatFilter::tl(FilterId replaceId) const {
if (_flags & Flag::Chatlist) {
using TLFlag = MTPDdialogFilterChatlist::Flag;
const auto flags = TLFlag::f_emoticon
| (_colorIndex ? TLFlag::f_color : TLFlag(0));
| (_colorIndex ? TLFlag::f_color : TLFlag(0))
| (staticTitle() ? TLFlag::f_title_noanimate : TLFlag(0));
return MTP_dialogFilterChatlist(
MTP_flags(flags),
MTP_int(replaceId ? replaceId : _id),
@ -232,6 +261,7 @@ MTPDialogFilter ChatFilter::tl(FilterId replaceId) const {
using TLFlag = MTPDdialogFilter::Flag;
const auto flags = TLFlag::f_emoticon
| (_colorIndex ? TLFlag::f_color : TLFlag(0))
| (staticTitle() ? TLFlag::f_title_noanimate : TLFlag(0))
| ((_flags & Flag::Contacts) ? TLFlag::f_contacts : TLFlag(0))
| ((_flags & Flag::NonContacts) ? TLFlag::f_non_contacts : TLFlag(0))
| ((_flags & Flag::Groups) ? TLFlag::f_groups : TLFlag(0))
@ -262,10 +292,14 @@ FilterId ChatFilter::id() const {
return _id;
}
TextWithEntities ChatFilter::title() const {
const TextWithEntities &ChatFilter::titleText() const {
return _title;
}
ChatFilterTitle ChatFilter::title() const {
return { _title, !!(_flags & Flag::StaticTitle) };
}
QString ChatFilter::iconEmoji() const {
return _iconEmoji;
}
@ -278,6 +312,10 @@ ChatFilter::Flags ChatFilter::flags() const {
return _flags;
}
bool ChatFilter::staticTitle() const {
return _flags & Flag::StaticTitle;
}
bool ChatFilter::chatlist() const {
return _flags & Flag::Chatlist;
}
@ -669,7 +707,8 @@ bool ChatFilters::applyChange(ChatFilter &filter, ChatFilter &&updated) {
|| (filter.hasMyLinks() != updated.hasMyLinks());
const auto listUpdated = rulesChanged
|| pinnedChanged
|| (filter.title() != updated.title())
|| (filter.titleText() != updated.titleText())
|| (filter.staticTitle() != updated.staticTitle())
|| (filter.iconEmoji() != updated.iconEmoji());
const auto colorChanged = filter.colorIndex() != updated.colorIndex();
const auto colorExistenceChanged = (!filter.colorIndex())

View file

@ -25,6 +25,17 @@ namespace Data {
class Session;
struct ChatFilterTitle {
TextWithEntities text;
bool isStatic = false;
[[nodiscard]] bool empty() const {
return text.empty();
}
};
[[nodiscard]] TextWithEntities ForceCustomEmojiStatic(TextWithEntities text);
class ChatFilter final {
public:
enum class Flag : ushort {
@ -40,9 +51,10 @@ public:
Chatlist = (1 << 8),
HasMyLinks = (1 << 9),
StaticTitle = (1 << 10),
NewChats = (1 << 10), // Telegram Business exceptions.
ExistingChats = (1 << 11),
NewChats = (1 << 11), // Telegram Business exceptions.
ExistingChats = (1 << 12),
};
friend constexpr inline bool is_flag_type(Flag) { return true; };
using Flags = base::flags<Flag>;
@ -50,7 +62,7 @@ public:
ChatFilter() = default;
ChatFilter(
FilterId id,
TextWithEntities title,
ChatFilterTitle title,
QString iconEmoji,
std::optional<uint8> colorIndex,
Flags flags,
@ -59,7 +71,7 @@ public:
base::flat_set<not_null<History*>> never);
[[nodiscard]] ChatFilter withId(FilterId id) const;
[[nodiscard]] ChatFilter withTitle(TextWithEntities title) const;
[[nodiscard]] ChatFilter withTitle(ChatFilterTitle title) const;
[[nodiscard]] ChatFilter withColorIndex(std::optional<uint8>) const;
[[nodiscard]] ChatFilter withChatlist(
bool chatlist,
@ -72,10 +84,12 @@ public:
[[nodiscard]] MTPDialogFilter tl(FilterId replaceId = 0) const;
[[nodiscard]] FilterId id() const;
[[nodiscard]] TextWithEntities title() const;
[[nodiscard]] ChatFilterTitle title() const;
[[nodiscard]] const TextWithEntities &titleText() const;
[[nodiscard]] QString iconEmoji() const;
[[nodiscard]] std::optional<uint8> colorIndex() const;
[[nodiscard]] Flags flags() const;
[[nodiscard]] bool staticTitle() const;
[[nodiscard]] bool chatlist() const;
[[nodiscard]] bool hasMyLinks() const;
[[nodiscard]] const base::flat_set<not_null<History*>> &always() const;
@ -99,7 +113,7 @@ private:
};
inline bool operator==(const ChatFilter &a, const ChatFilter &b) {
return (a.title() == b.title())
return (a.titleText() == b.titleText())
&& (a.iconEmoji() == b.iconEmoji())
&& (a.colorIndex() == b.colorIndex())
&& (a.flags() == b.flags())

View file

@ -113,6 +113,10 @@ private:
return u"scaled-custom:"_q;
}
[[nodiscard]] QString ForceStaticPrefix() {
return u"force-static:"_q;
}
[[nodiscard]] QString InternalPadding(QMargins value) {
return value.isNull() ? QString() : QString(",%1,%2,%3,%4"
).arg(value.left()
@ -554,6 +558,10 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
const auto original = data.mid(ScaledCustomPrefix().size());
return Ui::MakeScaledCustomEmoji(
create(original, std::move(update), SizeTag::Large));
} else if (data.startsWith(ForceStaticPrefix())) {
const auto original = data.mid(ForceStaticPrefix().size());
return std::make_unique<Ui::Text::FirstFrameEmoji>(
create(original, std::move(update), tag, sizeOverride));
} else if (data.startsWith(InternalPrefix())) {
return internal(data);
} else if (data.startsWith(UserpicEmojiPrefix())) {

View file

@ -4181,7 +4181,7 @@ QImage *InnerWidget::cacheChatsFilterTag(
auto roundedText = TextWithEntities();
auto colorIndex = -1;
if (filter.id()) {
roundedText = filter.title();
roundedText = filter.title().text;
roundedText.text = roundedText.text.toUpper();
if (filter.colorIndex()) {
colorIndex = *(filter.colorIndex());

View file

@ -194,13 +194,15 @@ void FilterRowButton::updateData(
bool ignoreCount) {
Expects(_session != nullptr);
const auto title = filter.title();
_title.setMarkedText(
st::contactsNameStyle,
filter.title(),
title.text,
kMarkupTextOptions,
Core::MarkedTextContext{
.session = _session,
.customEmojiRepaint = [=] { update(); },
.customEmojiLoopLimit = title.isStatic ? -1 : 0,
});
_icon = Ui::ComputeFilterIcon(filter);
_colorIndex = filter.colorIndex();

View file

@ -151,9 +151,10 @@ void ShowFiltersListMenu(
for (auto i = 0; i < list.size(); ++i) {
const auto &filter = list[i];
auto text = filter.title().empty()
auto title = filter.title();
auto text = title.text.empty()
? tr::lng_filters_all_short(tr::now)
: filter.title().text; // todo filter emoji
: title.text.text; // todo filter emoji
const auto action = state->menu->addAction(std::move(text), [=] {
if (i != active) {
@ -340,9 +341,12 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
ranges::views::all(
list
) | ranges::views::transform([](const Data::ChatFilter &filter) {
return filter.title().empty()
auto title = filter.title();
return title.text.empty()
? TextWithEntities{ tr::lng_filters_all_short(tr::now) }
: filter.title();
: title.isStatic
? Data::ForceCustomEmojiStatic(title.text)
: title.text;
}) | ranges::to_vector, context, paused);
if (!sectionsChanged) {
return;

View file

@ -285,8 +285,9 @@ windowFilterSmallRemoveRight: 10px;
windowFilterNameInput: InputField(defaultInputField) {
textMargins: margins(0px, 26px, 36px, 4px);
}
windowFilterStaticTitlePosition: point(0px, 5px);
windowFilterIconToggleSize: size(36px, 36px);
windowFilterIconTogglePosition: point(-4px, 12px);
windowFilterIconTogglePosition: point(-4px, 18px);
windwoFilterIconPanelPosition: point(-2px, -1px);
windowFilterIconSingle: size(44px, 42px);
windowFilterIconPadding: margins(10px, 36px, 10px, 8px);

View file

@ -218,7 +218,7 @@ void FiltersMenu::setupList() {
_setup = prepareButton(
_container,
-1,
TextWithEntities{ tr::lng_filters_setup(tr::now) },
{ TextWithEntities{ tr::lng_filters_setup(tr::now) } },
Ui::FilterIcon::Edit);
_reorder = std::make_unique<Ui::VerticalLayoutReorder>(_list, &_scroll);
@ -249,13 +249,15 @@ base::unique_qptr<Ui::SideBarButton> FiltersMenu::prepareAll() {
base::unique_qptr<Ui::SideBarButton> FiltersMenu::prepareButton(
not_null<Ui::VerticalLayout*> container,
FilterId id,
TextWithEntities title,
Data::ChatFilterTitle title,
Ui::FilterIcon icon,
bool toBeginning) {
const auto isStatic = title.isStatic;
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = &_session->session(),
.customEmojiRepaint = std::move(update),
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
const auto paused = [=] {
@ -264,7 +266,7 @@ base::unique_qptr<Ui::SideBarButton> FiltersMenu::prepareButton(
};
auto prepared = object_ptr<Ui::SideBarButton>(
container,
id ? title : TextWithEntities{ tr::lng_filters_all(tr::now) },
id ? title.text : TextWithEntities{ tr::lng_filters_all(tr::now) },
st::windowFiltersButton,
makeContext,
paused);

View file

@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/side_bar_button.h"
#include "ui/widgets/scroll_area.h"
namespace Data {
struct ChatFilterTitle;
} // namespace Data
namespace Ui {
class VerticalLayout;
class VerticalLayoutReorder;
@ -44,7 +48,7 @@ private:
[[nodiscard]] base::unique_qptr<Ui::SideBarButton> prepareButton(
not_null<Ui::VerticalLayout*> container,
FilterId id,
TextWithEntities title,
Data::ChatFilterTitle title,
Ui::FilterIcon icon,
bool toBeginning = false);
void setupMainMenuIcon();