diff options
Diffstat (limited to 'examples/webenginequick')
16 files changed, 1586 insertions, 28 deletions
diff --git a/examples/webenginequick/nanobrowser/ApplicationRoot.qml b/examples/webenginequick/nanobrowser/ApplicationRoot.qml new file mode 100644 index 000000000..55c414409 --- /dev/null +++ b/examples/webenginequick/nanobrowser/ApplicationRoot.qml @@ -0,0 +1,40 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtWebEngine + +QtObject { + id: root + + property QtObject defaultProfile: WebEngineProfile { + storageName: "Profile" + offTheRecord: false + } + + property QtObject otrProfile: WebEngineProfile { + offTheRecord: true + } + + property Component browserWindowComponent: BrowserWindow { + applicationRoot: root + } + property Component browserDialogComponent: BrowserDialog { + onClosing: destroy() + } + function createWindow(profile) { + var newWindow = browserWindowComponent.createObject(root); + newWindow.currentWebView.profile = profile; + profile.downloadRequested.connect(newWindow.onDownloadRequested); + return newWindow; + } + function createDialog(profile) { + var newDialog = browserDialogComponent.createObject(root); + newDialog.currentWebView.profile = profile; + return newDialog; + } + function load(url) { + var browserWindow = createWindow(defaultProfile); + browserWindow.currentWebView.url = url; + } +} diff --git a/examples/webenginequick/nanobrowser/BrowserDialog.qml b/examples/webenginequick/nanobrowser/BrowserDialog.qml new file mode 100644 index 000000000..7af347ec3 --- /dev/null +++ b/examples/webenginequick/nanobrowser/BrowserDialog.qml @@ -0,0 +1,27 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Window +import QtWebEngine + +Window { + id: window + property alias currentWebView: webView + flags: Qt.Dialog + width: 800 + height: 600 + visible: true + onClosing: destroy() + WebEngineView { + id: webView + anchors.fill: parent + + onGeometryChangeRequested: function(geometry) { + window.x = geometry.x + window.y = geometry.y + window.width = geometry.width + window.height = geometry.height + } + } +} diff --git a/examples/webenginequick/nanobrowser/BrowserWindow.qml b/examples/webenginequick/nanobrowser/BrowserWindow.qml new file mode 100644 index 000000000..a517c5a51 --- /dev/null +++ b/examples/webenginequick/nanobrowser/BrowserWindow.qml @@ -0,0 +1,817 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtCore +import QtQml +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Window +import QtWebEngine +import BrowserUtils + +ApplicationWindow { + id: browserWindow + property QtObject applicationRoot + property Item currentWebView: tabBar.currentIndex < tabBar.count ? tabLayout.children[tabBar.currentIndex] : null + property int previousVisibility: Window.Windowed + property int createdTabs: 0 + + width: 1300 + height: 900 + visible: true + title: currentWebView && currentWebView.title + + // Make sure the Qt.WindowFullscreenButtonHint is set on OS X. + Component.onCompleted: flags = flags | Qt.WindowFullscreenButtonHint + + onCurrentWebViewChanged: { + findBar.reset(); + } + + // When using style "mac", ToolButtons are not supposed to accept focus. + property bool platformIsMac: Qt.platform.os == "osx" + + Settings { + id : appSettings + property alias autoLoadImages: loadImages.checked + property alias javaScriptEnabled: javaScriptEnabled.checked + property alias errorPageEnabled: errorPageEnabled.checked + property alias pluginsEnabled: pluginsEnabled.checked + property alias fullScreenSupportEnabled: fullScreenSupportEnabled.checked + property alias autoLoadIconsForPage: autoLoadIconsForPage.checked + property alias touchIconsEnabled: touchIconsEnabled.checked + property alias webRTCPublicInterfacesOnly : webRTCPublicInterfacesOnly.checked + property alias devToolsEnabled: devToolsEnabled.checked + property alias pdfViewerEnabled: pdfViewerEnabled.checked + } + + Action { + shortcut: "Ctrl+D" + onTriggered: { + downloadView.visible = !downloadView.visible; + } + } + Action { + id: focus + shortcut: "Ctrl+L" + onTriggered: { + addressBar.forceActiveFocus(); + addressBar.selectAll(); + } + } + Action { + shortcut: StandardKey.Refresh + onTriggered: { + if (currentWebView) + currentWebView.reload(); + } + } + Action { + shortcut: StandardKey.AddTab + onTriggered: { + tabBar.createTab(tabBar.count != 0 ? currentWebView.profile : defaultProfile); + addressBar.forceActiveFocus(); + addressBar.selectAll(); + } + } + Action { + shortcut: StandardKey.Close + onTriggered: { + currentWebView.triggerWebAction(WebEngineView.RequestClose); + } + } + Action { + shortcut: StandardKey.Quit + onTriggered: browserWindow.close() + } + Action { + shortcut: "Escape" + onTriggered: { + if (currentWebView.state == "FullScreen") { + browserWindow.visibility = browserWindow.previousVisibility; + fullScreenNotification.hide(); + currentWebView.triggerWebAction(WebEngineView.ExitFullScreen); + } + + if (findBar.visible) + findBar.visible = false; + } + } + Action { + shortcut: "Ctrl+0" + onTriggered: currentWebView.zoomFactor = 1.0 + } + Action { + shortcut: StandardKey.ZoomOut + onTriggered: currentWebView.zoomFactor -= 0.1 + } + Action { + shortcut: StandardKey.ZoomIn + onTriggered: currentWebView.zoomFactor += 0.1 + } + + Action { + shortcut: StandardKey.Copy + onTriggered: currentWebView.triggerWebAction(WebEngineView.Copy) + } + Action { + shortcut: StandardKey.Cut + onTriggered: currentWebView.triggerWebAction(WebEngineView.Cut) + } + Action { + shortcut: StandardKey.Paste + onTriggered: currentWebView.triggerWebAction(WebEngineView.Paste) + } + Action { + shortcut: "Shift+"+StandardKey.Paste + onTriggered: currentWebView.triggerWebAction(WebEngineView.PasteAndMatchStyle) + } + Action { + shortcut: StandardKey.SelectAll + onTriggered: currentWebView.triggerWebAction(WebEngineView.SelectAll) + } + Action { + shortcut: StandardKey.Undo + onTriggered: currentWebView.triggerWebAction(WebEngineView.Undo) + } + Action { + shortcut: StandardKey.Redo + onTriggered: currentWebView.triggerWebAction(WebEngineView.Redo) + } + Action { + shortcut: StandardKey.Back + onTriggered: currentWebView.triggerWebAction(WebEngineView.Back) + } + Action { + 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() + } + + menuBar: ToolBar { + id: navigationBar + RowLayout { + anchors.fill: parent + ToolButton { + enabled: currentWebView && (currentWebView.canGoBack || currentWebView.canGoForward) + onClicked: historyMenu.open() + text: qsTr("▼") + Menu { + id: historyMenu + Instantiator { + model: currentWebView && currentWebView.history.items + MenuItem { + text: model.title + onTriggered: currentWebView.goBackOrForward(model.offset) + checkable: !enabled + checked: !enabled + enabled: model.offset + } + + onObjectAdded: function(index, object) { + historyMenu.insertItem(index, object) + } + onObjectRemoved: function(index, object) { + historyMenu.removeItem(object) + } + } + } + } + + ToolButton { + id: backButton + icon.source: "qrc:/icons/go-previous.png" + onClicked: currentWebView.goBack() + enabled: currentWebView && currentWebView.canGoBack + activeFocusOnTab: !browserWindow.platformIsMac + } + ToolButton { + id: forwardButton + icon.source: "qrc:/icons/go-next.png" + onClicked: currentWebView.goForward() + enabled: currentWebView && currentWebView.canGoForward + activeFocusOnTab: !browserWindow.platformIsMac + } + ToolButton { + id: reloadButton + icon.source: currentWebView && currentWebView.loading ? "qrc:/icons/process-stop.png" : "qrc:/icons/view-refresh.png" + onClicked: currentWebView && currentWebView.loading ? currentWebView.stop() : currentWebView.reload() + activeFocusOnTab: !browserWindow.platformIsMac + } + TextField { + id: addressBar + Image { + anchors.verticalCenter: addressBar.verticalCenter; + x: 5 + z: 2 + id: faviconImage + width: 16; height: 16 + sourceSize: Qt.size(width, height) + source: currentWebView && currentWebView.icon ? currentWebView.icon : '' + } + MouseArea { + id: textFieldMouseArea + acceptedButtons: Qt.RightButton + anchors.fill: parent + onClicked: { + var textSelectionStartPos = addressBar.selectionStart; + var textSelectionEndPos = addressBar.selectionEnd; + textFieldContextMenu.open(); + addressBar.select(textSelectionStartPos, textSelectionEndPos); + } + Menu { + id: textFieldContextMenu + x: textFieldMouseArea.mouseX + y: textFieldMouseArea.mouseY + MenuItem { + text: qsTr("Cut") + onTriggered: addressBar.cut() + enabled: addressBar.selectedText.length > 0 + } + MenuItem { + text: qsTr("Copy") + onTriggered: addressBar.copy() + enabled: addressBar.selectedText.length > 0 + } + MenuItem { + text: qsTr("Paste") + onTriggered: addressBar.paste() + enabled: addressBar.canPaste + } + MenuItem { + text: qsTr("Delete") + onTriggered: addressBar.text = qsTr("") + enabled: addressBar.selectedText.length > 0 + } + MenuSeparator {} + MenuItem { + text: qsTr("Select All") + onTriggered: addressBar.selectAll() + enabled: addressBar.text.length > 0 + } + } + } + leftPadding: 26 + focus: true + Layout.fillWidth: true + Binding on text { + when: currentWebView + value: currentWebView.url + } + onAccepted: currentWebView.url = Utils.fromUserInput(text) + selectByMouse: true + } + ToolButton { + id: settingsMenuButton + text: qsTr("⋮") + onClicked: settingsMenu.open() + Menu { + id: settingsMenu + y: settingsMenuButton.height + MenuItem { + id: loadImages + text: "Autoload images" + checkable: true + checked: WebEngine.settings.autoLoadImages + } + MenuItem { + id: javaScriptEnabled + text: "JavaScript On" + checkable: true + checked: WebEngine.settings.javascriptEnabled + } + MenuItem { + id: errorPageEnabled + text: "ErrorPage On" + checkable: true + checked: WebEngine.settings.errorPageEnabled + } + MenuItem { + id: pluginsEnabled + text: "Plugins On" + checkable: true + checked: true + } + MenuItem { + id: fullScreenSupportEnabled + text: "FullScreen On" + checkable: true + checked: WebEngine.settings.fullScreenSupportEnabled + } + MenuItem { + id: offTheRecordEnabled + text: "Off The Record" + checkable: true + checked: currentWebView && currentWebView.profile === otrProfile + onToggled: function(checked) { + if (currentWebView) { + currentWebView.profile = checked ? otrProfile : defaultProfile; + } + } + } + MenuItem { + id: httpDiskCacheEnabled + text: "HTTP Disk Cache" + checkable: currentWebView && !currentWebView.profile.offTheRecord + checked: currentWebView && (currentWebView.profile.httpCacheType === WebEngineProfile.DiskHttpCache) + onToggled: function(checked) { + if (currentWebView) { + currentWebView.profile.httpCacheType = checked ? WebEngineProfile.DiskHttpCache : WebEngineProfile.MemoryHttpCache; + } + } + } + MenuItem { + id: autoLoadIconsForPage + text: "Icons On" + checkable: true + checked: WebEngine.settings.autoLoadIconsForPage + } + MenuItem { + id: touchIconsEnabled + text: "Touch Icons On" + checkable: true + checked: WebEngine.settings.touchIconsEnabled + enabled: autoLoadIconsForPage.checked + } + MenuItem { + id: webRTCPublicInterfacesOnly + text: "WebRTC Public Interfaces Only" + checkable: true + checked: WebEngine.settings.webRTCPublicInterfacesOnly + } + MenuItem { + id: devToolsEnabled + text: "Open DevTools" + checkable: true + checked: false + } + MenuItem { + id: pdfViewerEnabled + text: "PDF viewer enabled" + checkable: true + checked: WebEngine.settings.pdfViewerEnabled + } + } + } + } + ProgressBar { + id: progressBar + height: 3 + anchors { + left: parent.left + top: parent.bottom + right: parent.right + leftMargin: parent.leftMargin + rightMargin: parent.rightMargin + } + background: Item {} + z: -2 + from: 0 + to: 100 + value: (currentWebView && currentWebView.loadProgress < 100) ? currentWebView.loadProgress : 0 + } + } + + StackLayout { + id: tabLayout + currentIndex: tabBar.currentIndex + + anchors.top: tabBar.bottom + anchors.bottom: devToolsView.top + anchors.left: parent.left + anchors.right: parent.right + } + + Component { + id: tabButtonComponent + + TabButton { + property color frameColor: "#999" + property color fillColor: "#eee" + property color nonSelectedColor: "#ddd" + property string tabTitle: "New Tab" + + id: tabButton + contentItem: Rectangle { + id: tabRectangle + color: tabButton.down ? fillColor : nonSelectedColor + border.width: 1 + border.color: frameColor + implicitWidth: Math.max(text.width + 30, 80) + implicitHeight: Math.max(text.height + 10, 20) + Rectangle { height: 1 ; width: parent.width ; color: frameColor} + Rectangle { height: parent.height ; width: 1; color: frameColor} + Rectangle { x: parent.width - 2; height: parent.height ; width: 1; color: frameColor} + Text { + id: text + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 6 + text: tabButton.tabTitle + elide: Text.ElideRight + color: tabButton.down ? "black" : frameColor + width: parent.width - button.background.width + } + Button { + id: button + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 4 + height: 12 + background: Rectangle { + implicitWidth: 12 + implicitHeight: 12 + color: button.hovered ? "#ccc" : tabRectangle.color + Text {text: "x"; anchors.centerIn: parent; color: "gray"} + } + onClicked: tabButton.closeTab() + } + } + + onClicked: addressBar.text = tabLayout.itemAt(TabBar.index).url; + function closeTab() { + tabBar.removeView(TabBar.index); + } + } + } + + TabBar { + id: tabBar + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + Component.onCompleted: createTab(defaultProfile) + + function createTab(profile, focusOnNewTab = true, url = undefined) { + var webview = tabComponent.createObject(tabLayout, {profile: profile}); + var newTabButton = tabButtonComponent.createObject(tabBar, {tabTitle: Qt.binding(function () { return webview.title; })}); + tabBar.addItem(newTabButton); + if (focusOnNewTab) { + tabBar.setCurrentIndex(tabBar.count - 1); + } + if (url !== undefined) { + webview.url = url; + } + return webview; + } + + function removeView(index) { + tabBar.removeItem(index); + if (tabBar.count > 1) { + tabBar.removeItem(tabBar.itemAt(index)); + tabLayout.children[index].destroy(); + } else { + browserWindow.close(); + } + } + + Component { + id: tabComponent + WebEngineView { + id: webEngineView + focus: true + + onLinkHovered: function(hoveredUrl) { + if (hoveredUrl == "") + hideStatusText.start(); + else { + statusText.text = hoveredUrl; + statusBubble.visible = true; + hideStatusText.stop(); + } + } + + states: [ + State { + name: "FullScreen" + PropertyChanges { + target: tabBar + visible: false + height: 0 + } + PropertyChanges { + target: navigationBar + visible: false + } + } + ] + settings.localContentCanAccessRemoteUrls: true + settings.localContentCanAccessFileUrls: false + settings.autoLoadImages: appSettings.autoLoadImages + settings.javascriptEnabled: appSettings.javaScriptEnabled + settings.errorPageEnabled: appSettings.errorPageEnabled + settings.pluginsEnabled: appSettings.pluginsEnabled + settings.fullScreenSupportEnabled: appSettings.fullScreenSupportEnabled + settings.autoLoadIconsForPage: appSettings.autoLoadIconsForPage + settings.touchIconsEnabled: appSettings.touchIconsEnabled + settings.webRTCPublicInterfacesOnly: appSettings.webRTCPublicInterfacesOnly + settings.pdfViewerEnabled: appSettings.pdfViewerEnabled + + onCertificateError: function(error) { + error.defer(); + sslDialog.enqueue(error); + } + + onNewWindowRequested: function(request) { + if (!request.userInitiated) + console.warn("Blocked a popup window."); + else if (request.destination === WebEngineNewWindowRequest.InNewTab) { + var tab = tabBar.createTab(currentWebView.profile, true, request.requestedUrl); + tab.acceptAsNewWindow(request); + } else if (request.destination === WebEngineNewWindowRequest.InNewBackgroundTab) { + var backgroundTab = tabBar.createTab(currentWebView.profile, false); + backgroundTab.acceptAsNewWindow(request); + } else if (request.destination === WebEngineNewWindowRequest.InNewDialog) { + var dialog = applicationRoot.createDialog(currentWebView.profile); + dialog.currentWebView.acceptAsNewWindow(request); + } else { + var window = applicationRoot.createWindow(currentWebView.profile); + window.currentWebView.acceptAsNewWindow(request); + } + } + + onFullScreenRequested: function(request) { + if (request.toggleOn) { + webEngineView.state = "FullScreen"; + browserWindow.previousVisibility = browserWindow.visibility; + browserWindow.showFullScreen(); + fullScreenNotification.show(); + } else { + webEngineView.state = ""; + browserWindow.visibility = browserWindow.previousVisibility; + fullScreenNotification.hide(); + } + request.accept(); + } + + onRegisterProtocolHandlerRequested: function(request) { + console.log("accepting registerProtocolHandler request for " + + request.scheme + " from " + request.origin); + request.accept(); + } + + onRenderProcessTerminated: function(terminationStatus, exitCode) { + var status = ""; + switch (terminationStatus) { + case WebEngineView.NormalTerminationStatus: + status = "(normal exit)"; + break; + case WebEngineView.AbnormalTerminationStatus: + status = "(abnormal exit)"; + break; + case WebEngineView.CrashedTerminationStatus: + status = "(crashed)"; + break; + case WebEngineView.KilledTerminationStatus: + status = "(killed)"; + break; + } + + print("Render process exited with code " + exitCode + " " + status); + reloadTimer.running = true; + } + + onSelectClientCertificate: function(selection) { + 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(); + } + + onFeaturePermissionRequested: function(securityOrigin, feature) { + featurePermissionDialog.securityOrigin = securityOrigin; + featurePermissionDialog.feature = feature; + featurePermissionDialog.visible = true; + } + + Timer { + id: reloadTimer + interval: 0 + running: false + repeat: false + onTriggered: currentWebView.reload() + } + } + } + } + WebEngineView { + id: devToolsView + visible: devToolsEnabled.checked + height: visible ? 400 : 0 + inspectedView: visible && tabBar.currentIndex < tabBar.count ? tabLayout.children[tabBar.currentIndex] : null + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + onNewWindowRequested: function(request) { + var tab = tabBar.createTab(currentWebView.profile); + request.openIn(tab); + } + + Timer { + id: hideTimer + interval: 0 + running: false + repeat: false + onTriggered: devToolsEnabled.checked = false + } + onWindowCloseRequested: function(request) { + // Delay hiding for keep the inspectedView set to receive the ACK message of close. + hideTimer.running = true; + } + } + Dialog { + id: sslDialog + anchors.centerIn: parent + contentWidth: Math.max(mainTextForSSLDialog.width, detailedTextForSSLDialog.width) + contentHeight: mainTextForSSLDialog.height + detailedTextForSSLDialog.height + property var certErrors: [] + // fixme: icon! + // icon: StandardIcon.Warning + standardButtons: Dialog.No | Dialog.Yes + title: "Server's certificate not trusted" + contentItem: Item { + Label { + id: mainTextForSSLDialog + text: "Do you wish to continue?" + } + Text { + id: detailedTextForSSLDialog + anchors.top: mainTextForSSLDialog.bottom + text: "If you wish so, you may continue with an unverified certificate.\n" + + "Accepting an unverified certificate means\n" + + "you may not be connected with the host you tried to connect to.\n" + + "Do you wish to override the security check and continue?" + } + } + + onAccepted: { + certErrors.shift().acceptCertificate(); + presentError(); + } + onRejected: reject() + + function reject(){ + certErrors.shift().rejectCertificate(); + presentError(); + } + function enqueue(error){ + certErrors.push(error); + presentError(); + } + function presentError(){ + visible = certErrors.length > 0 + } + } + Dialog { + id: featurePermissionDialog + anchors.centerIn: parent + width: Math.min(browserWindow.width, browserWindow.height) / 3 * 2 + contentWidth: mainTextForPermissionDialog.width + contentHeight: mainTextForPermissionDialog.height + standardButtons: Dialog.No | Dialog.Yes + title: "Permission Request" + + property var feature; + property url securityOrigin; + + contentItem: Item { + Label { + id: mainTextForPermissionDialog + text: featurePermissionDialog.questionForFeature() + } + } + + onAccepted: currentWebView && currentWebView.grantFeaturePermission(securityOrigin, feature, true) + onRejected: currentWebView && currentWebView.grantFeaturePermission(securityOrigin, feature, false) + onVisibleChanged: { + if (visible) + width = contentWidth + 20; + } + + function questionForFeature() { + var question = "Allow " + securityOrigin + " to " + + switch (feature) { + case WebEngineView.Geolocation: + question += "access your location information?"; + break; + case WebEngineView.MediaAudioCapture: + question += "access your microphone?"; + break; + case WebEngineView.MediaVideoCapture: + question += "access your webcam?"; + break; + case WebEngineView.MediaVideoCapture: + question += "access your microphone and webcam?"; + break; + case WebEngineView.MouseLock: + question += "lock your mouse cursor?"; + break; + case WebEngineView.DesktopVideoCapture: + question += "capture video of your desktop?"; + break; + case WebEngineView.DesktopAudioVideoCapture: + question += "capture audio and video of your desktop?"; + break; + case WebEngineView.Notifications: + question += "show notification on your desktop?"; + break; + default: + question += "access unknown or unsupported feature [" + feature + "] ?"; + break; + } + + return question; + } + } + + FullScreenNotification { + id: fullScreenNotification + } + + DownloadView { + id: downloadView + visible: false + anchors.fill: parent + } + + function onDownloadRequested(download) { + downloadView.visible = true; + downloadView.append(download); + 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" + property int padding: 8 + visible: false + + anchors.left: parent.left + anchors.bottom: parent.bottom + width: statusText.paintedWidth + padding + height: statusText.paintedHeight + padding + + Text { + id: statusText + anchors.centerIn: statusBubble + elide: Qt.ElideMiddle + + Timer { + id: hideStatusText + interval: 750 + onTriggered: { + statusText.text = ""; + statusBubble.visible = false; + } + } + } + } +} diff --git a/examples/webenginequick/nanobrowser/DownloadView.qml b/examples/webenginequick/nanobrowser/DownloadView.qml new file mode 100644 index 000000000..e16647cdb --- /dev/null +++ b/examples/webenginequick/nanobrowser/DownloadView.qml @@ -0,0 +1,127 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls +import QtWebEngine +import QtQuick.Layouts + +Rectangle { + id: downloadView + color: "lightgray" + + ListModel { + id: downloadModel + property var downloads: [] + } + + function append(download) { + downloadModel.append(download); + downloadModel.downloads.push(download); + } + + Component { + id: downloadItemDelegate + + Rectangle { + width: listView.width + height: childrenRect.height + anchors.margins: 10 + radius: 3 + color: "transparent" + border.color: "black" + Rectangle { + id: progressBar + + property real progress: downloadModel.downloads[index] + ? downloadModel.downloads[index].receivedBytes / downloadModel.downloads[index].totalBytes : 0 + + radius: 3 + color: width == listView.width ? "green" : "#2b74c7" + width: listView.width * progress + height: cancelButton.height + + Behavior on width { + SmoothedAnimation { duration: 100 } + } + } + Rectangle { + anchors { + left: parent.left + right: parent.right + leftMargin: 20 + } + Label { + id: label + text: downloadModel.downloads[index] ? downloadModel.downloads[index].downloadDirectory + "/" + downloadModel.downloads[index].downloadFileName : qsTr("") + anchors { + verticalCenter: cancelButton.verticalCenter + left: parent.left + right: cancelButton.left + } + } + Button { + id: cancelButton + anchors.right: parent.right + icon.source: "qrc:/icons/process-stop.png" + onClicked: { + var download = downloadModel.downloads[index]; + + download.cancel(); + + downloadModel.downloads = downloadModel.downloads.filter(function (el) { + return el.id !== download.id; + }); + downloadModel.remove(index); + } + } + } + } + + } + ListView { + id: listView + anchors { + topMargin: 10 + top: parent.top + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + } + width: parent.width - 20 + spacing: 5 + + model: downloadModel + delegate: downloadItemDelegate + + Text { + visible: !listView.count + horizontalAlignment: Text.AlignHCenter + height: 30 + anchors { + top: parent.top + left: parent.left + right: parent.right + } + font.pixelSize: 20 + text: "No active downloads." + } + + Rectangle { + color: "gray" + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + height: 30 + Button { + id: okButton + text: "OK" + anchors.centerIn: parent + onClicked: { + downloadView.visible = false; + } + } + } + } +} diff --git a/examples/webenginequick/nanobrowser/FindBar.qml b/examples/webenginequick/nanobrowser/FindBar.qml new file mode 100644 index 000000000..4d130a22b --- /dev/null +++ b/examples/webenginequick/nanobrowser/FindBar.qml @@ -0,0 +1,95 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +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 + 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/webenginequick/nanobrowser/FullScreenNotification.qml b/examples/webenginequick/nanobrowser/FullScreenNotification.qml new file mode 100644 index 000000000..779406432 --- /dev/null +++ b/examples/webenginequick/nanobrowser/FullScreenNotification.qml @@ -0,0 +1,62 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Rectangle { + id: fullScreenNotification + width: 500 + height: 40 + color: "white" + radius: 7 + + visible: false + opacity: 0 + + function show() { + visible = true; + opacity = 1; + reset.start(); + } + + function hide() { + reset.stop(); + opacity = 0; + } + + Behavior on opacity { + NumberAnimation { + duration: 750 + onStopped: { + if (opacity == 0) + visible = false; + } + } + } + + Timer { + id: reset + interval: 5000 + onTriggered: hide() + } + + anchors.horizontalCenter: parent.horizontalCenter + y: 125 + + Text { + id: message + width: parent.width + + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + wrapMode: Text.WordWrap + elide: Text.ElideNone + clip: true + + text: qsTr("You are now in fullscreen mode. Press ESC to quit!") + } +} diff --git a/examples/webenginequick/nanobrowser/browser.qml b/examples/webenginequick/nanobrowser/browser.qml deleted file mode 100644 index d63bcd546..000000000 --- a/examples/webenginequick/nanobrowser/browser.qml +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause - -import QtQuick -import QtQuick.Window -import QtWebEngine - -Window { - width: 1024 - height: 768 - visible: true - WebEngineView { - anchors.fill: parent - url: "https://www.qt.io" - } -} diff --git a/examples/webenginequick/nanobrowser/icons/3rdparty/COPYING b/examples/webenginequick/nanobrowser/icons/3rdparty/COPYING new file mode 100644 index 000000000..220881da6 --- /dev/null +++ b/examples/webenginequick/nanobrowser/icons/3rdparty/COPYING @@ -0,0 +1 @@ +The icons in this repository are herefore released into the Public Domain. diff --git a/examples/webenginequick/nanobrowser/icons/3rdparty/go-next.png b/examples/webenginequick/nanobrowser/icons/3rdparty/go-next.png Binary files differnew file mode 100644 index 000000000..6f3f65d33 --- /dev/null +++ b/examples/webenginequick/nanobrowser/icons/3rdparty/go-next.png diff --git a/examples/webenginequick/nanobrowser/icons/3rdparty/go-previous.png b/examples/webenginequick/nanobrowser/icons/3rdparty/go-previous.png Binary files differnew file mode 100644 index 000000000..93be3d1ee --- /dev/null +++ b/examples/webenginequick/nanobrowser/icons/3rdparty/go-previous.png diff --git a/examples/webenginequick/nanobrowser/icons/3rdparty/process-stop.png b/examples/webenginequick/nanobrowser/icons/3rdparty/process-stop.png Binary files differnew file mode 100644 index 000000000..b68290bf1 --- /dev/null +++ b/examples/webenginequick/nanobrowser/icons/3rdparty/process-stop.png diff --git a/examples/webenginequick/nanobrowser/icons/3rdparty/view-refresh.png b/examples/webenginequick/nanobrowser/icons/3rdparty/view-refresh.png Binary files differnew file mode 100644 index 000000000..cab4d02c7 --- /dev/null +++ b/examples/webenginequick/nanobrowser/icons/3rdparty/view-refresh.png diff --git a/examples/webenginequick/nanobrowser/nanobrowser.pyproject b/examples/webenginequick/nanobrowser/nanobrowser.pyproject index dd9039229..c86c57f67 100644 --- a/examples/webenginequick/nanobrowser/nanobrowser.pyproject +++ b/examples/webenginequick/nanobrowser/nanobrowser.pyproject @@ -1,3 +1,6 @@ { - "files": ["quicknanobrowser.py", "browser.qml"] + "files": ["quicknanobrowser.py", "ApplicationRoot.qml", + "BrowserDialog.qml", "BrowserWindow.qml", "DownloadView.qml", + "FindBar.qml", "FullScreenNotification.qml", + "resources.qrc"] } diff --git a/examples/webenginequick/nanobrowser/quicknanobrowser.py b/examples/webenginequick/nanobrowser/quicknanobrowser.py index 1191d61fd..aee79c2aa 100644 --- a/examples/webenginequick/nanobrowser/quicknanobrowser.py +++ b/examples/webenginequick/nanobrowser/quicknanobrowser.py @@ -4,21 +4,67 @@ """PySide6 WebEngine QtQuick 2 Example""" import os -from PySide6.QtCore import QUrl -from PySide6.QtQml import QQmlApplicationEngine -from PySide6.QtWidgets import QApplication +import sys +from argparse import ArgumentParser, RawTextHelpFormatter +from pathlib import Path + +from PySide6.QtCore import (QCoreApplication, QFileInfo, QMetaObject, QObject, + QUrl, Slot, Q_ARG) +from PySide6.QtQml import QQmlApplicationEngine, QmlElement, QmlSingleton +from PySide6.QtGui import QGuiApplication from PySide6.QtWebEngineQuick import QtWebEngineQuick +import rc_resources # noqa: F401 + + +# To be used on the @QmlElement decorator +# (QML_IMPORT_MINOR_VERSION is optional) +QML_IMPORT_NAME = "BrowserUtils" +QML_IMPORT_MAJOR_VERSION = 1 + + +def url_from_user_input(user_input): + file_info = QFileInfo(user_input) + if file_info.exists(): + return QUrl.fromLocalFile(file_info.absoluteFilePath()) + return QUrl.fromUserInput(user_input) + + +@QmlElement +@QmlSingleton +class Utils(QObject): + + @Slot(str, result=QUrl) + def fromUserInput(self, user_input): + return url_from_user_input(user_input) + + +if __name__ == '__main__': + QCoreApplication.setApplicationName("Quick Nano Browser") + QCoreApplication.setOrganizationName("QtProject") -def main(): - app = QApplication([]) QtWebEngineQuick.initialize() + + argument_parser = ArgumentParser(description="Quick Nano Browser", + formatter_class=RawTextHelpFormatter) + argument_parser.add_argument("--single-process", "-s", action="store_true", + help="Run in single process mode (trouble shooting)") + argument_parser.add_argument("url", help="The URL to open", + nargs='?', type=str) + options = argument_parser.parse_args() + + url = url_from_user_input(options.url) if options.url else QUrl("https://www.qt.io") + + app_args = sys.argv + if options.single_process: + app_args.extend(["--webEngineArgs", "--single-process"]) + app = QGuiApplication(app_args) engine = QQmlApplicationEngine() - qml_file_path = os.path.join(os.path.dirname(__file__), 'browser.qml') - qml_url = QUrl.fromLocalFile(os.path.abspath(qml_file_path)) - engine.load(qml_url) - app.exec() + qml_file = os.fspath(Path(__file__).resolve().parent / 'ApplicationRoot.qml') + engine.load(QUrl.fromLocalFile(qml_file)) + if not engine.rootObjects(): + sys.exit(-1) + QMetaObject.invokeMethod(engine.rootObjects()[0], "load", Q_ARG("QVariant", url)) -if __name__ == '__main__': - main() + app.exec() diff --git a/examples/webenginequick/nanobrowser/rc_resources.py b/examples/webenginequick/nanobrowser/rc_resources.py new file mode 100644 index 000000000..990f10274 --- /dev/null +++ b/examples/webenginequick/nanobrowser/rc_resources.py @@ -0,0 +1,348 @@ +# Resource object code (Python 3) +# Created by: object code +# Created by: The Resource Compiler for Qt version 6.4.0 +# WARNING! All changes made in this file will be lost! + +from PySide6 import QtCore + +qt_resource_data = b"\ +\x00\x00\x03\xa2\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x16\x00\x00\x00\x16\x08\x06\x00\x00\x00\xc4\xb4l;\ +\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\ +\x00\x00\x00\x19tEXtSoftware\ +\x00www.inkscape.or\ +g\x9b\xee<\x1a\x00\x00\x034IDAT8\x8d\xb5\ +\x95[h\x1dU\x14\x86\xbf5\xb3\xe7\xe4b\xedE)\ +)(\xd6'o\xed\x83\x85\x0a5\x89\xd0\x82V\x04A\ +E\xad\xfa\x22R\xa5\x14|iII\x0b\x82\xa2`Q\ +\xa4>\xf4EPKi\xccC\x88Z\x0a)bK4\ +\x86&\xa6*\x9a\x1654\xc6K\xd4\xa4\xb1\xc6\x04\xd3\ +\x93\xe4\xcce\xcf^>\x9c9u\x926\xd1\x87t\xc1\ +\xcf\xcc\x865\xdf\xda\xff\xda\x8b\xd9\xa2\xaa\x5c\x8d\xf0\xae\ +\x0a\xf5\xff\x82\x1b\x9b\x83\xae\xfa&\xf3\xe0\x92\x83\xd5\xb1\ +ym\xdd-\xef76\x17v/)\x18`\xc7\xc3/\ +T\xaf\xbby\xe3\xab\xf7\xec5oo\xdb&\xfe\x92\x81\ +}?\xe0\x91\xcd\xdbk6\xad\xbb\xff\xe9\xf3kM\xe7\ +\x96\xe7e\xd9b\xf9&\xbfhh\x0e\xfe\xc0QwY\ +u#\xa1s\xb6\xfa\xcc\xf9N\x1a7l\xadZ\xb1l\ +U\xc3\xc7}m\xfd\x9b\xf6\xca\x96\xd3\xaf\xeb\xc8\x95\xc0\ +\x92\x1f\xb7\x86=\x81\xee\xdf\xd92'A\xd5\xa1(N\ +\x1d]C\xad\x08\x1ew\xaci`l|\xd4\x1d\xebj\ +\xf9\xdbZ{\xef\xe7\x07\xe2\xfe\xff\x04\xbf\xb2\xe3]N\ +\xfd\xd4\x9eA\x15\xc5\x91\xba\x14\xa7\x0e\xa7\x96\xd4YR\ +\xb5\xdcVw7\x1a{\x1c\xfd\xe4\xf0L),>u\ +\xea\x0d\xdb1\xc7\xe5\xfcJ\xa9\xb3D\xb6DdK\xc4\ +iHdC\x924\x22ICb\x1b\x12\xa7!\xb1-\ +\xf1\xcd\xef'\xb8\x98\x8e\xf1\xe4\x03;\xafYq\xed\xea\ +\xb6\x86=A\xd3\xa2`\xeb\x12JI\x91\xc9\xd91.\ +\x14\x87\x19\x9f\xfe\x8d\xa9\xd2\x9f\x84\xb6HBH*\x11\ +Nb\x9c\xc4\x0c\x5c\xe8ah\xe2\x0b\x1e\xbb\xef\x99\xda\ +\x1b\xebnz\xb9\xb1\xd9\x1c\xacp\xcc|p1\x9cd\ +x\xe2[\x94r\x8b\xc4\x03?\xf0\xf0\x9d\xe0\x07>\x05\ +S\xc03>\xe2\x04')j\xa2\xf2S\x10DnX\ +\x10\x9c\xb8h\x0e4\xa8\xf20\x85\xb2<#x\xbe\x05\ +,\xce)\xb7\xae\xacgM\xe1v><qdvj\ +z\xe2Pom\xbakAp>\xfc\xc0\xc3\x0f<L\ +\x95w\xa9\x80g\x04O<\xd6\xaf\xda\x8a?\xbb\x9c\xb6\ +\x8e\xc3\xb3\xd3S3\xfbN\x1fL\xdf\x01\xe0\xa5y`\ +\x11\x09\xea\x9b\xca\xcb\xc77\xecC\x00\xcfH\x19n\x04\ +\xdf\x08'\xc7\xde\xa4\xb6\xa6\x86;\xaf\x7f\x88\xf1\x91)\ +=v\xbcef\xf2\x97\xe8\xd9\xef\xda]7P\x0b\x18\ +\x11\x89T51\x19\xd4\x00\x05\x85\xf1\xb7\xda\xf6\xaf\xbe\ +l\xe7F\xe2\xdd\xcf\xbdX\xa8\xf6\x97\xb3\xf1\xbaG9\ +\xf7\xfd\xb9\xb4\xf3\xb3\x93\x13\xc3\xbd\xba}\xb4\xcf\x0d\x01\ +5\xfc;\x08*\x22ie\xc7>\xe0\xf7\x1d\xb0\xeb\x81\ +\xaaL\xd5\x95\xf7\xfa&\xd3\xebR\xe5\xae\x95O\xd0\xd3\ +\xdd\x13\x7f}\xf6\xcb\x9f\x07>\xb0\xbb.\x8e\xf0\x17P\ +\x00\x1c\x90f\xb2\x80Y\xb4\xc7\xf9\x08K!\x1f}\xda\ +\x11\x0f\x0e\x0e\xf6\x9c9\xe2^K#f\x16Iw\x15\ +p\xbe\x9a\x9f)\x06\x04\xca#\xd2~\xbc5\xfe\xf5\x87\ +\xd1Cg\xdfs\xadYn\x92)\xce\xbe\xcb\xab\xdc\x0a\ +U\xb5\x22B\x06\x22g\xcd\x02&M\xf4\xab\x1f\xfbG\ +Z\x06\x8e\xba\xee\xac\x90\xcbA*\xf0\x04(\x01\xa1\xaa\ +\xea\x9c\x7f\x85\x94\xe9A\xd6\xdb\x80\xf2\xd4T\x1cH\xce\ +\x81\xces\x19\x01\x91\xaa\xdaK\xac\x85.S\x11\xf1)\ +\x9f\xb4\x9fS\xc5M\xfe\xb0R\xbd\x02\xe4\x1f-'\x87\ +\xbf\xf3\xe1\xd64\x00\x00\x00\x00IEND\xaeB`\ +\x82\ +\x00\x00\x05T\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x16\x00\x00\x00\x16\x08\x06\x00\x00\x00\xc4\xb4l;\ +\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\ +\x00\x00\x00\x19tEXtSoftware\ +\x00www.inkscape.or\ +g\x9b\xee<\x1a\x00\x00\x04\xe6IDAT8\x8d\xb5\ +\x95iL\x14g\x18\xc7\xff\xef;3;\xbb\xb3K9\ +\x96[\xe4\x10k\x91\xa0\x16E\xa8h\x1b\x8f\x9a&\xad\ +1i\xda\x10\x82\x9a\x98\xa6\x02\xd6O\xb6M\x13\x93&\ +\xfd\xd2415i\xd2Z\x14$5\xb6\x80\xa44i\ +\xb5\xb1Gb<J\xad\x0a\x04A[\x05B=\x10\x16\ +\x01q\x81\xbd\xe7x\x9f~`!\x1c~\xf0K'\xf9\ +g\x92\x99g~\xf3\x9bg\x9e\x99\x97\x11\x11\xfe\x8f\x8d\ +?KQYY\x8b\xf4,u\x1b\xf67g\x97\xbe\xdb\ +\x92\x00\x00\xeci\xc6%\x95\xdf\xaeP\x14\xf5#0l\ +5M+M\x10\x14\x893?\xe7l\xd4\x12tA\x90\ +h\x11\x14\xf8\xa3\xa3\xb6\xd2\x98\xb9\xe6\xa5}\xa7S\x04\ +\xa7\xbfL\x0b[;\xeb+\x1e\xcc\x033\x06V\xfa^\ +s=\xc0\xca_~q\xa9meN\xb2\xecr\xaa\xe0\ +\x9cA\xd7\x0d\x04B\x06\x1e<\x9a\xb4n\xf4z\x02#\ +\xe3\x01\x891\x1c\xf6s\xe5\x0b\x97\xa4s\xe8\xbc\xdd\x12\ +b\x051\x91\xd5Q\xbbg`\x1ex\xe3\x81\xe6\x93\xd9\ +iqe{^_\xadE\x0c\x0b\x13\xfe0b4\x15\ +\x9a\xaa@\xe2\x00\xe3\x0c\xa0i\x03\x7f0\x82K\x9d\xf7\ +\xc3\xd7\xff\x1e\xd2I\xc0\xb3\xa90k\xd9\xad\xfe\x11c\ +r\x22\x98w\xed\xc4\xaeAy\x06ZT\xf5]I\x82\ +K{{{\xf12\xed\xeb\x96v1\xe6\x0dr\xce\x01\ +a\x11\x11\x83\x99\xeav\x85J\x0a2\x9c\x85/\xa4J\ +D@\xc8 l]\x9fk/\xca\xcf\xb0\xf7?|\x12\ +\xf3jQ\x16\xeb\xea\x1d6u!\x89y/\xcf.+\ +\xd5\xcf\xb9Tg\xfd\x99N\x9a\x0a\x18\xccaW\xc2\x96\ +\x10\xdf_\xab\xad\xe0!\x8e\x14\xcf\x88\x7f\xfb\xb9\xd6\xbe\ +\x1f>\xfd\xa65\xd8\xd63LNU\xc2\xd0x\x08O\ +\x02&\xd6\xe7\xa73E\xe6\xb0,\x01\xd9\x1e\x9e\x0f\x06\ +\xc3k\x03\xc3\x93\xcc\x1d\x17\xe3]\x9e\x9dzC\xd7M\ +/ >\x07\x80\x9b5\x15\xde\xf6\xfa\x8a\xb6+5\xe5\ +\xe5\x86nm\xfc\xfdJ_\xfb\x89\x1f;\x82\xf1\x0e\x8e\ +\xcc$\x0d\x97n\x8dB\x918,!Xd\xa11\x09\ +\xa8\x9aC\x1d\xca\xccH\xae\x8d\xd5\xec\x0d`\xbc\xcf\x22\ +\x9bw\xe1\xc4\xb4\xd5Ut\xe9\x86yh*\xa0K1\ +\x0e\x05C\xe3!\x08\x00v\x85\xc3\xb2\x88)B\x16\x00\ +0\xdbcK\x88`\xa2\xcb\xf1e\x8c\xc3\xfe\xb3\xae\xab\ +\x8f)\xc2N\xc5,M\x9eZ\x08.\xaaj\x5c\xa5\xc8\ +\xd2\xd9\xea\xb7\x8a\xd4\xa4x\x0di\x89Nl.H\x86\ +\xaaH0\x051\x9b\xb5\x00\xecHK\xcf9\x8b\xcb\xe2\ +\xcc\xde7\x04\x00\xa0r\xc5\xa2\xf9.\xaajHc\xc4\ +\x7f\xb3,\xd2\x8e\xb6\xb4GH\x10\x88\x00\xc2\xf4^\x08\ +\xb2\xe9V@\x00s>\x90\xe2\xaa\xa61\x06\xb8\x16\xd1\ +\x00\x10\xc1\xc7\xb8\xd8\xfe\xc4\xe6\xeeq\xf9F\x1cO\xab\ +\x01\x00\x85\xa0\xb6\x9f\xda;J\x04\x92\xe7\x1c\x8fk\xf8\ +d\x87l\x939\x00\x06\xc6\x80\xb0n\xe2\xe3\xba\xd6\xc8\ +\xfd\x91\xc9\xf7\xaf\x1f\xdf\xdd\x1d\xad\x8bL\xdb\xd7)@\ +\xbcVZP\x18v\xc7\xe8\xf1\xdd\xff>,\x1d\x1c}\ +\x5cG\x84\xc4y\xad\x00\x11\xb3\xdbd\x5c\xb85\x02\x06\ +Bi^\x12\x9a\xce\xdf\x89\x0c\x8c\xfa\x8e\xb6\x1d\xdf\xd5\ +\xb0\xd0N\x16\xce\x14I\x15\xc7T\x87\xef\xa7\xc9\xb0H\ +\x9a\xf0\xfb\xabl\xb2l\xcd\x9c\x9f3n\xd3\x96\x86)\ +\x90\x91\xe8\x04\x03\xd0\xdd?F\x9c\xb1U\xc5\xef4/\ +]\xf4\xdc\x12\x9e\xb3,\xb1a\xd2\x17\xdc}\xfb\xde\xe0\ +\x87\xbe@8\x9bq\xdc\x9d\xbd\xf1\xac0\x881\x00k\ +r\xe208\x1e\xc4\x90\x97\xe3\xc8\x81-\xf6\xa6\xf3=\ +\xdb\xce]\xe9\xef)\xa9n\xfaJX\xd4\x01b\x03\x5c\ +\xb1d\x22\xe9\x88CU\xe2z\xee\x0em\xb6)\x12\xb9\ +c5\xc3;\x11\xaa_\x04\x9e\x96f\x08\x86t\xe4/\ +\x89E\xc4\xb4\xd0y\xcf\x8bW\x0a3\xa5\xe2\xfct\xad\ +\xe3\xce\xf0\x07\x9e\xc7\xbe\xb0g\xcc\xc7\xc6'\x82N\xc9\ +\xc6\x90\xe2\xd6\x90\x97\x93\x8c\xc48'k\xfc\xa5+\xac\ +\x99z\xe3\x220g\x8c\xdd\x7f4\x85C5\x97&8\ +\x03\xdf\xb1i\xb9m\xcb\xba,\xbb7`\xc2\x22\xa00\ +/M.\xb0R]\x8cM\xf7\xcf\x10\x04\xd3\x14\x98\xf0\ +G\xd0\xf8\xeb\x0d\xbfe\x18\x07/\x9e\xdc\x1b\x9e\x95$\ +\x220\xc6\xa4\xf5\x95\x0dF\xac\xd3\x16\xe8\xef\xba\xb8\xc3\ +\xd3q\xe6Q\xee\x96\xca\x9d\xee%\xb9\xd5\xab\x9fO\xcb\ +\xc8\xcfMQ2Sc\x99\xd3aC\xc4\x100L\x0b\ +\x93\x01\x1d]\xbd\x1eq\xf5\xe6C=81r\xb8\xfb\ +\xf4\xc1\x1a\x00a\x00a\x22\xd2g\xc0\xf6u\xfb\x1a\xbc\ +~\xcf\xed\xb2\xdes\x9f\xfd\x03\xc0\x11\x8d=}\xed\xce\ +5)+\xb7\x95+Z\xfcZp\xa6:U\xc5\x08F\ +L\x89\x08\x10\x11\xdf\x9f\xa3}\xad\xc7\x06\xae6u\x03\ +\x08E\x13$\xa2\xe0\x0c\x98\xad\xde}\xec\xcd\x9b\x0d\xfb\ +/\x03\xb0\x03P\xe7D\x89F\x92\x9cn%!\xa3 \ +!\xe8\x1d\xf6\x06F\xfb\xbc\x00\xf4\xe8\x5cG\xa2P\x1f\ +\x80\x08\x11\xd1\xa2\xa5\x891&\xcd\x81\xcdD\x06 a\ +\xfa7oEcD\xa3\x030\x89\xc8\x9a\xcb\xf9\x0f)\ +i\x5c\x9ar\xdc\xdf`\x00\x00\x00\x00IEND\xae\ +B`\x82\ +\x00\x00\x04\xf8\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x16\x00\x00\x00\x16\x08\x06\x00\x00\x00\xc4\xb4l;\ +\x00\x00\x00\x06bKGD\x00\x00\x00\x00\x00\x00\xf9C\ +\xbb\x7f\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\ +\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\ +\xd5\x0a\x11\x17+\x00C+G\xfb\x00\x00\x04\x85ID\ +AT8\xcb\x8d\x95[hTG\x18\xc7\x7fs\xcen\ +\xf6\x92l\xe2\x0d\xb5\xb5jH[M\x0d\x16\xdcf\xc5\ +\x18\x0d\x16\x95R\x0a\x81\xbeD\xc4\x06\x94\xb6\xd8B)\ +\xb6T\x8b\xd1\x07\x95\xc4z\x81\xfa\xd0\x87B\xfb\x16\xa1\ +PA\x10\xa9\xad\xb6\x09\xa5(\xda\x82\xa6\x18s\xd1\xb4\ +I\x9b\xdb\xba\xbbI\xdc\xdd\xec\xd9\xeb9s\xa6\x0f\xbb\ +\xae\xc6D\xe9\xc0\xc7<\xcc\xcco\xbe\xf9.\xff\x11<\ +c\x1c\x87%\x80x\xc6\x96H\x0b\xd8s-<\xf5\xd0\ +I\xf8B\xc1\xa7\x02\xe4\x5c\xeb\x0a4\x01\xe7%\xbc\xd3\ +\x02\xea\x7f\x81O\xc2\xe9\xea\x86\x86\x8f\xb665\xb9u\ +\x97\x0b\x94*\x9a\xb2m\xb0,\xacT\x8a\x9f\xdb\xdb3\ +\x83}}\xdfK\xd8\xf3$|\x16\xf8\x04\x9c\xa9\xae\xaf\ +\xff`\xdb\xce\x9d\xeex_\x1f\xa9\xf1q\xb0m\xb0\xed\ +<TJ\x94\x94\xb8\x17-b\xfe\x9a5\x5c9w.\ +3\xd4\xdb\xfb\xdd\xe7\xf0\xee\xe3\x1c\xfd\x09\xe8W\xd5u\ +u\xefo\xdb\xb5\xcb\x13\xef\xeb#\x1d\x0a!D\xe1n\ +\xa5\x1e\xcdJa\x19\x06\x96aPS_\xef\x88\xc6\xe3\ +\xd5\xaf\x85\xc3\xcb;\xe0\x87Y\xe0\x13\xf0\xf5\xea@`\ +\xcf\xf6\xe6fO\xac\xa7\x87t$B\xcc\xe3A*\x85\ +\xcb\xb6g\x84\xc3\x10\x82\xe9\x92\x12\x1c\xb1\x18f2\xc9\ +\x9a\xf5\xeb\x9dS\xb1XM\xed\xc4\xc4\xd2\x0e\xf8\xa9\x08\ +>\x01\xdf\xae\xf6\xfb\x9b\xb7\xef\xde\xed\x89\xde\xbeM:\ +\x12!\xeav#\xaa\xaa\xc8\x98&v6[\x84'\x84\ + \xe1\xf3\xe1]\xbb\x96x8\x8c#\x16\xc3J\xa5x\ +\xc5\xefwF\x13\x89\xb5\xb5\x13\x13\x0b;\xe0\x8a\x06 \ +\x1c\x8e\xf7\xb677{\xa2]]dB!rJ\x91\ +\xb5,\x16\xec\xd8\xc1\xd2}\xfbH\xf9|L\x0b\x81\xa1\ +i\x18ee\xacjme\xc5\xde\xbd\x98B\x90V\x0a\ +#\x12\xe1\xc1\xe0 [\xb7l\xf1h\x1e\xcf'\x00\x1a\ +\x80\xae\xebhB\x90\xbe\x7f\x1f\x0aO\xf7Z\x16\xa13\ +g\x10B\xb0\xec\xc0\x01R\xe5\xe5\x18\xa5\xa5\xbc\xdc\xda\ +\x8a\xb3\xb4\x94\xbf\x0e\x1d\xc2m\x9a\xb8\x00\xa5\x14F8\ +\x8c\xaei\xa8B.\x1c\xc5\xbaT*\x9fy!@)\ +*l\x9b\xf8\xd4\x14\xc1\xd3\xa7Yq\xf0 /\x1e?\ +\x8e\xaeihJ1p\xf80\x04\x83\xf8r9\x94R\ +(\xa5\xb0\x95\x02)\x8bI\xd6\x1eU\xbc*\x96\xd3\xc3\ +\x92*\xcf\xe5`r\x92\xa9\x0b\x17pz<8\xbd^\ +\x22\x17/b\x8d\x8d\x15\xa1\xb6mc?\x84\xcbG\xbd\ +\xe4x\x1c\x8cm\xe7=\xd7\xf2\xf7%t\x1d\xe9v\xb3\ +\xb8\xb1\x11M\x084\xe0\xb9\xc6F\xe2\xb7naI\x89\ +\xea\xeeF\x9a&\xb6i\xa24\x0dJJ\xf2\xce\xcd\xf2\ +X\xca|3HI\x020<\x1e^:v\x0cgY\ +\x19\xff\x9c:E\xdf\xfe\xfdhBPs\xf4(S\xe3\ +\xe3\xc4\xa5$\xd9\xd3C\xf2\xde=\x8c\xfe~\xe4\xf00\ +\xb6i\xce\x04?\x84*)\xc9HIr\xde<V\xb5\ +\xb5\xa1{\xbd\xfc\xdd\xd6F\xa6\xab\x8b\xe8\xd9\xb3\xf4\x1f\ +9\x82\xd3\xe5bc{;\x19\x97\x8b\x14y\x15r,\ +^\x8c\xf2\xf9f\x86BJ\x8998\x88\x98\x9a\x22\x15\ +\x0cb\x99&l\xdaD\xe2\xe6M\xee_\xbaD\xee\xfa\ +uJGG)\x07\x12\x97/\xd3\xadi\xbc\xd0\xd4\x84\ +\xab\xa2\x02+/H\xe8.\x17b\xe9\xd2\x99-\xbd\xcd\ +\xb6\xad\xb1\xe1\xe1\xbau\x0d\x0d\xce\x5c$Bnb\x02\ +g\x22\xc1\x83p\x1852\x82g`\x00\xbb\x00\xd0\xa6\ +\xa7\x89\xde\xb9C.\x99\xc4\x15\x8d\x92\x1b\x19\xc1\xb3r\ +%U\x81\x00=7n\x98\xa1\xc9\xc9\xdb\x1d\xf0\x8d\x0e\ +\xd0\x01W\xebR)5:6\xb6q\xdd\xe6\xcd\xce\x9c\ +a\x90\x09\x06\xd1\x86\x86\xd0C\xa1\x22\xd4.\x98SJ\ +r\xbd\xbddGF(\xad\xac\xa42\x10\xe0No\xaf\ +\xf9\xe7\xdd\xbb\x7f\xd8\xf0z'XE\xad\xe8\x80\xab\x1b\ +\xd3i9:>^\xef\xaf\xafw\xe6\x92I\xb2\xf1x\ +\x11\xa6\xe6\x98\xcb*+YY[\xcb\xef\x9d\x9d\xd9\xfe\ +\xb1\xb1_mx\xb3\x05\xb2\xb3\xd4\xad\x03\xae\xd5e2\ +\xe6h0\xb8\xc9\x1f\x088s\x99\x0c\xd9t\x1at\x1d\ +\xa5\xeb\xa0i\xa8\x82\x95WU\xb1\xdc\xef\xe7\xda/\xbf\ +d\x86b\xb1\x1fmx\xbb\x05\xac\xa7\xe9\xb1\x13X\xf2\ +1|V\xe3\xf3}\xf8\xd6\x86\x0d%\xce\xf9\xf3\x11e\ +e\xf9\xe2/h\xb2\x92\x12\xe5\xf5\xd2y\xfe\xbc\xd9\x1d\ +\x8f\xff\xf6%\x1c\xb2!\x02\x84\x81\xf4\x93\xe0R`9\ +\xf0<\xb0\xac\x09v\xbc\x0ao\x00B\xe5\xf7\x09\x05B\ +\xe4\xa3\xa0\x04\xa8\x01\xe8o\x87s\xc08\x10\x04\xfe\x05\ +\x06\x01S\xcc\xf1Uy\x01\x1fP\x01,\x04\x16\x00\x8b\ +\x80y\x85\x17\x19@\x1cxP\xb0(0\x0d\xc4(\xc4\ +\x17\xe0?\x98\x97d\xf0\xa2y\x08\xf3\x00\x00\x00\x00I\ +END\xaeB`\x82\ +\x00\x00\x03\xbb\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x16\x00\x00\x00\x16\x08\x06\x00\x00\x00\xc4\xb4l;\ +\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\ +\x00\x00\x00\x19tEXtSoftware\ +\x00www.inkscape.or\ +g\x9b\xee<\x1a\x00\x00\x03MIDAT8\x8d\xb5\ +\x95Mh\x5cU\x18\x86\x9f\xef\x9cs\xef\xcd\x9fR\xfb\ +#\x18\x8d4\x05\x8d\x88\xa0\x82\xc6\x98\x99,\xda(\x11\ +\xa9\xc1\xfa\x1f\x8a]H\xe9\xa6+k\x13\xa1.\xa2\x88\ +\x1b\x11\x84\x8a.L7\xadbuQ\x8d\x22B-\xb6\ +\x1b\x15\x22\x9d*(\x88A\x94X\xd3\x88\xa16i\xe8\ +df\xee\xb9\xf7s1w\x921\xa6c\x5c\xf4\xc0\xcb\ +9\x8b\x8f\xe7{\xbf\x97{\xee\x11U\xe5J,sE\ +\xa8\x80\xfb?\xc5\xbd\xfb\x83\x01\x11\x0e\x84m\xbe\xff\xd4\ +\xa8\xfaF\xb5kv\xdc7\x12\xee\xbd~S\xe7\x87F\ +l\xdfZ\x0c\xfd'X^\x12\x93\x1bvo\xde\xdcq\ +\xfb\xab{\x06\x0f\xb4X\xe3\xd2\xb5\x18i\xd8y`X\ +Zs\xe2\xc6\xbbo\xdd\x9a\xbf\xaf\xfb\xd1\xa6\xc0\x85k\ +a6\x06\xe7\x9f\x97v\xc1\x9d\xea\xbf{G\xe7\x9d]\ +\xf7\x06\xdfN\x7fN\xcf\xe6A\x8c\xa3R^p\x8b\xb9\ +\xfd\xc1?\xea\xc50\x1b\xb4\xfa\xf6Z\xf6\xab\x82s#\ +\xe1\x1d\xd64\x9d\x18\xdc\xb6\xf3\x9a\xeb6\xdd`'\xa6\ +>\x05\x94$Mxa\xd7[\xcdF\x0c\x82 \xb2\x9c\ +\xe4\xe8\xd8\xee\xf5\xe0\x1d\xb0:\xb8\xf79\xb7\xbd\xb5\xb9\ +\xed\xe8\xc3[\x9fn3\x9121\xf5\x09F,\xd6\x04\ +|1y\x18#\x0e#\x06k,\x82AD\xc8oy\ +\xbcq\x14\xb9\xe1`\xdf\xba\xab7\xbc\xbc\xa3\x7fW\xcb\ +\xf9\xc5)~>{\x06k\x02\x9c\x09H5%M\x13\ +\xacq\x18\xb1\xf8\xd4`2\xc7I\x1a\xa3(\xb2\x1a8\ +?\xe2\x0e\xb6o\xecxf \xffX\xcb\xe4\xf9o\x98\ +\x9e\xfbi\x19\xa8\x09\x81\xa4\xa4\xb6BE<\x89\xc6\xa0\ +\xe0\x08\x89\xecUx\x8dYy\x83\x97\x1d\x1b\xda1\x88\ +\x92\x10\xfb\x12e_$\x16\x8b\xa2\xd8\x10\xc0cP\x8c\ +\x15\xc4T\xbdi\xaah\x09\x8a\xe9\x85\x7fE\xb1\x94\xfe\ +W\xcd\xc9\x13\xe7\xfe\xfc\xfd\xd0G'\xdf\xbdt\xd3\x86\ +n\xba\xae\xbd\x87\xb2/\x92\x9a\x12\xd8\x0a.\x82\xb0\xd9\ +\x12\xb5X\x9aZ-Qk\xf5\x1c4\x1bR\xa9\x5c\x1e\ +\xac\xa3\x9a~\xfd\x9a\xdf\xf7\xd7\xec\x85\xe1c\xc7\x0f\x17\ +\xd7\x87\x1d\xf4t>D\x10\x04\xd8\xc0\xe0BC\x10\x99\ +*|\x09jq\x91\xc1XA\x81\x85s\xcb\xbc\xa5(\ +D$\x00\xa2\x897\xfc\x07\xb7=U\x9c\x19O\xde;\ +r\x7f\xef\xf6\xb6\xbe\xce'\xa50\xfb1j*<\xb8\ +\xe5Y\x9cs\xd8\xc0\xa0\xaa\xa4^I|uG`\xe6\ +\x0c-\x22\x12\xabj\xec2\xa8\x03\xc2L\xd1\x0f\xef'\ +\xdf\xdd\x98\xe7\x91\xe3~\xfc\x9d\xdc]\xdb6\xe6o\xd9\ +\xe9N\xcf\x1eCSx\xfd\xd0+\xe5r1\x8eV\x8e\ +.07\x7f\x16\x03\x84\x22\x92\xd4\x1c\xdbL\xae\xa6\xdf\ +\xbeL\xfe\x98\xff\x95\xa1\xc4\x9f<8\x7fq\xbe+\xd7\ +3\x14j\x22\xc4e\x0d\x0ac\xfe\x81\xf2E\x16\x80r\ +\xa6R\xb6\xd7X\xae\xe1Oh~\x9a\x85\xc2\xdb\xc9\xde\ +B\xe1\xf4\xc9\xcfN\x8c\x97\x17\x8b\xa5F\xe5\xf5+\xad\ +9N2\xf9:\xf7\x15@|\x09-\x8c\xa5/V\x86\ +~\x99\xbcT<\xb2\x874\x8d\xc4P\xcc\x5c\xc6Y\x9d\ +_\xa1j\x14\xaa\xeaE$\x8b\xaa\xda\xb1\xae\x91\x03\xdc\ +\xf7G\x93\xb1\xca\xe0\xcc\x8f\xeb6\xcb\xee$f.\x1b\ +\xdd\xd7\xc1c`\x11(\xa9\xaaJ\xfd\x8d\x91*=\x00\ +\xa2lwu\x13H&\xcdT?e\x19(\xab.\xbf\ +*r\xb9\xc7TD,\xd5\xef\xdc\xd6\xa96Mm\xa2\ +\x04Ht\x15\xc8\xdfq\xe6X\xba\xbc$\xce\xad\x00\x00\ +\x00\x00IEND\xaeB`\x82\ +" + +qt_resource_name = b"\ +\x00\x05\ +\x00o\xa6S\ +\x00i\ +\x00c\x00o\x00n\x00s\ +\x00\x0b\ +\x0c+\x1f\xc7\ +\x00g\ +\x00o\x00-\x00n\x00e\x00x\x00t\x00.\x00p\x00n\x00g\ +\x00\x10\ +\x08\x15\x13g\ +\x00v\ +\x00i\x00e\x00w\x00-\x00r\x00e\x00f\x00r\x00e\x00s\x00h\x00.\x00p\x00n\x00g\ +\x00\x10\ +\x08\xea\xfbg\ +\x00p\ +\x00r\x00o\x00c\x00e\x00s\x00s\x00-\x00s\x00t\x00o\x00p\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x0e6v\xc7\ +\x00g\ +\x00o\x00-\x00p\x00r\x00e\x00v\x00i\x00o\x00u\x00s\x00.\x00p\x00n\x00g\ +" + +qt_resource_struct = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x02\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00,\x00\x00\x00\x00\x00\x01\x00\x00\x03\xa6\ +\x00\x00\x01{\xe0\xa8\xe4\xe2\ +\x00\x00\x00R\x00\x00\x00\x00\x00\x01\x00\x00\x08\xfe\ +\x00\x00\x01{\xe0\xa8\xe4\xe2\ +\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01{\xe0\xa8\xe4\xe2\ +\x00\x00\x00x\x00\x00\x00\x00\x00\x01\x00\x00\x0d\xfa\ +\x00\x00\x01{\xe0\xa8\xe4\xe2\ +" + +def qInitResources(): + QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() diff --git a/examples/webenginequick/nanobrowser/resources.qrc b/examples/webenginequick/nanobrowser/resources.qrc new file mode 100644 index 000000000..829285ec0 --- /dev/null +++ b/examples/webenginequick/nanobrowser/resources.qrc @@ -0,0 +1,8 @@ +<RCC> + <qresource prefix="/icons"> + <file alias="go-next.png">icons/3rdparty/go-next.png</file> + <file alias="go-previous.png">icons/3rdparty/go-previous.png</file> + <file alias="process-stop.png">icons/3rdparty/process-stop.png</file> + <file alias="view-refresh.png">icons/3rdparty/view-refresh.png</file> + </qresource> +</RCC> |