tdesktop/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp

384 lines
10 KiB
C++

/*
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 "ui/boxes/choose_language_box.h"
#include "lang/lang_keys.h"
#include "spellcheck/spellcheck_types.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/multi_select.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/painter.h"
#include "base/debug_log.h"
#include "styles/style_info.h"
#include "styles/style_layers.h"
#include "styles/style_settings.h"
namespace Ui {
namespace {
const auto kLanguageNamePrefix = "cloud_lng_language_";
const auto kTranslateToPrefix = "cloud_lng_translate_to_";
[[nodiscard]] std::vector<LanguageId> TranslationLanguagesList() {
// If adding some languages here you need to check that it is
// supported on the server. Right now server supports those:
//
// 'af', 'sq', 'am', 'ar', 'hy', 'az', 'eu', 'be', 'bn', 'bs', 'bg',
// 'ca', 'ceb', 'zh-CN', 'zh', 'zh-TW', 'co', 'hr', 'cs', 'da', 'nl',
// 'en', 'eo', 'et', 'fi', 'fr', 'fy', 'gl', 'ka', 'de', 'el', 'gu',
// 'ht', 'ha', 'haw', 'he', 'iw', 'hi', 'hmn', 'hu', 'is', 'ig', 'id',
// 'ga', 'it', 'ja', 'jv', 'kn', 'kk', 'km', 'rw', 'ko', 'ku', 'ky',
// 'lo', 'la', 'lv', 'lt', 'lb', 'mk', 'mg', 'ms', 'ml', 'mt', 'mi',
// 'mr', 'mn', 'my', 'ne', 'no', 'ny', 'or', 'ps', 'fa', 'pl', 'pt',
// 'pa', 'ro', 'ru', 'sm', 'gd', 'sr', 'st', 'sn', 'sd', 'si', 'sk',
// 'sl', 'so', 'es', 'su', 'sw', 'sv', 'tl', 'tg', 'ta', 'tt', 'te',
// 'th', 'tr', 'tk', 'uk', 'ur', 'ug', 'uz', 'vi', 'cy', 'xh', 'yi',
// 'yo', 'zu',
return {
{ QLocale::English },
{ QLocale::Arabic },
{ QLocale::Belarusian },
{ QLocale::Catalan },
{ QLocale::Chinese },
{ QLocale::Dutch },
{ QLocale::French },
{ QLocale::German },
{ QLocale::Indonesian },
{ QLocale::Italian },
{ QLocale::Japanese },
{ QLocale::Korean },
{ QLocale::Polish },
{ QLocale::Portuguese },
{ QLocale::Russian },
{ QLocale::Spanish },
{ QLocale::Ukrainian },
{ QLocale::Afrikaans },
{ QLocale::Albanian },
{ QLocale::Amharic },
{ QLocale::Armenian },
{ QLocale::Azerbaijani },
{ QLocale::Basque },
{ QLocale::Bosnian },
{ QLocale::Bulgarian },
{ QLocale::Burmese },
{ QLocale::Croatian },
{ QLocale::Czech },
{ QLocale::Danish },
{ QLocale::Esperanto },
{ QLocale::Estonian },
{ QLocale::Finnish },
{ QLocale::Gaelic },
{ QLocale::Galician },
{ QLocale::Georgian },
{ QLocale::Greek },
{ QLocale::Gusii },
{ QLocale::Hausa },
{ QLocale::Hebrew },
{ QLocale::Hungarian },
{ QLocale::Icelandic },
{ QLocale::Igbo },
{ QLocale::Irish },
{ QLocale::Kazakh },
{ QLocale::Kinyarwanda },
{ QLocale::Kurdish },
{ QLocale::Lao },
{ QLocale::Latvian },
{ QLocale::Lithuanian },
{ QLocale::Luxembourgish },
{ QLocale::Macedonian },
{ QLocale::Malagasy },
{ QLocale::Malay },
{ QLocale::Maltese },
{ QLocale::Maori },
{ QLocale::Mongolian },
{ QLocale::Nepali },
{ QLocale::Pashto },
{ QLocale::Persian },
{ QLocale::Romanian },
{ QLocale::Serbian },
{ QLocale::Shona },
{ QLocale::Sindhi },
{ QLocale::Sinhala },
{ QLocale::Slovak },
{ QLocale::Slovenian },
{ QLocale::Somali },
{ QLocale::Sundanese },
{ QLocale::Swahili },
{ QLocale::Swedish },
{ QLocale::Tajik },
{ QLocale::Tatar },
{ QLocale::Teso },
{ QLocale::Thai },
{ QLocale::Turkish },
{ QLocale::Turkmen },
{ QLocale::Urdu },
{ QLocale::Uzbek },
{ QLocale::Vietnamese },
{ QLocale::Welsh },
{ QLocale::WesternFrisian },
{ QLocale::Xhosa },
{ QLocale::Yiddish },
};
}
class Row final : public SettingsButton {
public:
Row(not_null<RpWidget*> parent, LanguageId id);
[[nodiscard]] bool filtered(const QString &query) const;
[[nodiscard]] LanguageId id() const;
int resizeGetHeight(int newWidth) override;
protected:
void paintEvent(QPaintEvent *e) override;
private:
const style::PeerListItem &_st;
const LanguageId _id;
const QString _status;
const QString _titleText;
Text::String _title;
};
Row::Row(not_null<RpWidget*> parent, LanguageId id)
: SettingsButton(parent, rpl::never<QString>())
, _st(st::inviteLinkListItem)
, _id(id)
, _status(LanguageName(id))
, _titleText(LanguageNameNative(id))
, _title(_st.nameStyle, _titleText) {
}
LanguageId Row::id() const {
return _id;
}
bool Row::filtered(const QString &query) const {
return _status.startsWith(query, Qt::CaseInsensitive)
|| _titleText.startsWith(query, Qt::CaseInsensitive);
}
int Row::resizeGetHeight(int newWidth) {
return _st.height;
}
void Row::paintEvent(QPaintEvent *e) {
auto p = Painter(this);
const auto paintOver = (isOver() || isDown()) && !isDisabled();
SettingsButton::paintBg(p, e->rect(), paintOver);
SettingsButton::paintRipple(p, 0, 0);
SettingsButton::paintToggle(p, width());
const auto &color = st::windowSubTextFg;
p.setPen(Qt::NoPen);
p.setBrush(color);
const auto left = st::settingsSubsectionTitlePadding.left();
const auto toggleRect = SettingsButton::maybeToggleRect();
const auto right = left
+ (toggleRect.isEmpty() ? 0 : (width() - toggleRect.x()));
const auto availableWidth = std::min(
_title.maxWidth(),
width() - left - right);
p.setPen(_st.nameFg);
_title.drawLeft(
p,
left,
_st.namePosition.y(),
availableWidth,
width() - left - right);
p.setPen(paintOver ? _st.statusFgOver : _st.statusFg);
p.setFont(st::contactsStatusFont);
p.drawTextLeft(
left,
_st.statusPosition.y(),
width() - left - right,
_status);
}
} // namespace
QString LanguageNameTranslated(const QString &twoLetterCode) {
return Lang::GetNonDefaultValue(
kLanguageNamePrefix + twoLetterCode.toUtf8());
}
QString LanguageNameLocal(LanguageId id) {
return QLocale::languageToString(id.language());
}
QString LanguageName(LanguageId id) {
const auto translated = LanguageNameTranslated(id.twoLetterCode());
return translated.isEmpty() ? LanguageNameLocal(id) : translated;
}
QString LanguageNameNative(LanguageId id) {
const auto locale = id.locale();
if (locale.language() == QLocale::English
&& (locale.country() == QLocale::UnitedStates
|| locale.country() == QLocale::AnyCountry)) {
return u"English"_q;
} else if (locale.language() == QLocale::Spanish) {
return QString::fromUtf8("\x45\x73\x70\x61\xc3\xb1\x6f\x6c");
} else {
const auto name = locale.nativeLanguageName();
return name.left(1).toUpper() + name.mid(1);
}
}
rpl::producer<QString> TranslateBarTo(LanguageId id) {
const auto translated = Lang::GetNonDefaultValue(
kTranslateToPrefix + id.twoLetterCode().toUtf8());
return (translated.isEmpty()
? tr::lng_translate_bar_to_other
: tr::lng_translate_bar_to)(
lt_name,
rpl::single(translated.isEmpty()
? LanguageNameLocal(id)
: translated));
}
QString TranslateMenuDont(tr::now_t, LanguageId id) {
const auto translated = Lang::GetNonDefaultValue(
kTranslateToPrefix + id.twoLetterCode().toUtf8());
return (translated.isEmpty()
? tr::lng_translate_menu_dont_other
: tr::lng_translate_menu_dont)(
tr::now,
lt_name,
translated.isEmpty() ? LanguageNameLocal(id) : translated);
}
void ChooseLanguageBox(
not_null<GenericBox*> box,
rpl::producer<QString> title,
Fn<void(std::vector<LanguageId>)> callback,
std::vector<LanguageId> selected,
bool multiselect,
Fn<bool(LanguageId)> toggleCheck) {
box->setMinHeight(st::boxWidth);
box->setMaxHeight(st::boxWidth);
box->setTitle(std::move(title));
const auto multiSelect = box->setPinnedToTopContent(
object_ptr<MultiSelect>(
box,
st::defaultMultiSelect,
tr::lng_participant_filter()));
box->setFocusCallback([=] { multiSelect->setInnerFocus(); });
const auto container = box->verticalLayout();
const auto langs = [&] {
auto list = TranslationLanguagesList();
for (const auto id : list) {
LOG(("cloud_lng_language_%1").arg(id.twoLetterCode()));
}
const auto current = LanguageId{ QLocale(
Lang::LanguageIdOrDefault(Lang::Id())).language() };
if (const auto i = ranges::find(list, current); i != end(list)) {
base::reorder(list, std::distance(begin(list), i), 0);
}
ranges::stable_partition(list, [&](LanguageId id) {
return ranges::contains(selected, id);
});
return list;
}();
struct ToggleOne {
LanguageId id;
bool selected = false;
};
struct State {
rpl::event_stream<ToggleOne> toggles;
};
const auto state = box->lifetime().make_state<State>();
auto rows = std::vector<not_null<SlideWrap<Row>*>>();
rows.reserve(langs.size());
for (const auto &id : langs) {
const auto button = container->add(
object_ptr<SlideWrap<Row>>(
container,
object_ptr<Row>(container, id)));
if (multiselect) {
button->entity()->toggleOn(rpl::single(
ranges::contains(selected, id)
) | rpl::then(state->toggles.events(
) | rpl::filter([=](ToggleOne one) {
return one.id == id;
}) | rpl::map([=](ToggleOne one) {
return one.selected;
})));
button->entity()->toggledChanges(
) | rpl::start_with_next([=](bool value) {
if (toggleCheck && !toggleCheck(id)) {
state->toggles.fire({ .id = id, .selected = !value });
}
}, button->lifetime());
} else {
button->entity()->setClickedCallback([=] {
callback({ id });
box->closeBox();
});
}
rows.push_back(button);
}
multiSelect->setQueryChangedCallback([=](const QString &query) {
for (const auto &row : rows) {
const auto toggled = row->entity()->filtered(query);
if (toggled != row->toggled()) {
row->toggle(toggled, anim::type::instant);
}
}
});
{
const auto label = CreateChild<FlatLabel>(
box.get(),
tr::lng_languages_none(),
st::membersAbout);
box->verticalLayout()->geometryValue(
) | rpl::start_with_next([=](const QRect &geometry) {
const auto shown = (geometry.height() <= 0);
label->setVisible(shown);
if (shown) {
label->moveToLeft(
(geometry.width() - label->width()) / 2,
geometry.y() + st::membersAbout.style.font->height * 4);
label->stackUnder(box->verticalLayout());
}
}, label->lifetime());
}
if (multiselect) {
box->addButton(tr::lng_settings_save(), [=] {
auto result = ranges::views::all(
rows
) | ranges::views::filter([](const auto &row) {
return row->entity()->toggled();
}) | ranges::views::transform([](const auto &row) {
return row->entity()->id();
}) | ranges::to_vector;
if (!result.empty()) {
callback(std::move(result));
}
box->closeBox();
});
}
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
} // namespace Ui