From 375a8cea12e4f9b05fc9213300681776766736b4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 29 Dec 2025 17:26:21 +0400 Subject: [PATCH] Initial settings builder experiment. --- Telegram/CMakeLists.txt | 4 + .../settings/builder/settings_builder.cpp | 125 +++++++++++++++ .../settings/builder/settings_builder.h | 91 +++++++++++ .../builder/settings_main_builder.cpp | 145 ++++++++++++++++++ .../settings/builder/settings_main_builder.h | 33 ++++ .../SourceFiles/settings/settings_main.cpp | 32 +++- Telegram/SourceFiles/settings/settings_main.h | 1 + 7 files changed, 430 insertions(+), 1 deletion(-) create mode 100644 Telegram/SourceFiles/settings/builder/settings_builder.cpp create mode 100644 Telegram/SourceFiles/settings/builder/settings_builder.h create mode 100644 Telegram/SourceFiles/settings/builder/settings_main_builder.cpp create mode 100644 Telegram/SourceFiles/settings/builder/settings_main_builder.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index d3e757a5d6..5ac4f287e4 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1567,6 +1567,10 @@ PRIVATE settings/settings_local_passcode.h settings/settings_main.cpp settings/settings_main.h + settings/builder/settings_builder.cpp + settings/builder/settings_builder.h + settings/builder/settings_main_builder.cpp + settings/builder/settings_main_builder.h settings/settings_notifications.cpp settings/settings_notifications.h settings/settings_notifications_type.cpp diff --git a/Telegram/SourceFiles/settings/builder/settings_builder.cpp b/Telegram/SourceFiles/settings/builder/settings_builder.cpp new file mode 100644 index 0000000000..0717eae1bd --- /dev/null +++ b/Telegram/SourceFiles/settings/builder/settings_builder.cpp @@ -0,0 +1,125 @@ +/* +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 "settings/builder/settings_builder.h" + +#include "settings/settings_common.h" +#include "ui/vertical_list.h" +#include "ui/widgets/buttons.h" +#include "ui/wrap/vertical_layout.h" +#include "styles/style_settings.h" + +namespace Settings::Builder { +namespace { + +[[nodiscard]] QString ResolveTitle(rpl::producer title) { + auto result = QString(); + auto lifetime = rpl::lifetime(); + std::move(title).start( + [&](QString value) { result = std::move(value); }, + [](auto&&) {}, + [] {}, + lifetime); + return result; +} + +} // namespace + +SectionBuilder::SectionBuilder(BuildContext context) +: _context(std::move(context)) { +} + +Ui::RpWidget *SectionBuilder::addControl(ControlArgs &&args) { + return v::match(_context, [&](const WidgetContext &ctx) { + if (!args.factory) { + return static_cast(nullptr); + } + auto widget = args.factory(ctx.container); + const auto raw = widget.data(); + ctx.container->add(std::move(widget), args.margin); + return raw; + }, [&](const SearchContext &ctx) { + if (!args.id.isEmpty()) { + ctx.entries->push_back({ + .id = std::move(args.id), + .title = ResolveTitle(std::move(args.title)), + .keywords = std::move(args.keywords), + }); + } + return static_cast(nullptr); + }); +} + +Ui::SettingsButton *SectionBuilder::addSettingsButton(ButtonArgs &&args) { + const auto &st = args.st ? *args.st : st::settingsButton; + const auto button = v::match(_context, [&](const WidgetContext &ctx) -> Ui::SettingsButton* { + if (args.label) { + return AddButtonWithLabel( + ctx.container, + rpl::duplicate(args.title), + std::move(args.label), + st, + std::move(args.icon)); + } else { + return ctx.container->add(CreateButtonWithIcon( + ctx.container, + rpl::duplicate(args.title), + st, + std::move(args.icon))); + } + }, [&](const SearchContext &ctx) -> Ui::SettingsButton* { + if (!args.id.isEmpty()) { + ctx.entries->push_back({ + .id = std::move(args.id), + .title = ResolveTitle(std::move(args.title)), + .keywords = std::move(args.keywords), + }); + } + return nullptr; + }); + if (button && args.onClick) { + button->addClickHandler(std::move(args.onClick)); + } + return button; +} + +Ui::SettingsButton *SectionBuilder::addLabeledButton(ButtonArgs &&args) { + return addSettingsButton(std::move(args)); +} + +Ui::SettingsButton *SectionBuilder::addSectionButton(SectionArgs &&args) { + const auto button = addSettingsButton({ + .id = std::move(args.id), + .title = std::move(args.title), + .icon = std::move(args.icon), + .keywords = std::move(args.keywords), + }); + if (button) { + const auto showOther = std::get(_context).showOther; + const auto target = args.targetSection; + button->addClickHandler([=] { + showOther(target); + }); + } + return button; +} + +void SectionBuilder::addDivider() { + v::match(_context, [&](const WidgetContext &ctx) { + Ui::AddDivider(ctx.container); + }, [](const SearchContext &) { + }); +} + +void SectionBuilder::addSkip() { + v::match(_context, [&](const WidgetContext &ctx) { + Ui::AddSkip(ctx.container); + }, [](const SearchContext &) { + }); +} + +} // namespace Settings::Builder diff --git a/Telegram/SourceFiles/settings/builder/settings_builder.h b/Telegram/SourceFiles/settings/builder/settings_builder.h new file mode 100644 index 0000000000..0379b56f69 --- /dev/null +++ b/Telegram/SourceFiles/settings/builder/settings_builder.h @@ -0,0 +1,91 @@ +/* +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 "base/object_ptr.h" +#include "settings/settings_common.h" +#include "settings/settings_type.h" + +#include + +namespace Ui { +class RpWidget; +class VerticalLayout; +class SettingsButton; +} // namespace Ui + +namespace Window { +class SessionController; +} // namespace Window + +namespace style { +struct SettingsButton; +} // namespace style + +namespace Settings::Builder { + +struct SearchEntry { + QString id; + QString title; + QStringList keywords; +}; + +struct WidgetContext { + not_null container; + not_null controller; + Fn showOther; +}; + +struct SearchContext { + not_null*> entries; +}; + +using BuildContext = std::variant; + +class SectionBuilder { +public: + explicit SectionBuilder(BuildContext context); + + struct ControlArgs { + Fn(not_null)> factory; + QString id; + rpl::producer title; + QStringList keywords; + style::margins margin; + }; + Ui::RpWidget *addControl(ControlArgs &&args); + + struct ButtonArgs { + QString id; + rpl::producer title; + const style::SettingsButton *st = nullptr; + IconDescriptor icon; + rpl::producer label; + Fn onClick; + QStringList keywords; + }; + Ui::SettingsButton *addSettingsButton(ButtonArgs &&args); + Ui::SettingsButton *addLabeledButton(ButtonArgs &&args); + + struct SectionArgs { + QString id; + rpl::producer title; + Type targetSection; + IconDescriptor icon; + QStringList keywords; + }; + Ui::SettingsButton *addSectionButton(SectionArgs &&args); + + void addDivider(); + void addSkip(); + +private: + BuildContext _context; +}; + +} // namespace Settings::Builder diff --git a/Telegram/SourceFiles/settings/builder/settings_main_builder.cpp b/Telegram/SourceFiles/settings/builder/settings_main_builder.cpp new file mode 100644 index 0000000000..ec58216333 --- /dev/null +++ b/Telegram/SourceFiles/settings/builder/settings_main_builder.cpp @@ -0,0 +1,145 @@ +/* +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 "settings/builder/settings_main_builder.h" + +#include "ui/layers/generic_box.h" +#include "settings/builder/settings_builder.h" +#include "settings/settings_advanced.h" +#include "settings/settings_calls.h" +#include "settings/settings_chat.h" +#include "settings/settings_folders.h" +#include "settings/settings_information.h" +#include "settings/settings_notifications.h" +#include "settings/settings_power_saving.h" +#include "settings/settings_privacy_security.h" +#include "boxes/language_box.h" +#include "lang/lang_instance.h" +#include "lang/lang_keys.h" +#include "window/window_controller.h" +#include "window/window_session_controller.h" +#include "styles/style_menu_icons.h" +#include "styles/style_settings.h" + +namespace Settings::Builder { +namespace { + +void BuildMainSectionContent( + SectionBuilder &builder, + Window::SessionController *controller) { + builder.addDivider(); + builder.addSkip(); + + builder.addSectionButton({ + .id = u"main/account"_q, + .title = tr::lng_settings_my_account(), + .targetSection = Information::Id(), + .icon = { &st::menuIconProfile }, + .keywords = { u"profile"_q, u"edit"_q, u"information"_q }, + }); + + builder.addSectionButton({ + .id = u"main/notifications"_q, + .title = tr::lng_settings_section_notify(), + .targetSection = Notifications::Id(), + .icon = { &st::menuIconNotifications }, + .keywords = { u"alerts"_q, u"sounds"_q, u"badge"_q }, + }); + + builder.addSectionButton({ + .id = u"main/privacy"_q, + .title = tr::lng_settings_section_privacy(), + .targetSection = PrivacySecurity::Id(), + .icon = { &st::menuIconLock }, + .keywords = { u"security"_q, u"passcode"_q, u"password"_q, u"2fa"_q }, + }); + + builder.addSectionButton({ + .id = u"main/chat"_q, + .title = tr::lng_settings_section_chat_settings(), + .targetSection = Chat::Id(), + .icon = { &st::menuIconChatBubble }, + .keywords = { u"themes"_q, u"appearance"_q, u"stickers"_q }, + }); + + builder.addSectionButton({ + .id = u"main/folders"_q, + .title = tr::lng_settings_section_filters(), + .targetSection = Folders::Id(), + .icon = { &st::menuIconShowInFolder }, + .keywords = { u"filters"_q, u"tabs"_q }, + }); + + builder.addSectionButton({ + .id = u"main/advanced"_q, + .title = tr::lng_settings_advanced(), + .targetSection = Advanced::Id(), + .icon = { &st::menuIconManage }, + .keywords = { u"performance"_q, u"proxy"_q, u"experimental"_q }, + }); + + builder.addSectionButton({ + .id = u"main/devices"_q, + .title = tr::lng_settings_section_devices(), + .targetSection = Calls::Id(), + .icon = { &st::menuIconUnmute }, + .keywords = { u"sessions"_q, u"calls"_q }, + }); + + const auto window = controller ? &controller->window() : nullptr; + builder.addSettingsButton({ + .id = u"main/power"_q, + .title = tr::lng_settings_power_menu(), + .icon = { &st::menuIconPowerUsage }, + .onClick = window + ? [=] { window->show(Box(PowerSavingBox)); } + : Fn(nullptr), + .keywords = { u"battery"_q, u"animations"_q, u"power"_q, u"saving"_q }, + }); + + builder.addLabeledButton({ + .id = u"main/language"_q, + .title = tr::lng_settings_language(), + .icon = { &st::menuIconTranslate }, + .label = rpl::single( + Lang::GetInstance().id() + ) | rpl::then( + Lang::GetInstance().idChanges() + ) | rpl::map([] { return Lang::GetInstance().nativeName(); }), + .onClick = controller + ? [=] { LanguageBox::Show(controller); } + : Fn(nullptr), + .keywords = { u"translate"_q, u"localization"_q }, + }); + + builder.addSkip(); +} + +} // namespace + +void BuildMainSection( + not_null container, + not_null controller, + Fn showOther) { + auto builder = SectionBuilder(WidgetContext{ + .container = container, + .controller = controller, + .showOther = std::move(showOther), + }); + BuildMainSectionContent(builder, controller); +} + +std::vector BuildMainSectionForSearch() { + auto entries = std::vector(); + auto builder = SectionBuilder(SearchContext{ + .entries = &entries, + }); + BuildMainSectionContent(builder, nullptr); + return entries; +} + +} // namespace Settings::Builder diff --git a/Telegram/SourceFiles/settings/builder/settings_main_builder.h b/Telegram/SourceFiles/settings/builder/settings_main_builder.h new file mode 100644 index 0000000000..9c5273cf5f --- /dev/null +++ b/Telegram/SourceFiles/settings/builder/settings_main_builder.h @@ -0,0 +1,33 @@ +/* +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 "settings/settings_type.h" + +#include + +namespace Ui { +class VerticalLayout; +} // namespace Ui + +namespace Window { +class SessionController; +} // namespace Window + +namespace Settings::Builder { + +struct SearchEntry; + +void BuildMainSection( + not_null container, + not_null controller, + Fn showOther); + +[[nodiscard]] std::vector BuildMainSectionForSearch(); + +} // namespace Settings::Builder diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp index 5f24dcf3d1..1b7f6c5ee7 100644 --- a/Telegram/SourceFiles/settings/settings_main.cpp +++ b/Telegram/SourceFiles/settings/settings_main.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "settings/settings_main.h" +#include "settings/builder/settings_main_builder.h" #include "api/api_credits.h" #include "core/application.h" #include "core/click_handler_types.h" @@ -1034,7 +1035,7 @@ Main::Main( not_null controller) : Section(parent) , _controller(controller) { - setupContent(controller); + setupContentWithBuilder(controller); _controller->session().api().premium().reload(); } @@ -1099,6 +1100,35 @@ void Main::setupContent(not_null controller) { controller->session().data().cloudThemes().refresh(); } +void Main::setupContentWithBuilder( + not_null controller) { + const auto content = Ui::CreateChild(this); + + content->add(object_ptr( + content, + controller, + controller->session().user())); + + Builder::BuildMainSection(content, controller, showOtherMethod()); + + if (HasInterfaceScale()) { + Ui::AddDivider(content); + Ui::AddSkip(content); + SetupInterfaceScale(&controller->window(), content); + Ui::AddSkip(content); + } + SetupPremium(controller, content, showOtherMethod()); + SetupHelp(controller, content); + + Ui::ResizeFitChild(this, content); + + controller->session().api().cloudPassword().reload(); + controller->session().api().reloadContactSignupSilent(); + controller->session().api().sensitiveContent().reload(); + controller->session().api().globalPrivacy().reload(); + controller->session().data().cloudThemes().refresh(); +} + void OpenFaq(base::weak_ptr weak) { UrlClickHandler::Open( tr::lng_settings_faq_link(tr::now), diff --git a/Telegram/SourceFiles/settings/settings_main.h b/Telegram/SourceFiles/settings/settings_main.h index a0f7e15ca6..9da6d278c3 100644 --- a/Telegram/SourceFiles/settings/settings_main.h +++ b/Telegram/SourceFiles/settings/settings_main.h @@ -45,6 +45,7 @@ protected: private: void setupContent(not_null controller); + void setupContentWithBuilder(not_null controller); const not_null _controller;