System Default keyboard hides QLineEdit / Emoji Picker Issue
-
Hello All.
I have written a qt C++ desktop application which I have port on QWebAssembly. The problem is, when there is a focus on the QLineEdit and the user has to type something, the system default keyboard hides the QLineEdit.
In my current implementation, I had to use a class (DisplayWdget) which copy the QLineEdit's field and display it at the top of the window which isn't that good.
class DisplayWidget : public QWidget { Q_OBJECT private: QLineEdit *_lineEdit{}; public: explicit DisplayWidget(QWidget *parent = nullptr) : QWidget(parent) { QHBoxLayout *layout = new QHBoxLayout(this); _lineEdit = new QLineEdit(this); layout->addWidget(_lineEdit); setLayout(layout); } public slots: void setText(const QString &text) { _lineEdit->setText(text); _lineEdit->setCursorPosition(_lineEdit->text().length()); } };
And below is the window:
void client_chat_window::set_up_window() { QWidget *central_widget = new QWidget(); setCentralWidget(central_widget); ask_microphone_permission(); #if !defined(EMSCRIPTEN) _dir.mkdir(_destinator_name); _dir.setPath("./" + _destinator_name); #endif QPushButton *member_list = new QPushButton("Server's Conversation", this); member_list->setStyleSheet("border: none;"); connect(member_list, &QPushButton::clicked, this, [=]() { if (_group_name.isEmpty()) { #if !defined(EMSCRIPTEN) folder(); #endif } else { ListDialog *members = new ListDialog(_group_members, "Group Members", this); connect(members, &QDialog::accepted, this, [=]() { QString name = members->name_selected().first(); emit item_clicked(name); members->deleteLater(); }); members->open(); } }); connect(this, &client_chat_window::update_button_file, this, [=]() { member_list->setText(QString("%1's Conversation").arg(_window_name)); }); _list = new Swipeable_list_widget(this, this); _list->setItemDelegate(new SeparatorDelegate(_list)); _list->setSelectionMode(QAbstractItemView::NoSelection); _insert_message = new CustomLineEdit(this); _insert_message->setPlaceholderText("Insert New Message"); connect(_insert_message, &CustomLineEdit::textChanged, this, [=]() { (_group_name.isEmpty()) ? _client->send_is_typing(my_name(), _destinator) : _client->send_group_is_typing(_group_ID, _group_name, my_name()); }); _emoji_button = new QPushButton("😀", this); _emoji_button->setFixedSize(30, _insert_message->height()); _emoji_button->setFont(_emoji_font); EmojiPicker *emojiPicker = new EmojiPicker(this); emojiPicker->hide(); connect(_emoji_button, &QPushButton::clicked, emojiPicker, [emojiPicker]() { emojiPicker->setVisible(!emojiPicker->isVisible()); }); connect(emojiPicker, &EmojiPicker::emoji_selected, _insert_message, [=](const QString &emoji) { _insert_message->insert(emoji); }); #ifdef __EMSCRIPTEN__ _display_widget = new DisplayWidget(this); _display_widget->hide(); connect(_insert_message, &CustomLineEdit::textChanged, _display_widget, &DisplayWidget::setText); connect(_insert_message, &CustomLineEdit::focusGained, _display_widget, &QWidget::show); connect(_insert_message, &CustomLineEdit::focusLost, _display_widget, &QWidget::hide); int font_ID = QFontDatabase::addApplicationFont(":/emoji/NotoColorEmoji.ttf"); if (font_ID == -1) qWarning("Failed to load emoji font."); else { QStringList font_families = QFontDatabase::applicationFontFamilies(font_ID); if (!font_families.isEmpty()) _emoji_font = QFont(font_families.at(0)); } _insert_message->setFont(_emoji_font); _display_widget->setFont(_emoji_font); _emoji_button->setFont(_emoji_font); #endif QPixmap image_send(":/images/send_icon.png"); _send_button = new QPushButton(this); _send_button->setIcon(image_send); _send_button->setIconSize(QSize(30, 30)); _send_button->setFixedSize(30, 30); _send_button->setStyleSheet("border: none"); connect(_send_button, &QPushButton::clicked, this, &client_chat_window::send_message); _send_file_button = new QPushButton("...", this); connect(_send_file_button, &QPushButton::clicked, this, &client_chat_window::send_file); _hbox = new QHBoxLayout(); _hbox->addWidget(_emoji_button); _hbox->addWidget(_insert_message); _hbox->addWidget(_send_button); _hbox->addWidget(_send_file_button); _buttons = new QHBoxLayout(); _buttons->addWidget(member_list); QVBoxLayout *VBOX = new QVBoxLayout(central_widget); #ifdef __EMSCRIPTEN__ VBOX->addWidget(_display_widget); #endif VBOX->addLayout(_buttons); VBOX->addWidget(_list); VBOX->addLayout(_hbox); VBOX->addWidget(emojiPicker); }
Is there any way to make the QLineEdit always visible when there is a focus on it ?
I know in javascript the window is automatically adjusted and the input field is placed right above the keyboard.I also would like to know how to enable emoji picker in WebAssembly cause I had to implement a whole class to support it and list my own emojis. I had changed the font to Noto Color to support them otherwise they would appear as squares.
-
I’m facing the same problem when using QML. When the app is run on a Mobile and the virtual keyboard appears, it’s hides some contents.
import QtQuick; import QtQuick.Controls; import QtQuick.Window; Rectangle { id: root; Image { id: welcomeImage; mipmap: true; fillMode: Image.PreserveAspectFit; anchors.top: parent.top; anchors.topMargin: 10; anchors.horizontalCenter: parent.horizontalCenter; width: 300; height: 300; source: "qrc:/QML_modules/ClientApp/icons/hi_icon.png"; } Text { id: textWelcome; text: "GET ON BOARD,"; wrapMode: Text.WrapAtWordBoundaryOrAnywhere; font.bold: true; font.pixelSize: 30; leftPadding: 15; rightPadding: 15; anchors.top: welcomeImage.bottom; anchors.horizontalCenter: parent.horizontalCenter; } Text { id: textAboutUs; text: "Create your Profile and Start the Journey with US"; wrapMode: Text.WrapAtWordBoundaryOrAnywhere; font.bold: true; font.pixelSize: 10; leftPadding: 15; rightPadding: 15; anchors.top: textWelcome.bottom; anchors.topMargin: 5; anchors.horizontalCenter: parent.horizontalCenter; } InputField { id: signUpFirstName; image1Source: "qrc:/QML_modules/ClientApp/icons/name_icon.png"; echoMode: 0; placeHolder: "First Name"; width: parent.width * 0.6; anchors.top: textAboutUs.bottom; anchors.topMargin: 10; anchors.horizontalCenter: parent.horizontalCenter; } InputField { id: signUpLastName; image1Source: "qrc:/QML_modules/ClientApp/icons/name_icon.png"; echoMode: 0; placeHolder: "Last Name"; width: parent.width * 0.6; anchors.top: signUpFirstName.bottom; anchors.topMargin: 10; anchors.horizontalCenter: parent.horizontalCenter; } InputField { id: signUpPhoneNumber; image1Source: "qrc:/QML_modules/ClientApp/icons/phone_icon.png"; echoMode: 0; placeHolder: "Phone Number"; width: parent.width * 0.6; anchors.top: signUpLastName.bottom; anchors.topMargin: 10; anchors.horizontalCenter: parent.horizontalCenter; } InputField { id: signUpPassword; image1Source: "qrc:/QML_modules/ClientApp/icons/hide_icon.png"; image2Source: "qrc:/QML_modules/ClientApp/icons/see_icon.png"; echoMode: 2; placeHolder: "Password"; width: parent.width * 0.6; anchors.top: signUpPhoneNumber.bottom; anchors.topMargin: 10; anchors.horizontalCenter: parent.horizontalCenter; } InputField { id: signUpPasswordConfirmation; image1Source: "qrc:/QML_modules/ClientApp/icons/hide_icon.png"; image2Source: "qrc:/QML_modules/ClientApp/icons/see_icon.png"; echoMode: 2; placeHolder: "Password Confirmation"; width: parent.width * 0.6; anchors.top: signUpPassword.bottom; anchors.topMargin: 10; anchors.horizontalCenter: parent.horizontalCenter; } Rectangle { id: signUpButton; color: "black"; radius: 5; width: parent.width * 0.6; height: 50; Text { text: "SIGN UP"; color: "white"; anchors.centerIn: parent; } MouseArea { anchors.fill: parent; onPressed: parent.color = "#ed7bb4"; onReleased: parent.color = "black"; // FIXME: handle click button correctly onClicked: console.log("Sign Up Button Click"); } anchors.top: signUpPasswordConfirmation.bottom; anchors.topMargin: 20; anchors.horizontalCenter: parent.horizontalCenter; } Row { spacing: 10; Text { id: question; text: "Already have an Account?"; } Rectangle { id: signUp; color: "transparent"; width: question.width * .5; height: 20; Text { id: signUpText; text: "LOGIN"; color: "#DE02B5"; font.bold: true; leftPadding: 5; anchors.centerIn: parent; } MouseArea { anchors.fill: parent; onClicked: stackView.replace(loginWindow, StackView.PopTransition); } } anchors.top: signUpButton.bottom; anchors.topMargin: 20; anchors.horizontalCenter: parent.horizontalCenter; } }
Here the InputField module implementation:
import QtQuick; import QtQuick.Controls; import QtQuick.Window; import QtQuick.Controls.Basic; Rectangle { id: root; property alias inputField: textInput.text; required property string image1Source; property string image2Source; required property string placeHolder; property alias echoMode: textInput.echoMode; signal accepted(string value); height: 50; radius: 20; border.color: textInput.focus ? "#a10e7a" : "black"; border.width: textInput.focus ? 4 : 2; opacity: enabled ? 1 : 0.6; TextField { id: textInput; anchors.fill: parent; anchors.margins: 2; font.pixelSize: 14; color: "black"; placeholderText: root.placeHolder; placeholderTextColor: "black"; leftPadding: 30; verticalAlignment: TextField.AlignVCenter; onAccepted: root.accepted(text); background: Rectangle { radius: root.radius - 2; implicitWidth: 200; implicitHeight: 40; color: "#e6e8e8"; border.color: "transparent"; } Image { id: toggleImage; anchors.left: parent.left; anchors.leftMargin: 5; anchors.verticalCenter: parent.verticalCenter; width: 20; height: 20; mipmap: true; source: root.image1Source; MouseArea { anchors.fill: parent; onClicked: { textInput.echoMode = textInput.echoMode === TextInput.Normal ? TextInput.Password : TextInput.Normal; toggleImage.source = textInput.echoMode === TextInput.Normal ? root.image2Source : root.image1Source; } } } } Component.onCompleted: textInput.focus = false; }
-
As written on the mailing list:
- Which Qt Version are you using?
- Which (mobile) platform is this related to?
- Try a focus proxy on widgets, focus scope in QML.
Please boil the code down to a minimal compilable reproducer.
-
I am using Qt 6.7.2
The problem isn't related to any mobile platform.
It is just that I had built the app to WebAssembly and deploy it using github pages.
When I am using it on my a PC there is no problem but when I try it using a Phone, the phone's native virtual keyboard hides the InputField which has the focus on it.I would like to know how I can automatically scroll the InputField which has gained focus to the middle of the screen i.e above the virtual keyboard so it is always visible.
This is a screenshot before clicking on the password input field and the one below is after I click on it.
If I am using a PC there is no problem cause there is not virtual keyboard.
-
As said: Please provide a minimal reproducer.
I don't have any issue on mobile devices, if I just use aTextField
.
So the problem seems to be caused by the custom typeInputField
. My suspicion is that the mouse area within the type causes the focus. You'll find that out by creating that reproducer.