Alex Spataru
Alex Spataru

Reputation: 1217

Resize QML window when the virtual keyboard is shown

I am writing a chat application using Qt/QML. However, I found an issue while testing the application on my Android device: the virtual keyboard "moves" the window upward and does not allow me to see many of the displayed messages, only the bottom part of my app.

Ideally, I would like to resize the window so that both the message controls (such as the text box and attach files button) and the title bar to be shown. For a graphical example, you can take a look at this:

screenshot of UI with keyboard shown

Is it possible to do this in QML?

Upvotes: 5

Views: 2935

Answers (3)

tanius
tanius

Reputation: 16849

There is a QML-only way of reacting by resizing your window contents when the virtual keyboard is shown or hidden by the user.

First, make sure that the window resizing is not already done by Android for you (which is also possible). So you would tell Android that the keyboard should overlap the window, by adjusting the <activity> tag in AndroidManifest.xml as follows:

<activity ... android:windowSoftInputMode="adjustPan">

Then, you would place the following into a QML file where you have access to the window or window contents you want to resize and / or reposition:

Connections {
    target: Qt.inputMethod

    onKeyboardRectangleChanged: {
        var newRect = Qt.inputMethod.keyboardRectangle

        console.log(
            "New keyboard rectangle size:" + 
            " x: " + newRect.x + 
            " y: " + newRect.y + 
            " width: " + newRect.width + 
            " height: " + newRect.height
        )

        // Your UI resizing / repositioning code goes here.
    }
}

Explanations and details:

  • The Qt QML Type is not instantiable (source), so you cannot write Qt { inputMethod.onKeyboardRectangleChanged: { }}.

  • The Connections QML type is made for these cases, allowing to implement a signal handler outside of the object emitting it (details).

  • Another alternative is to connect the signal to a JavaScript function with connect(), as demonstrated here.

  • The QRectF type used in the underlaying C++ class QInputMethod is available in QML as QML Basic Type rect. This is documented here:

    When integrating with C++, note that any QRect or QRectF value passed into QML from C++ is automatically converted into a rect value, and vice-versa.

  • You should not implement this in a onVisibleChanged signal handler because that event is not fired on Android when the user clicks the "hide keyboard" button. (Tested with Android 6.0 and Qt 5.12.) This seems to be a bug in Qt, since a keyboard of height 0 is definitely not visible.

Upvotes: 1

tanius
tanius

Reputation: 16849

You can tell Android to do this for you.

Android will resize your application window whenever the virtual keyboard shows up after you adjust the <activity> tag of your AndroidManifest.xml like this:

<activity ... android:windowSoftInputMode="adjustResize">

Source: This was discussed as a workaround in two comments on a Qt bug that prevented manually resizing the window for some time until the end of 2015.

Upvotes: 3

kcrossen
kcrossen

Reputation: 179

Deploying to Android 10 from Qt 5.12 (C++, no QML required). There don't seem to be any non-QML C++ examples out there of resizing an application in response to on-screen keyboard visibility changes. The ones I did find require interfacing w/ Java from Qt4.

It's necessary to create a container, separate from the QMainWindow, for all of your visible UI. QMainWindow normally occupies the entire screen and will be overlapped by the on-screen keyboard. The container QWidget is what can be resized and must contain every UI element you expect not to be under the keyboard.

The example uses QFrame as being a very minimal (lightweight) container.

YourApp.cpp:

YourApp::YourApp ( QWidget *parent ) : QMainWindow ( parent ) {

    // With Android, an application running normally ...
    // ... occupies the whole screen. Plan accordingly.
    QSize availableSize = qApp->desktop()->availableGeometry().size();
    Application_Width = availableSize.width();
    Application_Height = availableSize.height();

    App_Frame = new QFrame(this);
    // Build your UI inside this QFrame

    setCentralWidget(App_Frame);

    Virtual_Keyboard_Enabled = true;
    App_Input_Method = QApplication::inputMethod();
    connect(App_Input_Method, SIGNAL(keyboardRectangleChanged()),
            this, SLOT(onKeyboardRectangleChanged()));

    this->show();
}

void
YourApp::onKeyboardRectangleChanged ( ) {
#if defined(Q_OS_ANDROID)
    bool keyboard_visible = App_Input_Method->isVisible();
    QRectF keyboard_rectangle = App_Input_Method->keyboardRectangle();

    if (not keyboard_visible) {
        App_Frame->resize(Application_Width, Application_Height);
    }
    else {
        int keyboard_height = int(keyboard_rectangle.height());
        App_Frame->resize(Application_Width, 
                          (Application_Height - keyboard_height));
    }
#endif
}

void
YourApp::Toggle_Virtual_Keyboard_Enabled ( ) {
#if defined(Q_OS_ANDROID)
    Virtual_Keyboard_Enabled = not Virtual_Keyboard_Enabled;
    App_Input_Method->setVisible(Virtual_Keyboard_Enabled);
    qApp->setAutoSipEnabled(Virtual_Keyboard_Enabled);
#endif
}

YourApp.h:

class YourApp : public QMainWindow {
    Q_OBJECT

public:
    YourApp ( QWidget *parent = nullptr );
    ~YourApp ( );

private:
    bool Virtual_Keyboard_Enabled;
    QInputMethod *App_Input_Method;

    QFrame *App_Frame;

    void
    Toggle_Virtual_Keyboard_Enabled ( );

private slots:
    void
    onKeyboardRectangleChanged ( );
}

Upvotes: 1

Related Questions