With the help of wasm, we are able to port the apps written by Qt to the browser, this save us a lot of times in many cases, but same as android/ios, wasm of Qt do have their limitations and the Qt company do not have much interest to fix those issues, I guess this is because major market of Qt are automobile, IOT etc, but not mobile phone or browser.
In this post I would like to write down how do I get around the issue 78826--Cannot enter non ascii characters to the TextField and QLineEdit from browser(wasm).
Limitations
1.Cannot access system font
QFile file("OPPOSans-H.ttf"); if(file.open(QIODevice::ReadOnly)){ auto const contents = qCompress(file.readAll(), 9); QFile fwrite("OPPOSans-H.zip"); if(fwrite.open(QIODevice::WriteOnly)){ fwrite.write(contents); }else{ qDebug()<<__func__<<": cannot write file"; } }else{ qDebug()<<__func__<<": cannot open file"; }
After that, add the zip file into the Qt resource system, load it and set it as the font of the QPlainTextEdit.
//MainWindow.cpp, set font for QPlainTextEdit QFont const monospace(font_manager().get_font_family()); ui->plainTextEdit->setFont(monospace); //font_manager.cpp, a class use to register the font and access the font id font_manager::font_manager() { QFile ifile(":/assets/OPPOSans-H.zip"); if(ifile.open(QIODevice::ReadOnly)){ auto const font_id = QFontDatabase::addApplicationFontFromData(qUncompress(ifile.readAll())); qDebug()<<__func__<<"font id:"<<font_id; font_family_ = QFontDatabase::applicationFontFamilies(font_id).at(0); } } QFont font_manager::get_font() const { return QFont(get_font_family()); } QString font_manager::get_font_family() const noexcept { return font_family_; }
2. Cannot enter Chinese from QPlainTextEdit
This issue is quite annoying, but we do have a "stupid" solution to overcome this problem. The way I found is use the prompt dialog of js library to input the text. The library I pick called bootbox. You can get around this limit by following steps
a. Use js to enter the text
var _global_text_process_result = '';
//You need to allocate memory before return the array to the c++ side
function getStringFromJS(targetStr, msg) { console.log(msg + ":" + targetStr); var jsString = targetStr; var lengthBytes = lengthBytesUTF8(jsString)+1; var stringOnWasmHeap = _malloc(lengthBytes); stringToUTF8(jsString, stringOnWasmHeap, lengthBytes); console.log(msg + " heap:" + stringOnWasmHeap); return stringOnWasmHeap; }
//call the prompt dialog of bootbox
function multiLinesPrompt(inputString, inputMode, inputTitle) { bootbox.prompt({ title: inputTitle, inputType: "textarea", value: inputString, callback: function (result) { console.log(result); _global_text_process_result = result; } }); }
//This function use to avoid the text update repeatly
function globalTextProcessResultIsValid() { return _global_text_process_result !== "" } function getGlobalTextProcessResult() { results = getStringFromJS(_global_text_process_result, "js getGlobalTextProcessResult") _global_text_process_result = "" return results }
b. Call the js function from c++
#include "custom_qplain_text_edit.hpp" #include <QDebug> #include <QTimer> #ifdef Q_OS_WASM #include <emscripten.h> namespace{ EM_JS(char*, global_text_process_result_is_valid, (), { return globalTextProcessResultIsValid(); }) EM_JS(char*, get_global_text_process_result, (), { return getGlobalTextProcessResult(); }) EM_JS(void, multi_lines_prompt, (char const *input_strings, char const *input_mode, char const *title), { multiLinesPrompt(UTF8ToString(input_strings), UTF8ToString(input_mode), UTF8ToString(title)); }) } #endif custom_qplain_text_edit::custom_qplain_text_edit(QWidget *parent) : QPlainTextEdit(parent) { #ifdef Q_OS_WASM
timer_ = new QTimer(this); timer_->setInterval(100); connect(timer_, &QTimer::timeout, [this]() { if(global_text_process_result_is_valid()){ char *msg = get_global_text_process_result(); setPlainText(QString(msg)); free(msg); timer_->stop(); } }); #endif } void custom_qplain_text_edit::mousePressEvent(QMouseEvent *e) { #ifdef Q_OS_WASM if(e->button() == Qt::RightButton){ QPlainTextEdit::mousePressEvent(e); }else{ multi_lines_prompt(toPlainText().toUtf8().data(), "textarea", "Input plain text"); timer_->start(); } #else QPlainTextEdit::mousePressEvent(e); #endif }
c. Include js script and css in generated html file
To be honest, this is really troublesome, and I hope that one day this shortcoming can be resolved.