mirror of
https://github.com/fcitx5-android/fcitx5-android.git
synced 2026-08-05 11:19:58 +08:00
236 lines
7.8 KiB
C++
236 lines
7.8 KiB
C++
#include <fcitx/addonfactory.h>
|
|
#include <fcitx/addonmanager.h>
|
|
#include <fcitx/inputcontextmanager.h>
|
|
#include <fcitx/inputmethodengine.h>
|
|
#include <fcitx/inputpanel.h>
|
|
#include <fcitx/instance.h>
|
|
|
|
#include "androidfrontend.h"
|
|
|
|
namespace fcitx {
|
|
|
|
class AndroidInputContext : public InputContext {
|
|
public:
|
|
AndroidInputContext(AndroidFrontend *frontend,
|
|
InputContextManager &inputContextManager,
|
|
const std::string &program)
|
|
: InputContext(inputContextManager, program), frontend_(frontend) {
|
|
created();
|
|
}
|
|
|
|
~AndroidInputContext() override { destroy(); }
|
|
|
|
[[nodiscard]] const char *frontend() const override { return "androidfrontend"; }
|
|
|
|
void commitStringImpl(const std::string &text) override {
|
|
frontend_->commitString(text);
|
|
}
|
|
|
|
void forwardKeyImpl(const ForwardKeyEvent &key) override {
|
|
frontend_->forwardKey(key.rawKey(), key.isRelease());
|
|
}
|
|
|
|
void deleteSurroundingTextImpl(int offset, unsigned int size) override {
|
|
FCITX_INFO() << "DeleteSurrounding: " << offset << " " << size;
|
|
}
|
|
|
|
void updatePreeditImpl() override {
|
|
// if PreeditInApplication is disabled, this function is not called
|
|
// moved to `updateClientSideUIImpl`
|
|
}
|
|
|
|
void updateClientSideUIImpl() override {
|
|
InputPanel &ip = inputPanel();
|
|
frontend_->updatePreedit(
|
|
frontend_->instance()->outputFilter(this, ip.preedit()),
|
|
frontend_->instance()->outputFilter(this, ip.clientPreedit())
|
|
);
|
|
frontend_->updateInputPanelAux(filterText(ip.auxUp()), filterText(ip.auxDown()));
|
|
std::vector<std::string> candidates;
|
|
const auto &list = ip.candidateList();
|
|
if (list) {
|
|
const auto &bulk = list->toBulk();
|
|
if (bulk) {
|
|
const int size = bulk->totalSize();
|
|
for (int i = 0; i < size; i++) {
|
|
auto &candidate = bulk->candidateFromAll(i);
|
|
// maybe unnecessary; I don't see anywhere using `CandidateWord::setPlaceHolder`
|
|
// if (candidate.isPlaceHolder()) continue;
|
|
candidates.emplace_back(std::move(filterText(candidate.text())));
|
|
}
|
|
} else {
|
|
const int size = list->size();
|
|
for (int i = 0; i < size; i++) {
|
|
candidates.emplace_back(std::move(filterText(list->candidate(i).text())));
|
|
}
|
|
}
|
|
}
|
|
frontend_->updateCandidateList(candidates);
|
|
}
|
|
|
|
void selectCandidate(int idx) {
|
|
const auto &list = inputPanel().candidateList();
|
|
if (!list) {
|
|
return;
|
|
}
|
|
const auto &bulk = list->toBulk();
|
|
try {
|
|
if (bulk) {
|
|
bulk->candidateFromAll(idx).select(this);
|
|
} else {
|
|
list->candidate(idx).select(this);
|
|
}
|
|
} catch (const std::invalid_argument &e) {
|
|
FCITX_WARN() << "selectCandidate index out of range";
|
|
}
|
|
}
|
|
|
|
private:
|
|
AndroidFrontend *frontend_;
|
|
|
|
std::string filterText(const fcitx::Text &orig) {
|
|
return frontend_->instance()->outputFilter(this, orig).toString();
|
|
}
|
|
};
|
|
|
|
AndroidFrontend::AndroidFrontend(Instance *instance)
|
|
: instance_(instance) {
|
|
eventHandlers_.emplace_back(instance_->watchEvent(
|
|
EventType::InputContextInputMethodActivated,
|
|
EventWatcherPhase::Default,
|
|
[this](Event &event) { imChangeCallback(); }
|
|
));
|
|
eventHandlers_.emplace_back(instance_->watchEvent(
|
|
EventType::InputContextUpdateUI,
|
|
EventWatcherPhase::Default,
|
|
[this](Event &event) {
|
|
auto &e = static_cast<InputContextUpdateUIEvent &>(event);
|
|
if (e.component() == UserInterfaceComponent::StatusArea) {
|
|
statusAreaUpdateCallback();
|
|
}
|
|
}
|
|
));
|
|
}
|
|
|
|
AndroidFrontend::~AndroidFrontend() = default;
|
|
|
|
ICUUID AndroidFrontend::createInputContext(const std::string &program) {
|
|
auto *ic = new AndroidInputContext(this, instance_->inputContextManager(), program);
|
|
return ic->uuid();
|
|
}
|
|
|
|
void AndroidFrontend::destroyInputContext(ICUUID uuid) {
|
|
auto *ic = instance_->inputContextManager().findByUUID(uuid);
|
|
delete ic;
|
|
}
|
|
|
|
void AndroidFrontend::keyEvent(ICUUID uuid, const Key &key, bool isRelease, const int64_t timestamp) {
|
|
auto *ic = instance_->inputContextManager().findByUUID(uuid);
|
|
if (!ic) {
|
|
return;
|
|
}
|
|
KeyEvent keyEvent(ic, key, isRelease);
|
|
ic->keyEvent(keyEvent);
|
|
if (!keyEvent.accepted()) {
|
|
auto sym = key.sym();
|
|
keyEventCallback(sym, key.states(), fcitx::Key::keySymToUnicode(sym), isRelease, timestamp);
|
|
}
|
|
}
|
|
|
|
void AndroidFrontend::forwardKey(const Key &key, bool isRelease) {
|
|
auto sym = key.sym();
|
|
keyEventCallback(sym, key.states(), fcitx::Key::keySymToUnicode(sym), isRelease, 0);
|
|
}
|
|
|
|
void AndroidFrontend::commitString(const std::string &str) {
|
|
commitStringCallback(str);
|
|
}
|
|
|
|
void AndroidFrontend::updateCandidateList(const std::vector<std::string> &candidates) {
|
|
candidateListCallback(candidates);
|
|
}
|
|
|
|
void AndroidFrontend::updatePreedit(const Text &preedit, const Text &clientPreedit) {
|
|
preeditCallback(preedit.toString(), preedit.cursor(), clientPreedit.toString(), clientPreedit.cursor());
|
|
}
|
|
|
|
void AndroidFrontend::updateInputPanelAux(const std::string &auxUp, const std::string &auxDown) {
|
|
inputPanelAuxCallback(auxUp, auxDown);
|
|
}
|
|
|
|
void AndroidFrontend::selectCandidate(ICUUID uuid, int idx) {
|
|
auto *ic = dynamic_cast<AndroidInputContext *>(instance_->inputContextManager().findByUUID(uuid));
|
|
ic->selectCandidate(idx);
|
|
}
|
|
|
|
bool AndroidFrontend::isInputPanelEmpty(ICUUID uuid) {
|
|
auto *ic = instance_->inputContextManager().findByUUID(uuid);
|
|
return ic->inputPanel().empty();
|
|
}
|
|
|
|
void AndroidFrontend::resetInputContext(ICUUID uuid) {
|
|
auto *ic = instance_->inputContextManager().findByUUID(uuid);
|
|
ic->reset();
|
|
}
|
|
|
|
void AndroidFrontend::repositionCursor(ICUUID uuid, int position) {
|
|
auto ic = instance_->inputContextManager().findByUUID(uuid);
|
|
auto engine = instance_->inputMethodEngine(ic);
|
|
InvokeActionEvent event(InvokeActionEvent::Action::LeftClick, position, ic);
|
|
engine->invokeAction(*(instance_->inputMethodEntry(ic)), event);
|
|
}
|
|
|
|
void AndroidFrontend::focusInputContext(ICUUID uuid, bool focus) {
|
|
auto ic = instance_->inputContextManager().findByUUID(uuid);
|
|
bool focused = ic->hasFocus();
|
|
if (focus && !focused) {
|
|
ic->focusIn();
|
|
} else if (!focus && focused) {
|
|
ic->focusOut();
|
|
}
|
|
}
|
|
|
|
void AndroidFrontend::setCapabilityFlags(ICUUID uuid, uint64_t flag) {
|
|
auto ic = instance_->inputContextManager().findByUUID(uuid);
|
|
ic->setCapabilityFlags(CapabilityFlags{flag});
|
|
}
|
|
|
|
void AndroidFrontend::setCandidateListCallback(const CandidateListCallback &callback) {
|
|
candidateListCallback = callback;
|
|
}
|
|
|
|
void AndroidFrontend::setCommitStringCallback(const CommitStringCallback &callback) {
|
|
commitStringCallback = callback;
|
|
}
|
|
|
|
void AndroidFrontend::setPreeditCallback(const PreeditCallback &callback) {
|
|
preeditCallback = callback;
|
|
}
|
|
|
|
void AndroidFrontend::setInputPanelAuxCallback(const InputPanelAuxCallback &callback) {
|
|
inputPanelAuxCallback = callback;
|
|
}
|
|
|
|
void AndroidFrontend::setKeyEventCallback(const KeyEventCallback &callback) {
|
|
keyEventCallback = callback;
|
|
}
|
|
|
|
void AndroidFrontend::setInputMethodChangeCallback(const InputMethodChangeCallback &callback) {
|
|
imChangeCallback = callback;
|
|
}
|
|
|
|
void AndroidFrontend::setStatusAreaUpdateCallback(const StatusAreaUpdateCallback &callback) {
|
|
statusAreaUpdateCallback = callback;
|
|
}
|
|
|
|
class AndroidFrontendFactory : public AddonFactory {
|
|
public:
|
|
AddonInstance *create(AddonManager *manager) override {
|
|
return new AndroidFrontend(manager->instance());
|
|
}
|
|
};
|
|
|
|
} // namespace fcitx
|
|
|
|
FCITX_ADDON_FACTORY(fcitx::AndroidFrontendFactory)
|