File indexing completed on 2024-04-28 11:21:06
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2012 Martin Kuettler <martin.kuettler@gmail.com> 0004 SPDX-FileCopyrightText: 2018-2022 Alexander Semke <alexander.semke@web.de> 0005 */ 0006 0007 #include "worksheetview.h" 0008 #include "worksheet.h" 0009 0010 #include <QApplication> 0011 #include <QFocusEvent> 0012 #include <QParallelAnimationGroup> 0013 #include <QPropertyAnimation> 0014 #include <QTimeLine> 0015 #include <QScrollBar> 0016 0017 WorksheetView::WorksheetView(Worksheet* scene, QWidget* parent) : QGraphicsView(scene, parent), 0018 m_worksheet(scene) 0019 { 0020 connect(scene, SIGNAL(sceneRectChanged(QRectF)), 0021 this, SLOT(sceneRectChanged(QRectF))); 0022 setAlignment(Qt::AlignLeft | Qt::AlignTop); 0023 //setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0024 setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); 0025 } 0026 0027 void WorksheetView::makeVisible(const QRectF& sceneRect) 0028 { 0029 const qreal w = viewport()->width(); 0030 const qreal h = viewport()->height(); 0031 0032 QRectF rect(m_scale*sceneRect.topLeft(), m_scale*sceneRect.size()); 0033 0034 qreal x,y; 0035 if (m_animation) { 0036 x = m_hAnimation->endValue().toReal(); 0037 y = m_vAnimation->endValue().toReal(); 0038 0039 if (QRectF(x,y,w,h).contains(rect)) 0040 return; 0041 } 0042 0043 if (horizontalScrollBar()) 0044 x = horizontalScrollBar()->value(); 0045 else 0046 x = 0; 0047 if (verticalScrollBar()) 0048 y = verticalScrollBar()->value(); 0049 else 0050 y = 0; 0051 0052 if (!m_animation && QRectF(x,y,w,h).contains(rect)) 0053 return; 0054 0055 qreal nx, ny; 0056 if (y > rect.y() || rect.height() > h) 0057 ny = rect.y(); 0058 else 0059 ny = rect.y() + rect.height() - h; 0060 if (rect.x() + rect.width() <= w || x > rect.x()) 0061 nx = 0; 0062 else 0063 nx = rect.x() + rect.width() - w; 0064 0065 if (!m_worksheet->animationsEnabled()) { 0066 if (horizontalScrollBar()) 0067 horizontalScrollBar()->setValue(nx); 0068 if (verticalScrollBar()) 0069 verticalScrollBar()->setValue(ny); 0070 return; 0071 } 0072 0073 if (!m_animation) 0074 m_animation = new QParallelAnimationGroup(this); 0075 0076 if (horizontalScrollBar()) { 0077 if (!m_hAnimation) { 0078 m_hAnimation = new QPropertyAnimation(horizontalScrollBar(), 0079 "value", this); 0080 m_hAnimation->setStartValue(horizontalScrollBar()->value()); 0081 nx = qBound(qreal(0.0), nx, qreal(0.0+horizontalScrollBar()->maximum())); 0082 m_hAnimation->setEndValue(nx); 0083 m_hAnimation->setDuration(100); 0084 m_animation->addAnimation(m_hAnimation); 0085 } else { 0086 qreal progress = static_cast<qreal>(m_hAnimation->currentTime()) / 0087 m_hAnimation->totalDuration(); 0088 QEasingCurve curve = m_hAnimation->easingCurve(); 0089 qreal value = curve.valueForProgress(progress); 0090 qreal sx = 1/(1-value)*(m_hAnimation->currentValue().toReal() - 0091 value * nx); 0092 m_hAnimation->setStartValue(sx); 0093 m_hAnimation->setEndValue(nx); 0094 } 0095 } else { 0096 m_hAnimation = nullptr; 0097 } 0098 0099 if (verticalScrollBar()) { 0100 if (!m_vAnimation) { 0101 m_vAnimation = new QPropertyAnimation(verticalScrollBar(), 0102 "value", this); 0103 m_vAnimation->setStartValue(verticalScrollBar()->value()); 0104 ny = qBound(qreal(0.0), ny, qreal(0.0+verticalScrollBar()->maximum())); 0105 m_vAnimation->setEndValue(ny); 0106 m_vAnimation->setDuration(100); 0107 m_animation->addAnimation(m_vAnimation); 0108 } else { 0109 qreal progress = static_cast<qreal>(m_vAnimation->currentTime()) / 0110 m_vAnimation->totalDuration(); 0111 QEasingCurve curve = m_vAnimation->easingCurve(); 0112 qreal value = curve.valueForProgress(progress); 0113 qreal sy = 1/(1-value)*(m_vAnimation->currentValue().toReal() - 0114 value * ny); 0115 m_vAnimation->setStartValue(sy); 0116 m_vAnimation->setEndValue(ny); 0117 } 0118 } else { 0119 m_vAnimation = nullptr; 0120 } 0121 0122 connect(m_animation, &QParallelAnimationGroup::finished, this, &WorksheetView::endAnimation); 0123 m_animation->start(); 0124 } 0125 0126 void WorksheetView::scrollTo(int y) 0127 { 0128 if (!verticalScrollBar()) 0129 return; 0130 0131 qreal dy = y - verticalScrollBar()->value(); 0132 scrollBy(dy); 0133 } 0134 0135 0136 bool WorksheetView::isVisible(const QRectF& sceneRect) const 0137 { 0138 const qreal w = viewport()->width(); 0139 const qreal h = viewport()->height(); 0140 0141 QRectF rect(m_scale*sceneRect.topLeft(), m_scale*sceneRect.size()); 0142 0143 qreal x,y; 0144 if (m_animation) { 0145 x = m_hAnimation->endValue().toReal(); 0146 y = m_vAnimation->endValue().toReal(); 0147 } else { 0148 if (horizontalScrollBar()) 0149 x = horizontalScrollBar()->value(); 0150 else 0151 x = 0; 0152 if (verticalScrollBar()) 0153 y = verticalScrollBar()->value(); 0154 else 0155 y = 0; 0156 } 0157 0158 return QRectF(x,y,w,h).contains(rect); 0159 } 0160 0161 bool WorksheetView::isAtEnd() const 0162 { 0163 bool atEnd = true; 0164 if (verticalScrollBar()) 0165 atEnd &= (verticalScrollBar()->value()==verticalScrollBar()->maximum()); 0166 return atEnd; 0167 } 0168 0169 void WorksheetView::scrollToEnd() const 0170 { 0171 if (verticalScrollBar()) 0172 verticalScrollBar()->setValue(verticalScrollBar()->maximum()); 0173 } 0174 0175 void WorksheetView::scrollBy(int dy) 0176 { 0177 if (!verticalScrollBar()) 0178 return; 0179 0180 int ny = verticalScrollBar()->value() + dy; 0181 if (ny < 0) 0182 ny = 0; 0183 else if (ny > verticalScrollBar()->maximum()) 0184 ny = verticalScrollBar()->maximum(); 0185 0186 int x; 0187 if (horizontalScrollBar()) 0188 x = horizontalScrollBar()->value(); 0189 else 0190 x = 0; 0191 0192 const qreal w = viewport()->width() / m_scale; 0193 const qreal h = viewport()->height() / m_scale; 0194 makeVisible(QRectF(x, ny, w, h)); 0195 } 0196 0197 void WorksheetView::endAnimation() 0198 { 0199 if (!m_animation) 0200 return; 0201 0202 m_animation->deleteLater(); 0203 m_hAnimation = nullptr; 0204 m_vAnimation = nullptr; 0205 m_animation = nullptr; 0206 } 0207 0208 QPoint WorksheetView::viewCursorPos() const 0209 { 0210 return viewport()->mapFromGlobal(QCursor::pos()); 0211 } 0212 0213 QPointF WorksheetView::sceneCursorPos() const 0214 { 0215 return mapToScene(viewCursorPos()); 0216 } 0217 0218 QRectF WorksheetView::viewRect() const 0219 { 0220 const qreal w = viewport()->width() / m_scale; 0221 const qreal h = viewport()->height() / m_scale; 0222 qreal y = verticalScrollBar()->value(); 0223 qreal x = horizontalScrollBar() ? horizontalScrollBar()->value() : 0; 0224 return QRectF(x, y, w, h); 0225 } 0226 0227 void WorksheetView::resizeEvent(QResizeEvent* event) 0228 { 0229 QGraphicsView::resizeEvent(event); 0230 updateSceneSize(); 0231 } 0232 0233 void WorksheetView::focusInEvent(QFocusEvent* event) 0234 { 0235 QGraphicsView::focusInEvent(event); 0236 m_worksheet->resumeAnimations(); 0237 } 0238 0239 void WorksheetView::focusOutEvent(QFocusEvent* event) 0240 { 0241 QGraphicsView::focusOutEvent(event); 0242 if (!scene()->hasFocus()) 0243 m_worksheet->stopAnimations(); 0244 } 0245 0246 void WorksheetView::wheelEvent(QWheelEvent* event) 0247 { 0248 if ((QApplication::keyboardModifiers() & Qt::ControlModifier)) { 0249 //https://wiki.qt.io/Smooth_Zoom_In_QGraphicsView 0250 QPoint numDegrees = event->angleDelta() / 8; 0251 int numSteps = numDegrees.y() / 15; // see QWheelEvent documentation 0252 zoom(numSteps); 0253 } else 0254 QGraphicsView::wheelEvent(event); 0255 } 0256 0257 void WorksheetView::zoom(int numSteps) 0258 { 0259 m_numScheduledScalings += numSteps; 0260 if (m_numScheduledScalings * numSteps < 0) // if user moved the wheel in another direction, we reset previously scheduled scalings 0261 m_numScheduledScalings = numSteps; 0262 0263 auto* anim = new QTimeLine(350, this); 0264 anim->setUpdateInterval(20); 0265 0266 connect(anim, &QTimeLine::valueChanged, this, &WorksheetView::scalingTime); 0267 connect(anim, &QTimeLine::finished, this, &WorksheetView::animFinished); 0268 anim->start(); 0269 } 0270 0271 void WorksheetView::scalingTime() 0272 { 0273 qreal factor = 1.0 + qreal(m_numScheduledScalings) / 300.0; 0274 m_scale *= factor; 0275 updateSceneSize(); 0276 scale(factor, factor); 0277 } 0278 0279 void WorksheetView::animFinished() 0280 { 0281 if (m_numScheduledScalings > 0) 0282 m_numScheduledScalings--; 0283 else 0284 m_numScheduledScalings++; 0285 sender()->~QObject(); 0286 emit scaleFactorChanged(m_scale); 0287 } 0288 0289 qreal WorksheetView::scaleFactor() const 0290 { 0291 return m_scale; 0292 } 0293 0294 void WorksheetView::setScaleFactor(qreal zoom, bool emitSignal) 0295 { 0296 scale(1/m_scale * zoom, 1/m_scale * zoom); 0297 m_scale = zoom; 0298 updateSceneSize(); 0299 if (emitSignal) 0300 emit scaleFactorChanged(m_scale); 0301 } 0302 0303 void WorksheetView::updateSceneSize() 0304 { 0305 QSize s = viewport()->size(); 0306 m_worksheet->setViewSize(s.width()/m_scale, s.height()/m_scale, m_scale); 0307 sendViewRectChange(); 0308 } 0309 0310 void WorksheetView::sceneRectChanged(const QRectF& sceneRect) const 0311 { 0312 Q_UNUSED(sceneRect); 0313 if (verticalScrollBar()) 0314 connect(verticalScrollBar(), SIGNAL(valueChanged(int)), 0315 this, SLOT(sendViewRectChange()), Qt::UniqueConnection); 0316 if (horizontalScrollBar()) 0317 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), 0318 this, SLOT(sendViewRectChange()), Qt::UniqueConnection); 0319 } 0320 0321 void WorksheetView::sendViewRectChange() const 0322 { 0323 emit viewRectChanged(viewRect()); 0324 } 0325 0326 void WorksheetView::zoomIn() 0327 { 0328 m_scale *= 1.1; 0329 scale(1.1, 1.1); 0330 updateSceneSize(); 0331 emit scaleFactorChanged(m_scale); 0332 } 0333 0334 void WorksheetView::zoomOut() 0335 { 0336 m_scale /= 1.1; 0337 scale(1/1.1, 1/1.1); 0338 updateSceneSize(); 0339 emit scaleFactorChanged(m_scale); 0340 } 0341 0342 void WorksheetView::actualSize() 0343 { 0344 scale (1/m_scale, 1/m_scale); 0345 m_scale = 1.0; 0346 updateSceneSize(); 0347 emit scaleFactorChanged(m_scale); 0348 }