Make use of new commitStringWithCursor API

This commit is contained in:
Rocka 2023-10-11 21:41:34 +08:00
parent 2b64744e61
commit 6e22d34eb7
No known key found for this signature in database
GPG Key ID: 28031158FFDD6853
13 changed files with 82 additions and 51 deletions

View File

@ -10,13 +10,13 @@
namespace fcitx {
class AndroidInputContext : public InputContext {
class AndroidInputContext : public InputContextV2 {
public:
AndroidInputContext(AndroidFrontend *frontend,
InputContextManager &inputContextManager,
int uid,
const std::string &pkgName)
: InputContext(inputContextManager, pkgName),
: InputContextV2(inputContextManager, pkgName),
frontend_(frontend),
uid_(uid) {
created();
@ -30,7 +30,11 @@ public:
[[nodiscard]] const char *frontend() const override { return "androidfrontend"; }
void commitStringImpl(const std::string &text) override {
frontend_->commitString(text);
frontend_->commitString(text, -1);
}
void commitStringWithCursorImpl(const std::string &text, size_t cursor) override {
frontend_->commitString(text, static_cast<int>(cursor));
}
void forwardKeyImpl(const ForwardKeyEvent &key) override {
@ -202,8 +206,8 @@ void AndroidFrontend::forwardKey(const Key &key, bool isRelease) {
keyEventCallback(sym, key.states(), Key::keySymToUnicode(sym), isRelease, -1);
}
void AndroidFrontend::commitString(const std::string &str) {
commitStringCallback(str);
void AndroidFrontend::commitString(const std::string &str, const int cursor) {
commitStringCallback(str, cursor);
}
void AndroidFrontend::updateCandidateList(const std::vector<std::string> &candidates, const int size) {

View File

@ -17,7 +17,7 @@ public:
Instance *instance() { return instance_; }
void updateCandidateList(const std::vector<std::string> &candidates, const int size);
void commitString(const std::string &str);
void commitString(const std::string &str, const int cursor);
void updateClientPreedit(const Text &clientPreedit);
void updateInputPanel(const Text &preedit, const Text &auxUp, const Text &auxDown);
void releaseInputContext(const int uid);
@ -77,7 +77,7 @@ private:
void handleStatusAreaUpdate();
CandidateListCallback candidateListCallback = [](const std::vector<std::string> &, const int) {};
CommitStringCallback commitStringCallback = [](const std::string &) {};
CommitStringCallback commitStringCallback = [](const std::string &, const int) {};
ClientPreeditCallback preeditCallback = [](const Text &) {};
InputPanelCallback inputPanelAuxCallback = [](const fcitx::Text &, const fcitx::Text &, const Text &) {};
KeyEventCallback keyEventCallback = [](const int, const uint32_t, const uint32_t, const bool, const int) {};

View File

@ -5,7 +5,7 @@
#include <fcitx-utils/key.h>
typedef std::function<void(const std::vector<std::string> &, const int)> CandidateListCallback;
typedef std::function<void(const std::string &)> CommitStringCallback;
typedef std::function<void(const std::string &, const int)> CommitStringCallback;
typedef std::function<void(const fcitx::Text &)> ClientPreeditCallback;
typedef std::function<void(const fcitx::Text &, const fcitx::Text &, const fcitx::Text &)> InputPanelCallback;
typedef std::function<void(const int, const uint32_t, const uint32_t, const bool, const int)> KeyEventCallback;

View File

@ -10,8 +10,6 @@
#include "androidkeyboard.h"
#define FCITX_KEYBOARD_MAX_BUFFER 20
namespace fcitx {
namespace {
@ -85,7 +83,7 @@ void AndroidKeyboardEngine::keyEvent(const InputMethodEntry &entry, KeyEvent &ev
// check if we can select candidate.
if (auto candList = inputContext->inputPanel().candidateList()) {
int idx = key.keyListIndex(selectionKeys_);
const int idx = key.keyListIndex(selectionKeys_);
if (idx >= 0 && idx < candList->size()) {
event.filterAndAccept();
candList->candidate(idx).select(inputContext);
@ -93,9 +91,9 @@ void AndroidKeyboardEngine::keyEvent(const InputMethodEntry &entry, KeyEvent &ev
}
}
bool validSym = isValidSym(key);
const bool validSym = isValidSym(key);
static KeyList FCITX_HYPHEN_APOS = {Key(FcitxKey_minus), Key(FcitxKey_apostrophe)};
static const KeyList FCITX_HYPHEN_APOS = {Key(FcitxKey_minus), Key(FcitxKey_apostrophe)};
// check for valid character
if (key.isSimple() || validSym) {
// prepend space before input next word
@ -173,7 +171,7 @@ std::vector<InputMethodEntry> AndroidKeyboardEngine::listInputMethods() {
void AndroidKeyboardEngine::reloadConfig() {
readAsIni(config_, ConfPath);
selectionKeys_.clear();
KeySym syms[] = {
const std::array<KeySym, 10> syms{
FcitxKey_1, FcitxKey_2, FcitxKey_3, FcitxKey_4, FcitxKey_5,
FcitxKey_6, FcitxKey_7, FcitxKey_8, FcitxKey_9, FcitxKey_0,
};
@ -252,7 +250,7 @@ void AndroidKeyboardEngine::updateCandidate(const InputMethodEntry &entry, Input
results = spell()->call<ISpell::hintForDisplay>(entry.languageCode(),
SpellProvider::Default,
state->buffer_.userInput(),
20);
SpellCandidateSize);
}
auto candidateList = std::make_unique<CommonCandidateList>();
for (const auto &result: results) {
@ -267,10 +265,10 @@ void AndroidKeyboardEngine::updateCandidate(const InputMethodEntry &entry, Input
}
void AndroidKeyboardEngine::updateUI(InputContext *inputContext) {
auto *state = inputContext->propertyFor(&factory_);
Text preedit(preeditString(inputContext), TextFormatFlag::Underline);
preedit.setCursor(static_cast<int>(state->buffer_.cursorByChar()));
inputContext->inputPanel().setClientPreedit(preedit);
auto [preedit, cursor] = preeditWithCursor(inputContext);
Text clientPreedit(preedit, TextFormatFlag::Underline);
clientPreedit.setCursor(static_cast<int>(cursor));
inputContext->inputPanel().setClientPreedit(clientPreedit);
// we don't want preedit here ...
// if (!inputContext->capabilityFlags().test(CapabilityFlag::Preedit)) {
// inputContext->inputPanel().setPreedit(preedit);
@ -296,7 +294,7 @@ bool AndroidKeyboardEngine::updateBuffer(InputContext *inputContext, const std::
}
auto &buffer = state->buffer_;
auto preedit = preeditString(inputContext);
auto [preedit, cursor] = preeditWithCursor(inputContext);
if (preedit != buffer.userInput()) {
buffer.clear();
buffer.type(preedit);
@ -304,7 +302,7 @@ bool AndroidKeyboardEngine::updateBuffer(InputContext *inputContext, const std::
buffer.type(chr);
if (buffer.size() >= FCITX_KEYBOARD_MAX_BUFFER) {
if (buffer.size() >= MaxBufferSize) {
commitBuffer(inputContext);
return true;
}
@ -314,11 +312,15 @@ bool AndroidKeyboardEngine::updateBuffer(InputContext *inputContext, const std::
}
void AndroidKeyboardEngine::commitBuffer(InputContext *inputContext) {
auto preedit = preeditString(inputContext);
auto [preedit, cursor] = preeditWithCursor(inputContext);
if (preedit.empty()) {
return;
}
inputContext->commitString(preedit);
if (auto icv2 = dynamic_cast<InputContextV2 *>(inputContext)) {
icv2->commitStringWithCursor(preedit, cursor);
} else {
inputContext->commitString(preedit);
}
resetState(inputContext);
inputContext->inputPanel().reset();
inputContext->updatePreedit();
@ -330,13 +332,13 @@ bool AndroidKeyboardEngine::supportHint(const std::string &language) {
return hasSpell;
}
std::string AndroidKeyboardEngine::preeditString(InputContext *inputContext) {
std::pair<std::string, size_t> AndroidKeyboardEngine::preeditWithCursor(InputContext *inputContext) {
auto *state = inputContext->propertyFor(&factory_);
return state->buffer_.userInput();
return {state->buffer_.userInput(), state->buffer_.cursorByChar()};
}
void AndroidKeyboardEngine::invokeActionImpl(const InputMethodEntry &entry, InvokeActionEvent &event) {
int cursor = event.cursor();
const int cursor = event.cursor();
auto inputContext = event.inputContext();
auto *state = inputContext->propertyFor(&factory_);
if (event.action() != InvokeActionEvent::Action::LeftClick

View File

@ -50,6 +50,9 @@ struct AndroidKeyboardEngineState : public InputContextProperty {
class AndroidKeyboardEngine final : public InputMethodEngineV3 {
public:
static int constexpr MaxBufferSize = 20;
static int constexpr SpellCandidateSize = 20;
AndroidKeyboardEngine(Instance *instance);
~AndroidKeyboardEngine() = default;
@ -100,7 +103,7 @@ public:
private:
bool supportHint(const std::string &language);
std::string preeditString(InputContext *inputContext);
std::pair<std::string, size_t> preeditWithCursor(InputContext *inputContext);
Instance *instance_;
AndroidKeyboardEngineConfig config_;

View File

@ -564,10 +564,12 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(JNIEnv *env, jclass clazz,
env->SetObjectArrayElement(vararg, 1, *candidatesArray);
env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->HandleFcitxEvent, 0, *vararg);
};
auto commitStringCallback = [](const std::string &str) {
auto commitStringCallback = [](const std::string &str, const int cursor) {
auto env = GlobalRef->AttachEnv();
auto vararg = JRef<jobjectArray>(env, env->NewObjectArray(1, GlobalRef->String, nullptr));
auto stringCursor = JRef(env, env->NewObject(GlobalRef->Integer, GlobalRef->IntegerInit, cursor));
auto vararg = JRef<jobjectArray>(env, env->NewObjectArray(2, GlobalRef->Object, nullptr));
env->SetObjectArrayElement(vararg, 0, JString(env, str));
env->SetObjectArrayElement(vararg, 1, stringCursor);
env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->HandleFcitxEvent, 1, *vararg);
};
auto preeditCallback = [](const fcitx::Text &clientPreedit) {

View File

@ -35,10 +35,12 @@ sealed class FcitxEvent<T>(open val data: T) {
}
}
data class CommitStringEvent(override val data: String) :
FcitxEvent<String>(data) {
data class CommitStringEvent(override val data: Data) :
FcitxEvent<CommitStringEvent.Data>(data) {
override val eventType: EventType
get() = EventType.Commit
data class Data(val text: String, val cursor: Int)
}
data class ClientPreeditEvent(override val data: FormattedText) :
@ -157,7 +159,12 @@ sealed class FcitxEvent<T>(open val data: T) {
params[1] as Array<String>
)
)
EventType.Commit -> CommitStringEvent(params[0] as String)
EventType.Commit -> CommitStringEvent(
CommitStringEvent.Data(
params[0] as String,
params[1] as Int
)
)
EventType.ClientPreedit -> ClientPreeditEvent(params[0] as FormattedText)
EventType.InputPanel -> InputPanelEvent(
InputPanelEvent.Data(

View File

@ -30,8 +30,6 @@ class AppPrefs(private val sharedPreferences: SharedPreferences) {
inner class Advanced : ManagedPreferenceCategory(R.string.advanced, sharedPreferences) {
val ignoreSystemCursor = switch(R.string.ignore_sys_cursor, "ignore_system_cursor", true)
val resetCursorAfterCommit =
switch(R.string.reset_cursor_after_commit, "reset_cursor_after_commit", true)
val hideKeyConfig = switch(R.string.hide_key_config, "hide_key_config", true)
val disableAnimation = switch(R.string.disable_animation, "disable_animation", false)
val vivoKeypressWorkaround = switch(
@ -363,7 +361,6 @@ class AppPrefs(private val sharedPreferences: SharedPreferences) {
internal.verboseLog,
internal.editorInfoInspector,
advanced.ignoreSystemCursor,
advanced.resetCursorAfterCommit,
advanced.disableAnimation,
advanced.vivoKeypressWorkaround
).forEach {

View File

@ -85,7 +85,6 @@ class FcitxInputMethodService : LifecycleInputMethodService() {
private var highlightColor: Int = 0x66008577 // material_deep_teal_500 with alpha 0.4
private val ignoreSystemCursor by AppPrefs.getInstance().advanced.ignoreSystemCursor
private val resetCursor by AppPrefs.getInstance().advanced.resetCursorAfterCommit
private val inlineSuggestions by AppPrefs.getInstance().keyboard.inlineSuggestions
@ -126,7 +125,7 @@ class FcitxInputMethodService : LifecycleInputMethodService() {
private fun handleFcitxEvent(event: FcitxEvent<*>) {
when (event) {
is FcitxEvent.CommitStringEvent -> {
commitText(event.data)
commitText(event.data.text, event.data.cursor)
}
is FcitxEvent.KeyEvent -> event.data.let event@{
if (it.states.virtual) {
@ -223,24 +222,31 @@ class FcitxInputMethodService : LifecycleInputMethodService() {
}
}
fun commitText(text: String) {
fun commitText(text: String, cursor: Int = -1) {
val ic = currentInputConnection ?: return
val targetCursor = if (cursor == -1) text.length else cursor
val currentCursor = selection.current.start - composing.start
// when composing text equals commit content, finish composing text as-is
if (composing.isNotEmpty() && composingText.toString() == text) {
// when composing text equals commit content, finish composing text as-is
val cursor = composing.end
resetComposingState()
currentInputConnection?.finishComposingText()
if (resetCursor) {
selection.predict(cursor)
currentInputConnection?.setSelection(cursor, cursor)
if (targetCursor != currentCursor) {
val c = composing.start + targetCursor
selection.predict(c)
ic.setSelection(c, c)
}
resetComposingState()
ic.finishComposingText()
return
}
// committed text should replace composing (if any), replace selected range (if any),
// or simply prepend before cursor
val start = if (composing.isEmpty()) selection.latest.start else composing.start
selection.predict(start + text.length)
resetComposingState()
currentInputConnection?.commitText(text, 1)
ic.commitText(text, 1)
if (targetCursor != currentCursor) {
val c = start + targetCursor
selection.predict(c)
ic.setSelection(c, c)
}
}
private fun sendDownKeyEvent(eventTime: Long, keyEventCode: Int, metaState: Int = 0) {
@ -596,6 +602,18 @@ class FcitxInputMethodService : LifecycleInputMethodService() {
ic.endBatchEdit()
}
/**
* Finish composing text and leave cursor position as-is.
* Also updates internal composing state of [FcitxInputMethodService].
*/
fun finishComposing() {
val ic = currentInputConnection ?: return
if (composing.isEmpty()) return
composing.clear()
composingText = FormattedText.Empty
ic.finishComposingText()
}
@SuppressLint("RestrictedApi")
@RequiresApi(Build.VERSION_CODES.R)
override fun onCreateInlineSuggestionsRequest(uiExtras: Bundle): InlineSuggestionsRequest? {

View File

@ -59,7 +59,7 @@ class CommonKeyActionListener :
reset()
} else if (inputMethodEntryCached.uniqueName.let { it == "keyboard-us" || it == "unikey" }) {
// androidkeyboard clears composing on reset, but we want to commit it as-is
service.currentInputConnection?.finishComposingText()
service.finishComposing()
reset()
} else {
if (!select(0)) reset()

View File

@ -249,5 +249,4 @@
<string name="i_do_not_need_it">我不需要</string>
<string name="grant_permission">授予权限</string>
<string name="clear_clp_db_confirm">剪贴板数据库将会被完全清空,包括已置顶的条目。继续吗?</string>
<string name="reset_cursor_after_commit">提交文本后重置光标位置</string>
</resources>

View File

@ -249,5 +249,4 @@
<string name="i_do_not_need_it">I don\'t need it</string>
<string name="grant_permission">Grant permission</string>
<string name="clear_clp_db_confirm">Clipboard database would be completely cleared, including pinned items. Processed?</string>
<string name="reset_cursor_after_commit">Reset cursor position after commit text</string>
</resources>

@ -1 +1 @@
Subproject commit ce98537ffe0a1f642c8369b3e6183ce536b0fdf4
Subproject commit edef0e17f4dc96498f609e6bd5e4c8e98e4973d7