Markers are back

master
Jacopo De Simoi 4 weeks ago
parent a844c57fb8
commit 84ea861d93
  1. 78
      www-client/falkon/0001-Load-markers-and-test.patch
  2. 48
      www-client/falkon/0001-Zoom-in-out-with-Plus-and-Minus.patch
  3. 132
      www-client/falkon/0002-Add-method-to-cleanup-link-markers.patch
  4. 24
      www-client/falkon/0002-Zoom-in-with-too.patch
  5. 32
      www-client/falkon/0003-Back-Forward-with-c-v.patch
  6. 27
      www-client/falkon/0003-fix-compilation-squash.patch
  7. 64
      www-client/falkon/0004-Use-standard-wording-for-show-hide-in-Qt.patch
  8. 72
      www-client/falkon/0005-Clear-link-markers-when-needed.patch
  9. 306
      www-client/falkon/0006-add-the-forgotten-js-file.patch
  10. 105
      www-client/falkon/0007-Put-in-a-working-implementation.patch
  11. 103
      www-client/falkon/0008-Move-marker-handling-to-keypress.patch
  12. 26
      www-client/falkon/0009-Reset-the-label-when-the-markers-disappear.patch
  13. 47
      www-client/falkon/0010-Lunarize-the-markers.patch
  14. 35
      www-client/falkon/0011-Do-not-propagate-keypresses-to-view-when-markers-are.patch
  15. 30
      www-client/falkon/0012-Prevent-triggering-markers-with-modifiers.patch
  16. 25
      www-client/falkon/0013-Colemak-markers.patch
  17. 25
      www-client/falkon/0014-fix-marker-shortcut.patch
  18. 33
      www-client/falkon/0015-Use-sans-serif-family.patch
  19. 45
      www-client/falkon/0016-Add-shortcuts-for-zooming-and-back-forward.patch

@ -0,0 +1,78 @@
From 9f5d59938267f897628b911acb42c15c2896007f Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Tue, 17 Jun 2025 11:12:02 -0400
Subject: [PATCH 01/16] Load markers and test
---
src/lib/data/html.qrc | 1 +
src/lib/webengine/webpage.cpp | 6 ++++++
src/lib/webengine/webpage.h | 2 ++
src/lib/webengine/webview.cpp | 3 +++
4 files changed, 12 insertions(+)
diff --git a/src/lib/data/html.qrc b/src/lib/data/html.qrc
index 34f890a2e..ecd44d677 100644
--- a/src/lib/data/html.qrc
+++ b/src/lib/data/html.qrc
@@ -11,6 +11,7 @@
<file>html/config.html</file>
<file>html/restore.html</file>
<file>html/restore.user.js</file>
+ <file>html/marker.js</file>
<file>html/tabcrash.html</file>
<file>html/close.svg</file>
<file>html/configure.svg</file>
diff --git a/src/lib/webengine/webpage.cpp b/src/lib/webengine/webpage.cpp
index 0794b092b..92c6300b8 100644
--- a/src/lib/webengine/webpage.cpp
+++ b/src/lib/webengine/webpage.cpp
@@ -305,6 +305,8 @@ void WebPage::finished()
// AutoFill
m_autoFillUsernames = mApp->autoFill()->completePage(this, url());
+ // inject marker.js
+ runJavaScript(QzTools::readAllFileContents(QSL(":html/marker.js")));
}
void WebPage::watchedFileChanged(const QString &file)
@@ -314,6 +316,10 @@ void WebPage::watchedFileChanged(const QString &file)
}
}
+void WebPage::getLinkMarkers() {
+ runJavaScript(QLatin1String("Marker.generateMarker('a, input, button, [class*=\"btn\"], [aria-haspopup], [role=\"button\"], textarea, select, summary, [class=\"gap\"], [ng-click]')"));
+}
+
void WebPage::handleUnknownProtocol(const QUrl &url)
{
const QString protocol = url.scheme();
diff --git a/src/lib/webengine/webpage.h b/src/lib/webengine/webpage.h
index 3f9bf151a..7db16f2cb 100644
--- a/src/lib/webengine/webpage.h
+++ b/src/lib/webengine/webpage.h
@@ -78,6 +78,8 @@ public:
static void addSupportedScheme(const QString &scheme);
static void removeSupportedScheme(const QString &scheme);
+ void getLinkMarkers();
+
Q_SIGNALS:
void privacyChanged(bool status);
void printRequested();
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index 4871e8b3f..56e3461be 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -1234,6 +1234,9 @@ void WebView::_keyReleaseEvent(QKeyEvent *event)
event->accept();
}
break;
+ case Qt::Key_F:
+ page()->getLinkMarkers();
+ event->accept();
default:
break;
--
2.53.0

@ -1,48 +0,0 @@
From 05288a5f93c8d7123a807884ba2c003b7bcd9da2 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <jacopods@gmail.com>
Date: Tue, 5 Feb 2019 21:49:30 -0500
Subject: [PATCH 01/19] Zoom in/out with Plus and Minus
---
src/lib/webengine/webview.cpp | 16 ++--------------
1 file changed, 2 insertions(+), 14 deletions(-)
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index 0d68da7bb..ff0b5fb68 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -1159,29 +1159,17 @@ void WebView::_keyPressEvent(QKeyEvent *event)
switch (event->key()) {
case Qt::Key_ZoomIn:
+ case Qt::Key_Plus:
zoomIn();
event->accept();
break;
case Qt::Key_ZoomOut:
+ case Qt::Key_Minus:
zoomOut();
event->accept();
break;
- case Qt::Key_Plus:
- if (event->modifiers() & Qt::ControlModifier) {
- zoomIn();
- event->accept();
- }
- break;
-
- case Qt::Key_Minus:
- if (event->modifiers() & Qt::ControlModifier) {
- zoomOut();
- event->accept();
- }
- break;
-
case Qt::Key_0:
if (event->modifiers() & Qt::ControlModifier) {
zoomReset();
--
2.41.0

@ -0,0 +1,132 @@
From 639d16e98ca076d1b29b42ac5824eaffec16331a Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Tue, 22 Jul 2025 16:06:35 +0200
Subject: [PATCH 02/16] Add method to cleanup link markers
---
src/lib/webengine/webpage.cpp | 5 ++++
src/lib/webengine/webpage.h | 3 +-
src/lib/webengine/webview.cpp | 55 ++++++++++++++++++++---------------
src/lib/webengine/webview.h | 1 +
4 files changed, 39 insertions(+), 25 deletions(-)
diff --git a/src/lib/webengine/webpage.cpp b/src/lib/webengine/webpage.cpp
index 92c6300b8..e14c51ba4 100644
--- a/src/lib/webengine/webpage.cpp
+++ b/src/lib/webengine/webpage.cpp
@@ -320,6 +320,11 @@ void WebPage::getLinkMarkers() {
runJavaScript(QLatin1String("Marker.generateMarker('a, input, button, [class*=\"btn\"], [aria-haspopup], [role=\"button\"], textarea, select, summary, [class=\"gap\"], [ng-click]')"));
}
+void WebPage::clearLinkMarkers() {
+ runJavaScript("document.querySelector('.eaf-marker-container').remove();");
+ runJavaScript("document.querySelector('.eaf-style').remove();");
+}
+
void WebPage::handleUnknownProtocol(const QUrl &url)
{
const QString protocol = url.scheme();
diff --git a/src/lib/webengine/webpage.h b/src/lib/webengine/webpage.h
index 7db16f2cb..3cfdbb38e 100644
--- a/src/lib/webengine/webpage.h
+++ b/src/lib/webengine/webpage.h
@@ -79,8 +79,9 @@ public:
static void removeSupportedScheme(const QString &scheme);
void getLinkMarkers();
+ void clearLinkMarkers();
-Q_SIGNALS:
+ Q_SIGNALS:
void privacyChanged(bool status);
void printRequested();
void navigationRequestAccepted(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame);
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index 56e3461be..01f402a75 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -54,30 +54,30 @@
bool WebView::s_forceContextMenuOnMouseRelease = false;
-WebView::WebView(QWidget* parent)
- : QWebEngineView(parent)
- , m_progress(100)
- , m_backgroundActivity(false)
- , m_page(nullptr)
- , m_firstLoad(false)
-{
- connect(this, &QWebEngineView::loadStarted, this, &WebView::slotLoadStarted);
- connect(this, &QWebEngineView::loadProgress, this, &WebView::slotLoadProgress);
- connect(this, &QWebEngineView::loadFinished, this, &WebView::slotLoadFinished);
- connect(this, &QWebEngineView::iconChanged, this, &WebView::slotIconChanged);
- connect(this, &QWebEngineView::urlChanged, this, &WebView::slotUrlChanged);
- connect(this, &QWebEngineView::titleChanged, this, &WebView::slotTitleChanged);
- connect(this, &QWebEngineView::printFinished, this, &WebView::slotPrintFinished);
-
- m_currentZoomLevel = zoomLevels().indexOf(100);
-
- setAcceptDrops(true);
- installEventFilter(this);
- if (parentWidget()) {
- parentWidget()->installEventFilter(this);
- }
-
- WebInspector::registerView(this);
+WebView::WebView(QWidget *parent)
+ : QWebEngineView(parent), m_progress(100), m_backgroundActivity(false),
+ m_page(nullptr), m_firstLoad(false), m_followLink(false) {
+ connect(this, &QWebEngineView::loadStarted, this, &WebView::slotLoadStarted);
+ connect(this, &QWebEngineView::loadProgress, this,
+ &WebView::slotLoadProgress);
+ connect(this, &QWebEngineView::loadFinished, this,
+ &WebView::slotLoadFinished);
+ connect(this, &QWebEngineView::iconChanged, this, &WebView::slotIconChanged);
+ connect(this, &QWebEngineView::urlChanged, this, &WebView::slotUrlChanged);
+ connect(this, &QWebEngineView::titleChanged, this,
+ &WebView::slotTitleChanged);
+ connect(this, &QWebEngineView::printFinished, this,
+ &WebView::slotPrintFinished);
+
+ m_currentZoomLevel = zoomLevels().indexOf(100);
+
+ setAcceptDrops(true);
+ installEventFilter(this);
+ if (parentWidget()) {
+ parentWidget()->installEventFilter(this);
+ }
+
+ WebInspector::registerView(this);
}
WebView::~WebView()
@@ -1235,7 +1235,14 @@ void WebView::_keyReleaseEvent(QKeyEvent *event)
}
break;
case Qt::Key_F:
+ if (!m_followLink) {
page()->getLinkMarkers();
+ m_followLink = true;
+ } else {
+ page()->clearLinkMarkers();
+ m_followLink = false;
+ }
+
event->accept();
default:
diff --git a/src/lib/webengine/webview.h b/src/lib/webengine/webview.h
index 61e7281f8..bec16ea25 100644
--- a/src/lib/webengine/webview.h
+++ b/src/lib/webengine/webview.h
@@ -190,6 +190,7 @@ private:
WebPage* m_page;
bool m_firstLoad;
+ bool m_followLink;
QPointer<QWidget> m_rwhvqt;
WheelHelper m_wheelHelper;
--
2.53.0

@ -1,24 +0,0 @@
From 1bce476a2b27e442f54af15e94b4805addd29272 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <jacopods@gmail.com>
Date: Tue, 5 Feb 2019 21:50:51 -0500
Subject: [PATCH 02/19] Zoom in with = too
---
src/lib/webengine/webview.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index ff0b5fb68..4047e24dc 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -1160,6 +1160,7 @@ void WebView::_keyPressEvent(QKeyEvent *event)
switch (event->key()) {
case Qt::Key_ZoomIn:
case Qt::Key_Plus:
+ case Qt::Key_Equal:
zoomIn();
event->accept();
break;
--
2.41.0

@ -1,32 +0,0 @@
From d7ceeb8ec5e4774d84f33bcdb4f20fc8132bcdfb Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <jacopods@gmail.com>
Date: Tue, 5 Feb 2019 22:02:11 -0500
Subject: [PATCH 03/19] Back/Forward with c/v
---
src/lib/app/browserwindow.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/lib/app/browserwindow.cpp b/src/lib/app/browserwindow.cpp
index bf1381d73..8f432415b 100644
--- a/src/lib/app/browserwindow.cpp
+++ b/src/lib/app/browserwindow.cpp
@@ -1270,6 +1270,7 @@ void BrowserWindow::keyPressEvent(QKeyEvent* event)
switch (event->key()) {
case Qt::Key_Back:
+ case Qt::Key_C:
if (view) {
view->back();
event->accept();
@@ -1277,6 +1278,7 @@ void BrowserWindow::keyPressEvent(QKeyEvent* event)
break;
case Qt::Key_Forward:
+ case Qt::Key_V:
if (view) {
view->forward();
event->accept();
--
2.41.0

@ -0,0 +1,27 @@
From 71cde1693896f99c8cd8b958abe20195cc57fe3e Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Tue, 22 Jul 2025 16:11:20 +0200
Subject: [PATCH 03/16] fix compilation --- squash
---
src/lib/webengine/webpage.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/lib/webengine/webpage.cpp b/src/lib/webengine/webpage.cpp
index e14c51ba4..92796df43 100644
--- a/src/lib/webengine/webpage.cpp
+++ b/src/lib/webengine/webpage.cpp
@@ -321,8 +321,8 @@ void WebPage::getLinkMarkers() {
}
void WebPage::clearLinkMarkers() {
- runJavaScript("document.querySelector('.eaf-marker-container').remove();");
- runJavaScript("document.querySelector('.eaf-style').remove();");
+ runJavaScript(QLatin1String("document.querySelector('.eaf-marker-container').remove();"));
+ runJavaScript(QLatin1String("document.querySelector('.eaf-style').remove();"));
}
void WebPage::handleUnknownProtocol(const QUrl &url)
--
2.53.0

@ -0,0 +1,64 @@
From c6ee23bb6cdb9fc84f19017ea55d1c000c55d0f0 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Wed, 17 Dec 2025 14:44:51 -0500
Subject: [PATCH 04/16] Use standard wording for show/hide in Qt
---
src/lib/webengine/webpage.cpp | 4 ++--
src/lib/webengine/webpage.h | 4 ++--
src/lib/webengine/webview.cpp | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/lib/webengine/webpage.cpp b/src/lib/webengine/webpage.cpp
index 92796df43..c269f9666 100644
--- a/src/lib/webengine/webpage.cpp
+++ b/src/lib/webengine/webpage.cpp
@@ -316,11 +316,11 @@ void WebPage::watchedFileChanged(const QString &file)
}
}
-void WebPage::getLinkMarkers() {
+void WebPage::showLinkMarkers() {
runJavaScript(QLatin1String("Marker.generateMarker('a, input, button, [class*=\"btn\"], [aria-haspopup], [role=\"button\"], textarea, select, summary, [class=\"gap\"], [ng-click]')"));
}
-void WebPage::clearLinkMarkers() {
+void WebPage::hideLinkMarkers() {
runJavaScript(QLatin1String("document.querySelector('.eaf-marker-container').remove();"));
runJavaScript(QLatin1String("document.querySelector('.eaf-style').remove();"));
}
diff --git a/src/lib/webengine/webpage.h b/src/lib/webengine/webpage.h
index 3cfdbb38e..2b459bfd4 100644
--- a/src/lib/webengine/webpage.h
+++ b/src/lib/webengine/webpage.h
@@ -78,8 +78,8 @@ public:
static void addSupportedScheme(const QString &scheme);
static void removeSupportedScheme(const QString &scheme);
- void getLinkMarkers();
- void clearLinkMarkers();
+ void showLinkMarkers();
+ void hideLinkMarkers();
Q_SIGNALS:
void privacyChanged(bool status);
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index 01f402a75..c91ac1e7a 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -1236,10 +1236,10 @@ void WebView::_keyReleaseEvent(QKeyEvent *event)
break;
case Qt::Key_F:
if (!m_followLink) {
- page()->getLinkMarkers();
+ page()->showLinkMarkers();
m_followLink = true;
} else {
- page()->clearLinkMarkers();
+ page()->hideLinkMarkers();
m_followLink = false;
}
--
2.53.0

@ -0,0 +1,72 @@
From 6af474b8550a75a0f8e4e84782875107e6522746 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Fri, 19 Dec 2025 09:13:05 -0500
Subject: [PATCH 05/16] Clear link markers when needed
---
src/lib/webengine/webpage.cpp | 3 +++
src/lib/webengine/webpage.h | 4 +++-
src/lib/webengine/webview.cpp | 2 ++
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/lib/webengine/webpage.cpp b/src/lib/webengine/webpage.cpp
index c269f9666..4a953ae23 100644
--- a/src/lib/webengine/webpage.cpp
+++ b/src/lib/webengine/webpage.cpp
@@ -105,6 +105,8 @@ WebPage::WebPage(QObject* parent)
connect(this, &QWebEnginePage::proxyAuthenticationRequired, this, [this](const QUrl &, QAuthenticator *auth, const QString &proxyHost) {
mApp->networkManager()->proxyAuthentication(proxyHost, auth, view());
});
+ connect(this, &QWebEnginePage::scrollPositionChanged, this, &WebPage::hideLinkMarkers);
+ connect(this, &QWebEnginePage::contentsSizeChanged, this, &WebPage::hideLinkMarkers);
// Workaround QWebEnginePage not scrolling to anchors when opened in background tab
m_contentsResizedConnection = connect(this, &QWebEnginePage::contentsSizeChanged, this, [this]() {
@@ -323,6 +325,7 @@ void WebPage::showLinkMarkers() {
void WebPage::hideLinkMarkers() {
runJavaScript(QLatin1String("document.querySelector('.eaf-marker-container').remove();"));
runJavaScript(QLatin1String("document.querySelector('.eaf-style').remove();"));
+ Q_EMIT linkMarkersCleared();
}
void WebPage::handleUnknownProtocol(const QUrl &url)
diff --git a/src/lib/webengine/webpage.h b/src/lib/webengine/webpage.h
index 2b459bfd4..ad8739801 100644
--- a/src/lib/webengine/webpage.h
+++ b/src/lib/webengine/webpage.h
@@ -79,16 +79,18 @@ public:
static void removeSupportedScheme(const QString &scheme);
void showLinkMarkers();
- void hideLinkMarkers();
Q_SIGNALS:
void privacyChanged(bool status);
void printRequested();
void navigationRequestAccepted(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame);
+ void linkMarkersCleared();
protected Q_SLOTS:
void progress(int prog);
void finished();
+ void hideLinkMarkers();
+
private Q_SLOTS:
void urlChanged(const QUrl &url);
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index c91ac1e7a..c6e52dfa5 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -161,6 +161,8 @@ void WebView::setPage(WebPage *page)
connect(m_page, &WebPage::privacyChanged, this, &WebView::privacyChanged);
connect(m_page, &WebPage::printRequested, this, &WebView::printPage);
+ connect(m_page, &WebPage::linkMarkersCleared,
+ [=]() {m_followLink = false;});
// Set default zoom level
zoomReset();
--
2.53.0

@ -0,0 +1,306 @@
From 02be1f34051e681d35cf4e7f0d04b2d841a12380 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Fri, 19 Dec 2025 09:32:02 -0500
Subject: [PATCH 06/16] add the forgotten js file
---
src/lib/data/html/marker.js | 287 ++++++++++++++++++++++++++++++++++++
1 file changed, 287 insertions(+)
create mode 100644 src/lib/data/html/marker.js
diff --git a/src/lib/data/html/marker.js b/src/lib/data/html/marker.js
new file mode 100644
index 000000000..d04075029
--- /dev/null
+++ b/src/lib/data/html/marker.js
@@ -0,0 +1,287 @@
+(function(_) {
+ let self;
+ _.Marker = self = {};
+
+
+ function getVisibleElements(filter) {
+ let all = Array.from(document.documentElement.getElementsByTagName("*"));
+ let visibleElements = [];
+ for (let i = 0; i < all.length; i++) {
+ let e = all[i];
+ // include elements in a shadowRoot.
+ if (e.shadowRoot) {
+ let cc = e.shadowRoot.querySelectorAll('*');
+ for (let j = 0; j < cc.length; j++) {
+ all.push(cc[j]);
+ }
+ }
+ let rect = e.getBoundingClientRect();
+ if ( (rect.top <= window.innerHeight) && (rect.bottom >= 0)
+ && (rect.left <= window.innerWidth) && (rect.right >= 0)
+ && rect.height > 0
+ && getComputedStyle(e).visibility !== 'hidden'
+ ) {
+ filter(e, visibleElements);
+ }
+ }
+ return visibleElements;
+ }
+
+ function moveCursorToEnd(el) {
+ if (typeof el.selectionStart == "number") {
+ el.selectionStart = el.selectionEnd = el.value.length;
+ } else if (typeof el.createTextRange != "undefined") {
+ el.focus();
+ let range = el.createTextRange();
+ range.collapse(false);
+ range.select();
+ }
+ }
+
+ function cssSelector(el) {
+ let path = [], parent;
+ while (parent = el.parentNode) {
+ path.unshift(`${el.tagName}:nth-child(${[].indexOf.call(parent.children, el)+1})`);
+ el = parent;
+ }
+ return `${path.join(' > ')}`.toLowerCase();
+ }
+
+ function getCoords(node){
+ if (node.getBoundingClientRect){
+ let rect = node.getBoundingClientRect();
+ return [ rect.top, rect.left, rect.right, rect.bottom, cssSelector(node) ];
+ }
+
+ return getCoords(node.parentNode); // TextNode not define getBoundingClientRect
+ }
+
+ function isElementOnScreen(rect){
+ let clientHeight = document.documentElement.clientHeight;
+ let clientWidth = document.documentElement.clientWidth;
+ return (rect[0] >= 0 && rect[0] <= clientHeight &&
+ rect[1] >= 0 && rect[1] <= clientWidth &&
+ rect[2] != 0 && rect[3] != 0);
+ }
+
+ function isElementOnTop(element, rect){
+ let topElement = document.elementFromPoint((rect[1] + rect[2])/2, (rect[0] + rect[3])/2);
+ return topElement != undefined && (element.isSameNode(topElement) || element.contains(topElement) || topElement.contains(element));
+ }
+
+ function hasCopy(validRects, rect){
+ for(let i = 0; i < validRects.length; i++) {
+ let each = validRects[i];
+ if(each[0] === rect[0] && each[1] === rect[1]){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function addElementToRects(validRects, elements){
+ let rect;
+ for(let i = 0; i < elements.length; i++) {
+ rect = getCoords(elements[i]);
+ if(!hasCopy(validRects, rect) &&
+ isElementOnScreen(rect) &&
+ isElementOnTop(elements[i], rect)){
+ validRects.push(rect);
+ }
+ }
+ }
+
+ function cAdd1(keyCounter, index, maxDigit){
+ if(keyCounter[index] + 1 == maxDigit){
+ keyCounter[index] = 0;
+ cAdd1(keyCounter, index + 1, maxDigit);
+ } else {
+ keyCounter[index]++;
+ }
+ }
+
+ function generateKeys(markerContainer) {
+ let lettersString = "%1";
+ let letters = lettersString.split("");
+ let nodeNum = markerContainer.children.length;
+ let keyLen = nodeNum == 1 ? 1 : Math.ceil(Math.log(nodeNum)/Math.log(letters.length));
+ let keyCounter = [];
+ for(let i = 0; i < keyLen; i++) keyCounter[i] = 0;
+ for(let l = 0; l < nodeNum; l++) {
+ let keyStr = '';
+ for(let k = 0; k < keyLen; k++) {
+ let key = letters[keyCounter[k]];
+ keyStr = key + keyStr;
+ cAdd1(keyCounter, 0, letters.length);
+ }
+ let mark = document.createElement('span');
+ mark.setAttribute('class', 'eaf-mark');
+ mark.textContent = keyStr;
+ markerContainer.children[l].appendChild(mark);
+ markerContainer.children[l].id = keyStr;
+ }
+ }
+
+
+ self.generateMarker = (selectors) => {
+
+ let style = document.createElement('style');
+ document.head.appendChild(style);
+ style.type = 'text/css';
+ style.setAttribute('class', 'eaf-style');
+ style.appendChild(document.createTextNode('\
+.eaf-mark {\
+background: none;\
+border: none;\
+bottom: auto;\
+box-shadow: none;\
+color: black !important;\
+cursor: auto;\
+display: inline;\
+float: none;\
+font-size: inherit;\
+font-variant: normal;\
+font-weight: bold;\
+height: auto;\
+left: auto;\
+letter-spacing: 0;\
+line-height: 100%;\
+margin: 0;\
+max-height: none;\
+max-width: none;\
+min-height: 0;\
+min-width: 0;\
+opacity: 1;\
+padding: 0;\
+position: static;\
+right: auto;\
+text-align: left;\
+text-decoration: none;\
+text-indent: 0;\
+text-shadow: none;\
+text-transform: none;\
+top: auto;\
+vertical-align: baseline;\
+white-space: normal;\
+width: auto;\
+z-index: 100000;\
+}'));
+ style.appendChild(document.createTextNode('\
+.eaf-marker {\
+position: fixed;\
+display: block;\
+white-space: nowrap;\
+overflow: hidden;\
+font-size: 11.5px;\
+background: linear-gradient(to bottom, #ffdd6e 0%, #deb050 100%);\
+padding-left: 3px;\
+padding-right: 3px;\
+border: 1px solid #c38a22;\
+border-radius: 3px;\
+box-shadow: 0px 3px 7px 0px rgba(0, 0, 0, 0.3);\
+z-index: 100000;\
+}'));
+
+ let validRects = [];
+ if (typeof(selectors)=="function"){
+ addElementToRects(validRects, selectors());
+ }else if (typeof(selectors) == "string"){
+ selectors = selectors.split(",");
+ selectors.forEach((s)=>addElementToRects(validRects, document.querySelectorAll(s.trim())));
+ }
+
+ let body = document.querySelector('body');
+ let markerContainer = document.createElement('div');
+ markerContainer.setAttribute('class', 'eaf-marker-container');
+ body.insertAdjacentElement('afterend', markerContainer);
+ for(let i = 0; i < validRects.length; i++) {
+ let marker = document.createElement('div');
+ marker.setAttribute('class', 'eaf-marker');
+ marker.setAttribute('style', 'left: ' + validRects[i][1] + 'px; top: ' + validRects[i][0] + 'px;');
+ marker.setAttribute('pointed-link', validRects[i][4]);
+
+ markerContainer.appendChild(marker);
+ }
+ generateKeys(markerContainer);
+ }
+
+ self.gotoMarker = (key, callback)=>{
+ let markers = document.querySelectorAll('.eaf-marker');
+ let match;
+ for(let i = 0; i < markers.length; i++) {
+ if(markers[i].id === key.toUpperCase()) {
+ match = markers[i];
+ break;
+ }
+ }
+ if (match !== undefined && callback !== undefined){
+ let selectors = match.getAttribute('pointed-link');
+ let node = document.querySelector(selectors);
+ return callback(node);
+ } else
+ return "";
+ }
+
+ // this is callback function which call by core/browser.py get_mark_link
+ self.getMarkerLink = (node) => {
+ if(node.nodeName.toLowerCase() === 'select'){
+ node.focus();
+ }else if(node.nodeName.toLowerCase() === 'input' ||
+ node.nodeName.toLowerCase() === 'textarea') {
+ if((node.getAttribute('type') === 'submit') ||
+ (node.getAttribute('type') === 'checkbox')){
+ node.click();
+ } else {
+ node.focus(); // focus
+ node.click(); // show blink cursor
+ moveCursorToEnd(node); // move cursor to the end of line after focus.
+ }
+ } else if((node.nodeName.toLowerCase() === 'button') || // normal button
+ (node.nodeName.toLowerCase() === 'summary') || // summary button
+ (node.hasAttribute('aria-haspopup')) || // menu button
+ (node.getAttribute('role') === 'button') || // role="button" buttons
+ (node.hasAttribute('ng-click')) || // ng-click buttons
+ (node.classList.contains('btn')) || // class="btn" buttons
+ (node.classList.contains('gap')) || // class="gap" links
+ (node.getAttribute('href') === '') || // special href button
+ (node.getAttribute('href') === '#')){ // special href # button
+ node.click();
+ } else if(node.nodeName.toLowerCase() === 'p'||
+ node.nodeName.toLowerCase() === 'span') { // select text section
+ window.getSelection().selectAllChildren(node);
+ } else if(node.href != undefined && node.href != '' && node.getAttribute('href') != ''){
+ return node.href;
+ } else if(node.nodeName.toLowerCase() === 'a') {
+ node.click(); // most general a tag without href
+ }
+ return "";
+ }
+
+
+ self.generateTextNodeMarker = () => {
+
+ let elements = getVisibleElements(function(e, v) {
+ let aa = e.childNodes;
+ for (let i = 0, len = aa.length; i < len; i++) {
+ if (aa[i].nodeType == Node.TEXT_NODE && aa[i].data.length > 0) {
+ v.push(e);
+ break;
+ }
+ }
+ });
+
+ elements = Array.prototype.concat.apply([], elements.map(function (e) {
+ let aa = e.childNodes;
+ let bb = [];
+ for (let i = 0, len = aa.length; i < len; i++) {
+ if (aa[i].nodeType == Node.TEXT_NODE && aa[i].data.trim().length > 1) {
+ bb.push(aa[i]);
+ }
+ }
+ return bb;
+ }));
+
+ return elements;
+ }
+
+})(window);
--
2.53.0

@ -0,0 +1,105 @@
From d061f8e6adbc3b8c3b0ef6d98099a37a3ba40054 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Mon, 23 Feb 2026 12:44:31 -0500
Subject: [PATCH 07/16] Put in a working implementation
---
src/lib/webengine/webpage.cpp | 4 ++++
src/lib/webengine/webpage.h | 3 +++
src/lib/webengine/webview.cpp | 26 ++++++++++++++++++++------
src/lib/webengine/webview.h | 1 +
4 files changed, 28 insertions(+), 6 deletions(-)
diff --git a/src/lib/webengine/webpage.cpp b/src/lib/webengine/webpage.cpp
index 4a953ae23..c186695db 100644
--- a/src/lib/webengine/webpage.cpp
+++ b/src/lib/webengine/webpage.cpp
@@ -816,3 +816,7 @@ void WebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, cons
std::cout << qPrintable(sourceID) << ":" << lineNumber << " " << qPrintable(message) << '\n';
}
+
+void WebPage::followLinkMarker(QString label) {
+ runJavaScript(QString::fromLatin1("Marker.gotoMarker('")+label+QString::fromLatin1("',Marker.getMarkerLink)"),[this](const QVariant& v) { QString url=v.toString(); if (!url.isEmpty()) load(v.toUrl());});
+}
diff --git a/src/lib/webengine/webpage.h b/src/lib/webengine/webpage.h
index ad8739801..a5fd57c23 100644
--- a/src/lib/webengine/webpage.h
+++ b/src/lib/webengine/webpage.h
@@ -79,6 +79,9 @@ public:
static void removeSupportedScheme(const QString &scheme);
void showLinkMarkers();
+ void followLinkMarker(QString);
+ void markerCallback(const QVariant &);
+
Q_SIGNALS:
void privacyChanged(bool status);
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index c6e52dfa5..de99f8831 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -1228,7 +1228,26 @@ void WebView::_keyReleaseEvent(QKeyEvent *event)
if (mApp->plugins()->processKeyRelease(Qz::ON_WebView, this, event)) {
event->accept();
}
+ if (m_followLink) {
+ // We are following links; therefore we need to process the
+ // key
+ switch (event->key()) {
+ case Qt::Key_Space:
+ // Accept current label
+ page()->followLinkMarker(m_currentLinkMarkerLabel);
+ case Qt::Key_Escape:
+ page()->hideLinkMarkers();
+ m_currentLinkMarkerLabel=QString();
+ m_followLink = false;
+ event->accept();
+ break;
+ default:
+ m_currentLinkMarkerLabel += event->text();
+ qDebug() << "process letter" << m_currentLinkMarkerLabel;
+ break;
+ }
+ } else {
switch (event->key()) {
case Qt::Key_Escape:
if (isFullScreen()) {
@@ -1237,19 +1256,14 @@ void WebView::_keyReleaseEvent(QKeyEvent *event)
}
break;
case Qt::Key_F:
- if (!m_followLink) {
page()->showLinkMarkers();
m_followLink = true;
- } else {
- page()->hideLinkMarkers();
- m_followLink = false;
- }
-
event->accept();
default:
break;
}
+ }
}
void WebView::_contextMenuEvent(QContextMenuEvent *event)
diff --git a/src/lib/webengine/webview.h b/src/lib/webengine/webview.h
index bec16ea25..5130174b7 100644
--- a/src/lib/webengine/webview.h
+++ b/src/lib/webengine/webview.h
@@ -191,6 +191,7 @@ private:
WebPage* m_page;
bool m_firstLoad;
bool m_followLink;
+ QString m_currentLinkMarkerLabel;
QPointer<QWidget> m_rwhvqt;
WheelHelper m_wheelHelper;
--
2.53.0

@ -0,0 +1,103 @@
From 23d255b888d51226239f9c1567407ecc0d8c84a3 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Thu, 30 Apr 2026 23:02:31 -0400
Subject: [PATCH 08/16] Move marker handling to keypress
This is done to avoid clashes with other key handling
routines (e.g. writing in a form)
There are still issues with (e.g.) pages entirely made of forms so
that the key would never be handled by us. This will only get clearer
by eating the dog food in real life.
---
src/lib/webengine/webview.cpp | 46 ++++++++++++++++++-----------------
1 file changed, 24 insertions(+), 22 deletions(-)
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index de99f8831..ca3d8180e 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -1178,9 +1178,29 @@ void WebView::_keyPressEvent(QKeyEvent *event)
event->accept();
return;
}
+ if (m_followLink) {
+ // We are following links; therefore we need to process the
+ // key
+ switch (event->key()) {
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ // Accept current label
+ page()->followLinkMarker(m_currentLinkMarkerLabel);
+ case Qt::Key_Escape:
+ page()->hideLinkMarkers();
+ m_currentLinkMarkerLabel = QString();
+ m_followLink = false;
+ event->accept();
+ break;
- switch (event->key()) {
- case Qt::Key_ZoomIn:
+ default:
+ m_currentLinkMarkerLabel += event->text();
+ qDebug() << "process letter" << m_currentLinkMarkerLabel;
+ break;
+ }
+ } else {
+ switch (event->key()) {
+ case Qt::Key_ZoomIn:
zoomIn();
event->accept();
break;
@@ -1221,6 +1241,7 @@ void WebView::_keyPressEvent(QKeyEvent *event)
default:
break;
}
+ }
}
void WebView::_keyReleaseEvent(QKeyEvent *event)
@@ -1228,26 +1249,7 @@ void WebView::_keyReleaseEvent(QKeyEvent *event)
if (mApp->plugins()->processKeyRelease(Qz::ON_WebView, this, event)) {
event->accept();
}
- if (m_followLink) {
- // We are following links; therefore we need to process the
- // key
- switch (event->key()) {
- case Qt::Key_Space:
- // Accept current label
- page()->followLinkMarker(m_currentLinkMarkerLabel);
- case Qt::Key_Escape:
- page()->hideLinkMarkers();
- m_currentLinkMarkerLabel=QString();
- m_followLink = false;
- event->accept();
- break;
- default:
- m_currentLinkMarkerLabel += event->text();
- qDebug() << "process letter" << m_currentLinkMarkerLabel;
- break;
- }
- } else {
switch (event->key()) {
case Qt::Key_Escape:
if (isFullScreen()) {
@@ -1258,12 +1260,12 @@ void WebView::_keyReleaseEvent(QKeyEvent *event)
case Qt::Key_F:
page()->showLinkMarkers();
m_followLink = true;
+
event->accept();
default:
break;
}
- }
}
void WebView::_contextMenuEvent(QContextMenuEvent *event)
--
2.53.0

@ -0,0 +1,26 @@
From 019199187164af4c13d62462a24ecc8eae412149 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <jacopods@gmail.com>
Date: Thu, 29 Oct 2020 11:18:11 -0400
Subject: [PATCH 09/16] Reset the label when the markers disappear
This should really be made into a slot.
---
src/lib/webengine/webview.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index ca3d8180e..ea5e356d1 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -162,7 +162,7 @@ void WebView::setPage(WebPage *page)
connect(m_page, &WebPage::privacyChanged, this, &WebView::privacyChanged);
connect(m_page, &WebPage::printRequested, this, &WebView::printPage);
connect(m_page, &WebPage::linkMarkersCleared,
- [=]() {m_followLink = false;});
+ [=]() {m_followLink = false;m_currentLinkMarkerLabel=QString(); });
// Set default zoom level
zoomReset();
--
2.53.0

@ -0,0 +1,47 @@
From cd13e15084a229685f23cff79ed5ccbabd8b9daf Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <jacopods@gmail.com>
Date: Mon, 2 Nov 2020 12:08:46 -0500
Subject: [PATCH 10/16] Lunarize the markers
This is merely an aesthetic change; will need to adjust this in the
future
---
src/lib/data/html/marker.js | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/src/lib/data/html/marker.js b/src/lib/data/html/marker.js
index d04075029..35db02098 100644
--- a/src/lib/data/html/marker.js
+++ b/src/lib/data/html/marker.js
@@ -135,7 +135,7 @@ background: none;\
border: none;\
bottom: auto;\
box-shadow: none;\
-color: black !important;\
+color: #969696!important;\
cursor: auto;\
display: inline;\
float: none;\
@@ -172,13 +172,12 @@ position: fixed;\
display: block;\
white-space: nowrap;\
overflow: hidden;\
-font-size: 11.5px;\
-background: linear-gradient(to bottom, #ffdd6e 0%, #deb050 100%);\
-padding-left: 3px;\
-padding-right: 3px;\
-border: 1px solid #c38a22;\
-border-radius: 3px;\
-box-shadow: 0px 3px 7px 0px rgba(0, 0, 0, 0.3);\
+font-size: 14px;\
+background: #F6F6F6;\
+padding-left: 6px;\
+padding-right: 6px;\
+border: 1px solid #262626;\
+border-radius: 1px;\
z-index: 100000;\
}'));
--
2.53.0

@ -0,0 +1,35 @@
From dbb54db080f741ee3d51e11b1ce8b5e92f833fb4 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Wed, 6 May 2026 16:48:54 -0400
Subject: [PATCH 11/16] Do not propagate keypresses to view when markers are
active
---
src/lib/webengine/webview.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index ea5e356d1..1929e361e 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -1196,6 +1196,7 @@ void WebView::_keyPressEvent(QKeyEvent *event)
default:
m_currentLinkMarkerLabel += event->text();
qDebug() << "process letter" << m_currentLinkMarkerLabel;
+ event->accept();
break;
}
} else {
@@ -1356,7 +1357,8 @@ bool WebView::eventFilter(QObject *obj, QEvent *event)
}
}
- if (obj == parentWidget()) {
+ if (obj == parentWidget()
+ || ((obj == m_rwhvqt) && (m_followLink))) {
switch (event->type()) {
case QEvent::KeyPress:
HANDLE_EVENT(_keyPressEvent, QKeyEvent);
--
2.53.0

@ -0,0 +1,30 @@
From 2bad488dcf8aaa99473cba510661a9134850b571 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Wed, 6 May 2026 16:53:02 -0400
Subject: [PATCH 12/16] Prevent triggering markers with modifiers
---
src/lib/webengine/webview.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index 1929e361e..aa6c9839d 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -1259,11 +1259,11 @@ void WebView::_keyReleaseEvent(QKeyEvent *event)
}
break;
case Qt::Key_F:
+ if (!event->modifiers()) {
page()->showLinkMarkers();
m_followLink = true;
-
event->accept();
-
+ }
default:
break;
}
--
2.53.0

@ -0,0 +1,25 @@
From 74f4eff8b5771c4eb328cfc25d4d5f561822baf8 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Wed, 6 May 2026 16:57:31 -0400
Subject: [PATCH 13/16] Colemak markers
---
src/lib/data/html/marker.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib/data/html/marker.js b/src/lib/data/html/marker.js
index 35db02098..7694164f1 100644
--- a/src/lib/data/html/marker.js
+++ b/src/lib/data/html/marker.js
@@ -101,7 +101,7 @@
}
function generateKeys(markerContainer) {
- let lettersString = "%1";
+ let lettersString = "ARSTDHNEIOPL";
let letters = lettersString.split("");
let nodeNum = markerContainer.children.length;
let keyLen = nodeNum == 1 ? 1 : Math.ceil(Math.log(nodeNum)/Math.log(letters.length));
--
2.53.0

@ -0,0 +1,25 @@
From 424a120837aebe78dabd6601813b58fafcfdb116 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Wed, 6 May 2026 20:17:42 -0400
Subject: [PATCH 14/16] fix marker shortcut
---
src/lib/webengine/webview.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index aa6c9839d..dcc4d2e84 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -1258,7 +1258,7 @@ void WebView::_keyReleaseEvent(QKeyEvent *event)
event->accept();
}
break;
- case Qt::Key_F:
+ case Qt::Key_T:
if (!event->modifiers()) {
page()->showLinkMarkers();
m_followLink = true;
--
2.53.0

@ -0,0 +1,33 @@
From d1ca5bcfed4e513ee5087c70a7651839a1343d20 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Wed, 6 May 2026 22:28:46 -0400
Subject: [PATCH 15/16] Use sans-serif family
---
src/lib/data/html/marker.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/lib/data/html/marker.js b/src/lib/data/html/marker.js
index 7694164f1..37934e6e8 100644
--- a/src/lib/data/html/marker.js
+++ b/src/lib/data/html/marker.js
@@ -141,7 +141,7 @@ display: inline;\
float: none;\
font-size: inherit;\
font-variant: normal;\
-font-weight: bold;\
+font-weight: regular;\
height: auto;\
left: auto;\
letter-spacing: 0;\
@@ -172,6 +172,7 @@ position: fixed;\
display: block;\
white-space: nowrap;\
overflow: hidden;\
+font-family: sans-serif;\
font-size: 14px;\
background: #F6F6F6;\
padding-left: 6px;\
--
2.53.0

@ -0,0 +1,45 @@
From ac9a424b3da19bd8117cb4ec90c7b51d5ac17c01 Mon Sep 17 00:00:00 2001
From: Jacopo De Simoi <wilderjds@protonmail.com>
Date: Wed, 6 May 2026 22:29:12 -0400
Subject: [PATCH 16/16] Add shortcuts for zooming and back/forward
---
src/lib/app/browserwindow.cpp | 2 ++
src/lib/webengine/webview.cpp | 1 +
2 files changed, 3 insertions(+)
diff --git a/src/lib/app/browserwindow.cpp b/src/lib/app/browserwindow.cpp
index e847068bf..4b13751b8 100644
--- a/src/lib/app/browserwindow.cpp
+++ b/src/lib/app/browserwindow.cpp
@@ -1288,6 +1288,7 @@ void BrowserWindow::keyPressEvent(QKeyEvent* event)
switch (event->key()) {
case Qt::Key_Back:
+ case Qt::Key_C:
if (view) {
view->back();
event->accept();
@@ -1295,6 +1296,7 @@ void BrowserWindow::keyPressEvent(QKeyEvent* event)
break;
case Qt::Key_Forward:
+ case Qt::Key_V:
if (view) {
view->forward();
event->accept();
diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp
index dcc4d2e84..c793b8bc7 100644
--- a/src/lib/webengine/webview.cpp
+++ b/src/lib/webengine/webview.cpp
@@ -1212,6 +1212,7 @@ void WebView::_keyPressEvent(QKeyEvent *event)
break;
case Qt::Key_Plus:
+ case Qt::Key_Equal:
if (event->modifiers() & Qt::ControlModifier) {
zoomIn();
event->accept();
--
2.53.0
Loading…
Cancel
Save