diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1986916..29b0661 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,12 +1,13 @@ set(angelfish_SRCS main.cpp browsermanager.cpp urlmodel.cpp + urlfilterproxymodel.cpp ) qt5_add_resources(RESOURCES resources.qrc) add_executable(angelfish ${angelfish_SRCS} ${RESOURCES}) target_link_libraries(angelfish Qt5::Core Qt5::Qml Qt5::Quick Qt5::Svg Qt5::WebEngine) install(TARGETS angelfish ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/contents/ui/Navigation.qml b/src/contents/ui/Navigation.qml index 296b633..c57b426 100644 --- a/src/contents/ui/Navigation.qml +++ b/src/contents/ui/Navigation.qml @@ -1,156 +1,159 @@ /*************************************************************************** * * * 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.0 as Kirigami import "regex-weburl.js" as RegexWebUrl Item { id: errorHandler property string searchUrl: "https://duckduckgo.com/?q=" property string errorCode: "" property bool navigationShown: errorCode != "" || webBrowser.url === "" || true + property alias textFocus: urlInput.activeFocus + property alias text: urlInput.text + property int expandedHeight: Kirigami.Units.gridUnit * 2.5 property int buttonSize: Kirigami.Units.gridUnit * 2 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 /* PlasmaComponents.ToolButton { id: backButton Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize visible: currentWebView.canGoBack iconSource: "go-previous" onClicked: currentWebView.goBack() } PlasmaComponents.ToolButton { id: forwardButton Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize visible: currentWebView.canGoForward iconSource: "go-next" onClicked: currentWebView.goForward() } PlasmaComponents.ToolButton { id: reloadButton Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize iconSource: currentWebView.loading ? "process-stop" : "view-refresh" onClicked: currentWebView.loading ? currentWebView.stop() : currentWebView.reload() } */ Controls.TextField { id: urlInput Layout.fillWidth: true text: currentWebView.url selectByMouse: true focus: false onAccepted: { if (text.match(RegexWebUrl.re_weburl)) { load(browserManager.urlFromUserInput(text)) } else { load(browserManager.urlFromUserInput(searchUrl + text)) } } } Item { Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize visible: currentWebView.loading Controls.BusyIndicator { width: buttonSize height: width anchors.centerIn: parent running: currentWebView.loading } } OptionButton { id: optionsButton property string targetState: "overview" Layout.fillWidth: false Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize iconSource: "open-menu-symbolic" onClicked: options.state = (options.state != "hidden" ? "hidden" : targetState) } } states: [ State { name: "shown" when: navigationShown PropertyChanges { target: errorHandler; x: -expandedHeight} }, State { name: "hidden" when: !navigationShown PropertyChanges { target: errorHandler; x: 0} } ] } diff --git a/src/contents/ui/UrlDelegate.qml b/src/contents/ui/UrlDelegate.qml index 180a37c..18d2eb3 100644 --- a/src/contents/ui/UrlDelegate.qml +++ b/src/contents/ui/UrlDelegate.qml @@ -1,115 +1,123 @@ /*************************************************************************** * * * 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 QtQuick.Layouts 1.0 import org.kde.kirigami 2.0 as Kirigami Controls.ItemDelegate { id: urlDelegate + property bool showRemove: true + + property string highlightText + property var regex: new RegExp(highlightText, 'i') + property string highlightedText: "$&" + height: Kirigami.Units.gridUnit * 3 width: parent.width //Rectangle { anchors.fill: parent; color: "white"; opacity: 0.5; } onClicked: { load(url) // tabs.newTab(url) // contentView.state = "hidden" } signal removed Kirigami.Icon { id: urlIcon width: height anchors { left: parent.left top: parent.top topMargin: Kirigami.Units.gridUnit / 2 bottomMargin: Kirigami.Units.gridUnit / 2 bottom: parent.bottom margins: Kirigami.Units.smallSpacing } source: icon } Image { anchors.fill: urlIcon source: preview == undefined ? "" : preview } Controls.Label { id: urlTitle - text: title + text: title ? title.replace(regex, highlightedText) : "" anchors { left: urlIcon.right leftMargin: Kirigami.Units.largeSpacing / 2 right: parent.right bottom: parent.verticalCenter top: urlIcon.top //margins: Kirigami.Units.smallSpacing } + elide: Qt.ElideRight } Controls.Label { id: urlUrl - text: url + text: url ? url.replace(regex, highlightedText) : "" opacity: 0.6 font.pointSize: theme.smallestFont.pointSize anchors { left: urlIcon.right leftMargin: Kirigami.Units.largeSpacing / 2 right: removeIcon.left top: urlIcon.verticalCenter bottom: parent.bottom //margins: Kirigami.Units.smallSpacing } + elide: Qt.ElideRight } Kirigami.Icon { id: removeIcon width: height source: "list-remove" - //visible: bookmarked + visible: parent.showRemove anchors { right: parent.right top: parent.top topMargin: Kirigami.Units.gridUnit bottomMargin: Kirigami.Units.gridUnit bottom: parent.bottom margins: Kirigami.Units.smallSpacing } MouseArea { anchors.fill: parent onClicked: urlDelegate.removed(); } } } diff --git a/src/contents/ui/webbrowser.qml b/src/contents/ui/webbrowser.qml index 4321f55..610ffd7 100644 --- a/src/contents/ui/webbrowser.qml +++ b/src/contents/ui/webbrowser.qml @@ -1,178 +1,200 @@ /*************************************************************************** * * * 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.1 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: webBrowser title: "Angelfish Webbrowser" /** 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.currentIndex < tabs.count ? tabs.currentItem : null onCurrentWebViewChanged: { print("Current WebView is now : " + tabs.currentIndex); } property int borderWidth: Math.round(Kirigami.Units.gridUnit / 18); property color borderColor: Kirigami.Theme.highlightColor; /** * Load a url in the current tab */ function load(url) { print("Loading url: " + url); currentWebView.url = url; } width: Kirigami.Units.gridUnit * 20 height: Kirigami.Units.gridUnit * 30 function addHistoryEntry() { //print("Adding history"); var request = new Object;// FIXME request.url = currentWebView.url; request.title = currentWebView.title; request.icon = currentWebView.icon; browserManager.addToHistory(request); } property bool layerShown : pageStack.layers.depth > 1 pageStack.globalToolBar.style: layerShown ? Kirigami.ApplicationHeaderStyle.Auto : Kirigami.ApplicationHeaderStyle.None pageStack.initialPage: Kirigami.Page { leftPadding: 0 rightPadding: 0 topPadding: 0 bottomPadding: 0 ListWebView { id: tabs anchors { top: navigation.bottom left: parent.left right: parent.right bottom: parent.bottom } } ErrorHandler { id: errorHandler errorString: currentWebView.errorString errorCode: currentWebView.errorCode anchors { top: navigation.bottom left: parent.left right: parent.right } + visible: !navigation.textFocus } // Container for the progress bar Item { id: progressItem height: Math.round(Kirigami.Units.gridUnit / 6) z: navigation.z + 1 anchors { top: tabs.top topMargin: -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 } } } // When clicked outside the menu, hide it MouseArea { id: optionsDismisser visible: options.state != "hidden" onClicked: options.state = "hidden" anchors.fill: parent } // The menu at the top right Options { id: options anchors { top: navigation.bottom } } Navigation { id: navigation height: Kirigami.Units.gridUnit * 3 anchors { top: parent.top left: parent.left right: parent.right } + + + onTextChanged: urlFilter.setFilterFixedString(text) + } + + ListView { + id: completion + property string searchText: navigation.text + anchors.top: navigation.bottom + anchors.horizontalCenter: navigation.horizontalCenter + width: 0.9* navigation.width + height: 0.5*parent.height + z: 10 + visible: navigation.textFocus + model: urlFilter + delegate: UrlDelegate { + showRemove: false + onClicked: tabs.forceActiveFocus() + highlightText: completion.searchText + } + clip: true } // Thin line underneath navigation Rectangle { height: webBrowser.borderWidth color: webBrowser.borderColor anchors { left: parent.left bottom: navigation.bottom right: options.left } } } Component.onCompleted: { if (!initialUrl.isEmpty) load(initialUrl) } } diff --git a/src/main.cpp b/src/main.cpp index a230317..a90547a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,51 +1,57 @@ #include #include #include #include #include #include #include #include "browsermanager.h" +#include "urlfilterproxymodel.h" Q_DECL_EXPORT int main(int argc, char *argv[]) { QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication app(argc, argv); QCoreApplication::setOrganizationName("KDE"); QCoreApplication::setOrganizationDomain("kde.org"); QCoreApplication::setApplicationName("angelfish"); // Command line parser QCommandLineParser parser; QCommandLineOption helpOption = parser.addHelpOption(); parser.addPositionalArgument("url", "An url to open", "[url]"); parser.parse(QGuiApplication::arguments()); // QML loading QQmlApplicationEngine engine; QtWebEngine::initialize(); // initial url command line parameter if (!parser.positionalArguments().isEmpty()) engine.rootContext()->setContextProperty("initialUrl", QUrl::fromUserInput(parser.positionalArguments()[0].toUtf8())); engine.load(QUrl(QStringLiteral("qrc:///webbrowser.qml"))); // Browser managger AngelFish::BrowserManager *browserManager = new AngelFish::BrowserManager(engine.rootContext()); engine.rootContext()->setContextProperty("browserManager", browserManager); + UrlFilterProxyModel *proxy = new UrlFilterProxyModel(browserManager); + proxy->setSourceModel(browserManager->history()); + proxy->setFilterCaseSensitivity(Qt::CaseInsensitive); + engine.rootContext()->setContextProperty("urlFilter", proxy); + qmlRegisterUncreatableType("org.kde.mobile.angelfish", 1, 0, "BrowserManager", ""); qmlRegisterType(); // Error handling if (engine.rootObjects().isEmpty()) { return -1; } int ret = app.exec(); return ret; } diff --git a/src/urlfilterproxymodel.cpp b/src/urlfilterproxymodel.cpp new file mode 100644 index 0000000..8c6c284 --- /dev/null +++ b/src/urlfilterproxymodel.cpp @@ -0,0 +1,40 @@ +/*************************************************************************** + * * + * Copyright 2019 Simon Schmeisser * + * * + * 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 . * + * * + ***************************************************************************/ + + +#include "urlfilterproxymodel.h" + +#include "urlmodel.h" + +using namespace AngelFish; + +UrlFilterProxyModel::UrlFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) +{ + +} + +bool UrlFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + + return (sourceModel()->data(index, UrlModel::url).toString().contains(filterRegExp()) + || sourceModel()->data(index, UrlModel::title).toString().contains(filterRegExp())); +} diff --git a/src/urlfilterproxymodel.h b/src/urlfilterproxymodel.h new file mode 100644 index 0000000..6edc0cf --- /dev/null +++ b/src/urlfilterproxymodel.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * * + * Copyright 2019 Simon Schmeisser * + * * + * 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 . * + * * + ***************************************************************************/ + +#ifndef URLFILTERPROXYMODEL_H +#define URLFILTERPROXYMODEL_H + +#include + +class UrlFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + UrlFilterProxyModel(QObject *parent = nullptr); + + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; +}; + +#endif // URLFILTERPROXYMODEL_H