项目代码
项目代码【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