WebEngineView + 虚拟键盘 - 调整大小后保持滚动位置(集中输入)

WebEngineView + virtual keyboard - maintain scroll position after resizing (focused input)

我有一个非常简单的浏览器应用程序,它基于 WebEngineView 和用 Qt Quick 制作的虚拟键盘。

一切正常 - 每次我在 web 视图中单击输入时,键盘都会完美显示,但令我困扰的是,如果我单击底部的输入,键盘会在打开后覆盖它并我看不到我输入的内容。

我尝试通过调整 WebEngineView 元素的大小以适应键盘高度来解决此问题,就像大多数移动应用程序一样。它有效,我可以在键盘下滚动页面,但键盘仍然覆盖输入,我需要手动滚动。

有什么方法可以调整 Web 视图滚动位置,使键盘不覆盖来自 QML 的聚焦输入? 我无法在单个网站上执行此操作,因为我允许导航到用户想要的任何网站,因此我需要一些通用方法。

这是我的 qml 代码:

import QtQuick 2.12
import QtQuick.Window 2.12
import FreeVirtualKeyboard 1.0
import QtWebEngine 1.8

Window {
    id: appContainer;
    visible: true
    width: 1280
    height: 600
    title: qsTr("WebEngineView")
    property string pathUrl: "https://www.w3schools.com/html/html_forms.asp"

    WebEngineView {
        id: webview
        width: appContainer.width
        url: appContainer.pathUrl
        height: appContainer.height
    }

    /*
      Virtual keyboard
    */
     InputPanel {
        id: inputPanel
        z: 99
        y: appContainer.height
        anchors.left: parent.left
        anchors.right: parent.right
        states: State {
            name: "visible"
            when: Qt.inputMethod.visible
            PropertyChanges {
                target: inputPanel
                y: appContainer.height - inputPanel.height

            }
        }
        transitions: Transition {
            from: ""
            to: "visible"
            reversible: true
            ParallelAnimation {
                NumberAnimation {
                    properties: "y"
                    duration: 150
                    easing.type: Easing.InOutQuad
                }
            }

            onRunningChanged: {
                if(!running && inputPanel.state == "visible") {
                    // finished showing keyboard
                    webview.height = appContainer.height - inputPanel.height
                    console.log('Keyboard shown')
                } else if(running && inputPanel.state != "visible") {
                    // begins to hide keyboard
                    webview.height = appContainer.height
                    console.log('Keyboard starts to hide');
                }
            }
        }
    }
}

到目前为止,调整大小部分工作正常 - 我在 onRunningChanged 中执行此操作,因此 webview 在过渡开始之前和结束之后调整大小 - 这可以防止在过渡期间显示丑陋的空白 space。

更新 在显示键盘后,我使用 webview.runJavaScriptscrollIntoView 达到了我想要的效果:

webview.runJavaScript("document.activeElement.scrollIntoView({block: 'nearest', inline: 'nearest', behavior: 'smooth'})");

但是我不确定这是否是最好的解决方案,因为我不喜欢在过程中涉及 javascript 评估这一事实。我想知道是否有更多的“本地”方式来执行此操作。

调整 WebEngineView 的大小,滚动到视图中

调整 WebEngineView 大小的问题是 HTML 会看到您的设备屏幕突然缩小,并可能决定呈现截然不同的布局,例如将菜单从顶部移动到屏幕的另一侧屏幕.

即使这没有发生,布局也发生了变化。新“屏幕”上的位置与旧“屏幕”上的位置不对应,没有 1:1 关系,这就是为什么它首先滚动到看似随机的位置。

我们可以tell webpage to scroll a focused element into view of new viewport:

  • 如果它已经出现在屏幕上,那么什么也不会发生。
  • 如果不是,则网页会滚动以使该元素尽可能适合屏幕。 scrollIntoView 具有根据需要滚动到屏幕 top/bottom 的参数

所以当屏幕键盘打开时:

  1. 保存原件scrollPosition
  2. 调整大小WebEngineView
  3. 可选择将 scrollPosition 分配给保存的值 - 尽管它可能对您没有任何好处
  4. runJavaScript确定activeElement,使scrollIntoView

关闭屏幕键盘时重复相同的步骤。

不调整大小,手动滚动

另一种方法是不调整“屏幕”的大小,如果元素被覆盖,则将其滚动到视图中。

这需要 Qt 进行更改 VisualViewport while leaving LayoutViewport intact (see this and this 以获取更多信息)但似乎 Qt 无法做到这一点,至少不能单独通过 QML。

这让我们不得不手动完成:用 getBoundingClientRect, calculate how much space does keyboard cover, and if it is not inside our calculated uncovered view area - scrollTo 确定位置。

(您仍然需要 runJavaScript 才能进入网页)

也许 this and this SO 问题可以提供帮助

其他选项

@Hazelnutek 报告说 document.activeElement.scrollIntoViewIfNeeded()

取得了一些成功

请参阅下面对此答案的评论中的讨论: