# 在自绘编辑框中使用输入法开发指导 (C/C++)

#### 场景介绍

IME Kit支持开发者在自绘编辑框中使用输入法，与输入法应用交互，包括显示、隐藏输入法，接收来自输入法应用的文本编辑操作通知等，本文档介绍开发者如何使用C/C++完成此功能开发。

#### 接口说明

详细接口说明请参考InputMethod接口文档。

#### 添加动态链接库

CMakeLists.txt中添加以下lib。

```
libohinputmethod.so
```

#### 引用头文件

```
#include <inputmethod/inputmethod_controller_capi.h>
```

#### 绑定输入法

开发者需要在输入框获焦时，通过调用接口OH\_InputMethodController\_Attach绑定输入法，绑定成功后用户可以通过输入法输入文字。

1.  创建InputMethod\_TextEditorProxy实例，示例代码如下所示：
    
    ```
    // 创建InputMethod_TextEditorProxy实例
    textEditorProxy = OH_TextEditorProxy_Create();
    ```
    
2.  创建InputMethod\_AttachOptions实例，设置绑定输入法时的选项。示例代码如下所示：
    
    ```
    // 创建InputMethod_AttachOptions实例，选项showKeyboard用于指定此次绑定成功后是否显示键盘，设置为true 表示绑定成功后自动显示键盘；false 表示不自动显示
    bool showKeyboard = true;
    attachOptions = OH_AttachOptions_Create(showKeyboard);
    ```
    
3.  调用OH\_InputMethodController\_Attach发起绑定输入法服务，调用成功后，可以获取到用于和输入法交互的InputMethod\_InputMethodProxy。示例代码如下所示：
    
    ```
    // 发起绑定请求
    auto ret = OH_InputMethodController_Attach(textEditorProxy, attachOptions, &inputMethodProxy);
    if (ret != IME_ERR_OK) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "testTag", "Attach failed, ret=%{public}d.", ret);
        OH_TextEditorProxy_Destroy(textEditorProxy);
        OH_AttachOptions_Destroy(attachOptions);
        return;
    }
    ```
    

#### 显示/隐藏面板功能

绑定成功后，可以使用获取到的InputMethod\_InputMethodProxy对象向输入法发送消息。示例代码如下所示：

```
// 显示键盘
if (OH_InputMethodProxy_ShowKeyboard(inputMethodProxy) != InputMethod_ErrorCode::IME_ERR_OK) {
    OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "testTag", "ShowKeyboard failed!");
}
// 隐藏键盘
if (OH_InputMethodProxy_HideKeyboard(inputMethodProxy) != InputMethod_ErrorCode::IME_ERR_OK) {
    OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "testTag", "HideKeyboard failed!");
}
// 通知输入框配置信息变化
if (OH_InputMethodProxy_NotifyConfigurationChange(inputMethodProxy, InputMethod_EnterKeyType::IME_ENTER_KEY_GO, InputMethod_TextInputType::IME_TEXT_INPUT_TYPE_TEXT) != InputMethod_ErrorCode::IME_ERR_OK) {
    OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "testTag", "NotifyConfigurationChange failed!");
}
```

#### 监听输入法应用的请求/通知

1.  需要先实现对输入法应用发送的请求或通知的响应处理函数，示例代码如下所示：
    
    ```
    // 实现InputMethod_TextEditorProxy中的输入法应用事件响应函数
    void GetTextConfigFunc(InputMethod_TextEditorProxy *textEditorProxy, InputMethod_TextConfig *config)
    {
        // 处理输入法发送的获取输入框配置请求
    }
    void InsertTextFunc(InputMethod_TextEditorProxy *textEditorProxy, const char16_t *text, size_t length)
    {
        // 处理输入法发送的插入文本请求
    }
    void DeleteForwardFunc(InputMethod_TextEditorProxy *textEditorProxy, int32_t length)
    {
        // 处理输入法发送的删除文本请求
    }
    // ......
    ```
    
2.  将实现后的响应函数，设置到InputMethod\_TextEditorProxy中，再通过绑定输入法时调用的OH\_InputMethodController\_Attach将其设置到输入法框架中，完成监听注册。示例代码如下所示：
    
    ```
    OH_TextEditorProxy_SetGetTextConfigFunc(textEditorProxy, GetTextConfigFunc);
    OH_TextEditorProxy_SetInsertTextFunc(textEditorProxy, InsertTextFunc);
    OH_TextEditorProxy_SetDeleteForwardFunc(textEditorProxy, DeleteForwardFunc);
    OH_TextEditorProxy_SetDeleteBackwardFunc(textEditorProxy, DeleteBackwardFunc);
    OH_TextEditorProxy_SetSendKeyboardStatusFunc(textEditorProxy, SendKeyboardStatusFunc);
    OH_TextEditorProxy_SetSendEnterKeyFunc(textEditorProxy, SendEnterKeyFunc);
    OH_TextEditorProxy_SetMoveCursorFunc(textEditorProxy, MoveCursorFunc);
    OH_TextEditorProxy_SetHandleSetSelectionFunc(textEditorProxy, HandleSetSelectionFunc);
    OH_TextEditorProxy_SetHandleExtendActionFunc(textEditorProxy, HandleExtendActionFunc);
    OH_TextEditorProxy_SetGetLeftTextOfCursorFunc(textEditorProxy, GetLeftTextOfCursorFunc);
    OH_TextEditorProxy_SetGetRightTextOfCursorFunc(textEditorProxy, GetRightTextOfCursorFunc);
    OH_TextEditorProxy_SetGetTextIndexAtCursorFunc(textEditorProxy, GetTextIndexAtCursorFunc);
    OH_TextEditorProxy_SetReceivePrivateCommandFunc(textEditorProxy, ReceivePrivateCommandFunc);
    OH_TextEditorProxy_SetSetPreviewTextFunc(textEditorProxy, SetPreviewTextFunc);
    OH_TextEditorProxy_SetFinishTextPreviewFunc(textEditorProxy, FinishTextPreviewFunc);
    ```
    

#### 解绑输入法

当编辑框失焦，需要结束使用输入法，通过接口OH\_InputMethodController\_Detach与输入法框架解绑。

```
// 发起解绑请求
OH_InputMethodController_Detach(inputMethodProxy);
OH_TextEditorProxy_Destroy(textEditorProxy);
OH_AttachOptions_Destroy(attachOptions);
```

#### 完整示例

示例代码展示了绑定输入法、隐藏输入法、解绑输入法的完整流程。

示例代码总入口为InputMethodNdkDemo函数。

说明：

需要在CMakeList.txt中添加libohinputmethod.so libhilog\_ndk.z.so依赖。

```
#include "napi/native_api.h"
#include <codecvt>
#include <locale>
#include <thread>

#include "hilog/log.h"
#include "inputmethod/inputmethod_controller_capi.h"

 constexpr int32_t TEXTSIZE = 1024;

static std::string g_strText;
char g_strTextChar[TEXTSIZE];
int32_t g_strTextCharLen = 0;
bool g_flagShow = false;
std::mutex g_textMutex;
InputMethod_TextEditorProxy *textEditorProxy = nullptr;
InputMethod_AttachOptions *attachOptions = nullptr;
InputMethod_InputMethodProxy *inputMethodProxy = nullptr;

void InputMethodDestroy();

void InitText()
{
    std::lock_guard<std::mutex> lock(g_textMutex);
    if (g_flagShow) {
        memset(g_strTextChar, 0x00, sizeof(g_strTextChar));
        g_strTextCharLen = 0;
        g_flagShow = false;
    }
}

void SetText(const char* input)
{
    std::lock_guard<std::mutex> lock(g_textMutex);
    g_strTextCharLen = strlen(input);
    if (g_strTextCharLen > TEXTSIZE) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "testTag", "Length greater than 1024 , ret=%{public}d", g_strTextCharLen);
    }
    strncpy(g_strTextChar, input, g_strTextCharLen);
}

void GetTextConfigFunc(InputMethod_TextEditorProxy *proxy, InputMethod_TextConfig *config)
{ // 处理获取输入框配置请求
    auto ret = OH_TextConfig_SetEnterKeyType(config, InputMethod_EnterKeyType::IME_ENTER_KEY_SEND);
    if (ret != IME_ERR_OK) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "testTag", "SetEnterKeyType failed, ret=%{public}d", ret);
        return;
    }

    ret = OH_TextConfig_SetInputType(config, InputMethod_TextInputType::IME_TEXT_INPUT_TYPE_PHONE);
    if (ret != IME_ERR_OK) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "testTag", "SetInputType failed, ret=%{public}d", ret);
        return;
    }
}

void InsertTextFunc(InputMethod_TextEditorProxy *proxy, const char16_t *text, size_t length)
{
    InitText();

    // 处理插入文本请求
    // 将char16_t类型的字符串转换为u16string
    std::u16string u16Str(text, length + 1);

    // 转换为UTF-8编码的string
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
    std::string utf8Str = converter.to_bytes(u16Str);
    for (size_t i = 0; i < utf8Str.size(); ++i) {
        unsigned char c = static_cast<unsigned char>(utf8Str[i]);
        if (c != 0x00) {
            std::lock_guard<std::mutex> lock(g_textMutex);
            g_strTextChar[g_strTextCharLen] = c;
            g_strTextCharLen += 1;
        }
    }
}

void DeleteForwardFunc(InputMethod_TextEditorProxy *proxy, int32_t length)
{
    std::lock_guard<std::mutex> lock(g_textMutex);
    if (g_strTextCharLen > 0) {
        strncpy(g_strTextChar, g_strTextChar + 1, g_strTextCharLen - 1);
        g_strTextCharLen = (g_strTextCharLen > 0) ? g_strTextCharLen - 1 : g_strTextCharLen;
    }
}

void DeleteBackwardFunc(InputMethod_TextEditorProxy *proxy, int32_t length)
{
    std::lock_guard<std::mutex> lock(g_textMutex);
    g_strTextCharLen = (g_strTextCharLen > 0) ? g_strTextCharLen - 1 : g_strTextCharLen;
    g_strTextChar[g_strTextCharLen] = '\0';
}

void SendKeyboardStatusFunc(InputMethod_TextEditorProxy *proxy, InputMethod_KeyboardStatus status)
{
    if (status == InputMethod_KeyboardStatus::IME_KEYBOARD_STATUS_HIDE) {
        g_flagShow = false;
        SetText("键盘已经被隐藏");
    } else if (status == InputMethod_KeyboardStatus::IME_KEYBOARD_STATUS_SHOW && g_flagShow != true) {
        g_flagShow = true;
        SetText("键盘已经被拉起");
    }
}

void SendEnterKeyFunc(InputMethod_TextEditorProxy *proxy, InputMethod_EnterKeyType type)
{
    SetText("处理回车键请求事件");
    g_flagShow = true;
}

void MoveCursorFunc(InputMethod_TextEditorProxy *proxy, InputMethod_Direction direction)
{
    if (direction == InputMethod_Direction::IME_DIRECTION_UP) {
        SetText("光标正在向 上 移动");
    } else if (direction == InputMethod_Direction::IME_DIRECTION_DOWN) {
        SetText("光标正在向 下 移动");
    } else if (direction == InputMethod_Direction::IME_DIRECTION_LEFT) {
        SetText("光标正在向 左 移动");
    } else if (direction == InputMethod_Direction::IME_DIRECTION_RIGHT) {
        SetText("光标正在向 右  移动");
    } else {
        SetText("光标移动 出现错误");
    }
}

void HandleSetSelectionFunc(InputMethod_TextEditorProxy *proxy, int32_t start, int32_t end)
{
    SetText("处理选中文本请求");
}

void HandleExtendActionFunc(InputMethod_TextEditorProxy *proxy, InputMethod_ExtendAction action)
{
    SetText("处理扩展编辑请求");
}

void GetLeftTextOfCursorFunc(InputMethod_TextEditorProxy *proxy, int32_t number, char16_t text[], size_t *length)
{
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "testTag", "处理获取光标左侧文本请求  ...");
}

void GetRightTextOfCursorFunc(InputMethod_TextEditorProxy *proxy, int32_t number, char16_t text[], size_t *length)
{
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "testTag", "处理获取光标右侧文本请求  ...");
}

int32_t GetTextIndexAtCursorFunc(InputMethod_TextEditorProxy *proxy)
{
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "testTag", "处理获取光标所在输入框文本索引请求  ...");
    return 0;
}
int32_t ReceivePrivateCommandFunc(InputMethod_TextEditorProxy *proxy, InputMethod_PrivateCommand *privateCommand[],
    size_t size)
{
    SetText("处理扩展编辑请求");
    return 0;
}

int32_t SetPreviewTextFunc(InputMethod_TextEditorProxy *proxy, const char16_t *text, size_t length, int32_t start,
    int32_t end)
{
    SetText("处理设置预上屏文本请求");
    return 0;
}

void FinishTextPreviewFunc(InputMethod_TextEditorProxy *proxy)
{
    SetText("处理结束预上屏请求");
}

void ConstructTextEditorProxy(InputMethod_TextEditorProxy *textEditorProxy)
{
    OH_TextEditorProxy_SetGetTextConfigFunc(textEditorProxy, GetTextConfigFunc);
    OH_TextEditorProxy_SetInsertTextFunc(textEditorProxy, InsertTextFunc);
    OH_TextEditorProxy_SetDeleteForwardFunc(textEditorProxy, DeleteForwardFunc);
    OH_TextEditorProxy_SetDeleteBackwardFunc(textEditorProxy, DeleteBackwardFunc);
    OH_TextEditorProxy_SetSendKeyboardStatusFunc(textEditorProxy, SendKeyboardStatusFunc);
    OH_TextEditorProxy_SetSendEnterKeyFunc(textEditorProxy, SendEnterKeyFunc);
    OH_TextEditorProxy_SetMoveCursorFunc(textEditorProxy, MoveCursorFunc);
    OH_TextEditorProxy_SetHandleSetSelectionFunc(textEditorProxy, HandleSetSelectionFunc);
    OH_TextEditorProxy_SetHandleExtendActionFunc(textEditorProxy, HandleExtendActionFunc);
    OH_TextEditorProxy_SetGetLeftTextOfCursorFunc(textEditorProxy, GetLeftTextOfCursorFunc);
    OH_TextEditorProxy_SetGetRightTextOfCursorFunc(textEditorProxy, GetRightTextOfCursorFunc);
    OH_TextEditorProxy_SetGetTextIndexAtCursorFunc(textEditorProxy, GetTextIndexAtCursorFunc);
    OH_TextEditorProxy_SetReceivePrivateCommandFunc(textEditorProxy, ReceivePrivateCommandFunc);
    OH_TextEditorProxy_SetSetPreviewTextFunc(textEditorProxy, SetPreviewTextFunc);
    OH_TextEditorProxy_SetFinishTextPreviewFunc(textEditorProxy, FinishTextPreviewFunc);
}
```

```
void InputMethodNdkDemo()
{
    // 创建InputMethod_TextEditorProxy实例
    textEditorProxy = OH_TextEditorProxy_Create();
    if (textEditorProxy == nullptr) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "testTag", "Create TextEditorProxy failed.");
        return;
    }

    // 将实现好的响应处理函数设置到InputMethod_TextEditorProxy中
    ConstructTextEditorProxy(textEditorProxy);

    // 创建InputMethod_AttachOptions实例，选项showKeyboard用于指定此次绑定成功后是否显示键盘，设置为true 表示绑定成功后自动显示键盘；false 表示不自动显示
    bool showKeyboard = true;
    attachOptions = OH_AttachOptions_Create(showKeyboard);
    if (attachOptions == nullptr) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "testTag", "Create AttachOptions failed.");
        OH_TextEditorProxy_Destroy(textEditorProxy);
        return;
    }

    // 发起绑定请求
    auto ret = OH_InputMethodController_Attach(textEditorProxy, attachOptions, &inputMethodProxy);
    if (ret != IME_ERR_OK) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "testTag", "Attach failed, ret=%{public}d.", ret);
        OH_TextEditorProxy_Destroy(textEditorProxy);
        OH_AttachOptions_Destroy(attachOptions);
        return;
    }
}

static napi_value InputMethodDestroy(napi_env env, napi_callback_info info)
{
   // 隐藏键盘
    int ret = OH_InputMethodProxy_HideKeyboard(inputMethodProxy);
    if (ret != IME_ERR_OK) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, 0, "testTag", "HideKeyboard failed, ret=%{public}d.", ret);
        OH_TextEditorProxy_Destroy(textEditorProxy);
        OH_AttachOptions_Destroy(attachOptions);
        return nullptr;
    }

    // 发起解绑请求
    OH_InputMethodController_Detach(inputMethodProxy);
    OH_TextEditorProxy_Destroy(textEditorProxy);
    OH_AttachOptions_Destroy(attachOptions);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "testTag", "Finished.");
    return nullptr;
}

static napi_value AttachInputMethod(napi_env env, napi_callback_info info)
{
    InputMethodNdkDemo();

    napi_value result;
    napi_create_string_utf8(env,  g_strText.c_str(),  g_strText.length(),  &result);
    return result;
}

static napi_value GetText(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_create_string_utf8(env, g_strTextChar, g_strTextCharLen,  &result);
    return result;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        { "attachInputMethod", nullptr, AttachInputMethod, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "getText", nullptr, GetText, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "inputMethodDestroy", nullptr, InputMethodDestroy, nullptr, nullptr, nullptr, napi_default, nullptr },
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&demoModule);
}
```