diff --git a/src/contents/ui/Navigation.qml b/src/contents/ui/Navigation.qml index fd0c74c..2d2b9db 100644 --- a/src/contents/ui/Navigation.qml +++ b/src/contents/ui/Navigation.qml @@ -1,203 +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; - Behavior on height { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad} } - 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 - visible: navigationShown spacing: Kirigami.Units.smallSpacing Kirigami.Theme.inherit: true Controls.ToolButton { icon.name: "open-menu-symbolic" Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize Kirigami.Theme.inherit: true onClicked: globalDrawer.open() } Controls.ToolButton { - icon.name: "tab-duplicate" - Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize - Kirigami.Theme.inherit: true + Rectangle { + anchors.centerIn: parent + height: Kirigami.Units.gridUnit * 1.25 + width: Kirigami.Units.gridUnit * 1.25 - onClicked: { - pageStack.push(Qt.resolvedUrl("Tabs.qml")) + 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() } - Item { + Controls.ToolButton { id: labelItem Layout.fillWidth: true - Layout.preferredHeight: layout.height + 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 } - MouseArea { - anchors.fill: parent - onClicked: activateUrlEntry() - } + 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() } 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 - PropertyChanges { target: navigation; height: expandedHeight} + AnchorChanges { + target: navigation + anchors.bottom: navigation.parent.bottom + anchors.top: undefined + } }, State { name: "hidden" when: !navigationShown - PropertyChanges { target: navigation; height: 0} + AnchorChanges { + target: navigation + anchors.bottom: undefined + anchors.top: parent.bottom + } } ] + transitions: Transition { + AnchorAnimation { duration: Kirigami.Units.longDuration; } + } } diff --git a/src/contents/ui/webbrowser.qml b/src/contents/ui/webbrowser.qml index a8cb660..7e93d11 100644 --- a/src/contents/ui/webbrowser.qml +++ b/src/contents/ui/webbrowser.qml @@ -1,354 +1,392 @@ /*************************************************************************** * * * 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 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 /** * Add page of currently active webview to history */ function addHistoryEntry() { //print("Adding history"); var request = new Object;// FIXME request.url = currentWebView.url; request.title = currentWebView.title; request.icon = currentWebView.icon; request.lastVisited = new Date(); BrowserManager.addToHistory(request); } 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 { - top: parent.top - left: parent.left - right: parent.right - bottom: navigation.top - } + anchors.fill: parent activeTabs: rootPage.initialized && !rootPage.privateMode } ListWebView { id: privateTabs - anchors { - top: parent.top - left: parent.left - right: parent.right - bottom: navigation.top - } + 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 { - top: tabs.bottom - topMargin: -Math.round(height / 2) + 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() } }, 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; request.bookmarked = true; 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: !webappcontainer && webBrowser.visibility !== Window.FullScreen + navigationShown: visible && rootPage.navigationAutoShow + visible: !webappcontainer && webBrowser.visibility !== Window.FullScreen Kirigami.Theme.colorSet: rootPage.privateMode ? Kirigami.Theme.Complementary : Kirigami.Theme.Window - layer.enabled: navigation.visible + 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: "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(); } } Component.onCompleted: rootPage.initialized = true }