diff options
Diffstat (limited to 'examples')
44 files changed, 1346 insertions, 31 deletions
diff --git a/examples/webengine/customdialogs/WebView.qml b/examples/webengine/customdialogs/WebView.qml index 0715bc709..d754ea7dc 100644 --- a/examples/webengine/customdialogs/WebView.qml +++ b/examples/webengine/customdialogs/WebView.qml @@ -49,7 +49,7 @@ ****************************************************************************/ import QtQuick 2.0 -import QtWebEngine 1.4 +import QtWebEngine 1.10 WebEngineView { @@ -57,6 +57,32 @@ WebEngineView { property bool useDefaultDialogs: true signal openForm(var form) + Rectangle { + id: tooltip + width: 200 + height: 30 + z: 50 + visible: false + color: "gray" + border.color: "black" + border.width: 2 + radius: 3 + + property string text: "" + + Text { + x: 0 + y: 0 + color: "#ffffff" + text: parent.text + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.bold: false + } + + } + onContextMenuRequested: function(request) { // we only show menu for links with #openMenu if (!request.linkUrl.toString().endsWith("#openMenu")) { @@ -72,6 +98,22 @@ WebEngineView { properties: {"request": request}}); } + onTooltipRequested: function(request) { + if (useDefaultDialogs) + return; + + if (request.type == TooltipRequest.Show) { + tooltip.visible = true; + tooltip.x = request.x; + tooltip.y = request.y; + tooltip.text = request.text; + } else { + tooltip.visible = false; + } + + request.accepted = true; + } + onAuthenticationDialogRequested: function(request) { if (useDefaultDialogs) return; diff --git a/examples/webengine/customdialogs/doc/images/customdialogs-auth1.png b/examples/webengine/customdialogs/doc/images/customdialogs-auth1.png Binary files differindex 2bde8bd8e..5e8f8d6bd 100644 --- a/examples/webengine/customdialogs/doc/images/customdialogs-auth1.png +++ b/examples/webengine/customdialogs/doc/images/customdialogs-auth1.png diff --git a/examples/webengine/customdialogs/doc/images/customdialogs-auth2.png b/examples/webengine/customdialogs/doc/images/customdialogs-auth2.png Binary files differindex ce358fca0..41828d36d 100644 --- a/examples/webengine/customdialogs/doc/images/customdialogs-auth2.png +++ b/examples/webengine/customdialogs/doc/images/customdialogs-auth2.png diff --git a/examples/webengine/customdialogs/doc/images/customdialogs-color1.png b/examples/webengine/customdialogs/doc/images/customdialogs-color1.png Binary files differindex a51d1bdd3..9208045b2 100644 --- a/examples/webengine/customdialogs/doc/images/customdialogs-color1.png +++ b/examples/webengine/customdialogs/doc/images/customdialogs-color1.png diff --git a/examples/webengine/customdialogs/doc/images/customdialogs-color2.png b/examples/webengine/customdialogs/doc/images/customdialogs-color2.png Binary files differindex 3b0b2e986..9087fdf14 100644 --- a/examples/webengine/customdialogs/doc/images/customdialogs-color2.png +++ b/examples/webengine/customdialogs/doc/images/customdialogs-color2.png diff --git a/examples/webengine/customdialogs/doc/images/customdialogs-file1.png b/examples/webengine/customdialogs/doc/images/customdialogs-file1.png Binary files differindex 0ff39bf38..ba8bdf78c 100644 --- a/examples/webengine/customdialogs/doc/images/customdialogs-file1.png +++ b/examples/webengine/customdialogs/doc/images/customdialogs-file1.png diff --git a/examples/webengine/customdialogs/doc/images/customdialogs-file2.png b/examples/webengine/customdialogs/doc/images/customdialogs-file2.png Binary files differindex e56078c44..aa25579d7 100644 --- a/examples/webengine/customdialogs/doc/images/customdialogs-file2.png +++ b/examples/webengine/customdialogs/doc/images/customdialogs-file2.png diff --git a/examples/webengine/customdialogs/doc/images/customdialogs-prompt1.png b/examples/webengine/customdialogs/doc/images/customdialogs-prompt1.png Binary files differindex 988b4deea..e36ba4a13 100644 --- a/examples/webengine/customdialogs/doc/images/customdialogs-prompt1.png +++ b/examples/webengine/customdialogs/doc/images/customdialogs-prompt1.png diff --git a/examples/webengine/customdialogs/doc/images/customdialogs-prompt2.png b/examples/webengine/customdialogs/doc/images/customdialogs-prompt2.png Binary files differindex 085339378..2c8d92649 100644 --- a/examples/webengine/customdialogs/doc/images/customdialogs-prompt2.png +++ b/examples/webengine/customdialogs/doc/images/customdialogs-prompt2.png diff --git a/examples/webengine/customdialogs/doc/images/customdialogs-tooltip.png b/examples/webengine/customdialogs/doc/images/customdialogs-tooltip.png Binary files differnew file mode 100644 index 000000000..498de9595 --- /dev/null +++ b/examples/webengine/customdialogs/doc/images/customdialogs-tooltip.png diff --git a/examples/webengine/customdialogs/doc/images/customdialogs.png b/examples/webengine/customdialogs/doc/images/customdialogs.png Binary files differindex 13322d2f6..c42114a16 100644 --- a/examples/webengine/customdialogs/doc/images/customdialogs.png +++ b/examples/webengine/customdialogs/doc/images/customdialogs.png diff --git a/examples/webengine/customdialogs/doc/src/customdialogs.qdoc b/examples/webengine/customdialogs/doc/src/customdialogs.qdoc index 5c550ed5e..6319ce53b 100644 --- a/examples/webengine/customdialogs/doc/src/customdialogs.qdoc +++ b/examples/webengine/customdialogs/doc/src/customdialogs.qdoc @@ -140,6 +140,38 @@ To keep things simple, we do not provide any logic on component completion, and we simply close the form on any action. + \section2 Tooltip Requests + + \l [QML]{TooltipRequest} is a request object that is passed as a + parameter of the WebEngineView::tooltipRequested signal. We use the + \c onTooltipRequested signal handler to handle requests for + custom tooltip menus at specific positions: + + \quotefromfile webengine/customdialogs/WebView.qml + \skipto WebEngineView + \printuntil { + \dots 4 + \skipto onTooltipRequested + \printuntil } + \printuntil } + \printuntil } + \dots 4 + \skipuntil onFileDialogRequested + \skipuntil }}); + \skipuntil } + \skipto } + \printline } + + The second text field from the top on our page triggers the request. Next, + we check whether we should use the default menu. If not, we accept the + request and show a custom QML element as tooltip: + + \image customdialogs-tooltip.png + + \quotefromfile webengine/customdialogs/WebView.qml + \skipto Rectangle + \printuntil } + \section2 Authentication Dialog Requests \image customdialogs-auth1.png diff --git a/examples/webengine/customdialogs/index.html b/examples/webengine/customdialogs/index.html index 490dd79fd..69c0e6b21 100644 --- a/examples/webengine/customdialogs/index.html +++ b/examples/webengine/customdialogs/index.html @@ -11,6 +11,9 @@ <td><div class="div"><a href="#openMenu" class="link">Right click on text to see link context menu</a></div></td> </tr> <tr> + <td><div class="div"><p title="I am a tooltip.">Hover this text to display a tooltip</a></div></td> + </tr> + <tr> <td><button class="button" onclick="window.location = 'http://localhost.:5555/OPEN_AUTH'"> Open Authentication Dialog</button></td> </tr> diff --git a/examples/webengine/lifecycle/WebBrowser.qml b/examples/webengine/lifecycle/WebBrowser.qml new file mode 100644 index 000000000..23c2500e2 --- /dev/null +++ b/examples/webengine/lifecycle/WebBrowser.qml @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Window 2.12 + +ApplicationWindow { + id: root + + readonly property Action newTabAction: Action { + text: qsTr("New tab") + shortcut: StandardKey.AddTab + onTriggered: root.createNewTab({url: "about:blank"}) + } + + visible: true + width: Screen.width * 0.5 + height: Screen.height * 0.5 + title: tabStack.currentTab ? tabStack.currentTab.title : "" + + header: WebTabBar { + id: tabBar + + z: 1 + + newTabAction: root.newTabAction + } + + WebTabStack { + id: tabStack + + z: 0 + anchors.fill: parent + + currentIndex: tabBar.currentIndex + freezeDelay: freezeSpin.enabled && freezeSpin.value + discardDelay: discardSpin.enabled && discardSpin.value + + onCloseRequested: function(index) { + root.closeTab(index) + } + + onDrawerRequested: drawer.toggle() + } + + Drawer { + id: drawer + + edge: Qt.RightEdge + height: root.height + + Control { + padding: 16 + contentItem: ColumnLayout { + Label { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Settings") + font.capitalization: Font.AllUppercase + } + MenuSeparator {} + CheckBox { + id: lifecycleCheck + text: qsTr("Automatic lifecycle control") + checked: true + } + CheckBox { + id: freezeCheck + text: qsTr("Freeze after delay (seconds)") + enabled: lifecycleCheck.checked + checked: true + } + SpinBox { + id: freezeSpin + editable: true + enabled: freezeCheck.checked + value: 60 + from: 1 + to: 60*60 + } + CheckBox { + id: discardCheck + text: qsTr("Discard after delay (seconds)") + enabled: lifecycleCheck.checked + checked: true + } + SpinBox { + id: discardSpin + editable: true + enabled: discardCheck.checked + value: 60*60 + from: 1 + to: 60*60 + } + } + } + + function toggle() { + if (drawer.visible) + drawer.close() + else + drawer.open() + } + } + + Component.onCompleted: { + createNewTab({url: "https://www.qt.io"}) + } + + function createNewTab(properties) { + const tab = tabStack.createNewTab(properties) + tabBar.createNewTab({tab: tab}) + tabBar.currentIndex = tab.index + return tab + } + + function closeTab(index) { + if (tabStack.count == 1) + Qt.quit() + tabBar.closeTab(index) + tabStack.closeTab(index) + } +} diff --git a/examples/webengine/lifecycle/WebTab.qml b/examples/webengine/lifecycle/WebTab.qml new file mode 100644 index 000000000..645758104 --- /dev/null +++ b/examples/webengine/lifecycle/WebTab.qml @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQml 2.12 +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Layouts 1.12 +import QtWebEngine 1.11 + +ColumnLayout { + id: root + + signal closeRequested + signal drawerRequested + + property int freezeDelay + property int discardDelay + + property alias icon : view.icon + property alias loading : view.loading + property alias url : view.url + property alias lifecycleState : view.lifecycleState + property alias recommendedState : view.recommendedState + + readonly property string title : { + if (view.url == "about:blank") + return qsTr("New tab") + if (view.title) + return view.title + return view.url + } + + readonly property Action backAction: Action { + property WebEngineAction webAction: view.action(WebEngineView.Back) + enabled: webAction.enabled + text: qsTr("Back") + shortcut: root.visible && StandardKey.Back + onTriggered: webAction.trigger() + } + + readonly property Action forwardAction: Action { + property WebEngineAction webAction: view.action(WebEngineView.Forward) + enabled: webAction.enabled + text: qsTr("Forward") + shortcut: root.visible && StandardKey.Forward + onTriggered: webAction.trigger() + } + + readonly property Action reloadAction: Action { + property WebEngineAction webAction: view.action(WebEngineView.Reload) + enabled: webAction.enabled + text: qsTr("Reload") + shortcut: root.visible && StandardKey.Refresh + onTriggered: webAction.trigger() + } + + readonly property Action stopAction: Action { + property WebEngineAction webAction: view.action(WebEngineView.Stop) + enabled: webAction.enabled + text: qsTr("Stop") + shortcut: root.visible && StandardKey.Cancel + onTriggered: webAction.trigger() + } + + readonly property Action closeAction: Action { + text: qsTr("Close") + shortcut: root.visible && StandardKey.Close + onTriggered: root.closeRequested() + } + + readonly property Action activateAction: Action { + text: qsTr("Active") + checkable: true + checked: view.lifecycleState == WebEngineView.LifecycleState.Active + enabled: checked || (view.lifecycleState != WebEngineView.LifecycleState.Active) + onTriggered: view.lifecycleState = WebEngineView.LifecycleState.Active + } + + readonly property Action freezeAction: Action { + text: qsTr("Frozen") + checkable: true + checked: view.lifecycleState == WebEngineView.LifecycleState.Frozen + enabled: checked || (!view.visible && view.lifecycleState == WebEngineView.LifecycleState.Active) + onTriggered: view.lifecycleState = WebEngineView.LifecycleState.Frozen + } + + readonly property Action discardAction: Action { + text: qsTr("Discarded") + checkable: true + checked: view.lifecycleState == WebEngineView.LifecycleState.Discarded + enabled: checked || (!view.visible && view.lifecycleState == WebEngineView.LifecycleState.Frozen) + onTriggered: view.lifecycleState = WebEngineView.LifecycleState.Discarded + } + + spacing: 0 + + ToolBar { + Layout.fillWidth: true + Material.elevation: 0 + Material.background: Material.color(Material.Grey, Material.Shade800) + + RowLayout { + anchors.fill: parent + WebToolButton { + action: root.backAction + text: "←" + ToolTip.text: root.backAction.text + } + WebToolButton { + action: root.forwardAction + text: "→" + ToolTip.text: root.forwardAction.text + } + WebToolButton { + action: root.reloadAction + visible: root.reloadAction.enabled + text: "↻" + ToolTip.text: root.reloadAction.text + } + WebToolButton { + action: root.stopAction + visible: root.stopAction.enabled + text: "✕" + ToolTip.text: root.stopAction.text + } + TextField { + Layout.fillWidth: true + Layout.topMargin: 6 + + placeholderText: qsTr("Type a URL") + text: view.url == "about:blank" ? "" : view.url + selectByMouse: true + + onAccepted: { view.url = text } + } + WebToolButton { + text: "⋮" + ToolTip.text: qsTr("Settings") + onClicked: root.drawerRequested() + } + } + } + + WebEngineView { + id: view + Layout.fillHeight: true + Layout.fillWidth: true + } + + Timer { + interval: { + switch (view.recommendedState) { + case WebEngineView.LifecycleState.Active: + return 1 + case WebEngineView.LifecycleState.Frozen: + return root.freezeDelay * 1000 + case WebEngineView.LifecycleState.Discarded: + return root.discardDelay * 1000 + } + } + running: interval && view.lifecycleState != view.recommendedState + onTriggered: view.lifecycleState = view.recommendedState + } +} diff --git a/examples/webengine/lifecycle/WebTabBar.qml b/examples/webengine/lifecycle/WebTabBar.qml new file mode 100644 index 000000000..326ad39d2 --- /dev/null +++ b/examples/webengine/lifecycle/WebTabBar.qml @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Layouts 1.12 + +Pane { + id: root + + property Action newTabAction + + property alias currentIndex: tabBar.currentIndex + + signal closeRequested(int index) + + Material.background: Material.color(Material.Grey, Material.Shade900) + Material.elevation: 4 + padding: 0 + + RowLayout { + spacing: 0 + anchors.fill: parent + + TabBar { + id: tabBar + Layout.fillWidth: true + Layout.fillHeight: true + } + + WebToolButton { + Layout.bottomMargin: 2 + + action: root.newTabAction + text: "+" + ToolTip.text: root.newTabAction.text + } + } + + Component { + id: factory + WebTabButton {} + } + + function createNewTab(properties) { + return factory.createObject(tabBar, properties) + } + + function closeTab(index) { + tabBar.takeItem(index).destroy() + } +} diff --git a/examples/webengine/lifecycle/WebTabButton.qml b/examples/webengine/lifecycle/WebTabButton.qml new file mode 100644 index 000000000..815e2fb09 --- /dev/null +++ b/examples/webengine/lifecycle/WebTabButton.qml @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Layouts 1.12 +import QtWebEngine 1.11 + +TabButton { + id: root + + property WebTab tab + + text: root.tab.title + + ToolTip.delay: 1000 + ToolTip.visible: root.hovered + ToolTip.text: root.text + + padding: 6 + + contentItem: RowLayout { + Item { + implicitWidth: 16 + implicitHeight: 16 + BusyIndicator { + visible: root.tab.loading + anchors.fill: parent + leftInset: 0 + topInset: 0 + rightInset: 0 + bottomInset: 0 + padding: 0 + } + Image { + visible: !root.tab.loading + source: root.tab.icon + anchors.fill: parent + } + } + Label { + Layout.fillWidth: true + Layout.leftMargin: 4 + Layout.rightMargin: 4 + text: root.text + elide: Text.ElideRight + color: { + switch (root.tab.lifecycleState) { + case WebEngineView.LifecycleState.Active: + return Material.color(Material.Grey, Material.Shade100) + case WebEngineView.LifecycleState.Frozen: + return Material.color(Material.Blue, Material.Shade400) + case WebEngineView.LifecycleState.Discarded: + return Material.color(Material.Red, Material.Shade400) + } + } + + } + WebToolButton { + action: root.tab.closeAction + text: "✕" + ToolTip.text: action.text + } + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + propagateComposedEvents: true + onClicked: contextMenu.popup() + } + + Menu { + id: contextMenu + Control { + contentItem: Label { + text: qsTr("Manual lifecycle control") + } + verticalPadding: 9 + horizontalPadding: 14 + } + Repeater { + model: [root.tab.activateAction, root.tab.freezeAction, root.tab.discardAction] + RadioButton { + action: modelData + verticalPadding: 9 + horizontalPadding: 14 + } + } + Control { + contentItem: Label { + text: qsTr("Recommended: %1").arg(recommendedStateText) + property string recommendedStateText: { + switch (root.tab.recommendedState) { + case WebEngineView.LifecycleState.Active: + return root.tab.activateAction.text + case WebEngineView.LifecycleState.Frozen: + return root.tab.freezeAction.text + case WebEngineView.LifecycleState.Discarded: + return root.tab.discardAction.text + } + } + color: Material.hintTextColor + } + font.pointSize: 8 + verticalPadding: 9 + horizontalPadding: 14 + } + } +} diff --git a/examples/webengine/lifecycle/WebTabStack.qml b/examples/webengine/lifecycle/WebTabStack.qml new file mode 100644 index 000000000..75cbba861 --- /dev/null +++ b/examples/webengine/lifecycle/WebTabStack.qml @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQml.Models 2.12 +import QtQuick 2.12 +import QtQuick.Controls 2.12 + +Rectangle { + id: root + + signal closeRequested(int index) + signal drawerRequested + + property int freezeDelay + property int discardDelay + + property int currentIndex + property var currentTab: model.children[currentIndex] + property alias count: model.count + + color: "white" + + ObjectModel { + id: model + } + + Component { + id: factory + WebTab { + readonly property int index : ObjectModel.index + anchors.fill: parent + visible: index == root.currentIndex + freezeDelay: root.freezeDelay + discardDelay: root.discardDelay + onCloseRequested: root.closeRequested(index) + onDrawerRequested: root.drawerRequested() + } + } + + function createNewTab(properties) { + const tab = factory.createObject(root, properties) + model.append(tab) + return tab + } + + function closeTab(index) { + const tab = model.get(index) + model.remove(index) + tab.destroy() + } +} diff --git a/examples/webengine/lifecycle/WebToolButton.qml b/examples/webengine/lifecycle/WebToolButton.qml new file mode 100644 index 000000000..958c083cd --- /dev/null +++ b/examples/webengine/lifecycle/WebToolButton.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 + +ToolButton { + id: root + font.bold: true + font.pointSize: 12 + ToolTip.delay: 1000 + ToolTip.visible: hovered + implicitWidth: 32 + implicitHeight: 32 +} diff --git a/examples/webengine/lifecycle/doc/images/lifecycle-automatic.png b/examples/webengine/lifecycle/doc/images/lifecycle-automatic.png Binary files differnew file mode 100644 index 000000000..2c62967af --- /dev/null +++ b/examples/webengine/lifecycle/doc/images/lifecycle-automatic.png diff --git a/examples/webengine/lifecycle/doc/images/lifecycle-manual.png b/examples/webengine/lifecycle/doc/images/lifecycle-manual.png Binary files differnew file mode 100644 index 000000000..7fb5c5a80 --- /dev/null +++ b/examples/webengine/lifecycle/doc/images/lifecycle-manual.png diff --git a/examples/webengine/lifecycle/doc/images/lifecycle.png b/examples/webengine/lifecycle/doc/images/lifecycle.png Binary files differnew file mode 100644 index 000000000..87b719022 --- /dev/null +++ b/examples/webengine/lifecycle/doc/images/lifecycle.png diff --git a/examples/webengine/lifecycle/doc/src/lifecycle.qdoc b/examples/webengine/lifecycle/doc/src/lifecycle.qdoc new file mode 100644 index 000000000..d10e617e3 --- /dev/null +++ b/examples/webengine/lifecycle/doc/src/lifecycle.qdoc @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example webengine/lifecycle + \title WebEngine Lifecycle Example + \ingroup webengine-examples + \brief Freezes and discards background tabs to reduce CPU and memory usage. + + \image lifecycle.png + + \e {WebEngine Lifecycle Example} demonstrates how the \l + {WebEngineView::}{lifecycleState} and \l {WebEngineView::}{recommendedState} + properties of the \l {WebEngineView} can be used to reduce the CPU and + memory usage of background tabs in a tabbed browser. + + For an overview of the lifecycle feature, see \l {Page Lifecycle API}. + + \include examples-run.qdocinc + + \section1 UI Elements of the Example + + The example uses \l {Qt Quick Controls 2} to implement a traditional tabbed + browser in the \l {Material Style} (dark variant). The main application + window (\c {WebBrowser.qml}) is divided into a header bar at the top and a + main viewing area filling the rest of the window. The header contains the + tab bar (\c {WebTabBar.qml}) with one button per tab (\c + {WebTabButton.qml}). The main area consists of a stack of tabs (\c + {WebTabStack.qml} and \c {WebTab.qml}). Each tab in turn has a tool bar at + the top and a \l {WebEngineView} for displaying web pages. Finally, the main + window also has a \l {Drawer} for changing settings. The drawer can be + opened by clicking the "⋮" button on the tool bar. + + \section1 Lifecycle States in the Example + + The example implements two ways of changing the lifecycle state: manual and + automatic. The manual way uses the \l {WebEngineView::}{lifecycleState} + property directly to change the web view lifecycle state, while the + automatic way is timer-based and also takes into account the \l + {WebEngineView::}{recommendedState}. + + The tab titles in the tab bar are color coded with frozen tabs shown in blue + and discarded in red. + + \section2 Manual Lifecycle Control + + \image lifecycle-manual.png + + Manual control is provided by context menus on the tab bar buttons (\c + {WebTabButton.qml}). The menu has three radio buttons, one for each + lifecycle state, with the current state checked. Some buttons may be + disabled, either because they represent illegal state transitions (for + example, a \c {Discarded} view cannot directly transition to the \c {Frozen} + state), or because other preconditions are not fulfilled (for example, a + visible view can only be in the \c {Active} state). + + \section2 Automatic Lifecycle Control + + \image lifecycle-automatic.png + + Automatic control is implemented with a \l {Timer} in the \c {WebTab} + component (\c {WebTab.qml}). The timer is started whenever the \l + {WebEngineView::}{lifecycleState} of the web view does not match it's \l + {WebEngineView::}{recommendedState}. Once the timer fires, the view's + lifecycle state is set to the recommended state. + + The time delay is used to avoid changing the lifecycle state too quickly + when the user is switching between tabs. The freezing and discarding delays + can be changed in the settings drawer accessed through the "⋮" button on the + tool bar. + + This is a rather simple algorithm for automatic lifecycle control, however + more sophisticated algorithms could also be conceived and implemented on the + basis of the \l {WebEngineView::}{lifecycleState} property. For example, the + Chromium browser experimentally uses a pretrained deep neural network to + predict the next tab activation time by the user, essentially ranking tabs + based on how interesting they are to the user. Implementing such an + algorithm is left as an exercise to the reader for now. + +*/ diff --git a/examples/webengine/lifecycle/lifecycle.pro b/examples/webengine/lifecycle/lifecycle.pro new file mode 100644 index 000000000..74fbf23c1 --- /dev/null +++ b/examples/webengine/lifecycle/lifecycle.pro @@ -0,0 +1,10 @@ +TEMPLATE = app + +QT += quickcontrols2 webengine + +SOURCES += main.cpp + +RESOURCES += resources.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/webengine/lifecycle +INSTALLS += target diff --git a/examples/webengine/lifecycle/main.cpp b/examples/webengine/lifecycle/main.cpp new file mode 100644 index 000000000..83907cbaf --- /dev/null +++ b/examples/webengine/lifecycle/main.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QQmlContext> +#include <QQuickStyle> +#include <qtwebengineglobal.h> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication app(argc, argv); + QtWebEngine::initialize(); + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/WebBrowser.qml"))); + return app.exec(); +} diff --git a/examples/webengine/lifecycle/qtquickcontrols2.conf b/examples/webengine/lifecycle/qtquickcontrols2.conf new file mode 100644 index 000000000..68c77cec5 --- /dev/null +++ b/examples/webengine/lifecycle/qtquickcontrols2.conf @@ -0,0 +1,6 @@ +[Controls] +Style=Material + +[Material] +Theme=Dark +Variant=Dense diff --git a/examples/webengine/lifecycle/resources.qrc b/examples/webengine/lifecycle/resources.qrc new file mode 100644 index 000000000..41e5092ce --- /dev/null +++ b/examples/webengine/lifecycle/resources.qrc @@ -0,0 +1,11 @@ +<RCC> + <qresource prefix="/"> + <file>WebBrowser.qml</file> + <file>WebTab.qml</file> + <file>WebTabBar.qml</file> + <file>WebTabButton.qml</file> + <file>WebTabStack.qml</file> + <file>WebToolButton.qml</file> + <file>qtquickcontrols2.conf</file> + </qresource> +</RCC> diff --git a/examples/webengine/minimal/main.cpp b/examples/webengine/minimal/main.cpp index 348517ee0..86109f97a 100644 --- a/examples/webengine/minimal/main.cpp +++ b/examples/webengine/minimal/main.cpp @@ -56,6 +56,7 @@ int main(int argc, char *argv[]) { QCoreApplication::setOrganizationName("QtExamples"); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); QtWebEngine::initialize(); QGuiApplication app(argc, argv); diff --git a/examples/webengine/quicknanobrowser/ApplicationRoot.qml b/examples/webengine/quicknanobrowser/ApplicationRoot.qml index 3bc571546..df4bbdb4a 100644 --- a/examples/webengine/quicknanobrowser/ApplicationRoot.qml +++ b/examples/webengine/quicknanobrowser/ApplicationRoot.qml @@ -49,7 +49,7 @@ ****************************************************************************/ import QtQuick 2.1 -import QtWebEngine 1.9 +import QtWebEngine 1.10 QtObject { id: root diff --git a/examples/webengine/quicknanobrowser/BrowserWindow.qml b/examples/webengine/quicknanobrowser/BrowserWindow.qml index d095703fb..2d3168382 100644 --- a/examples/webengine/quicknanobrowser/BrowserWindow.qml +++ b/examples/webengine/quicknanobrowser/BrowserWindow.qml @@ -57,7 +57,7 @@ import QtQuick.Controls.Styles 1.0 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.0 import QtQuick.Window 2.1 -import QtWebEngine 1.9 +import QtWebEngine 1.10 ApplicationWindow { id: browserWindow @@ -73,6 +73,10 @@ ApplicationWindow { // Make sure the Qt.WindowFullscreenButtonHint is set on OS X. Component.onCompleted: flags = flags | Qt.WindowFullscreenButtonHint + onCurrentWebViewChanged: { + findBar.reset(); + } + // Create a styleItem to determine the platform. // When using style "mac", ToolButtons are not supposed to accept focus. QQCPrivate.StyleItem { id: styleItem } @@ -136,6 +140,9 @@ ApplicationWindow { fullScreenNotification.hide(); currentWebView.triggerWebAction(WebEngineView.ExitFullScreen); } + + if (findBar.visible) + findBar.visible = false; } } Action { @@ -187,6 +194,21 @@ ApplicationWindow { shortcut: StandardKey.Forward onTriggered: currentWebView.triggerWebAction(WebEngineView.Forward) } + Action { + shortcut: StandardKey.Find + onTriggered: { + if (!findBar.visible) + findBar.visible = true; + } + } + Action { + shortcut: StandardKey.FindNext + onTriggered: findBar.findNext() + } + Action { + shortcut: StandardKey.FindPrevious + onTriggered: findBar.findPrevious() + } toolBar: ToolBar { id: navigationBar @@ -553,6 +575,19 @@ ApplicationWindow { selection.certificates[0].select(); } + onFindTextFinished: function(result) { + if (!findBar.visible) + findBar.visible = true; + + findBar.numberOfMatches = result.numberOfMatches; + findBar.activeMatch = result.activeMatch; + } + + onLoadingChanged: function(loadRequest) { + if (loadRequest.status == WebEngineView.LoadStartedStatus) + findBar.reset(); + } + Timer { id: reloadTimer interval: 0 @@ -625,6 +660,28 @@ ApplicationWindow { download.accept(); } + FindBar { + id: findBar + visible: false + anchors.right: parent.right + anchors.rightMargin: 10 + anchors.top: parent.top + + onFindNext: { + if (text) + currentWebView && currentWebView.findText(text); + else if (!visible) + visible = true; + } + onFindPrevious: { + if (text) + currentWebView && currentWebView.findText(text, WebEngineView.FindBackward); + else if (!visible) + visible = true; + } + } + + Rectangle { id: statusBubble color: "oldlace" diff --git a/examples/webengine/quicknanobrowser/DownloadView.qml b/examples/webengine/quicknanobrowser/DownloadView.qml index f6ebeab4a..e26c770bc 100644 --- a/examples/webengine/quicknanobrowser/DownloadView.qml +++ b/examples/webengine/quicknanobrowser/DownloadView.qml @@ -101,7 +101,7 @@ Rectangle { } Label { id: label - text: path + text: downloadDirectory + "/" + downloadFileName anchors { verticalCenter: cancelButton.verticalCenter left: parent.left diff --git a/examples/webengine/quicknanobrowser/FindBar.qml b/examples/webengine/quicknanobrowser/FindBar.qml new file mode 100644 index 000000000..de407ac33 --- /dev/null +++ b/examples/webengine/quicknanobrowser/FindBar.qml @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.0 + +Rectangle { + id: root + + property int numberOfMatches: 0 + property int activeMatch: 0 + property alias text: findTextField.text + + function reset() { + numberOfMatches = 0; + activeMatch = 0; + visible = false; + } + + signal findNext() + signal findPrevious() + + width: 250 + height: 35 + radius: 2 + + border.width: 1 + border.color: "black" + color: "white" + + onVisibleChanged: { + if (visible) + findTextField.forceActiveFocus(); + } + + + RowLayout { + anchors.fill: parent + anchors.topMargin: 5 + anchors.bottomMargin: 5 + anchors.leftMargin: 10 + anchors.rightMargin: 10 + + spacing: 5 + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + + TextField { + id: findTextField + anchors.fill: parent + + style: TextFieldStyle { + background: Rectangle { + color: "transparent" + } + } + + onAccepted: root.findNext() + onTextChanged: root.findNext() + onActiveFocusChanged: activeFocus ? selectAll() : deselect() + } + } + + Label { + text: activeMatch + "/" + numberOfMatches + visible: findTextField.text != "" + } + + Rectangle { + border.width: 1 + border.color: "#ddd" + width: 2 + height: parent.height + anchors.topMargin: 5 + anchors.bottomMargin: 5 + } + + ToolButton { + text: "<" + enabled: numberOfMatches > 0 + onClicked: root.findPrevious() + } + + ToolButton { + text: ">" + enabled: numberOfMatches > 0 + onClicked: root.findNext() + } + + ToolButton { + text: "x" + onClicked: root.visible = false + } + } +} diff --git a/examples/webengine/quicknanobrowser/quicknanobrowser.pro b/examples/webengine/quicknanobrowser/quicknanobrowser.pro index 5a27f5fd4..922cf79e2 100644 --- a/examples/webengine/quicknanobrowser/quicknanobrowser.pro +++ b/examples/webengine/quicknanobrowser/quicknanobrowser.pro @@ -10,6 +10,7 @@ OTHER_FILES += ApplicationRoot.qml \ BrowserDialog.qml \ BrowserWindow.qml \ DownloadView.qml \ + FindBar.qml \ FullScreenNotification.qml RESOURCES += resources.qrc diff --git a/examples/webengine/quicknanobrowser/resources.qrc b/examples/webengine/quicknanobrowser/resources.qrc index c6270897d..50ea05f5e 100644 --- a/examples/webengine/quicknanobrowser/resources.qrc +++ b/examples/webengine/quicknanobrowser/resources.qrc @@ -4,6 +4,7 @@ <file>BrowserDialog.qml</file> <file>BrowserWindow.qml</file> <file>DownloadView.qml</file> + <file>FindBar.qml</file> <file>FullScreenNotification.qml</file> </qresource> <qresource prefix="icons"> diff --git a/examples/webengine/webengine.pro b/examples/webengine/webengine.pro index 5ad620390..058868395 100644 --- a/examples/webengine/webengine.pro +++ b/examples/webengine/webengine.pro @@ -8,5 +8,6 @@ SUBDIRS += \ qtHaveModule(quickcontrols2) { SUBDIRS += \ + lifecycle \ recipebrowser } diff --git a/examples/webenginewidgets/simplebrowser/browserwindow.cpp b/examples/webenginewidgets/simplebrowser/browserwindow.cpp index 5d00cd19a..c1d0ea359 100644 --- a/examples/webenginewidgets/simplebrowser/browserwindow.cpp +++ b/examples/webenginewidgets/simplebrowser/browserwindow.cpp @@ -66,6 +66,7 @@ #include <QStatusBar> #include <QToolBar> #include <QVBoxLayout> +#include <QWebEngineFindTextResult> #include <QWebEngineProfile> BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile, bool forDevTools) @@ -99,7 +100,7 @@ BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile, bool QWidget *centralWidget = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout; layout->setSpacing(0); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); if (!forDevTools) { addToolBarBreak(); @@ -129,6 +130,7 @@ BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile, bool connect(m_urlLineEdit, &QLineEdit::returnPressed, [this]() { m_tabWidget->setUrl(QUrl::fromUserInput(m_urlLineEdit->text())); }); + connect(m_tabWidget, &TabWidget::findTextFinished, this, &BrowserWindow::handleFindTextFinished); QAction *focusUrlLineEditAction = new QAction(this); addAction(focusUrlLineEditAction); @@ -460,10 +462,7 @@ void BrowserWindow::handleFindActionTriggered() m_lastSearch, &ok); if (ok && !search.isEmpty()) { m_lastSearch = search; - currentTab()->findText(m_lastSearch, 0, [this](bool found) { - if (!found) - statusBar()->showMessage(tr("\"%1\" not found.").arg(m_lastSearch)); - }); + currentTab()->findText(m_lastSearch); } } @@ -526,3 +525,14 @@ void BrowserWindow::handleDevToolsRequested(QWebEnginePage *source) source->setDevToolsPage(m_browser->createDevToolsWindow()->currentTab()->page()); source->triggerAction(QWebEnginePage::InspectElement); } + +void BrowserWindow::handleFindTextFinished(const QWebEngineFindTextResult &result) +{ + if (result.numberOfMatches() == 0) { + statusBar()->showMessage(tr("\"%1\" not found.").arg(m_lastSearch)); + } else { + statusBar()->showMessage(tr("\"%1\" found: %2/%3").arg(m_lastSearch, + QString::number(result.activeMatch()), + QString::number(result.numberOfMatches()))); + } +} diff --git a/examples/webenginewidgets/simplebrowser/browserwindow.h b/examples/webenginewidgets/simplebrowser/browserwindow.h index 8f328b751..11a655469 100644 --- a/examples/webenginewidgets/simplebrowser/browserwindow.h +++ b/examples/webenginewidgets/simplebrowser/browserwindow.h @@ -88,6 +88,7 @@ private slots: void handleWebViewTitleChanged(const QString &title); void handleWebActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled); void handleDevToolsRequested(QWebEnginePage *source); + void handleFindTextFinished(const QWebEngineFindTextResult &result); private: QMenu *createFileMenu(TabWidget *tabWidget); diff --git a/examples/webenginewidgets/simplebrowser/downloadmanagerwidget.cpp b/examples/webenginewidgets/simplebrowser/downloadmanagerwidget.cpp index e0a511475..b6f9e9c13 100644 --- a/examples/webenginewidgets/simplebrowser/downloadmanagerwidget.cpp +++ b/examples/webenginewidgets/simplebrowser/downloadmanagerwidget.cpp @@ -55,6 +55,7 @@ #include "downloadwidget.h" #include <QFileDialog> +#include <QDir> #include <QWebEngineDownloadItem> DownloadManagerWidget::DownloadManagerWidget(QWidget *parent) @@ -68,11 +69,12 @@ void DownloadManagerWidget::downloadRequested(QWebEngineDownloadItem *download) { Q_ASSERT(download && download->state() == QWebEngineDownloadItem::DownloadRequested); - QString path = QFileDialog::getSaveFileName(this, tr("Save as"), download->path()); + QString path = QFileDialog::getSaveFileName(this, tr("Save as"), QDir(download->downloadDirectory()).filePath(download->downloadFileName())); if (path.isEmpty()) return; - download->setPath(path); + download->setDownloadDirectory(QFileInfo(path).path()); + download->setDownloadFileName(QFileInfo(path).fileName()); download->accept(); add(new DownloadWidget(download)); diff --git a/examples/webenginewidgets/simplebrowser/downloadwidget.cpp b/examples/webenginewidgets/simplebrowser/downloadwidget.cpp index 1d599a19d..ddddc5e5d 100644 --- a/examples/webenginewidgets/simplebrowser/downloadwidget.cpp +++ b/examples/webenginewidgets/simplebrowser/downloadwidget.cpp @@ -57,10 +57,11 @@ DownloadWidget::DownloadWidget(QWebEngineDownloadItem *download, QWidget *parent) : QFrame(parent) , m_download(download) - , m_timeAdded(QTime::currentTime()) + , m_timeAdded() { + m_timeAdded.start(); setupUi(this); - m_dstName->setText(QFileInfo(m_download->path()).fileName()); + m_dstName->setText(m_download->downloadFileName()); m_srcUrl->setText(m_download->url().toDisplayString()); connect(m_cancelButton, &QPushButton::clicked, diff --git a/examples/webenginewidgets/simplebrowser/downloadwidget.h b/examples/webenginewidgets/simplebrowser/downloadwidget.h index c08b298bc..c20676aa6 100644 --- a/examples/webenginewidgets/simplebrowser/downloadwidget.h +++ b/examples/webenginewidgets/simplebrowser/downloadwidget.h @@ -54,7 +54,7 @@ #include "ui_downloadwidget.h" #include <QFrame> -#include <QTime> +#include <QElapsedTimer> QT_BEGIN_NAMESPACE class QWebEngineDownloadItem; @@ -80,7 +80,7 @@ private: QString withUnit(qreal bytes); QWebEngineDownloadItem *m_download; - QTime m_timeAdded; + QElapsedTimer m_timeAdded; }; #endif // DOWNLOADWIDGET_H diff --git a/examples/webenginewidgets/simplebrowser/tabwidget.cpp b/examples/webenginewidgets/simplebrowser/tabwidget.cpp index 369bebfd9..3b6d84ebe 100644 --- a/examples/webenginewidgets/simplebrowser/tabwidget.cpp +++ b/examples/webenginewidgets/simplebrowser/tabwidget.cpp @@ -200,6 +200,10 @@ void TabWidget::setupView(WebView *webView) closeTab(index); }); connect(webView, &WebView::devToolsRequested, this, &TabWidget::devToolsRequested); + connect(webPage, &QWebEnginePage::findTextFinished, [this, webView](const QWebEngineFindTextResult &result) { + if (currentIndex() == indexOf(webView)) + emit findTextFinished(result); + }); } WebView *TabWidget::createTab() diff --git a/examples/webenginewidgets/simplebrowser/tabwidget.h b/examples/webenginewidgets/simplebrowser/tabwidget.h index bf83781df..fba61d44f 100644 --- a/examples/webenginewidgets/simplebrowser/tabwidget.h +++ b/examples/webenginewidgets/simplebrowser/tabwidget.h @@ -78,6 +78,7 @@ signals: void favIconChanged(const QIcon &icon); void webActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled); void devToolsRequested(QWebEnginePage *source); + void findTextFinished(const QWebEngineFindTextResult &result); public slots: // current tab/page slots diff --git a/examples/webenginewidgets/simplebrowser/webpage.cpp b/examples/webenginewidgets/simplebrowser/webpage.cpp index 99849c77d..2e49f651f 100644 --- a/examples/webenginewidgets/simplebrowser/webpage.cpp +++ b/examples/webenginewidgets/simplebrowser/webpage.cpp @@ -57,6 +57,7 @@ #include <QAuthenticator> #include <QMessageBox> #include <QStyle> +#include <QTimer> #include <QWebEngineCertificateError> WebPage::WebPage(QWebEngineProfile *profile, QObject *parent) @@ -74,22 +75,33 @@ WebPage::WebPage(QWebEngineProfile *profile, QObject *parent) bool WebPage::certificateError(const QWebEngineCertificateError &error) { QWidget *mainWindow = view()->window(); - if (error.isOverridable()) { - QDialog dialog(mainWindow); - dialog.setModal(true); - dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); - Ui::CertificateErrorDialog certificateDialog; - certificateDialog.setupUi(&dialog); - certificateDialog.m_iconLabel->setText(QString()); - QIcon icon(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxWarning, 0, mainWindow)); - certificateDialog.m_iconLabel->setPixmap(icon.pixmap(32, 32)); - certificateDialog.m_errorLabel->setText(error.errorDescription()); - dialog.setWindowTitle(tr("Certificate Error")); - return dialog.exec() == QDialog::Accepted; - } - QMessageBox::critical(mainWindow, tr("Certificate Error"), error.errorDescription()); - return false; + QWebEngineCertificateError deferredError = error; + deferredError.defer(); + + QTimer::singleShot(0, mainWindow, [mainWindow, error = std::move(deferredError)] () mutable { + if (!error.deferred()) { + QMessageBox::critical(mainWindow, tr("Certificate Error"), error.errorDescription()); + } else { + QDialog dialog(mainWindow); + dialog.setModal(true); + dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); + + Ui::CertificateErrorDialog certificateDialog; + certificateDialog.setupUi(&dialog); + certificateDialog.m_iconLabel->setText(QString()); + QIcon icon(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxWarning, 0, mainWindow)); + certificateDialog.m_iconLabel->setPixmap(icon.pixmap(32, 32)); + certificateDialog.m_errorLabel->setText(error.errorDescription()); + dialog.setWindowTitle(tr("Certificate Error")); + + if (dialog.exec() == QDialog::Accepted) + error.ignoreCertificateError(); + else + error.rejectCertificate(); + } + }); + return true; } void WebPage::handleAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth) diff --git a/examples/webenginewidgets/simplebrowser/webpopupwindow.cpp b/examples/webenginewidgets/simplebrowser/webpopupwindow.cpp index 19e3b0124..566723e1f 100644 --- a/examples/webenginewidgets/simplebrowser/webpopupwindow.cpp +++ b/examples/webenginewidgets/simplebrowser/webpopupwindow.cpp @@ -66,7 +66,7 @@ WebPopupWindow::WebPopupWindow(QWebEngineProfile *profile) setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); QVBoxLayout *layout = new QVBoxLayout; - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); setLayout(layout); layout->addWidget(m_urlLineEdit); layout->addWidget(m_view); |