Start animating emoji in filter titles.

This commit is contained in:
John Preston 2024-12-24 13:59:18 +04:00
parent 6cfbccd955
commit d874829b06
16 changed files with 363 additions and 144 deletions

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/filters/edit_filter_links.h" // FilterChatStatusText #include "boxes/filters/edit_filter_links.h" // FilterChatStatusText
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"
#include "core/ui_integration.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
@ -26,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "ui/controls/filter_link_header.h" #include "ui/controls/filter_link_header.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/filter_icons.h" #include "ui/filter_icons.h"
#include "ui/vertical_list.h" #include "ui/vertical_list.h"
@ -105,7 +107,7 @@ private:
Unexpected("Ui::FilterLinkHeaderType in TitleText."); Unexpected("Ui::FilterLinkHeaderType in TitleText.");
} }
[[nodiscard]] TextWithEntities AboutText( // todo filter emoji [[nodiscard]] TextWithEntities AboutText(
Ui::FilterLinkHeaderType type, Ui::FilterLinkHeaderType type,
TextWithEntities title) { TextWithEntities title) {
using Type = Ui::FilterLinkHeaderType; using Type = Ui::FilterLinkHeaderType;
@ -147,10 +149,17 @@ void InitFilterLinkHeader(
Ui::LookupFilterIconByEmoji( Ui::LookupFilterIconByEmoji(
iconEmoji iconEmoji
).value_or(Ui::FilterIcon::Custom)).active; ).value_or(Ui::FilterIcon::Custom)).active;
const auto makeContext = [=](Fn<void()> repaint) {
return Core::MarkedTextContext{
.session = &box->peerListUiShow()->session(),
.customEmojiRepaint = std::move(repaint),
};
};
auto header = Ui::MakeFilterLinkHeader(box, { auto header = Ui::MakeFilterLinkHeader(box, {
.type = type, .type = type,
.title = TitleText(type)(tr::now), .title = TitleText(type)(tr::now),
.about = AboutText(type, title), .about = AboutText(type, title),
.makeAboutContext = makeContext,
.folderTitle = title, .folderTitle = title,
.folderIcon = icon, .folderIcon = icon,
.badge = (type == Ui::FilterLinkHeaderType::AddingChats .badge = (type == Ui::FilterLinkHeaderType::AddingChats
@ -541,7 +550,7 @@ void ShowImportToast(
const auto phrase = created const auto phrase = created
? tr::lng_filters_added_title ? tr::lng_filters_added_title
: tr::lng_filters_updated_title; : tr::lng_filters_updated_title;
auto text = Ui::Text::Wrapped( // todo filter emoji auto text = Ui::Text::Wrapped(
phrase(tr::now, lt_folder, title, Ui::Text::WithEntities), phrase(tr::now, lt_folder, title, Ui::Text::WithEntities),
EntityType::Bold); EntityType::Bold);
if (added > 0) { if (added > 0) {
@ -550,7 +559,16 @@ void ShowImportToast(
: tr::lng_filters_updated_also; : tr::lng_filters_updated_also;
text.append('\n').append(phrase(tr::now, lt_count, added)); text.append('\n').append(phrase(tr::now, lt_count, added));
} }
strong->showToast(std::move(text)); const auto makeContext = [=](not_null<QWidget*> widget) {
return Core::MarkedTextContext{
.session = &strong->session(),
.customEmojiRepaint = [=] { widget->update(); },
};
};
strong->showToast({
.text = std::move(text),
.textContext = makeContext,
});
} }
void HandleEnterInBox(not_null<Ui::BoxContent*> box) { void HandleEnterInBox(not_null<Ui::BoxContent*> box) {
@ -619,10 +637,17 @@ void ProcessFilterInvite(
raw->setRealContentHeight(box->heightValue()); raw->setRealContentHeight(box->heightValue());
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = &strong->session(),
.customEmojiRepaint = update,
};
};
auto owned = Ui::FilterLinkProcessButton( auto owned = Ui::FilterLinkProcessButton(
box, box,
type, type,
title, title,
makeContext,
std::move(badge)); std::move(badge));
const auto button = owned.data(); const auto button = owned.data();
@ -842,10 +867,17 @@ void ProcessFilterRemove(
raw->adjust(min, max, addedTop); raw->adjust(min, max, addedTop);
}, type, title, iconEmoji, rpl::single(0), horizontalFilters); }, type, title, iconEmoji, rpl::single(0), horizontalFilters);
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = &strong->session(),
.customEmojiRepaint = update,
};
};
auto owned = Ui::FilterLinkProcessButton( auto owned = Ui::FilterLinkProcessButton(
box, box,
type, type,
title, title,
makeContext,
std::move(badge)); std::move(badge));
const auto button = owned.data(); const auto button = owned.data();

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/premium_limits_box.h" #include "boxes/premium_limits_box.h"
#include "boxes/premium_preview_box.h" #include "boxes/premium_preview_box.h"
#include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h"
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"
#include "data/data_channel.h" #include "data/data_channel.h"
@ -394,28 +395,28 @@ void EditFilterBox(
tr::lng_filters_edit())); tr::lng_filters_edit()));
box->setCloseByOutsideClick(false); box->setCloseByOutsideClick(false);
const auto session = &window->session();
Data::AmPremiumValue( Data::AmPremiumValue(
&window->session() session
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
box->closeBox(); box->closeBox();
}, box->lifetime()); }, box->lifetime());
const auto content = box->verticalLayout(); const auto content = box->verticalLayout();
const auto current = filter.title();
const auto name = content->add( const auto name = content->add(
object_ptr<Ui::InputField>( object_ptr<Ui::InputField>(
box, box,
st::windowFilterNameInput, st::windowFilterNameInput,
tr::lng_filters_new_name(), Ui::InputField::Mode::SingleLine,
filter.title().text), // todo filter emoji tr::lng_filters_new_name()),
st::markdownLinkFieldPadding); st::markdownLinkFieldPadding);
InitMessageFieldHandlers(window, name, ChatHelpers::PauseReason::Layer);
name->setTextWithTags({
current.text,
TextUtilities::ConvertEntitiesToTextTags(current.entities),
}, Ui::InputField::HistoryAction::Clear);
name->setMaxLength(kMaxFilterTitleLength); name->setMaxLength(kMaxFilterTitleLength);
name->setInstantReplaces(Ui::InstantReplaces::Default());
name->setInstantReplacesEnabled(
Core::App().settings().replaceEmojiValue());
Ui::Emoji::SuggestionsController::Init(
box->getDelegate()->outerContainer(),
name,
&window->session());
const auto nameEditing = box->lifetime().make_state<NameEditing>( const auto nameEditing = box->lifetime().make_state<NameEditing>(
NameEditing{ name }); NameEditing{ name });
@ -672,8 +673,11 @@ void EditFilterBox(
} }
const auto collect = [=]() -> std::optional<Data::ChatFilter> { const auto collect = [=]() -> std::optional<Data::ChatFilter> {
// todo filter emoji const auto entered = name->getTextWithTags();
const auto title = TextWithEntities{ name->getLastText().trimmed() }; const auto title = TextWithEntities{
entered.text,
TextUtilities::ConvertTextTagsToEntities(entered.tags),
};
const auto rules = data->current(); const auto rules = data->current();
if (title.empty()) { if (title.empty()) {
name->showError(); name->showError();

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "boxes/filters/edit_filter_chats_list.h" #include "boxes/filters/edit_filter_chats_list.h"
#include "core/ui_integration.h"
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
#include "data/data_premium_limits.h" #include "data/data_premium_limits.h"
#include "data/data_session.h" #include "data/data_session.h"
@ -63,13 +64,27 @@ private:
class ExceptionRow final : public ChatsListBoxController::Row { class ExceptionRow final : public ChatsListBoxController::Row {
public: public:
explicit ExceptionRow(not_null<History*> history); ExceptionRow(
not_null<History*> history,
not_null<PeerListDelegate*> delegate);
QString generateName() override; QString generateName() override;
QString generateShortName() override; QString generateShortName() override;
PaintRoundImageCallback generatePaintUserpicCallback( PaintRoundImageCallback generatePaintUserpicCallback(
bool forceRound) override; bool forceRound) override;
void paintStatusText(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int availableWidth,
int outerWidth,
bool selected) override;
private:
Ui::Text::String _filtersText;
}; };
class TypeController final : public PeerListController { class TypeController final : public PeerListController {
@ -126,15 +141,29 @@ Flag TypeRow::flag() const {
return static_cast<Flag>(id() & 0xFFFF); return static_cast<Flag>(id() & 0xFFFF);
} }
ExceptionRow::ExceptionRow(not_null<History*> history) : Row(history) { ExceptionRow::ExceptionRow(
auto filters = QStringList(); not_null<History*> history,
not_null<PeerListDelegate*> delegate)
: Row(history) {
auto filters = TextWithEntities();
for (const auto &filter : history->owner().chatsFilters().list()) { for (const auto &filter : history->owner().chatsFilters().list()) {
if (filter.contains(history) && filter.id()) { if (filter.contains(history) && filter.id()) {
filters << filter.title().text; // todo filter emoji if (!filters.empty()) {
filters.append(u", "_q);
}
filters.append(filter.title());
} }
} }
if (!filters.isEmpty()) { if (!filters.empty()) {
setCustomStatus(filters.join(", ")); const auto repaint = [=] { delegate->peerListUpdateRow(this); };
_filtersText.setMarkedText(
st::defaultTextStyle,
filters,
kMarkupTextOptions,
Core::MarkedTextContext{
.session = &history->session(),
.customEmojiRepaint = repaint,
});
} else if (peer()->isSelf()) { } else if (peer()->isSelf()) {
setCustomStatus(tr::lng_saved_forward_here(tr::now)); setCustomStatus(tr::lng_saved_forward_here(tr::now));
} }
@ -176,6 +205,37 @@ PaintRoundImageCallback ExceptionRow::generatePaintUserpicCallback(
}; };
} }
void ExceptionRow::paintStatusText(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int availableWidth,
int outerWidth,
bool selected) {
if (_filtersText.isEmpty()) {
Row::paintStatusText(
p,
st,
x,
y,
availableWidth,
outerWidth,
selected);
} else {
p.setPen(selected ? st.statusFgOver : st.statusFg);
_filtersText.draw(p, {
.position = { x, y },
.outerWidth = outerWidth,
.availableWidth = availableWidth,
.palette = &st::defaultTextPalette,
.now = crl::now(),
.pausedEmoji = false,
.elisionLines = 1,
});
}
}
TypeController::TypeController( TypeController::TypeController(
not_null<Main::Session*> session, not_null<Main::Session*> session,
Flags options, Flags options,
@ -418,7 +478,7 @@ void EditFilterChatsListController::prepareViewHook() {
const auto rows = std::make_unique<std::optional<ExceptionRow>[]>(count); const auto rows = std::make_unique<std::optional<ExceptionRow>[]>(count);
auto i = 0; auto i = 0;
for (const auto &history : _peers) { for (const auto &history : _peers) {
rows[i++].emplace(history); rows[i++].emplace(history, delegate());
} }
auto pointers = std::vector<ExceptionRow*>(); auto pointers = std::vector<ExceptionRow*>();
pointers.reserve(count); pointers.reserve(count);
@ -499,7 +559,7 @@ auto EditFilterChatsListController::createRow(not_null<History*> history)
return nullptr; return nullptr;
} }
return history->inChatList() return history->inChatList()
? std::make_unique<ExceptionRow>(history) ? std::make_unique<ExceptionRow>(history, delegate())
: nullptr; : nullptr;
} }

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/edit_peer_invite_link.h" // InviteLinkQrBox. #include "boxes/peers/edit_peer_invite_link.h" // InviteLinkQrBox.
#include "boxes/peer_list_box.h" #include "boxes/peer_list_box.h"
#include "boxes/premium_limits_box.h" #include "boxes/premium_limits_box.h"
#include "core/ui_integration.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
@ -535,6 +536,12 @@ void LinkController::addHeader(not_null<Ui::VerticalLayout*> container) {
}, verticalLayout->lifetime()); }, verticalLayout->lifetime());
verticalLayout->add(std::move(icon.widget)); verticalLayout->add(std::move(icon.widget));
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = &_window->session(),
.customEmojiRepaint = update,
};
};
verticalLayout->add( verticalLayout->add(
object_ptr<Ui::CenterWrap<>>( object_ptr<Ui::CenterWrap<>>(
verticalLayout, verticalLayout,
@ -543,12 +550,14 @@ void LinkController::addHeader(not_null<Ui::VerticalLayout*> container) {
(_data.url.isEmpty() (_data.url.isEmpty()
? tr::lng_filters_link_no_about(Ui::Text::WithEntities) ? tr::lng_filters_link_no_about(Ui::Text::WithEntities)
: tr::lng_filters_link_share_about( : tr::lng_filters_link_share_about(
lt_folder, // todo filter emoji lt_folder,
rpl::single(Ui::Text::Wrapped( rpl::single(Ui::Text::Wrapped(
_filterTitle, _filterTitle,
EntityType::Bold)), EntityType::Bold)),
Ui::Text::WithEntities)), Ui::Text::WithEntities)),
st::settingsFilterDividerLabel)), st::settingsFilterDividerLabel,
st::defaultPopupMenu,
makeContext)),
st::filterLinkDividerLabelPadding); st::filterLinkDividerLabelPadding);
verticalLayout->geometryValue( verticalLayout->geometryValue(

View file

@ -356,7 +356,8 @@ void ShareBox::prepare() {
[this](FilterId id) { [this](FilterId id) {
_inner->applyChatFilter(id); _inner->applyChatFilter(id);
scrollToY(0); scrollToY(0);
}); },
Window::GifPauseReason::Layer);
chatsFilters->lower(); chatsFilters->lower();
chatsFilters->heightValue() | rpl::start_with_next([this](int h) { chatsFilters->heightValue() | rpl::start_with_next([this](int h) {
updateScrollSkips(); updateScrollSkips();

View file

@ -1345,6 +1345,7 @@ void Widget::toggleFiltersMenu(bool enabled) {
controller()->setActiveChatsFilter(id); controller()->setActiveChatsFilter(id);
} }
}, },
Window::GifPauseReason::Any,
controller(), controller(),
true); true);
raw->show(); raw->show();

View file

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/premium_limits_box.h" #include "boxes/premium_limits_box.h"
#include "boxes/premium_preview_box.h" #include "boxes/premium_preview_box.h"
#include "core/application.h" #include "core/application.h"
#include "core/ui_integration.h"
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_peer.h" #include "data/data_peer.h"
@ -57,14 +58,13 @@ public:
FilterRowButton( FilterRowButton(
not_null<QWidget*> parent, not_null<QWidget*> parent,
not_null<Main::Session*> session, not_null<Main::Session*> session,
const Data::ChatFilter &filter);
FilterRowButton(
not_null<QWidget*> parent,
const Data::ChatFilter &filter, const Data::ChatFilter &filter,
const QString &description); const QString &description = {});
void setRemoved(bool removed); void setRemoved(bool removed);
void updateData(const Data::ChatFilter &filter); void updateData(
const Data::ChatFilter &filter,
bool ignoreCount = false);
void updateCount(const Data::ChatFilter &filter); void updateCount(const Data::ChatFilter &filter);
[[nodiscard]] rpl::producer<> removeRequests() const; [[nodiscard]] rpl::producer<> removeRequests() const;
@ -80,20 +80,13 @@ private:
Normal, Normal,
}; };
FilterRowButton(
not_null<QWidget*> parent,
Main::Session *session,
const Data::ChatFilter &filter,
const QString &description,
State state);
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
void setup(const Data::ChatFilter &filter, const QString &status); void setup(const Data::ChatFilter &filter, const QString &status);
void setState(State state, bool force = false); void setState(State state, bool force = false);
void updateButtonsVisibility(); void updateButtonsVisibility();
Main::Session *_session = nullptr; const not_null<Main::Session*> _session;
Ui::IconButton _remove; Ui::IconButton _remove;
Ui::RoundButton _restore; Ui::RoundButton _restore;
@ -177,50 +170,43 @@ struct FilterRow {
FilterRowButton::FilterRowButton( FilterRowButton::FilterRowButton(
not_null<QWidget*> parent, not_null<QWidget*> parent,
not_null<Main::Session*> session, not_null<Main::Session*> session,
const Data::ChatFilter &filter)
: FilterRowButton(
parent,
session,
filter,
ComputeCountString(session, filter),
State::Normal) {
}
FilterRowButton::FilterRowButton(
not_null<QWidget*> parent,
const Data::ChatFilter &filter, const Data::ChatFilter &filter,
const QString &description) const QString &description)
: FilterRowButton(parent, nullptr, filter, description, State::Suggested) {
}
FilterRowButton::FilterRowButton(
not_null<QWidget*> parent,
Main::Session *session,
const Data::ChatFilter &filter,
const QString &status,
State state)
: RippleButton(parent, st::defaultRippleAnimation) : RippleButton(parent, st::defaultRippleAnimation)
, _session(session) , _session(session)
, _remove(this, st::filtersRemove) , _remove(this, st::filtersRemove)
, _restore(this, tr::lng_filters_restore(), st::stickersUndoRemove) , _restore(this, tr::lng_filters_restore(), st::stickersUndoRemove)
, _add(this, tr::lng_filters_recommended_add(), st::stickersTrendingAdd) , _add(this, tr::lng_filters_recommended_add(), st::stickersTrendingAdd)
, _state(state) { , _state(description.isEmpty() ? State::Normal : State::Suggested) {
_restore.setTextTransform(Ui::RoundButton::TextTransform::NoTransform); _restore.setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
_add.setTextTransform(Ui::RoundButton::TextTransform::NoTransform); _add.setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
setup(filter, status); setup(filter, description.isEmpty()
? ComputeCountString(session, filter)
: description);
} }
void FilterRowButton::setRemoved(bool removed) { void FilterRowButton::setRemoved(bool removed) {
setState(removed ? State::Removed : State::Normal); setState(removed ? State::Removed : State::Normal);
} }
void FilterRowButton::updateData(const Data::ChatFilter &filter) { void FilterRowButton::updateData(
const Data::ChatFilter &filter,
bool ignoreCount) {
Expects(_session != nullptr); Expects(_session != nullptr);
// todo filter emoji
_title.setText(st::contactsNameStyle, filter.title().text); _title.setMarkedText(
st::contactsNameStyle,
filter.title(),
kMarkupTextOptions,
Core::MarkedTextContext{
.session = _session,
.customEmojiRepaint = [=] { update(); },
});
_icon = Ui::ComputeFilterIcon(filter); _icon = Ui::ComputeFilterIcon(filter);
_colorIndex = filter.colorIndex(); _colorIndex = filter.colorIndex();
updateCount(filter); if (!ignoreCount) {
updateCount(filter);
}
} }
void FilterRowButton::updateCount(const Data::ChatFilter &filter) { void FilterRowButton::updateCount(const Data::ChatFilter &filter) {
@ -243,12 +229,9 @@ void FilterRowButton::setup(
const Data::ChatFilter &filter, const Data::ChatFilter &filter,
const QString &status) { const QString &status) {
resize(width(), st::defaultPeerListItem.height); resize(width(), st::defaultPeerListItem.height);
// todo filter emoji
_title.setText(st::contactsNameStyle, filter.title().text);
_status = status;
_icon = Ui::ComputeFilterIcon(filter);
_colorIndex = filter.colorIndex();
_status = status;
updateData(filter, true);
setState(_state, true); setState(_state, true);
sizeValue() | rpl::start_with_next([=](QSize size) { sizeValue() | rpl::start_with_next([=](QSize size) {
@ -648,6 +631,7 @@ void FilterRowButton::paintEvent(QPaintEvent *e) {
state->suggested = state->suggested.current() + 1; state->suggested = state->suggested.current() + 1;
const auto button = aboutRows->add(object_ptr<FilterRowButton>( const auto button = aboutRows->add(object_ptr<FilterRowButton>(
aboutRows, aboutRows,
session,
filter, filter,
suggestion.description)); suggestion.description));
button->addRequests( button->addRequests(

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "ui/image/image_prepare.h" #include "ui/image/image_prepare.h"
#include "ui/text/text_utilities.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
@ -28,6 +29,11 @@ namespace {
constexpr auto kBodyAnimationPart = 0.90; constexpr auto kBodyAnimationPart = 0.90;
constexpr auto kTitleAdditionalScale = 0.05; constexpr auto kTitleAdditionalScale = 0.05;
struct PreviewState {
Fn<QImage()> frame;
rpl::lifetime lifetime;
};
class Widget final : public RpWidget { class Widget final : public RpWidget {
public: public:
Widget( Widget(
@ -61,7 +67,7 @@ private:
} _progress; } _progress;
rpl::variable<int> _badge; rpl::variable<int> _badge;
QImage _preview; PreviewState _preview;
QRectF _previewRect; QRectF _previewRect;
QString _titleText; QString _titleText;
@ -71,6 +77,7 @@ private:
QPainterPath _titlePath; QPainterPath _titlePath;
TextWithEntities _folderTitle; TextWithEntities _folderTitle;
Fn<std::any(Fn<void()>)> _makeContext;
not_null<const style::icon*> _folderIcon; not_null<const style::icon*> _folderIcon;
bool _horizontalFilters = false; bool _horizontalFilters = false;
@ -80,55 +87,94 @@ private:
}; };
[[nodiscard]] QImage GeneratePreview( [[nodiscard]] PreviewState GeneratePreview(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
const TextWithEntities &title, const TextWithEntities &title,
Fn<std::any(Fn<void()>)> makeContext,
int badge) { int badge) {
using Tabs = Ui::ChatsFiltersTabs; using Tabs = Ui::ChatsFiltersTabs;
auto owned = parent->lifetime().make_state<base::unique_qptr<Tabs>>( auto preview = PreviewState();
base::make_unique_q<Tabs>(parent, st::dialogsSearchTabs));
const auto raw = owned->get(); struct State {
State(not_null<Ui::RpWidget*> parent)
: tabs(parent, st::dialogsSearchTabs) {
}
Tabs tabs;
QImage cache;
bool dirty = true;
};
const auto state = preview.lifetime.make_state<State>(parent);
preview.frame = [=] {
if (state->dirty) {
const auto raw = &state->tabs;
state->cache.fill(st::windowBg->c);
auto p = QPainter(&state->cache);
Ui::RenderWidget(p, raw, QPoint(), raw->rect());
const auto &r = st::defaultEmojiSuggestions.fadeRight;
const auto &l = st::defaultEmojiSuggestions.fadeLeft;
const auto padding = st::filterLinkSubsectionTitlePadding.top();
const auto w = raw->width();
const auto h = raw->height();
r.fill(p, QRect(w - r.width() - padding, 0, r.width(), h));
l.fill(p, QRect(padding, 0, l.width(), h));
p.fillRect(0, 0, padding, h, st::windowBg);
p.fillRect(w - padding, 0, padding, raw->height(), st::windowBg);
}
return state->cache;
};
const auto raw = &state->tabs;
const auto repaint = [=] {
state->dirty = true;
};
raw->setSections({ raw->setSections({
tr::lng_filters_name_people(tr::now), TextWithEntities{ tr::lng_filters_name_people(tr::now) },
title.text, // todo filter emoji title,
tr::lng_filters_name_unread(tr::now), TextWithEntities{ tr::lng_filters_name_unread(tr::now) },
}); }, makeContext(repaint));
raw->fitWidthToSections(); raw->fitWidthToSections();
raw->setActiveSectionFast(1); raw->setActiveSectionFast(1);
raw->stopAnimation(); raw->stopAnimation();
auto result = QImage( auto &result = state->cache;
result = QImage(
raw->size() * style::DevicePixelRatio(), raw->size() * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied); QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(style::DevicePixelRatio()); result.setDevicePixelRatio(style::DevicePixelRatio());
result.fill(st::windowBg->c); raw->hide();
{
auto p = QPainter(&result);
Ui::RenderWidget(p, raw, QPoint(), raw->rect());
const auto &r = st::defaultEmojiSuggestions.fadeRight; style::PaletteChanged(
const auto &l = st::defaultEmojiSuggestions.fadeLeft; ) | rpl::start_with_next(repaint, preview.lifetime);
const auto padding = st::filterLinkSubsectionTitlePadding.top();
const auto w = raw->width(); return preview;
const auto h = raw->height();
r.fill(p, QRect(w - r.width() - padding, 0, r.width(), h));
l.fill(p, QRect(padding, 0, l.width(), h));
p.fillRect(0, 0, padding, h, st::windowBg);
p.fillRect(w - padding, 0, padding, raw->height(), st::windowBg);
}
owned->reset();
return result;
} }
[[nodiscard]] QImage GeneratePreview( [[nodiscard]] PreviewState GeneratePreview(
const TextWithEntities &title, const TextWithEntities &title,
Fn<std::any(Fn<void()>)> makeContext,
not_null<const style::icon*> icon, not_null<const style::icon*> icon,
int badge) { int badge) {
auto preview = PreviewState();
struct State {
QImage bg;
QImage composed;
Ui::Text::String string;
bool dirty = true;
};
const auto state = preview.lifetime.make_state<State>();
const auto repaint = [=] {
state->dirty = true;
};
const auto size = st::filterLinkPreview; const auto size = st::filterLinkPreview;
const auto ratio = style::DevicePixelRatio(); const auto ratio = style::DevicePixelRatio();
const auto radius = st::filterLinkPreviewRadius; const auto radius = st::filterLinkPreviewRadius;
const auto full = QSize(size, size) * ratio; const auto full = QSize(size, size) * ratio;
auto result = QImage(full, QImage::Format_ARGB32_Premultiplied); auto &result = state->bg;
result = QImage(full, QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(ratio); result.setDevicePixelRatio(ratio);
result.fill(st::windowBg->c); result.fill(st::windowBg->c);
@ -149,16 +195,19 @@ private:
const auto myIconTop = st::filterLinkPreviewMyBottom - iconHeight; const auto myIconTop = st::filterLinkPreviewMyBottom - iconHeight;
icon->paint(p, iconLeft, myIconTop, size); icon->paint(p, iconLeft, myIconTop, size);
const auto paintName = [&](const QString &text, int top) { const auto fillName = [=](const TextWithEntities &text) {
auto string = Ui::Text::String( state->string = Ui::Text::String(
st.style, st::windowFiltersButton.style,
text, text,
kDefaultTextOptions, kMarkupTextOptions,
available); available,
string.draw(p, { makeContext(repaint));
};
const auto paintName = [=](QPainter &p, int top) {
state->string.draw(p, {
.position = QPoint( .position = QPoint(
std::max( std::max(
(column - string.maxWidth()) / 2, (column - state->string.maxWidth()) / 2,
skip), skip),
top), top),
.outerWidth = available, .outerWidth = available,
@ -169,9 +218,9 @@ private:
}; };
p.setFont(st.style.font); p.setFont(st.style.font);
p.setPen(st.textFg); p.setPen(st.textFg);
paintName(tr::lng_filters_all(tr::now), st::filterLinkPreviewAllTop); fillName({ tr::lng_filters_all(tr::now) });
p.setPen(st.textFgActive); paintName(p, st::filterLinkPreviewAllTop);
paintName(title.text, st::filterLinkPreviewMyTop); // todo filter emoji fillName(title);
auto hq = PainterHighQualityEnabler(p); auto hq = PainterHighQualityEnabler(p);
@ -226,7 +275,19 @@ private:
p.drawRoundedRect(0, 0, size, size, radius, radius); p.drawRoundedRect(0, 0, size, size, radius, radius);
p.end(); p.end();
return Images::Round(std::move(result), Images::CornersMask(radius)); result = Images::Round(std::move(result), Images::CornersMask(radius));
preview.frame = [=] {
if (state->dirty) {
state->composed = state->bg;
auto p = QPainter(&state->composed);
p.setPen(st.textFgActive);
paintName(p, st::filterLinkPreviewMyTop);
}
return state->composed;
};
return preview;
} }
Widget::Widget( Widget::Widget(
@ -236,7 +297,9 @@ Widget::Widget(
, _about(CreateChild<FlatLabel>( , _about(CreateChild<FlatLabel>(
this, this,
rpl::single(descriptor.about.value()), rpl::single(descriptor.about.value()),
st::filterLinkAbout)) st::filterLinkAbout,
st::defaultPopupMenu,
descriptor.makeAboutContext))
, _close(CreateChild<IconButton>(this, st::boxTitleClose)) , _close(CreateChild<IconButton>(this, st::boxTitleClose))
, _aboutPadding(st::boxRowPadding) , _aboutPadding(st::boxRowPadding)
, _badge(std::move(descriptor.badge)) , _badge(std::move(descriptor.badge))
@ -244,19 +307,15 @@ Widget::Widget(
, _titleFont(st::boxTitle.style.font) , _titleFont(st::boxTitle.style.font)
, _titlePadding(st::filterLinkTitlePadding) , _titlePadding(st::filterLinkTitlePadding)
, _folderTitle(descriptor.folderTitle) , _folderTitle(descriptor.folderTitle)
, _makeContext(descriptor.makeAboutContext)
, _folderIcon(descriptor.folderIcon) , _folderIcon(descriptor.folderIcon)
, _horizontalFilters(descriptor.horizontalFilters) { , _horizontalFilters(descriptor.horizontalFilters) {
setMinimumHeight(st::boxTitleHeight); setMinimumHeight(st::boxTitleHeight);
refreshTitleText(); refreshTitleText();
setTitlePosition(st::boxTitlePosition.x(), st::boxTitlePosition.y()); setTitlePosition(st::boxTitlePosition.x(), st::boxTitlePosition.y());
style::PaletteChanged(
) | rpl::start_with_next([this] {
_preview = QImage();
}, lifetime());
_badge.changes() | rpl::start_with_next([this] { _badge.changes() | rpl::start_with_next([this] {
_preview = QImage(); _preview = PreviewState();
update(); update();
}, lifetime()); }, lifetime());
} }
@ -332,8 +391,9 @@ QRectF Widget::previewRect(
float64 topProgress, float64 topProgress,
float64 sizeProgress) const { float64 sizeProgress) const {
if (_horizontalFilters) { if (_horizontalFilters) {
const auto size = (_preview.size() / style::DevicePixelRatio()) const auto size = (_preview.frame
* sizeProgress; ? (_preview.frame().size() / style::DevicePixelRatio())
: QSize()) * sizeProgress;
return QRectF( return QRectF(
(width() - size.width()) / 2., (width() - size.width()) / 2.,
st::filterLinkPreviewTop * 1.5 * topProgress, st::filterLinkPreviewTop * 1.5 * topProgress,
@ -355,16 +415,27 @@ void Widget::paintEvent(QPaintEvent *e) {
p.setOpacity(_progress.body); p.setOpacity(_progress.body);
if (_progress.top) { if (_progress.top) {
auto hq = PainterHighQualityEnabler(p); auto hq = PainterHighQualityEnabler(p);
if (_preview.isNull()) { if (!_preview.frame) {
const auto badge = _badge.current(); const auto badge = _badge.current();
const auto makeContext = [=](Fn<void()> repaint) {
return _makeContext([=] { repaint(); update(); });
};
if (_horizontalFilters) { if (_horizontalFilters) {
_preview = GeneratePreview(this, _folderTitle, badge); _preview = GeneratePreview(
this,
_folderTitle,
makeContext,
badge);
Widget::resizeEvent(nullptr); Widget::resizeEvent(nullptr);
} else { } else {
_preview = GeneratePreview(_folderTitle, _folderIcon, badge); _preview = GeneratePreview(
_folderTitle,
makeContext,
_folderIcon,
badge);
} }
} }
p.drawImage(_previewRect, _preview); p.drawImage(_previewRect, _preview.frame());
} }
p.resetTransform(); p.resetTransform();
@ -415,13 +486,14 @@ object_ptr<RoundButton> FilterLinkProcessButton(
not_null<QWidget*> parent, not_null<QWidget*> parent,
FilterLinkHeaderType type, FilterLinkHeaderType type,
TextWithEntities title, TextWithEntities title,
Fn<std::any(Fn<void()>)> makeContext,
rpl::producer<int> badge) { rpl::producer<int> badge) {
const auto st = &st::filterInviteBox.button; const auto st = &st::filterInviteBox.button;
const auto badgeSt = &st::filterInviteButtonBadgeStyle; const auto badgeSt = &st::filterInviteButtonBadgeStyle;
auto result = object_ptr<RoundButton>(parent, rpl::single(u""_q), *st); auto result = object_ptr<RoundButton>(parent, rpl::single(u""_q), *st);
struct Data { struct Data {
QString text; TextWithEntities text;
QString badge; QString badge;
}; };
auto data = std::move( auto data = std::move(
@ -429,32 +501,41 @@ object_ptr<RoundButton> FilterLinkProcessButton(
) | rpl::map([=](int count) { ) | rpl::map([=](int count) {
const auto badge = count ? QString::number(count) : QString(); const auto badge = count ? QString::number(count) : QString();
const auto with = [&](QString badge) { const auto with = [&](QString badge) {
return rpl::map([=](QString text) { return rpl::map([=](TextWithEntities text) {
return Data{ text, badge }; return Data{ text, badge };
}); });
}; };
switch (type) { switch (type) {
case FilterLinkHeaderType::AddingFilter: case FilterLinkHeaderType::AddingFilter:
return badge.isEmpty() return badge.isEmpty()
? tr::lng_filters_by_link_add_no() | with(QString()) ? tr::lng_filters_by_link_add_no(
Ui::Text::WithEntities
) | with(QString())
: tr::lng_filters_by_link_add_button( : tr::lng_filters_by_link_add_button(
lt_folder, lt_folder,
rpl::single(title.text) // todo filter emoji rpl::single(title),
Ui::Text::WithEntities
) | with(badge); ) | with(badge);
case FilterLinkHeaderType::AddingChats: case FilterLinkHeaderType::AddingChats:
return badge.isEmpty() return badge.isEmpty()
? tr::lng_filters_by_link_join_no() | with(QString()) ? tr::lng_filters_by_link_join_no(
Ui::Text::WithEntities
) | with(QString())
: tr::lng_filters_by_link_and_join_button( : tr::lng_filters_by_link_and_join_button(
lt_count, lt_count,
rpl::single(float64(count))) | with(badge); rpl::single(float64(count)),
Ui::Text::WithEntities) | with(badge);
case FilterLinkHeaderType::AllAdded: case FilterLinkHeaderType::AllAdded:
return tr::lng_box_ok() | with(QString()); return tr::lng_box_ok(Ui::Text::WithEntities) | with(QString());
case FilterLinkHeaderType::Removing: case FilterLinkHeaderType::Removing:
return badge.isEmpty() return badge.isEmpty()
? tr::lng_filters_by_link_remove_button() | with(QString()) ? tr::lng_filters_by_link_remove_button(
Ui::Text::WithEntities
) | with(QString())
: tr::lng_filters_by_link_and_quit_button( : tr::lng_filters_by_link_and_quit_button(
lt_count, lt_count,
rpl::single(float64(count))) | with(badge); rpl::single(float64(count)),
Ui::Text::WithEntities) | with(badge);
} }
Unexpected("Type in FilterLinkProcessButton."); Unexpected("Type in FilterLinkProcessButton.");
}) | rpl::flatten_latest(); }) | rpl::flatten_latest();
@ -520,7 +601,11 @@ object_ptr<RoundButton> FilterLinkProcessButton(
}, label->lifetime()); }, label->lifetime());
std::move(data) | rpl::start_with_next([=](Data data) { std::move(data) | rpl::start_with_next([=](Data data) {
label->text.setText(st::filterInviteButtonStyle, data.text); label->text.setMarkedText(
st::filterInviteButtonStyle,
data.text,
kMarkupTextOptions,
makeContext([=] { label->update(); }));
label->badge.setText(st::filterInviteButtonBadgeStyle, data.badge); label->badge.setText(st::filterInviteButtonBadgeStyle, data.badge);
label->update(); label->update();
}, label->lifetime()); }, label->lifetime());

View file

@ -26,6 +26,7 @@ struct FilterLinkHeaderDescriptor {
base::required<FilterLinkHeaderType> type; base::required<FilterLinkHeaderType> type;
base::required<QString> title; base::required<QString> title;
base::required<TextWithEntities> about; base::required<TextWithEntities> about;
Fn<std::any(Fn<void()>)> makeAboutContext;
base::required<TextWithEntities> folderTitle; base::required<TextWithEntities> folderTitle;
not_null<const style::icon*> folderIcon; not_null<const style::icon*> folderIcon;
rpl::producer<int> badge; rpl::producer<int> badge;
@ -46,6 +47,7 @@ struct FilterLinkHeader {
not_null<QWidget*> parent, not_null<QWidget*> parent,
FilterLinkHeaderType type, FilterLinkHeaderType type,
TextWithEntities title, TextWithEntities title,
Fn<std::any(Fn<void()>)> makeContext,
rpl::producer<int> badge); rpl::producer<int> badge);
} // namespace Ui } // namespace Ui

View file

@ -46,22 +46,25 @@ ChatsFiltersTabs::ChatsFiltersTabs(
} }
bool ChatsFiltersTabs::setSectionsAndCheckChanged( bool ChatsFiltersTabs::setSectionsAndCheckChanged(
std::vector<QString> &&sections) { std::vector<TextWithEntities> &&sections,
const std::any &context,
Fn<bool()> paused) {
const auto &was = sectionsRef(); const auto &was = sectionsRef();
const auto changed = [&] { const auto changed = [&] {
if (was.size() != sections.size()) { if (was.size() != sections.size()) {
return true; return true;
} }
for (auto i = 0; i < sections.size(); i++) { for (auto i = 0; i < sections.size(); i++) {
if (was[i].label.toString() != sections[i]) { if (was[i].label.toTextWithEntities() != sections[i]) {
return true; return true;
} }
} }
return false; return false;
}(); }();
if (changed) { if (changed) {
Ui::DiscreteSlider::setSections(std::move(sections)); Ui::DiscreteSlider::setSections(std::move(sections), context);
} }
_emojiPaused = std::move(paused);
return changed; return changed;
} }
@ -171,6 +174,7 @@ void ChatsFiltersTabs::paintEvent(QPaintEvent *e) {
const auto clip = e->rect(); const auto clip = e->rect();
const auto range = getCurrentActiveRange(); const auto range = getCurrentActiveRange();
const auto activeIndex = activeSection(); const auto activeIndex = activeSection();
const auto now = crl::now();
auto index = 0; auto index = 0;
auto raisedIndex = -1; auto raisedIndex = -1;
@ -225,6 +229,8 @@ void ChatsFiltersTabs::paintEvent(QPaintEvent *e) {
.position = QPoint(labelLeft, _st.labelTop), .position = QPoint(labelLeft, _st.labelTop),
.outerWidth = width(), .outerWidth = width(),
.availableWidth = section.label.maxWidth(), .availableWidth = section.label.maxWidth(),
.now = now,
.pausedEmoji = _emojiPaused && _emojiPaused(),
}); });
{ {
const auto it = _unreadCounts.find(index); const auto it = _unreadCounts.find(index);

View file

@ -27,7 +27,10 @@ public:
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
const style::SettingsSlider &st); const style::SettingsSlider &st);
bool setSectionsAndCheckChanged(std::vector<QString> &&sections); bool setSectionsAndCheckChanged(
std::vector<TextWithEntities> &&sections,
const std::any &context,
Fn<bool()> paused);
void fitWidthToSections() override; void fitWidthToSections() override;
void setUnreadCount(int index, int unreadCount, bool muted); void setUnreadCount(int index, int unreadCount, bool muted);
@ -84,6 +87,7 @@ private:
std::optional<Ui::RoundRect> _bar; std::optional<Ui::RoundRect> _bar;
std::optional<Ui::RoundRect> _barActive; std::optional<Ui::RoundRect> _barActive;
std::optional<QImage> _lockCache; std::optional<QImage> _lockCache;
Fn<bool()> _emojiPaused;
int _reordering = 0; int _reordering = 0;

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/filters/edit_filter_box.h" #include "boxes/filters/edit_filter_box.h"
#include "boxes/premium_limits_box.h" #include "boxes/premium_limits_box.h"
#include "core/application.h" #include "core/application.h"
#include "core/ui_integration.h"
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
#include "data/data_peer_values.h" // Data::AmPremiumValue. #include "data/data_peer_values.h" // Data::AmPremiumValue.
#include "data/data_premium_limits.h" #include "data/data_premium_limits.h"
@ -20,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "settings/settings_folders.h" #include "settings/settings_folders.h"
#include "ui/power_saving.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "ui/widgets/chat_filters_tabs_slider_reorder.h" #include "ui/widgets/chat_filters_tabs_slider_reorder.h"
#include "ui/widgets/menu/menu_add_action_callback_factory.h" #include "ui/widgets/menu/menu_add_action_callback_factory.h"
@ -177,6 +179,7 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
not_null<Main::Session*> session, not_null<Main::Session*> session,
Fn<void(FilterId)> choose, Fn<void(FilterId)> choose,
ChatHelpers::PauseReason pauseLevel,
Window::SessionController *controller, Window::SessionController *controller,
bool trackActiveFilterAndUnreadAndReorder) { bool trackActiveFilterAndUnreadAndReorder) {
@ -325,14 +328,22 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
if ((list.size() <= 1 && !slider->width()) || state->ignoreRefresh) { if ((list.size() <= 1 && !slider->width()) || state->ignoreRefresh) {
return; return;
} }
const auto context = Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [=] { slider->update(); },
};
const auto paused = [=] {
return On(PowerSaving::kEmojiChat)
|| controller->isGifPausedAtLeastFor(pauseLevel);
};
const auto sectionsChanged = slider->setSectionsAndCheckChanged( const auto sectionsChanged = slider->setSectionsAndCheckChanged(
ranges::views::all( ranges::views::all(
list list
) | ranges::views::transform([](const Data::ChatFilter &filter) { ) | ranges::views::transform([](const Data::ChatFilter &filter) {
return filter.title().empty() return filter.title().empty()
? tr::lng_filters_all_short(tr::now) ? TextWithEntities{ tr::lng_filters_all_short(tr::now) }
: filter.title().text; // todo filter emoji : filter.title();
}) | ranges::to_vector); }) | ranges::to_vector, context, paused);
if (!sectionsChanged) { if (!sectionsChanged) {
return; return;
} }

View file

@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
namespace ChatHelpers {
enum class PauseReason;
} // namespace ChatHelpers
namespace Main { namespace Main {
class Session; class Session;
} // namespace Main } // namespace Main
@ -25,6 +29,7 @@ not_null<Ui::RpWidget*> AddChatFiltersTabsStrip(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
not_null<Main::Session*> session, not_null<Main::Session*> session,
Fn<void(FilterId)> choose, Fn<void(FilterId)> choose,
ChatHelpers::PauseReason pauseLevel,
Window::SessionController *controller = nullptr, Window::SessionController *controller = nullptr,
bool trackActiveFilterAndUnreadAndReorder = false); bool trackActiveFilterAndUnreadAndReorder = false);

View file

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_main_menu.h" #include "window/window_main_menu.h"
#include "window/window_peer_menu.h" #include "window/window_peer_menu.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "core/ui_integration.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
#include "data/data_user.h" #include "data/data_user.h"
@ -26,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/menu/menu_add_action_callback_factory.h" #include "ui/widgets/menu/menu_add_action_callback_factory.h"
#include "ui/widgets/popup_menu.h" #include "ui/widgets/popup_menu.h"
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "ui/power_saving.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "boxes/filters/edit_filter_box.h" #include "boxes/filters/edit_filter_box.h"
#include "boxes/premium_limits_box.h" #include "boxes/premium_limits_box.h"
@ -46,7 +48,7 @@ FiltersMenu::FiltersMenu(
: _session(session) : _session(session)
, _parent(parent) , _parent(parent)
, _outer(_parent) , _outer(_parent)
, _menu(&_outer, QString(), st::windowFiltersMainMenu) , _menu(&_outer, TextWithEntities(), st::windowFiltersMainMenu)
, _scroll(&_outer) , _scroll(&_outer)
, _container( , _container(
_scroll.setOwnedWidget( _scroll.setOwnedWidget(
@ -250,10 +252,22 @@ base::unique_qptr<Ui::SideBarButton> FiltersMenu::prepareButton(
TextWithEntities title, TextWithEntities title,
Ui::FilterIcon icon, Ui::FilterIcon icon,
bool toBeginning) { bool toBeginning) {
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = &_session->session(),
.customEmojiRepaint = std::move(update),
};
};
const auto paused = [=] {
return On(PowerSaving::kEmojiChat)
|| _session->isGifPausedAtLeastFor(Window::GifPauseReason::Any);
};
auto prepared = object_ptr<Ui::SideBarButton>( auto prepared = object_ptr<Ui::SideBarButton>(
container, container,
id ? title.text : tr::lng_filters_all(tr::now), // todo filter emoji id ? title : TextWithEntities{ tr::lng_filters_all(tr::now) },
st::windowFiltersButton); st::windowFiltersButton,
makeContext,
paused);
auto added = toBeginning auto added = toBeginning
? container->insert(0, std::move(prepared)) ? container->insert(0, std::move(prepared))
: container->add(std::move(prepared)); : container->add(std::move(prepared));

View file

@ -2241,7 +2241,8 @@ QPointer<Ui::BoxContent> ShowForwardMessagesBox(
[=](FilterId id) { [=](FilterId id) {
*lastFilterId = id; *lastFilterId = id;
applyFilter(box, id); applyFilter(box, id);
}); },
Window::GifPauseReason::Layer);
chatsFilters->lower(); chatsFilters->lower();
rpl::combine( rpl::combine(
chatsFilters->heightValue(), chatsFilters->heightValue(),

@ -1 +1 @@
Subproject commit ab8d8fcfd265f94b2a63f94a0f1796b907818b74 Subproject commit b063197b5d05de96754894630e45c8b3f95ade62