diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ set(KDEVPLATFORM_VERSION_PATCH 1) # plugin versions listed in the .desktop files -set(KDEV_PLUGIN_VERSION 26) +set(KDEV_PLUGIN_VERSION 27) # Increase this to reset incompatible item-repositories set(KDEV_ITEMREPOSITORY_VERSION 85) diff --git a/documentation/CMakeLists.txt b/documentation/CMakeLists.txt --- a/documentation/CMakeLists.txt +++ b/documentation/CMakeLists.txt @@ -15,7 +15,8 @@ kdevplatform_add_library(KDevPlatformDocumentation SOURCES ${KDevPlatformDocumentation_LIB_SRCS}) target_link_libraries(KDevPlatformDocumentation - LINK_PUBLIC KDev::Interfaces Qt5::WebKitWidgets) + LINK_PUBLIC KDev::Interfaces Qt5::WebKitWidgets + LINK_PRIVATE KDev::Util) install(FILES documentationfindwidget.h diff --git a/documentation/standarddocumentationview.h b/documentation/standarddocumentationview.h --- a/documentation/standarddocumentationview.h +++ b/documentation/standarddocumentationview.h @@ -1,6 +1,7 @@ /* * This file is part of KDevelop * Copyright 2010 Aleix Pol Gonzalez + * Copyright 2016 Igor Kushnir * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as @@ -21,13 +22,16 @@ #ifndef KDEVPLATFORM_STANDARDDOCUMENTATIONVIEW_H #define KDEVPLATFORM_STANDARDDOCUMENTATIONVIEW_H -#include #include "documentationexport.h" #include "documentationfindwidget.h" #include -namespace KDevelop -{ +#include +#include + +namespace KDevelop { + +class StandardDocumentationViewPrivate; /** * The standard documentation view, based on QWebView. @@ -37,6 +41,18 @@ Q_OBJECT public: explicit StandardDocumentationView(DocumentationFindWidget* findWidget, QWidget* parent = nullptr ); + ~StandardDocumentationView(); + + /** + * @brief Enables zoom functionality + * + * @param configSubGroup KConfigGroup nested group name used to store zoom factor. + * Should uniquely describe current documentation provider. + * + * @warning Calling this function a second time has no effect + */ + void initZoom(const QString& configSubGroup); + void setDocumentation(const IDocumentation::Ptr& doc); public slots: @@ -53,8 +69,14 @@ */ void update(); +private slots: + void updateZoomFactor(double zoomFactor); + private: - IDocumentation::Ptr m_doc; + void keyPressEvent(QKeyEvent* event) override; + void wheelEvent(QWheelEvent* event) override; + + QScopedPointer const d; }; } diff --git a/documentation/standarddocumentationview.cpp b/documentation/standarddocumentationview.cpp --- a/documentation/standarddocumentationview.cpp +++ b/documentation/standarddocumentationview.cpp @@ -1,6 +1,7 @@ /* * This file is part of KDevelop * Copyright 2010 Aleix Pol Gonzalez + * Copyright 2016 Igor Kushnir * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as @@ -21,20 +22,32 @@ #include "standarddocumentationview.h" #include "documentationfindwidget.h" #include "debug.h" +#include + +#include +#include #include +#include using namespace KDevelop; +class KDevelop::StandardDocumentationViewPrivate +{ +public: + ZoomController* m_zoomController = nullptr; + IDocumentation::Ptr m_doc; +}; + StandardDocumentationView::StandardDocumentationView(DocumentationFindWidget* findWidget, QWidget* parent) - : QWebView (parent) + : QWebView(parent) + , d{new StandardDocumentationViewPrivate()} { findWidget->setEnabled(true); connect(findWidget, &DocumentationFindWidget::newSearch, this, &StandardDocumentationView::search); QFont sansSerifFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont); QFont monospaceFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); - QFont minimalFont = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont); QWebSettings* s = settings(); @@ -45,7 +58,10 @@ s->setFontSize(QWebSettings::DefaultFontSize, QFontInfo(sansSerifFont).pixelSize()); s->setFontSize(QWebSettings::DefaultFixedFontSize, QFontInfo(monospaceFont).pixelSize()); - s->setFontSize(QWebSettings::MinimumFontSize, QFontInfo(minimalFont).pixelSize()); +} + +StandardDocumentationView::~StandardDocumentationView() +{ } void StandardDocumentationView::search ( const QString& text, DocumentationFindWidget::FindOptions options ) @@ -63,20 +79,60 @@ page()->findText(text, ff); } +void StandardDocumentationView::initZoom(const QString& configSubGroup) +{ + if (Q_UNLIKELY(d->m_zoomController)) { + qCWarning(DOCUMENTATION) << "Calling StandardDocumentationView::initZoom a second time has no effect."; + return; + } + + { + const KConfigGroup outerGroup(KSharedConfig::openConfig(), QStringLiteral("Documentation View")); + const KConfigGroup cg(&outerGroup, configSubGroup); + d->m_zoomController = new ZoomController(cg, this); + } + connect(d->m_zoomController, &ZoomController::factorChanged, + this, &StandardDocumentationView::updateZoomFactor); + updateZoomFactor(d->m_zoomController->factor()); +} + void StandardDocumentationView::setDocumentation(const IDocumentation::Ptr& doc) { - if(m_doc) - disconnect(m_doc.data()); - m_doc = doc; + if(d->m_doc) + disconnect(d->m_doc.data()); + d->m_doc = doc; update(); - if(m_doc) - connect(m_doc.data(), &IDocumentation::descriptionChanged, this, &StandardDocumentationView::update); + if(d->m_doc) + connect(d->m_doc.data(), &IDocumentation::descriptionChanged, this, &StandardDocumentationView::update); } void StandardDocumentationView::update() { - if(m_doc) - setHtml(m_doc->description()); + if(d->m_doc) + setHtml(d->m_doc->description()); else qCDebug(DOCUMENTATION) << "calling StandardDocumentationView::update() on an uninitialized view"; } + + +void StandardDocumentationView::updateZoomFactor(double zoomFactor) +{ + setZoomFactor(zoomFactor); +} + +void StandardDocumentationView::keyPressEvent(QKeyEvent* event) +{ + if (d->m_zoomController && event->modifiers() == Qt::ControlModifier && event->key() == Qt::Key_0) { + d->m_zoomController->resetZoom(); + event->accept(); + return; + } + QWebView::keyPressEvent(event); +} + +void StandardDocumentationView::wheelEvent(QWheelEvent* event) +{ + if (d->m_zoomController && d->m_zoomController->handleWheelEvent(event)) + return; + QWebView::wheelEvent(event); +} diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -7,6 +7,7 @@ foregroundlock.cpp formattinghelpers.cpp richtextpushbutton.cpp + zoomcontroller.cpp kdevstringhandler.cpp focusedtreeview.cpp processlinemaker.cpp @@ -64,6 +65,7 @@ foregroundlock.h formattinghelpers.h richtextpushbutton.h + zoomcontroller.h kdevstringhandler.h ksharedobject.h focusedtreeview.h diff --git a/util/zoomcontroller.h b/util/zoomcontroller.h new file mode 100644 --- /dev/null +++ b/util/zoomcontroller.h @@ -0,0 +1,110 @@ +/* + * Copyright 2016 Igor Kushnir + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef KDEVPLATFORM_ZOOMCONTROLLER_H +#define KDEVPLATFORM_ZOOMCONTROLLER_H + +#include "utilexport.h" + +#include +#include + +class KConfigGroup; +class QWheelEvent; + +namespace KDevelop { + +class ZoomControllerPrivate; + +/** + * @brief Stores zoom factor; provides common zoom operations and notifications + */ +class KDEVPLATFORMUTIL_EXPORT ZoomController : public QObject +{ + Q_OBJECT +public: + /** + * @param configGroup A place to store zoom factor in + */ + explicit ZoomController(const KConfigGroup& configGroup, + QObject* parent = nullptr); + + ~ZoomController(); + + /** + * @return Current zoom factor + */ + double factor() const; + + /** + * @brief Sets current zoom factor to @p factor + */ + void setFactor(double factor); + + /** + * @brief Changes current zoom factor according to @p scale + * + * @param scale If equal to 0, factor remains the same; + * otherwise, larger scale leads to larger factor. + * Can be both positive (zoom in) and negative (zoom out). + */ + void zoomBy(double scale); + + /** + * @brief Changes current zoom factor in response to Ctrl+mouse_scroll + * + * @param event Event to be analyzed and possibly accepted + * + * @return true If the event was accepted as a zoom scroll + */ + bool handleWheelEvent(QWheelEvent* event); + +public Q_SLOTS: + /** + * @brief Increases current zoom factor by the default multiplier + */ + void zoomIn(); + + /** + * @brief Decreases current zoom factor by the default multiplier + */ + void zoomOut(); + + /** + * @brief Sets current zoom factor to the default value (1.0) + */ + void resetZoom(); + +Q_SIGNALS: + /** + * @brief Notifies when current zoom factor is changed + * + * @param factor Current zoom factor + */ + void factorChanged(double factor); + +private: + QScopedPointer const d; +}; + +} + +#endif // KDEVPLATFORM_ZOOMCONTROLLER_H diff --git a/util/zoomcontroller.cpp b/util/zoomcontroller.cpp new file mode 100644 --- /dev/null +++ b/util/zoomcontroller.cpp @@ -0,0 +1,142 @@ +/* + * Copyright 2016 Igor Kushnir + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "zoomcontroller.h" + +#include "debug.h" + +#include + +#include +#include + +#include + + +namespace { + +constexpr const char* factorConfigEntryKey() { return "Zoom Factor"; } + +constexpr double defaultFactor{1}; +constexpr double defaultMultiplier{1.1}; + +double multiplier(double scale) +{ + // Allow finer-grained control over zoom factor compared to defaultMultiplier + constexpr double smallMultiplier{1.05}; + return std::pow(smallMultiplier, scale); +} + +} // namespace + + +using KDevelop::ZoomControllerPrivate; +using KDevelop::ZoomController; + +class KDevelop::ZoomControllerPrivate +{ +public: + explicit ZoomControllerPrivate(const KConfigGroup& configGroup); + void writeConfig(); + + KConfigGroup m_configGroup; + double m_factor{defaultFactor}; +}; + +ZoomControllerPrivate::ZoomControllerPrivate(const KConfigGroup& configGroup) + : m_configGroup{configGroup} +{ + m_factor = m_configGroup.readEntry(factorConfigEntryKey(), defaultFactor); +} + +void ZoomControllerPrivate::writeConfig() +{ + m_configGroup.writeEntry(factorConfigEntryKey(), m_factor); + m_configGroup.sync(); +} + +ZoomController::ZoomController(const KConfigGroup& configGroup, + QObject* parent) + : QObject(parent) + , d{new ZoomControllerPrivate(configGroup)} +{ +} + +ZoomController::~ZoomController() +{ +} + +double ZoomController::factor() const +{ + return d->m_factor; +} + +void ZoomController::setFactor(double factor) +{ + factor = qBound(0.1, factor, 10.0); + if (d->m_factor == factor) { + return; + } + d->m_factor = factor; + d->writeConfig(); + emit factorChanged(d->m_factor); +} + +void ZoomController::zoomBy(double scale) +{ + setFactor(d->m_factor * multiplier(scale)); +} + +bool ZoomController::handleWheelEvent(QWheelEvent* event) +{ + if (Q_UNLIKELY(!event)) { + qCWarning(UTIL) << "ZoomController::handleWheelEvent: null event."; + return false; + } + if (!(event->modifiers() & Qt::ControlModifier)) { + return false; + } + + constexpr double minStandardDelta{120}; + const QPoint delta = event->angleDelta(); + const double scale{(delta.x() + delta.y()) / minStandardDelta}; + + zoomBy(scale); + event->accept(); + return true; +} + +void ZoomController::zoomIn() +{ + setFactor(d->m_factor * defaultMultiplier); +} + +void ZoomController::zoomOut() +{ + setFactor(d->m_factor / defaultMultiplier); +} + +void ZoomController::resetZoom() +{ + setFactor(defaultFactor); +} + +#include "moc_zoomcontroller.cpp"