Compare commits
3 commits
363b374fc8
...
266f408640
Author | SHA1 | Date | |
---|---|---|---|
266f408640 | |||
dac0c64d4c | |||
47c42dbdf9 |
3 changed files with 311 additions and 61 deletions
|
@ -3,6 +3,8 @@ target_link_libraries(ipa-sil PRIVATE Fcitx5::Core)
|
||||||
set_target_properties(ipa-sil PROPERTIES PREFIX "")
|
set_target_properties(ipa-sil PROPERTIES PREFIX "")
|
||||||
install(TARGETS ipa-sil DESTINATION "${FCITX_INSTALL_LIBDIR}/fcitx5")
|
install(TARGETS ipa-sil DESTINATION "${FCITX_INSTALL_LIBDIR}/fcitx5")
|
||||||
|
|
||||||
|
set_property(TARGET ipa-sil PROPERTY CXX_STANDARD 20)
|
||||||
|
|
||||||
# Addon config file
|
# Addon config file
|
||||||
# We need additional layer of conversion because we want PROJECT_VERSION in it.
|
# We need additional layer of conversion because we want PROJECT_VERSION in it.
|
||||||
configure_file(ipa-sil-addon.conf.in ipa-sil-addon.conf)
|
configure_file(ipa-sil-addon.conf.in ipa-sil-addon.conf)
|
||||||
|
|
363
src/ipa-sil.cpp
363
src/ipa-sil.cpp
|
@ -9,58 +9,257 @@
|
||||||
#include <fcitx/inputpanel.h>
|
#include <fcitx/inputpanel.h>
|
||||||
#include <fcitx/instance.h>
|
#include <fcitx/instance.h>
|
||||||
|
|
||||||
|
#include <fcitx-utils/utf8.h>
|
||||||
|
|
||||||
|
#include <codecvt>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
// This is a giant map from a base character (stored as std::string because it's
|
||||||
|
// easier that way) to a map. This new map maps SIL modifiers to the resulting
|
||||||
|
// IPA character.
|
||||||
|
// In the end, SILtoIPA["s"][FcitxKey_equal] == "ʃ", as `s=`
|
||||||
|
// makes the IPA /ʃ/.
|
||||||
|
static std::unordered_map<
|
||||||
|
std::string,
|
||||||
|
std::unordered_map<fcitx::KeySym const, std::string const> const> const
|
||||||
|
SILtoIPA = {
|
||||||
|
// ----- Consonants
|
||||||
|
{"t",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "ʈ"},
|
||||||
|
}},
|
||||||
|
{"d",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "ɖ"},
|
||||||
|
}},
|
||||||
|
{"j",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ʄ"},
|
||||||
|
{FcitxKey_less, "ʝ"},
|
||||||
|
{FcitxKey_equal, "ɟ"},
|
||||||
|
}},
|
||||||
|
{"g",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ɠ"},
|
||||||
|
{FcitxKey_less, "ɡ"},
|
||||||
|
{FcitxKey_equal, "ɣ"},
|
||||||
|
}},
|
||||||
|
{"G",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ʛ"},
|
||||||
|
{FcitxKey_equal, "ɢ"},
|
||||||
|
}},
|
||||||
|
{"?",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "ʕ"},
|
||||||
|
{FcitxKey_equal, "ʔ"},
|
||||||
|
}},
|
||||||
|
{"m",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ɱ"},
|
||||||
|
}},
|
||||||
|
{"n",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ŋ"},
|
||||||
|
{FcitxKey_less, "ɳ"},
|
||||||
|
{FcitxKey_equal, "ɲ"},
|
||||||
|
}},
|
||||||
|
{"N",
|
||||||
|
{
|
||||||
|
{FcitxKey_equal, "ɴ"},
|
||||||
|
}},
|
||||||
|
{"B",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ʁ"},
|
||||||
|
{FcitxKey_equal, "ʙ"},
|
||||||
|
}},
|
||||||
|
{"R",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "ɻ"},
|
||||||
|
{FcitxKey_equal, "ʀ"},
|
||||||
|
}},
|
||||||
|
{"v",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "ⱱ"},
|
||||||
|
{FcitxKey_equal, "ʋ"},
|
||||||
|
}},
|
||||||
|
{"r",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ɾ"},
|
||||||
|
{FcitxKey_equal, "ɹ"},
|
||||||
|
}},
|
||||||
|
{"f",
|
||||||
|
{
|
||||||
|
{FcitxKey_equal, "ɸ"},
|
||||||
|
}},
|
||||||
|
{"b",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ɓ"},
|
||||||
|
{FcitxKey_equal, "β"},
|
||||||
|
}},
|
||||||
|
{"t",
|
||||||
|
{
|
||||||
|
{FcitxKey_equal, "θ"},
|
||||||
|
}},
|
||||||
|
{"d",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ɗ"},
|
||||||
|
{FcitxKey_equal, "ð"},
|
||||||
|
}},
|
||||||
|
{"s",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "ʂ"},
|
||||||
|
{FcitxKey_equal, "ʃ"},
|
||||||
|
}},
|
||||||
|
{"z",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "ʐ"},
|
||||||
|
{FcitxKey_greater, "ʑ"},
|
||||||
|
{FcitxKey_equal, "ʒ"},
|
||||||
|
}},
|
||||||
|
{"c",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "ɕ"},
|
||||||
|
{FcitxKey_equal, "ç"},
|
||||||
|
}},
|
||||||
|
{"x",
|
||||||
|
{
|
||||||
|
{FcitxKey_equal, "χ"},
|
||||||
|
}},
|
||||||
|
{"h",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ħ"},
|
||||||
|
{FcitxKey_less, "ɦ"},
|
||||||
|
{FcitxKey_equal, "ɥ"},
|
||||||
|
}},
|
||||||
|
{"l",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ɮ"},
|
||||||
|
{FcitxKey_less, "ɭ"},
|
||||||
|
{FcitxKey_equal, "ɬ"},
|
||||||
|
}},
|
||||||
|
{"w",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ɰ"},
|
||||||
|
{FcitxKey_equal, "ʍ"},
|
||||||
|
}},
|
||||||
|
{"L",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ɺ"},
|
||||||
|
{FcitxKey_less, "ʎ"},
|
||||||
|
{FcitxKey_equal, "ʟ"},
|
||||||
|
}},
|
||||||
|
{"p",
|
||||||
|
{
|
||||||
|
{FcitxKey_equal, "ʘ"},
|
||||||
|
}},
|
||||||
|
{"!",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ǁ"},
|
||||||
|
{FcitxKey_equal, "ǂ"},
|
||||||
|
}},
|
||||||
|
{"y",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "ɥ"},
|
||||||
|
{FcitxKey_equal, "ʏ"},
|
||||||
|
}},
|
||||||
|
{"H",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ɧ"},
|
||||||
|
{FcitxKey_equal, "ʜ"},
|
||||||
|
}},
|
||||||
|
{"Q",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "ʢ"},
|
||||||
|
{FcitxKey_equal, "ʡ"},
|
||||||
|
}},
|
||||||
|
|
||||||
|
// ----- Vowels
|
||||||
|
|
||||||
|
{"I",
|
||||||
|
{
|
||||||
|
{FcitxKey_equal, "ɨ"},
|
||||||
|
}},
|
||||||
|
{"U",
|
||||||
|
{
|
||||||
|
{FcitxKey_equal, "ʉ"},
|
||||||
|
}},
|
||||||
|
{"u",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ʌ"},
|
||||||
|
{FcitxKey_equal, "ɯ"},
|
||||||
|
}},
|
||||||
|
{"i",
|
||||||
|
{
|
||||||
|
{FcitxKey_equal, "ɪ"},
|
||||||
|
}},
|
||||||
|
{"o",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "ɔ"},
|
||||||
|
{FcitxKey_greater, "ø"},
|
||||||
|
}},
|
||||||
|
{"E",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "œ"},
|
||||||
|
{FcitxKey_greater, "ɶ"},
|
||||||
|
{FcitxKey_equal, "ɘ"},
|
||||||
|
}},
|
||||||
|
{"O",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ɤ"},
|
||||||
|
{FcitxKey_equal, "ɵ"},
|
||||||
|
}},
|
||||||
|
{"e",
|
||||||
|
{
|
||||||
|
{FcitxKey_greater, "ɜ"},
|
||||||
|
{FcitxKey_less, "ɛ"},
|
||||||
|
{FcitxKey_equal, "ə"},
|
||||||
|
}},
|
||||||
|
{"O",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "ɞ"},
|
||||||
|
}},
|
||||||
|
{"a",
|
||||||
|
{
|
||||||
|
{FcitxKey_less, "æ"},
|
||||||
|
{FcitxKey_greater, "ɐ"},
|
||||||
|
{FcitxKey_equal, "ɑ"},
|
||||||
|
}},
|
||||||
|
};
|
||||||
|
|
||||||
|
// This maps KeySyms to a list of characters indicating what repeating the key
|
||||||
|
// should convert to.
|
||||||
|
static std::unordered_map<fcitx::KeySym const,
|
||||||
|
std::vector<std::string> const> const
|
||||||
|
keyToRepeatConversion = {
|
||||||
|
{FcitxKey_colon, {"ː", "ˑ", "ːː"}},
|
||||||
|
{FcitxKey_braceright, {"ˈ", "ˌ"}},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maps regular IPA characters to their superscript variants
|
||||||
|
static std::unordered_map<std::string, std::string> const superscripts = {
|
||||||
|
{"-", "⁻"},
|
||||||
|
{"h", "ʰ"},
|
||||||
|
{"w", "ʷ"},
|
||||||
|
{"j", "ʲ"},
|
||||||
|
{"ɣ", "ˠ"},
|
||||||
|
{"ʕ", "ˤ"},
|
||||||
|
{"n", "ⁿ"},
|
||||||
|
{"l", "ˡ"},
|
||||||
|
|
||||||
|
// TODO: Find a complete list of all IPA superscripts and complete this.
|
||||||
|
};
|
||||||
|
|
||||||
std::optional<std::string> getIPAForSIL(std::string baseCharacter,
|
std::optional<std::string> getIPAForSIL(std::string baseCharacter,
|
||||||
fcitx::KeySym silModifier)
|
fcitx::KeySym silModifier)
|
||||||
{
|
{
|
||||||
// TODO: Use a proper data structure, like a hash map
|
try {
|
||||||
// Even better: load that data off of a file!
|
return SILtoIPA.at(baseCharacter).at(silModifier);
|
||||||
|
} catch (std::out_of_range& e) {
|
||||||
// TODO: Add more characters! This is only enough for narrow RP
|
return {};
|
||||||
// transcriptions...
|
|
||||||
if (baseCharacter == "s") {
|
|
||||||
if (silModifier == FcitxKey_equal) return "ʃ";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (baseCharacter == "z") {
|
|
||||||
if (silModifier == FcitxKey_equal) return "ʒ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseCharacter == "t") {
|
|
||||||
if (silModifier == FcitxKey_equal) return "θ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseCharacter == "d") {
|
|
||||||
if (silModifier == FcitxKey_equal) return "ð";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseCharacter == "n") {
|
|
||||||
if (silModifier == FcitxKey_greater) return "ŋ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseCharacter == "i") {
|
|
||||||
if (silModifier == FcitxKey_equal) return "ɪ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseCharacter == "e") {
|
|
||||||
if (silModifier == FcitxKey_equal) return "ə";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseCharacter == "a") {
|
|
||||||
if (silModifier == FcitxKey_less) return "æ";
|
|
||||||
if (silModifier == FcitxKey_equal) return "ɑ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseCharacter == "o") {
|
|
||||||
if (silModifier == FcitxKey_less) return "ɔ";
|
|
||||||
if (silModifier == FcitxKey_equal) return "ɒ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseCharacter == "u") {
|
|
||||||
if (silModifier == FcitxKey_greater) return "ʌ";
|
|
||||||
if (silModifier == FcitxKey_less) return "ʊ";
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSILModifier(fcitx::KeySym key)
|
bool isSILModifier(fcitx::KeySym key)
|
||||||
|
@ -71,27 +270,62 @@ bool isSILModifier(fcitx::KeySym key)
|
||||||
|
|
||||||
void SILState::handleAlphaKey(fcitx::Key key)
|
void SILState::handleAlphaKey(fcitx::Key key)
|
||||||
{
|
{
|
||||||
// TODO: Don't make this a special case, have a better system for handling
|
// This ignores all non-alpha keys, with built-in flags and by banning
|
||||||
// this!
|
// modifiers ranges.
|
||||||
if (key.check(FcitxKey_colon)) {
|
if (key.isModifier() || key.isCursorMove() || key.hasModifier() ||
|
||||||
m_buffer.type("ː");
|
((key.sym() & 0xff00) == 0xff00 || (key.sym() & 0xff00) == 0xfe00))
|
||||||
m_lastKey.reset();
|
return;
|
||||||
|
|
||||||
|
if (keyToRepeatConversion.contains(key.sym())) {
|
||||||
|
auto keyConversions = keyToRepeatConversion.at(key.sym());
|
||||||
|
if (m_repeatedKeyCount == 0) {
|
||||||
|
m_buffer.type(keyConversions[m_repeatedKeyCount++]);
|
||||||
|
m_repeatedKey = key.sym();
|
||||||
|
} else {
|
||||||
|
if (m_repeatedKey == key.sym()) {
|
||||||
|
if (m_repeatedKeyCount >= keyConversions.size()) {
|
||||||
|
m_buffer.type(keyConversions[0]);
|
||||||
|
m_repeatedKeyCount = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0;
|
||||||
|
i < keyConversions[m_repeatedKeyCount - 1].size();
|
||||||
|
++i)
|
||||||
|
m_buffer.backspace();
|
||||||
|
m_buffer.type(keyConversions[m_repeatedKeyCount++]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_buffer.type(keyConversions[0]);
|
||||||
|
m_repeatedKey = key.sym();
|
||||||
|
m_repeatedKeyCount = 0;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSILModifier(key.sym()) || !m_lastKey.has_value()) {
|
m_repeatedKeyCount = 0;
|
||||||
m_lastKey = key.toString();
|
m_repeatedKey = FcitxKey_None;
|
||||||
|
|
||||||
|
auto lastCharacter = getLastCharacter();
|
||||||
|
|
||||||
|
if (key.sym() == FcitxKey_asciicircum &&
|
||||||
|
superscripts.contains(lastCharacter)) {
|
||||||
|
m_buffer.backspace();
|
||||||
|
m_buffer.type(superscripts.at(lastCharacter));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSILModifier(key.sym()) || lastCharacter.empty()) {
|
||||||
m_buffer.type(key.sym());
|
m_buffer.type(key.sym());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSILModifier(key.sym())) {
|
if (isSILModifier(key.sym())) {
|
||||||
auto ipaChar = getIPAForSIL(*m_lastKey, key.sym());
|
auto ipaChar = getIPAForSIL(lastCharacter, key.sym());
|
||||||
if (ipaChar) {
|
if (ipaChar) {
|
||||||
m_buffer.backspace();
|
m_buffer.backspace();
|
||||||
m_buffer.type(*ipaChar);
|
m_buffer.type(*ipaChar);
|
||||||
}
|
}
|
||||||
m_lastKey.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,9 +344,9 @@ void SILState::keyEvent(fcitx::KeyEvent& keyEvent)
|
||||||
reset();
|
reset();
|
||||||
} else if (keyEvent.key().check(FcitxKey_BackSpace))
|
} else if (keyEvent.key().check(FcitxKey_BackSpace))
|
||||||
m_buffer.backspace();
|
m_buffer.backspace();
|
||||||
else if (keyEvent.key().isSimple())
|
else
|
||||||
handleAlphaKey(keyEvent.key());
|
handleAlphaKey(keyEvent.key());
|
||||||
// m_buffer.type(keyEvent.key().sym());
|
|
||||||
updateUI();
|
updateUI();
|
||||||
|
|
||||||
keyEvent.filterAndAccept();
|
keyEvent.filterAndAccept();
|
||||||
|
@ -135,6 +369,15 @@ void SILState::updateUI()
|
||||||
m_ic->updatePreedit();
|
m_ic->updatePreedit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string SILState::getLastCharacter()
|
||||||
|
{
|
||||||
|
// TODO: Use fcitx::InputBuffer::charAt once
|
||||||
|
// https://github.com/fcitx/fcitx5/issues/965 is fixed.
|
||||||
|
if (m_buffer.size() == 0) return "";
|
||||||
|
auto text = m_buffer.userInput();
|
||||||
|
return std::string(&text[m_buffer.cursorByChar() - 1], 1);
|
||||||
|
}
|
||||||
|
|
||||||
SILEngine::SILEngine(fcitx::Instance* instance)
|
SILEngine::SILEngine(fcitx::Instance* instance)
|
||||||
: m_factory(
|
: m_factory(
|
||||||
[this](fcitx::InputContext& ic) { return new SILState(this, &ic); })
|
[this](fcitx::InputContext& ic) { return new SILState(this, &ic); })
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <fcitx-utils/utf8.h>
|
||||||
|
|
||||||
class SILEngine;
|
class SILEngine;
|
||||||
|
|
||||||
class SILState : public fcitx::InputContextProperty
|
class SILState : public fcitx::InputContextProperty
|
||||||
|
@ -37,12 +39,15 @@ class SILState : public fcitx::InputContextProperty
|
||||||
updateUI();
|
updateUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string getLastCharacter();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SILEngine* m_engine;
|
SILEngine* m_engine;
|
||||||
fcitx::InputContext* m_ic;
|
fcitx::InputContext* m_ic;
|
||||||
fcitx::InputBuffer m_buffer{{fcitx::InputBufferOption::NoOption}};
|
fcitx::InputBuffer m_buffer{{fcitx::InputBufferOption::NoOption}};
|
||||||
|
|
||||||
std::optional<std::string> m_lastKey{};
|
fcitx::KeySym m_repeatedKey = FcitxKey_None;
|
||||||
|
uint16_t m_repeatedKeyCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SILEngine : public fcitx::InputMethodEngineV2
|
class SILEngine : public fcitx::InputMethodEngineV2
|
||||||
|
|
Loading…
Reference in a new issue