From 39b15cd2b4b309300709d183fa985b5718375e81 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Mon, 23 Sep 2024 10:59:03 -0400 Subject: [PATCH 01/14] Introduce QWebEngine Javascript Infrastructure Setup for TOC Javascript/CSS Injection --- kiwix-desktop.pro | 3 ++- resources/css/toc.css | 0 resources/js.qrc | 6 ++++++ resources/js/toc.js | 0 resources/js/tocCSS.js | 5 +++++ resources/style.qrc | 1 + src/kprofile.cpp | 20 +++++++++++++++++++- 7 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 resources/css/toc.css create mode 100644 resources/js.qrc create mode 100644 resources/js/toc.js create mode 100644 resources/js/tocCSS.js diff --git a/kiwix-desktop.pro b/kiwix-desktop.pro index 2d20be53..fd860ad6 100644 --- a/kiwix-desktop.pro +++ b/kiwix-desktop.pro @@ -217,6 +217,7 @@ RESOURCES += \ resources/translations.qrc \ resources/contentmanager.qrc \ resources/settingsmanager.qrc \ - resources/style.qrc + resources/style.qrc \ + resources/js.qrc RC_ICONS = resources/icons/kiwix/app_icon.ico diff --git a/resources/css/toc.css b/resources/css/toc.css new file mode 100644 index 00000000..e69de29b diff --git a/resources/js.qrc b/resources/js.qrc new file mode 100644 index 00000000..c220bd62 --- /dev/null +++ b/resources/js.qrc @@ -0,0 +1,6 @@ + + + js/toc.js + js/tocCSS.js + + diff --git a/resources/js/toc.js b/resources/js/toc.js new file mode 100644 index 00000000..e69de29b diff --git a/resources/js/tocCSS.js b/resources/js/tocCSS.js new file mode 100644 index 00000000..d66add37 --- /dev/null +++ b/resources/js/tocCSS.js @@ -0,0 +1,5 @@ +var style = document.createElement('link'); +style.rel = "stylesheet"; +style.type = "text/css"; +style.href = "qrc:/css/toc.css"; +document.head.appendChild(style); diff --git a/resources/style.qrc b/resources/style.qrc index bb0bef4f..04fba0f5 100644 --- a/resources/style.qrc +++ b/resources/style.qrc @@ -6,5 +6,6 @@ css/confirmBox.css css/contentmanagerside.css css/choiceBox.css + css/toc.css diff --git a/src/kprofile.cpp b/src/kprofile.cpp index d1297c20..6a4275f5 100644 --- a/src/kprofile.cpp +++ b/src/kprofile.cpp @@ -4,6 +4,21 @@ #include #include #include +#include +#include + +QWebEngineScript getScript(QString filename, + QWebEngineScript::InjectionPoint point = QWebEngineScript::DocumentReady) +{ + QWebEngineScript script; + script.setInjectionPoint(point); + script.setWorldId(QWebEngineScript::UserWorld); + + QFile scriptFile(filename); + scriptFile.open(QIODevice::ReadOnly); + script.setSourceCode(scriptFile.readAll()); + return script; +} KProfile::KProfile(QObject *parent) : QWebEngineProfile(parent) @@ -16,6 +31,9 @@ KProfile::KProfile(QObject *parent) : #else // Qt 5.13 and later setUrlRequestInterceptor(new ExternalReqInterceptor(this)); #endif + + scripts()->insert(getScript(":/js/toc.js")); + scripts()->insert(getScript(":/js/tocCSS.js")); } #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) @@ -73,7 +91,7 @@ void KProfile::downloadFinished() void ExternalReqInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) { const QString reqUrl = info.requestUrl().toString(); - if (!reqUrl.startsWith("zim://")) + if (!reqUrl.startsWith("zim://") && !reqUrl.startsWith("qrc:/")) { qDebug() << "Blocked external request to URL: " << reqUrl; info.block(true); From b92451568df8fe36a3f62194f715ee31896f2648 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Mon, 23 Sep 2024 22:53:31 -0400 Subject: [PATCH 02/14] Reserve Space Left for TOC Same width as reading list. --- resources/css/toc.css | 13 +++++++++++++ resources/js/toc.js | 11 +++++++++++ ui/mainwindow.ui | 12 ++++++++++++ 3 files changed, 36 insertions(+) diff --git a/resources/css/toc.css b/resources/css/toc.css index e69de29b..c337cb91 100644 --- a/resources/css/toc.css +++ b/resources/css/toc.css @@ -0,0 +1,13 @@ +#kiwix-toc-side { + all: initial; + position: fixed; + top: 0px; + left: 0px; + width: 306px; /* ui/mainwindow.ui width and spacing */ + height: 100vh; + border-right: 1px solid #ccc; + background: white; + margin: 0px; + padding: 0px; + z-index: 10000; +} diff --git a/resources/js/toc.js b/resources/js/toc.js index e69de29b..406e4767 100644 --- a/resources/js/toc.js +++ b/resources/js/toc.js @@ -0,0 +1,11 @@ +function setupTOC() +{ + var tocSideDiv = document.createElement('div'); + tocSideDiv.id = "kiwix-toc-side"; + + document.body.prepend(tocSideDiv); +} + +document.body.style.marginLeft = "310px"; +document.body.style.maxWidth = "calc(100vw - 310px)"; +setupTOC(); diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index dd2749e3..e062685b 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -128,6 +128,18 @@ 0 + + + 300 + 0 + + + + + 300 + 16777215 + + QFrame::NoFrame From 0dd1b37003b0add5d48fe3619c6f5836cb517c1a Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Wed, 25 Sep 2024 02:29:47 -0400 Subject: [PATCH 03/14] Parses HTML and inject Table of Content --- resources/js/toc.js | 67 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/resources/js/toc.js b/resources/js/toc.js index 406e4767..ff3a85ca 100644 --- a/resources/js/toc.js +++ b/resources/js/toc.js @@ -1,7 +1,74 @@ +/** + * Construct recurseData.str as a nested list of headers by recursively going + * through all children of elem that has header tags. + * + * References: + * https://stackoverflow.com/questions/187619/is-there-a-javascript-solution-to-generating-a-table-of-contents-for-a-page + * @param elem DOM element + * @param recurseData Object with fields: { int: level, int: count, str: toc } + */ +function recurseChild(elem, recurseData) +{ + if (elem !== "undefined") + { + if(elem.nodeName.match(/^H\d+$/) && elem.textContent) + { + var headerText = elem.textContent; + var prevLevel = recurseData.level; + var level = elem.nodeName.substr(1); + var anchor = "kiwix-toc-" + recurseData.count; + recurseData.count += 1; + + /* Wrap header content with something we can reference. */ + elem.innerHTML = '' + headerText + ''; + + /* Start or end a list or item based on current and previous level */ + if (level > prevLevel) + recurseData.toc += '
    '; + else if (level < prevLevel) + recurseData.toc += '
'; + else + recurseData.toc += ''; + + recurseData.level = parseInt(level); + recurseData.toc += '
  • ' + headerText + ''; + } + + var c = elem.children; + for (var i = 0; i < c.length; i++) + recurseChild(c[i], recurseData); + } +} + +function tocHTMLStr() +{ + /* level used to track current header level. + toc used to store constructed list. + count used to uniquely identify each list item in toc. + */ + var recurseData = { level: 0, toc: "", count: 0,}; + recurseChild(document.body, recurseData); + + /* End list when non-empty */ + if (recurseData.level) + recurseData.toc += (new Array(recurseData.level + 1)).join(''); + return recurseData.toc; +} + function setupTOC() { + var toc = document.createElement('div'); + toc.id = "kiwix-toc"; + toc.innerHTML += tocHTMLStr(); + + var tocTitle = document.createElement('p'); + tocTitle.id = "kiwix-toc-title"; + tocTitle.textContent = "Table of content"; + var tocSideDiv = document.createElement('div'); tocSideDiv.id = "kiwix-toc-side"; + tocSideDiv.append(tocTitle); + tocSideDiv.append(toc); document.body.prepend(tocSideDiv); } From f7b84cd2ec7db4476ffcd669a635ddc89a89fbcf Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Wed, 25 Sep 2024 15:21:18 -0400 Subject: [PATCH 04/14] Introduce KiwixWebChannelObject.{h, cpp} Pass TOC title translation. More to come later --- kiwix-desktop.pro | 4 +++- resources/js/toc.js | 14 ++++++++++---- src/kiwixwebchannelobject.cpp | 7 +++++++ src/kiwixwebchannelobject.h | 16 ++++++++++++++++ src/kprofile.cpp | 2 ++ src/webpage.cpp | 9 +++++++++ 6 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 src/kiwixwebchannelobject.cpp create mode 100644 src/kiwixwebchannelobject.h diff --git a/kiwix-desktop.pro b/kiwix-desktop.pro index fd860ad6..cbfd68e7 100644 --- a/kiwix-desktop.pro +++ b/kiwix-desktop.pro @@ -5,7 +5,7 @@ #------------------------------------------------- QT += core gui network -QT += webenginewidgets +QT += webenginewidgets webchannel QT += printsupport # Avoid stripping incompatible files, due to false identification as executables, on WSL @@ -89,6 +89,7 @@ SOURCES += \ src/fullscreenwindow.cpp \ src/fullscreennotification.cpp \ src/zimview.cpp \ + src/kiwixwebchannelobject.cpp \ HEADERS += \ src/choiceitem.h \ @@ -138,6 +139,7 @@ HEADERS += \ src/menuproxystyle.h \ src/zimview.h \ src/portutils.h \ + src/kiwixwebchannelobject.h \ FORMS += \ src/choiceitem.ui \ diff --git a/resources/js/toc.js b/resources/js/toc.js index ff3a85ca..a9870fae 100644 --- a/resources/js/toc.js +++ b/resources/js/toc.js @@ -63,7 +63,6 @@ function setupTOC() var tocTitle = document.createElement('p'); tocTitle.id = "kiwix-toc-title"; - tocTitle.textContent = "Table of content"; var tocSideDiv = document.createElement('div'); tocSideDiv.id = "kiwix-toc-side"; @@ -73,6 +72,13 @@ function setupTOC() document.body.prepend(tocSideDiv); } -document.body.style.marginLeft = "310px"; -document.body.style.maxWidth = "calc(100vw - 310px)"; -setupTOC(); +new QWebChannel(qt.webChannelTransport, function(channel) { + + var kiwixObj = channel.objects.kiwixChannelObj + document.body.style.marginLeft = "310px"; + document.body.style.maxWidth = "calc(100vw - 310px)"; + setupTOC(); + + document.getElementById("kiwix-toc-title").textContent = kiwixObj.tocTitle; +}); + diff --git a/src/kiwixwebchannelobject.cpp b/src/kiwixwebchannelobject.cpp new file mode 100644 index 00000000..bb48935d --- /dev/null +++ b/src/kiwixwebchannelobject.cpp @@ -0,0 +1,7 @@ +#include "kiwixwebchannelobject.h" +#include "kiwixapp.h" + +QString KiwixWebChannelObject::getTocTitle() const +{ + return gt("table-of-content"); +} diff --git a/src/kiwixwebchannelobject.h b/src/kiwixwebchannelobject.h new file mode 100644 index 00000000..6be31137 --- /dev/null +++ b/src/kiwixwebchannelobject.h @@ -0,0 +1,16 @@ +#ifndef KIWIXWEBCHANNELOBJECT_H +#define KIWIXWEBCHANNELOBJECT_H + +#include + +class KiwixWebChannelObject : public QObject +{ + Q_OBJECT +public: + explicit KiwixWebChannelObject(QObject *parent = nullptr) : QObject(parent) {}; + + Q_INVOKABLE QString getTocTitle() const; + Q_PROPERTY(QString tocTitle READ getTocTitle CONSTANT); +}; + +#endif // KIWIXWEBCHANNELOBJECT_H diff --git a/src/kprofile.cpp b/src/kprofile.cpp index 6a4275f5..466487d1 100644 --- a/src/kprofile.cpp +++ b/src/kprofile.cpp @@ -34,6 +34,8 @@ KProfile::KProfile(QObject *parent) : scripts()->insert(getScript(":/js/toc.js")); scripts()->insert(getScript(":/js/tocCSS.js")); + scripts()->insert(getScript(":/qtwebchannel/qwebchannel.js", + QWebEngineScript::DocumentCreation)); } #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) diff --git a/src/webpage.cpp b/src/webpage.cpp index 8824710d..553961dc 100644 --- a/src/webpage.cpp +++ b/src/webpage.cpp @@ -5,6 +5,10 @@ #include #include "kiwixapp.h" #include +#include +#include + +#include "kiwixwebchannelobject.h" WebPage::WebPage(QObject *parent) : QWebEnginePage(KiwixApp::instance()->getProfile(), parent) @@ -12,6 +16,11 @@ WebPage::WebPage(QObject *parent) : action(QWebEnginePage::SavePage)->setVisible(false); action(QWebEnginePage::ViewSource)->setVisible(false); action(QWebEnginePage::Reload)->setVisible(false); + + QWebChannel *channel = new QWebChannel(this); + KiwixWebChannelObject *kiwixChannelObj = new KiwixWebChannelObject(this); + setWebChannel(channel, QWebEngineScript::UserWorld); + channel->registerObject("kiwixChannelObj", kiwixChannelObj); } bool WebPage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType /*type*/, bool /*isMainFrame*/) From 65ddbb57797655885826928bd6f08eb39c392ce4 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Wed, 25 Sep 2024 23:54:05 -0400 Subject: [PATCH 05/14] Enable ToggleTOCAction Show/Hide TOC with button and shortcut --- resources/js/toc.js | 14 ++++++++++++-- src/kiwixapp.cpp | 5 +++-- src/kiwixwebchannelobject.cpp | 5 +++++ src/kiwixwebchannelobject.h | 6 ++++++ src/topwidget.cpp | 3 +++ src/webpage.cpp | 4 ++++ 6 files changed, 33 insertions(+), 4 deletions(-) diff --git a/resources/js/toc.js b/resources/js/toc.js index a9870fae..3690cf8e 100644 --- a/resources/js/toc.js +++ b/resources/js/toc.js @@ -55,6 +55,14 @@ function tocHTMLStr() return recurseData.toc; } +function makeTOCVisible(visible) +{ + var tocElem = document.getElementById("kiwix-toc-side"); + tocElem.style.display = visible ? "block" : "none"; + document.body.style.marginLeft = visible ? "310px" : null; + document.body.style.maxWidth = visible ? "calc(100vw - 310px)" : null; +} + function setupTOC() { var toc = document.createElement('div'); @@ -75,10 +83,12 @@ function setupTOC() new QWebChannel(qt.webChannelTransport, function(channel) { var kiwixObj = channel.objects.kiwixChannelObj - document.body.style.marginLeft = "310px"; - document.body.style.maxWidth = "calc(100vw - 310px)"; setupTOC(); document.getElementById("kiwix-toc-title").textContent = kiwixObj.tocTitle; + kiwixObj.tocVisibleChanged.connect(function(visible) { + makeTOCVisible(visible); + }); + makeTOCVisible(kiwixObj.tocVisible); }); diff --git a/src/kiwixapp.cpp b/src/kiwixapp.cpp index ce071290..b99ff462 100644 --- a/src/kiwixapp.cpp +++ b/src/kiwixapp.cpp @@ -435,8 +435,8 @@ void KiwixApp::createActions() }); mpa_actions[ToggleFullscreenAction]->setCheckable(true); - CREATE_ACTION_SHORTCUT(ToggleTOCAction, gt("table-of-content"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_1)); - HIDE_ACTION(ToggleTOCAction); + CREATE_ACTION_ICON_SHORTCUT(ToggleTOCAction, "toc", gt("table-of-content"), QKeySequence(Qt::CTRL | Qt::Key_M)); + mpa_actions[ToggleTOCAction]->setCheckable(true); CREATE_ACTION_ONOFF_ICON_SHORTCUT(ToggleReadingListAction, "reading-list-active", "reading-list", gt("reading-list"), QKeySequence(Qt::CTRL | Qt::Key_B)); @@ -498,6 +498,7 @@ void KiwixApp::handleItemsState(TabType tabType) app->getAction(KiwixApp::ZoomResetAction)->setDisabled(libraryOrSettingsTab); app->getAction(KiwixApp::RandomArticleAction)->setDisabled(libraryOrSettingsTab); app->getAction(KiwixApp::OpenHomePageAction)->setDisabled(libraryOrSettingsTab); + app->getAction(KiwixApp::ToggleTOCAction)->setDisabled(libraryOrSettingsTab); /* Non-Zim tabs are not bookmarkable therefore never in reading list. */ if (notBookmarkableTab) diff --git a/src/kiwixwebchannelobject.cpp b/src/kiwixwebchannelobject.cpp index bb48935d..92eb7703 100644 --- a/src/kiwixwebchannelobject.cpp +++ b/src/kiwixwebchannelobject.cpp @@ -5,3 +5,8 @@ QString KiwixWebChannelObject::getTocTitle() const { return gt("table-of-content"); } + +bool KiwixWebChannelObject::getTocVisible() const +{ + return KiwixApp::instance()->getAction(KiwixApp::ToggleTOCAction)->isChecked(); +} diff --git a/src/kiwixwebchannelobject.h b/src/kiwixwebchannelobject.h index 6be31137..45d6d5e3 100644 --- a/src/kiwixwebchannelobject.h +++ b/src/kiwixwebchannelobject.h @@ -11,6 +11,12 @@ class KiwixWebChannelObject : public QObject Q_INVOKABLE QString getTocTitle() const; Q_PROPERTY(QString tocTitle READ getTocTitle CONSTANT); + + Q_INVOKABLE bool getTocVisible() const; + Q_PROPERTY(bool tocVisible READ getTocVisible NOTIFY tocVisibleChanged); + +signals: + void tocVisibleChanged(bool visible); }; #endif // KIWIXWEBCHANNELOBJECT_H diff --git a/src/topwidget.cpp b/src/topwidget.cpp index c29d5927..9843b0bf 100644 --- a/src/topwidget.cpp +++ b/src/topwidget.cpp @@ -30,6 +30,9 @@ TopWidget::TopWidget(QWidget *parent) : QAction *random = app->getAction(KiwixApp::RandomArticleAction); addAction(random); + QAction *toc = app->getAction(KiwixApp::ToggleTOCAction); + addAction(toc); + // For CSS if (QGuiApplication::isLeftToRight()) { widgetForAction(back)->setObjectName("leftHistoryButton"); diff --git a/src/webpage.cpp b/src/webpage.cpp index 553961dc..f137a53c 100644 --- a/src/webpage.cpp +++ b/src/webpage.cpp @@ -21,6 +21,10 @@ WebPage::WebPage(QObject *parent) : KiwixWebChannelObject *kiwixChannelObj = new KiwixWebChannelObject(this); setWebChannel(channel, QWebEngineScript::UserWorld); channel->registerObject("kiwixChannelObj", kiwixChannelObj); + + auto app = KiwixApp::instance(); + connect(app->getAction(KiwixApp::ToggleTOCAction), &QAction::toggled, + kiwixChannelObj, &KiwixWebChannelObject::tocVisibleChanged); } bool WebPage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType /*type*/, bool /*isMainFrame*/) From 0a8946c085edfb9bff6f2c20292bd66cb077e508 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Thu, 26 Sep 2024 02:05:55 -0400 Subject: [PATCH 06/14] Add Hide Button in TOC Extra button for user to close TOC --- resources/js/toc.js | 15 ++++++++++++++- src/kiwixwebchannelobject.cpp | 11 +++++++++++ src/kiwixwebchannelobject.h | 6 +++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/resources/js/toc.js b/resources/js/toc.js index 3690cf8e..08807933 100644 --- a/resources/js/toc.js +++ b/resources/js/toc.js @@ -72,9 +72,17 @@ function setupTOC() var tocTitle = document.createElement('p'); tocTitle.id = "kiwix-toc-title"; + var tocHideButton = document.createElement('button'); + tocHideButton.id = "kiwix-toc-hide-button" + + var tocTitleDiv = document.createElement('div'); + tocTitleDiv.id = "kiwix-toc-title-div"; + tocTitleDiv.append(tocTitle); + tocTitleDiv.append(tocHideButton); + var tocSideDiv = document.createElement('div'); tocSideDiv.id = "kiwix-toc-side"; - tocSideDiv.append(tocTitle); + tocSideDiv.append(tocTitleDiv); tocSideDiv.append(toc); document.body.prepend(tocSideDiv); @@ -86,6 +94,11 @@ new QWebChannel(qt.webChannelTransport, function(channel) { setupTOC(); document.getElementById("kiwix-toc-title").textContent = kiwixObj.tocTitle; + + var tocHideButton = document.getElementById("kiwix-toc-hide-button"); + tocHideButton.textContent = kiwixObj.hideButtontext; + tocHideButton.onclick = () => { kiwixObj.tocVisible = false; }; + kiwixObj.tocVisibleChanged.connect(function(visible) { makeTOCVisible(visible); }); diff --git a/src/kiwixwebchannelobject.cpp b/src/kiwixwebchannelobject.cpp index 92eb7703..2333991e 100644 --- a/src/kiwixwebchannelobject.cpp +++ b/src/kiwixwebchannelobject.cpp @@ -6,7 +6,18 @@ QString KiwixWebChannelObject::getTocTitle() const return gt("table-of-content"); } +QString KiwixWebChannelObject::getHideButtonText() const +{ + return gt("hide"); +} + bool KiwixWebChannelObject::getTocVisible() const { return KiwixApp::instance()->getAction(KiwixApp::ToggleTOCAction)->isChecked(); } + +void KiwixWebChannelObject::setTocVisible(bool visible) +{ + if (getTocVisible() != visible) + KiwixApp::instance()->getAction(KiwixApp::ToggleTOCAction)->toggle(); +} diff --git a/src/kiwixwebchannelobject.h b/src/kiwixwebchannelobject.h index 45d6d5e3..a3072632 100644 --- a/src/kiwixwebchannelobject.h +++ b/src/kiwixwebchannelobject.h @@ -12,8 +12,12 @@ class KiwixWebChannelObject : public QObject Q_INVOKABLE QString getTocTitle() const; Q_PROPERTY(QString tocTitle READ getTocTitle CONSTANT); + Q_INVOKABLE QString getHideButtonText() const; + Q_PROPERTY(QString hideButtontext READ getHideButtonText CONSTANT); + Q_INVOKABLE bool getTocVisible() const; - Q_PROPERTY(bool tocVisible READ getTocVisible NOTIFY tocVisibleChanged); + Q_INVOKABLE void setTocVisible(bool visible); + Q_PROPERTY(bool tocVisible READ getTocVisible WRITE setTocVisible NOTIFY tocVisibleChanged); signals: void tocVisibleChanged(bool visible); From a717b02e4b3aface1e616966886750fb85cffe14 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Thu, 26 Sep 2024 02:19:37 -0400 Subject: [PATCH 07/14] Only One of ReadingList&TOC Visible Two side bar will be mutually exclusive. --- src/webpage.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/webpage.cpp b/src/webpage.cpp index f137a53c..a55eb574 100644 --- a/src/webpage.cpp +++ b/src/webpage.cpp @@ -23,8 +23,18 @@ WebPage::WebPage(QObject *parent) : channel->registerObject("kiwixChannelObj", kiwixChannelObj); auto app = KiwixApp::instance(); - connect(app->getAction(KiwixApp::ToggleTOCAction), &QAction::toggled, + auto tocAction = app->getAction(KiwixApp::ToggleTOCAction); + auto readingListAction = app->getAction(KiwixApp::ToggleReadingListAction); + connect(tocAction, &QAction::toggled, kiwixChannelObj, &KiwixWebChannelObject::tocVisibleChanged); + connect(readingListAction, &QAction::toggled, this, [=](bool visible){ + if (visible && tocAction->isChecked()) + tocAction->toggle(); + }); + connect(tocAction, &QAction::toggled, this, [=](bool visible){ + if (visible && readingListAction->isChecked()) + readingListAction->toggle(); + }); } bool WebPage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType /*type*/, bool /*isMainFrame*/) From 0d7f73564b26bc8c2c8f55dfda27e40b445e074b Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Fri, 27 Sep 2024 20:35:40 -0400 Subject: [PATCH 08/14] Proper TOC Style, Numbering and Indentation --- resources/css/toc.css | 48 +++++++++++++++++++++++++++++++++++++++++++ resources/js/toc.js | 27 ++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/resources/css/toc.css b/resources/css/toc.css index c337cb91..dcdb4d4e 100644 --- a/resources/css/toc.css +++ b/resources/css/toc.css @@ -11,3 +11,51 @@ padding: 0px; z-index: 10000; } + +.kiwix-toc-item { + all: initial; + display: list-item; + list-style-type: none; + position: relative; + width: 100%; +} + +.kiwix-toc-list { + all: initial; + display: block; + width: 100%; + margin: 0px; + padding: 0px; + counter-reset: item; +} + +.kiwix-toc-item-a { + all: initial; + color: inherit; + outline: none; + display: block; + position: relative; + + padding-bottom: 3px; + padding-top: 3px; + border: none; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + + font-size: 16px; + line-height: 24px; + font-family: Arial; +} + +.kiwix-toc-item-a:hover { + color: inherit; + text-decoration: none; + + border-top: 1px solid #3366CC; + border-bottom: 1px solid #3366CC; + + background-color: #D9E9FF; + cursor: pointer; +} + +.kiwix-toc-item-a:before { content: counters(item, ".") " "; counter-increment: item; margin-right: 10px; padding-left: 10px; } diff --git a/resources/js/toc.js b/resources/js/toc.js index 08807933..118c472b 100644 --- a/resources/js/toc.js +++ b/resources/js/toc.js @@ -24,14 +24,14 @@ function recurseChild(elem, recurseData) /* Start or end a list or item based on current and previous level */ if (level > prevLevel) - recurseData.toc += '
      '; + recurseData.toc += '
        '; else if (level < prevLevel) recurseData.toc += '
      '; else recurseData.toc += ''; recurseData.level = parseInt(level); - recurseData.toc += '
    • ' + headerText + ''; + recurseData.toc += '
    • ' + headerText + ''; } var c = elem.children; @@ -63,6 +63,28 @@ function makeTOCVisible(visible) document.body.style.maxWidth = visible ? "calc(100vw - 310px)" : null; } +function setupTOCItems() +{ + var c = document.getElementsByClassName("kiwix-toc-item-a"); + for (var i = 0; i < c.length; i++) + { + if (c[i] !== "undefined" && c[i].style) + { + var p = c[i].parentNode; + var count = -1; + while (p) + { + if (p.nodeName.match(/^UL$/)) + count += 1; + p = p.parentNode; + } + + /* We need manual padding to achieve visual effects on hover. */ + c[i].style.paddingLeft = ((count == -1 ? 0 : count) * 30).toString() + "px"; + } + } +} + function setupTOC() { var toc = document.createElement('div'); @@ -92,6 +114,7 @@ new QWebChannel(qt.webChannelTransport, function(channel) { var kiwixObj = channel.objects.kiwixChannelObj setupTOC(); + setupTOCItems(); document.getElementById("kiwix-toc-title").textContent = kiwixObj.tocTitle; From ba70d89bdaaedfa245cb5f56fe41864ec72f5d51 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Fri, 27 Sep 2024 21:17:25 -0400 Subject: [PATCH 09/14] Proper Style for TOC title section Style title and hide button text --- resources/css/toc.css | 48 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/resources/css/toc.css b/resources/css/toc.css index dcdb4d4e..509acfef 100644 --- a/resources/css/toc.css +++ b/resources/css/toc.css @@ -12,6 +12,54 @@ z-index: 10000; } +#kiwix-toc-title-div { + all: initial; + display: flex; + justify-content: space-between; + position: relative; + font-size: 0px; + height: 30px; + margin: 0px; + border: 0px; + padding: 10px; +} + +#kiwix-toc-title { + all: initial; + display: block; + position: relative; + + font-size: 24px; + line-height: 30px; + font-weight: bold; + font-family: Arial; + + margin: 0px; + padding: 0px; +} + +#kiwix-toc-hide-button { + all: initial; + display: flex; + align-items: center; + position: relative; + + background: white; + color: #3366CC; + + margin: 0px; + margin-top: 7px; + padding: 0px; + + font-size: 12px; + font-family: Arial; + font-weight: bold; +} + +#kiwix-toc-hide-button:hover { + text-decoration: underline; +} + .kiwix-toc-item { all: initial; display: list-item; From 2e9431efb0e32013dd0a9c5caa97eb2c8fc925c4 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Fri, 27 Sep 2024 21:27:54 -0400 Subject: [PATCH 10/14] Proper Scroll Area Styling for TOC --- resources/css/toc.css | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/resources/css/toc.css b/resources/css/toc.css index 509acfef..f02a8a80 100644 --- a/resources/css/toc.css +++ b/resources/css/toc.css @@ -107,3 +107,27 @@ } .kiwix-toc-item-a:before { content: counters(item, ".") " "; counter-increment: item; margin-right: 10px; padding-left: 10px; } + +#kiwix-toc { + all: initial; + display: block; + position: relative; + + /* #kiwix-toc-title-div height&padding and #kiwix-toc border-top */ + height: calc(100vh - 51px); + + width: 100%; + overflow: auto; + border-top: 1px solid #ccc; +} + +#kiwix-toc::-webkit-scrollbar { + width: 5px; + border: 1px solid #ccc; + outline: none; +} + +#kiwix-toc::-webkit-scrollbar-thumb { + background-color: darkgrey; + outline: none; +} From d384b7ce7633efcc9a6ebb1d9521cc82e7f4029d Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Fri, 27 Sep 2024 22:03:39 -0400 Subject: [PATCH 11/14] Remove Visual Side-Effect of Tags Retain the original look of the headers --- resources/css/toc.css | 13 +++++++++++++ resources/js/toc.js | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/resources/css/toc.css b/resources/css/toc.css index f02a8a80..71900f81 100644 --- a/resources/css/toc.css +++ b/resources/css/toc.css @@ -108,6 +108,19 @@ .kiwix-toc-item-a:before { content: counters(item, ".") " "; counter-increment: item; margin-right: 10px; padding-left: 10px; } +.kiwix-toc-a { + all: inherit; + pointer-events: none; +} + +.kiwix-toc-a:hover { + all: inherit; +} + +.kiwix-toc-a:visited { + all: inherit; +} + #kiwix-toc { all: initial; display: block; diff --git a/resources/js/toc.js b/resources/js/toc.js index 118c472b..4f899ec7 100644 --- a/resources/js/toc.js +++ b/resources/js/toc.js @@ -20,7 +20,7 @@ function recurseChild(elem, recurseData) recurseData.count += 1; /* Wrap header content with something we can reference. */ - elem.innerHTML = '' + headerText + ''; + elem.innerHTML = '' + headerText + ''; /* Start or end a list or item based on current and previous level */ if (level > prevLevel) From a02e401ce4b4819c5c7ae6d4fd5fb19312a8b3f7 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Fri, 27 Sep 2024 22:07:36 -0400 Subject: [PATCH 12/14] Text Elide for TOC items --- resources/css/toc.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/resources/css/toc.css b/resources/css/toc.css index 71900f81..6cc53a72 100644 --- a/resources/css/toc.css +++ b/resources/css/toc.css @@ -33,6 +33,9 @@ line-height: 30px; font-weight: bold; font-family: Arial; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; margin: 0px; padding: 0px; @@ -93,6 +96,9 @@ font-size: 16px; line-height: 24px; font-family: Arial; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .kiwix-toc-item-a:hover { From 78f256b39a018cc212eee8dd6510faab89984a56 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Fri, 27 Sep 2024 22:40:07 -0400 Subject: [PATCH 13/14] Add Tooltip to Elided TOC Items --- resources/css/toc.css | 16 ++++++++++++++++ resources/js/toc.js | 27 ++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/resources/css/toc.css b/resources/css/toc.css index 6cc53a72..714d171a 100644 --- a/resources/css/toc.css +++ b/resources/css/toc.css @@ -114,6 +114,22 @@ .kiwix-toc-item-a:before { content: counters(item, ".") " "; counter-increment: item; margin-right: 10px; padding-left: 10px; } +.kiwix-tool-tip { + all: initial; + display: none; + position: fixed; + + background-color: #ffffe1; + border: 1px solid black; + padding: 5px; + + font-size: 16px; + line-height: 20px; + font-family: Arial; + + z-index: 10000; +} + .kiwix-toc-a { all: inherit; pointer-events: none; diff --git a/resources/js/toc.js b/resources/js/toc.js index 4f899ec7..9035e1d3 100644 --- a/resources/js/toc.js +++ b/resources/js/toc.js @@ -31,7 +31,8 @@ function recurseChild(elem, recurseData) recurseData.toc += '
    • '; recurseData.level = parseInt(level); - recurseData.toc += '
    • ' + headerText + ''; + recurseData.toc += '
    • ' + + '' + headerText + '' + headerText + ''; } var c = elem.children; @@ -63,6 +64,27 @@ function makeTOCVisible(visible) document.body.style.maxWidth = visible ? "calc(100vw - 310px)" : null; } +function showToolTip (elem) { + var tooltip = elem.getElementsByClassName("kiwix-tool-tip")[0]; + + /* Check if there is overflow. */ + if (tooltip && elem.offsetWidth < elem.scrollWidth) + { + tooltip.style.display = "block"; + + var rect = elem.getBoundingClientRect(); + tooltip.style.top = (rect.top).toString() + "px"; + tooltip.style.left = "306px"; + } +} + +function hideToolTip (elem) { + var tooltip = elem.getElementsByClassName("kiwix-tool-tip")[0]; + + if (tooltip) + tooltip.style.display = ""; +} + function setupTOCItems() { var c = document.getElementsByClassName("kiwix-toc-item-a"); @@ -81,6 +103,9 @@ function setupTOCItems() /* We need manual padding to achieve visual effects on hover. */ c[i].style.paddingLeft = ((count == -1 ? 0 : count) * 30).toString() + "px"; + + c[i].addEventListener("mouseover", (event) => { showToolTip(event.target); }); + c[i].addEventListener("mouseout", (event) => { hideToolTip(event.target); }); } } } From 200d2ea7be2ab3e46cee87aab5a68d6753df7771 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Fri, 27 Sep 2024 22:50:57 -0400 Subject: [PATCH 14/14] Adjust SideBar Layout to Zoom Level --- resources/js/toc.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/resources/js/toc.js b/resources/js/toc.js index 9035e1d3..61246e06 100644 --- a/resources/js/toc.js +++ b/resources/js/toc.js @@ -56,12 +56,18 @@ function tocHTMLStr() return recurseData.toc; } +function getPixelWithZoom(pixel) +{ + var zoom = window.outerWidth / window.document.documentElement.clientWidth; + return (pixel / zoom.toFixed(1)).toString() + "px"; +} + function makeTOCVisible(visible) { var tocElem = document.getElementById("kiwix-toc-side"); tocElem.style.display = visible ? "block" : "none"; - document.body.style.marginLeft = visible ? "310px" : null; - document.body.style.maxWidth = visible ? "calc(100vw - 310px)" : null; + document.body.style.marginLeft = visible ? getPixelWithZoom(310) : null; + document.body.style.maxWidth = visible ? "calc(100vw - " + getPixelWithZoom(310) + ")" : null; } function showToolTip (elem) { @@ -74,7 +80,7 @@ function showToolTip (elem) { var rect = elem.getBoundingClientRect(); tooltip.style.top = (rect.top).toString() + "px"; - tooltip.style.left = "306px"; + tooltip.style.left = getPixelWithZoom(306); } } @@ -135,6 +141,16 @@ function setupTOC() document.body.prepend(tocSideDiv); } +function resize(){ + document.getElementById("kiwix-toc-side").style.width = getPixelWithZoom(306); + + if (document.getElementById("kiwix-toc-side").style.display !== "none") + { + document.body.style.marginLeft = getPixelWithZoom(310); + document.body.style.maxWidth = "calc(100vw - " + getPixelWithZoom(310) + ")"; + } +} + new QWebChannel(qt.webChannelTransport, function(channel) { var kiwixObj = channel.objects.kiwixChannelObj @@ -151,5 +167,8 @@ new QWebChannel(qt.webChannelTransport, function(channel) { makeTOCVisible(visible); }); makeTOCVisible(kiwixObj.tocVisible); + + window.onresize = resize; + resize(); });