okular/part/toc.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

231 lines
6.3 KiB
C++
Raw Normal View History

2021-05-24 07:25:56 +00:00
/*
SPDX-FileCopyrightText: 2004-2006 Albert Astals Cid <aacid@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "toc.h"
// qt/kde includes
#include <QContextMenuEvent>
2020-07-08 11:54:37 +00:00
#include <QHeaderView>
#include <QLayout>
#include <QTreeView>
#include <qdom.h>
2020-07-08 11:54:37 +00:00
#include <KLineEdit>
#include <KLocalizedString>
Improve the sidebar's navigation and UX Okular's sidebar vertical view chooser toolbar suffers from a few issues: * It's a nonstandard UI not used for category choosers in other pieces of KDE software, and not used in other FOSS document readers * What is shown and what is hidden is simultaneously too configurable while still not offering the desirable UI common to other programs (i.e. no visible category chooser, but a sidebar capable of displaying thumbnails, table of contents, search results, etc.) * With labels on it takes up quite a bit of horizontal space, while with labels off, the categories are less than clear * UX is kind of clunky with nonstandard behaviors (e.g. clicking on the current category to hide that category's view while keeping the view chooser visible, showing mostly disabled items) * It's made with custom painting code, which reduces maintainability and introduces bugs (e.g. https://bugs.kde.org/show_bug.cgi?id=408190) This patch removes the vertical category chooser entirely and replaces it with a tabbed view on the top of the sidebar itself. The tabs are icons-only and have large icons. A button is added on the left side of the default toolbar to quickly hide or show the sidebar. In order to make room for the new button, the Previous and Next buttons on the toolbar are removed, as previous/next buttons are already present on the Page Bar on the bottom of the window so there's no need to duplicate this functionality. This improves the UX, fixes a variety of bugs, and deletes a lot of custom code of dubious long-term maintainability. ![vokoscreenNG-2020-04-16_13-29-24](https://invent.kde.org/graphics/okular/uploads/a1f96a315b69282df51de9993b1befaf/vokoscreenNG-2020-04-16_13-29-24.webm) BUG: 213508 BUG: 334441 BUG: 344599 BUG: 408190 CCBUG: 335189 FIXED-IN: 1.11.0 CHANGELOG: The sidebar can now be easily shown or hidden with a toolbar button, and the category chooser no longer takes up so much space
2020-05-27 13:37:42 +00:00
#include <KTitleWidget>
#include <kwidgetsaddons_version.h>
// local includes
#include "core/action.h"
#include "gui/tocmodel.h"
#include "ktreeviewsearchline.h"
#include "pageitemdelegate.h"
#include "settings.h"
TOC::TOC(QWidget *parent, Okular::Document *document)
: QWidget(parent)
, m_document(document)
{
QVBoxLayout *mainlay = new QVBoxLayout(this);
mainlay->setSpacing(6);
Improve the sidebar's navigation and UX Okular's sidebar vertical view chooser toolbar suffers from a few issues: * It's a nonstandard UI not used for category choosers in other pieces of KDE software, and not used in other FOSS document readers * What is shown and what is hidden is simultaneously too configurable while still not offering the desirable UI common to other programs (i.e. no visible category chooser, but a sidebar capable of displaying thumbnails, table of contents, search results, etc.) * With labels on it takes up quite a bit of horizontal space, while with labels off, the categories are less than clear * UX is kind of clunky with nonstandard behaviors (e.g. clicking on the current category to hide that category's view while keeping the view chooser visible, showing mostly disabled items) * It's made with custom painting code, which reduces maintainability and introduces bugs (e.g. https://bugs.kde.org/show_bug.cgi?id=408190) This patch removes the vertical category chooser entirely and replaces it with a tabbed view on the top of the sidebar itself. The tabs are icons-only and have large icons. A button is added on the left side of the default toolbar to quickly hide or show the sidebar. In order to make room for the new button, the Previous and Next buttons on the toolbar are removed, as previous/next buttons are already present on the Page Bar on the bottom of the window so there's no need to duplicate this functionality. This improves the UX, fixes a variety of bugs, and deletes a lot of custom code of dubious long-term maintainability. ![vokoscreenNG-2020-04-16_13-29-24](https://invent.kde.org/graphics/okular/uploads/a1f96a315b69282df51de9993b1befaf/vokoscreenNG-2020-04-16_13-29-24.webm) BUG: 213508 BUG: 334441 BUG: 344599 BUG: 408190 CCBUG: 335189 FIXED-IN: 1.11.0 CHANGELOG: The sidebar can now be easily shown or hidden with a toolbar button, and the category chooser no longer takes up so much space
2020-05-27 13:37:42 +00:00
KTitleWidget *titleWidget = new KTitleWidget(this);
titleWidget->setLevel(4);
Improve the sidebar's navigation and UX Okular's sidebar vertical view chooser toolbar suffers from a few issues: * It's a nonstandard UI not used for category choosers in other pieces of KDE software, and not used in other FOSS document readers * What is shown and what is hidden is simultaneously too configurable while still not offering the desirable UI common to other programs (i.e. no visible category chooser, but a sidebar capable of displaying thumbnails, table of contents, search results, etc.) * With labels on it takes up quite a bit of horizontal space, while with labels off, the categories are less than clear * UX is kind of clunky with nonstandard behaviors (e.g. clicking on the current category to hide that category's view while keeping the view chooser visible, showing mostly disabled items) * It's made with custom painting code, which reduces maintainability and introduces bugs (e.g. https://bugs.kde.org/show_bug.cgi?id=408190) This patch removes the vertical category chooser entirely and replaces it with a tabbed view on the top of the sidebar itself. The tabs are icons-only and have large icons. A button is added on the left side of the default toolbar to quickly hide or show the sidebar. In order to make room for the new button, the Previous and Next buttons on the toolbar are removed, as previous/next buttons are already present on the Page Bar on the bottom of the window so there's no need to duplicate this functionality. This improves the UX, fixes a variety of bugs, and deletes a lot of custom code of dubious long-term maintainability. ![vokoscreenNG-2020-04-16_13-29-24](https://invent.kde.org/graphics/okular/uploads/a1f96a315b69282df51de9993b1befaf/vokoscreenNG-2020-04-16_13-29-24.webm) BUG: 213508 BUG: 334441 BUG: 344599 BUG: 408190 CCBUG: 335189 FIXED-IN: 1.11.0 CHANGELOG: The sidebar can now be easily shown or hidden with a toolbar button, and the category chooser no longer takes up so much space
2020-05-27 13:37:42 +00:00
titleWidget->setText(i18n("Contents"));
mainlay->addWidget(titleWidget);
mainlay->setAlignment(titleWidget, Qt::AlignHCenter);
m_searchLine = new KTreeViewSearchLine(this);
mainlay->addWidget(m_searchLine);
m_searchLine->setPlaceholderText(i18n("Search..."));
m_searchLine->setCaseSensitivity(Okular::Settings::self()->contentsSearchCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive);
m_searchLine->setRegularExpression(Okular::Settings::self()->contentsSearchRegularExpression());
2014-10-01 17:40:48 +00:00
connect(m_searchLine, &KTreeViewSearchLine::searchOptionsChanged, this, &TOC::saveSearchOptions);
m_treeView = new QTreeView(this);
mainlay->addWidget(m_treeView);
m_model = new TOCModel(document, m_treeView);
m_treeView->setModel(m_model);
m_treeView->setSortingEnabled(false);
m_treeView->setRootIsDecorated(true);
m_treeView->setAlternatingRowColors(true);
m_treeView->setItemDelegate(new PageItemDelegate(m_treeView));
m_treeView->header()->hide();
m_treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
2014-10-01 17:40:48 +00:00
connect(m_treeView, &QTreeView::clicked, this, &TOC::slotExecuted);
connect(m_treeView, &QTreeView::activated, this, &TOC::slotExecuted);
m_searchLine->setTreeView(m_treeView);
}
TOC::~TOC()
{
m_document->removeObserver(this);
}
void TOC::notifySetup(const QVector<Okular::Page *> & /*pages*/, int setupFlags)
{
if (!(setupFlags & Okular::DocumentObserver::DocumentChanged)) {
return;
}
// clear contents
m_model->clear();
// request synopsis description (is a dom tree)
const Okular::DocumentSynopsis *syn = m_document->documentSynopsis();
if (!syn) {
if (m_document->isOpened()) {
// Make sure we clear the reload old model data
m_model->setOldModelData(nullptr, QVector<QModelIndex>());
}
Q_EMIT hasTOC(false);
return;
}
m_model->fill(syn);
Q_EMIT hasTOC(!m_model->isEmpty());
}
void TOC::notifyCurrentPageChanged(int, int)
{
m_model->setCurrentViewport(m_document->viewport());
}
void TOC::prepareForReload()
{
if (m_model->isEmpty()) {
return;
}
const QVector<QModelIndex> list = expandedNodes();
TOCModel *m = m_model;
m_model = new TOCModel(m_document, m_treeView);
m_model->setOldModelData(m, list);
m->setParent(nullptr);
}
void TOC::rollbackReload()
{
if (!m_model->hasOldModelData()) {
return;
}
TOCModel *m = m_model;
m_model = m->clearOldModelData();
m_model->setParent(m_treeView);
delete m;
}
void TOC::finishReload()
{
m_treeView->setModel(m_model);
m_model->setParent(m_treeView);
}
QVector<QModelIndex> TOC::expandedNodes(const QModelIndex &parent) const
{
QVector<QModelIndex> list;
for (int i = 0; i < m_model->rowCount(parent); i++) {
const QModelIndex index = m_model->index(i, 0, parent);
if (m_treeView->isExpanded(index)) {
list << index;
}
if (m_model->hasChildren(index)) {
list << expandedNodes(index);
}
}
return list;
}
void TOC::reparseConfig()
{
m_searchLine->setCaseSensitivity(Okular::Settings::contentsSearchCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive);
m_searchLine->setRegularExpression(Okular::Settings::contentsSearchRegularExpression());
m_treeView->update();
}
void TOC::slotExecuted(const QModelIndex &index)
{
if (!index.isValid()) {
return;
}
QString url = m_model->urlForIndex(index);
if (!url.isEmpty()) {
2014-08-10 18:36:41 +00:00
Okular::BrowseAction action(QUrl::fromLocalFile(url));
m_document->processAction(&action);
return;
}
QString externalFileName = m_model->externalFileNameForIndex(index);
Okular::DocumentViewport viewport = m_model->viewportForIndex(index);
if (!externalFileName.isEmpty()) {
Okular::GotoAction action(externalFileName, viewport);
m_document->processAction(&action);
} else if (viewport.isValid()) {
m_document->setViewport(viewport);
}
}
void TOC::saveSearchOptions()
{
Okular::Settings::setContentsSearchRegularExpression(m_searchLine->regularExpression());
Okular::Settings::setContentsSearchCaseSensitive(m_searchLine->caseSensitivity() == Qt::CaseSensitive ? true : false);
2014-10-01 05:27:09 +00:00
Okular::Settings::self()->save();
}
void TOC::contextMenuEvent(QContextMenuEvent *e)
{
QModelIndex index = m_treeView->currentIndex();
if (!index.isValid()) {
return;
}
Okular::DocumentViewport viewport = m_model->viewportForIndex(index);
Q_EMIT rightClick(viewport, e->globalPos(), m_model->data(index).toString());
}
void TOC::expandRecursively()
{
QList<QModelIndex> worklist = {m_treeView->currentIndex()};
if (!worklist[0].isValid()) {
return;
}
while (!worklist.isEmpty()) {
QModelIndex index = worklist.takeLast();
m_treeView->expand(index);
for (int i = 0; i < m_model->rowCount(index); i++) {
worklist += m_model->index(i, 0, index);
}
}
}
void TOC::collapseRecursively()
{
QList<QModelIndex> worklist = {m_treeView->currentIndex()};
if (!worklist[0].isValid()) {
return;
}
while (!worklist.isEmpty()) {
QModelIndex index = worklist.takeLast();
m_treeView->collapse(index);
for (int i = 0; i < m_model->rowCount(index); i++) {
worklist += m_model->index(i, 0, index);
}
}
}
void TOC::expandAll()
{
m_treeView->expandAll();
}
void TOC::collapseAll()
{
m_treeView->collapseAll();
}