[education/rkward] rkward/windows: Start fixing navigation
Thomas Friedrichsmeier
null at kde.org
Thu Apr 9 20:44:24 BST 2026
Git commit 464fca86656f228a3b86bc397f18cd575d3b8eab by Thomas Friedrichsmeier.
Committed on 09/04/2026 at 19:44.
Pushed by tfry into branch 'master'.
Start fixing navigation
M +1 -0 rkward/windows/rkhtmlviewer.h
M +3 -0 rkward/windows/rkhtmlwindow.cpp
M +44 -7 rkward/windows/rkqwebview.cpp
M +5 -0 rkward/windows/rkqwebview.h
M +14 -0 rkward/windows/rkqwebview.qml
https://invent.kde.org/education/rkward/-/commit/464fca86656f228a3b86bc397f18cd575d3b8eab
diff --git a/rkward/windows/rkhtmlviewer.h b/rkward/windows/rkhtmlviewer.h
index 7bf96a2a2..ea4270fc8 100644
--- a/rkward/windows/rkhtmlviewer.h
+++ b/rkward/windows/rkhtmlviewer.h
@@ -53,6 +53,7 @@ class RKHTMLViewer : public QObject {
void pageInternalNavigation(const QUrl &new_url);
void selectionChanged(bool has_selection);
void loadFinished();
+ void navigationRequest(const QUrl ¤t_real_url, const QUrl &requested_url, bool is_new_window);
protected:
RKHTMLViewer(QObject *parent);
diff --git a/rkward/windows/rkhtmlwindow.cpp b/rkward/windows/rkhtmlwindow.cpp
index e1e0cf4e7..3e88a2a33 100644
--- a/rkward/windows/rkhtmlwindow.cpp
+++ b/rkward/windows/rkhtmlwindow.cpp
@@ -105,6 +105,9 @@ RKHTMLWindow::RKHTMLWindow(QWidget *parent, WindowMode mode) : RKMDIWindow(paren
// We have to connect this in order to allow browsing.
connect(page, &RKHTMLViewer::pageInternalNavigation, this, &RKHTMLWindow::internalNavigation);
+ connect(page, &RKHTMLViewer::navigationRequest, this, [this](const QUrl ¤t_real_url, const QUrl &requested_url, bool is_new_window) {
+ openURL(requested_url);
+ });
connect(view, &QWidget::customContextMenuRequested, this, &RKHTMLWindow::makeContextMenu);
diff --git a/rkward/windows/rkqwebview.cpp b/rkward/windows/rkqwebview.cpp
index df9462428..2c2f80ee8 100644
--- a/rkward/windows/rkqwebview.cpp
+++ b/rkward/windows/rkqwebview.cpp
@@ -15,8 +15,28 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include "../debug.h"
-RKQWebView::RKQWebView(RKHTMLWindow *parent) : RKHTMLViewer(parent) {
- RK_TRACE(APP);
+RKQWebView::RKQWebView(RKHTMLWindow *parent) : RKHTMLViewer(parent), view(nullptr) {
+ RK_TRACE(APP);
+}
+
+void RKQWebView::onPageLoad(const QUrl &_url, const QString &error, int status) {
+ RK_DEBUG(APP, DL_WARNING, "loading %s %d", qPrintable(_url.toString()), status);
+ /* TODO: we may need to inject a script similar for handling target=new
+ and page internal navigation?
+ RKHTMLViewer::runJS(u"document.addEventListener('click', e => {"
+ " const origin = e.target.closest('a');"
+ " if (origin) alert(origin.href);"
+ "})\n"_s); */
+ if (status == 0) {
+ bool new_window = false;
+ Q_EMIT navigationRequest(webView()->property("acceptedUrl").toUrl(), _url, new_window);
+ // we may be redirected via the above signal
+ if (_url != url()) {
+ RK_DEBUG(APP, DL_DEBUG, "redirecting %s to %s", qPrintable(_url.toString()), qPrintable(url().toString()));
+ }
+ } else if (status == 2) {
+ Q_EMIT loadFinished();
+ }
}
QWidget *RKQWebView::createWidget(QWidget *parent) {
@@ -24,24 +44,40 @@ QWidget *RKQWebView::createWidget(QWidget *parent) {
view = new QQuickWidget(parent);
view->setSource(QUrl(QStringLiteral("qrc:/qml/rkqwebview.qml")));
view->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ connect(webView(), SIGNAL(loaded(const QUrl &, const QString &, int)), this, SLOT(onPageLoad(const QUrl &, const QString &, int)));
return view;
}
+QObject *RKQWebView::webView() const {
+ // TODO: we have some premature calls to this function. Find and fix them
+ if (!view) {
+ RK_ASSERT(view);
+ return nullptr;
+ }
+ if (view->status() != QQuickWidget::Ready) {
+ RK_ASSERT(view->status() == QQuickWidget::Ready);
+ return nullptr;
+ }
+ auto wv = view->rootObject()->findChild<QObject *>("webView");
+ RK_ASSERT(wv);
+ return wv;
+}
+
QUrl RKQWebView::url() const {
RK_TRACE(APP);
- return QUrl();
+ auto wv = webView();
+ return wv ? wv->property("url").toUrl() : QUrl();
}
void RKQWebView::load(const QUrl &url) {
RK_TRACE(APP);
- auto wv = view->rootObject()->findChild<QObject *>("webView");
- RK_ASSERT(wv);
- if (!wv) return;
- wv->setProperty("url", url);
+ webView()->setProperty("acceptedUrl", url);
+ webView()->setProperty("url", url);
}
void RKQWebView::print() {
RK_TRACE(APP);
+ RKHTMLViewer::runJS(u"window.print()"_s);
}
void RKQWebView::installPersistentJS(const QString &script, const QString &id) {
@@ -50,6 +86,7 @@ void RKQWebView::installPersistentJS(const QString &script, const QString &id) {
void RKQWebView::runJS(const QString &script, std::function<void(const QVariant &)> callback) {
RK_TRACE(APP);
+ QMetaObject::invokeMethod(webView(), "runJavaScript", Q_ARG(QString, script), Q_ARG(QJSValue, QJSValue()));
}
void RKQWebView::setHTML(const QString &html, const QUrl &url) {
diff --git a/rkward/windows/rkqwebview.h b/rkward/windows/rkqwebview.h
index 078bed461..e18780ac7 100644
--- a/rkward/windows/rkqwebview.h
+++ b/rkward/windows/rkqwebview.h
@@ -13,8 +13,10 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include "rkhtmlviewer.h"
class QQuickWidget;
+class QQuickItem;
class RKQWebView : public RKHTMLViewer {
+ Q_OBJECT
public:
QWidget *createWidget(QWidget *parent) override;
QUrl url() const override;
@@ -38,6 +40,9 @@ class RKQWebView : public RKHTMLViewer {
friend class RKHTMLViewer;
RKQWebView(RKHTMLWindow *parent);
QPointer<QQuickWidget> view;
+ private Q_SLOTS:
+ void onPageLoad(const QUrl &url, const QString &error, int status);
+ QObject *webView() const;
};
#endif
diff --git a/rkward/windows/rkqwebview.qml b/rkward/windows/rkqwebview.qml
index 761a1ea7e..1059a24ab 100644
--- a/rkward/windows/rkqwebview.qml
+++ b/rkward/windows/rkqwebview.qml
@@ -13,10 +13,24 @@ Item {
visible: true
WebView {
+ signal loaded(url murl, string error, int status)
id: webView
objectName: "webView"
visible: true
width: parent.width
height: parent.height
+ property string acceptedUrl: ""
+
+ onLoadingChanged: request => {
+ // give the frontend a chance to intervene, e.g. rewriting rkward:// url, opening in new window,
+ // denying external urls, etc.
+ loaded(request.url, request.error, request.status);
+ if (request.status == WebView.LoadStartedStatus) {
+ if (request.url != acceptedUrl) {
+ //console.log("Navigation request to " + request.url + "denied");
+ url = acceptedUrl;
+ }
+ }
+ }
}
}
More information about the rkward-tracker
mailing list