diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 6cee0790b..b8c24968d 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -146,6 +146,12 @@ PRIVATE api/api_single_message_search.h api/api_text_entities.cpp api/api_text_entities.h + boxes/filters/edit_filter_box.cpp + boxes/filters/edit_filter_box.h + boxes/filters/edit_filter_chats_list.cpp + boxes/filters/edit_filter_chats_list.h + boxes/filters/manage_filters_box.cpp + boxes/filters/manage_filters_box.h boxes/peers/add_participants_box.cpp boxes/peers/add_participants_box.h boxes/peers/edit_contact_box.cpp @@ -204,8 +210,6 @@ PRIVATE boxes/language_box.h boxes/local_storage_box.cpp boxes/local_storage_box.h - boxes/manage_filters_box.cpp - boxes/manage_filters_box.h boxes/mute_settings_box.cpp boxes/mute_settings_box.h boxes/peer_list_box.cpp diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp new file mode 100644 index 000000000..027968e11 --- /dev/null +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp @@ -0,0 +1,466 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "boxes/filters/edit_filter_box.h" + +#include "boxes/filters/edit_filter_chats_list.h" +#include "ui/layers/generic_box.h" +#include "ui/text/text_utilities.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" +#include "data/data_chat_filters.h" +#include "data/data_peer.h" +#include "data/data_session.h" +#include "settings/settings_common.h" +#include "lang/lang_keys.h" +#include "history/history.h" +#include "main/main_session.h" +#include "window/window_session_controller.h" +#include "window/window_controller.h" +#include "styles/style_settings.h" +#include "styles/style_boxes.h" +#include "styles/style_layers.h" +#include "styles/style_window.h" + +namespace { + +using namespace Settings; + +constexpr auto kMaxFilterTitleLength = 20; + +using Flag = Data::ChatFilter::Flag; +using Flags = Data::ChatFilter::Flags; +using ExceptionPeersRef = const base::flat_set> &; +using ExceptionPeersGetter = ExceptionPeersRef(Data::ChatFilter::*)() const; + +constexpr auto kAllTypes = { + Flag::Contacts, + Flag::NonContacts, + Flag::Groups, + Flag::Channels, + Flag::Bots, + Flag::NoMuted, + Flag::NoArchived, + Flag::NoRead +}; + +class FilterChatsPreview final : public Ui::RpWidget { +public: + FilterChatsPreview( + not_null parent, + Flags flags, + const base::flat_set> &peers); + + [[nodiscard]] rpl::producer flagRemoved() const; + [[nodiscard]] rpl::producer> peerRemoved() const; + + void updateData( + Flags flags, + const base::flat_set> &peers); + + int resizeGetHeight(int newWidth) override; + +private: + using Button = base::unique_qptr; + struct FlagButton { + Flag flag = Flag(); + Button button; + }; + struct PeerButton { + not_null history; + Button button; + }; + + void paintEvent(QPaintEvent *e) override; + + void setup( + Flags flags, + const base::flat_set> &peers); + void refresh(); + void removeFlag(Flag flag); + void removePeer(not_null history); + void paintFlagIcon(QPainter &p, int left, int top, Flag flag) const; + + std::vector _removeFlag; + std::vector _removePeer; + + rpl::event_stream _flagRemoved; + rpl::event_stream> _peerRemoved; + +}; + +not_null SetupChatsPreview( + not_null content, + not_null data, + Flags flags, + ExceptionPeersGetter peers) { + const auto preview = content->add(object_ptr( + content, + data->flags() & flags, + (data->*peers)())); + + preview->flagRemoved( + ) | rpl::start_with_next([=](Flag flag) { + *data = Data::ChatFilter( + data->id(), + data->title(), + (data->flags() & ~flag), + data->always(), + data->never()); + }, preview->lifetime()); + + preview->peerRemoved( + ) | rpl::start_with_next([=](not_null history) { + auto always = data->always(); + auto never = data->never(); + always.remove(history); + never.remove(history); + *data = Data::ChatFilter( + data->id(), + data->title(), + data->flags(), + std::move(always), + std::move(never)); + }, preview->lifetime()); + + return preview; +} + +FilterChatsPreview::FilterChatsPreview( + not_null parent, + Flags flags, + const base::flat_set> &peers) +: RpWidget(parent) { + updateData(flags, peers); +} + +void FilterChatsPreview::refresh() { + resizeToWidth(width()); +} + +void FilterChatsPreview::updateData( + Flags flags, + const base::flat_set> &peers) { + _removeFlag.clear(); + _removePeer.clear(); + const auto makeButton = [&](Fn handler) { + auto result = base::make_unique_q( + this, + st::windowFilterSmallRemove); + result->setClickedCallback(std::move(handler)); + return result; + }; + for (const auto flag : kAllTypes) { + if (flags & flag) { + _removeFlag.push_back({ + flag, + makeButton([=] { removeFlag(flag); }) }); + } + } + for (const auto history : peers) { + _removePeer.push_back({ + history, + makeButton([=] { removePeer(history); }) }); + } + refresh(); +} + +int FilterChatsPreview::resizeGetHeight(int newWidth) { + const auto right = st::windowFilterSmallRemoveRight; + const auto add = (st::windowFilterSmallItem.height + - st::windowFilterSmallRemove.height) / 2; + auto top = 0; + const auto moveNextButton = [&](not_null button) { + button->moveToRight(right, top + add, newWidth); + top += st::windowFilterSmallItem.height; + }; + for (const auto &[flag, button] : _removeFlag) { + moveNextButton(button.get()); + } + for (const auto &[history, button] : _removePeer) { + moveNextButton(button.get()); + } + return top; +} + +[[nodiscard]] QString TypeName(Flag flag) { + switch (flag) { + case Flag::Contacts: return tr::lng_filters_type_contacts(tr::now); + case Flag::NonContacts: + return tr::lng_filters_type_non_contacts(tr::now); + case Flag::Groups: return tr::lng_filters_type_groups(tr::now); + case Flag::Channels: return tr::lng_filters_type_channels(tr::now); + case Flag::Bots: return tr::lng_filters_type_bots(tr::now); + case Flag::NoMuted: return tr::lng_filters_type_no_muted(tr::now); + case Flag::NoArchived: return tr::lng_filters_type_no_archived(tr::now); + case Flag::NoRead: return tr::lng_filters_type_no_read(tr::now); + } + Unexpected("Flag in TypeName."); +} + +void FilterChatsPreview::paintEvent(QPaintEvent *e) { + auto p = Painter(this); + auto top = 0; + const auto &st = st::windowFilterSmallItem; + const auto iconLeft = st.photoPosition.x(); + const auto iconTop = st.photoPosition.y(); + const auto nameLeft = st.namePosition.x(); + p.setFont(st::windowFilterSmallItem.nameStyle.font); + const auto nameTop = st.namePosition.y(); + for (const auto &[flag, button] : _removeFlag) { + paintFlagIcon(p, iconLeft, top + iconTop, flag); + + p.setPen(st::contactsNameFg); + p.drawTextLeft(nameLeft, top + nameTop, width(), TypeName(flag)); + top += st.height; + } + for (const auto &[history, button] : _removePeer) { + history->peer->paintUserpicLeft( + p, + iconLeft, + top + iconTop, + width(), + st.photoSize); + p.setPen(st::contactsNameFg); + history->peer->nameText().drawLeftElided( + p, + nameLeft, + top + nameTop, + button->x() - nameLeft, + width()); + top += st.height; + } +} + +void FilterChatsPreview::paintFlagIcon( + QPainter &p, + int left, + int top, + Flag flag) const { + const auto &color = [&]() -> const style::color& { + switch (flag) { + case Flag::Contacts: return st::historyPeer4UserpicBg; + case Flag::NonContacts: return st::historyPeer7UserpicBg; + case Flag::Groups: return st::historyPeer2UserpicBg; + case Flag::Channels: return st::historyPeer1UserpicBg; + case Flag::Bots: return st::historyPeer6UserpicBg; + case Flag::NoMuted: return st::historyPeer6UserpicBg; + case Flag::NoArchived: return st::historyPeer4UserpicBg; + case Flag::NoRead: return st::historyPeer7UserpicBg; + } + Unexpected("Flag in color paintFlagIcon."); + }(); + const auto &icon = [&]() -> const style::icon& { + switch (flag) { + case Flag::Contacts: return st::windowFilterTypeContacts; + case Flag::NonContacts: return st::windowFilterTypeNonContacts; + case Flag::Groups: return st::windowFilterTypeGroups; + case Flag::Channels: return st::windowFilterTypeChannels; + case Flag::Bots: return st::windowFilterTypeBots; + case Flag::NoMuted: return st::windowFilterTypeNoMuted; + case Flag::NoArchived: return st::windowFilterTypeNoArchived; + case Flag::NoRead: return st::windowFilterTypeNoRead; + } + Unexpected("Flag in icon paintFlagIcon."); + }(); + const auto size = st::windowFilterSmallItem.photoSize; + const auto rect = QRect(left, top, size, size); + auto hq = PainterHighQualityEnabler(p); + p.setBrush(color->b); + p.setPen(Qt::NoPen); + p.drawEllipse(rect); + icon.paintInCenter(p, rect); +} + +void FilterChatsPreview::removeFlag(Flag flag) { + const auto i = ranges::find(_removeFlag, flag, &FlagButton::flag); + Assert(i != end(_removeFlag)); + _removeFlag.erase(i); + refresh(); + _flagRemoved.fire_copy(flag); +} + +void FilterChatsPreview::removePeer(not_null history) { + const auto i = ranges::find(_removePeer, history, &PeerButton::history); + Assert(i != end(_removePeer)); + _removePeer.erase(i); + refresh(); + _peerRemoved.fire_copy(history); +} + +rpl::producer FilterChatsPreview::flagRemoved() const { + return _flagRemoved.events(); +} + +rpl::producer> FilterChatsPreview::peerRemoved() const { + return _peerRemoved.events(); +} + +void EditExceptions( + not_null window, + not_null context, + Flags options, + not_null data, + Fn refresh) { + const auto include = (options & Flag::Contacts) != Flags(0); + const auto initBox = [=](not_null box) { + box->addButton(tr::lng_settings_save(), crl::guard(context, [=] { + const auto peers = box->peerListCollectSelectedRows(); + auto &&histories = ranges::view::all( + peers + ) | ranges::view::transform([=](not_null peer) { + return window->session().data().history(peer); + }); + auto changed = base::flat_set>{ + histories.begin(), + histories.end() + }; + auto removeFrom = include ? data->never() : data->always(); + for (const auto &history : changed) { + removeFrom.remove(history); + } + *data = Data::ChatFilter( + data->id(), + data->title(), + data->flags(), + include ? std::move(changed) : std::move(removeFrom), + include ? std::move(removeFrom) : std::move(changed)); + refresh(); + box->closeBox(); + })); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + }; + window->window().show( + Box( + std::make_unique( + window, + (include + ? tr::lng_filters_include_title() + : tr::lng_filters_exclude_title()), + options, + data->flags() & options, + include ? data->always() : data->never()), + std::move(initBox)), + Ui::LayerOption::KeepOther); +} + +} // namespace + +void EditFilterBox( + not_null box, + not_null window, + const Data::ChatFilter &filter, + Fn doneCallback) { + const auto creating = filter.title().isEmpty(); + box->setTitle(creating ? tr::lng_filters_new() : tr::lng_filters_edit()); + + const auto content = box->verticalLayout(); + const auto name = content->add( + object_ptr( + box, + st::defaultInputField, + tr::lng_filters_new_name(), + filter.title()), + st::markdownLinkFieldPadding); + name->setMaxLength(kMaxFilterTitleLength); + + const auto data = box->lifetime().make_state(filter); + + constexpr auto kTypes = Flag::Contacts + | Flag::NonContacts + | Flag::Groups + | Flag::Channels + | Flag::Bots; + constexpr auto kExcludeTypes = Flag::NoMuted + | Flag::NoArchived + | Flag::NoRead; + + box->setFocusCallback([=] { + name->setFocusFast(); + }); + + AddSkip(content); + AddDivider(content); + AddSkip(content); + AddSubsectionTitle(content, tr::lng_filters_include()); + + const auto include = SetupChatsPreview( + content, + data, + kTypes, + &Data::ChatFilter::always); + + const auto includeAdd = AddButton( + content, + tr::lng_filters_add_chats() | Ui::Text::ToUpper(), + st::settingsUpdate); + + AddSkip(content); + AddDividerText(content, tr::lng_filters_include_about()); + AddSkip(content); + + AddSubsectionTitle(content, tr::lng_filters_exclude()); + + const auto exclude = SetupChatsPreview( + content, + data, + kExcludeTypes, + &Data::ChatFilter::never); + + const auto excludeAdd = AddButton( + content, + tr::lng_filters_add_chats() | Ui::Text::ToUpper(), + st::settingsUpdate); + + AddSkip(content); + content->add( + object_ptr( + content, + tr::lng_filters_exclude_about(), + st::boxDividerLabel), + st::settingsDividerLabelPadding); + + const auto refreshPreviews = [=] { + include->updateData(data->flags() & kTypes, data->always()); + exclude->updateData(data->flags() & kExcludeTypes, data->never()); + }; + includeAdd->setClickedCallback([=] { + EditExceptions(window, box, kTypes, data, refreshPreviews); + }); + excludeAdd->setClickedCallback([=] { + EditExceptions(window, box, kExcludeTypes, data, refreshPreviews); + }); + + const auto save = [=] { + const auto title = name->getLastText().trimmed(); + if (title.isEmpty()) { + name->showError(); + return; + } else if (!(data->flags() & kTypes) && data->always().empty()) { + window->window().showToast(tr::lng_filters_empty(tr::now)); + return; + } else if ((data->flags() == (kTypes | Flag::NoArchived)) + && data->always().empty() + && data->never().empty()) { + window->window().showToast(tr::lng_filters_default(tr::now)); + return; + } + const auto result = Data::ChatFilter( + data->id(), + title, + data->flags(), + data->always(), + data->never()); + box->closeBox(); + + doneCallback(result); + }; + box->addButton( + creating ? tr::lng_filters_create_button() : tr::lng_settings_save(), + save); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} \ No newline at end of file diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_box.h b/Telegram/SourceFiles/boxes/filters/edit_filter_box.h new file mode 100644 index 000000000..8cd87f8d2 --- /dev/null +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_box.h @@ -0,0 +1,26 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Ui { +class GenericBox; +} // namespace Ui + +namespace Window { +class SessionController; +} // namespace Window + +namespace Data { +class ChatFilter; +} // namespace Data + +void EditFilterBox( + not_null box, + not_null window, + const Data::ChatFilter &filter, + Fn doneCallback); diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp new file mode 100644 index 000000000..c2e8e96d4 --- /dev/null +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp @@ -0,0 +1,66 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "boxes/filters/edit_filter_chats_list.h" + +#include "history/history.h" +#include "window/window_session_controller.h" +#include "lang/lang_keys.h" + +namespace { + +constexpr auto kMaxExceptions = 100; + +} // namespace + +EditFilterChatsListController::EditFilterChatsListController( + not_null navigation, + rpl::producer title, + Flags options, + Flags selected, + const base::flat_set> &peers) +: ChatsListBoxController(navigation) +, _navigation(navigation) +, _title(std::move(title)) +, _peers(peers) { +} + +Main::Session &EditFilterChatsListController::session() const { + return _navigation->session(); +} + +void EditFilterChatsListController::rowClicked(not_null row) { + const auto count = delegate()->peerListSelectedRowsCount(); + if (count < kMaxExceptions || row->checked()) { + delegate()->peerListSetRowChecked(row, !row->checked()); + updateTitle(); + } +} + +void EditFilterChatsListController::itemDeselectedHook( + not_null peer) { + updateTitle(); +} + +void EditFilterChatsListController::prepareViewHook() { + delegate()->peerListSetTitle(std::move(_title)); + delegate()->peerListAddSelectedRows( + _peers | ranges::view::transform(&History::peer)); +} + +auto EditFilterChatsListController::createRow(not_null history) +-> std::unique_ptr { + return std::make_unique(history); +} + +void EditFilterChatsListController::updateTitle() { + const auto count = delegate()->peerListSelectedRowsCount(); + const auto additional = qsl("%1 / %2").arg(count).arg(kMaxExceptions); + delegate()->peerListSetTitle(tr::lng_profile_add_participant()); + delegate()->peerListSetAdditionalTitle(rpl::single(additional)); + +} diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h new file mode 100644 index 000000000..6840b2b56 --- /dev/null +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.h @@ -0,0 +1,55 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "boxes/peer_list_controllers.h" +#include "data/data_chat_filters.h" + +class History; + +namespace Ui { +class GenericBox; +} // namespace Ui + +namespace Window { +class SessionController; +} // namespace Window + +namespace Main { +class Session; +} // namespace Main + +class EditFilterChatsListController final : public ChatsListBoxController { +public: + using Flag = Data::ChatFilter::Flag; + using Flags = Data::ChatFilter::Flags; + + EditFilterChatsListController( + not_null navigation, + rpl::producer title, + Flags options, + Flags selected, + const base::flat_set> &peers); + + Main::Session &session() const override; + + void rowClicked(not_null row) override; + void itemDeselectedHook(not_null peer) override; + +protected: + void prepareViewHook() override; + std::unique_ptr createRow(not_null history) override; + +private: + void updateTitle(); + + const not_null _navigation; + rpl::producer _title; + base::flat_set> _peers; + +}; diff --git a/Telegram/SourceFiles/boxes/manage_filters_box.cpp b/Telegram/SourceFiles/boxes/filters/manage_filters_box.cpp similarity index 60% rename from Telegram/SourceFiles/boxes/manage_filters_box.cpp rename to Telegram/SourceFiles/boxes/filters/manage_filters_box.cpp index 3f194157d..a6bd98d8e 100644 --- a/Telegram/SourceFiles/boxes/manage_filters_box.cpp +++ b/Telegram/SourceFiles/boxes/filters/manage_filters_box.cpp @@ -5,10 +5,10 @@ the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ -#include "boxes/manage_filters_box.h" +#include "boxes/filters/manage_filters_box.h" +#include "boxes/filters/edit_filter_box.h" #include "data/data_session.h" -#include "data/data_chat_filters.h" #include "data/data_folder.h" #include "data/data_peer.h" #include "history/history.h" @@ -35,66 +35,11 @@ namespace { constexpr auto kRefreshSuggestedTimeout = 7200 * crl::time(1000); constexpr auto kFiltersLimit = 10; -constexpr auto kMaxFilterTitleLength = 20; using namespace Settings; using Flag = Data::ChatFilter::Flag; using Flags = Data::ChatFilter::Flags; -using ExceptionPeersRef = const base::flat_set> &; -using ExceptionPeersGetter = ExceptionPeersRef(Data::ChatFilter::*)() const; - -constexpr auto kAllTypes = { - Flag::Contacts, - Flag::NonContacts, - Flag::Groups, - Flag::Channels, - Flag::Bots, - Flag::NoMuted, - Flag::NoArchived, - Flag::NoRead -}; - -class FilterChatsPreview final : public Ui::RpWidget { -public: - FilterChatsPreview( - not_null parent, - Flags flags, - const base::flat_set> &peers); - - [[nodiscard]] rpl::producer flagRemoved() const; - [[nodiscard]] rpl::producer> peerRemoved() const; - - int resizeGetHeight(int newWidth) override; - -private: - using Button = base::unique_qptr; - struct FlagButton { - Flag flag = Flag(); - Button button; - }; - struct PeerButton { - not_null history; - Button button; - }; - - void paintEvent(QPaintEvent *e) override; - - void setup( - Flags flags, - const base::flat_set> &peers); - void refresh(); - void removeFlag(Flag flag); - void removePeer(not_null history); - void paintFlagIcon(QPainter &p, int left, int top, Flag flag) const; - - std::vector _removeFlag; - std::vector _removePeer; - - rpl::event_stream _flagRemoved; - rpl::event_stream> _peerRemoved; - -}; class FilterRowButton final : public Ui::RippleButton { public: @@ -196,173 +141,6 @@ private: : tr::lng_filters_no_chats(tr::now); } -FilterChatsPreview::FilterChatsPreview( - not_null parent, - Flags flags, - const base::flat_set> &peers) -: RpWidget(parent) { - setup(flags, peers); -} - -void FilterChatsPreview::setup( - Flags flags, - const base::flat_set> &peers) { - const auto makeButton = [&](Fn handler) { - auto result = base::make_unique_q( - this, - st::windowFilterSmallRemove); - result->setClickedCallback(std::move(handler)); - return result; - }; - for (const auto flag : kAllTypes) { - if (flags & flag) { - _removeFlag.push_back({ - flag, - makeButton([=] { removeFlag(flag); }) }); - } - } - for (const auto history : peers) { - _removePeer.push_back({ - history, - makeButton([=] { removePeer(history); }) }); - } - refresh(); -} - -void FilterChatsPreview::refresh() { - resizeToWidth(width()); -} - -int FilterChatsPreview::resizeGetHeight(int newWidth) { - const auto right = st::windowFilterSmallRemoveRight; - const auto add = (st::windowFilterSmallItem.height - - st::windowFilterSmallRemove.height) / 2; - auto top = 0; - const auto moveNextButton = [&](not_null button) { - button->moveToRight(right, top + add, newWidth); - top += st::windowFilterSmallItem.height; - }; - for (const auto &[flag, button] : _removeFlag) { - moveNextButton(button.get()); - } - for (const auto &[history, button] : _removePeer) { - moveNextButton(button.get()); - } - return top; -} - -[[nodiscard]] QString TypeName(Flag flag) { - switch (flag) { - case Flag::Contacts: return tr::lng_filters_type_contacts(tr::now); - case Flag::NonContacts: - return tr::lng_filters_type_non_contacts(tr::now); - case Flag::Groups: return tr::lng_filters_type_groups(tr::now); - case Flag::Channels: return tr::lng_filters_type_channels(tr::now); - case Flag::Bots: return tr::lng_filters_type_bots(tr::now); - case Flag::NoMuted: return tr::lng_filters_type_no_muted(tr::now); - case Flag::NoArchived: return tr::lng_filters_type_no_archived(tr::now); - case Flag::NoRead: return tr::lng_filters_type_no_read(tr::now); - } - Unexpected("Flag in TypeName."); -} - -void FilterChatsPreview::paintEvent(QPaintEvent *e) { - auto p = Painter(this); - auto top = 0; - const auto &st = st::windowFilterSmallItem; - const auto iconLeft = st.photoPosition.x(); - const auto iconTop = st.photoPosition.y(); - const auto nameLeft = st.namePosition.x(); - p.setFont(st::windowFilterSmallItem.nameStyle.font); - const auto nameTop = st.namePosition.y(); - for (const auto &[flag, button] : _removeFlag) { - paintFlagIcon(p, iconLeft, top + iconTop, flag); - - p.setPen(st::contactsNameFg); - p.drawTextLeft(nameLeft, top + nameTop, width(), TypeName(flag)); - top += st.height; - } - for (const auto &[history, button] : _removePeer) { - history->peer->paintUserpicLeft( - p, - iconLeft, - top + iconTop, - width(), - st.photoSize); - history->peer->nameText().drawLeftElided( - p, - nameLeft, - top + nameTop, - button->x() - nameLeft, - width()); - top += st.height; - } -} - -void FilterChatsPreview::paintFlagIcon( - QPainter &p, - int left, - int top, - Flag flag) const { - const auto &color = [&]() -> const style::color& { - switch (flag) { - case Flag::Contacts: return st::historyPeer4UserpicBg; - case Flag::NonContacts: return st::historyPeer7UserpicBg; - case Flag::Groups: return st::historyPeer2UserpicBg; - case Flag::Channels: return st::historyPeer1UserpicBg; - case Flag::Bots: return st::historyPeer6UserpicBg; - case Flag::NoMuted: return st::historyPeer6UserpicBg; - case Flag::NoArchived: return st::historyPeer4UserpicBg; - case Flag::NoRead: return st::historyPeer7UserpicBg; - } - Unexpected("Flag in color paintFlagIcon."); - }(); - const auto &icon = [&]() -> const style::icon& { - switch (flag) { - case Flag::Contacts: return st::windowFilterTypeContacts; - case Flag::NonContacts: return st::windowFilterTypeNonContacts; - case Flag::Groups: return st::windowFilterTypeGroups; - case Flag::Channels: return st::windowFilterTypeChannels; - case Flag::Bots: return st::windowFilterTypeBots; - case Flag::NoMuted: return st::windowFilterTypeNoMuted; - case Flag::NoArchived: return st::windowFilterTypeNoArchived; - case Flag::NoRead: return st::windowFilterTypeNoRead; - } - Unexpected("Flag in icon paintFlagIcon."); - }(); - const auto size = st::windowFilterSmallItem.photoSize; - const auto rect = QRect(left, top, size, size); - auto hq = PainterHighQualityEnabler(p); - p.setBrush(color->b); - p.setPen(Qt::NoPen); - p.drawEllipse(rect); - icon.paintInCenter(p, rect); -} - -void FilterChatsPreview::removeFlag(Flag flag) { - const auto i = ranges::find(_removeFlag, flag, &FlagButton::flag); - Assert(i != end(_removeFlag)); - _removeFlag.erase(i); - refresh(); - _flagRemoved.fire_copy(flag); -} - -void FilterChatsPreview::removePeer(not_null history) { - const auto i = ranges::find(_removePeer, history, &PeerButton::history); - Assert(i != end(_removePeer)); - _removePeer.erase(i); - refresh(); - _peerRemoved.fire_copy(history); -} - -rpl::producer FilterChatsPreview::flagRemoved() const { - return _flagRemoved.events(); -} - -rpl::producer> FilterChatsPreview::peerRemoved() const { - return _peerRemoved.events(); -} - FilterRowButton::FilterRowButton( not_null parent, not_null session, @@ -609,7 +387,7 @@ void ManageFiltersPrepare::SetupBox( button->updateData(result); }; window->window().show(Box( - EditBox, + EditFilterBox, window, found->filter, crl::guard(button, doneCallback))); @@ -633,7 +411,7 @@ void ManageFiltersPrepare::SetupBox( addFilter(result); }; window->window().show(Box( - EditBox, + EditFilterBox, window, Data::ChatFilter(), crl::guard(box, doneCallback))); @@ -765,145 +543,3 @@ void ManageFiltersPrepare::SetupBox( box->boxClosing() | rpl::start_with_next(save, box->lifetime()); box->addButton(tr::lng_about_done(), [=] { box->closeBox(); }); } - -void SetupChatsPreview( - not_null content, - not_null data, - Flags flags, - ExceptionPeersGetter peers) { - const auto preview = content->add(object_ptr( - content, - data->flags() & flags, - (data->*peers)())); - - preview->flagRemoved( - ) | rpl::start_with_next([=](Flag flag) { - *data = Data::ChatFilter( - data->id(), - data->title(), - (data->flags() & ~flag), - data->always(), - data->never()); - }, preview->lifetime()); - - preview->peerRemoved( - ) | rpl::start_with_next([=](not_null history) { - auto always = data->always(); - auto never = data->never(); - always.remove(history); - never.remove(history); - *data = Data::ChatFilter( - data->id(), - data->title(), - data->flags(), - std::move(always), - std::move(never)); - }, preview->lifetime()); -} - -void ManageFiltersPrepare::EditBox( - not_null box, - not_null window, - const Data::ChatFilter &filter, - Fn doneCallback) { - const auto creating = filter.title().isEmpty(); - box->setTitle(creating ? tr::lng_filters_new() : tr::lng_filters_edit()); - - const auto content = box->verticalLayout(); - const auto name = content->add( - object_ptr( - box, - st::defaultInputField, - tr::lng_filters_new_name(), - filter.title()), - st::markdownLinkFieldPadding); - name->setMaxLength(kMaxFilterTitleLength); - - const auto data = box->lifetime().make_state(filter); - - constexpr auto kTypes = Flag::Contacts - | Flag::NonContacts - | Flag::Groups - | Flag::Channels - | Flag::Bots; - constexpr auto kExcludeTypes = Flag::NoMuted - | Flag::NoArchived - | Flag::NoRead; - - box->setFocusCallback([=] { - name->setFocusFast(); - }); - - AddSkip(content); - AddDivider(content); - AddSkip(content); - AddSubsectionTitle(content, tr::lng_filters_include()); - - SetupChatsPreview( - content, - data, - kTypes, - &Data::ChatFilter::always); - - AddButton( - content, - tr::lng_filters_add_chats() | Ui::Text::ToUpper(), - st::settingsUpdate - )->setClickedCallback([=] { - }); - - AddSkip(content); - AddDividerText(content, tr::lng_filters_include_about()); - AddSkip(content); - - AddSubsectionTitle(content, tr::lng_filters_exclude()); - - SetupChatsPreview( - content, - data, - kExcludeTypes, - &Data::ChatFilter::never); - - AddButton( - content, - tr::lng_filters_add_chats() | Ui::Text::ToUpper(), - st::settingsUpdate - )->setClickedCallback([=] { - }); - AddSkip(content); - content->add( - object_ptr( - content, - tr::lng_filters_exclude_about(), - st::boxDividerLabel), - st::settingsDividerLabelPadding); - - const auto save = [=] { - const auto title = name->getLastText().trimmed(); - if (title.isEmpty()) { - name->showError(); - return; - } else if (!(data->flags() & kTypes) && data->always().empty()) { - window->window().showToast(tr::lng_filters_empty(tr::now)); - return; - } else if ((data->flags() == (kTypes | Flag::NoArchived)) - && data->always().empty() - && data->never().empty()) { - window->window().showToast(tr::lng_filters_default(tr::now)); - return; - } - const auto result = Data::ChatFilter( - data->id(), - title, - data->flags(), - data->always(), - data->never()); - box->closeBox(); - - doneCallback(result); - }; - box->addButton( - creating ? tr::lng_filters_create_button() : tr::lng_settings_save(), - save); - box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); -} \ No newline at end of file diff --git a/Telegram/SourceFiles/boxes/manage_filters_box.h b/Telegram/SourceFiles/boxes/filters/manage_filters_box.h similarity index 84% rename from Telegram/SourceFiles/boxes/manage_filters_box.h rename to Telegram/SourceFiles/boxes/filters/manage_filters_box.h index 9900927e5..955ecf311 100644 --- a/Telegram/SourceFiles/boxes/manage_filters_box.h +++ b/Telegram/SourceFiles/boxes/filters/manage_filters_box.h @@ -38,11 +38,6 @@ private: not_null box, not_null window, const std::vector &suggested); - static void EditBox( - not_null box, - not_null window, - const Data::ChatFilter &filter, - Fn doneCallback); const not_null _window; const not_null _api; diff --git a/Telegram/SourceFiles/window/window_filters_menu.cpp b/Telegram/SourceFiles/window/window_filters_menu.cpp index 7e9bd6141..1ab31de89 100644 --- a/Telegram/SourceFiles/window/window_filters_menu.cpp +++ b/Telegram/SourceFiles/window/window_filters_menu.cpp @@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "data/data_session.h" #include "data/data_chat_filters.h" -#include "boxes/manage_filters_box.h" +#include "boxes/filters/manage_filters_box.h" #include "lang/lang_keys.h" #include "styles/style_widgets.h" #include "styles/style_window.h"