Skip to content

Show floating CandidatesView for hardware keyboard #571

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .idea/dictionaries/project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 52 additions & 3 deletions app/src/main/cpp/androidfrontend/androidfrontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,12 @@ class AndroidInputContext : public InputContextV2 {
filterText(ip.auxUp()),
filterText(ip.auxDown())
);
}

void updateCandidatesBulk() {
std::vector<std::string> candidates;
int size = 0;
const auto &list = ip.candidateList();
const auto &list = inputPanel().candidateList();
if (list) {
const auto &bulk = list->toBulk();
if (bulk) {
Expand Down Expand Up @@ -96,6 +99,32 @@ class AndroidInputContext : public InputContextV2 {
frontend_->updateCandidateList(candidates, size);
}

void updateCandidatesPaged() {
const auto &list = inputPanel().candidateList();
if (!list) {
PagedCandidateEntity empty({}, -1, CandidateLayoutHint::NotSet, false, false);
frontend_->updatePagedCandidate(empty);
return;
}
int cursorIndex = list->cursorIndex();
CandidateLayoutHint layoutHint = list->layoutHint();
bool hasPrev = false;
bool hasNext = false;
const auto &pageable = list->toPageable();
if (pageable) {
hasPrev = pageable->hasPrev();
hasNext = pageable->hasNext();
}
int size = list->size();
std::vector<CandidateEntity> candidates;
candidates.reserve(size);
for (int i = 0; i < size; i++) {
candidates.emplace_back(list->candidate(i), list->label(i));
}
PagedCandidateEntity paged(candidates, cursorIndex, layoutHint, hasPrev, hasNext);
frontend_->updatePagedCandidate(paged);
}

bool selectCandidate(int idx) {
const auto &list = inputPanel().candidateList();
if (!list) {
Expand Down Expand Up @@ -220,7 +249,8 @@ AndroidFrontend::AndroidFrontend(Instance *instance)
focusGroup_("android", instance->inputContextManager()),
activeIC_(nullptr),
icCache_(),
eventHandlers_() {
eventHandlers_(),
pagingMode_(0) {
eventHandlers_.emplace_back(instance_->watchEvent(
EventType::InputContextInputMethodActivated,
EventWatcherPhase::Default,
Expand All @@ -236,7 +266,14 @@ AndroidFrontend::AndroidFrontend(Instance *instance)
auto &e = static_cast<InputContextFlushUIEvent &>(event);
switch (e.component()) {
case UserInterfaceComponent::InputPanel: {
if (activeIC_) activeIC_->updateInputPanel();
if (activeIC_) {
activeIC_->updateInputPanel();
if (pagingMode_ == 0) {
activeIC_->updateCandidatesBulk();
} else {
activeIC_->updateCandidatesPaged();
}
}
break;
}
case UserInterfaceComponent::StatusArea: {
Expand Down Expand Up @@ -368,6 +405,14 @@ void AndroidFrontend::showToast(const std::string &s) {
toastCallback(s);
}

void AndroidFrontend::setCandidatePagingMode(const int mode) {
pagingMode_ = mode;
}

void AndroidFrontend::updatePagedCandidate(const PagedCandidateEntity &paged) {
pagedCandidateCallback(paged);
}

void AndroidFrontend::setCommitStringCallback(const CommitStringCallback &callback) {
commitStringCallback = callback;
}
Expand Down Expand Up @@ -400,6 +445,10 @@ void AndroidFrontend::setToastCallback(const ToastCallback &callback) {
toastCallback = callback;
}

void AndroidFrontend::setPagedCandidateCallback(const PagedCandidateCallback &callback) {
pagedCandidateCallback = callback;
}

class AndroidFrontendFactory : public AddonFactory {
public:
AddonInstance *create(AddonManager *manager) override {
Expand Down
14 changes: 11 additions & 3 deletions app/src/main/cpp/androidfrontend/androidfrontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* SPDX-License-Identifier: LGPL-2.1-or-later
* SPDX-FileCopyrightText: Copyright 2021-2023 Fcitx5 for Android Contributors
*/
#ifndef _FCITX5_ANDROID_ANDROIDFRONTEND_H_
#define _FCITX5_ANDROID_ANDROIDFRONTEND_H_

#ifndef FCITX5_ANDROID_ANDROIDFRONTEND_H
#define FCITX5_ANDROID_ANDROIDFRONTEND_H

#include <fcitx/instance.h>
#include <fcitx/addoninstance.h>
Expand All @@ -27,6 +28,7 @@ class AndroidFrontend : public AddonInstance {
void updateClientPreedit(const Text &clientPreedit);
void updateInputPanel(const Text &preedit, const Text &auxUp, const Text &auxDown);
void releaseInputContext(const int uid);
void updatePagedCandidate(const PagedCandidateEntity &paged);

void keyEvent(const Key &key, bool isRelease, const int timestamp);
void forwardKey(const Key &key, bool isRelease);
Expand All @@ -44,6 +46,7 @@ class AndroidFrontend : public AddonInstance {
void triggerCandidateAction(const int idx, const int actionIdx);
void deleteSurrounding(const int before, const int after);
void showToast(const std::string &s);
void setCandidatePagingMode(const int mode);
void setCandidateListCallback(const CandidateListCallback &callback);
void setCommitStringCallback(const CommitStringCallback &callback);
void setPreeditCallback(const ClientPreeditCallback &callback);
Expand All @@ -53,6 +56,7 @@ class AndroidFrontend : public AddonInstance {
void setStatusAreaUpdateCallback(const StatusAreaUpdateCallback &callback);
void setDeleteSurroundingCallback(const DeleteSurroundingCallback &callback);
void setToastCallback(const ToastCallback &callback);
void setPagedCandidateCallback(const PagedCandidateCallback &callback);

private:
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, keyEvent);
Expand All @@ -69,6 +73,7 @@ class AndroidFrontend : public AddonInstance {
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, getCandidateActions);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, triggerCandidateAction);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, showToast);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setCandidatePagingMode);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setCandidateListCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setCommitStringCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setPreeditCallback);
Expand All @@ -78,12 +83,14 @@ class AndroidFrontend : public AddonInstance {
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setStatusAreaUpdateCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setDeleteSurroundingCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setToastCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setPagedCandidateCallback);

Instance *instance_;
FocusGroup focusGroup_;
AndroidInputContext *activeIC_;
InputContextCache icCache_;
std::vector<std::unique_ptr<HandlerTableEntry<EventHandler>>> eventHandlers_;
int pagingMode_;

CandidateListCallback candidateListCallback = [](const std::vector<std::string> &, const int) {};
CommitStringCallback commitStringCallback = [](const std::string &, const int) {};
Expand All @@ -94,7 +101,8 @@ class AndroidFrontend : public AddonInstance {
StatusAreaUpdateCallback statusAreaUpdateCallback = [] {};
DeleteSurroundingCallback deleteSurroundingCallback = [](const int, const int) {};
ToastCallback toastCallback = [](const std::string &) {};
PagedCandidateCallback pagedCandidateCallback = [](const PagedCandidateEntity &) {};
};
} // namespace fcitx

#endif //_FCITX5_ANDROID_ANDROIDFRONTEND_H_
#endif //FCITX5_ANDROID_ANDROIDFRONTEND_H
9 changes: 9 additions & 0 deletions app/src/main/cpp/androidfrontend/androidfrontend_public.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <fcitx/candidateaction.h>
#include <fcitx-utils/key.h>

#include "../helper-types.h"

typedef std::function<void(const std::vector<std::string> &, const int)> CandidateListCallback;
typedef std::function<void(const std::string &, const int)> CommitStringCallback;
typedef std::function<void(const fcitx::Text &)> ClientPreeditCallback;
Expand All @@ -19,6 +21,7 @@ typedef std::function<void()> InputMethodChangeCallback;
typedef std::function<void()> StatusAreaUpdateCallback;
typedef std::function<void(const int, const int)> DeleteSurroundingCallback;
typedef std::function<void(const std::string &)> ToastCallback;
typedef std::function<void(const PagedCandidateEntity &)> PagedCandidateCallback;

FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, keyEvent,
void(const fcitx::Key &, bool isRelease, const int timestamp))
Expand Down Expand Up @@ -62,6 +65,9 @@ FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, triggerCandidateAction,
FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, showToast,
void(const std::string &))

FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setCandidatePagingMode,
void(const int))

FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setCandidateListCallback,
void(const CandidateListCallback &))

Expand Down Expand Up @@ -89,4 +95,7 @@ FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setDeleteSurroundingCallback,
FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setToastCallback,
void(const ToastCallback &))

FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setPagedCandidateCallback,
void(const PagedCandidateCallback &))

#endif // FCITX5_ANDROID_ANDROIDFRONTEND_PUBLIC_H
37 changes: 37 additions & 0 deletions app/src/main/cpp/helper-types.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
#include <fcitx/menu.h>
#include <fcitx/inputcontext.h>
#include <fcitx/candidateaction.h>
#include <fcitx/inputmethodengine.h>
#include <fcitx/inputmethodentry.h>
#include <fcitx/candidatelist.h>

#include <utility>

class InputMethodStatus {
public:
Expand Down Expand Up @@ -102,4 +107,36 @@ class CandidateActionEntity {
isChecked(act.isChecked()) {}
};

class CandidateEntity {
public:
std::string label;
std::string text;
std::string comment;

explicit CandidateEntity(const fcitx::CandidateWord &c, const fcitx::Text &label) :
label(label.toString()),
text(c.text().toString()),
comment(c.comment().toString()) {}
};

class PagedCandidateEntity {
public:
std::vector<CandidateEntity> candidates;
int cursorIndex;
fcitx::CandidateLayoutHint layoutHint;
bool hasPrev;
bool hasNext;

explicit PagedCandidateEntity(std::vector<CandidateEntity> candidates,
int cursorIndex,
fcitx::CandidateLayoutHint layoutHint,
bool hasPrev,
bool hasNext) :
candidates(std::move(candidates)),
cursorIndex(cursorIndex),
layoutHint(layoutHint),
hasPrev(hasPrev),
hasNext(hasNext) {}
};

#endif //FCITX5_ANDROID_HELPER_TYPES_H
6 changes: 6 additions & 0 deletions app/src/main/cpp/jni-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ class GlobalRefSingleton {
jclass CandidateAction;
jmethodID CandidateActionInit;

jclass Candidate;
jmethodID CandidateInit;

explicit GlobalRefSingleton(JavaVM *jvm_) : jvm(jvm_) {
JNIEnv *env;
jvm->AttachCurrentThread(&env, nullptr);
Expand Down Expand Up @@ -190,6 +193,9 @@ class GlobalRefSingleton {

CandidateAction = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("org/fcitx/fcitx5/android/core/CandidateAction")));
CandidateActionInit = env->GetMethodID(CandidateAction, "<init>", "(ILjava/lang/String;ZLjava/lang/String;ZZ)V");

Candidate = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("org/fcitx/fcitx5/android/core/FcitxEvent$Candidate")));
CandidateInit = env->GetMethodID(Candidate, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
}

[[nodiscard]] JEnv AttachEnv() const { return JEnv(jvm); }
Expand Down
31 changes: 31 additions & 0 deletions app/src/main/cpp/native-lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,10 @@ class Fcitx {
return p_frontend->call<fcitx::IAndroidFrontend::triggerCandidateAction>(idx, actionIdx);
}

void setCandidatePagingMode(int mode) {
return p_frontend->call<fcitx::IAndroidFrontend::setCandidatePagingMode>(mode);
}

void save() {
p_instance->save();
}
Expand Down Expand Up @@ -678,6 +682,25 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(
env->SetObjectArrayElement(vararg, 0, intArray);
env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->HandleFcitxEvent, 8, *vararg);
};
auto pagedCandidateCallback = [](const PagedCandidateEntity &paged) {
auto env = GlobalRef->AttachEnv();
const int size = static_cast<int>(paged.candidates.size());
auto candidatesArray = JRef<jobjectArray>(env, env->NewObjectArray(size, GlobalRef->Candidate, nullptr));
for (int i = 0; i < size; ++i) {
env->SetObjectArrayElement(candidatesArray, i, candidateEntityToObject(env, paged.candidates[i]));
}
auto cursorIndex = JRef(env, env->NewObject(GlobalRef->Integer, GlobalRef->IntegerInit, paged.cursorIndex));
auto layoutHint = JRef(env, env->NewObject(GlobalRef->Integer, GlobalRef->IntegerInit, static_cast<int>(paged.layoutHint)));
auto hasPrev = JRef(env, env->NewObject(GlobalRef->Boolean, GlobalRef->BooleanInit, paged.hasPrev));
auto hasNext = JRef(env, env->NewObject(GlobalRef->Boolean, GlobalRef->BooleanInit, paged.hasNext));
auto vararg = JRef<jobjectArray>(env, env->NewObjectArray(5, GlobalRef->Object, nullptr));
env->SetObjectArrayElement(vararg, 0, candidatesArray);
env->SetObjectArrayElement(vararg, 1, cursorIndex);
env->SetObjectArrayElement(vararg, 2, layoutHint);
env->SetObjectArrayElement(vararg, 3, hasPrev);
env->SetObjectArrayElement(vararg, 4, hasNext);
env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->HandleFcitxEvent, 9, *vararg);
};
auto toastCallback = [](const std::string &s) {
auto env = GlobalRef->AttachEnv();
env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->ShowToast, *JString(env, s));
Expand All @@ -697,6 +720,7 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(
androidfrontend->template call<fcitx::IAndroidFrontend::setInputMethodChangeCallback>(imChangeCallback);
androidfrontend->template call<fcitx::IAndroidFrontend::setStatusAreaUpdateCallback>(statusAreaUpdateCallback);
androidfrontend->template call<fcitx::IAndroidFrontend::setDeleteSurroundingCallback>(deleteSurroundingCallback);
androidfrontend->template call<fcitx::IAndroidFrontend::setPagedCandidateCallback>(pagedCandidateCallback);
androidfrontend->template call<fcitx::IAndroidFrontend::setToastCallback>(toastCallback);
});
FCITX_INFO() << "Finishing startup";
Expand Down Expand Up @@ -1050,6 +1074,13 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_triggerFcitxCandidateAction(JNIEnv *env
Fcitx::Instance().triggerCandidateAction(idx, action_idx);
}

extern "C"
JNIEXPORT void JNICALL
Java_org_fcitx_fcitx5_android_core_Fcitx_setFcitxCandidatePagingMode(JNIEnv *env, jclass clazz, jint mode) {
RETURN_IF_NOT_RUNNING
Fcitx::Instance().setCandidatePagingMode(mode);
}

extern "C"
JNIEXPORT void JNICALL
Java_org_fcitx_fcitx5_android_core_Fcitx_loopOnce(JNIEnv *env, jclass clazz) {
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/cpp/object-conversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,13 @@ jobject fcitxCandidateActionToObject(JNIEnv *env, const CandidateActionEntity &a
return obj;
}

jobject candidateEntityToObject(JNIEnv *env, const CandidateEntity &c) {
auto obj = env->NewObject(GlobalRef->Candidate, GlobalRef->CandidateInit,
*JString(env, c.label),
*JString(env, c.text),
*JString(env, c.comment)
);
return obj;
}

#endif //FCITX5_ANDROID_OBJECT_CONVERSION_H
6 changes: 6 additions & 0 deletions app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ class Fcitx(private val context: Context) : FcitxAPI, FcitxLifecycleOwner {
override suspend fun triggerCandidateAction(idx: Int, actionIdx: Int) =
withFcitxContext { triggerFcitxCandidateAction(idx, actionIdx) }

override suspend fun setCandidatePagingMode(mode: Int) =
withFcitxContext { setFcitxCandidatePagingMode(mode) }

init {
if (lifecycle.currentState != FcitxLifecycle.State.STOPPED)
throw IllegalAccessException("Fcitx5 has already been created!")
Expand Down Expand Up @@ -339,6 +342,9 @@ class Fcitx(private val context: Context) : FcitxAPI, FcitxLifecycleOwner {
@JvmStatic
external fun triggerFcitxCandidateAction(idx: Int, actionIdx: Int)

@JvmStatic
external fun setFcitxCandidatePagingMode(mode: Int)

@JvmStatic
external fun loopOnce()

Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/org/fcitx/fcitx5/android/core/FcitxAPI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,6 @@ interface FcitxAPI {
suspend fun getCandidateActions(idx: Int): Array<CandidateAction>
suspend fun triggerCandidateAction(idx: Int, actionIdx: Int)

suspend fun setCandidatePagingMode(mode: Int)

}
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy