Initial commit: added current work/poc

This commit is contained in:
Yuki Joou 2023-11-12 00:39:52 +01:00
commit 9514c8d53e
9 changed files with 299 additions and 0 deletions

23
.clang-format Normal file
View file

@ -0,0 +1,23 @@
BasedOnStyle: Microsoft
UseTab: ForIndentation
TabWidth: 4
BreakBeforeBraces: Linux
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: WithoutElse
PointerAlignment: Left
AlignAfterOpenBracket: Align
BinPackArguments: false
BinPackParameters: false
AlignConsecutiveAssignments: Consecutive
AlignConsecutiveBitFields: Consecutive
AlignConsecutiveDeclarations: Consecutive
AlignConsecutiveMacros: Consecutive
AlignEscapedNewlines: Right
AlignOperands: AlignAfterOperator
AlignTrailingComments: true
ColumnLimit: 80

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build/

8
CMakeLists.txt Normal file
View file

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.21)
project(fcitx5-ipa-sil)
find_package(Fcitx5Core REQUIRED)
# Setup some compiler option that is generally useful and compatible with Fcitx 5 (C++17)
include("${FCITX_INSTALL_CMAKECONFIG_DIR}/Fcitx5Utils/Fcitx5CompilerSettings.cmake")
add_subdirectory(src)

28
README.md Normal file
View file

@ -0,0 +1,28 @@
# fcitx5-ipa-sil
This is an IME for [Fctix 5](https://fcitx-im.org/wiki/Fcitx_5) that brings support for the [SIL IPA](https://keyman.com/keyboards/sil_ipa) input method.
This allows the user to type IPA characters fairly easly by inputing only ASCII.
Here are a few examples of SIL to IPA conversions:
| IPA | SIL |
|-----|------|
| ə | `e=` |
| ʃ | `s=` |
| ŋ | `n>` |
| a | `a` |
**This is still a work-in-progress**! This is mostly a rough draft to allow me to follow my phonetics class on a computer. I didn't put much thought into getting it to work for other people's needs! Feel free to send me suggestions though \:)
## Building & trying it out!
```console
$ mkdir -p build/
$ cd build/
$ cmake ../ -DCMAKE_INSTALL_PREFIX=/usr
$ make
$ sudo make install
```
You can then restart fcitx, and add the "SIL IPA" input method to your available input methods!

12
src/CMakeLists.txt Normal file
View file

@ -0,0 +1,12 @@
add_library(ipa-sil SHARED ipa-sil.cpp)
target_link_libraries(ipa-sil PRIVATE Fcitx5::Core)
set_target_properties(ipa-sil PROPERTIES PREFIX "")
install(TARGETS ipa-sil DESTINATION "${FCITX_INSTALL_LIBDIR}/fcitx5")
# Addon config file
# We need additional layer of conversion because we want PROJECT_VERSION in it.
configure_file(ipa-sil-addon.conf.in ipa-sil-addon.conf)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ipa-sil-addon.conf" RENAME ipa-sil.conf DESTINATION "${FCITX_INSTALL_PKGDATADIR}/addon")
# Input Method registration file
install(FILES "ipa-sil.conf" DESTINATION "${FCITX_INSTALL_PKGDATADIR}/inputmethod")

View file

@ -0,0 +1,8 @@
[Addon]
Name=IPA SIL
Category=InputMethod
Version=@PROJECT_VERSION@
Library=ipa-sil
Type=SharedLibrary
OnDemand=True
Configurable=True

13
src/ipa-sil.conf Normal file
View file

@ -0,0 +1,13 @@
[InputMethod]
# Translatable name of the input method
Name=IPA SIL
# Icon name
Icon=fcitx-ipa-sil
# A short label that present the name of input method
Label=sil
# ISO 639 language code
LangCode=en_GB
# Match addon name
Addon=ipa-sil
# Whether this input method support customization
# Configurable=True

138
src/ipa-sil.cpp Normal file
View file

@ -0,0 +1,138 @@
/*
* SPDX-FileCopyrightText: 2023 Yuki Joou <yukijoou [a] kemonomimi [-] gay>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "ipa-sil.hpp"
#include <fcitx/inputpanel.h>
#include <fcitx/instance.h>
std::optional<std::string> getIPAForSIL(std::string baseCharacter,
fcitx::KeySym silModifier)
{
// TODO: Use a proper data structure, like a hash map
// Even better: load that data off of a file!
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 "ə";
}
return {};
}
bool isSILModifier(fcitx::KeySym key)
{
return key == FcitxKey_less || key == FcitxKey_greater ||
key == FcitxKey_equal;
}
void SILState::handleAlphaKey(fcitx::Key key)
{
if (!isSILModifier(key.sym()) || !m_lastKey.has_value()) {
m_lastKey = key.toString();
m_buffer.type(key.sym());
return;
}
if (isSILModifier(key.sym())) {
auto ipaChar = getIPAForSIL(*m_lastKey, key.sym());
if (ipaChar) {
m_buffer.backspace();
m_buffer.type(*ipaChar);
}
m_lastKey.reset();
}
}
void SILState::keyEvent(fcitx::KeyEvent& keyEvent)
{
// If it's not a simple letter key, and we have no text input yet, let it
// through.
if (m_buffer.empty() && !keyEvent.key().isSimple()) return;
if (keyEvent.key().check(FcitxKey_Return)) {
m_ic->commitString(m_buffer.userInput());
reset();
} else if (keyEvent.key().check(FcitxKey_space)) {
m_ic->commitString(m_buffer.userInput());
m_ic->commitString(" ");
reset();
} else if (keyEvent.key().check(FcitxKey_BackSpace))
m_buffer.backspace();
else if (keyEvent.key().isSimple())
handleAlphaKey(keyEvent.key());
// m_buffer.type(keyEvent.key().sym());
updateUI();
keyEvent.filterAndAccept();
}
void SILState::updateUI()
{
auto& inputPanel = m_ic->inputPanel();
inputPanel.reset();
if (m_ic->capabilityFlags().test(fcitx::CapabilityFlag::Preedit)) {
fcitx::Text preedit(m_buffer.userInput(),
fcitx::TextFormatFlag::HighLight);
inputPanel.setClientPreedit(preedit);
} else {
fcitx::Text preedit(m_buffer.userInput());
inputPanel.setPreedit(preedit);
}
m_ic->updateUserInterface(fcitx::UserInterfaceComponent::InputPanel);
m_ic->updatePreedit();
}
SILEngine::SILEngine(fcitx::Instance* instance)
: m_factory(
[this](fcitx::InputContext& ic) { return new SILState(this, &ic); })
{
instance->inputContextManager().registerProperty("silState", &m_factory);
}
void SILEngine::keyEvent(const fcitx::InputMethodEntry&,
fcitx::KeyEvent& keyEvent)
{
if (keyEvent.isRelease() || keyEvent.key().states()) {
return;
}
auto ic = keyEvent.inputContext();
auto* state = ic->propertyFor(&m_factory);
state->keyEvent(keyEvent);
}
void SILEngine::reset(const fcitx::InputMethodEntry&,
fcitx::InputContextEvent& event)
{
auto* state = event.inputContext()->propertyFor(&m_factory);
state->reset();
}
FCITX_ADDON_FACTORY(SILEngineFactory);

68
src/ipa-sil.hpp Normal file
View file

@ -0,0 +1,68 @@
/*
* SPDX-FileCopyrightText: 2023 Yuki Joou <yukijoou [a] kemonomimi [-] gay>
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#pragma once
#include <fcitx-utils/inputbuffer.h>
#include <fcitx/addonfactory.h>
#include <fcitx/addonmanager.h>
#include <fcitx/inputcontextproperty.h>
#include <fcitx/inputmethodengine.h>
#include <fcitx/instance.h>
#include <optional>
#include <string>
class SILEngine;
class SILState : public fcitx::InputContextProperty
{
public:
SILState(SILEngine* engine, fcitx::InputContext* ic)
: m_engine(engine), m_ic(ic)
{
}
void handleAlphaKey(fcitx::Key);
void keyEvent(fcitx::KeyEvent& keyEvent);
void updateUI();
void reset()
{
m_buffer.clear();
updateUI();
}
private:
SILEngine* m_engine;
fcitx::InputContext* m_ic;
fcitx::InputBuffer m_buffer{{fcitx::InputBufferOption::NoOption}};
std::optional<std::string> m_lastKey{};
};
class SILEngine : public fcitx::InputMethodEngineV2
{
public:
SILEngine(fcitx::Instance*);
void keyEvent(const fcitx::InputMethodEntry&, fcitx::KeyEvent&) override;
void reset(const fcitx::InputMethodEntry&,
fcitx::InputContextEvent&) override;
private:
fcitx::FactoryFor<SILState> m_factory;
};
class SILEngineFactory : public fcitx::AddonFactory
{
fcitx::AddonInstance* create(fcitx::AddonManager* manager) override
{
FCITX_UNUSED(manager);
return new SILEngine(manager->instance());
}
};