Send candidate list tab actions on input panel update

This commit is contained in:
Rocka 2026-05-30 21:22:23 +08:00
parent 87bde649ed
commit 873c08255c
No known key found for this signature in database
GPG Key ID: 28031158FFDD6853
11 changed files with 118 additions and 20 deletions

View File

@ -65,10 +65,22 @@ public:
void updateInputPanel() {
const InputPanel &ip = inputPanel();
std::vector<CandidateActionEntity> tabs;
auto list = ip.candidateList();
if (list) {
auto tabbed = ip.candidateList()->toTabbed();
if (tabbed) {
auto tabActions = tabbed->tabActions();
for (const auto &a: tabActions) {
tabs.emplace_back(a);
}
}
}
frontend_->updateInputPanel(
filterText(ip.preedit()),
filterText(ip.auxUp()),
filterText(ip.auxDown())
filterText(ip.auxDown()),
tabs
);
}
@ -237,6 +249,18 @@ public:
}
}
void triggerTabAction(const int idx) {
const auto &list = inputPanel().candidateList();
if (!list) return;
const auto &tabbed = list->toTabbed();
if (!tabbed) return;
try {
tabbed->triggerTabAction(idx);
} catch (const std::exception &e) {
FCITX_WARN() << "triggerTabAction(" << idx << ") failed:" << e.what();
}
}
void offsetCandidatePage(int delta) {
if (delta == 0) {
return;
@ -360,8 +384,8 @@ void AndroidFrontend::updateClientPreedit(const Text &clientPreedit) {
preeditCallback(clientPreedit);
}
void AndroidFrontend::updateInputPanel(const Text &preedit, const Text &auxUp, const Text &auxDown) {
inputPanelCallback(preedit, auxUp, auxDown);
void AndroidFrontend::updateInputPanel(const Text &preedit, const Text &auxUp, const Text &auxDown, const std::vector<CandidateActionEntity> &tabs) {
inputPanelCallback(preedit, auxUp, auxDown, tabs);
}
void AndroidFrontend::releaseInputContext(const int uid) {
@ -387,6 +411,11 @@ void AndroidFrontend::triggerCandidateAction(const int idx, const int actionIdx)
activeIC_->triggerCandidateAction(idx, actionIdx);
}
void AndroidFrontend::triggerTabAction(const int idx) {
if (!activeIC_) return;
activeIC_->triggerTabAction(idx);
}
bool AndroidFrontend::isInputPanelEmpty() {
if (!activeIC_) return true;
return activeIC_->inputPanel().empty();
@ -475,6 +504,11 @@ void AndroidFrontend::offsetCandidatePage(int delta) {
activeIC_->offsetCandidatePage(delta);
}
void AndroidFrontend::triggerCandidateListTabAction(int id) {
if (!activeIC_) return;
activeIC_->triggerTabAction(id);
}
void AndroidFrontend::setCommitStringCallback(const CommitStringCallback &callback) {
commitStringCallback = callback;
}
@ -483,7 +517,7 @@ void AndroidFrontend::setPreeditCallback(const ClientPreeditCallback &callback)
preeditCallback = callback;
}
void AndroidFrontend::setInputPanelAuxCallback(const InputPanelCallback &callback) {
void AndroidFrontend::setInputPanelCallback(const InputPanelCallback &callback) {
inputPanelCallback = callback;
}

View File

@ -26,7 +26,7 @@ public:
void updateCandidateList(const std::vector<CandidateEntity> &candidates, int total);
void commitString(const std::string &str, int cursor);
void updateClientPreedit(const Text &clientPreedit);
void updateInputPanel(const Text &preedit, const Text &auxUp, const Text &auxDown);
void updateInputPanel(const Text &preedit, const Text &auxUp, const Text &auxDown, const std::vector<CandidateActionEntity> &tabs);
void releaseInputContext(int uid);
void updatePagedCandidate(const PagedCandidateEntity &paged);
@ -44,14 +44,16 @@ public:
std::vector<CandidateEntity> getCandidates(int offset, int limit);
std::vector<CandidateActionEntity> getCandidateActions(int idx);
void triggerCandidateAction(int idx, int actionIdx);
void triggerTabAction(int idx);
void deleteSurrounding(int before, int after);
void showToast(const std::string &s);
void setCandidatePagingMode(int mode);
void offsetCandidatePage(int delta);
void triggerCandidateListTabAction(int id);
void setCandidateListCallback(const CandidateListCallback &callback);
void setCommitStringCallback(const CommitStringCallback &callback);
void setPreeditCallback(const ClientPreeditCallback &callback);
void setInputPanelAuxCallback(const InputPanelCallback &callback);
void setInputPanelCallback(const InputPanelCallback &callback);
void setKeyEventCallback(const KeyEventCallback &callback);
void setInputMethodChangeCallback(const InputMethodChangeCallback &callback);
void setStatusAreaUpdateCallback(const StatusAreaUpdateCallback &callback);
@ -74,13 +76,15 @@ private:
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, getCandidates);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, getCandidateActions);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, triggerCandidateAction);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, triggerTabAction);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, showToast);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setCandidatePagingMode);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, offsetCandidatePage);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, triggerCandidateListTabAction);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setCandidateListCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setCommitStringCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setPreeditCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setInputPanelAuxCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setInputPanelCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setKeyEventCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setInputMethodChangeCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setStatusAreaUpdateCallback);
@ -99,7 +103,7 @@ private:
CandidateListCallback candidateListCallback = [](const std::vector<CandidateEntity> &, const int) {};
CommitStringCallback commitStringCallback = [](const std::string &, const int) {};
ClientPreeditCallback preeditCallback = [](const Text &) {};
InputPanelCallback inputPanelCallback = [](const fcitx::Text &, const fcitx::Text &, const Text &) {};
InputPanelCallback inputPanelCallback = [](const fcitx::Text &, const fcitx::Text &, const Text &, const std::vector<CandidateActionEntity> &) {};
KeyEventCallback keyEventCallback = [](const int, const uint32_t, const uint32_t, const bool, const int) {};
InputMethodChangeCallback imChangeCallback = [](const InputMethodStatus &) {};
StatusAreaUpdateCallback statusAreaUpdateCallback = [](const std::vector<ActionEntity> &, const InputMethodStatus &) {};

View File

@ -15,7 +15,7 @@
typedef std::function<void(const std::vector<CandidateEntity> &, const int)> CandidateListCallback;
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 fcitx::Text &, const fcitx::Text &, const fcitx::Text &, const std::vector<CandidateActionEntity> &)> InputPanelCallback;
typedef std::function<void(const int, const uint32_t, const uint32_t, const bool, const int)> KeyEventCallback;
typedef std::function<void(const InputMethodStatus &)> InputMethodChangeCallback;
typedef std::function<void(const std::vector<ActionEntity> &, const InputMethodStatus &)> StatusAreaUpdateCallback;
@ -63,6 +63,9 @@ FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, getCandidateActions,
FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, triggerCandidateAction,
void(const int, const int))
FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, triggerTabAction,
void(const int))
FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, showToast,
void(const std::string &))
@ -72,6 +75,9 @@ FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setCandidatePagingMode,
FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, offsetCandidatePage,
void(int))
FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, triggerCandidateListTabAction,
void(int))
FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setCandidateListCallback,
void(const CandidateListCallback &))
@ -81,7 +87,7 @@ FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setCommitStringCallback,
FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setPreeditCallback,
void(const ClientPreeditCallback &))
FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setInputPanelAuxCallback,
FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setInputPanelCallback,
void(const InputPanelCallback &))
FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setKeyEventCallback,

View File

@ -434,6 +434,10 @@ public:
return p_frontend->call<fcitx::IAndroidFrontend::offsetCandidatePage>(delta);
}
void triggerCandidateListTabAction(int id) {
return p_frontend->call<fcitx::IAndroidFrontend::triggerCandidateListTabAction>(id);
}
void save() {
p_instance->save();
}
@ -614,12 +618,18 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(
env->SetObjectArrayElement(vararg, 0, fcitxTextToJObject(env, clientPreedit));
env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->HandleFcitxEvent, 2, *vararg);
};
auto inputPanelAuxCallback = [](const fcitx::Text &preedit, const fcitx::Text &auxUp, const fcitx::Text &auxDown) {
auto inputPanelCallback = [](const fcitx::Text &preedit, const fcitx::Text &auxUp, const fcitx::Text &auxDown, const std::vector<CandidateActionEntity> &tabs) {
auto env = GlobalRef->AttachEnv();
auto vararg = JRef<jobjectArray>(env, env->NewObjectArray(3, GlobalRef->FormattedText, nullptr));
auto vararg = JRef<jobjectArray>(env, env->NewObjectArray(4, GlobalRef->Object, nullptr));
env->SetObjectArrayElement(vararg, 0, fcitxTextToJObject(env, preedit));
env->SetObjectArrayElement(vararg, 1, fcitxTextToJObject(env, auxUp));
env->SetObjectArrayElement(vararg, 2, fcitxTextToJObject(env, auxDown));
auto tabsArray = JRef<jobjectArray>(env, env->NewObjectArray(static_cast<int>(tabs.size()), GlobalRef->CandidateAction, nullptr));
int i = 0;
for (const auto &tab: tabs) {
env->SetObjectArrayElement(tabsArray, i++, fcitxCandidateActionToObject(env, tab));
}
env->SetObjectArrayElement(vararg, 3, *tabsArray);
env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->HandleFcitxEvent, 3, *vararg);
};
auto readyCallback = []() {
@ -713,7 +723,7 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(
androidfrontend->template call<fcitx::IAndroidFrontend::setCandidateListCallback>(candidateListCallback);
androidfrontend->template call<fcitx::IAndroidFrontend::setCommitStringCallback>(commitStringCallback);
androidfrontend->template call<fcitx::IAndroidFrontend::setPreeditCallback>(preeditCallback);
androidfrontend->template call<fcitx::IAndroidFrontend::setInputPanelAuxCallback>(inputPanelAuxCallback);
androidfrontend->template call<fcitx::IAndroidFrontend::setInputPanelCallback>(inputPanelCallback);
androidfrontend->template call<fcitx::IAndroidFrontend::setKeyEventCallback>(keyEventCallback);
androidfrontend->template call<fcitx::IAndroidFrontend::setInputMethodChangeCallback>(imChangeCallback);
androidfrontend->template call<fcitx::IAndroidFrontend::setStatusAreaUpdateCallback>(statusAreaUpdateCallback);
@ -1096,6 +1106,13 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_offsetFcitxCandidatePage(JNIEnv *env, j
Fcitx::Instance().offsetCandidatePage(delta);
}
extern "C"
JNIEXPORT void JNICALL
Java_org_fcitx_fcitx5_android_core_Fcitx_triggerFcitxCandidateListTabAction(JNIEnv *env, jclass clazz, jint id) {
RETURN_IF_NOT_RUNNING
Fcitx::Instance().triggerCandidateListTabAction(id);
}
extern "C"
JNIEXPORT void JNICALL
Java_org_fcitx_fcitx5_android_core_Fcitx_loopOnce(JNIEnv *env, jclass clazz) {

View File

@ -194,6 +194,9 @@ class Fcitx(private val context: Context) : FcitxAPI, FcitxLifecycleOwner {
override suspend fun offsetCandidatePage(delta: Int) =
withFcitxContext { offsetFcitxCandidatePage(delta) }
override suspend fun triggerCandidateListTabAction(id: Int) =
withFcitxContext { triggerFcitxCandidateListTabAction(id) }
init {
if (lifecycle.currentState != FcitxLifecycle.State.STOPPED)
throw IllegalAccessException("Fcitx5 has already been created!")
@ -378,6 +381,9 @@ class Fcitx(private val context: Context) : FcitxAPI, FcitxLifecycleOwner {
@JvmStatic
external fun offsetFcitxCandidatePage(delta: Int)
@JvmStatic
external fun triggerFcitxCandidateListTabAction(id: Int)
@JvmStatic
external fun loopOnce()

View File

@ -108,4 +108,6 @@ interface FcitxAPI {
suspend fun setCandidatePagingMode(mode: Int)
suspend fun offsetCandidatePage(delta: Int)
suspend fun triggerCandidateListTabAction(id: Int)
}

View File

@ -61,9 +61,37 @@ sealed class FcitxEvent<T>(open val data: T) {
data class Data(
val preedit: FormattedText,
val auxUp: FormattedText,
val auxDown: FormattedText
val auxDown: FormattedText,
val tabs: Array<CandidateAction>
) {
constructor() : this(FormattedText.Empty, FormattedText.Empty, FormattedText.Empty)
constructor() : this(
FormattedText.Empty,
FormattedText.Empty,
FormattedText.Empty,
emptyArray()
)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Data
if (preedit != other.preedit) return false
if (auxUp != other.auxUp) return false
if (auxDown != other.auxDown) return false
if (!tabs.contentEquals(other.tabs)) return false
return true
}
override fun hashCode(): Int {
var result = preedit.hashCode()
result = 31 * result + auxUp.hashCode()
result = 31 * result + auxDown.hashCode()
result = 31 * result + tabs.contentHashCode()
return result
}
}
}
@ -261,7 +289,8 @@ sealed class FcitxEvent<T>(open val data: T) {
InputPanelEvent.Data(
params[0] as FormattedText,
params[1] as FormattedText,
params[2] as FormattedText
params[2] as FormattedText,
params[3] as Array<CandidateAction>
)
)
EventType.Ready -> ReadyEvent()

@ -1 +1 @@
Subproject commit 940b3b3db521a6fcb14e4d67ea522217cf244c3b
Subproject commit 3a94a96006f8e67665fa9bb1b9ef4602415ddd04

@ -1 +1 @@
Subproject commit e5809953e5c2d31c285f8b0c182e219962a12d4f
Subproject commit 52978aa88b4979c057fe94d68291883d9c27085f

@ -1 +1 @@
Subproject commit ac7a6b937d69acf812a58eea6926182ceb3e502c
Subproject commit 04eff1334eb2eab45245c492bf5b85be4c4eda6b

@ -1 +1 @@
Subproject commit c16d4e52c21d98ae71ffce355b395631a05acc42
Subproject commit 7197ff04d43e192a87da157bebaaf386cf98ed3c