summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@theqtcompany.com>2015-06-22 21:38:53 +0100
committerAndras Becsi <andras.becsi@theqtcompany.com>2015-08-12 17:21:42 +0200
commit13730318d4261fbc9f3225a27e38578e61b75583 (patch)
tree4c2b47c703d4297a01f043bed46b31649c5fdf4a
Initial code drop
-rw-r--r--qtbrowser.pro3
-rw-r--r--src/browserwindow.cpp138
-rw-r--r--src/browserwindow.h64
-rw-r--r--src/main.cpp82
-rw-r--r--src/qml/BrowserDialog.qml56
-rw-r--r--src/qml/BrowserWindow.qml355
-rw-r--r--src/qml/FeaturePermissionBar.qml98
-rw-r--r--src/qml/LoadIndicator.qml59
-rw-r--r--src/qml/MockTouchPoint.qml63
-rw-r--r--src/qml/PageView.qml395
-rw-r--r--src/qml/assets/UIButton.qml25
-rw-r--r--src/qml/assets/icons/busy.pngbin0 -> 547 bytes
-rw-r--r--src/qml/assets/icons/home.pngbin0 -> 596 bytes
-rw-r--r--src/qml/assets/icons/next.pngbin0 -> 1501 bytes
-rw-r--r--src/qml/assets/icons/previous.pngbin0 -> 1389 bytes
-rw-r--r--src/qml/assets/icons/refresh.pngbin0 -> 1811 bytes
-rw-r--r--src/qml/assets/icons/stop.pngbin0 -> 1675 bytes
-rw-r--r--src/qml/assets/icons/tabs.pngbin0 -> 1548 bytes
-rw-r--r--src/qml/assets/icons/touchpoint.pngbin0 -> 1331 bytes
-rw-r--r--src/resources.qrc21
-rw-r--r--src/src.pro30
-rw-r--r--src/touchmockingapplication.cpp278
-rw-r--r--src/touchmockingapplication.h75
-rw-r--r--src/utils.h75
24 files changed, 1817 insertions, 0 deletions
diff --git a/qtbrowser.pro b/qtbrowser.pro
new file mode 100644
index 0000000..069da53
--- /dev/null
+++ b/qtbrowser.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+SUBDIRS += src
diff --git a/src/browserwindow.cpp b/src/browserwindow.cpp
new file mode 100644
index 0000000..5823014
--- /dev/null
+++ b/src/browserwindow.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBrowser project.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPLv2 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "browserwindow.h"
+#include "utils.h"
+
+#include <QList>
+#include <QQmlContext>
+#include <QQmlEngine>
+#include <QQuickItem>
+#include <QRectF>
+#include <QUrl>
+#include <QVariant>
+
+namespace {
+
+static QUrl startupUrl()
+{
+ QUrl ret;
+ QStringList args(qApp->arguments());
+ args.takeFirst();
+ Q_FOREACH (const QString& arg, args) {
+ if (arg.startsWith(QLatin1Char('-')))
+ continue;
+ ret = Utils::fromUserInput(arg);
+ if (ret.isValid())
+ return ret;
+ }
+ return QUrl(QStringLiteral("http://qt.io/"));
+}
+
+}
+
+void BrowserWindow::ensureProfileInstance()
+{
+ if (m_lazyProfileInstance)
+ return;
+ QQmlComponent *component = new QQmlComponent(engine(), this);
+
+ component->setData(
+ QByteArrayLiteral("import QtQuick 2.0\n"
+ "import QtWebEngine 1.1\n"
+ "WebEngineProfile {\n"
+ " storageName: \"YABProfile\"\n"
+ "}")
+ , QUrl());
+ m_lazyProfileInstance = component->create(engine()->rootContext());
+ Q_ASSERT(m_lazyProfileInstance);
+ QQmlEngine::setObjectOwnership(m_lazyProfileInstance, QQmlEngine::JavaScriptOwnership);
+}
+
+QObject *BrowserWindow::defaultProfile()
+{
+ ensureProfileInstance();
+ return m_lazyProfileInstance;
+}
+
+BrowserWindow::BrowserWindow(QWindow *)
+ : m_lazyProfileInstance(0)
+{
+ setTitle("Yet Another Browser");
+ setFlags(Qt::Window | Qt::WindowTitleHint);
+ setResizeMode(QQuickView::SizeRootObjectToView);
+ setColor(Qt::black);
+
+ engine()->rootContext()->setContextProperty("engine", new Utils(this));
+ setSource(QUrl("qrc:///qml/BrowserWindow.qml"));
+}
+
+BrowserWindow::~BrowserWindow()
+{
+}
+
+void BrowserWindow::updateVisualMockTouchPoints(const QList<QTouchEvent::TouchPoint>& touchPoints)
+{
+ if (touchPoints.isEmpty()) {
+ // Hide all touch indicator items.
+ foreach (QQuickItem* item, m_activeMockComponents.values())
+ item->setProperty("pressed", false);
+
+ return;
+ }
+
+ foreach (const QTouchEvent::TouchPoint& touchPoint, touchPoints) {
+ QQuickItem* mockTouchPointItem = m_activeMockComponents.value(touchPoint.id());
+
+ if (!mockTouchPointItem) {
+ QQmlComponent touchMockPointComponent(engine(), QUrl("qrc:///qml/MockTouchPoint.qml"));
+ mockTouchPointItem = qobject_cast<QQuickItem*>(touchMockPointComponent.create());
+ Q_ASSERT(mockTouchPointItem);
+ m_activeMockComponents.insert(touchPoint.id(), mockTouchPointItem);
+ mockTouchPointItem->setProperty("pointId", QVariant(touchPoint.id()));
+ mockTouchPointItem->setParent(rootObject());
+ mockTouchPointItem->setParentItem(rootObject());
+ }
+
+ QRectF touchRect = touchPoint.rect();
+ mockTouchPointItem->setX(touchRect.center().x());
+ mockTouchPointItem->setY(touchRect.center().y());
+ mockTouchPointItem->setWidth(touchRect.width());
+ mockTouchPointItem->setHeight(touchRect.height());
+ mockTouchPointItem->setProperty("pressed", QVariant(touchPoint.state() != Qt::TouchPointReleased));
+ }
+}
diff --git a/src/browserwindow.h b/src/browserwindow.h
new file mode 100644
index 0000000..1d055bf
--- /dev/null
+++ b/src/browserwindow.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBrowser project.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPLv2 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BROWSERWINDOW_H
+#define BROWSERWINDOW_H
+
+#include <QHash>
+#include <QTouchEvent>
+#include <QQuickView>
+#include <QQmlComponent>
+
+class BrowserWindow : public QQuickView
+{
+ Q_OBJECT
+
+ QHash<int, QQuickItem*> m_activeMockComponents;
+ QObject *m_lazyProfileInstance;
+
+ void ensureProfileInstance();
+public:
+ BrowserWindow(QWindow *parent = 0);
+ ~BrowserWindow();
+
+ void updateVisualMockTouchPoints(const QList<QTouchEvent::TouchPoint>& touchPoints);
+
+public Q_SLOTS:
+ QObject *defaultProfile();
+};
+
+#endif // BROWSERWINDOW_H
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..339847b
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBrowser project.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPLv2 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQmlContext>
+#include <QQmlEngine>
+#include <QQuickView>
+#include <QtWebEngine/qtwebengineglobal.h>
+
+#include "touchmockingapplication.h"
+#include "utils.h"
+
+int main(int argc, char **argv)
+{
+ qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
+#if defined(HOST_BUILD)
+ // We use touch mocking on desktop and apply all the mobile switches.
+ QByteArrayList args = QByteArrayList()
+ << QByteArrayLiteral("--enable-embedded-switches");
+ const int count = args.size() + argc;
+ QVector<char*> qargv(count);
+
+ qargv[0] = argv[0];
+ for (int i = 0; i < args.size(); ++i)
+ qargv[i + 1] = args[i].data();
+ for (int i = args.size() + 1; i < count; ++i)
+ qargv[i] = argv[i - args.size()];
+
+ int qAppArgCount = qargv.size();
+ TouchMockingApplication app(qAppArgCount, qargv.data());
+#else
+ QGuiApplication app(argc, argv);
+#endif
+ QtWebEngine::initialize();
+
+ BrowserWindow window;
+ QObject::connect(window.rootContext()->engine(), SIGNAL(quit()), &app, SLOT(quit()));
+
+#if defined(HOST_BUILD)
+ window.show();
+ if (window.size().isEmpty())
+ window.setGeometry(0, 0, 800, 600);
+#else
+ window.showFullScreen();
+#endif
+
+ app.exec();
+}
diff --git a/src/qml/BrowserDialog.qml b/src/qml/BrowserDialog.qml
new file mode 100644
index 0000000..d463be6
--- /dev/null
+++ b/src/qml/BrowserDialog.qml
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtQuick.Window 2.2
+import QtWebEngine 1.1
+
+Window {
+ property alias defaultProfile: webView.profile
+ property alias currentWebView: webView
+ flags: Qt.Dialog
+ width: 800
+ height: 600
+ visible: true
+ onClosing: destroy()
+ WebEngineView {
+ id: webView
+ anchors.fill: parent
+ }
+}
diff --git a/src/qml/BrowserWindow.qml b/src/qml/BrowserWindow.qml
new file mode 100644
index 0000000..642c16c
--- /dev/null
+++ b/src/qml/BrowserWindow.qml
@@ -0,0 +1,355 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBrowser project.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPLv2 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtWebEngine 1.1
+import QtWebEngine.experimental 1.0
+
+import QtQuick.Controls 1.0
+import QtQuick.Controls.Styles 1.0
+import QtQuick.Layouts 1.0
+import QtQuick.Window 2.1
+import QtQuick.Controls.Private 1.0
+import Qt.labs.settings 1.0
+import QtQuick.Dialogs 1.2
+
+import "assets"
+
+Item {
+ id: browserWindow
+
+ property alias currentWebView: tabs.currentWebView
+ property int visibility: Window.Windowed
+ property int previousVisibility: Window.Windowed
+
+ property string uiColor: "#46a1da"
+ property string uiSelectionColor: "#fad84a"
+ property string uiBorderColor: "#377fac"
+
+ property QtObject otrProfile: WebEngineProfile {
+ offTheRecord: true
+ }
+
+ property bool isFullScreen: visibility == Window.FullScreen
+ onIsFullScreenChanged: {
+ // This is for the case where the system forces us to leave fullscreen.
+ if (currentWebView && !isFullScreen) {
+ currentWebView.state = ""
+ if (currentWebView.isFullScreen)
+ currentWebView.fullScreenCancelled()
+ }
+ }
+
+ height: 600
+ width: 800
+ visible: true
+
+ Settings {
+ id : appSettings
+ property alias autoLoadImages: loadImages.checked;
+ property alias javaScriptEnabled: javaScriptEnabled.checked;
+ property alias errorPageEnabled: errorPageEnabled.checked;
+ property alias pluginsEnabled: pluginsEnabled.checked;
+ }
+
+ Action {
+ shortcut: "Ctrl+D"
+ onTriggered: {
+ downloadView.visible = !downloadView.visible
+ }
+ }
+
+ Action {
+ id: focus
+ shortcut: "Ctrl+L"
+ onTriggered: {
+ addressBar.forceActiveFocus();
+ addressBar.selectAll();
+ }
+ }
+ Action {
+ shortcut: "Ctrl+R"
+ onTriggered: {
+ if (currentWebView)
+ currentWebView.reload()
+ }
+ }
+ Action {
+ shortcut: "Ctrl+T"
+ onTriggered: {
+ tabs.createEmptyTab()
+ tabs.currentIndex = tabs.count - 1
+ addressBar.forceActiveFocus();
+ addressBar.selectAll();
+ }
+ }
+ Action {
+ shortcut: "Ctrl+W"
+ onTriggered: {
+ if (tabs.count == 1)
+ browserWindow.close()
+ else
+ tabs.remove(tabs.currentIndex)
+ }
+ }
+
+ Action {
+ shortcut: "Escape"
+ onTriggered: {
+ if (browserWindow.isFullScreen)
+ browserWindow.visibility = browserWindow.previousVisibility
+ }
+ }
+
+ ToolBar {
+ id: navigationBar
+
+ style: ToolBarStyle {
+ padding {
+ left: 8
+ right: 8
+ top: 3
+ bottom: 3
+ }
+ background: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 50
+ color: "#46a1da"
+ }
+ }
+
+ RowLayout {
+ anchors.fill: parent;
+
+ UIButton {
+ id: backButton
+ source: "qrc:///previous.png"
+ color: uiColor
+ onClicked: tabs.currentWebView.goBack()
+ enabled: tabs.currentWebView && tabs.currentWebView.canGoBack
+ }
+
+ UIButton {
+ id: forwardButton
+ source: "qrc:///next.png"
+ color: uiColor
+ onClicked: tabs.currentWebView.goForward()
+ enabled: tabs.currentWebView && tabs.currentWebView.canGoForward
+ }
+
+
+ TextField {
+ id: addressBar
+ UIButton {
+ id: reloadButton
+ source: currentWebView && currentWebView.loading ? "qrc:///stop.png" : "qrc:///refresh.png"
+ anchors {
+ rightMargin: 10
+ right: parent.right
+ verticalCenter: addressBar.verticalCenter;
+ }
+ onClicked: currentWebView.loading ? currentWebView.stop() : currentWebView.reload()
+ }
+ style: TextFieldStyle {
+ textColor: "black"
+ font.family: "Helvetica"
+ font.pointSize: 18
+ selectionColor: uiSelectionColor
+ background: Rectangle {
+ implicitWidth: 200
+ implicitHeight: 40
+ border.color: uiBorderColor
+ border.width: 1
+ }
+ padding {
+ right: 10
+ left: 10
+ }
+ }
+ focus: true
+ Layout.fillWidth: true
+ text: tabs.currentWebView ? tabs.currentWebView.url : "about:blank"
+ onAccepted: {
+ console.log("WEBVIEW "+ tabs.get(tabs.currentIndex).item.webView.url)
+ tabs.get(tabs.currentIndex).item.webView.url = engine.fromUserInput(text)
+ tabs.viewState = "page"
+ }
+ }
+
+ UIButton {
+ id: pageViewButton
+ source: "qrc:///tabs.png"
+ color: uiColor
+ onClicked: {
+ if (tabs.viewState == "list") {
+ tabs.viewState = "page"
+ } else {
+ tabs.viewState = "list"
+ }
+ console.log("BUTTON " + tabs.viewState)
+ }
+ }
+
+ ToolButton {
+ id: settingsMenuButton
+ menu: Menu {
+ MenuItem {
+ id: loadImages
+ text: "Autoload images"
+ checkable: true
+ checked: true
+ }
+ MenuItem {
+ id: javaScriptEnabled
+ text: "JavaScript On"
+ checkable: true
+ checked: true
+ }
+ MenuItem {
+ id: errorPageEnabled
+ text: "ErrorPage On"
+ checkable: true
+ checked: true
+ }
+ MenuItem {
+ id: pluginsEnabled
+ text: "Plugins On"
+ checkable: true
+ checked: true
+ }
+ MenuItem {
+ id: offTheRecordEnabled
+ text: "Off The Record"
+ checkable: true
+ checked: currentWebView ? currentWebView.profile.offTheRecord : true
+ onToggled: currentWebView.profile = (checked ? otrProfile : engine.rootWindow.defaultProfile());
+ }
+ MenuItem {
+ id: httpDiskCacheEnabled
+ text: "HTTP Disk Cache"
+ checkable: currentWebView && !currentWebView.profile.offTheRecord
+ checked: currentWebView && (currentWebView.profile.httpCacheType == WebEngineProfile.DiskHttpCache)
+ onToggled: currentWebView.profile.httpCacheType = checked ? WebEngineProfile.DiskHttpCache : WebEngineProfile.MemoryHttpCache;
+ }
+ }
+ }
+ }
+ ProgressBar {
+ id: progressBar
+ height: 3
+ anchors {
+ left: parent.left
+ top: parent.bottom
+ right: parent.right
+ leftMargin: -parent.leftMargin
+ rightMargin: -parent.rightMargin
+ }
+ style: ProgressBarStyle {
+ background: Item {}
+ }
+ z: -2;
+ minimumValue: 0
+ maximumValue: 100
+ value: (currentWebView && currentWebView.loadProgress < 100) ? currentWebView.loadProgress : 0
+ }
+ }
+
+ PageView {
+ id: tabs
+
+ itemWidth: browserWindow.width / 2
+ itemHeight: browserWindow.height / 2
+
+ anchors {
+ top: navigationBar.bottom
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+
+ Component.onCompleted: {
+ var tab = createEmptyTab()
+ tab.webView.url = "about:blank"
+ }
+ }
+
+ QtObject{
+ id:acceptedCertificates
+
+ property var acceptedUrls : []
+
+ function shouldAutoAccept(certificateError){
+ var domain = engine.domainFromString(certificateError.url)
+ return acceptedUrls.indexOf(domain) >= 0
+ }
+ }
+
+ MessageDialog {
+ id: sslDialog
+
+ property var certErrors: []
+ icon: StandardIcon.Warning
+ standardButtons: StandardButton.No | StandardButton.Yes
+ title: "Server's certificate not trusted"
+ text: "Do you wish to continue?"
+ detailedText: "If you wish so, you may continue with an unverified certificate. " +
+ "Accepting an unverified certificate means " +
+ "you may not be connected with the host you tried to connect to.\n" +
+ "Do you wish to override the security check and continue?"
+ onYes: {
+ var cert = certErrors.shift()
+ var domain = engine.domainFromString(cert.url)
+ acceptedCertificates.acceptedUrls.push(domain)
+ cert.ignoreCertificateError()
+ presentError()
+ }
+ onNo: reject()
+ onRejected: reject()
+
+ function reject(){
+ certErrors.shift().rejectCertificate()
+ presentError()
+ }
+ function enqueue(error){
+ certErrors.push(error)
+ presentError()
+ }
+ function presentError(){
+ visible = certErrors.length > 0
+ }
+ }
+}
diff --git a/src/qml/FeaturePermissionBar.qml b/src/qml/FeaturePermissionBar.qml
new file mode 100644
index 0000000..908c85b
--- /dev/null
+++ b/src/qml/FeaturePermissionBar.qml
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBrowser project.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPLv2 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtQuick.Controls 1.0
+import QtWebEngine 1.1
+import QtQuick.Layouts 1.0
+
+Rectangle {
+ property var requestedFeature;
+ property url securityOrigin;
+ property WebEngineView view;
+
+ id: permissionBar
+ visible: false
+ height: acceptButton.height + 4
+
+ onRequestedFeatureChanged: {
+ message.text = securityOrigin + " wants to access " + message.textForFeature(requestedFeature);
+ }
+
+
+ RowLayout {
+ anchors {
+ fill: permissionBar
+ leftMargin: 5
+ rightMargin: 5
+ }
+ Label {
+ id: message
+ Layout.fillWidth: true
+
+ function textForFeature(feature) {
+ if (feature === WebEngineView.MediaAudioCapture)
+ return "your microphone"
+ if (feature === WebEngineView.MediaVideoCapture)
+ return "your camera"
+ if (feature === WebEngineView.MediaAudioVideoCapture)
+ return "your camera and microphone"
+ if (feature === WebEngineView.Geolocation)
+ return "your position"
+ }
+ }
+
+ Button {
+ id: acceptButton
+ text: "Accept"
+ Layout.alignment: Qt.AlignRight
+ onClicked: {
+ view.grantFeaturePermission(securityOrigin, requestedFeature, true);
+ permissionBar.visible = false;
+ }
+ }
+
+ Button {
+ text: "Deny"
+ Layout.alignment: Qt.AlignRight
+ onClicked: {
+ view.grantFeaturePermission(securityOrigin, requestedFeature, false);
+ permissionBar.visible = false
+ }
+ }
+ }
+}
diff --git a/src/qml/LoadIndicator.qml b/src/qml/LoadIndicator.qml
new file mode 100644
index 0000000..43af34c
--- /dev/null
+++ b/src/qml/LoadIndicator.qml
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+ id: container
+
+ property bool running: false
+ property string imageSource: "busy.png"
+
+ visible: running
+
+ Image {
+ id: image
+ anchors.centerIn: parent
+ source: container.imageSource
+ ParallelAnimation {
+ running: container.running
+ NumberAnimation { target: image; property: "rotation"; from: 0; to: 360; loops: Animation.Infinite; duration: 1200 }
+ }
+ }
+}
diff --git a/src/qml/MockTouchPoint.qml b/src/qml/MockTouchPoint.qml
new file mode 100644
index 0000000..4784ff7
--- /dev/null
+++ b/src/qml/MockTouchPoint.qml
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBrowser project.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPLv2 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ id: mockTouchPoint
+
+ property bool pressed: false
+ property int pointId: 0
+
+ Image {
+ source: "qrc:///touchpoint.png"
+ x: -(width / 2)
+ y: -(height / 2)
+ height: parent.height
+ width: parent.width
+ opacity: parent.pressed ? 0.6 : 0.0
+
+ Behavior on opacity {
+ NumberAnimation { duration: 200 }
+ }
+
+ Text {
+ text: mockTouchPoint.pointId
+ anchors.centerIn: parent
+ }
+ }
+}
diff --git a/src/qml/PageView.qml b/src/qml/PageView.qml
new file mode 100644
index 0000000..ec1b205
--- /dev/null
+++ b/src/qml/PageView.qml
@@ -0,0 +1,395 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBrowser project.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPLv2 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtWebEngine 1.2
+import QtWebEngine.experimental 1.0
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import QtQuick.Layouts 1.3
+import QtGraphicalEffects 1.0
+
+Rectangle {
+ id: root
+ property int animationDuration: 200
+ property int tabDisplacement: 100
+ property int itemWidth: root.width / 2
+ property int itemHeight: root.height / 2
+
+ property alias currentIndex: pathView.currentIndex
+ property alias count: pathView.count
+ property Item currentWebView: {
+ console.log(count)
+ return get(currentIndex) ? get(currentIndex).item.webView : null
+ }
+
+
+ property string viewState: "page"
+
+ Component {
+ id: tabComponent
+ Item {
+ id: tabItem
+ property alias webView: webEngineView
+ property alias title: webEngineView.title
+
+ property var image: QtObject {
+ property string imageUrl: "qrc:///busy.png"
+ property string url: "about:blank"
+ }
+
+ function grabImage() {
+ if (image.url == webEngineView.url)
+ return
+
+ image.url = webEngineView.url
+ webEngineView.grabToImage(function(result) {
+ image.imageUrl = result.url;
+ console.log("grabImage("+result.url+")")
+ });
+ }
+
+ visible: opacity != 0.0
+
+ Behavior on opacity {
+ NumberAnimation { duration: animationDuration / 4; easing.type: Easing.InQuad}
+ }
+
+ anchors.fill: parent
+
+ Action {
+ shortcut: "Ctrl+F"
+ onTriggered: {
+ findBar.visible = !findBar.visible
+ if (findBar.visible) {
+ findTextField.forceActiveFocus()
+ }
+ }
+ }
+ FeaturePermissionBar {
+ id: permBar
+ view: webEngineView
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ }
+ z: 3
+ }
+
+ WebEngineView {
+ id: webEngineView
+
+ //visible: parent.visible
+
+ anchors {
+ fill: parent
+ top: permBar.bottom
+ }
+
+// settings.autoLoadImages: appSettings.autoLoadImages
+// settings.javascriptEnabled: appSettings.javaScriptEnabled
+// settings.errorPageEnabled: appSettings.errorPageEnabled
+// settings.pluginsEnabled: appSettings.pluginsEnabled
+
+ onCertificateError: {
+ if (!acceptedCertificates.shouldAutoAccept(error)){
+ error.defer()
+ sslDialog.enqueue(error)
+ } else{
+ error.ignoreCertificateError()
+ }
+ }
+
+ onNewViewRequested: {
+ if (!request.userInitiated)
+ print("Warning: Blocked a popup window.")
+ else if (request.destination == WebEngineView.NewViewInTab) {
+ var tab = tabs.createEmptyTab()
+ tabs.currentIndex = tabs.count - 1
+ console.log("newWindow inTab")
+ request.openIn(tab.webView)
+ } else if (request.destination == WebEngineView.NewViewInBackgroundTab) {
+ var tab = tabs.createEmptyTab()
+ console.log("newWindow inBackTab")
+ request.openIn(tab.webView)
+ } else if (request.destination == WebEngineView.NewViewInDialog) {
+ var dialog = engine.rootWindow.newDialog()
+ request.openIn(dialog.currentWebView)
+ } else {
+ console.log("newWindow BLA")
+ var window = engine.rootWindow.newWindow()
+ request.openIn(window.currentWebView)
+ }
+ }
+
+ onFullScreenRequested: {
+ if (request.toggleOn) {
+ webEngineView.state = "FullScreen"
+ browserWindow.previousVisibility = browserWindow.visibility
+ browserWindow.showFullScreen()
+ } else {
+ webEngineView.state = ""
+ browserWindow.visibility = browserWindow.previousVisibility
+ }
+ request.accept()
+ }
+
+ onFeaturePermissionRequested: {
+ permBar.securityOrigin = securityOrigin;
+ permBar.requestedFeature = feature;
+ permBar.visible = true;
+ }
+
+ onLoadingChanged: {
+ if (!loading && visible)
+ grabImage()
+ }
+ }
+
+ Rectangle {
+ id: findBar
+ anchors.top: webEngineView.top
+ anchors.right: webEngineView.right
+ width: 240
+ height: 35
+ border.color: "lightgray"
+ border.width: 1
+ radius: 5
+ visible: false
+ color: "gray"
+
+ RowLayout {
+ anchors.centerIn: findBar
+ TextField {
+ id: findTextField
+ onAccepted: {
+ webEngineView.findText(text)
+ }
+ }
+ ToolButton {
+ id: findBackwardButton
+ iconSource: "qrc:///previous.png"
+ onClicked: webEngineView.findText(findTextField.text, WebEngineView.FindBackward)
+ }
+ ToolButton {
+ id: findForwardButton
+ iconSource: "qrc:///next.png"
+ onClicked: webEngineView.findText(findTextField.text)
+ }
+ ToolButton {
+ id: findCancelButton
+ iconSource: "qrc:///stop.png"
+ onClicked: findBar.visible = false
+ }
+ }
+ }
+ }
+ }
+
+ ListModel {
+ id: listModel
+ }
+
+ function createEmptyTab() {
+ var tab = add(tabComponent)
+ //timer.running = true
+ return tab
+ }
+
+ function add(component) {
+ var element = {"item": null }
+ element.item = component.createObject(root, { "anchors.top": root.top, "anchors.left": root.left, "width": root.width, "height": root.height })
+
+ if (element.item == null) {
+ // Error Handling
+ console.log("Error creating object");
+ return
+ }
+
+ element.item.webView.profile = engine.rootWindow.defaultProfile()
+ element.item.webView.url = "about:blank"
+ element.index = listModel.count
+ listModel.append(element)
+
+ console.log("index " + element.index)
+ return element.item
+ }
+
+ function remove(index) {
+ listModel.remove(index)
+ }
+
+ function get(index) {
+ return listModel.get(index)
+ }
+
+ Component {
+ id: delegate
+
+ Rectangle {
+ id: wrapper
+
+ state: index == pathView.currentIndex ? root.viewState : "list"
+
+ property real visibility: 1.0
+
+ visible: visibility != 0.0
+
+ onStateChanged: {
+ console.log("WRAPPER " + state)
+ }
+
+ states: [
+ State {
+ name: "page"
+ PropertyChanges { target: wrapper; x: 0; y: 0; width: root.width; height: root.height; color: "grey"; visibility: 0.0 }
+ PropertyChanges { target: pathView; interactive: false }
+ PropertyChanges { target: item; opacity: 1.0; visible: visibility < 0.1; z: 5 }
+ },
+ State {
+ name: "list"
+ PropertyChanges { target: wrapper; width: itemWidth; height: itemHeight; color: "white"; visibility: 1.0 }
+ PropertyChanges { target: pathView; interactive: true }
+ PropertyChanges { target: item; opacity: 0.0; visible: opacity != 0.0 }
+ }
+ ]
+
+ transitions: Transition {
+ SequentialAnimation {
+ ParallelAnimation {
+ ColorAnimation { property: "color"; duration: animationDuration }
+ PropertyAnimation { properties: "x,y"; duration: animationDuration }
+ PropertyAnimation { properties: "width,height"; duration: animationDuration; easing.type: Easing.OutQuad }
+ PropertyAnimation { properties: "visibility"; duration: animationDuration; easing.type: Easing.OutQuad }
+ }
+ }
+ }
+
+ width: itemWidth; height: itemHeight
+ scale: PathView.isCurrentItem ? 1 : 0.5
+ z: PathView.isCurrentItem ? 1 : 0
+
+ Column {
+ anchors.fill: wrapper
+ Rectangle {
+ color: "#83bfe5"
+
+
+ Image {
+ smooth: false
+ source: item.image.imageUrl
+ anchors.fill: parent
+ }
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: wrapper.width
+ height: wrapper.height
+ }
+ Text {
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: item.title
+ font.pointSize: 12
+ color: wrapper.PathView.isCurrentItem ? uiBorderColor : uiColor
+ }
+ }
+ MouseArea {
+ anchors.fill: wrapper
+ onClicked: {
+ var pos = pathView.mapFromItem(wrapper, mouse.x, mouse.y)
+ var index = pathView.indexAt(pos.x, pos.y)
+
+ if (index < 0)
+ return
+
+ if (index === pathView.currentIndex) {
+ if (root.viewState == "list")
+ root.viewState = "page"
+ return
+ }
+
+ if (pathView.currentIndex == 0 && index === pathView.count - 1) {
+ pathView.decrementCurrentIndex()
+ return
+ }
+
+ if (pathView.currentIndex == pathView.count - 1 && index == 0) {
+ pathView.incrementCurrentIndex()
+ return
+ }
+
+ if (pathView.currentIndex > index) {
+ pathView.decrementCurrentIndex()
+ return
+ }
+
+ if (pathView.currentIndex < index) {
+ pathView.incrementCurrentIndex()
+ return
+ }
+ }
+ }
+ Behavior on scale {
+ NumberAnimation { duration: animationDuration }
+ }
+ }
+ }
+
+ PathView {
+ id: pathView
+ pathItemCount: 3
+ anchors.fill: parent
+ model: listModel
+ delegate: delegate
+ preferredHighlightBegin: 0.5
+ preferredHighlightEnd: 0.5
+
+ path: Path {
+ id: path
+ startX: 0 - itemWidth / 4; startY: root.height / 2 - tabDisplacement
+ //PathLine { x: root.width; y: root.height / 2 }
+ PathCurve { x: root.width / 4; y: root.height / 2 - tabDisplacement / 2 }
+ PathCurve { x: root.width / 2; y: root.height / 2 }
+ PathCurve { x: 3/4 * root.width; y: root.height / 2 - tabDisplacement / 2 }
+ PathCurve { x: root.width + itemWidth / 4; y: root.height / 2 - tabDisplacement }
+ }
+
+ focus: true
+ Keys.onLeftPressed: decrementCurrentIndex()
+ Keys.onRightPressed: incrementCurrentIndex()
+ }
+}
diff --git a/src/qml/assets/UIButton.qml b/src/qml/assets/UIButton.qml
new file mode 100644
index 0000000..a415f3f
--- /dev/null
+++ b/src/qml/assets/UIButton.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.5
+import QtQuick.Controls 1.5
+import QtQuick.Controls.Styles 1.4
+import QtQuick.Layouts 1.3
+
+
+ToolButton {
+ id: root
+
+ property string source;
+ property string color: "white";
+ style: ButtonStyle {
+ background: Rectangle {
+ implicitWidth: 30
+ implicitHeight: 30
+ radius: 4
+ color: root.color
+ Image {
+ source: root.source
+ anchors.fill: parent
+ }
+ }
+ }
+}
+
diff --git a/src/qml/assets/icons/busy.png b/src/qml/assets/icons/busy.png
new file mode 100644
index 0000000..3b60d26
--- /dev/null
+++ b/src/qml/assets/icons/busy.png
Binary files differ
diff --git a/src/qml/assets/icons/home.png b/src/qml/assets/icons/home.png
new file mode 100644
index 0000000..92d01d1
--- /dev/null
+++ b/src/qml/assets/icons/home.png
Binary files differ
diff --git a/src/qml/assets/icons/next.png b/src/qml/assets/icons/next.png
new file mode 100644
index 0000000..757ca9e
--- /dev/null
+++ b/src/qml/assets/icons/next.png
Binary files differ
diff --git a/src/qml/assets/icons/previous.png b/src/qml/assets/icons/previous.png
new file mode 100644
index 0000000..4eadb93
--- /dev/null
+++ b/src/qml/assets/icons/previous.png
Binary files differ
diff --git a/src/qml/assets/icons/refresh.png b/src/qml/assets/icons/refresh.png
new file mode 100644
index 0000000..585152f
--- /dev/null
+++ b/src/qml/assets/icons/refresh.png
Binary files differ
diff --git a/src/qml/assets/icons/stop.png b/src/qml/assets/icons/stop.png
new file mode 100644
index 0000000..26d585b
--- /dev/null
+++ b/src/qml/assets/icons/stop.png
Binary files differ
diff --git a/src/qml/assets/icons/tabs.png b/src/qml/assets/icons/tabs.png
new file mode 100644
index 0000000..492d83d
--- /dev/null
+++ b/src/qml/assets/icons/tabs.png
Binary files differ
diff --git a/src/qml/assets/icons/touchpoint.png b/src/qml/assets/icons/touchpoint.png
new file mode 100644
index 0000000..7649ee9
--- /dev/null
+++ b/src/qml/assets/icons/touchpoint.png
Binary files differ
diff --git a/src/resources.qrc b/src/resources.qrc
new file mode 100644
index 0000000..bb43044
--- /dev/null
+++ b/src/resources.qrc
@@ -0,0 +1,21 @@
+<RCC>
+ <qresource prefix="/">
+ <file>qml/BrowserDialog.qml</file>
+ <file>qml/BrowserWindow.qml</file>
+ <file>qml/FeaturePermissionBar.qml</file>
+ <file>qml/LoadIndicator.qml</file>
+ <file>qml/MockTouchPoint.qml</file>
+ <file>qml/PageView.qml</file>
+
+ <file>qml/assets/UIButton.qml</file>
+
+ <file alias="busy.png">qml/assets/icons/busy.png</file>
+ <file alias="home.png">qml/assets/icons/home.png</file>
+ <file alias="tabs.png">qml/assets/icons/tabs.png</file>
+ <file alias="next.png">qml/assets/icons/next.png</file>
+ <file alias="previous.png">qml/assets/icons/previous.png</file>
+ <file alias="refresh.png">qml/assets/icons/refresh.png</file>
+ <file alias="stop.png">qml/assets/icons/stop.png</file>
+ <file alias="touchpoint.png">qml/assets/icons/touchpoint.png</file>
+ </qresource>
+</RCC>
diff --git a/src/src.pro b/src/src.pro
new file mode 100644
index 0000000..b9596b0
--- /dev/null
+++ b/src/src.pro
@@ -0,0 +1,30 @@
+TARGET = qtbrowser
+
+DESTDIR = ../
+CONFIG += c++11
+CONFIG -= app_bundle
+
+SOURCES = main.cpp \
+ touchmockingapplication.cpp \
+ browserwindow.cpp
+
+HEADERS = utils.h \
+ touchmockingapplication.h \
+ browserwindow.h
+
+OTHER_FILES = \
+ qml/assets/UIButton.qml \
+ qml/ApplicationRoot.qml \
+ qml/BrowserDialog.qml \
+ qml/BrowserWindow.qml \
+ qml/FeaturePermissionBar.qml \
+ qml/LoadIndicator.qml \
+ qml/MockTouchPoint.qml \
+ qml/PageView.qml \
+
+QT += qml quick webengine
+QT_PRIVATE += quick-private gui-private core-private
+
+RESOURCES += resources.qrc
+
+!cross_compile: DEFINES += HOST_BUILD
diff --git a/src/touchmockingapplication.cpp b/src/touchmockingapplication.cpp
new file mode 100644
index 0000000..2685152
--- /dev/null
+++ b/src/touchmockingapplication.cpp
@@ -0,0 +1,278 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBrowser project.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPLv2 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "touchmockingapplication.h"
+
+#include <qpa/qwindowsysteminterface.h>
+
+#include <QRegExp>
+#include <QEvent>
+#include <QMouseEvent>
+#include <QTouchEvent>
+
+static inline QRectF touchRectForPosition(QPointF centerPoint)
+{
+ QRectF touchRect(0, 0, 40, 40);
+ touchRect.moveCenter(centerPoint);
+ return touchRect;
+}
+
+static inline bool isTouchEvent(const QEvent* event)
+{
+ switch (event->type()) {
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool isMouseEvent(const QEvent* event)
+{
+ switch (event->type()) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseMove:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ return true;
+ default:
+ return false;
+ }
+}
+
+TouchMockingApplication::TouchMockingApplication(int& argc, char** argv)
+ : QGuiApplication(argc, argv)
+ , m_realTouchEventReceived(false)
+ , m_pendingFakeTouchEventCount(0)
+ , m_holdingControl(false)
+{
+}
+
+bool TouchMockingApplication::notify(QObject* target, QEvent* event)
+{
+ // We try to be smart, if we received real touch event, we are probably on a device
+ // with touch screen, and we should not have touch mocking.
+
+ if (!event->spontaneous() || m_realTouchEventReceived)
+ return QGuiApplication::notify(target, event);
+
+ if (isTouchEvent(event)) {
+ if (m_pendingFakeTouchEventCount)
+ --m_pendingFakeTouchEventCount;
+ else
+ m_realTouchEventReceived = true;
+ return QGuiApplication::notify(target, event);
+ }
+
+ BrowserWindow* window = qobject_cast<BrowserWindow*>(target);
+ if (!window)
+ return QGuiApplication::notify(target, event);
+
+ m_holdingControl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
+
+ if (event->type() == QEvent::KeyRelease && static_cast<QKeyEvent*>(event)->key() == Qt::Key_Control) {
+ foreach (int id, m_heldTouchPoints)
+ if (m_touchPoints.contains(id) && !QGuiApplication::mouseButtons().testFlag(Qt::MouseButton(id))) {
+ m_touchPoints[id].setState(Qt::TouchPointReleased);
+ m_heldTouchPoints.remove(id);
+ } else
+ m_touchPoints[id].setState(Qt::TouchPointStationary);
+
+ sendTouchEvent(window, m_heldTouchPoints.isEmpty() ? QEvent::TouchEnd : QEvent::TouchUpdate, static_cast<QKeyEvent*>(event)->timestamp());
+ }
+
+ if (event->type() == QEvent::Wheel) {
+ // Eat wheel events.
+ event->accept();
+ return true;
+ }
+
+ if (isMouseEvent(event)) {
+ const QMouseEvent* const mouseEvent = static_cast<QMouseEvent*>(event);
+
+ QTouchEvent::TouchPoint touchPoint;
+ touchPoint.setPressure(1);
+
+ QEvent::Type touchType = QEvent::None;
+
+ switch (mouseEvent->type()) {
+ case QEvent::MouseButtonPress:
+ touchPoint.setId(mouseEvent->button());
+ if (m_touchPoints.contains(touchPoint.id())) {
+ touchPoint.setState(Qt::TouchPointMoved);
+ touchType = QEvent::TouchUpdate;
+ } else {
+ touchPoint.setState(Qt::TouchPointPressed);
+ // Check if more buttons are held down than just the event triggering one.
+ if (mouseEvent->buttons() > mouseEvent->button())
+ touchType = QEvent::TouchUpdate;
+ else
+ touchType = QEvent::TouchBegin;
+ }
+ break;
+ case QEvent::MouseMove:
+ if (!mouseEvent->buttons()) {
+ // We have to swallow the event instead of propagating it,
+ // since we avoid sending the mouse release events and if the
+ // Flickable is the mouse grabber it would receive the event
+ // and would move the content.
+ event->accept();
+ return true;
+ }
+ touchType = QEvent::TouchUpdate;
+ touchPoint.setId(mouseEvent->buttons());
+ touchPoint.setState(Qt::TouchPointMoved);
+ break;
+ case QEvent::MouseButtonRelease:
+ // Check if any buttons are still held down after this event.
+ if (mouseEvent->buttons())
+ touchType = QEvent::TouchUpdate;
+ else
+ touchType = QEvent::TouchEnd;
+ touchPoint.setId(mouseEvent->button());
+ touchPoint.setState(Qt::TouchPointReleased);
+ break;
+ case QEvent::MouseButtonDblClick:
+ // Eat double-clicks, their accompanying press event is all we need.
+ event->accept();
+ return true;
+ default:
+ Q_ASSERT_X(false, "multi-touch mocking", "unhandled event type");
+ }
+
+ // A move can have resulted in multiple buttons, so we need check them individually.
+ if (touchPoint.id() & Qt::LeftButton)
+ updateTouchPoint(mouseEvent, touchPoint, Qt::LeftButton);
+ if (touchPoint.id() & Qt::MidButton)
+ updateTouchPoint(mouseEvent, touchPoint, Qt::MidButton);
+ if (touchPoint.id() & Qt::RightButton)
+ updateTouchPoint(mouseEvent, touchPoint, Qt::RightButton);
+
+ if (m_holdingControl && touchPoint.state() == Qt::TouchPointReleased) {
+ // We avoid sending the release event because the Flickable is
+ // listening to mouse events and would start a bounce-back
+ // animation if it received a mouse release.
+ event->accept();
+ return true;
+ }
+
+ // Update states for all other touch-points
+ for (QHash<int, QTouchEvent::TouchPoint>::iterator it = m_touchPoints.begin(), end = m_touchPoints.end(); it != end; ++it) {
+ if (!(it.value().id() & touchPoint.id()))
+ it.value().setState(Qt::TouchPointStationary);
+ }
+
+ Q_ASSERT(touchType != QEvent::None);
+
+ if (!sendTouchEvent(window, touchType, mouseEvent->timestamp()))
+ return QGuiApplication::notify(target, event);
+
+ event->accept();
+ return true;
+ }
+
+ return QGuiApplication::notify(target, event);
+}
+
+void TouchMockingApplication::updateTouchPoint(const QMouseEvent* mouseEvent, QTouchEvent::TouchPoint touchPoint, Qt::MouseButton mouseButton)
+{
+ // Ignore inserting additional touch points if Ctrl isn't held because it produces
+ // inconsistent touch events and results in assers in the gesture recognizers.
+ if (!m_holdingControl && m_touchPoints.size() && !m_touchPoints.contains(mouseButton))
+ return;
+
+ if (m_holdingControl && touchPoint.state() == Qt::TouchPointReleased) {
+ m_heldTouchPoints.insert(mouseButton);
+ return;
+ }
+
+ // Gesture recognition uses the screen position for the initial threshold
+ // but since the canvas translates touch events we actually need to pass
+ // the screen position as the scene position to deliver the appropriate
+ // coordinates to the target.
+ touchPoint.setRect(touchRectForPosition(mouseEvent->localPos()));
+ touchPoint.setSceneRect(touchRectForPosition(mouseEvent->screenPos()));
+
+ if (touchPoint.state() == Qt::TouchPointPressed)
+ touchPoint.setStartScenePos(mouseEvent->screenPos());
+ else {
+ const QTouchEvent::TouchPoint& oldTouchPoint = m_touchPoints[mouseButton];
+ touchPoint.setStartScenePos(oldTouchPoint.startScenePos());
+ touchPoint.setLastPos(oldTouchPoint.pos());
+ touchPoint.setLastScenePos(oldTouchPoint.scenePos());
+ }
+
+ // Update current touch-point.
+ touchPoint.setId(mouseButton);
+ m_touchPoints.insert(mouseButton, touchPoint);
+}
+
+bool TouchMockingApplication::sendTouchEvent(BrowserWindow* window, QEvent::Type type, ulong timestamp)
+{
+ static QTouchDevice* device = 0;
+ if (!device) {
+ device = new QTouchDevice;
+ device->setType(QTouchDevice::TouchScreen);
+ QWindowSystemInterface::registerTouchDevice(device);
+ }
+
+ m_pendingFakeTouchEventCount++;
+
+ const QList<QTouchEvent::TouchPoint>& currentTouchPoints = m_touchPoints.values();
+ Qt::TouchPointStates touchPointStates = 0;
+ foreach (const QTouchEvent::TouchPoint& touchPoint, currentTouchPoints)
+ touchPointStates |= touchPoint.state();
+
+ QTouchEvent event(type, device, Qt::NoModifier, touchPointStates, currentTouchPoints);
+ event.setTimestamp(timestamp);
+ event.setAccepted(false);
+
+ QGuiApplication::notify(window, &event);
+
+ window->updateVisualMockTouchPoints(m_holdingControl ? currentTouchPoints : QList<QTouchEvent::TouchPoint>());
+
+ // Get rid of touch-points that are no longer valid
+ foreach (const QTouchEvent::TouchPoint& touchPoint, currentTouchPoints) {
+ if (touchPoint.state() == Qt::TouchPointReleased)
+ m_touchPoints.remove(touchPoint.id());
+ }
+
+ return event.isAccepted();
+}
+
diff --git a/src/touchmockingapplication.h b/src/touchmockingapplication.h
new file mode 100644
index 0000000..6350436
--- /dev/null
+++ b/src/touchmockingapplication.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBrowser project.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPLv2 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TOUCHMOCKINGAPPLICATION_H
+#define TOUCHMOCKINGAPPLICATION_H
+
+#include "browserwindow.h"
+
+#include <QHash>
+#include <QGuiApplication>
+#include <QTouchEvent>
+#include <QUrl>
+
+class TouchMockingApplication : public QGuiApplication
+{
+ Q_OBJECT
+
+public:
+ TouchMockingApplication(int &argc, char** argv);
+
+ virtual bool notify(QObject*, QEvent*) override;
+
+private:
+ void updateTouchPoint(const QMouseEvent*, QTouchEvent::TouchPoint, Qt::MouseButton);
+ bool sendTouchEvent(BrowserWindow *, QEvent::Type, ulong timestamp);
+
+private:
+ bool m_realTouchEventReceived;
+ int m_pendingFakeTouchEventCount;
+
+ QPointF m_lastPos;
+ QPointF m_lastScreenPos;
+ QPointF m_startScreenPos;
+
+ QHash<int, QTouchEvent::TouchPoint> m_touchPoints;
+ QSet<int> m_heldTouchPoints;
+
+ bool m_holdingControl;
+};
+
+#endif // TOUCHMOCKINGAPPLICATION_H
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..8ea1aea
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBrowser project.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPLv2 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <QtCore/QFileInfo>
+#include <QtCore/QUrl>
+
+class Utils : public QObject {
+ Q_OBJECT
+
+ Q_PROPERTY(QObject * rootWindow READ rootWindow FINAL CONSTANT)
+public:
+ Utils(QObject *parent)
+ : QObject(parent)
+ {
+ }
+ QObject *rootWindow()
+ {
+ return parent();
+ }
+
+ Q_INVOKABLE static QUrl fromUserInput(const QString& userInput);
+ Q_INVOKABLE static QString domainFromString(const QString& urlString);
+};
+
+inline QUrl Utils::fromUserInput(const QString& userInput)
+{
+ QFileInfo fileInfo(userInput);
+ if (fileInfo.exists())
+ return QUrl::fromLocalFile(fileInfo.absoluteFilePath());
+ return QUrl::fromUserInput(userInput);
+}
+
+inline QString Utils::domainFromString(const QString& urlString)
+{
+ return QUrl::fromUserInput(urlString).host();
+}
+
+#endif // UTILS_H