QtWebAssembly实现中文输入

项目代码

项目代码【WasmInput】
请优先参考上面的项目代码来写你的程序,下面的代码可能更新没那么快。

效果演示

最终效果

实现原理为

用户点击输入控件时,动态创建一个html原生的input控件,然后将该控件覆盖在qml的原始控件上并获取焦点。同时,该控件在失去焦点时,需要将自身移除。至于获取input的输入结果,需要用到wasm的html5.h里面的事件监听函数,监听前面input的失去焦点事件(或者change事件),在回调函数中获取输入结果。

v0.1版本

需要用到的关键函数为:


---qml
            MouseArea{
                anchors.fill: parent

                visible: Qt.platform.os == "wasm"
                //计算原来控件的位置,给原生html控件提供x,y,width,height
                onClicked: {
                    var pos = mapToItem(mainForm, Qt.point(0, 0))
                    console.log("position", pos)

                    manager.getUserInput(textField_info.text, pos.x, pos.y, textField_info.width, textField_info.height)

                    window.theInput = root
                }
            }


---c++
//注入函数,这个相当于全局使用的函数,
EM_JS(void, injectJs, (), {
          var sObj = window.document.createElement("script");
          sObj.setAttribute("type", "text/javascript");
          sObj.setAttribute("id", "zyS");
          sObj.innerHTML = "
          function removeInput()
          {
              let obj = window.document.getElementById('zyInput');
              obj.parentNode.removeChild(obj);
              console.log('input value', obj.value);
          }";

          console.log("sObj", sObj.innerHTML, sObj.type);

          var muiDiv = window.document.getElementById('qtcanvas');
          muiDiv.appendChild(sObj);
      })

//动态创建输入框,然后让这个输入框覆盖在qml的输入框上
EM_JS(void, getInputX, (const char *str, const int x, const int y, const int width, const int height), {
          const text = UTF8ToString(str);

          var muiDiv = window.document.getElementById('qtcanvas');
          createInput('txtInput','text', text, muiDiv.parentElement);

          console.log("getInputX", muiDiv, muiDiv.parentElement);

          function createInput(inputName, inputType, inputValue, aDiv) {
            var input = window.document.createElement("input");
            input.setAttribute("type", inputType);
            input.setAttribute("name", inputName);
            input.setAttribute("value", inputValue);

            var style = "border:0px solid groove; position:absolute;  margin: 0px; padding: 0px; z-index: 10000; left:" + x
              + "px; top:" + y
              + "px; width:" + width
              + "px; height:" + height
              + "px; opacity:1.0";

            console.log("style", style);
            input.setAttribute("style", style);

            input.setAttribute("id", "zyInput");
            input.setAttribute("onBlur", "removeInput()");

            console.log("id", input.id);

            aDiv.appendChild(input);
            console.log("createInput");
            input.focus();
          }
      })

//获取输入控件的输入值
EM_JS(const char*, getText, (), {
          let obj = window.document.getElementById('zyInput');
          var jstring = "";
          if(obj !== null)
          {
              jstring = obj.value;
          }

          var lengthBytes = lengthBytesUTF8(jstring) + 1;
          var stringOnWasmHeap = _malloc(lengthBytes);
          stringToUTF8(jstring, stringOnWasmHeap, lengthBytes);
          return stringOnWasmHeap;
      })
      
//控件失去焦点时的回调函数
EM_BOOL focusevent_callback(int eventType, const EmscriptenFocusEvent *e, void *userData)
{
    QString itemID = QString(e->id);
    if(itemID == "zyInput")
    {
        QString inputText =  getText();

        Manager *man = (Manager*)userData;
        man->inputTextChanged(inputText);
    }
    return 0;
}


{
    injectJs();
    
    //注册函数
    auto ret = emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, 1, focusevent_callback);
}

v0.2版本

v0.2版本充分利用了指针以及回调函数,以及在js中调用c函数。使得整体更加整洁,运行得更加稳定
此版本不是在input在失去焦点后再更新qml的文字,而是“实时”更新。
特别注意的是要在.pro文件中加入

wasm:QMAKE_LFLAGS += -s EXTRA_EXPORTED_RUNTIME_METHODS=[\"UTF16ToString\",\"stringToUTF16\",\"ccall\"]

在这里插入图片描述
假如是使用cmake,可以这样:

target_link_options(WebTest_cmake PRIVATE -s EXTRA_EXPORTED_RUNTIME_METHODS=[\"ccall\"])

需要用到的关键函数为:

// 创建输入框,用于覆盖原始的qml输入框。创建的输入框的id为zyInput
EM_JS(void, createInputX, (quintptr ptr, const char *str, const int x, const int y, const int width, const int height), {
          const text = UTF8ToString(str);

          var canvas = window.document.getElementById('qtcanvas');
          var form = canvas.parentElement;

          var input = window.document.createElement('input');
          input.setAttribute('type', 'text');
          input.setAttribute('name', 'txtInput');
          input.setAttribute('value', text);

          var style = 'border:0px solid groove; position:absolute;  margin: 0px; padding: 0px; z-index: 10000; left:' + x
          + 'px; top:' + y
          + 'px; width:' + width
          + 'px; height:' + height
          + 'px; opacity:1.0';

          input.setAttribute('style', style);

          input.setAttribute('id', 'zyInput_' + ptr); // 设置id
          input.onblur = function(){
              form.removeChild(input);
          };  // 失去焦点时,就移除自身
          input.onchange = function(){
              // 调用 C 函数, 将文字拷贝到Qt的控件
              var result = Module.ccall('setWidgetText', // 函数名称
                                        'number',    // 返回值类型
                                        ['number', 'string'], // 参数类型
                                        [ptr, input.value]);   // 参数列表
          }; //文字发生更改时,修改Qt控件的值

          form.appendChild(input);

          input.focus();
//          console.log("createInputX complete", input.id, x, y, width, height);
      })

extern "C"{
EMSCRIPTEN_KEEPALIVE
int setWidgetText(quintptr ptr, const char *text)
{
//    qDebug() << "setWidgetText" << ptr << text;

    QObject *obj = (QObject*)ptr;
    obj->setProperty("text", text);

    return 0;
}
}

// 对所有生成的input进行blur处理
EM_JS(void, blurAllInput, (), {
          // 动态的 HTMLCollection 对象的问题,因此需要先转成数组在遍历
          let inputList = Array.from(document.getElementsByTagName('input'));
          for(let i = 0; i < inputList.length; i++){
              let input = inputList[i];
              if(input && input.parentElement && input.id.startsWith('zyInput_'))
              {
                  input.blur()
              }
          }
      })


#endif


参考:
【js动态添加、删除html代码】
【emscripten.h】
【html5.h】

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值