[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 &current_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 &current_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