Search code examples
qtdesktop-applicationvirtual-keyboardon-screen-keyboardqtwidgets

Virtual keyboard or onscreen keyboard for QtWidgets applications?


I'm going to deploy the qtvirtualkeyboard in my widget-based application like so:

#include <QtWidgets>

int main(int argc, char *argv[]) {
    qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
    QApplication app(argc, argv);
    QMainWindow window;
    QLineEdit input(&window);
    input.move(250, 250);
    window.show();
    return app.exec();
}

But the only issue is that the virtual keyboard input panel hides the underlying widgets and cover them!

How should I achieve this?

Is there any document or solution for widgets-based applications?


Solution

  • Finally got the solution!

    You just need to call QGuiApplication::inputMethod() to get the application-wide Qt input method and then call QInputMethod::keyboardRectangle() and QInputMethod::isVisible() to get input method properties then remain a calculation based on your widget position and keyboard coordinate, here is a full-working sample to share:

    lineedit.h:

    class LineEdit :public QLineEdit {
        Q_OBJECT
    
    public:
        LineEdit(QWidget *parent = nullptr);
        LineEdit(const QString&, QWidget *parent = nullptr);
    
    protected:
        bool event(QEvent*) override;
    
    private:
        bool _moved = false;
        int _lastDiff = 0;
    };
    

    lineedit.cpp:

    LineEdit::LineEdit(QWidget *parent) :QLineEdit(parent) {
        setAttribute(Qt::WA_InputMethodEnabled, true);
        setInputMethodHints(inputMethodHints() | Qt::InputMethodHint::ImhDigitsOnly);
    }
    
    LineEdit::LineEdit(const QString& txt, QWidget *parent) : QLineEdit(txt, parent) {
        setAttribute(Qt::WA_InputMethodEnabled, true);
        setInputMethodHints(inputMethodHints() | Qt::InputMethodHint::ImhDigitsOnly);
    }
    
    bool LineEdit::event(QEvent* e) {
        const auto keyboard_rect = QGuiApplication::inputMethod()->keyboardRectangle();
        const auto keyboard_visible = QGuiApplication::inputMethod()->isVisible();
        const auto global_y = QWidget::mapToGlobal(rect().topLeft()).y() + height();
        const auto k_global_y = keyboard_rect.topLeft().y();
        const auto diff = k_global_y - global_y;
        const auto need_to_move = diff < 0;
    
        /* move main widget */
        if (keyboard_visible && !_moved && need_to_move) {
            _moved = true;
            _lastDiff = diff;
            const auto g = parentWidget()->frameGeometry();
            parentWidget()->move(g.x(), g.y() - qAbs(_lastDiff));
        }
        /* roll back */
        if (!keyboard_visible && _moved) {
            _moved = false;
            const auto g = parentWidget()->frameGeometry();
            parentWidget()->move(g.x(), g.y() + qAbs(_lastDiff));
        }
        return QLineEdit::event(e);
    }
    

    main.cpp:

    #include <QtWidgets>
    
    #define W 1024
    #define H 768
    
    int main(int argc, char *argv[]) {
        qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
        QApplication app(argc, argv);
    
        QMainWindow window(nullptr, Qt::FramelessWindowHint);
    
        LineEdit lineedit1(&window);
        lineedit1.move(100, 450);
    
        LineEdit lineedit2(&window);
        lineedit2.move(100, 100);
    
        window.resize(W, H);
        window.show();
        return app.exec();
    }
    

    results:

    enter image description here