mirror of
https://github.com/fcitx5-android/fcitx5-android.git
synced 2026-08-03 10:33:50 +08:00
Make use of new commitStringWithCursor API
This commit is contained in:
parent
2b64744e61
commit
6e22d34eb7
@ -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) {
|
||||
|
||||
@ -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) {};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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? {
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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>
|
||||
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user