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