mirror of https://github.com/procxx/kepka.git
Added sequential background dictionary loader.
- Moved the Loader from the dictionaries manager to the spellchecker common space as a DictLoader.
This commit is contained in:
parent
9daf362df6
commit
bb8aead078
|
@ -44,24 +44,6 @@ constexpr auto kMaxQueryLength = 15;
|
||||||
using QStringView = QString;
|
using QStringView = QString;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Loader : public BlobLoader {
|
|
||||||
public:
|
|
||||||
Loader(
|
|
||||||
QObject *parent,
|
|
||||||
int id,
|
|
||||||
MTP::DedicatedLoader::Location location,
|
|
||||||
const QString &folder,
|
|
||||||
int size,
|
|
||||||
Fn<void()> destroyCallback);
|
|
||||||
|
|
||||||
void destroy() override;
|
|
||||||
void unpack(const QString &path) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Fn<void()> _destroyCallback;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class Inner : public Ui::RpWidget {
|
class Inner : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
Inner(QWidget *parent, Dictionaries enabledDictionaries);
|
Inner(QWidget *parent, Dictionaries enabledDictionaries);
|
||||||
|
@ -101,31 +83,6 @@ QString StateDescription(const DictState &state) {
|
||||||
tr::lng_settings_manage_enabled_dictionary);
|
tr::lng_settings_manage_enabled_dictionary);
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader::Loader(
|
|
||||||
QObject *parent,
|
|
||||||
int id,
|
|
||||||
MTP::DedicatedLoader::Location location,
|
|
||||||
const QString &folder,
|
|
||||||
int size,
|
|
||||||
Fn<void()> destroyCallback)
|
|
||||||
: BlobLoader(parent, id, location, folder, size)
|
|
||||||
, _destroyCallback(std::move(destroyCallback)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::unpack(const QString &path) {
|
|
||||||
Expects(_destroyCallback);
|
|
||||||
crl::async([=] {
|
|
||||||
const auto success = Spellchecker::UnpackDictionary(path, id());
|
|
||||||
if (success) {
|
|
||||||
QFile(path).remove();
|
|
||||||
}
|
|
||||||
crl::on_main(success ? _destroyCallback : [=] { fail(); });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::destroy() {
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CreateMultiSelect(QWidget *parent) {
|
auto CreateMultiSelect(QWidget *parent) {
|
||||||
const auto result = Ui::CreateChild<Ui::MultiSelect>(
|
const auto result = Ui::CreateChild<Ui::MultiSelect>(
|
||||||
parent,
|
parent,
|
||||||
|
@ -188,6 +145,9 @@ auto AddButtonWithLoader(
|
||||||
anim::type::instant);
|
anim::type::instant);
|
||||||
}, button->lifetime());
|
}, button->lifetime());
|
||||||
|
|
||||||
|
using Loader = Spellchecker::DictLoader;
|
||||||
|
using GlobalLoaderPtr = std::shared_ptr<base::unique_qptr<Loader>>;
|
||||||
|
|
||||||
const auto localLoader = button->lifetime()
|
const auto localLoader = button->lifetime()
|
||||||
.make_state<base::unique_qptr<Loader>>();
|
.make_state<base::unique_qptr<Loader>>();
|
||||||
const auto localLoaderValues = button->lifetime()
|
const auto localLoaderValues = button->lifetime()
|
||||||
|
@ -200,11 +160,45 @@ auto AddButtonWithLoader(
|
||||||
setLocalLoader(nullptr);
|
setLocalLoader(nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const auto buttonState = button->lifetime()
|
const auto buttonState = button->lifetime()
|
||||||
.make_state<rpl::variable<DictState>>();
|
.make_state<rpl::variable<DictState>>();
|
||||||
const auto dictionaryRemoved = button->lifetime()
|
const auto dictionaryRemoved = button->lifetime()
|
||||||
.make_state<rpl::event_stream<>>();
|
.make_state<rpl::event_stream<>>();
|
||||||
|
const auto dictionaryFromGlobalLoader = button->lifetime()
|
||||||
|
.make_state<rpl::event_stream<>>();
|
||||||
|
|
||||||
|
const auto globalLoader = button->lifetime()
|
||||||
|
.make_state<GlobalLoaderPtr>();
|
||||||
|
|
||||||
|
const auto rawGlobalLoaderPtr = [=]() -> Loader* {
|
||||||
|
if (!globalLoader || !*globalLoader || !*globalLoader->get()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return globalLoader->get()->get();
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto setGlobalLoaderPtr = [=](GlobalLoaderPtr loader) {
|
||||||
|
if (localLoader->get()) {
|
||||||
|
if (loader && loader->get()) {
|
||||||
|
loader->get()->destroy();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*globalLoader = std::move(loader);
|
||||||
|
localLoaderValues->fire(rawGlobalLoaderPtr());
|
||||||
|
if (rawGlobalLoaderPtr()) {
|
||||||
|
dictionaryFromGlobalLoader->fire({});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Spellchecker::GlobalLoaderChanged(
|
||||||
|
) | rpl::start_with_next([=](int langId) {
|
||||||
|
if (!langId && rawGlobalLoaderPtr()) {
|
||||||
|
setGlobalLoaderPtr(nullptr);
|
||||||
|
} else if (langId == id) {
|
||||||
|
setGlobalLoaderPtr(Spellchecker::GlobalLoader());
|
||||||
|
}
|
||||||
|
}, button->lifetime());
|
||||||
|
|
||||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||||
button,
|
button,
|
||||||
|
@ -243,21 +237,29 @@ auto AddButtonWithLoader(
|
||||||
buttonEnabled
|
buttonEnabled
|
||||||
) | rpl::then(
|
) | rpl::then(
|
||||||
rpl::merge(
|
rpl::merge(
|
||||||
dictionaryRemoved->events(),
|
// Events to toggle on.
|
||||||
buttonState->value(
|
dictionaryFromGlobalLoader->events(
|
||||||
) | rpl::filter([](const DictState &state) {
|
) | rpl::map([] {
|
||||||
return state.is<Failed>();
|
return true;
|
||||||
}) | rpl::map([] {
|
}),
|
||||||
return rpl::empty_value();
|
// Events to toggle off.
|
||||||
|
rpl::merge(
|
||||||
|
dictionaryRemoved->events(),
|
||||||
|
buttonState->value(
|
||||||
|
) | rpl::filter([](const DictState &state) {
|
||||||
|
return state.is<Failed>();
|
||||||
|
}) | rpl::map([] {
|
||||||
|
return rpl::empty_value();
|
||||||
|
})
|
||||||
|
) | rpl::map([] {
|
||||||
|
return false;
|
||||||
})
|
})
|
||||||
) | rpl::map([]() {
|
)
|
||||||
return false;
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
*buttonState = localLoaderValues->events_starting_with(
|
*buttonState = localLoaderValues->events_starting_with(
|
||||||
localLoader->get()
|
rawGlobalLoaderPtr() ? rawGlobalLoaderPtr() : localLoader->get()
|
||||||
) | rpl::map([=](Loader *loader) {
|
) | rpl::map([=](Loader *loader) {
|
||||||
return (loader && loader->id() == id)
|
return (loader && loader->id() == id)
|
||||||
? loader->state()
|
? loader->state()
|
||||||
|
@ -292,6 +294,10 @@ auto AddButtonWithLoader(
|
||||||
Spellchecker::GetDownloadSize(id),
|
Spellchecker::GetDownloadSize(id),
|
||||||
crl::guard(weak, destroyLocalLoader)));
|
crl::guard(weak, destroyLocalLoader)));
|
||||||
} else if (!toggled && state.is<Loading>()) {
|
} else if (!toggled && state.is<Loading>()) {
|
||||||
|
if (const auto g = rawGlobalLoaderPtr()) {
|
||||||
|
g->destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (localLoader && localLoader->get()->id() == id) {
|
if (localLoader && localLoader->get()->id() == id) {
|
||||||
destroyLocalLoader();
|
destroyLocalLoader();
|
||||||
}
|
}
|
||||||
|
@ -321,6 +327,12 @@ auto AddButtonWithLoader(
|
||||||
return base::EventFilterResult::Continue;
|
return base::EventFilterResult::Continue;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (const auto g = Spellchecker::GlobalLoader()) {
|
||||||
|
if (g.get() && g->get()->id() == id) {
|
||||||
|
setGlobalLoaderPtr(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_SPELLCHECK
|
#ifndef TDESKTOP_DISABLE_SPELLCHECK
|
||||||
|
|
||||||
|
#include "base/platform/base_platform_info.h"
|
||||||
|
#include "base/zlib_help.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "lang/lang_instance.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "base/zlib_help.h"
|
#include "mainwidget.h"
|
||||||
#include "spellcheck/platform/platform_spellcheck.h"
|
#include "spellcheck/platform/platform_spellcheck.h"
|
||||||
#include "spellcheck/spellcheck_utils.h"
|
#include "spellcheck/spellcheck_utils.h"
|
||||||
#include "spellcheck/spellcheck_value.h"
|
#include "spellcheck/spellcheck_value.h"
|
||||||
|
|
||||||
|
#include <QtGui/QGuiApplication>
|
||||||
|
|
||||||
namespace Spellchecker {
|
namespace Spellchecker {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -24,12 +30,20 @@ using namespace Storage::CloudBlob;
|
||||||
|
|
||||||
constexpr auto kDictExtensions = { "dic", "aff" };
|
constexpr auto kDictExtensions = { "dic", "aff" };
|
||||||
|
|
||||||
|
// 31 - QLocale::English, 91 - QLocale::Portuguese.
|
||||||
|
constexpr auto kLangsForLWC = { 31, 91 };
|
||||||
|
// 225 - QLocale::UnitesStates, 30 - QLocale::Brazil.
|
||||||
|
constexpr auto kDefaultCountries = { 225, 30 };
|
||||||
|
|
||||||
// Language With Country.
|
// Language With Country.
|
||||||
inline auto LWC(QLocale::Country country) {
|
inline auto LWC(QLocale::Country country) {
|
||||||
const auto l = QLocale::matchingLocales(
|
const auto l = QLocale::matchingLocales(
|
||||||
QLocale::AnyLanguage,
|
QLocale::AnyLanguage,
|
||||||
QLocale::AnyScript,
|
QLocale::AnyScript,
|
||||||
country)[0];
|
country)[0];
|
||||||
|
if (ranges::contains(kDefaultCountries, country)) {
|
||||||
|
return int(l.language());
|
||||||
|
}
|
||||||
return (l.language() * 1000) + country;
|
return (l.language() * 1000) + country;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,8 +104,104 @@ bool IsGoodPartName(const QString &name) {
|
||||||
}) != end(kDictExtensions);
|
}) != end(kDictExtensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using DictLoaderPtr = std::shared_ptr<base::unique_qptr<DictLoader>>;
|
||||||
|
|
||||||
|
DictLoaderPtr BackgroundLoader;
|
||||||
|
rpl::event_stream<int> BackgroundLoaderChanged;
|
||||||
|
|
||||||
|
void SetBackgroundLoader(DictLoaderPtr loader) {
|
||||||
|
BackgroundLoader = std::move(loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadDictionaryInBackground(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
int counter,
|
||||||
|
std::vector<int> langs) {
|
||||||
|
const auto id = langs[counter];
|
||||||
|
counter++;
|
||||||
|
const auto destroyer = [=] {
|
||||||
|
// This is a temporary workaround.
|
||||||
|
const auto copyId = id;
|
||||||
|
const auto copyLangs = langs;
|
||||||
|
const auto copySession = session;
|
||||||
|
const auto copyCounter = counter;
|
||||||
|
BackgroundLoader = nullptr;
|
||||||
|
BackgroundLoaderChanged.fire(0);
|
||||||
|
|
||||||
|
if (DictionaryExists(copyId)) {
|
||||||
|
auto dicts = copySession->settings().dictionariesEnabled();
|
||||||
|
if (!ranges::contains(dicts, copyId)) {
|
||||||
|
dicts.push_back(copyId);
|
||||||
|
copySession->settings().setDictionariesEnabled(std::move(dicts));
|
||||||
|
copySession->saveSettingsDelayed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copyCounter >= copyLangs.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DownloadDictionaryInBackground(copySession, copyCounter, copyLangs);
|
||||||
|
};
|
||||||
|
if (DictionaryExists(id)) {
|
||||||
|
destroyer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sharedLoader = std::make_shared<base::unique_qptr<DictLoader>>();
|
||||||
|
*sharedLoader = base::make_unique_q<DictLoader>(
|
||||||
|
App::main(),
|
||||||
|
id,
|
||||||
|
GetDownloadLocation(id),
|
||||||
|
DictPathByLangId(id),
|
||||||
|
GetDownloadSize(id),
|
||||||
|
crl::guard(session, destroyer));
|
||||||
|
SetBackgroundLoader(std::move(sharedLoader));
|
||||||
|
BackgroundLoaderChanged.fire_copy(id);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
DictLoaderPtr GlobalLoader() {
|
||||||
|
return BackgroundLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<int> GlobalLoaderChanged() {
|
||||||
|
return BackgroundLoaderChanged.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
DictLoader::DictLoader(
|
||||||
|
QObject *parent,
|
||||||
|
int id,
|
||||||
|
MTP::DedicatedLoader::Location location,
|
||||||
|
const QString &folder,
|
||||||
|
int size,
|
||||||
|
Fn<void()> destroyCallback)
|
||||||
|
: BlobLoader(parent, id, location, folder, size)
|
||||||
|
, _destroyCallback(std::move(destroyCallback)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DictLoader::unpack(const QString &path) {
|
||||||
|
Expects(_destroyCallback);
|
||||||
|
crl::async([=] {
|
||||||
|
const auto success = Spellchecker::UnpackDictionary(path, id());
|
||||||
|
if (success) {
|
||||||
|
QFile(path).remove();
|
||||||
|
}
|
||||||
|
crl::on_main(success ? _destroyCallback : [=] { fail(); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DictLoader::destroy() {
|
||||||
|
Expects(_destroyCallback);
|
||||||
|
|
||||||
|
_destroyCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DictLoader::fail() {
|
||||||
|
BlobLoader::fail();
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Dict> Dictionaries() {
|
std::vector<Dict> Dictionaries() {
|
||||||
return kDictionaries | ranges::to_vector;
|
return kDictionaries | ranges::to_vector;
|
||||||
}
|
}
|
||||||
|
@ -209,6 +319,24 @@ rpl::producer<QString> ButtonManageDictsState(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<int> DefaultLanguages() {
|
||||||
|
std::vector<int> langs;
|
||||||
|
|
||||||
|
const auto method = QGuiApplication::inputMethod();
|
||||||
|
langs.reserve(method ? 3 : 2);
|
||||||
|
if (method) {
|
||||||
|
const auto loc = method->locale();
|
||||||
|
const auto locLang = int(loc.language());
|
||||||
|
langs.push_back(ranges::contains(kLangsForLWC, locLang)
|
||||||
|
? LWC(loc.country())
|
||||||
|
: locLang);
|
||||||
|
}
|
||||||
|
langs.push_back(QLocale(Platform::SystemLanguage()).language());
|
||||||
|
langs.push_back(QLocale(Lang::Current().id()).language());
|
||||||
|
|
||||||
|
return langs;
|
||||||
|
}
|
||||||
|
|
||||||
void Start(not_null<Main::Session*> session) {
|
void Start(not_null<Main::Session*> session) {
|
||||||
Spellchecker::SetPhrases({ {
|
Spellchecker::SetPhrases({ {
|
||||||
{ &ph::lng_spellchecker_add, tr::lng_spellchecker_add() },
|
{ &ph::lng_spellchecker_add, tr::lng_spellchecker_add() },
|
||||||
|
@ -231,6 +359,17 @@ void Start(not_null<Main::Session*> session) {
|
||||||
? session->settings().dictionariesEnabled()
|
? session->settings().dictionariesEnabled()
|
||||||
: std::vector<int>());
|
: std::vector<int>());
|
||||||
}, session->lifetime());
|
}, session->lifetime());
|
||||||
|
|
||||||
|
session->data().contactsLoaded().changes(
|
||||||
|
) | rpl::start_with_next([=](bool loaded) {
|
||||||
|
if (!loaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadDictionaryInBackground(session, 0, DefaultLanguages());
|
||||||
|
}, session->lifetime());
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if (session->settings().spellcheckerEnabled()) {
|
if (session->settings().spellcheckerEnabled()) {
|
||||||
Platform::Spellchecker::UpdateLanguages(
|
Platform::Spellchecker::UpdateLanguages(
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#ifndef TDESKTOP_DISABLE_SPELLCHECK
|
#ifndef TDESKTOP_DISABLE_SPELLCHECK
|
||||||
|
|
||||||
#include "storage/storage_cloud_blob.h"
|
#include "storage/storage_cloud_blob.h"
|
||||||
|
#include "base/unique_qptr.h"
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
class Session;
|
class Session;
|
||||||
|
@ -37,6 +38,37 @@ void Start(not_null<Main::Session*> session);
|
||||||
[[nodiscard]] rpl::producer<QString> ButtonManageDictsState(
|
[[nodiscard]] rpl::producer<QString> ButtonManageDictsState(
|
||||||
not_null<Main::Session*> session);
|
not_null<Main::Session*> session);
|
||||||
|
|
||||||
|
std::vector<int> DefaultLanguages();
|
||||||
|
|
||||||
|
class DictLoader : public Storage::CloudBlob::BlobLoader {
|
||||||
|
public:
|
||||||
|
DictLoader(
|
||||||
|
QObject *parent,
|
||||||
|
int id,
|
||||||
|
MTP::DedicatedLoader::Location location,
|
||||||
|
const QString &folder,
|
||||||
|
int size,
|
||||||
|
Fn<void()> destroyCallback);
|
||||||
|
|
||||||
|
void destroy() override;
|
||||||
|
|
||||||
|
rpl::lifetime &lifetime() {
|
||||||
|
return _lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void unpack(const QString &path) override;
|
||||||
|
void fail() override;
|
||||||
|
|
||||||
|
Fn<void()> _destroyCallback;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<base::unique_qptr<DictLoader>> GlobalLoader();
|
||||||
|
rpl::producer<int> GlobalLoaderChanged();
|
||||||
|
|
||||||
} // namespace Spellchecker
|
} // namespace Spellchecker
|
||||||
|
|
||||||
#endif // !TDESKTOP_DISABLE_SPELLCHECK
|
#endif // !TDESKTOP_DISABLE_SPELLCHECK
|
||||||
|
|
|
@ -91,7 +91,7 @@ public:
|
||||||
virtual void unpack(const QString &path) = 0;
|
virtual void unpack(const QString &path) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void fail();
|
virtual void fail();
|
||||||
|
|
||||||
const QString _folder;
|
const QString _folder;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue