Commit de240d92 authored by Marco Martin's avatar Marco Martin

base on desktop file for app name/icon

open external links in external browser
parent 6092d883
......@@ -30,7 +30,7 @@ include(KDECompilerSettings NO_POLICY_SCOPE)
################# Find dependencies #################
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Quick Test Gui Svg QuickControls2 Sql)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 Purpose I18n)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 Purpose I18n Config CoreAddons)
# Necessary to support QtWebEngine installed in a different prefix than the rest of Qt (e.g flatpak)
find_package(Qt5WebEngine REQUIRED)
......
......@@ -32,6 +32,16 @@ set(angelfish_webapp_SRCS
qt5_add_resources(WEBAPP_RESOURCES webapp-resources.qrc)
add_executable(angelfish-webapp ${angelfish_webapp_SRCS} ${RESOURCES} ${WEBAPP_RESOURCES})
target_link_libraries(angelfish-webapp Qt5::Core Qt5::Qml Qt5::Quick Qt5::Sql Qt5::Svg Qt5::WebEngine KF5::I18n)
target_link_libraries(angelfish-webapp
Qt5::Core
Qt5::Qml
Qt5::Quick
Qt5::Sql
Qt5::Svg
Qt5::WebEngine
KF5::I18n
KF5::CoreAddons
KF5::ConfigCore
KF5::ConfigGui)
install(TARGETS angelfish-webapp ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
/***************************************************************************
* *
* Copyright 2014-2015 Sebastian Kügler <sebas@kde.org> *
* *
* 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
// 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()
}
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 {
autoLoadImages: webBrowser.settings.webAutoLoadImages
javascriptEnabled: webBrowser.settings.webJavascriptEnabled
// 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: Qt.openUrlExternally(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("Download")
onTriggered: webEngineView.triggerWebAction(WebEngineView.DownloadLinkToDisk)
}
}
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) {
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);
}
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;
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 (UrlUtils.urlHost(request.requestedUrl) === UrlUtils.urlHost( BrowserManager.initialUrl)) {
url = request.requestedUrl;
} else {
Qt.openUrlExternally(request.requestedUrl);
}
}
onUrlChanged: {
if (requestedUrl !== url) {
requestedUrl = url;
}
}
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; });
if (reloadOnVisible) {
reloadOnVisible = false;
reload();
}
}
}
function stopLoading() {
loadingActive = false;
stop();
}
}
......@@ -73,7 +73,8 @@ Kirigami.ApplicationWindow {
// tabs will work correctly
property bool initialized: false
WebView {
//FIXME: WebView assumes a multi tab ui, will probably need own implementation
WebAppView {
id: webView
anchors.fill: parent
url: BrowserManager.initialUrl
......
......@@ -72,3 +72,8 @@ QString UrlUtils::urlPath(const QString &url)
{
return QUrl::fromUserInput(url).path();
}
QString UrlUtils::urlHost(const QString &url)
{
return QUrl::fromUserInput(url).host();
}
......@@ -41,6 +41,7 @@ public:
Q_INVOKABLE static QString urlScheme(const QString &url);
Q_INVOKABLE static QString urlHostPort(const QString &url);
Q_INVOKABLE static QString urlPath(const QString &url);
Q_INVOKABLE static QString urlHost(const QString &url);
};
#endif // URLUTILS_H
<RCC>
<qresource prefix="/">
<file alias="webapp.qml">contents/webapp-ui/webapp.qml</file>
<file alias="WebAppView.qml">contents/webapp-ui/WebAppView.qml</file>
</qresource>
</RCC>
......@@ -25,6 +25,8 @@
#include <KLocalizedContext>
#include <KLocalizedString>
#include <KDesktopFile>
#include <KAboutData>
#include "bookmarkshistorymodel.h"
#include "browsermanager.h"
......@@ -45,9 +47,9 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
#endif
QApplication app(argc, argv);
QCoreApplication::setOrganizationName("KDE");
QCoreApplication::setOrganizationDomain("mobile.kde.org");
QCoreApplication::setApplicationName("angelfish");
//QCoreApplication::setOrganizationName("KDE");
//QCoreApplication::setOrganizationDomain("mobile.kde.org");
//QCoreApplication::setApplicationName("angelfish");
#if QT_VERSION <= QT_VERSION_CHECK(5, 14, 0)
QtWebEngine::initialize();
......@@ -65,10 +67,27 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
engine.addImageProvider(IconImageProvider::providerId(), new IconImageProvider(&engine));
// initial url command line parameter
QString initialUrl;
if (!parser.positionalArguments().isEmpty())
initialUrl = QUrl::fromUserInput(parser.positionalArguments().first()).toString();
if (parser.positionalArguments().isEmpty()) {
return 0;
}
const QString fileName = parser.positionalArguments().first();
KDesktopFile desktopFile(fileName);
if (desktopFile.readUrl().isEmpty()) {
return 0;
}
const QString initialUrl = QUrl::fromUserInput(desktopFile.readUrl()).toString();
const QString appName = desktopFile.readName().toLower().replace(QLatin1Char(' '), QLatin1Char('-')) + QLatin1String("-angelfish-webapp");
KAboutData aboutData(appName.toLower(), desktopFile.readName(),
QStringLiteral("0.1"),
i18n("Angelfish Web App runtime"),
KAboutLicense::GPL,
i18n("Copyright 2020 Angelfish developers"));
QApplication::setWindowIcon(QIcon::fromTheme(desktopFile.readIcon()));
aboutData.addAuthor(i18n("Marco Martin"), QString(), "mart@kde.org");
KAboutData::setApplicationData(aboutData);
// Exported types
qmlRegisterType<BookmarksHistoryModel>("org.kde.mobile.angelfish", 1, 0, "BookmarksHistoryModel");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment