| Summary: | runners/services: look for the same keystrokes search sequence in other keyboard language layouts | ||
|---|---|---|---|
| Product: | [Plasma] krunner | Reporter: | Sergey Katunin <sulmpx60> |
| Component: | general | Assignee: | Plasma Bugs List <plasma-bugs-null> |
| Status: | CONFIRMED --- | ||
| Severity: | wishlist | CC: | alexander.lohnau, eugene.savitsky, geekxx10, natalie_clarius, nate, sulmpx60 |
| Priority: | NOR | ||
| Version First Reported In: | unspecified | ||
| Target Milestone: | --- | ||
| Platform: | unspecified | ||
| OS: | Linux | ||
| See Also: | https://bugs.kde.org/show_bug.cgi?id=467924 | ||
| Latest Commit: | Version Fixed/Implemented In: | ||
| Sentry Crash Report: | |||
| Attachments: | Search example | ||
What a fascinatingly cool idea. *** Bug 477652 has been marked as a duplicate of this bug. *** *** Bug 485202 has been marked as a duplicate of this bug. *** |
Created attachment 161649 [details] Search example I would like to discuss this feature and how it can be implemented for all languages supported in KDE. ### How it is work For example, keystroke sequence like `лфеу` in `Cyrilic ЙЦУКЕН` is `kate` in `QWERTY`. And vice versa, not only the `RU -> EN` search is implemented, but also the `EN -> RU`. This is useful for systems with multiple keyboard language layouts in case when you forget to switch layout. ### Problem However, the task of implementation for all languages looks rather non-trivial. I assume that it is possible to use some kind of system library in this conversion, like `xkb`. So, I would like to know if the community is interested in this feature, and in what ways it could be implemented. ### How it is implemented now At the moment, I have implemented a prototype that has the desired behavior for EN (English) - RU (Russian) layouts by using simple convertation table. Convertation table: ``` class LayoutRuEnConverter { enum class LayoutType { RU_TO_EN = 0, EN_TO_RU = 1 }; using LayoutMap = QMap<QChar, QChar>; using LayoutMaps = QMap<LayoutType, LayoutMap>; public: LayoutRuEnConverter() = default; bool isStrHasRuChar(const QString &str) const { for (auto c: str) { if (ruCharsMap[c]) { return true; } } return false; } QString invertLayout(const QString &str) const { return convertLayout(str, isStrHasRuChar(str) ? LayoutType::RU_TO_EN : LayoutType::EN_TO_RU); } private: QMap<QChar, bool> getRuCharsMap() const { const QString ruChars = "абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"; QMap<QChar, bool> t; for (auto c: ruChars) { t[c] = true; } return t; } LayoutMaps initLayouts() const { const QString qwertyEn = "`qwertyuiop[]asdfghjkl;'zxcvbnm,./~@#$^&QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?"; const QString qwertyRu = "ёйцукенгшщзхъфывапролджэячсмитьбю.Ё\"№;:?ЙЦУКЕНГШЩЗХЪ/ФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,"; const int N = qwertyEn.count(); LayoutMap layoutRuToEnMap; LayoutMap layoutEnToRuMap; for (int i = 0; i < N; ++i) { layoutRuToEnMap[qwertyRu[i]] = qwertyEn[i]; layoutEnToRuMap[qwertyEn[i]] = qwertyRu[i]; } LayoutMaps res; res[LayoutType::RU_TO_EN] = layoutRuToEnMap; res[LayoutType::EN_TO_RU] = layoutEnToRuMap; return res; } QString convertLayout(const QString &str, const LayoutType layoutType) const { QString res = ""; for (auto c: str) { res += !layouts[layoutType][c].isNull() ? layouts[layoutType][c] : c; } return res; } const QMap<QChar, bool> ruCharsMap = getRuCharsMap(); const LayoutMaps layouts = initLayouts(); }; ``` And in match() function, converted search query is executed. ``` void match(Plasma::RunnerContext &context) { const auto doMatch = [this]() { matchNameKeywordAndGenericName(); matchCategories(); matchJumpListActions(); }; term = context.query(); // Splitting the query term to match using subsequences queryList = term.split(QLatin1Char(' ')); weightedTermLength = weightedLength(term); doMatch(); // Invert language layout (RU <-> EN) in queryList. std::transform(queryList.cbegin(), queryList.cend(), queryList.begin(), [this](const QString& str) { return m_layoutConverter->invertLayout(str); }); // Do match again with converted queryList. doMatch(); context.addMatches(matches); } ```