Optimize the support for virtual keyboard

1. There are two kinds of virtual keyboard at least.

2. The input method engine may support one or two function mode: full or limited.
   a) The full function mode is the default function mode. All the input method engine
   which does not implement InputMethodEngineV4 or its ascendants supports the full
   function mode.
   b) The limited function mode is a dedicated function mode which is used for the true
   virtual keyboard. The input method engine which implements InputMethodEngineV4 or its
   ascendants should tell fcitx which function mode it supports. That is, it may support
   full function mode or limited function mode or both.

3. The two different virtual keyboard use different function mode of input method engine.
   The virtual keyboard should tell fcitx which function mode it needs. Fcitx will decide
   which function mode is used. For example, if the virtual keyboard tells fcitx that it
   needs limited function mode, but the input method engine does not support that mode,
   then fcitx will use full function mode for compitibility reason.
This commit is contained in:
liulinsong 2023-04-27 12:55:58 +08:00 committed by hanteng
parent bbae9fc7f6
commit ab01c802e8
7 changed files with 161 additions and 27 deletions

View File

@ -319,6 +319,12 @@ bool InputContext::keyEvent(KeyEvent &event) {
return result;
}
bool InputContext::isFunctionModeSupported(FunctionMode functionMode) {
FCITX_D();
return d->isFunctionModeSupported(functionMode);
}
bool InputContext::virtualKeyboardEvent(VirtualKeyboardEvent &event) {
FCITX_D();
RETURN_IF_HAS_NO_FOCUS(false);

View File

@ -41,6 +41,8 @@ class InputPanel;
class StatusArea;
typedef std::function<bool(InputContext *ic)> InputContextVisitor;
enum class FunctionMode : uint32_t { Full = 1 << 0, Limited = 1 << 1 };
/**
* An input context represents a client of Fcitx. It can be a Window, or a text
* field depending on the application.
@ -133,6 +135,8 @@ public:
/// Send a key event to current input context.
bool keyEvent(KeyEvent &event);
bool isFunctionModeSupported(FunctionMode functionMode);
/**
* Send a virtual keyboard event to current input context.
*

View File

@ -13,6 +13,7 @@
#include <fcitx/inputcontext.h>
#include <fcitx/inputcontextmanager.h>
#include <fcitx/inputcontextproperty.h>
#include <fcitx/inputmethodengine.h>
#include <fcitx/inputpanel.h>
#include <fcitx/instance.h>
#include <fcitx/statusarea.h>
@ -132,6 +133,14 @@ public:
InputContextProperty *property(int slot) { return properties_[slot].get(); }
bool isFunctionModeSupported(FunctionMode functionMode) {
FCITX_Q();
auto *instance = manager_.instance();
auto *engine = instance->inputMethodEngine(q);
return engine->isFunctionModeSupported(functionMode);
}
InputContextManager &manager_;
FocusGroup *group_;
InputPanel inputPanel_;

View File

@ -5,7 +5,6 @@
*
*/
#include "inputmethodengine.h"
#include "inputcontext.h"
#include "inputpanel.h"
namespace fcitx {
@ -28,18 +27,43 @@ std::string InputMethodEngine::subModeLabel(const InputMethodEntry &entry,
void InputMethodEngine::virtualKeyboardEvent(
const InputMethodEntry &entry, VirtualKeyboardEvent &virtualKeyboardEvent) {
if (auto *this4 = dynamic_cast<InputMethodEngineV4 *>(this)) {
this4->virtualKeyboardEventImpl(entry, virtualKeyboardEvent);
} else if (auto virtualKeyEvent = virtualKeyboardEvent.toKeyEvent()) {
keyEvent(entry, *virtualKeyEvent);
// TODO: revisit the default action.
if (virtualKeyEvent->accepted()) {
virtualKeyboardEvent.accept();
} else if (virtualKeyboardEvent.text() != "") {
virtualKeyboardEvent.inputContext()->commitString(
virtualKeyboardEvent.text());
}
auto *this4 = dynamic_cast<InputMethodEngineV4 *>(this);
if (this4 == nullptr) {
FCITX_WARN() << "InputMethodEngine::virtualKeyboardEvent is called."
<< "But there is no valid InputMethodEngineV4 instance."
<< "So it returns early.";
return;
}
this4->virtualKeyboardEventImpl(entry, virtualKeyboardEvent);
}
FunctionMode InputMethodEngine::getFunctionMode() const {
const auto *this4 = dynamic_cast<const InputMethodEngineV4 *>(this);
if (this4 == nullptr) {
return FunctionMode::Full;
}
return this4->getFunctionModeImpl();
}
void InputMethodEngine::setFunctionMode(FunctionMode functionMode) {
auto *this4 = dynamic_cast<InputMethodEngineV4 *>(this);
if (this4 == nullptr) {
return;
}
this4->setFunctionModeImpl(functionMode);
}
bool InputMethodEngine::isFunctionModeSupported(
FunctionMode functionMode) const {
const auto *this4 = dynamic_cast<const InputMethodEngineV4 *>(this);
if (this4 == nullptr) {
return FunctionMode::Full == functionMode;
}
return this4->isFunctionModeSupportedImpl(functionMode);
}
void defaultInvokeActionBehavior(InvokeActionEvent &event) {

View File

@ -11,6 +11,7 @@
#include <fcitx/event.h>
#include <fcitx/inputmethodentry.h>
#include "fcitxcore_export.h"
#include "inputcontext.h"
namespace fcitx {
@ -182,6 +183,12 @@ public:
*/
void virtualKeyboardEvent(const InputMethodEntry &entry,
VirtualKeyboardEvent &VirtualKeyboardEvent);
FunctionMode getFunctionMode() const;
void setFunctionMode(FunctionMode functionMode);
bool isFunctionModeSupported(FunctionMode functionMode) const;
};
class FCITXCORE_EXPORT InputMethodEngineV2 : public InputMethodEngine {
@ -207,6 +214,13 @@ public:
virtual void
virtualKeyboardEventImpl(const InputMethodEntry &entry,
VirtualKeyboardEvent &VirtualKeyboardEvent) = 0;
virtual FunctionMode getFunctionModeImpl() const = 0;
virtual void setFunctionModeImpl(FunctionMode functionMode) = 0;
virtual bool
isFunctionModeSupportedImpl(FunctionMode functionMode) const = 0;
};
} // namespace fcitx

View File

@ -897,18 +897,24 @@ Instance::Instance(int argc, char **argv) {
}
inputState->hideInputMethodInfo();
}));
d->eventWatchers_.emplace_back(
watchEvent(EventType::InputContextKeyEvent,
EventWatcherPhase::InputMethod, [this](Event &event) {
auto &keyEvent = static_cast<KeyEvent &>(event);
auto *ic = keyEvent.inputContext();
auto *engine = inputMethodEngine(ic);
const auto *entry = inputMethodEntry(ic);
if (!engine || !entry) {
return;
}
engine->keyEvent(*entry, keyEvent);
}));
d->eventWatchers_.emplace_back(watchEvent(
EventType::InputContextKeyEvent, EventWatcherPhase::InputMethod,
[this](Event &event) {
auto &keyEvent = static_cast<KeyEvent &>(event);
auto *ic = keyEvent.inputContext();
auto *engine = inputMethodEngine(ic);
const auto *entry = inputMethodEntry(ic);
if (!engine || !entry ||
!engine->isFunctionModeSupported(FunctionMode::Full)) {
return;
}
if (engine->getFunctionMode() != FunctionMode::Full) {
engine->setFunctionMode(FunctionMode::Full);
}
engine->keyEvent(*entry, keyEvent);
}));
d->eventWatchers_.emplace_back(watchEvent(
EventType::InputContextVirtualKeyboardEvent,
EventWatcherPhase::InputMethod, [this](Event &event) {
@ -916,9 +922,15 @@ Instance::Instance(int argc, char **argv) {
auto *ic = keyEvent.inputContext();
auto *engine = inputMethodEngine(ic);
const auto *entry = inputMethodEntry(ic);
if (!engine || !entry) {
if (!engine || !entry ||
!engine->isFunctionModeSupported(FunctionMode::Limited)) {
return;
}
if (engine->getFunctionMode() != FunctionMode::Limited) {
engine->setFunctionMode(FunctionMode::Limited);
}
engine->virtualKeyboardEvent(*entry, keyEvent);
}));
d->eventWatchers_.emplace_back(watchEvent(

View File

@ -77,6 +77,10 @@ public:
void processKeyEvent(uint32_t keyval, uint32_t keycode, uint32_t state,
bool isRelease, uint32_t time);
void processKeyEventV2(uint32_t keyval, uint32_t keycode, uint32_t state,
bool isRelease, uint32_t time,
uint32_t functionMode);
void processVisibilityEvent(bool /*visible*/) {}
void selectCandidate(int index) {
@ -126,9 +130,18 @@ public:
private:
PageableCandidateList *getPageableCandidateList();
static bool isFunctionModeValid(uint32_t functionMode);
static bool shouldProcessKeyEvent(InputContext *inputContext,
uint32_t functionMode);
static bool shouldProcessVirtualKeyboardEvent(InputContext *inputContext,
FunctionMode functionMode);
private:
FCITX_OBJECT_VTABLE_METHOD(processKeyEvent, "ProcessKeyEvent", "uuubu", "");
FCITX_OBJECT_VTABLE_METHOD(processKeyEventV2, "ProcessKeyEventV2", "uuubuu",
"");
FCITX_OBJECT_VTABLE_METHOD(processVisibilityEvent, "ProcessVisibilityEvent",
"b", "");
@ -143,9 +156,45 @@ private:
VirtualKeyboard *parent_;
};
// static
bool VirtualKeyboardBackend::isFunctionModeValid(uint32_t functionMode) {
return functionMode == static_cast<uint32_t>(FunctionMode::Full) ||
functionMode == static_cast<uint32_t>(FunctionMode::Limited);
}
// static
bool VirtualKeyboardBackend::shouldProcessKeyEvent(InputContext *inputContext,
uint32_t functionMode) {
if (!isFunctionModeValid(functionMode)) {
return false;
}
if (shouldProcessVirtualKeyboardEvent(
inputContext, static_cast<FunctionMode>(functionMode))) {
return true;
}
return inputContext->isFunctionModeSupported(FunctionMode::Full);
}
// static
bool VirtualKeyboardBackend::shouldProcessVirtualKeyboardEvent(
InputContext *inputContext, FunctionMode functionMode) {
return functionMode == FunctionMode::Limited &&
inputContext->isFunctionModeSupported(FunctionMode::Limited);
}
void VirtualKeyboardBackend::processKeyEvent(uint32_t keyval, uint32_t keycode,
uint32_t state, bool isRelease,
uint32_t time) {
processKeyEventV2(keyval, keycode, state, isRelease, time,
static_cast<uint32_t>(FunctionMode::Full));
}
void VirtualKeyboardBackend::processKeyEventV2(uint32_t keyval,
uint32_t keycode, uint32_t state,
bool isRelease, uint32_t time,
uint32_t functionMode) {
auto *inputContext = parent_->instance()->mostRecentInputContext();
if (inputContext == nullptr || !inputContext->hasFocus()) {
// TODO: when keyboard is shown but no focused ic, send fake key via
@ -153,10 +202,21 @@ void VirtualKeyboardBackend::processKeyEvent(uint32_t keyval, uint32_t keycode,
return;
}
if (!shouldProcessKeyEvent(inputContext, functionMode)) {
return;
}
VirtualKeyboardEvent event(inputContext, isRelease, time);
event.setKey(Key(static_cast<KeySym>(keyval), KeyStates(state), keycode));
auto eventConsumed = inputContext->virtualKeyboardEvent(event);
auto eventConsumed = false;
if (shouldProcessVirtualKeyboardEvent(
inputContext, static_cast<FunctionMode>(functionMode))) {
eventConsumed = inputContext->virtualKeyboardEvent(event);
} else {
eventConsumed = inputContext->keyEvent(*event.toKeyEvent());
}
if (eventConsumed) {
return;
}
@ -270,7 +330,12 @@ void VirtualKeyboard::resume() {
}));
eventHandlers_.emplace_back(instance_->watchEvent(
EventType::InputContextKeyEvent, EventWatcherPhase::PreInputMethod,
[this](Event &) {
[this](Event &event) {
const auto &keyEvent = static_cast<KeyEvent &>(event);
if (keyEvent.origKey().states().test(KeyState::Virtual)) {
return;
}
instance_->setInputMethodMode(InputMethodMode::PhysicalKeyboard);
}));
}