mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Start animating emoji in filter titles.
This commit is contained in:
parent
6cfbccd955
commit
d874829b06
16 changed files with 363 additions and 144 deletions
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -46,22 +46,25 @@ ChatsFiltersTabs::ChatsFiltersTabs(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatsFiltersTabs::setSectionsAndCheckChanged(
|
bool ChatsFiltersTabs::setSectionsAndCheckChanged(
|
||||||
std::vector<QString> &§ions) {
|
std::vector<TextWithEntities> &§ions,
|
||||||
|
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);
|
||||||
|
|
|
@ -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> &§ions);
|
bool setSectionsAndCheckChanged(
|
||||||
|
std::vector<TextWithEntities> &§ions,
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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
|
Loading…
Add table
Reference in a new issue