diff --git a/src/contents/ui/ListWebView.qml b/src/contents/ui/ListWebView.qml index 6b2e51e..0539cb3 100644 --- a/src/contents/ui/ListWebView.qml +++ b/src/contents/ui/ListWebView.qml @@ -1,91 +1,93 @@ /*************************************************************************** * * * Copyright 2014-2015 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * * * ***************************************************************************/ import QtQuick 2.3 import QtQuick.Controls 2.0 import QtQml.Models 2.1 import QtWebEngine 1.6 import org.kde.kirigami 2.7 as Kirigami import org.kde.mobile.angelfish 1.0 Repeater { id: tabs clip: true property bool activeTabs: false property bool privateTabsMode: false property alias currentIndex: tabsModel.currentTab property WebView currentItem property alias tabsModel: tabsModel model: TabsModel { id: tabsModel isMobileDefault: Kirigami.Settings.isMobile privateMode: privateTabsMode Component.onCompleted: { tabsModel.loadInitialTabs(); loadTabsModel(); } signal loadTabsModel() } delegate: WebView { id: webView anchors { bottom: tabs.bottom top: tabs.top } privateMode: tabs.privateTabsMode userAgent.isMobile: model.isMobile width: tabs.width + property bool readyForSnapshot: false property bool showView: index === tabs.currentIndex - visible: showView && tabs.activeTabs - x: 0 + visible: (showView || readyForSnapshot || loadingActive) && tabs.activeTabs + x: showView && tabs.activeTabs ? 0 : -width + z: showView && tabs.activeTabs ? 0 : -1 onShowViewChanged: { if (showView) { tabs.currentItem = webView } } onRequestedUrlChanged: tabsModel.setUrl(index, requestedUrl) Component.onCompleted: url = model.pageurl Connections { target: webView.userAgent onUserAgentChanged: { tabsModel.setIsMobile(index, webView.userAgent.isMobile); } } Connections { target: tabs.model onLoadTabsModel: url = model.pageurl } } } diff --git a/src/contents/ui/Navigation.qml b/src/contents/ui/Navigation.qml index ea9430a..18ee836 100644 --- a/src/contents/ui/Navigation.qml +++ b/src/contents/ui/Navigation.qml @@ -1,238 +1,238 @@ /*************************************************************************** * * * Copyright 2014-2015 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * * * ***************************************************************************/ import QtQuick 2.3 import QtQuick.Layouts 1.0 import QtWebEngine 1.4 import QtQuick.Controls 2.0 as Controls import org.kde.kirigami 2.5 as Kirigami import org.kde.mobile.angelfish 1.0 Item { id: navigation anchors { bottom: parent.bottom left: parent.left right: parent.right } height: expandedHeight property bool navigationShown: true property int expandedHeight: Kirigami.Units.gridUnit * 3 property int buttonSize: Kirigami.Units.gridUnit * 2 signal activateUrlEntry; Rectangle { anchors.fill: parent; color: Kirigami.Theme.backgroundColor; } RowLayout { id: layout anchors.fill: parent anchors.leftMargin: Kirigami.Units.gridUnit / 2 anchors.rightMargin: Kirigami.Units.gridUnit / 2 spacing: Kirigami.Units.smallSpacing Kirigami.Theme.inherit: true Controls.ToolButton { icon.name: rootPage.privateMode ? "view-private" : "open-menu-symbolic" Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize Kirigami.Theme.inherit: true onClicked: globalDrawer.open() } Controls.ToolButton { Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize Rectangle { anchors.centerIn: parent height: Kirigami.Units.gridUnit * 1.25 width: Kirigami.Units.gridUnit * 1.25 color: "transparent" border.color: Kirigami.Theme.textColor border.width: Kirigami.Units.gridUnit / 10 radius: Kirigami.Units.gridUnit / 5 Kirigami.Theme.inherit: true Controls.Label { anchors.centerIn: parent height: Kirigami.Units.gridUnit width: Kirigami.Units.gridUnit fontSizeMode: Text.Fit minimumPixelSize: 0 minimumPointSize: 0 clip: true text: "%1".arg(tabs.count) horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter Kirigami.Theme.inherit: true } } onClicked: pageStack.push(Qt.resolvedUrl("Tabs.qml")) } Controls.ToolButton { id: backButton Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize visible: currentWebView.canGoBack && !Kirigami.Settings.isMobile icon.name: "go-previous" Kirigami.Theme.inherit: true onClicked: currentWebView.goBack() } Controls.ToolButton { id: forwardButton Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize visible: currentWebView.canGoForward && !Kirigami.Settings.isMobile icon.name: "go-next" Kirigami.Theme.inherit: true onClicked: currentWebView.goForward() } Controls.ToolButton { id: labelItem Layout.fillWidth: true Layout.preferredHeight: buttonSize property string scheme: UrlUtils.urlScheme(currentWebView.requestedUrl) Controls.ToolButton { id: schemeIcon anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter icon.name: { if (labelItem.scheme === "https") return "lock"; if (labelItem.scheme === "http") return "unlock"; return ""; } visible: icon.name height: buttonSize * 0.75 width: visible ? buttonSize * 0.75 : 0 Kirigami.Theme.inherit: true background: Rectangle { implicitWidth: schemeIcon.width implicitHeight: schemeIcon.height color: "transparent" } onClicked: activateUrlEntry() } Controls.Label { anchors.left: schemeIcon.right anchors.right: parent.right anchors.top: parent.top height: parent.height text: { if (labelItem.scheme === "http" || labelItem.scheme === "https") { var h = UrlUtils.urlHostPort(currentWebView.requestedUrl); var p = UrlUtils.urlPath(currentWebView.requestedUrl); if (p === "/") p = "" return '%1%2'.arg(h).arg(p); } return currentWebView.requestedUrl; } textFormat: Text.StyledText elide: Text.ElideRight verticalAlignment: Text.AlignVCenter Kirigami.Theme.inherit: true } onClicked: activateUrlEntry() } Controls.ToolButton { id: reloadButton Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize visible: !Kirigami.Settings.isMobile icon.name: currentWebView.loading ? "process-stop" : "view-refresh" Kirigami.Theme.inherit: true - onClicked: currentWebView.loading ? currentWebView.stop() : currentWebView.reload() + onClicked: currentWebView.loading ? currentWebView.stopLoading() : currentWebView.reload() } Controls.ToolButton { id: optionsButton property string targetState: "overview" Layout.fillWidth: false Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize icon.name: "overflow-menu" Kirigami.Theme.inherit: true onClicked: contextDrawer.open() } } states: [ State { name: "shown" when: navigationShown AnchorChanges { target: navigation anchors.bottom: navigation.parent.bottom anchors.top: undefined } }, State { name: "hidden" when: !navigationShown AnchorChanges { target: navigation anchors.bottom: undefined anchors.top: parent.bottom } } ] transitions: Transition { AnchorAnimation { duration: Kirigami.Units.longDuration; } } } diff --git a/src/contents/ui/Tabs.qml b/src/contents/ui/Tabs.qml index d75e6d1..97174e6 100644 --- a/src/contents/ui/Tabs.qml +++ b/src/contents/ui/Tabs.qml @@ -1,242 +1,199 @@ /*************************************************************************** * * * Copyright 2014-2015 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * * * ***************************************************************************/ import QtQuick 2.3 import QtQuick.Controls 2.0 as Controls import QtGraphicalEffects 1.0 //import QtWebEngine 1.0 import QtQuick.Layouts 1.0 import org.kde.kirigami 2.7 as Kirigami import org.kde.mobile.angelfish 1.0 Kirigami.ScrollablePage { id: tabsRoot title: rootPage.privateMode ? i18n("Private Tabs") : i18n("Tabs") leftPadding: 0 rightPadding: 0 topPadding: 0 bottomPadding: 0 Kirigami.ColumnView.fillWidth: false actions.main: Kirigami.Action { icon.name: "list-add" text: i18n("New") onTriggered: { tabs.tabsModel.newTab("about:blank") pageStack.pop() urlEntry.open(); } } property int itemHeight: Kirigami.Units.gridUnit * 6 property int itemWidth: { if (!landscapeMode) return width; // using grid width to take into account its scrollbar var n = Math.floor((grid.width - Kirigami.Units.largeSpacing) / (landscapeMinWidth + Kirigami.Units.largeSpacing)); return Math.floor(grid.width / n) - Kirigami.Units.largeSpacing; } property int landscapeMinWidth: Kirigami.Units.gridUnit * 12 property bool landscapeMode: grid.width > landscapeMinWidth * 2 + 3 * Kirigami.Units.largeSpacing //Rectangle { anchors.fill: parent; color: "brown"; opacity: 0.5; } GridView { id: grid anchors.fill: parent anchors.bottomMargin: Kirigami.Units.largeSpacing anchors.leftMargin: landscapeMode ? Kirigami.Units.largeSpacing / 2 : 0 // second half comes from item anchors.rightMargin: landscapeMode ? Kirigami.Units.largeSpacing / 2 : 0 // second half comes from item anchors.topMargin: Kirigami.Units.largeSpacing model: tabs.model cellWidth: itemWidth + (landscapeMode ? Kirigami.Units.largeSpacing : 0) cellHeight: itemHeight + Kirigami.Units.largeSpacing delegate: Item { // taking care of spacing width: grid.cellWidth height: grid.cellHeight Item { id: tabItem anchors.centerIn: parent width: itemWidth height: itemHeight - Image { + // ShaderEffectSource requires that corresponding WebEngineView is + // visible. Here, visibility is enabled while snapshot is taken and + // removed as soon as it is ready. + ShaderEffectSource { + id: shaderItem + + live: false anchors.fill: parent - clip: true - fillMode: Image.PreserveAspectCrop - source: tabs.itemAt(index) ? tabs.itemAt(index).thumb.source : "" - verticalAlignment: Image.AlignTop + sourceRect: Qt.rect(0, 0, sourceItem.width, height/width * sourceItem.width) + sourceItem: tabs.itemAt(index) + + Component.onCompleted: { + sourceItem.readyForSnapshot = true; + scheduleUpdate(); + } + onScheduledUpdateCompleted: sourceItem.readyForSnapshot = false LinearGradient { id: grad anchors.fill: parent cached: true start: Qt.point(0,0) end: Qt.point(0,height) gradient: Gradient { GradientStop { position: Math.max(0.25, 1 - 1.5 * (1 - label.y / itemHeight)); color: "transparent"; } GradientStop { position: Math.max(0.25, label.y / itemHeight); color: Kirigami.Theme.backgroundColor; } } } } - // ShaderEffectSource requires that corresponding WebEngineView is - // visible. Which is probably not the best practice as it seems to keep - // all the views active. -// ShaderEffectSource { -// id: shaderItem - -// //live: true -// anchors.fill: parent - -// sourceRect: Qt.rect(0, 0, width, height) - -// sourceItem: { -// tabs.itemAt(index); -// } -// //opacity: tabs.currentIndex == index ? 1 : 0.0 - -// Behavior on height { -// SequentialAnimation { -// ScriptAction { -// script: { -// print("Animation start"); -// // switch to tabs -// } -// } -// NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad } -// NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; target: contentView; property: opacity } -// ScriptAction { -// script: { -// print("Animation done"); -// contentView.state = "hidden" -// } -// } -// } -// } - -// Behavior on width { -// NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad} - -// } - -// LinearGradient { -// id: grad -// anchors.fill: parent -// cached: true -// start: Qt.point(0,0) -// end: Qt.point(0,height) -// gradient: Gradient { -// GradientStop { position: Math.max(0.25, 1 - 1.5*(1-label.y/itemHeight)); color: "transparent"; } -// GradientStop { position: Math.max(0.25, label.y/itemHeight); color: Kirigami.Theme.backgroundColor; } -// } -// } -// } - Rectangle { // border around a tile anchors.fill: parent; border.color: Kirigami.Theme.textColor border.width: webBrowser.borderWidth color: "transparent" opacity: tabs.currentIndex === index ? 0.75 : 0.3 } Rectangle { // selection indicator anchors.fill: parent color: mouse.pressed ? Kirigami.Theme.highlightColor : "transparent" opacity: 0.2 } MouseArea { id: mouse anchors.fill: parent onClicked: { print("Switch from " + tabs.currentIndex + " to tab " + index); tabs.currentIndex = index; pageStack.pop() } } Controls.ToolButton { icon.name: "window-close" height: Kirigami.gridUnit width: height anchors.right: parent.right anchors.rightMargin: Kirigami.Units.smallSpacing + Kirigami.Units.largeSpacing + (tabsRoot.landscapeMode ? 0 : tabsRoot.width-grid.width) anchors.top: parent.top anchors.topMargin: Kirigami.Units.smallSpacing onClicked: tabs.tabsModel.closeTab(index) } Column { id: label anchors { left: tabItem.left right: tabItem.right bottom: tabItem.bottom bottomMargin: Kirigami.Units.smallSpacing leftMargin: Kirigami.Units.largeSpacing rightMargin: Kirigami.Units.largeSpacing } spacing: 0 Kirigami.Heading { elide: Text.ElideRight level: 4 text: tabs.itemAt(index) ? tabs.itemAt(index).title : "" width: label.width } Controls.Label { elide: Text.ElideRight font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.5 text: tabs.itemAt(index) ? tabs.itemAt(index).url : "" width: label.width } } Image { anchors { bottom: tabItem.bottom right: tabItem.right bottomMargin: Kirigami.Units.smallSpacing rightMargin: Kirigami.Units.smallSpacing + Kirigami.Units.largeSpacing + (tabsRoot.landscapeMode ? 0 : tabsRoot.width-grid.width) } fillMode: Image.PreserveAspectFit height: Math.min(sourceSize.height, label.height) source: tabs.itemAt(index) ? tabs.itemAt(index).icon : "" } } } } Component.onCompleted: grid.currentIndex = tabs.currentIndex } diff --git a/src/contents/ui/WebView.qml b/src/contents/ui/WebView.qml index c47623e..07c7559 100644 --- a/src/contents/ui/WebView.qml +++ b/src/contents/ui/WebView.qml @@ -1,287 +1,280 @@ /*************************************************************************** * * * Copyright 2014-2015 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * * * ***************************************************************************/ import QtQuick 2.3 import QtQuick.Controls 2.4 as Controls import QtQuick.Window 2.1 import QtQuick.Layouts 1.3 import QtWebEngine 1.7 import org.kde.kirigami 2.4 as Kirigami import org.kde.mobile.angelfish 1.0 WebEngineView { id: webEngineView property string errorCode: "" property string errorString: "" property bool privateMode: false property alias userAgent: userAgent - property alias thumb: thumb + + // loadingActive property is set to true when loading is started + // and turned to false only after succesful or failed loading. It + // is possible to set it to false by calling stopLoading method. + // + // The property was introduced as it triggers visibility of the webEngineView + // in the other parts of the code. When using loading that is linked + // to visibility, stop/start loading was observed in some conditions. It looked as if + // there is an internal optimization of webengine in the case of parallel + // loading of several pages that could use visibility as one of the decision + // making parameters. + property bool loadingActive: false property bool reloadOnVisible: true // URL that was requested and should be used // as a base for user interaction. It reflects // last request (successful or failed) property url requestedUrl: url UserAgentGenerator { id: userAgent onUserAgentChanged: webEngineView.reload() } - Image { - id: thumb - visible: false - } - - Timer { - id: snaphotTimer - interval: 1000 - repeat: false - onTriggered: { - if (webEngineView.visible) - grabThumb(); - } - } - profile { offTheRecord: privateMode httpUserAgent: userAgent.userAgent onDownloadRequested: { // if we don't accept the request right away, it will be deleted download.accept() // therefore just stop the download again as quickly as possible, // and ask the user for confirmation download.pause() questionLoader.setSource("DownloadQuestion.qml") questionLoader.item.download = download questionLoader.item.visible = true } onDownloadFinished: { if (download.state === WebEngineDownloadItem.DownloadCompleted) { showPassiveNotification(i18n("Download finished")) } else if (download.state === WebEngineDownloadItem.DownloadInterrupted) { showPassiveNotification(i18n("Download failed")) console.log("Download interrupt reason: " + download.interruptReason) } else if (download.state === WebEngineDownloadItem.DownloadCancelled) { console.log("Download cancelled by the user") } } } settings { // Disable builtin error pages in favor of our own errorPageEnabled: false // Load larger touch icons touchIconsEnabled: true // Disable scrollbars on mobile showScrollBars: !Kirigami.Settings.isMobile } // Custom context menu Controls.Menu { property ContextMenuRequest request id: contextMenu Controls.MenuItem { enabled: contextMenu.request != null && (contextMenu.request.editFlags & ContextMenuRequest.CanCopy) != 0 text: i18n("Copy") onTriggered: webEngineView.triggerWebAction(WebEngineView.Copy) } Controls.MenuItem { enabled: contextMenu.request != null && (contextMenu.request.editFlags & ContextMenuRequest.CanCut) != 0 text: i18n("Cut") onTriggered: webEngineView.triggerWebAction(WebEngineView.Cut) } Controls.MenuItem { enabled: contextMenu.request != null && (contextMenu.request.editFlags & ContextMenuRequest.CanPaste) != 0 text: i18n("Paste") onTriggered: webEngineView.triggerWebAction(WebEngineView.Paste) } Controls.MenuItem { enabled: contextMenu.request != null && contextMenu.request.selectedText text: contextMenu.request && contextMenu.request.selectedText ? i18n("Search online for '%1'", contextMenu.request.selectedText) : i18n("Search online") onTriggered: tabsModel.newTab(UrlUtils.urlFromUserInput(BrowserManager.searchBaseUrl + contextMenu.request.selectedText)); } Controls.MenuItem { enabled: contextMenu.request !== null && contextMenu.request.linkUrl !== "" text: i18n("Copy Url") onTriggered: webEngineView.triggerWebAction(WebEngineView.CopyLinkToClipboard) } Controls.MenuItem { text: i18n("View source") onTriggered: tabsModel.newTab("view-source:" + webEngineView.url) } Controls.MenuItem { text: i18n("Download") onTriggered: webEngineView.triggerWebAction(WebEngineView.DownloadLinkToDisk) } Controls.MenuItem { enabled: contextMenu.request !== null && contextMenu.request.linkUrl !== "" text: i18n("Open in new Tab") onTriggered: webEngineView.triggerWebAction(WebEngineView.OpenLinkInNewTab) } } focus: true onLoadingChanged: { //print("Loading: " + loading); print(" url: " + loadRequest.url) //print(" icon: " + webEngineView.icon) //print(" title: " + webEngineView.title) /* Handle * - WebEngineView::LoadStartedStatus, * - WebEngineView::LoadStoppedStatus, * - WebEngineView::LoadSucceededStatus and * - WebEngineView::LoadFailedStatus */ var ec = ""; var es = ""; if (loadRequest.status === WebEngineView.LoadStartedStatus) { - thumb.source = ""; + loadingActive = true; } if (loadRequest.status === WebEngineView.LoadSucceededStatus) { if (!privateMode) { var request = new Object;// FIXME request.url = currentWebView.url; request.title = currentWebView.title; request.icon = currentWebView.icon; BrowserManager.addToHistory(request); BrowserManager.updateLastVisited(currentWebView.url); } - grabThumb(); + loadingActive = false; } if (loadRequest.status === WebEngineView.LoadFailedStatus) { print("Load failed: " + loadRequest.errorCode + " " + loadRequest.errorString); print("Load failed url: " + loadRequest.url + " " + url); ec = loadRequest.errorCode; es = loadRequest.errorString; - thumb.source = ""; + loadingActive = false; // update requested URL only after its clear that it fails. // Otherwise, its updated as a part of url property update. if (requestedUrl !== loadRequest.url) requestedUrl = loadRequest.url; } errorCode = ec; errorString = es; } Component.onCompleted: { print("WebView completed."); var settings = webEngineView.settings; print("Settings: " + settings); } onIconChanged: { if (icon && !privateMode) BrowserManager.updateIcon(url, icon) } onNewViewRequested: { if (request.userInitiated) { tabsModel.newTab(request.requestedUrl.toString()) showPassiveNotification(i18n("Website was opened in a new tab")) } else { questionLoader.setSource("NewTabQuestion.qml") questionLoader.item.url = request.requestedUrl questionLoader.item.visible = true } } onUrlChanged: { if (requestedUrl !== url) { requestedUrl = url; } - thumb.source = ""; } onFullScreenRequested: { request.accept() if (webBrowser.visibility !== Window.FullScreen) webBrowser.showFullScreen() else webBrowser.showNormal() } onContextMenuRequested: { request.accepted = true // Make sure QtWebEngine doesn't show its own context menu. contextMenu.request = request contextMenu.x = request.x contextMenu.y = request.y contextMenu.open() } onAuthenticationDialogRequested: { request.accepted = true sheetLoader.setSource("AuthSheet.qml") sheetLoader.item.request = request sheetLoader.item.open() } onFeaturePermissionRequested: { questionLoader.setSource("PermissionQuestion.qml") questionLoader.item.permission = feature questionLoader.item.origin = securityOrigin questionLoader.item.visible = true } onJavaScriptDialogRequested: { request.accepted = true sheetLoader.setSource("JavaScriptDialogSheet.qml") sheetLoader.item.request = request sheetLoader.item.open() } onVisibleChanged: { // set user agent to the current displayed tab // this ensures that we follow mobile preference // of the current webview. also update the current // snapshot image with short delay to be sure that // all kirigami pages have moved into place if (visible) { profile.httpUserAgent = Qt.binding(function() { return userAgent.userAgent; }); - snaphotTimer.start(); if (reloadOnVisible) { reloadOnVisible = false; reload(); } } } - function grabThumb() { - webEngineView.grabToImage(function(result) { - webEngineView.thumb.source = result.url; - }); + function stopLoading() { + loadingActive = false; + stop(); } } diff --git a/src/contents/ui/webbrowser.qml b/src/contents/ui/webbrowser.qml index 7b8978f..0bbacb8 100644 --- a/src/contents/ui/webbrowser.qml +++ b/src/contents/ui/webbrowser.qml @@ -1,386 +1,386 @@ /*************************************************************************** * * * Copyright 2014-2015 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * * * ***************************************************************************/ import QtQuick 2.1 import QtWebEngine 1.6 import QtQuick.Window 2.3 import QtGraphicalEffects 1.0 import Qt.labs.settings 1.0 import org.kde.kirigami 2.7 as Kirigami import org.kde.mobile.angelfish 1.0 import QtQuick.Layouts 1.2 Kirigami.ApplicationWindow { id: webBrowser title: i18n("Angelfish Web Browser") /** Pointer to the currently active view. * * Browser-level functionality should use this to refer to the current * view, rather than looking up views in the mode, as far as possible. */ property Item currentWebView: tabs.currentItem // Pointer to the currently active list of tabs. // // As there are private and normal tabs, switch between // them according to the current mode. property ListWebView tabs: rootPage.privateMode ? privateTabs : regularTabs onCurrentWebViewChanged: { print("Current WebView is now : " + tabs.currentIndex); } property int borderWidth: Math.round(Kirigami.Units.gridUnit / 18); property color borderColor: Kirigami.Theme.highlightColor; width: Kirigami.Units.gridUnit * 20 height: Kirigami.Units.gridUnit * 30 pageStack.globalToolBar.showNavigationButtons: { if (pageStack.depth <= 1) return Kirigami.ApplicationHeaderStyle.None; if (pageStack.currentIndex === pageStack.depth - 1) return Kirigami.ApplicationHeaderStyle.ShowBackButton; // not used so far, but maybe in future return (Kirigami.ApplicationHeaderStyle.ShowBackButton | Kirigami.ApplicationHeaderStyle.ShowForwardButton); } globalDrawer: Kirigami.GlobalDrawer { id: globalDrawer handleVisible: false actions: [ Kirigami.Action { icon.name: "tab-duplicate" onTriggered: { pageStack.push(Qt.resolvedUrl("Tabs.qml")) } text: i18n("Tabs") }, Kirigami.Action { icon.name: "view-private" onTriggered: { rootPage.privateMode ? rootPage.privateMode = false : rootPage.privateMode = true } text: rootPage.privateMode ? i18n("Leave private mode") : i18n("Private mode") }, Kirigami.Action { icon.name: "bookmarks" onTriggered: { pageStack.push(Qt.resolvedUrl("Bookmarks.qml")) } text: i18n("Bookmarks") }, Kirigami.Action { icon.name: "view-history" onTriggered: { pageStack.push(Qt.resolvedUrl("History.qml")) } text: i18n("History") }, Kirigami.Action { icon.name: "configure" text: i18n("Settings") onTriggered: { pageStack.push(Qt.resolvedUrl("Settings.qml")) } } ] } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer handleVisible: false } // Main Page pageStack.initialPage: Kirigami.Page { id: rootPage leftPadding: 0 rightPadding: 0 topPadding: 0 bottomPadding: 0 globalToolBarStyle: Kirigami.ApplicationHeaderStyle.None Kirigami.ColumnView.fillWidth: true Kirigami.ColumnView.pinned: true Kirigami.ColumnView.preventStealing: true // Required to enforce active tab reload // on start. As a result, mixed isMobile // tabs will work correctly property bool initialized: false property bool privateMode: false // Used for automatically show or hid navigation // bar. Set separately to combine with other options // for navigation bar management (webapp and others) property bool navigationAutoShow: true ListWebView { id: regularTabs anchors.fill: parent activeTabs: rootPage.initialized && !rootPage.privateMode } ListWebView { id: privateTabs anchors.fill: parent activeTabs: rootPage.initialized && rootPage.privateMode privateTabsMode: true } ErrorHandler { id: errorHandler errorString: currentWebView.errorString errorCode: currentWebView.errorCode anchors { top: parent.top left: parent.left right: parent.right bottom: navigation.top } visible: currentWebView.errorCode !== "" } Loader { id: questionLoader anchors.bottom: navigation.top anchors.left: parent.left anchors.right: parent.right } // Container for the progress bar Item { id: progressItem height: Math.round(Kirigami.Units.gridUnit / 6) z: navigation.z + 1 anchors { bottom: navigation.top bottomMargin: -Math.round(height / 2) left: tabs.left right: tabs.right } opacity: currentWebView.loading ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; } } Rectangle { color: Kirigami.Theme.highlightColor width: Math.round((currentWebView.loadProgress / 100) * parent.width) anchors { top: parent.top left: parent.left bottom: parent.bottom } } } Loader { id: sheetLoader } // The menu at the bottom right contextualActions: [ Kirigami.Action { icon.name: "edit-find" shortcut: "Ctrl+F" onTriggered: { if (!sheetLoader.item || !sheetLoader.item.sheetOpen) { sheetLoader.setSource("FindInPageSheet.qml") sheetLoader.item.open() } } text: i18n("Find in page") }, Kirigami.Action { icon.name: "document-share" text: i18n("Share page") onTriggered: { sheetLoader.setSource("ShareSheet.qml") sheetLoader.item.url = currentWebView.url sheetLoader.item.title = currentWebView.title sheetLoader.item.open() } }, Kirigami.Action { enabled: currentWebView.canGoBack icon.name: "go-previous" text: i18n("Go previous") onTriggered: { currentWebView.goBack() } }, Kirigami.Action { enabled: currentWebView.canGoForward icon.name: "go-next" text: i18n("Go forward") onTriggered: { currentWebView.goForward() } }, Kirigami.Action { icon.name: currentWebView.loading ? "process-stop" : "view-refresh" text: currentWebView.loading ? i18n("Stop loading") : i18n("Refresh") onTriggered: { - currentWebView.loading ? currentWebView.stop() : currentWebView.reload() + currentWebView.loading ? currentWebView.stopLoading() : currentWebView.reload() } }, Kirigami.Action { icon.name: "bookmarks" text: i18n("Add bookmark") onTriggered: { print("Adding bookmark"); var request = new Object;// FIXME request.url = currentWebView.url; request.title = currentWebView.title; request.icon = currentWebView.icon; BrowserManager.addBookmark(request); } }, Kirigami.Action { icon.name: "computer" text: i18n("Show desktop site") checkable: true checked: !currentWebView.userAgent.isMobile onTriggered: { currentWebView.userAgent.isMobile = !currentWebView.userAgent.isMobile; } }, Kirigami.Action { icon.name: "edit-select-text" text: rootPage.navigationAutoShow ? i18n("Hide navigation bar") : i18n("Show navigation bar") visible: navigation.visible onTriggered: { if (!navigation.visible) return; rootPage.navigationAutoShow = !rootPage.navigationAutoShow; } } ] // Bottom navigation bar Navigation { id: navigation navigationShown: visible && rootPage.navigationAutoShow && webBrowser.visibility !== Window.FullScreen visible: webBrowser.visibility !== Window.FullScreen Kirigami.Theme.colorSet: rootPage.privateMode ? Kirigami.Theme.Complementary : Kirigami.Theme.Window layer.enabled: navigation.navigationShown layer.effect: DropShadow { verticalOffset: - 1 color: Kirigami.Theme.disabledTextColor samples: 10 spread: 0.1 cached: true // element is static } anchors { bottom: parent.bottom left: parent.left right: parent.right } onActivateUrlEntry: urlEntry.open() } NavigationEntrySheet { id: urlEntry } // Thin line above navigation Rectangle { height: webBrowser.borderWidth color: webBrowser.borderColor anchors { left: parent.left bottom: navigation.top right: parent.right } visible: navigation.navigationShown } // dealing with hiding and showing navigation bar property point oldScrollPosition: Qt.point(0, 0) property bool pageAlmostReady: !currentWebView.loading || currentWebView.loadProgress > 90 onPageAlmostReadyChanged: { if (!rootPage.pageAlmostReady) rootPage.navigationAutoShow = true; else rootPage.oldScrollPosition = currentWebView.scrollPosition; } Connections { target: currentWebView onScrollPositionChanged: { var delta = 100; if (rootPage.navigationAutoShow && rootPage.pageAlmostReady) { if (rootPage.oldScrollPosition.y + delta < currentWebView.scrollPosition.y) { // hide navbar rootPage.oldScrollPosition = currentWebView.scrollPosition; rootPage.navigationAutoShow = false; } else if (rootPage.oldScrollPosition.y > currentWebView.scrollPosition.y) { // navbar open and scrolling up rootPage.oldScrollPosition = currentWebView.scrollPosition; } } else if (!rootPage.navigationAutoShow) { if (rootPage.oldScrollPosition.y - delta > currentWebView.scrollPosition.y) { // show navbar rootPage.oldScrollPosition = currentWebView.scrollPosition; rootPage.navigationAutoShow = true; } else if (rootPage.oldScrollPosition.y < currentWebView.scrollPosition.y) { // navbar closed and scrolling down rootPage.oldScrollPosition = currentWebView.scrollPosition; } } } } } Connections { target: webBrowser.pageStack onCurrentIndexChanged: { // drop all sub pages as soon as the browser window is the // focussed one if (webBrowser.pageStack.currentIndex === 0) webBrowser.pageStack.pop(); } } Settings { property alias x: webBrowser.x property alias y: webBrowser.y property alias width: webBrowser.width property alias height: webBrowser.height } Component.onCompleted: rootPage.initialized = true }