Skip to content

Commit

Permalink
Merge pull request #1237 from kiwix/Issue#42-toc-qtree-alternative
Browse files Browse the repository at this point in the history
Introduce Table of Content Without Intense JS Invasion
  • Loading branch information
kelson42 authored Nov 13, 2024
2 parents b1f02ec + 36a2782 commit b2aa32a
Show file tree
Hide file tree
Showing 16 changed files with 541 additions and 8 deletions.
11 changes: 8 additions & 3 deletions kiwix-desktop.pro
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -85,6 +85,7 @@ SOURCES += \
src/tabbar.cpp \
src/contentmanagerside.cpp \
src/readinglistbar.cpp \
src/tableofcontentbar.cpp \
src/klistwidgetitem.cpp \
src/opdsrequestmanager.cpp \
src/localkiwixserver.cpp \
Expand Down Expand Up @@ -135,6 +136,7 @@ HEADERS += \
src/tabbar.h \
src/contentmanagerside.h \
src/readinglistbar.h \
src/tableofcontentbar.h \
src/klistwidgetitem.h \
src/opdsrequestmanager.h \
src/localkiwixserver.h \
Expand All @@ -145,6 +147,7 @@ HEADERS += \
src/portutils.h \
src/css_constants.h \
src/multizimbutton.h \
src/kiwixwebchannelobject.h \

FORMS += \
src/choiceitem.ui \
Expand All @@ -157,7 +160,8 @@ FORMS += \
src/contentmanagerside.ui \
src/readinglistbar.ui \
ui/localkiwixserver.ui \
ui/settings.ui
ui/settings.ui \
src/tableofcontentbar.ui \

include(subprojects/QtSingleApplication/src/qtsingleapplication.pri)
CODECFORSRC = UTF-8
Expand Down Expand Up @@ -232,6 +236,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
62 changes: 62 additions & 0 deletions resources/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,65 @@ ContentTypeFilter {
width: 0;
height: 0;
}

#tableofcontentbar {
background-color: white;
}

#tableofcontentbar QTreeWidget,
#tableofcontentbar QLabel,
#tableofcontentbar QFrame {
background-color: white;
}

#tableofcontentbar QTreeWidget {
outline: none;
}

#tableofcontentbar QTreeWidget::item {
height: 26px;
padding: 0px 10px;
outline: none;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
}

#tableofcontentbar QTreeWidget::item:selected,
#tableofcontentbar QTreeWidget::item:hover {
outline: none;
border-top: 1px solid #3366CC;
border-bottom: 1px solid #3366CC;
background-color: #D9E9FF;
color: black;
}

#tableofcontentbar QTreeWidget::branch:selected,
#tableofcontentbar QTreeWidget::branch:hover {
outline: none;
border-top: 1px solid #3366CC;
border-bottom: 1px solid #3366CC;
background-color: #D9E9FF;
}

#tableofcontentbar QTreeWidget::branch {
image: none;
}

#tableofcontentbar #titleLabel {
padding: 0px;
margin: 10px;
}

#tableofcontentbar #hideLabel {
margin: 13px 10px 10px; /* 3px to match bottom with titleLabel */
}

#tableofcontentbar QScrollBar {
width: 5px;
border: none;
outline: none;
}

#tableofcontentbar QScrollBar::handle {
background-color: grey;
}
5 changes: 5 additions & 0 deletions resources/js.qrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>js/headerAnchor.js</file>
</qresource>
</RCC>
53 changes: 53 additions & 0 deletions resources/js/headerAnchor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
function isHeaderElement(elem)
{
return elem.nodeName.match(/^H\d+$/) && elem.textContent;
}

function getDOMElementsPreorderDFS(elem, pred)
{
var result = [];
if (pred(elem))
result.push(elem);

for ( const child of elem.children)
result = result.concat(getDOMElementsPreorderDFS(child, pred));
return result;
}

function anchorHeaderElements(headers)
{
return Array.from(headers, function(elem, i)
{
const text = elem.textContent.trim().replace(/"/g, '\\"');
const level = parseInt(elem.nodeName.substr(1));
const anchor = `kiwix-toc-${i}`;

const anchorElem = document.createElement("a");
anchorElem.id = anchor;

/* Mark header content with something we can reference. */
elem.insertAdjacentElement("afterbegin", anchorElem);
return { text, level, anchor };
});
}

function getHeaders()
{
const headerInfo = { url: window.location.href.replace(location.hash,""), headers: [] };

if (document.body !== undefined)
{
const headers = getDOMElementsPreorderDFS(document.body, isHeaderElement);
headerInfo.headers = anchorHeaderElements(headers);
}
return headerInfo;
}

new QWebChannel(qt.webChannelTransport, function(channel) {
var kiwixObj = channel.objects.kiwixChannelObj;
kiwixObj.sendHeaders(getHeaders());
kiwixObj.navigationRequested.connect(function(url, anchor) {
if (window.location.href.replace(location.hash,"") == url)
document.getElementById(anchor).scrollIntoView();
});
});
5 changes: 3 additions & 2 deletions src/kiwixapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_ICON_SHORTCUT(OpenMultiZimAction, "filter", gt("search-options"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_L));

Expand Down Expand Up @@ -492,6 +492,7 @@ void KiwixApp::handleItemsState(TabType tabType)
auto libraryOrSettingsTab = (tabType == TabType::LibraryTab || tabType == TabType::SettingsTab);
auto notBookmarkableTab = libraryOrSettingsTab || getTabWidget()->currentArticleUrl().isEmpty();
auto app = KiwixApp::instance();
app->getAction(KiwixApp::ToggleTOCAction)->setDisabled(libraryOrSettingsTab);
app->getAction(KiwixApp::ToggleReadingListAction)->setDisabled(libraryOrSettingsTab);
app->getAction(KiwixApp::ToggleAddBookmarkAction)->setDisabled(notBookmarkableTab);
app->getAction(KiwixApp::FindInPageAction)->setDisabled(libraryOrSettingsTab);
Expand Down
20 changes: 20 additions & 0 deletions src/kiwixwebchannelobject.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef KIWIXWEBCHANNELOBJECT_H
#define KIWIXWEBCHANNELOBJECT_H

#include <QObject>

class KiwixWebChannelObject : public QObject
{
Q_OBJECT

public:
explicit KiwixWebChannelObject(QObject *parent = nullptr) : QObject(parent) {};

Q_INVOKABLE void sendHeaders(const QJsonObject& headers) { emit headersChanged(headers); };

signals:
void headersChanged(const QJsonObject& headers);
void navigationRequested(const QString& url, const QString& anchor);
};

#endif // KIWIXWEBCHANNELOBJECT_H
24 changes: 24 additions & 0 deletions src/kprofile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,26 @@
#include <QFileDialog>
#include <QMessageBox>
#include <QWebEngineSettings>
#include <QWebEngineScript>
#include <QWebEngineScriptCollection>

namespace
{

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;
}

}

QString askForSaveFilePath(const QString& suggestedName)
{
Expand Down Expand Up @@ -36,6 +56,10 @@ KProfile::KProfile(QObject *parent) :
#else // Qt 5.13 and later
setUrlRequestInterceptor(new ExternalReqInterceptor(this));
#endif

scripts()->insert(getScript(":/js/headerAnchor.js"));
scripts()->insert(getScript(":/qtwebchannel/qwebchannel.js",
QWebEngineScript::DocumentCreation));
}

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Expand Down
37 changes: 35 additions & 2 deletions src/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ MainWindow::MainWindow(QWidget *parent) :
this, &MainWindow::toggleFullScreen);
connect(app->getAction(KiwixApp::ToggleReadingListAction), &QAction::toggled,
this, &MainWindow::readingListToggled);
connect(app->getAction(KiwixApp::ToggleTOCAction), &QAction::toggled,
this, &MainWindow::tableOfContentToggled);
connect(app->getAction(KiwixApp::AboutAction), &QAction::triggered,
mp_about, &QDialog::show);
connect(app->getAction(KiwixApp::DonateAction), &QAction::triggered,
Expand Down Expand Up @@ -170,9 +172,18 @@ void MainWindow::resizeEvent(QResizeEvent *event)
updateTabButtons();
}

void checkActionNoSignal(KiwixApp::Actions actionFlag, bool checked)
{
const auto action = KiwixApp::instance()->getAction(actionFlag);
const bool oldState = action->blockSignals(true);
action->setChecked(checked);
action->blockSignals(oldState);
}

void MainWindow::readingListToggled(bool state)
{
if (state) {
checkActionNoSignal(KiwixApp::ToggleTOCAction, false);
mp_ui->sideBar->setCurrentWidget(mp_ui->readinglistbar);
mp_ui->sideBar->show();
}
Expand All @@ -181,16 +192,33 @@ void MainWindow::readingListToggled(bool state)
}
}

void MainWindow::tableOfContentToggled(bool state)
{
if (state) {
checkActionNoSignal(KiwixApp::ToggleReadingListAction, false);
mp_ui->sideBar->setCurrentWidget(mp_ui->tableofcontentbar);
mp_ui->sideBar->show();
}
else {
mp_ui->sideBar->hide();
}
}

void MainWindow::tabChanged(TabType tabType)
{
QAction *readingList = KiwixApp::instance()->getAction(KiwixApp::ToggleReadingListAction);
QAction *tableOfContent = KiwixApp::instance()->getAction(KiwixApp::ToggleTOCAction);
if (tabType == TabType::SettingsTab) {
mp_ui->sideBar->hide();
} else if(tabType == TabType::LibraryTab) {
mp_ui->sideBar->setCurrentWidget(mp_ui->contentmanagerside);
mp_ui->sideBar->show();
} else {
readingListToggled(readingList->isChecked());
} else if (readingList->isChecked()) {
readingListToggled(true);
} else if (tableOfContent->isChecked()) {
tableOfContentToggled(true);
} else {
mp_ui->sideBar->hide();
}
}

Expand All @@ -203,3 +231,8 @@ TopWidget *MainWindow::getTopWidget()
{
return mp_ui->mainToolBar;
}

TableOfContentBar *MainWindow::getTableOfContentBar()
{
return mp_ui->tableofcontentbar;
}
4 changes: 4 additions & 0 deletions src/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace Ui {
class MainWindow;
}

class TableOfContentBar;

class MainWindow : public QMainWindow
{
Q_OBJECT
Expand All @@ -25,6 +27,7 @@ class MainWindow : public QMainWindow
TabBar* getTabBar();
TopWidget* getTopWidget();
QWidget getMainView();
TableOfContentBar *getTableOfContentBar();

protected:
bool eventFilter(QObject* object, QEvent* event) override;
Expand All @@ -35,6 +38,7 @@ private slots:
void toggleFullScreen();
void tabChanged(TabBar::TabType);
void readingListToggled(bool state);
void tableOfContentToggled(bool state);
void hideTabAndTop();
void showTabAndTop();
void updateTabButtons();
Expand Down
Loading

0 comments on commit b2aa32a

Please sign in to comment.