@@ -19,6 +19,7 @@ For license and copyright information please follow this link:
1919#include " mainwidget.h"
2020#include " mtproto/dedicated_file_loader.h"
2121#include " spellcheck/spellcheck_utils.h"
22+ #include " spellcheck/platform/platform_spellcheck.h"
2223#include " ui/wrap/vertical_layout.h"
2324#include " ui/widgets/buttons.h"
2425#include " ui/widgets/labels.h"
@@ -42,6 +43,32 @@ using DictState = BlobState;
4243using QueryCallback = Fn<void (const QString &)>;
4344constexpr auto kMaxQueryLength = 15 ;
4445
46+ // Get system dictionaries for enchant backend.
47+ std::vector<Spellchecker::Dict> SystemDictionaries () {
48+ const auto available = Platform::Spellchecker::AvailableLanguages ();
49+ std::vector<Spellchecker::Dict> result;
50+ std::set<int > seenIds;
51+ result.reserve (available.size ());
52+
53+ for (const auto &locale : available) {
54+ const auto langId = Spellchecker::LangIdFromLocale (locale);
55+
56+ // Deduplicate langIds. E.g.: en_GB and en_GB-large.
57+ if (!seenIds.insert (langId).second ) {
58+ continue ;
59+ }
60+
61+ const auto qlocale = QLocale (locale);
62+ auto name = QLocale::languageToString (qlocale.language ());
63+ if (qlocale.country () != QLocale::AnyCountry) {
64+ name += " (" + QLocale::countryToString (qlocale.country ()) + " )" ;
65+ }
66+ result.push_back (Spellchecker::Dict{ langId, 0 , 0 , name });
67+ }
68+
69+ return result;
70+ }
71+
4572class Inner : public Ui ::RpWidget {
4673public:
4774 Inner (
@@ -63,6 +90,16 @@ class Inner : public Ui::RpWidget {
6390};
6491
6592inline auto DictExists (int langId) {
93+ if (Platform::Spellchecker::IsSystemSpellchecker ()) {
94+ // Check if the locale exists in available languages.
95+ const auto available = Platform::Spellchecker::AvailableLanguages ();
96+ for (const auto &availableLocale : available) {
97+ if (Spellchecker::LangIdFromLocale (availableLocale) == langId) {
98+ return true ;
99+ }
100+ }
101+ return false ;
102+ }
66103 return Spellchecker::DictionaryExists (langId);
67104}
68105
@@ -77,6 +114,10 @@ DictState ComputeState(int id, bool enabled) {
77114 if (DictExists (id)) {
78115 return result;
79116 }
117+ // For system spellchecker, dictionaries that don't exist are not available.
118+ if (Platform::Spellchecker::IsSystemSpellchecker ()) {
119+ return Ready ();
120+ }
80121 return Available{ Spellchecker::GetDownloadSize (id) };
81122}
82123
@@ -113,6 +154,86 @@ Dictionaries Inner::enabledRows() const {
113154 return _enabledRows;
114155}
115156
157+ // Simplified button for system dictionaries (no download/remove).
158+ auto AddSystemDictButton (
159+ not_null<Ui::VerticalLayout*> content,
160+ const Spellchecker::Dict &dict,
161+ bool buttonEnabled,
162+ rpl::producer<QStringView> query) {
163+ const auto id = dict.id ;
164+ buttonEnabled &= DictExists (id);
165+
166+ const auto locale = Spellchecker::LocaleFromLangId (id);
167+ const std::vector<QString> indexList = {
168+ dict.name ,
169+ QLocale::languageToString (locale.language ()),
170+ QLocale::countryToString (locale.country ())
171+ };
172+
173+ const auto wrap = content->add (
174+ object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
175+ content,
176+ object_ptr<Ui::SettingsButton>(
177+ content,
178+ rpl::single (dict.name ),
179+ st::dictionariesSectionButton
180+ )
181+ )
182+ );
183+ const auto button = wrap->entity ();
184+
185+ std::move (
186+ query
187+ ) | rpl::start_with_next ([=](auto string) {
188+ wrap->toggle (
189+ ranges::any_of (indexList, [&](const QString &s) {
190+ return s.startsWith (string, Qt::CaseInsensitive);
191+ }),
192+ anim::type::instant);
193+ }, button->lifetime ());
194+
195+ const auto label = Ui::CreateChild<Ui::FlatLabel>(
196+ button,
197+ (buttonEnabled
198+ ? tr::lng_settings_manage_enabled_dictionary (tr::now)
199+ : QString ()),
200+ st::settingsUpdateState);
201+ label->setAttribute (Qt::WA_TransparentForMouseEvents);
202+
203+ rpl::combine (
204+ button->widthValue (),
205+ label->widthValue ()
206+ ) | rpl::start_with_next ([=] {
207+ label->moveToLeft (
208+ st::settingsUpdateStatePosition.x (),
209+ st::settingsUpdateStatePosition.y ());
210+ }, label->lifetime ());
211+
212+ button->toggleOn (rpl::single (buttonEnabled));
213+
214+ button->toggledValue (
215+ ) | rpl::start_with_next ([=](bool toggled) {
216+ const auto over = !button->isDisabled ()
217+ && (button->isDown () || button->isOver ());
218+ const auto toggledFloat = toggled ? 1 . : 0 .;
219+
220+ if (toggledFloat == 0 . && !over) {
221+ label->setTextColorOverride (std::nullopt );
222+ } else {
223+ label->setTextColorOverride (anim::color (
224+ over ? st::contactsStatusFgOver : st::contactsStatusFg,
225+ st::contactsStatusFgOnline,
226+ toggledFloat));
227+ }
228+
229+ label->setText (toggled
230+ ? tr::lng_settings_manage_enabled_dictionary (tr::now)
231+ : QString ());
232+ }, label->lifetime ());
233+
234+ return button;
235+ }
236+
116237auto AddButtonWithLoader (
117238 not_null<Ui::VerticalLayout*> content,
118239 not_null<Main::Session*> session,
@@ -344,14 +465,25 @@ void Inner::setupContent(
344465 const auto queryStream = content->lifetime ()
345466 .make_state <rpl::event_stream<QStringView>>();
346467
347- for (const auto &dict : Spellchecker::Dictionaries ()) {
468+ const auto isSystemSpellchecker = Platform::Spellchecker::IsSystemSpellchecker ();
469+ const auto dictionaries = isSystemSpellchecker
470+ ? SystemDictionaries ()
471+ : Spellchecker::Dictionaries ();
472+
473+ for (const auto &dict : dictionaries) {
348474 const auto id = dict.id ;
349- const auto row = AddButtonWithLoader (
350- content,
351- session,
352- dict,
353- ranges::contains (enabledDictionaries, id),
354- queryStream->events ());
475+ const auto row = isSystemSpellchecker
476+ ? AddSystemDictButton (
477+ content,
478+ dict,
479+ ranges::contains (enabledDictionaries, id),
480+ queryStream->events ())
481+ : AddButtonWithLoader (
482+ content,
483+ session,
484+ dict,
485+ ranges::contains (enabledDictionaries, id),
486+ queryStream->events ());
355487 row->toggledValue (
356488 ) | rpl::start_with_next ([=](auto enabled) {
357489 if (enabled) {
0 commit comments