mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Add translation bar dropdown menu.
This commit is contained in:
parent
1689c1a597
commit
f5be551ff8
27 changed files with 700 additions and 384 deletions
|
@ -12,9 +12,7 @@ add_subdirectory(lib_crl)
|
|||
add_subdirectory(lib_base)
|
||||
add_subdirectory(lib_ui)
|
||||
add_subdirectory(lib_tl)
|
||||
if (NOT DESKTOP_APP_DISABLE_SPELLCHECK)
|
||||
add_subdirectory(lib_spellcheck)
|
||||
endif()
|
||||
add_subdirectory(lib_spellcheck)
|
||||
add_subdirectory(lib_storage)
|
||||
add_subdirectory(lib_lottie)
|
||||
add_subdirectory(lib_qr)
|
||||
|
@ -72,6 +70,7 @@ PRIVATE
|
|||
desktop-app::lib_crl
|
||||
desktop-app::lib_ui
|
||||
desktop-app::lib_tl
|
||||
desktop-app::lib_spellcheck
|
||||
desktop-app::lib_storage
|
||||
desktop-app::lib_lottie
|
||||
desktop-app::lib_qr
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "boxes/language_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/boxes/choose_language_box.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
|
@ -44,16 +45,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] std::vector<QLocale> SkipLocalesFromSettings() {
|
||||
const auto list = Core::App().settings().skipTranslationLanguages();
|
||||
return list
|
||||
| ranges::views::transform(&LanguageId::locale)
|
||||
| ranges::to_vector;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
using Language = Lang::Language;
|
||||
using Languages = Lang::CloudManager::Languages;
|
||||
|
@ -1128,8 +1119,7 @@ void LanguageBox::prepare() {
|
|||
Core::App().saveSettingsDelayed();
|
||||
}, translateEnabled->lifetime());
|
||||
|
||||
using Locales = std::vector<QLocale>;
|
||||
const auto label = lifetime().make_state<rpl::event_stream<Locales>>();
|
||||
using Languages = std::vector<LanguageId>;
|
||||
const auto translateSkipWrap = topContainer->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
topContainer,
|
||||
|
@ -1141,28 +1131,16 @@ void LanguageBox::prepare() {
|
|||
const auto translateSkip = Settings::AddButtonWithLabel(
|
||||
translateSkipWrap->entity(),
|
||||
tr::lng_translate_settings_choose(),
|
||||
label->events(
|
||||
) | rpl::map([](const Locales &locales) {
|
||||
return (locales.size() > 1)
|
||||
? tr::lng_languages_count(tr::now, lt_count, locales.size())
|
||||
: Ui::LanguageName(locales.front());
|
||||
Core::App().settings().skipTranslationLanguagesValue(
|
||||
) | rpl::map([](const Languages &list) {
|
||||
return (list.size() > 1)
|
||||
? tr::lng_languages_count(tr::now, lt_count, list.size())
|
||||
: Ui::LanguageName(list.front());
|
||||
}),
|
||||
st::settingsButtonNoIcon);
|
||||
|
||||
label->fire(SkipLocalesFromSettings());
|
||||
translateSkip->setClickedCallback([=] {
|
||||
Ui::BoxShow(this).showBox(
|
||||
Box(Ui::ChooseLanguageBox, [=](Locales locales) {
|
||||
label->fire_copy(locales);
|
||||
using namespace ranges::views;
|
||||
Core::App().settings().setSkipTranslationLanguages(all(
|
||||
locales
|
||||
) | transform([](const QLocale &l) {
|
||||
return LanguageId{ l.language() };
|
||||
}) | ranges::to_vector);
|
||||
Core::App().saveSettingsDelayed();
|
||||
}, SkipLocalesFromSettings()),
|
||||
Ui::LayerOption::KeepOther);
|
||||
Ui::BoxShow(this).showBox(Ui::EditSkipTranslationLanguages());
|
||||
});
|
||||
Settings::AddSkip(topContainer);
|
||||
Settings::AddDividerText(
|
||||
|
|
|
@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "boxes/abstract_box.h"
|
||||
#include "base/binary_guard.h"
|
||||
|
||||
struct LanguageId;
|
||||
|
||||
namespace Ui {
|
||||
class MultiSelect;
|
||||
struct ScrollToRequest;
|
||||
|
|
|
@ -114,6 +114,8 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
|
|||
return tr::lng_premium_summary_subtitle_profile_badge();
|
||||
case PremiumPreview::AnimatedUserpics:
|
||||
return tr::lng_premium_summary_subtitle_animated_userpics();
|
||||
case PremiumPreview::RealTimeTranslation:
|
||||
return tr::lng_premium_summary_subtitle_translation();
|
||||
}
|
||||
Unexpected("PremiumPreview in SectionTitle.");
|
||||
}
|
||||
|
@ -142,6 +144,8 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
|
|||
return tr::lng_premium_summary_about_profile_badge();
|
||||
case PremiumPreview::AnimatedUserpics:
|
||||
return tr::lng_premium_summary_about_animated_userpics();
|
||||
case PremiumPreview::RealTimeTranslation:
|
||||
return tr::lng_premium_summary_about_translation();
|
||||
}
|
||||
Unexpected("PremiumPreview in SectionTitle.");
|
||||
}
|
||||
|
@ -453,6 +457,7 @@ struct VideoPreviewDocument {
|
|||
case PremiumPreview::InfiniteReactions: return "infinite_reactions";
|
||||
case PremiumPreview::ProfileBadge: return "profile_badge";
|
||||
case PremiumPreview::AnimatedUserpics: return "animated_userpics";
|
||||
case PremiumPreview::RealTimeTranslation: return "translations";
|
||||
}
|
||||
return "";
|
||||
}();
|
||||
|
|
|
@ -49,6 +49,7 @@ enum class PremiumPreview {
|
|||
AdvancedChatManagement,
|
||||
ProfileBadge,
|
||||
AnimatedUserpics,
|
||||
RealTimeTranslation,
|
||||
|
||||
kCount,
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "mtproto/sender.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "spellcheck/platform/platform_language.h"
|
||||
#include "ui/boxes/choose_language_box.h"
|
||||
#include "ui/effects/loading_element.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/painter.h"
|
||||
|
@ -36,171 +37,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] std::vector<QLocale::Language> Languages() {
|
||||
return std::vector<QLocale::Language>{
|
||||
QLocale::English,
|
||||
QLocale::Afrikaans,
|
||||
QLocale::Albanian,
|
||||
QLocale::Amharic,
|
||||
QLocale::Arabic,
|
||||
QLocale::Armenian,
|
||||
QLocale::Azerbaijani,
|
||||
QLocale::Basque,
|
||||
QLocale::Belarusian,
|
||||
QLocale::Bosnian,
|
||||
QLocale::Bulgarian,
|
||||
QLocale::Burmese,
|
||||
QLocale::Catalan,
|
||||
QLocale::Chinese,
|
||||
QLocale::Croatian,
|
||||
QLocale::Czech,
|
||||
QLocale::Danish,
|
||||
QLocale::Dutch,
|
||||
QLocale::Esperanto,
|
||||
QLocale::Estonian,
|
||||
QLocale::Finnish,
|
||||
QLocale::French,
|
||||
QLocale::Gaelic,
|
||||
QLocale::Galician,
|
||||
QLocale::Georgian,
|
||||
QLocale::German,
|
||||
QLocale::Greek,
|
||||
QLocale::Gusii,
|
||||
QLocale::Hausa,
|
||||
QLocale::Hebrew,
|
||||
QLocale::Hungarian,
|
||||
QLocale::Icelandic,
|
||||
QLocale::Igbo,
|
||||
QLocale::Indonesian,
|
||||
QLocale::Irish,
|
||||
QLocale::Italian,
|
||||
QLocale::Japanese,
|
||||
QLocale::Kazakh,
|
||||
QLocale::Kinyarwanda,
|
||||
QLocale::Korean,
|
||||
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::Polish,
|
||||
QLocale::Portuguese,
|
||||
QLocale::Romanian,
|
||||
QLocale::Russian,
|
||||
QLocale::Serbian,
|
||||
QLocale::Shona,
|
||||
QLocale::Sindhi,
|
||||
QLocale::Sinhala,
|
||||
QLocale::Slovak,
|
||||
QLocale::Slovenian,
|
||||
QLocale::Somali,
|
||||
QLocale::Spanish,
|
||||
QLocale::Sundanese,
|
||||
QLocale::Swahili,
|
||||
QLocale::Swedish,
|
||||
QLocale::Tajik,
|
||||
QLocale::Tatar,
|
||||
QLocale::Teso,
|
||||
QLocale::Thai,
|
||||
QLocale::Turkish,
|
||||
QLocale::Turkmen,
|
||||
QLocale::Ukrainian,
|
||||
QLocale::Urdu,
|
||||
QLocale::Uzbek,
|
||||
QLocale::Vietnamese,
|
||||
QLocale::Welsh,
|
||||
QLocale::WesternFrisian,
|
||||
QLocale::Xhosa,
|
||||
QLocale::Yiddish,
|
||||
};
|
||||
}
|
||||
|
||||
class Row final : public Ui::SettingsButton {
|
||||
public:
|
||||
Row(not_null<Ui::RpWidget*> parent, const QLocale &locale);
|
||||
|
||||
[[nodiscard]] bool filtered(const QString &query) const;
|
||||
[[nodiscard]] QLocale locale() const;
|
||||
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
const style::PeerListItem &_st;
|
||||
const QLocale _locale;
|
||||
const QString _status;
|
||||
const QString _titleText;
|
||||
Ui::Text::String _title;
|
||||
|
||||
};
|
||||
|
||||
Row::Row(not_null<Ui::RpWidget*> parent, const QLocale &locale)
|
||||
: SettingsButton(parent, rpl::never<QString>())
|
||||
, _st(st::inviteLinkListItem)
|
||||
, _locale(locale)
|
||||
, _status(QLocale::languageToString(locale.language()))
|
||||
, _titleText(LanguageName(locale))
|
||||
, _title(_st.nameStyle, _titleText) {
|
||||
}
|
||||
|
||||
QLocale Row::locale() const {
|
||||
return _locale;
|
||||
}
|
||||
|
||||
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();
|
||||
Ui::SettingsButton::paintBg(p, e->rect(), paintOver);
|
||||
Ui::SettingsButton::paintRipple(p, 0, 0);
|
||||
Ui::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 = Ui::SettingsButton::maybeToggleRect();
|
||||
const auto right = left
|
||||
+ (toggleRect.isEmpty() ? 0 : (width() - toggleRect.x()));
|
||||
|
||||
p.setPen(_st.nameFg);
|
||||
_title.drawLeft(
|
||||
p,
|
||||
left,
|
||||
_st.namePosition.y(),
|
||||
width() - left - right,
|
||||
width() - left - right);
|
||||
|
||||
p.setPen(paintOver ? _st.statusFgOver : _st.statusFg);
|
||||
p.setFont(st::contactsStatusFont);
|
||||
p.drawTextLeft(
|
||||
left,
|
||||
_st.statusPosition.y(),
|
||||
width() - left - right,
|
||||
_status);
|
||||
}
|
||||
|
||||
class ShowButton final : public RpWidget {
|
||||
public:
|
||||
ShowButton(not_null<Ui::RpWidget*> parent);
|
||||
|
@ -250,19 +86,6 @@ rpl::producer<Qt::MouseButton> ShowButton::clicks() const {
|
|||
|
||||
} // namespace
|
||||
|
||||
QString LanguageName(const QLocale &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);
|
||||
}
|
||||
}
|
||||
|
||||
void TranslateBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<PeerData*> peer,
|
||||
|
@ -272,14 +95,10 @@ void TranslateBox(
|
|||
box->setWidth(st::boxWideWidth);
|
||||
box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); });
|
||||
const auto container = box->verticalLayout();
|
||||
const auto translateTo = Core::App().settings().translateTo().locale();
|
||||
|
||||
auto id = Core::App().settings().translateToValue();
|
||||
const auto api = box->lifetime().make_state<MTP::Sender>(
|
||||
&peer->session().mtp());
|
||||
struct State {
|
||||
rpl::event_stream<QLocale> locale;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
|
||||
text.entities = ranges::views::all(
|
||||
text.entities
|
||||
|
@ -355,11 +174,10 @@ void TranslateBox(
|
|||
const auto padding = st::settingsSubsectionTitlePadding;
|
||||
const auto subtitle = Settings::AddSubsectionTitle(
|
||||
container,
|
||||
state->locale.events() | rpl::map(LanguageName));
|
||||
rpl::duplicate(id) | rpl::map(LanguageName));
|
||||
|
||||
// Workaround.
|
||||
state->locale.events(
|
||||
) | rpl::start_with_next([=] {
|
||||
rpl::duplicate(id) | rpl::start_with_next([=] {
|
||||
subtitle->resizeToWidth(container->width()
|
||||
- padding.left()
|
||||
- padding.right());
|
||||
|
@ -370,7 +188,6 @@ void TranslateBox(
|
|||
box,
|
||||
object_ptr<FlatLabel>(box, stLabel)));
|
||||
translated->entity()->setSelectable(!hasCopyRestriction);
|
||||
translated->hide(anim::type::instant);
|
||||
|
||||
constexpr auto kMaxLines = 3;
|
||||
container->resizeToWidth(box->width());
|
||||
|
@ -380,10 +197,9 @@ void TranslateBox(
|
|||
box,
|
||||
st::aboutLabel,
|
||||
std::min(original->entity()->height() / lineHeight, kMaxLines),
|
||||
state->locale.events() | rpl::map([=](const QLocale &locale) {
|
||||
return locale.textDirection() == Qt::RightToLeft;
|
||||
rpl::duplicate(id) | rpl::map([=](LanguageId id) {
|
||||
return id.locale().textDirection() == Qt::RightToLeft;
|
||||
}))));
|
||||
loading->show(anim::type::instant);
|
||||
|
||||
const auto showText = [=](const QString &text) {
|
||||
translated->entity()->setText(text);
|
||||
|
@ -391,7 +207,9 @@ void TranslateBox(
|
|||
loading->hide(anim::type::instant);
|
||||
};
|
||||
|
||||
const auto send = [=](const QString &toLang) {
|
||||
const auto send = [=](LanguageId to) {
|
||||
loading->show(anim::type::instant);
|
||||
translated->hide(anim::type::instant);
|
||||
api->request(MTPmessages_TranslateText(
|
||||
MTP_flags(flags),
|
||||
msgId ? peer->input : MTP_inputPeerEmpty(),
|
||||
|
@ -403,7 +221,7 @@ void TranslateBox(
|
|||
: MTP_vector<MTPTextWithEntities>(1, MTP_textWithEntities(
|
||||
MTP_string(text.text),
|
||||
MTP_vector<MTPMessageEntity>()))),
|
||||
MTP_string(toLang.mid(0, 2))
|
||||
MTP_string(to.locale().name().mid(0, 2))
|
||||
)).done([=](const MTPmessages_TranslatedText &result) {
|
||||
const auto &data = result.data();
|
||||
const auto &list = data.vresult().v;
|
||||
|
@ -414,117 +232,16 @@ void TranslateBox(
|
|||
showText(tr::lng_translate_box_error(tr::now));
|
||||
}).send();
|
||||
};
|
||||
send(translateTo.name());
|
||||
state->locale.fire_copy(translateTo);
|
||||
std::move(id) | rpl::start_with_next(send, box->lifetime());
|
||||
|
||||
box->addLeftButton(tr::lng_settings_language(), [=] {
|
||||
if (loading->toggled()) {
|
||||
return;
|
||||
}
|
||||
Ui::BoxShow(box).showBox(Box(ChooseLanguageBox, [=](
|
||||
std::vector<QLocale> locales) {
|
||||
const auto &locale = locales.front();
|
||||
send(locale.name());
|
||||
state->locale.fire_copy(locale);
|
||||
loading->show(anim::type::instant);
|
||||
translated->hide(anim::type::instant);
|
||||
}, std::vector<QLocale>()));
|
||||
Ui::BoxShow(box).showBox(ChooseTranslateToBox());
|
||||
});
|
||||
}
|
||||
|
||||
void ChooseLanguageBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
Fn<void(std::vector<QLocale>)> callback,
|
||||
std::vector<QLocale> toggled) {
|
||||
box->setMinHeight(st::boxWidth);
|
||||
box->setMaxHeight(st::boxWidth);
|
||||
box->setTitle(tr::lng_languages());
|
||||
|
||||
const auto hasToggled = !toggled.empty();
|
||||
|
||||
const auto multiSelect = box->setPinnedToTopContent(
|
||||
object_ptr<Ui::MultiSelect>(
|
||||
box,
|
||||
st::defaultMultiSelect,
|
||||
tr::lng_participant_filter()));
|
||||
box->setFocusCallback([=] { multiSelect->setInnerFocus(); });
|
||||
|
||||
const auto container = box->verticalLayout();
|
||||
const auto langs = [&] {
|
||||
auto langs = Languages();
|
||||
const auto current = QLocale(
|
||||
Lang::LanguageIdOrDefault(Lang::Id())).language();
|
||||
if (const auto it = ranges::find(langs, current); it != end(langs)) {
|
||||
base::reorder(langs, std::distance(begin(langs), it), 0);
|
||||
}
|
||||
return langs;
|
||||
}();
|
||||
auto rows = std::vector<not_null<Ui::SlideWrap<Row>*>>();
|
||||
rows.reserve(langs.size());
|
||||
for (const auto &lang : langs) {
|
||||
const auto locale = QLocale(lang);
|
||||
const auto button = container->add(
|
||||
object_ptr<Ui::SlideWrap<Row>>(
|
||||
container,
|
||||
object_ptr<Row>(container, locale)));
|
||||
if (hasToggled) {
|
||||
button->entity()->toggleOn(
|
||||
rpl::single(ranges::contains(toggled, locale)),
|
||||
false);
|
||||
} else {
|
||||
button->entity()->setClickedCallback([=] {
|
||||
callback({ locale });
|
||||
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 = Ui::CreateChild<Ui::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 (hasToggled) {
|
||||
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()->locale();
|
||||
}) | ranges::to_vector;
|
||||
if (!result.empty()) {
|
||||
callback(std::move(result));
|
||||
}
|
||||
box->closeBox();
|
||||
});
|
||||
}
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}
|
||||
|
||||
bool SkipTranslate(TextWithEntities textWithEntities) {
|
||||
const auto &text = textWithEntities.text;
|
||||
if (text.isEmpty()) {
|
||||
|
@ -557,4 +274,24 @@ bool SkipTranslate(TextWithEntities textWithEntities) {
|
|||
#endif
|
||||
}
|
||||
|
||||
object_ptr<BoxContent> EditSkipTranslationLanguages() {
|
||||
auto title = tr::lng_translate_settings_choose();
|
||||
return Box(ChooseLanguageBox, std::move(title), [=](
|
||||
std::vector<LanguageId> &&list) {
|
||||
Core::App().settings().setSkipTranslationLanguages(
|
||||
std::move(list));
|
||||
Core::App().saveSettingsDelayed();
|
||||
}, Core::App().settings().skipTranslationLanguages());
|
||||
}
|
||||
|
||||
object_ptr<BoxContent> ChooseTranslateToBox() {
|
||||
return Box(ChooseLanguageBox, tr::lng_languages(), [=](
|
||||
const std::vector<LanguageId> &ids) {
|
||||
Expects(!ids.empty());
|
||||
|
||||
Core::App().settings().setTranslateTo(ids.front());
|
||||
Core::App().saveSettingsDelayed();
|
||||
}, std::nullopt);
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -7,16 +7,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/object_ptr.h"
|
||||
|
||||
class PeerData;
|
||||
struct LanguageId;
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class BoxContent;
|
||||
class GenericBox;
|
||||
|
||||
[[nodiscard]] QString LanguageName(const QLocale &locale);
|
||||
|
||||
void TranslateBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<GenericBox*> box,
|
||||
not_null<PeerData*> peer,
|
||||
MsgId msgId,
|
||||
TextWithEntities text,
|
||||
|
@ -24,9 +26,7 @@ void TranslateBox(
|
|||
|
||||
[[nodiscard]] bool SkipTranslate(TextWithEntities textWithEntities);
|
||||
|
||||
void ChooseLanguageBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
Fn<void(std::vector<QLocale>)> callback,
|
||||
std::vector<QLocale> toggled);
|
||||
[[nodiscard]] object_ptr<BoxContent> EditSkipTranslationLanguages();
|
||||
[[nodiscard]] object_ptr<BoxContent> ChooseTranslateToBox();
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
@ -1447,6 +1447,7 @@ void StickersListFooter::paintSetIconToCache(
|
|||
p.restore();
|
||||
} else {
|
||||
paintOne(0, [&] {
|
||||
const auto selected = (info.index == _iconState.selected);
|
||||
if (icon.setId == Data::Stickers::FeaturedSetId) {
|
||||
const auto &stickers = _session->data().stickers();
|
||||
return stickers.featuredSetsUnreadCount()
|
||||
|
@ -1457,9 +1458,9 @@ void StickersListFooter::paintSetIconToCache(
|
|||
} else if (icon.setId == AllEmojiSectionSetId()) {
|
||||
return &st::emojiPeople;
|
||||
} else if (const auto section = SetIdEmojiSection(icon.setId)) {
|
||||
return sectionIcon(*section, false);
|
||||
return sectionIcon(*section, selected);
|
||||
}
|
||||
return &st::emojiRecent;
|
||||
return sectionIcon(Section::Recent, selected);
|
||||
}());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,9 +78,9 @@ constexpr auto kPinnedMessageTextLimit = 16;
|
|||
|
||||
using ItemPreview = HistoryView::ItemPreview;
|
||||
|
||||
[[nodiscard]] bool IsOnlyEmojiAndSpaces(const QString &text) {
|
||||
[[nodiscard]] bool HasNotEmojiAndSpaces(const QString &text) {
|
||||
if (text.isEmpty()) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
auto emoji = 0;
|
||||
auto start = text.data();
|
||||
|
@ -91,10 +91,10 @@ using ItemPreview = HistoryView::ItemPreview;
|
|||
} else if (Ui::Emoji::Find(start, end, &emoji)) {
|
||||
start += emoji;
|
||||
} else {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -4682,7 +4682,7 @@ void HistoryItem::cacheOnlyEmojiAndSpaces(bool only) {
|
|||
bool HistoryItem::isOnlyEmojiAndSpaces() const {
|
||||
if (!(_flags & MessageFlag::OnlyEmojiAndSpacesSet)) {
|
||||
const_cast<HistoryItem*>(this)->cacheOnlyEmojiAndSpaces(
|
||||
IsOnlyEmojiAndSpaces(_text.text));
|
||||
!HasNotEmojiAndSpaces(_text.text));
|
||||
}
|
||||
return (_flags & MessageFlag::OnlyEmojiAndSpaces);
|
||||
}
|
||||
|
|
|
@ -6254,6 +6254,7 @@ void HistoryWidget::setupTranslateBar() {
|
|||
|
||||
_translateBar = std::make_unique<HistoryView::TranslateBar>(
|
||||
this,
|
||||
controller(),
|
||||
_history);
|
||||
|
||||
controller()->adaptive().oneColumnValue(
|
||||
|
|
|
@ -655,10 +655,12 @@ const Ui::Text::String &Element::text() const {
|
|||
OnlyEmojiAndSpaces Element::isOnlyEmojiAndSpaces() const {
|
||||
if (data()->Has<HistoryMessageTranslation>()) {
|
||||
return OnlyEmojiAndSpaces::No;
|
||||
} else if (!_text.isEmpty() || data()->originalText().empty()) {
|
||||
return _text.isOnlyEmojiAndSpaces()
|
||||
? OnlyEmojiAndSpaces::Yes
|
||||
: OnlyEmojiAndSpaces::No;
|
||||
} else if (!_text.isEmpty()) {
|
||||
return _text.hasNotEmojiAndSpaces()
|
||||
? OnlyEmojiAndSpaces::No
|
||||
: OnlyEmojiAndSpaces::Yes;
|
||||
} else if (data()->originalText().empty()) {
|
||||
return OnlyEmojiAndSpaces::Yes;
|
||||
} else {
|
||||
return OnlyEmojiAndSpaces::Unknown;
|
||||
}
|
||||
|
|
|
@ -7,26 +7,42 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "history/view/history_view_translate_bar.h"
|
||||
|
||||
#include "boxes/translate_box.h" // Ui::LanguageName.
|
||||
#include "boxes/translate_box.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "history/history.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "spellcheck/spellcheck_types.h"
|
||||
#include "ui/boxes/choose_language_box.h" // EditSkipTranslationLanguages.
|
||||
#include "ui/layers/box_content.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
||||
#include <QtGui/QtEvents>
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
constexpr auto kToastDuration = 4 * crl::time(1000);
|
||||
|
||||
} // namespace
|
||||
|
||||
TranslateBar::TranslateBar(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> history)
|
||||
: _wrap(parent, object_ptr<Ui::FlatButton>(
|
||||
: _controller(controller)
|
||||
, _history(history)
|
||||
, _wrap(parent, object_ptr<Ui::FlatButton>(
|
||||
parent,
|
||||
QString(),
|
||||
st::historyComposeButton))
|
||||
|
@ -95,6 +111,8 @@ void TranslateBar::setup(not_null<History*> history) {
|
|||
const auto settings = Ui::CreateChild<Ui::IconButton>(
|
||||
button,
|
||||
st::historyTranslateSettings);
|
||||
settings->setClickedCallback([=] { showMenu(createMenu(settings)); });
|
||||
|
||||
const auto updateLabelGeometry = [=] {
|
||||
const auto full = _wrap.width() - icon->width();
|
||||
const auto skip = st::semiboldFont->spacew * 2;
|
||||
|
@ -125,14 +143,23 @@ void TranslateBar::setup(not_null<History*> history) {
|
|||
history->session().changes().historyFlagsValue(
|
||||
history,
|
||||
(Data::HistoryUpdate::Flag::TranslatedTo
|
||||
| Data::HistoryUpdate::Flag::TranslateFrom))
|
||||
) | rpl::map([=](LanguageId to, const auto&) {
|
||||
return history->translatedTo()
|
||||
? u"Show Original"_q
|
||||
| Data::HistoryUpdate::Flag::TranslateFrom)),
|
||||
history->session().changes().peerFlagsValue(
|
||||
history->peer,
|
||||
Data::PeerUpdate::Flag::TranslationDisabled)
|
||||
) | rpl::map([=](LanguageId to, const auto&, const auto&) {
|
||||
using Flag = PeerData::TranslationFlag;
|
||||
return (history->peer->translationFlag() != Flag::Enabled)
|
||||
? rpl::single(QString())
|
||||
: history->translatedTo()
|
||||
? tr::lng_translate_show_original()
|
||||
: history->translateOfferedFrom()
|
||||
? u"Translate to "_q + Ui::LanguageName(to.locale())
|
||||
: QString();
|
||||
}) | rpl::distinct_until_changed(
|
||||
? tr::lng_translate_bar_to(
|
||||
lt_name,
|
||||
rpl::single(Ui::LanguageName(to)))
|
||||
: rpl::single(QString());
|
||||
}) | rpl::flatten_latest(
|
||||
) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](QString phrase) {
|
||||
_shouldBeShown = !phrase.isEmpty();
|
||||
if (_shouldBeShown) {
|
||||
|
@ -145,6 +172,181 @@ void TranslateBar::setup(not_null<History*> history) {
|
|||
}, lifetime());
|
||||
}
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> TranslateBar::createMenu(
|
||||
not_null<Ui::IconButton*> button) {
|
||||
if (_menu) {
|
||||
return nullptr;
|
||||
}
|
||||
auto result = base::make_unique_q<Ui::PopupMenu>(
|
||||
&_wrap,
|
||||
st::popupMenuExpandedSeparator);
|
||||
result->setDestroyedCallback([
|
||||
this,
|
||||
weak = Ui::MakeWeak(&_wrap),
|
||||
weakButton = Ui::MakeWeak(button),
|
||||
menu = result.get()
|
||||
] {
|
||||
if (weak && _menu == menu) {
|
||||
if (weakButton) {
|
||||
weakButton->setForceRippled(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
button->setForceRippled(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
void TranslateBar::showMenu(base::unique_qptr<Ui::PopupMenu> menu) {
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
_menu = std::move(menu);
|
||||
_menu->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight);
|
||||
|
||||
const auto weak = base::make_weak(_controller);
|
||||
_menu->addAction(tr::lng_translate_menu_to(tr::now), [=] {
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->show(Ui::ChooseTranslateToBox());
|
||||
}
|
||||
}, &st::menuIconTranslate);
|
||||
_menu->addSeparator();
|
||||
const auto history = _history;
|
||||
if (const auto translateOfferedFrom = _history->translateOfferedFrom()) {
|
||||
const auto name = Ui::LanguageName(translateOfferedFrom);
|
||||
const auto addToIgnoreList = [=] {
|
||||
showSettingsToast(history->peer, translateOfferedFrom);
|
||||
|
||||
history->peer->saveTranslationDisabled(true);
|
||||
|
||||
auto &settings = Core::App().settings();
|
||||
auto skip = settings.skipTranslationLanguages();
|
||||
if (!ranges::contains(skip, translateOfferedFrom)) {
|
||||
skip.push_back(translateOfferedFrom);
|
||||
}
|
||||
settings.setSkipTranslationLanguages(std::move(skip));
|
||||
Core::App().saveSettingsDelayed();
|
||||
};
|
||||
_menu->addAction(
|
||||
tr::lng_translate_menu_dont(tr::now, lt_name, name),
|
||||
addToIgnoreList,
|
||||
&st::menuIconBlock);
|
||||
}
|
||||
const auto hideBar = [=] {
|
||||
showHiddenToast(history->peer);
|
||||
|
||||
history->peer->saveTranslationDisabled(true);
|
||||
};
|
||||
_menu->addAction(
|
||||
tr::lng_translate_menu_hide(tr::now),
|
||||
hideBar,
|
||||
&st::menuIconCancel);
|
||||
_menu->popup(_wrap.mapToGlobal(
|
||||
QPoint(_wrap.width(), 0) + st::historyTranslateMenuPosition));
|
||||
}
|
||||
|
||||
void TranslateBar::showSettingsToast(
|
||||
not_null<PeerData*> peer,
|
||||
LanguageId ignored) {
|
||||
const auto weak = base::make_weak(_controller);
|
||||
const auto text = tr::lng_translate_dont_added(
|
||||
tr::now,
|
||||
lt_name,
|
||||
Ui::Text::Bold(Ui::LanguageName(ignored)),
|
||||
Ui::Text::WithEntities);
|
||||
showToast(text, tr::lng_translate_settings(tr::now), [=] {
|
||||
if (const auto strong = weak.get()) {
|
||||
const auto box = strong->show(
|
||||
Ui::EditSkipTranslationLanguages());
|
||||
if (box) {
|
||||
box->boxClosing() | rpl::start_with_next([=] {
|
||||
const auto in = ranges::contains(
|
||||
Core::App().settings().skipTranslationLanguages(),
|
||||
ignored);
|
||||
if (!in && weak) {
|
||||
peer->saveTranslationDisabled(false);
|
||||
}
|
||||
}, box->lifetime());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void TranslateBar::showHiddenToast(not_null<PeerData*> peer) {
|
||||
const auto &phrase = peer->isUser()
|
||||
? tr::lng_translate_hidden_user
|
||||
: peer->isBroadcast()
|
||||
? tr::lng_translate_hidden_channel
|
||||
: tr::lng_translate_hidden_group;
|
||||
const auto proj = Ui::Text::WithEntities;
|
||||
showToast(phrase(tr::now, proj), tr::lng_translate_undo(tr::now), [=] {
|
||||
peer->saveTranslationDisabled(false);
|
||||
});
|
||||
}
|
||||
|
||||
void TranslateBar::showToast(
|
||||
TextWithEntities text,
|
||||
const QString &buttonText,
|
||||
Fn<void()> buttonCallback) {
|
||||
const auto st = std::make_shared<style::Toast>(st::historyPremiumToast);
|
||||
const auto skip = st->padding.top();
|
||||
st->padding.setRight(st::historyPremiumViewSet.font->width(buttonText)
|
||||
- st::historyPremiumViewSet.width);
|
||||
|
||||
const auto weak = Ui::Toast::Show(_wrap.window(), Ui::Toast::Config{
|
||||
.text = std::move(text),
|
||||
.st = st.get(),
|
||||
.durationMs = kToastDuration,
|
||||
.multiline = true,
|
||||
.dark = true,
|
||||
.slideSide = RectPart::Bottom,
|
||||
});
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
strong->setInputUsed(true);
|
||||
const auto widget = strong->widget();
|
||||
widget->lifetime().add([st] {});
|
||||
const auto hideToast = [weak] {
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->hideAnimated();
|
||||
}
|
||||
};
|
||||
|
||||
const auto clickableBackground = Ui::CreateChild<Ui::AbstractButton>(
|
||||
widget.get());
|
||||
clickableBackground->setPointerCursor(false);
|
||||
clickableBackground->setAcceptBoth();
|
||||
clickableBackground->show();
|
||||
clickableBackground->addClickHandler([=](Qt::MouseButton button) {
|
||||
if (button == Qt::RightButton) {
|
||||
hideToast();
|
||||
}
|
||||
});
|
||||
|
||||
const auto button = Ui::CreateChild<Ui::RoundButton>(
|
||||
widget.get(),
|
||||
rpl::single(buttonText),
|
||||
st::historyPremiumViewSet);
|
||||
button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
||||
button->show();
|
||||
rpl::combine(
|
||||
widget->sizeValue(),
|
||||
button->sizeValue()
|
||||
) | rpl::start_with_next([=](QSize outer, QSize inner) {
|
||||
button->moveToRight(
|
||||
0,
|
||||
(outer.height() - inner.height()) / 2,
|
||||
outer.width());
|
||||
clickableBackground->resize(outer);
|
||||
}, widget->lifetime());
|
||||
|
||||
button->setClickedCallback([=] {
|
||||
buttonCallback();
|
||||
hideToast();
|
||||
});
|
||||
}
|
||||
|
||||
void TranslateBar::show() {
|
||||
if (!_forceHidden) {
|
||||
return;
|
||||
|
|
|
@ -14,13 +14,22 @@ struct LanguageId;
|
|||
|
||||
namespace Ui {
|
||||
class PlainShadow;
|
||||
class PopupMenu;
|
||||
class IconButton;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
class TranslateBar final {
|
||||
public:
|
||||
TranslateBar(not_null<QWidget*> parent, not_null<History*> history);
|
||||
TranslateBar(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> history);
|
||||
~TranslateBar();
|
||||
|
||||
void show();
|
||||
|
@ -43,10 +52,23 @@ private:
|
|||
void setup(not_null<History*> history);
|
||||
void updateShadowGeometry(QRect wrapGeometry);
|
||||
void updateControlsGeometry(QRect wrapGeometry);
|
||||
[[nodiscard]] base::unique_qptr<Ui::PopupMenu> createMenu(
|
||||
not_null<Ui::IconButton*> button);
|
||||
void showMenu(base::unique_qptr<Ui::PopupMenu> menu);
|
||||
|
||||
void showHiddenToast(not_null<PeerData*> peer);
|
||||
void showSettingsToast(not_null<PeerData*> peer, LanguageId ignored);
|
||||
void showToast(
|
||||
TextWithEntities text,
|
||||
const QString &buttonText,
|
||||
Fn<void()> buttonCallback);
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
const not_null<History*> _history;
|
||||
Ui::SlideWrap<> _wrap;
|
||||
std::unique_ptr<Ui::PlainShadow> _shadow;
|
||||
Fn<QRect(QRect)> _shadowGeometryPostprocess;
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
bool _shouldBeShown = false;
|
||||
bool _forceHidden = false;
|
||||
|
||||
|
|
|
@ -57,13 +57,6 @@ void TranslateTracker::setup() {
|
|||
const auto session = &_history->session();
|
||||
peer->updateFull();
|
||||
|
||||
auto translationEnabled = session->changes().peerFlagsValue(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::TranslationDisabled
|
||||
) | rpl::map([=] {
|
||||
return peer->translationFlag() == PeerData::TranslationFlag::Enabled;
|
||||
}) | rpl::distinct_until_changed();
|
||||
|
||||
_trackingLanguage = Data::AmPremiumValue(&_history->session());
|
||||
|
||||
_trackingLanguage.value(
|
||||
|
@ -340,7 +333,14 @@ void TranslateTracker::checkRecognized(const std::vector<LanguageId> &skip) {
|
|||
: _allLoaded
|
||||
? std::min(count, kEnoughForTranslation)
|
||||
: kEnoughForTranslation;
|
||||
if (ranges::accumulate(languages, 0, ranges::plus(), p) >= threshold) {
|
||||
const auto translatable = ranges::accumulate(
|
||||
languages,
|
||||
0,
|
||||
ranges::plus(),
|
||||
p);
|
||||
if (count < kEnoughForTranslation) {
|
||||
// Don't change offer by small amount of messages.
|
||||
} else if (translatable >= threshold) {
|
||||
_history->translateOfferFrom(
|
||||
ranges::max_element(languages, ranges::less(), p)->first);
|
||||
} else {
|
||||
|
|
|
@ -121,7 +121,9 @@ TextWithEntities AddTimestampLinks(
|
|||
return text;
|
||||
}
|
||||
static const auto expression = QRegularExpression(
|
||||
"(?<![^\\s\\(\\)\"\\,\\.\\-])(?:(?:(\\d{1,2}):)?(\\d))?(\\d):(\\d\\d)(?![^\\s\\(\\)\",\\.\\-])");
|
||||
"(?<![^\\s\\(\\)\"\\,\\.\\-])"
|
||||
"(?:(?:(\\d{1,2}):)?(\\d))?(\\d):(\\d\\d)"
|
||||
"(?![^\\s\\(\\)\",\\.\\-\\+])");
|
||||
const auto &string = text.text;
|
||||
auto offset = 0;
|
||||
while (true) {
|
||||
|
|
|
@ -21,7 +21,6 @@ namespace {
|
|||
|
||||
const auto kSerializeVersionTag = u"#new"_q;
|
||||
constexpr auto kSerializeVersion = 1;
|
||||
constexpr auto kDefaultLanguage = "en"_cs;
|
||||
constexpr auto kCloudLangPackName = "tdesktop"_cs;
|
||||
constexpr auto kCustomLanguage = "#custom"_cs;
|
||||
constexpr auto kLangValuesLimit = 20000;
|
||||
|
@ -213,14 +212,6 @@ void ParseKeyValue(
|
|||
|
||||
} // namespace
|
||||
|
||||
QString DefaultLanguageId() {
|
||||
return kDefaultLanguage.utf16();
|
||||
}
|
||||
|
||||
QString LanguageIdOrDefault(const QString &id) {
|
||||
return !id.isEmpty() ? id : DefaultLanguageId();
|
||||
}
|
||||
|
||||
QString CloudLangPackName() {
|
||||
return kCloudLangPackName.utf16();
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/producer.h>
|
||||
#include "lang_auto.h"
|
||||
#include "base/const_string.h"
|
||||
#include "base/weak_ptr.h"
|
||||
|
||||
namespace Lang {
|
||||
|
@ -31,8 +31,6 @@ inline bool operator!=(const Language &a, const Language &b) {
|
|||
return !(a == b);
|
||||
}
|
||||
|
||||
QString DefaultLanguageId();
|
||||
QString LanguageIdOrDefault(const QString &id);
|
||||
QString CloudLangPackName();
|
||||
QString CustomLanguageId();
|
||||
Language DefaultLanguage();
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "lang/lang_keys.h"
|
||||
|
||||
#include "base/const_string.h"
|
||||
#include "lang/lang_file_parser.h"
|
||||
#include "ui/integration.h"
|
||||
|
||||
|
@ -14,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
namespace {
|
||||
|
||||
constexpr auto kDefaultLanguage = "en"_cs;
|
||||
|
||||
template <typename WithYear, typename WithoutYear>
|
||||
inline QString langDateMaybeWithYear(
|
||||
QDate date,
|
||||
|
@ -231,3 +234,15 @@ QString langDateTimeFull(const QDateTime &date) {
|
|||
lt_time,
|
||||
QLocale().toString(date.time(), QLocale::ShortFormat));
|
||||
}
|
||||
|
||||
namespace Lang {
|
||||
|
||||
QString DefaultLanguageId() {
|
||||
return kDefaultLanguage.utf16();
|
||||
}
|
||||
|
||||
QString LanguageIdOrDefault(const QString &id) {
|
||||
return !id.isEmpty() ? id : DefaultLanguageId();
|
||||
}
|
||||
|
||||
} // namespace Lang
|
||||
|
|
|
@ -34,5 +34,7 @@ namespace Lang {
|
|||
[[nodiscard]] QString Id();
|
||||
[[nodiscard]] rpl::producer<> Updated();
|
||||
[[nodiscard]] QString GetNonDefaultValue(const QByteArray &key);
|
||||
[[nodiscard]] QString DefaultLanguageId();
|
||||
[[nodiscard]] QString LanguageIdOrDefault(const QString &id);
|
||||
|
||||
} // namespace Lang
|
||||
|
|
|
@ -119,6 +119,7 @@ settingsPremiumIconSpeed: icon {{ "settings/premium/speed", settingsIconFg }};
|
|||
settingsPremiumIconStar: icon {{ "settings/premium/star", settingsIconFg }};
|
||||
settingsPremiumIconVoice: icon {{ "settings/premium/voice", settingsIconFg }};
|
||||
settingsPremiumIconFiles: icon {{ "settings/premium/files", settingsIconFg }};
|
||||
settingsPremiumIconTranslations: icon {{ "settings/premium/translations", settingsIconFg }};
|
||||
|
||||
settingsTTLChatsOff: icon {{ "settings/ttl/autodelete_off", windowSubTextFg }};
|
||||
settingsTTLChatsOn: icon {{ "settings/ttl/autodelete_on", windowActiveTextFg }};
|
||||
|
|
|
@ -221,6 +221,7 @@ using Order = std::vector<QString>;
|
|||
u"advanced_chat_management"_q,
|
||||
u"profile_badge"_q,
|
||||
u"animated_userpics"_q,
|
||||
u"translations"_q,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -333,6 +334,15 @@ using Order = std::vector<QString>;
|
|||
PremiumPreview::AnimatedUserpics,
|
||||
},
|
||||
},
|
||||
{
|
||||
u"translations"_q,
|
||||
Entry{
|
||||
&st::settingsPremiumIconTranslations,
|
||||
tr::lng_premium_summary_subtitle_translation(),
|
||||
tr::lng_premium_summary_about_translation(),
|
||||
PremiumPreview::RealTimeTranslation,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1887,6 +1897,8 @@ not_null<Ui::GradientButton*> CreateSubscribeButton(
|
|||
return PremiumPreview::ProfileBadge;
|
||||
} else if (s == u"animated_userpics"_q) {
|
||||
return PremiumPreview::AnimatedUserpics;
|
||||
} else if (s == u"translations"_q) {
|
||||
return PremiumPreview::RealTimeTranslation;
|
||||
}
|
||||
return PremiumPreview::kCount;
|
||||
}) | ranges::views::filter([](PremiumPreview type) {
|
||||
|
|
321
Telegram/SourceFiles/ui/boxes/choose_language_box.cpp
Normal file
321
Telegram/SourceFiles/ui/boxes/choose_language_box.cpp
Normal file
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
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 "styles/style_info.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
const auto kLanguageNamePrefix = "cloud_lng_passport_in_";
|
||||
|
||||
[[nodiscard]] std::vector<LanguageId> TranslationLanguagesList() {
|
||||
return {
|
||||
{ QLocale::English },
|
||||
{ QLocale::Afrikaans },
|
||||
{ QLocale::Albanian },
|
||||
{ QLocale::Amharic },
|
||||
{ QLocale::Arabic },
|
||||
{ QLocale::Armenian },
|
||||
{ QLocale::Azerbaijani },
|
||||
{ QLocale::Basque },
|
||||
{ QLocale::Belarusian },
|
||||
{ QLocale::Bosnian },
|
||||
{ QLocale::Bulgarian },
|
||||
{ QLocale::Burmese },
|
||||
{ QLocale::Catalan },
|
||||
{ QLocale::Chinese },
|
||||
{ QLocale::Croatian },
|
||||
{ QLocale::Czech },
|
||||
{ QLocale::Danish },
|
||||
{ QLocale::Dutch },
|
||||
{ QLocale::Esperanto },
|
||||
{ QLocale::Estonian },
|
||||
{ QLocale::Finnish },
|
||||
{ QLocale::French },
|
||||
{ QLocale::Gaelic },
|
||||
{ QLocale::Galician },
|
||||
{ QLocale::Georgian },
|
||||
{ QLocale::German },
|
||||
{ QLocale::Greek },
|
||||
{ QLocale::Gusii },
|
||||
{ QLocale::Hausa },
|
||||
{ QLocale::Hebrew },
|
||||
{ QLocale::Hungarian },
|
||||
{ QLocale::Icelandic },
|
||||
{ QLocale::Igbo },
|
||||
{ QLocale::Indonesian },
|
||||
{ QLocale::Irish },
|
||||
{ QLocale::Italian },
|
||||
{ QLocale::Japanese },
|
||||
{ QLocale::Kazakh },
|
||||
{ QLocale::Kinyarwanda },
|
||||
{ QLocale::Korean },
|
||||
{ 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::Polish },
|
||||
{ QLocale::Portuguese },
|
||||
{ QLocale::Romanian },
|
||||
{ QLocale::Russian },
|
||||
{ QLocale::Serbian },
|
||||
{ QLocale::Shona },
|
||||
{ QLocale::Sindhi },
|
||||
{ QLocale::Sinhala },
|
||||
{ QLocale::Slovak },
|
||||
{ QLocale::Slovenian },
|
||||
{ QLocale::Somali },
|
||||
{ QLocale::Spanish },
|
||||
{ QLocale::Sundanese },
|
||||
{ QLocale::Swahili },
|
||||
{ QLocale::Swedish },
|
||||
{ QLocale::Tajik },
|
||||
{ QLocale::Tatar },
|
||||
{ QLocale::Teso },
|
||||
{ QLocale::Thai },
|
||||
{ QLocale::Turkish },
|
||||
{ QLocale::Turkmen },
|
||||
{ QLocale::Ukrainian },
|
||||
{ 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 LanguageName(LanguageId id) {
|
||||
const auto code = id.locale().name().toLower().mid(0, 2);
|
||||
const auto translated = LanguageNameTranslated(code);
|
||||
return translated.isEmpty()
|
||||
? QLocale::languageToString(id.locale().language())
|
||||
: 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);
|
||||
}
|
||||
}
|
||||
|
||||
void ChooseLanguageBox(
|
||||
not_null<GenericBox*> box,
|
||||
rpl::producer<QString> title,
|
||||
Fn<void(std::vector<LanguageId>)> callback,
|
||||
std::optional<std::vector<LanguageId>> toggled) {
|
||||
box->setMinHeight(st::boxWidth);
|
||||
box->setMaxHeight(st::boxWidth);
|
||||
box->setTitle(std::move(title));
|
||||
|
||||
const auto hasToggles = toggled.has_value();
|
||||
|
||||
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();
|
||||
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);
|
||||
}
|
||||
if (hasToggles) {
|
||||
ranges::stable_partition(list, [&](LanguageId id) {
|
||||
return ranges::contains(*toggled, id);
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}();
|
||||
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 (hasToggles) {
|
||||
button->entity()->toggleOn(
|
||||
rpl::single(ranges::contains(*toggled, id)),
|
||||
false);
|
||||
} 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 (hasToggles) {
|
||||
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
|
26
Telegram/SourceFiles/ui/boxes/choose_language_box.h
Normal file
26
Telegram/SourceFiles/ui/boxes/choose_language_box.h
Normal file
|
@ -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
|
||||
|
||||
struct LanguageId;
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class GenericBox;
|
||||
|
||||
[[nodiscard]] QString LanguageNameTranslated(const QString &twoLetterCode);
|
||||
[[nodiscard]] QString LanguageName(LanguageId id);
|
||||
[[nodiscard]] QString LanguageNameNative(LanguageId id);
|
||||
|
||||
void ChooseLanguageBox(
|
||||
not_null<GenericBox*> box,
|
||||
rpl::producer<QString> title,
|
||||
Fn<void(std::vector<LanguageId>)> callback,
|
||||
std::optional<std::vector<LanguageId>> toggled);
|
||||
|
||||
} // namespace Ui
|
|
@ -1273,3 +1273,4 @@ historyTranslateSettings: IconButton(defaultIconButton) {
|
|||
rippleAreaSize: 38px;
|
||||
ripple: defaultRippleAnimation;
|
||||
}
|
||||
historyTranslateMenuPosition: point(-6px, 40px);
|
|
@ -156,6 +156,8 @@ PRIVATE
|
|||
ui/boxes/calendar_box.h
|
||||
ui/boxes/choose_date_time.cpp
|
||||
ui/boxes/choose_date_time.h
|
||||
ui/boxes/choose_language_box.cpp
|
||||
ui/boxes/choose_language_box.h
|
||||
ui/boxes/choose_time.cpp
|
||||
ui/boxes/choose_time.h
|
||||
ui/boxes/confirm_box.cpp
|
||||
|
@ -326,6 +328,7 @@ PRIVATE
|
|||
desktop-app::lib_ffmpeg
|
||||
desktop-app::lib_webview
|
||||
desktop-app::lib_webrtc
|
||||
desktop-app::lib_spellcheck
|
||||
desktop-app::lib_stripe
|
||||
desktop-app::external_kcoreaddons
|
||||
)
|
||||
|
|
|
@ -36,12 +36,6 @@ if (TDESKTOP_API_ID STREQUAL "0" OR TDESKTOP_API_HASH STREQUAL "")
|
|||
" ")
|
||||
endif()
|
||||
|
||||
if (DESKTOP_APP_DISABLE_SPELLCHECK)
|
||||
target_compile_definitions(Telegram PRIVATE TDESKTOP_DISABLE_SPELLCHECK)
|
||||
else()
|
||||
target_link_libraries(Telegram PRIVATE desktop-app::lib_spellcheck)
|
||||
endif()
|
||||
|
||||
if (DESKTOP_APP_DISABLE_AUTOUPDATE)
|
||||
target_compile_definitions(Telegram PRIVATE TDESKTOP_DISABLE_AUTOUPDATE)
|
||||
endif()
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f2e698f2209a86c133261196275ca98273c7a4dc
|
||||
Subproject commit 7bcca23bfe9f95bf9fd10a2e803734baccc517b4
|
Loading…
Add table
Reference in a new issue