1 /*
2     SPDX-FileCopyrightText: 2016 Igor Kushnir <igorkuo@gmail.com>
3 
4     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5 */
6 
7 #include "zoomcontroller.h"
8 
9 #include <KConfigGroup>
10 
11 #include <QPoint>
12 #include <QKeyEvent>
13 #include <QWheelEvent>
14 
15 #include <cmath>
16 
17 namespace {
18 
factorConfigEntryKey()19 constexpr const char* factorConfigEntryKey() { return "Zoom Factor"; }
20 
21 constexpr double defaultFactor{1};
22 constexpr double defaultMultiplier{1.1};
23 
multiplier(double scale)24 double multiplier(double scale)
25 {
26     // Allow finer-grained control over zoom factor compared to defaultMultiplier
27     constexpr double smallMultiplier{1.05};
28     return std::pow(smallMultiplier, scale);
29 }
30 
31 } // namespace
32 
33 using KDevelop::ZoomControllerPrivate;
34 using KDevelop::ZoomController;
35 
36 class KDevelop::ZoomControllerPrivate
37 {
38 public:
39     explicit ZoomControllerPrivate(const KConfigGroup& configGroup);
40     void writeConfig();
41 
42     KConfigGroup m_configGroup;
43     double m_factor{defaultFactor};
44 };
45 
ZoomControllerPrivate(const KConfigGroup & configGroup)46 ZoomControllerPrivate::ZoomControllerPrivate(const KConfigGroup& configGroup)
47     : m_configGroup{configGroup}
48 {
49     m_factor = m_configGroup.readEntry(factorConfigEntryKey(), defaultFactor);
50 }
51 
writeConfig()52 void ZoomControllerPrivate::writeConfig()
53 {
54     m_configGroup.writeEntry(factorConfigEntryKey(), m_factor);
55     m_configGroup.sync();
56 }
57 
ZoomController(const KConfigGroup & configGroup,QObject * parent)58 ZoomController::ZoomController(const KConfigGroup& configGroup, QObject* parent)
59     : QObject(parent)
60     , d_ptr{new ZoomControllerPrivate(configGroup)}
61 {
62 }
63 
64 ZoomController::~ZoomController() = default;
65 
factor() const66 double ZoomController::factor() const
67 {
68     Q_D(const ZoomController);
69 
70     return d->m_factor;
71 }
72 
setFactor(double factor)73 void ZoomController::setFactor(double factor)
74 {
75     Q_D(ZoomController);
76 
77     factor = qBound(0.1, factor, 10.0);
78     if (d->m_factor == factor) {
79         return;
80     }
81     d->m_factor = factor;
82     d->writeConfig();
83     emit factorChanged(d->m_factor);
84 }
85 
zoomBy(double scale)86 void ZoomController::zoomBy(double scale)
87 {
88     Q_D(ZoomController);
89 
90     setFactor(d->m_factor * multiplier(scale));
91 }
92 
handleKeyPressEvent(QKeyEvent * event)93 bool ZoomController::handleKeyPressEvent(QKeyEvent* event)
94 {
95     Q_ASSERT(event);
96 
97     const auto requiredModifiers = Qt::ControlModifier;
98     if (!(event->modifiers() & requiredModifiers)) {
99         return false;
100     }
101     // Qt::ShiftModifier is required for the 0 key in some keyboard layouts
102     // (such as Programmer Dvorak). Allow Qt::KeypadModifier to support numpad keys.
103     // Don't allow other modifiers, such as Alt and Meta, to minimize shortcut conflicts.
104     const auto allowedModifiers = Qt::ControlModifier | Qt::ShiftModifier | Qt::KeypadModifier;
105     if (event->modifiers() & ~allowedModifiers) {
106         return false;
107     }
108 
109     if (event->key() == Qt::Key_0) {
110         resetZoom();
111         event->accept();
112         return true;
113     }
114 
115     return false;
116 }
117 
handleWheelEvent(QWheelEvent * event)118 bool ZoomController::handleWheelEvent(QWheelEvent* event)
119 {
120     Q_ASSERT(event);
121 
122     if (!(event->modifiers() & Qt::ControlModifier)) {
123         return false;
124     }
125 
126     constexpr double minStandardDelta{120};
127     const QPoint delta = event->angleDelta();
128     const double scale{(delta.x() + delta.y()) / minStandardDelta};
129 
130     zoomBy(scale);
131     event->accept();
132     return true;
133 }
134 
zoomIn()135 void ZoomController::zoomIn()
136 {
137     Q_D(ZoomController);
138 
139     setFactor(d->m_factor * defaultMultiplier);
140 }
141 
zoomOut()142 void ZoomController::zoomOut()
143 {
144     Q_D(ZoomController);
145 
146     setFactor(d->m_factor / defaultMultiplier);
147 }
148 
resetZoom()149 void ZoomController::resetZoom()
150 {
151     setFactor(defaultFactor);
152 }
153