Search results look nicer now.

This commit is contained in:
John Preston
2026-01-13 14:28:31 +04:00
parent 1d0c41b295
commit df1410d84d
14 changed files with 428 additions and 279 deletions
@@ -168,7 +168,7 @@ void BuildDataStorageSection(
.keywords = { u"downloads"_q, u"files"_q },
});
const auto askDownloadPath = builder.addToggle({
const auto askDownloadPath = builder.addButton({
.id = u"advanced/ask_download"_q,
.title = tr::lng_download_path_ask(),
.st = &st::settingsButtonNoIcon,
@@ -375,19 +375,19 @@ void BuildSystemIntegrationSection(
: nullptr;
const auto monochrome = Platform::HasMonochromeSetting()
? builder.addSlideCheckbox({
? builder.addCheckbox({
.id = u"advanced/monochrome_icon"_q,
.title = tr::lng_settings_monochrome_icon(),
.checked = settings->trayIconMonochrome(),
.keywords = { u"monochrome"_q, u"icon"_q, u"tray"_q },
.shown = tray
? tray->checkedValue()
: rpl::single(trayEnabled()),
.keywords = { u"monochrome"_q, u"icon"_q, u"tray"_q },
})
: nullptr;
if (monochrome && monochrome->entity()) {
monochrome->entity()->checkedChanges(
if (monochrome) {
monochrome->checkedChanges(
) | rpl::filter([=](bool value) {
return (value != settings->trayIconMonochrome());
}) | rpl::on_next([=](bool value) {
@@ -501,17 +501,17 @@ void BuildSystemIntegrationSection(
}, container->lifetime());
}
const auto closeToTaskbar = builder.addSlideCheckbox({
const auto closeToTaskbar = builder.addCheckbox({
.id = u"advanced/close_to_taskbar"_q,
.title = tr::lng_settings_close_to_taskbar(),
.checked = settings->closeBehavior() == Behavior::CloseToTaskbar,
.keywords = { u"close"_q, u"taskbar"_q, u"minimize"_q },
.shown = closeToTaskbarShown
? closeToTaskbarShown->value()
: rpl::single(false),
.keywords = { u"close"_q, u"taskbar"_q, u"minimize"_q },
});
if (closeToTaskbar && closeToTaskbar->entity()) {
closeToTaskbar->entity()->checkedChanges(
if (closeToTaskbar) {
closeToTaskbar->checkedChanges(
) | rpl::map([=](bool checked) {
return checked ? Behavior::CloseToTaskbar : Behavior::Quit;
}) | rpl::filter([=](Behavior value) {
@@ -537,14 +537,14 @@ void BuildSystemIntegrationSection(
.keywords = { u"autostart"_q, u"startup"_q, u"boot"_q },
});
const auto minimized = builder.addSlideCheckbox({
const auto minimized = builder.addCheckbox({
.id = u"advanced/start_minimized"_q,
.title = tr::lng_settings_start_min(),
.checked = minimizedToggled(),
.keywords = { u"minimized"_q, u"startup"_q, u"hidden"_q },
.shown = autostart
? autostart->checkedValue()
: rpl::single(cAutoStart()),
.keywords = { u"minimized"_q, u"startup"_q, u"hidden"_q },
});
if (autostart) {
@@ -563,10 +563,10 @@ void BuildSystemIntegrationSection(
Ui::PostponeCall(autostart, [=] {
autostart->setChecked(enabled);
});
if (enabled || !minimized || !minimized->entity()->checked()) {
if (enabled || !minimized || !minimized->checked()) {
Local::writeSettings();
} else if (minimized) {
minimized->entity()->setChecked(false);
minimized->setChecked(false);
}
}));
}, autostart->lifetime());
@@ -578,13 +578,13 @@ void BuildSystemIntegrationSection(
}
}
if (minimized && minimized->entity() && controller) {
minimized->entity()->checkedChanges(
if (minimized && controller) {
minimized->checkedChanges(
) | rpl::filter([=](bool checked) {
return (checked != minimizedToggled());
}) | rpl::on_next([=](bool checked) {
if (controller->session().domain().local().hasLocalPasscode()) {
minimized->entity()->setChecked(false);
minimized->setChecked(false);
controller->show(Ui::MakeInformBox(
tr::lng_error_start_minimized_passcoded()));
} else {
@@ -595,7 +595,7 @@ void BuildSystemIntegrationSection(
controller->session().domain().local().localPasscodeChanged(
) | rpl::on_next([=] {
minimized->entity()->setChecked(minimizedToggled());
minimized->setChecked(minimizedToggled());
}, minimized->lifetime());
}
}
@@ -703,7 +703,7 @@ void BuildANGLEOption(
void BuildOpenGLOption(
SectionBuilder &builder,
Window::SessionController *controller) {
const auto opengl = builder.addToggle({
const auto opengl = builder.addButton({
.id = u"advanced/opengl"_q,
.title = tr::lng_settings_enable_opengl(),
.st = &st::settingsButtonNoIcon,
@@ -750,7 +750,7 @@ void BuildPerformanceSection(
.keywords = { u"power"_q, u"saving"_q, u"battery"_q, u"animation"_q },
});
const auto hwAccel = builder.addToggle({
const auto hwAccel = builder.addButton({
.id = u"advanced/hw_accel"_q,
.title = tr::lng_settings_enable_hwaccel(),
.st = &st::settingsButtonNoIcon,
@@ -794,7 +794,7 @@ void BuildSpellcheckerSection(
builder.addSkip();
builder.addSubsectionTitle(tr::lng_settings_spellchecker());
const auto spellchecker = builder.addToggle({
const auto spellchecker = builder.addButton({
.id = u"advanced/spellchecker"_q,
.title = isSystem
? tr::lng_settings_system_spellchecker()
@@ -823,7 +823,7 @@ void BuildSpellcheckerSection(
const auto inner = sliding ? sliding->entity() : nullptr;
if (!isSystem) {
const auto autoDownload = builder.addToggle({
const auto autoDownload = builder.addButton({
.id = u"advanced/auto_download_dictionaries"_q,
.title = tr::lng_settings_auto_download_dictionaries(),
.st = &st::settingsButtonNoIcon,
@@ -898,7 +898,7 @@ void BuildUpdateSection(
? Ui::CreateChild<rpl::event_stream<bool>>(container)
: nullptr;
const auto toggle = builder.addToggle({
const auto toggle = builder.addButton({
.id = u"advanced/auto_update"_q,
.title = tr::lng_settings_update_automatically(),
.st = &st::settingsUpdateToggle,
@@ -933,7 +933,7 @@ void BuildUpdateSection(
const auto install = cAlphaVersion()
? nullptr
: builder.addToggle({
: builder.addButton({
.id = u"advanced/install_beta"_q,
.title = tr::lng_settings_install_beta(),
.st = &st::settingsButtonNoIcon,
@@ -1112,25 +1112,29 @@ void BuildExportSection(
});
}
const auto kMeta = BuildHelper(Advanced::Id(), [](SectionBuilder &builder) {
const auto controller = builder.controller();
const auto showOther = builder.showOther();
const auto autoUpdate = cAutoUpdate();
const auto kMeta = BuildHelper(
Advanced::Id(),
tr::lng_settings_advanced,
[](SectionBuilder &builder) {
const auto controller = builder.controller();
const auto showOther = builder.showOther();
const auto autoUpdate = cAutoUpdate();
if (!autoUpdate) {
BuildUpdateSection(builder, controller, true);
}
BuildDataStorageSection(builder, controller);
BuildAutoDownloadSection(builder, controller);
BuildWindowTitleSection(builder, controller);
BuildSystemIntegrationSection(builder, controller);
BuildPerformanceSection(builder, controller);
BuildSpellcheckerSection(builder, controller);
if (autoUpdate) {
BuildUpdateSection(builder, controller, false);
}
BuildExportSection(builder, controller, showOther);
}, Main::Id());
if (!autoUpdate) {
BuildUpdateSection(builder, controller, true);
}
BuildDataStorageSection(builder, controller);
BuildAutoDownloadSection(builder, controller);
BuildWindowTitleSection(builder, controller);
BuildSystemIntegrationSection(builder, controller);
BuildPerformanceSection(builder, controller);
BuildSpellcheckerSection(builder, controller);
if (autoUpdate) {
BuildUpdateSection(builder, controller, false);
}
BuildExportSection(builder, controller, showOther);
},
Main::Id());
} // namespace
@@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "settings/builder/settings_builder.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "api/api_user_privacy.h"
#include "boxes/edit_privacy_box.h"
#include "main/main_session.h"
#include "settings/settings_common.h"
@@ -37,6 +40,7 @@ namespace {
BuildHelper::BuildHelper(
Type sectionId,
tr::phrase<> sectionTitle,
FnMut<void(SectionBuilder&)> method,
Type parentSectionId)
: build([=](
@@ -50,7 +54,10 @@ BuildHelper::BuildHelper(
controller,
Window::GifPauseReason::Layer);
auto builder = SectionBuilder(WidgetContext{
.container = container,
.container = static_cast<Ui::VerticalLayout*>(
container->add(object_ptr<Ui::OverrideMargins>(
container,
object_ptr<Ui::VerticalLayout>(container)))->entity()),
.controller = controller,
.showOther = std::move(showOther),
.isPaused = isPaused,
@@ -74,8 +81,10 @@ BuildHelper::BuildHelper(
, _method(std::move(method)) {
Expects(_method != nullptr);
SearchRegistry::Instance().add(
auto &registry = SearchRegistry::Instance();
registry.add(
sectionId,
sectionTitle,
parentSectionId,
[=](not_null<::Main::Session*> session) { return index(session); });
}
@@ -87,10 +96,12 @@ SearchRegistry &SearchRegistry::Instance() {
void SearchRegistry::add(
Type sectionId,
tr::phrase<> title,
Type parentSectionId,
SearchEntriesIndexer indexer) {
_indexers.push_back({ sectionId, std::move(indexer) });
_sectionTitles[sectionId] = title;
_parentSections[sectionId] = parentSectionId;
_indexers.push_back({ sectionId, std::move(indexer) });
}
std::vector<SearchEntry> SearchRegistry::collectAll(
@@ -103,6 +114,24 @@ std::vector<SearchEntry> SearchRegistry::collectAll(
return result;
}
QString SearchRegistry::sectionTitle(Type sectionId) const {
const auto it = _sectionTitles.find(sectionId);
return (it != _sectionTitles.end()) ? it->second(tr::now) : QString();
}
QString SearchRegistry::sectionPath(Type sectionId) const {
auto parts = QStringList();
auto current = sectionId;
while (current) {
if (const auto title = sectionTitle(current); !title.isEmpty()) {
parts.prepend(title);
}
const auto it = _parentSections.find(current);
current = (it != _parentSections.end()) ? it->second : nullptr;
}
return parts.join(u" > "_q);
}
std::vector<SearchEntry> BuildHelper::index(
not_null<Main::Session*> session) const {
auto entries = std::vector<SearchEntry>();
@@ -208,17 +237,20 @@ Ui::RpWidget *SectionBuilder::addControl(ControlArgs &&args) {
.align = args.align,
.highlight = std::move(args.highlight),
};
}, [&] {
}, [&]() mutable {
return SearchEntry{
.id = std::move(args.id),
.title = ResolveTitle(std::move(args.title)),
.keywords = std::move(args.keywords),
.icon = std::move(args.searchIcon),
.checkIcon = args.searchCheckIcon,
};
});
}
Ui::SettingsButton *SectionBuilder::addButton(ButtonArgs &&args) {
const auto &st = args.st ? *args.st : st::settingsButton;
auto iconForSearch = IconDescriptor{ args.icon.icon };
const auto factory = [&](not_null<Ui::VerticalLayout*> container) {
auto button = CreateButtonWithIcon(
args.container ? args.container : container.get(),
@@ -235,15 +267,20 @@ Ui::SettingsButton *SectionBuilder::addButton(ButtonArgs &&args) {
st,
rpl::duplicate(args.title));
}
if (args.toggled) {
button->toggleOn(std::move(args.toggled));
}
return std::move(button);
};
return static_cast<Ui::SettingsButton*>(addControl({
.factory = factory,
.id = std::move(args.id),
.title = rpl::duplicate(args.title),
.keywords = std::move(args.keywords),
.highlight = std::move(args.highlight),
.shown = std::move(args.shown),
.keywords = std::move(args.keywords),
.searchIcon = std::move(iconForSearch),
}));
}
@@ -297,7 +334,7 @@ Ui::SettingsButton *SectionBuilder::addPremiumButton(PremiumButtonArgs &&args) {
.keywords = std::move(args.keywords),
});
if (result) {
[[maybe_unused]] const auto decorated = AddPremiumStar(
AddPremiumStar(
result,
args.credits,
v::get<WidgetContext>(_context).isPaused);
@@ -306,120 +343,65 @@ Ui::SettingsButton *SectionBuilder::addPremiumButton(PremiumButtonArgs &&args) {
}
Ui::SettingsButton *SectionBuilder::addPrivacyButton(PrivacyButtonArgs &&args) {
const auto id = args.id;
const auto premium = args.premium;
auto title = std::move(args.title);
return v::match(_context, [&](const WidgetContext &ctx)
-> Ui::SettingsButton* {
const auto button = AddPrivacyButton(
ctx.controller,
ctx.container,
rpl::duplicate(title),
{},
args.key,
std::move(args.controllerFactory));
const auto controller = this->controller();
const auto session = this->session();
const auto button = addButton({
.id = args.id,
.title = rpl::duplicate(args.title),
.st = &st::settingsButtonNoIcon,
.label = PrivacyButtonLabel(session, args.key),
.keywords = args.keywords,
});
if (button) {
const auto id = args.id;
const auto premium = args.premium;
const auto shower = Ui::CreateChild<rpl::lifetime>(button);
const auto factory = args.controllerFactory;
button->addClickHandler([=, key = args.key] {
*shower = session->api().userPrivacy().value(
key
) | rpl::take(
1
) | rpl::on_next(crl::guard(controller, [=](
const Api::UserPrivacy::Rule &value) {
controller->show(Box<EditPrivacyBox>(
controller,
factory(),
value));
}));
});
if (premium) {
AddPrivacyPremiumStar(
button,
&ctx.controller->session(),
rpl::duplicate(title),
session,
std::move(args.title),
st::settingsButtonNoIcon.padding);
}
if (!id.isEmpty()) {
registerHighlight(id, button, {});
}
return button;
}, [&](const SearchContext &ctx) -> Ui::SettingsButton* {
if (!args.id.isEmpty()) {
ctx.entries->push_back({
.id = std::move(args.id),
.title = ResolveTitle(std::move(title)),
.keywords = std::move(args.keywords),
});
}
return nullptr;
});
}
Ui::SettingsButton *SectionBuilder::addToggle(ToggleArgs &&args) {
auto highlight = std::move(args.highlight);
const auto id = args.id;
const auto button = v::match(_context, [&](const WidgetContext &ctx)
-> Ui::SettingsButton* {
const auto &st = args.st ? *args.st : st::settingsButton;
const auto target = args.container ? args.container : ctx.container.get();
const auto button = target->add(CreateButtonWithIcon(
target,
rpl::duplicate(args.title),
st,
std::move(args.icon)));
button->toggleOn(std::move(args.toggled));
return button;
}, [&](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 && !id.isEmpty()) {
registerHighlight(id, button, std::move(highlight));
}
return button;
}
Ui::Checkbox *SectionBuilder::addCheckbox(CheckboxArgs &&args) {
return v::match(_context, [&](const WidgetContext &ctx) -> Ui::Checkbox* {
const auto checkbox = ctx.container->add(
object_ptr<Ui::Checkbox>(
ctx.container,
ResolveTitle(rpl::duplicate(args.title)),
args.checked,
st::settingsCheckbox),
st::settingsCheckboxPadding);
return checkbox;
}, [&](const SearchContext &ctx) -> Ui::Checkbox* {
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;
});
}
Ui::SlideWrap<Ui::Checkbox> *SectionBuilder::addSlideCheckbox(
SlideCheckboxArgs &&args) {
return v::match(_context, [&](const WidgetContext &ctx)
-> Ui::SlideWrap<Ui::Checkbox>* {
const auto wrap = ctx.container->add(
object_ptr<Ui::SlideWrap<Ui::Checkbox>>(
ctx.container,
object_ptr<Ui::Checkbox>(
ctx.container,
ResolveTitle(rpl::duplicate(args.title)),
args.checked,
st::settingsCheckbox),
st::settingsCheckboxPadding));
if (args.shown) {
wrap->toggleOn(std::move(args.shown));
}
return wrap;
}, [&](const SearchContext &ctx) -> Ui::SlideWrap<Ui::Checkbox>* {
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;
});
const auto factory = [&](not_null<Ui::VerticalLayout*> container) {
return object_ptr<Ui::Checkbox>(
container,
ResolveTitle(rpl::duplicate(args.title)),
args.checked,
st::settingsCheckbox);
};
return static_cast<Ui::Checkbox*>(addControl({
.factory = factory,
.id = std::move(args.id),
.title = rpl::duplicate(args.title),
.margin = st::settingsCheckboxPadding,
.highlight = std::move(args.highlight),
.shown = std::move(args.shown),
.keywords = std::move(args.keywords),
.searchCheckIcon = (args.checked
? SearchEntryCheckIcon::Checked
: SearchEntryCheckIcon::Unchecked),
}));
}
void SectionBuilder::addSubsectionTitle(rpl::producer<QString> text) {
@@ -36,20 +36,36 @@ namespace style {
struct SettingsButton;
} // namespace style
namespace st {
extern const int &boxRadius;
} // namespace st
namespace Ui {
template <typename Widget>
class SlideWrap;
} // namespace Ui
namespace tr {
template <typename ...Tags>
struct phrase;
} // namespace tr
namespace Settings::Builder {
class SectionBuilder;
enum class SearchEntryCheckIcon {
None,
Checked,
Unchecked,
};
struct SearchEntry {
QString id;
QString title;
QStringList keywords;
Type section;
IconDescriptor icon;
SearchEntryCheckIcon checkIcon = SearchEntryCheckIcon::None;
explicit operator bool() const {
return !id.isEmpty();
@@ -70,15 +86,20 @@ public:
void add(
Type sectionId,
tr::phrase<> title,
Type parentSectionId,
SearchEntriesIndexer indexer);
[[nodiscard]] std::vector<SearchEntry> collectAll(
not_null<::Main::Session*> session) const;
[[nodiscard]] QString sectionTitle(Type sectionId) const;
[[nodiscard]] QString sectionPath(Type sectionId) const;
private:
std::vector<SearchIndexerEntry> _indexers;
base::flat_map<Type, Type> _parentSections;
base::flat_map<Type, tr::phrase<>> _sectionTitles;
};
@@ -86,6 +107,7 @@ class BuildHelper {
public:
BuildHelper(
Type sectionId,
tr::phrase<> sectionTitle,
FnMut<void(SectionBuilder&)> method,
Type parentSectionId = nullptr);
@@ -147,11 +169,14 @@ public:
Fn<object_ptr<Ui::RpWidget>(not_null<Ui::VerticalLayout*>)> factory;
QString id;
rpl::producer<QString> title;
QStringList keywords;
style::margins margin;
style::align align = style::al_left;
HighlightArgs highlight;
rpl::producer<bool> shown;
QStringList keywords;
IconDescriptor searchIcon;
SearchEntryCheckIcon searchCheckIcon = SearchEntryCheckIcon::None;
};
Ui::RpWidget *addControl(ControlArgs &&args);
@@ -162,6 +187,7 @@ public:
IconDescriptor icon;
Ui::VerticalLayout *container = nullptr;
rpl::producer<QString> label;
rpl::producer<bool> toggled;
Fn<void()> onClick;
QStringList keywords;
HighlightArgs highlight;
@@ -198,35 +224,16 @@ public:
};
Ui::SettingsButton *addPrivacyButton(PrivacyButtonArgs &&args);
struct ToggleArgs {
QString id;
rpl::producer<QString> title;
const style::SettingsButton *st = nullptr;
IconDescriptor icon;
Ui::VerticalLayout *container = nullptr;
rpl::producer<bool> toggled;
QStringList keywords;
HighlightArgs highlight;
};
Ui::SettingsButton *addToggle(ToggleArgs &&args);
struct CheckboxArgs {
QString id;
rpl::producer<QString> title;
bool checked = false;
QStringList keywords;
HighlightArgs highlight = { .radius = st::boxRadius };
rpl::producer<bool> shown;
};
Ui::Checkbox *addCheckbox(CheckboxArgs &&args);
struct SlideCheckboxArgs {
QString id;
rpl::producer<QString> title;
bool checked = false;
rpl::producer<bool> shown;
QStringList keywords;
};
Ui::SlideWrap<Ui::Checkbox> *addSlideCheckbox(SlideCheckboxArgs &&args);
void addSubsectionTitle(rpl::producer<QString> text);
void addDivider();
void addDividerText(rpl::producer<QString> text);
@@ -54,12 +54,16 @@ void BuildChatSectionContent(
SetupArchive(controller, container, showOther);
}
const auto kMeta = BuildHelper(Chat::Id(), [](SectionBuilder &builder) {
const auto controller = builder.controller();
const auto showOther = builder.showOther();
const auto highlights = builder.highlights();
BuildChatSectionContent(builder, controller, showOther, highlights);
}, Main::Id());
const auto kMeta = BuildHelper(
Chat::Id(),
tr::lng_settings_section_chat_settings,
[](SectionBuilder &builder) {
const auto controller = builder.controller();
const auto showOther = builder.showOther();
const auto highlights = builder.highlights();
BuildChatSectionContent(builder, controller, showOther, highlights);
},
Main::Id());
} // namespace
@@ -138,14 +138,13 @@ void BuildSectionButtons(SectionBuilder &builder) {
.keywords = { u"sessions"_q, u"calls"_q },
});
const auto window = controller ? &controller->window() : nullptr;
builder.addButton({
.id = u"main/power"_q,
.title = tr::lng_settings_power_menu(),
.icon = { &st::menuIconPowerUsage },
.onClick = window
? Fn<void()>([=] { window->show(Box(PowerSavingBox, PowerSaving::Flags())); })
: Fn<void()>(nullptr),
.onClick = [=] {
controller->show(Box(PowerSavingBox, PowerSaving::Flags()));
},
.keywords = { u"battery"_q, u"animations"_q, u"power"_q, u"saving"_q },
});
@@ -158,10 +157,10 @@ void BuildSectionButtons(SectionBuilder &builder) {
) | rpl::then(
Lang::GetInstance().idChanges()
) | rpl::map([] { return Lang::GetInstance().nativeName(); }),
.onClick = controller ? Fn<void()>([=] {
.onClick = [=] {
static auto Guard = base::binary_guard();
Guard = LanguageBox::Show(controller);
}) : Fn<void()>(nullptr),
},
.keywords = { u"translate"_q, u"localization"_q, u"language"_q },
});
}
@@ -205,10 +204,10 @@ void BuildPremiumSection(SectionBuilder &builder) {
builder.addPremiumButton({
.id = u"main/premium"_q,
.title = tr::lng_premium_summary_title(),
.onClick = controller ? Fn<void()>([=] {
.onClick = [=] {
controller->setPremiumRef("settings");
showOther(PremiumId());
}) : Fn<void()>(nullptr),
},
.keywords = { u"subscription"_q },
});
@@ -217,16 +216,16 @@ void BuildPremiumSection(SectionBuilder &builder) {
.id = u"main/credits"_q,
.title = tr::lng_settings_credits(),
.label = session->credits().balanceValue(
) | rpl::map([](CreditsAmount c) {
return c
? Lang::FormatCreditsAmountToShort(c).string
: QString();
}),
) | rpl::map([](CreditsAmount c) {
return c
? Lang::FormatCreditsAmountToShort(c).string
: QString();
}),
.credits = true,
.onClick = controller ? Fn<void()>([=] {
.onClick = [=] {
controller->setPremiumRef("settings");
showOther(CreditsId());
}) : Fn<void()>(nullptr),
},
.keywords = { u"stars"_q, u"balance"_q },
});
@@ -237,14 +236,12 @@ void BuildPremiumSection(SectionBuilder &builder) {
.icon = { &st::menuIconTon },
.label = session->credits().tonBalanceValue(
) | rpl::map([](CreditsAmount c) {
return c
? Lang::FormatCreditsAmountToShort(c).string
: QString();
return c ? Lang::FormatCreditsAmountToShort(c).string : u""_q;
}),
.onClick = controller ? Fn<void()>([=] {
.onClick = [=] {
controller->setPremiumRef("settings");
showOther(CurrencyId());
}) : Fn<void()>(nullptr),
},
.keywords = { u"ton"_q, u"crypto"_q, u"wallet"_q },
.shown = session->credits().tonBalanceValue(
) | rpl::map([](CreditsAmount c) { return !c.empty(); }),
@@ -254,9 +251,7 @@ void BuildPremiumSection(SectionBuilder &builder) {
.id = u"main/business"_q,
.title = tr::lng_business_title(),
.icon = { .icon = &st::menuIconShop },
.onClick = showOther
? Fn<void()>([=] { showOther(BusinessId()); })
: Fn<void()>(nullptr),
.onClick = [=] { showOther(BusinessId()); },
.keywords = { u"work"_q, u"company"_q },
});
@@ -265,9 +260,7 @@ void BuildPremiumSection(SectionBuilder &builder) {
.id = u"main/send-gift"_q,
.title = tr::lng_settings_gift_premium(),
.icon = { .icon = &st::menuIconGiftPremium, .newBadge = true },
.onClick = controller
? Fn<void()>([=] { Ui::ChooseStarGiftRecipient(controller); })
: Fn<void()>(nullptr),
.onClick = [=] { Ui::ChooseStarGiftRecipient(controller); },
.keywords = { u"present"_q, u"send"_q },
});
}
@@ -327,19 +320,22 @@ void BuildValidationSuggestions(SectionBuilder &builder) {
});
}
const auto kMeta = BuildHelper(Main::Id(), [](SectionBuilder &builder) {
builder.addDivider();
builder.addSkip();
const auto kMeta = BuildHelper(
Main::Id(),
tr::lng_menu_settings,
[](SectionBuilder &builder) {
builder.addDivider();
builder.addSkip();
BuildValidationSuggestions(builder);
BuildSectionButtons(builder);
BuildValidationSuggestions(builder);
BuildSectionButtons(builder);
builder.addSkip();
builder.addSkip();
BuildInterfaceScale(builder);
BuildPremiumSection(builder);
BuildHelpSection(builder);
});
BuildInterfaceScale(builder);
BuildPremiumSection(builder);
BuildHelpSection(builder);
});
} // namespace
@@ -63,7 +63,7 @@ void BuildMultiAccountSection(
const auto container = builder.container();
if (!container) {
builder.addToggle({
builder.addButton({
.id = u"notifications/multi_account"_q,
.title = tr::lng_settings_notify_all(),
.toggled = rpl::single(Core::App().settings().notifyFromAll()),
@@ -75,7 +75,7 @@ void BuildMultiAccountSection(
return;
}
const auto fromAll = builder.addToggle({
const auto fromAll = builder.addButton({
.id = u"notifications/accounts"_q,
.title = tr::lng_settings_notify_all(),
.st = &st::settingsButtonNoIcon,
@@ -120,7 +120,7 @@ void BuildGlobalNotificationsSection(
const auto desktopToggles = container
? container->lifetime().make_state<rpl::event_stream<bool>>()
: nullptr;
const auto desktop = builder.addToggle({
const auto desktop = builder.addButton({
.id = u"notifications/desktop"_q,
.title = tr::lng_settings_desktop_notify(),
.icon = { &st::menuIconNotifications },
@@ -133,7 +133,7 @@ void BuildGlobalNotificationsSection(
const auto flashbounceToggles = container
? container->lifetime().make_state<rpl::event_stream<bool>>()
: nullptr;
const auto flashbounce = builder.addToggle({
const auto flashbounce = builder.addButton({
.id = u"notifications/flash"_q,
.title = (Platform::IsWindows()
? tr::lng_settings_alert_windows
@@ -153,7 +153,7 @@ void BuildGlobalNotificationsSection(
const auto allowed = [=] {
return Core::App().settings().soundNotify();
};
const auto sound = builder.addToggle({
const auto sound = builder.addButton({
.id = u"notifications/sound"_q,
.title = tr::lng_settings_sound_allowed(),
.icon = { &st::menuIconUnmute },
@@ -366,7 +366,7 @@ void BuildEventNotificationsSection(
session->api().contactSignupSilentCurrent().value_or(false)
) | rpl::then(session->api().contactSignupSilent());
const auto joined = builder.addToggle({
const auto joined = builder.addButton({
.id = u"notifications/events/joined"_q,
.title = tr::lng_settings_events_joined(),
.icon = { &st::menuIconInvite },
@@ -383,7 +383,7 @@ void BuildEventNotificationsSection(
}, joined->lifetime());
}
const auto pinned = builder.addToggle({
const auto pinned = builder.addButton({
.id = u"notifications/events/pinned"_q,
.title = tr::lng_settings_events_pinned(),
.icon = { &st::menuIconPin },
@@ -419,7 +419,7 @@ void BuildCallNotificationsSection(
const auto authorizations = &session->api().authorizations();
authorizations->reload();
const auto acceptCalls = builder.addToggle({
const auto acceptCalls = builder.addButton({
.id = u"notifications/calls/accept"_q,
.title = tr::lng_settings_call_accept_calls(),
.icon = { &st::menuIconCallsReceive },
@@ -448,7 +448,7 @@ void BuildBadgeCounterSection(
const auto &settings = Core::App().settings();
const auto muted = builder.addToggle({
const auto muted = builder.addButton({
.id = u"notifications/include-muted-chats"_q,
.title = tr::lng_settings_include_muted(),
.st = &st::settingsButtonNoIcon,
@@ -458,7 +458,7 @@ void BuildBadgeCounterSection(
const auto hasFolders = controller
&& controller->session().data().chatsFilters().has();
const auto mutedFolders = hasFolders ? builder.addToggle({
const auto mutedFolders = hasFolders ? builder.addButton({
.id = u"notifications/badge/muted_folders"_q,
.title = tr::lng_settings_include_muted_folders(),
.st = &st::settingsButtonNoIcon,
@@ -466,7 +466,7 @@ void BuildBadgeCounterSection(
.keywords = { u"muted"_q, u"folders"_q },
}) : nullptr;
const auto count = builder.addToggle({
const auto count = builder.addButton({
.id = u"notifications/count-unread-messages"_q,
.title = tr::lng_settings_count_unread(),
.st = &st::settingsButtonNoIcon,
@@ -534,7 +534,7 @@ void BuildSystemIntegrationAndAdvancedSection(
}
const auto &settings = Core::App().settings();
const auto native = nativeText ? builder.addToggle({
const auto native = nativeText ? builder.addButton({
.id = u"notifications/native"_q,
.title = std::move(nativeText),
.st = &st::settingsButtonNoIcon,
@@ -721,10 +721,14 @@ void BuildNotificationsSectionContent(
BuildSystemIntegrationAndAdvancedSection(builder, controller);
}
const auto kMeta = BuildHelper(Notifications::Id(), [](SectionBuilder &builder) {
const auto controller = builder.controller();
BuildNotificationsSectionContent(builder, controller);
}, Main::Id());
const auto kMeta = BuildHelper(
Notifications::Id(),
tr::lng_settings_section_notify,
[](SectionBuilder &builder) {
const auto controller = builder.controller();
BuildNotificationsSectionContent(builder, controller);
},
Main::Id());
} // namespace
@@ -484,7 +484,7 @@ void BuildTopPeersSection(
builder.addSkip();
builder.addSubsectionTitle(tr::lng_settings_top_peers_title());
const auto toggle = builder.addToggle({
const auto toggle = builder.addButton({
.id = u"privacy/top_peers"_q,
.title = tr::lng_settings_top_peers_suggest(),
.st = &st::settingsButtonNoIcon,
@@ -569,11 +569,15 @@ void BuildPrivacySecuritySectionContent(
BuildSelfDestructionSection(builder, controller, trigger());
}
const auto kMeta = BuildHelper(PrivacySecurity::Id(), [](SectionBuilder &builder) {
const auto controller = builder.controller();
const auto showOther = builder.showOther();
BuildPrivacySecuritySectionContent(builder, controller, showOther);
}, Main::Id());
const auto kMeta = BuildHelper(
PrivacySecurity::Id(),
tr::lng_settings_section_privacy,
[](SectionBuilder &builder) {
const auto controller = builder.controller();
const auto showOther = builder.showOther();
BuildPrivacySecuritySectionContent(builder, controller, showOther);
},
Main::Id());
} // namespace
@@ -535,6 +535,16 @@ settingsNotificationTypeDetails: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg;
}
settingsSearchResult: SettingsButton(settingsButton) {
height: 40px;
padding: margins(60px, 4px, 22px, 4px);
}
settingsSearchResultNoIcon: SettingsButton(settingsSearchResult) {
padding: margins(22px, 4px, 22px, 4px);
}
settingsSearchResultDetails: FlatLabel(settingsNotificationTypeDetails) {
}
powerSavingButton: SettingsButton(settingsButton) {
style: boxTextStyle;
padding: margins(57px, 8px, 22px, 8px);
@@ -641,7 +641,7 @@ void AddLottieIconWithCircle(
}, circle->lifetime());
}
not_null<Button*> AddPremiumStar(
void AddPremiumStar(
not_null<Button*> button,
bool credits,
Fn<bool()> isPaused) {
@@ -716,8 +716,6 @@ not_null<Button*> AddPremiumStar(
ministarsContainer->resize(fullHeight, fullHeight);
ministars->setCenter(ministarsContainer->rect());
return button;
}
} // namespace Settings
@@ -291,7 +291,7 @@ void AddLottieIconWithCircle(
QMargins iconPadding,
QSize circleSize);
[[nodiscard]] not_null<Button*> AddPremiumStar(
void AddPremiumStar(
not_null<Button*> button,
bool credits,
Fn<bool()> isPaused);
@@ -277,6 +277,12 @@ rpl::producer<int> BlockedPeersCount(not_null<::Main::Session*> session) {
} // namespace
rpl::producer<QString> PrivacyButtonLabel(
not_null<::Main::Session*> session,
Privacy::Key key) {
return PrivacyString(session, key);
}
void AddPrivacyPremiumStar(
not_null<Ui::SettingsButton*> button,
not_null<Main::Session*> session,
@@ -40,6 +40,10 @@ not_null<Ui::SettingsButton*> AddPrivacyButton(
Fn<std::unique_ptr<EditPrivacyController>()> controllerFactory,
const style::SettingsButton *stOverride = nullptr);
[[nodiscard]] rpl::producer<QString> PrivacyButtonLabel(
not_null<::Main::Session*> session,
Api::UserPrivacy::Key key);
void AddPrivacyPremiumStar(
not_null<Ui::SettingsButton*> button,
not_null<::Main::Session*> session,
+159 -37
View File
@@ -10,9 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "settings/builder/settings_builder.h"
#include "settings/settings_common.h"
#include "ui/painter.h"
#include "ui/search_field_controller.h"
#include "ui/vertical_list.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/padding_wrap.h"
@@ -22,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_layers.h"
#include "styles/style_menu_icons.h"
#include "styles/style_settings.h"
#include "styles/style_widgets.h"
namespace Settings {
namespace {
@@ -75,6 +78,70 @@ struct SearchResult {
return results;
}
void SetupCheckIcon(
not_null<Ui::SettingsButton*> button,
Builder::SearchEntryCheckIcon checkIcon,
const style::SettingsButton &st) {
struct CheckWidget {
CheckWidget(QWidget *parent, bool checked)
: widget(parent)
, view(st::defaultCheck, checked) {
view.finishAnimating();
}
Ui::RpWidget widget;
Ui::CheckView view;
};
const auto checked = (checkIcon == Builder::SearchEntryCheckIcon::Checked);
const auto check = button->lifetime().make_state<CheckWidget>(
button,
checked);
check->widget.setAttribute(Qt::WA_TransparentForMouseEvents);
check->widget.resize(check->view.getSize());
check->widget.show();
button->sizeValue(
) | rpl::on_next([=, left = st.iconLeft](QSize size) {
check->widget.moveToLeft(
left,
(size.height() - check->widget.height()) / 2,
size.width());
}, check->widget.lifetime());
check->widget.paintRequest(
) | rpl::on_next([=] {
auto p = QPainter(&check->widget);
p.setOpacity(0.5);
check->view.paint(p, 0, 0, check->widget.width());
}, check->widget.lifetime());
}
[[nodiscard]] not_null<Ui::SettingsButton*> CreateSearchResultButton(
not_null<Ui::VerticalLayout*> container,
const QString &title,
const QString &subtitle,
const style::SettingsButton &st,
IconDescriptor &&icon,
Builder::SearchEntryCheckIcon checkIcon) {
const auto button = AddButtonWithIcon(
container,
rpl::single(title),
st,
std::move(icon));
if (checkIcon != Builder::SearchEntryCheckIcon::None) {
SetupCheckIcon(button, checkIcon, st);
}
const auto details = Ui::CreateChild<Ui::FlatLabel>(
button.get(),
subtitle,
st::settingsSearchResultDetails);
details->show();
details->moveToLeft(
st.padding.left(),
st.padding.top() + st.height - details->height());
details->setAttribute(Qt::WA_TransparentForMouseEvents);
return button;
}
} // namespace
Search::Search(
@@ -89,57 +156,87 @@ rpl::producer<QString> Search::title() {
}
void Search::setInnerFocus() {
_searchField->setFocus();
if (_searchField) {
_searchField->setFocus();
}
}
base::weak_qptr<Ui::RpWidget> Search::createPinnedToTop(
not_null<QWidget*> parent) {
_searchController = std::make_unique<Ui::SearchFieldController>("");
auto rowView = _searchController->createRowView(
parent,
st::infoLayerMediaSearch);
_searchField = rowView.field;
const auto searchContainer = Ui::CreateChild<Ui::FixedHeightWidget>(
parent.get(),
st::infoLayerMediaSearch.height);
const auto wrap = rowView.wrap.release();
wrap->setParent(searchContainer);
wrap->show();
searchContainer->widthValue(
) | rpl::on_next([=](int width) {
wrap->resizeToWidth(width);
wrap->moveToLeft(0, 0);
}, searchContainer->lifetime());
_searchController->queryChanges() | rpl::on_next([=](QString &&query) {
rebuildResults(std::move(query));
}, searchContainer->lifetime());
return base::make_weak(not_null<Ui::RpWidget*>{ searchContainer });
}
void Search::setupContent() {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
auto searchController = std::make_unique<Ui::SearchFieldController>("");
auto rowView = searchController->createRowView(
content,
st::infoLayerMediaSearch);
_searchField = rowView.field;
const auto searchContainer = content->add(
object_ptr<Ui::FixedHeightWidget>(
content,
st::infoLayerMediaSearch.height));
_searchWrap = std::move(rowView.wrap);
_searchWrap->setParent(searchContainer);
_searchWrap->show();
searchContainer->widthValue(
) | rpl::on_next([wrap = _searchWrap.get()](int width) {
wrap->resizeToWidth(width);
wrap->moveToLeft(0, 0);
}, searchContainer->lifetime());
_resultsContainer = content->add(
object_ptr<Ui::VerticalLayout>(content));
searchController->queryChanges() | rpl::on_next([=](QString &&query) {
rebuildResults(std::move(query));
}, content->lifetime());
_searchController = std::move(searchController);
setupCustomizations();
rebuildResults(QString());
Ui::ResizeFitChild(this, content);
}
void Search::setupCustomizations() {
const auto isPaused = Window::PausedIn(
controller(),
Window::GifPauseReason::Layer);
const auto add = [&](const QString &id, ResultCustomization value) {
_customizations[id] = std::move(value);
};
add(u"main/credits"_q, {
.hook = [=](not_null<Ui::SettingsButton*> b) {
AddPremiumStar(b, true, isPaused);
},
.st = &st::settingsSearchResult,
});
add(u"main/premium"_q, {
.hook = [=](not_null<Ui::SettingsButton*> b) {
AddPremiumStar(b, false, isPaused);
},
.st = &st::settingsSearchResult,
});
}
void Search::rebuildResults(const QString &query) {
while (_resultsContainer->count() > 0) {
delete _resultsContainer->widgetAt(0);
}
if (query.trimmed().isEmpty()) {
_resultsContainer->resizeToWidth(_resultsContainer->width());
return;
}
const auto entries = Builder::SearchRegistry::Instance().collectAll(
&controller()->session());
const auto results = FilterAndSort(entries, query);
const auto results = query.trimmed().isEmpty()
? ranges::views::all(entries)
| ranges::views::transform([](const auto &e) {
return SearchResult{ e, 0 };
})
| ranges::to<std::vector<SearchResult>>()
: FilterAndSort(entries, query);
if (results.empty()) {
_resultsContainer->add(
@@ -150,13 +247,38 @@ void Search::rebuildResults(const QString &query) {
st::defaultSubsectionTitlePadding);
} else {
const auto showOther = showOtherMethod();
const auto &registry = Builder::SearchRegistry::Instance();
for (const auto &result : results) {
const auto entry = result.entry;
const auto button = AddButtonWithIcon(
const auto &entry = result.entry;
const auto subtitle = registry.sectionPath(entry.section);
const auto hasIcon = entry.icon.icon != nullptr;
const auto hasCheckIcon = !hasIcon
&& (entry.checkIcon != Builder::SearchEntryCheckIcon::None);
const auto it = _customizations.find(entry.id);
const auto custom = (it != _customizations.end())
? &it->second
: nullptr;
const auto &st = custom && custom->st
? *custom->st
: (hasIcon || hasCheckIcon)
? st::settingsSearchResult
: st::settingsSearchResultNoIcon;
const auto button = CreateSearchResultButton(
_resultsContainer,
rpl::single(entry.title),
st::settingsButton,
{ &st::menuIconInfo });
entry.title,
subtitle,
st,
IconDescriptor{ entry.icon.icon },
hasCheckIcon ? entry.checkIcon : Builder::SearchEntryCheckIcon::None);
if (custom && custom->hook) {
custom->hook(button);
}
const auto targetSection = entry.section;
const auto controlId = entry.id;
button->addClickHandler([=] {
@@ -29,15 +29,23 @@ public:
[[nodiscard]] rpl::producer<QString> title() override;
void setInnerFocus() override;
[[nodiscard]] base::weak_qptr<Ui::RpWidget> createPinnedToTop(
not_null<QWidget*> parent) override;
private:
struct ResultCustomization {
Fn<void(not_null<Ui::SettingsButton*>)> hook;
const style::SettingsButton *st = nullptr;
};
void setupContent();
void setupCustomizations();
void rebuildResults(const QString &query);
std::unique_ptr<Ui::SearchFieldController> _searchController;
base::unique_qptr<Ui::RpWidget> _searchWrap;
Ui::InputField *_searchField = nullptr;
Ui::VerticalLayout *_resultsContainer = nullptr;
base::flat_map<QString, ResultCustomization> _customizations;
};