File indexing completed on 2024-04-28 15:30:59

0001 /*
0002     SPDX-FileCopyrightText: 2013-2018 Dominik Haumann <dhaumann@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "katetextanimation.h"
0008 
0009 #include "katedocument.h"
0010 #include "katerenderer.h"
0011 #include "kateview.h"
0012 #include "kateviewinternal.h"
0013 
0014 #include <QPainter>
0015 #include <QPointF>
0016 #include <QRect>
0017 #include <QSizeF>
0018 #include <QTimeLine>
0019 
0020 KateTextAnimation::KateTextAnimation(KTextEditor::Range range, KTextEditor::Attribute::Ptr attribute, KateViewInternal *view)
0021     : QObject(view)
0022     , m_range(range)
0023     , m_attribute(std::move(attribute))
0024     , m_doc(view->view()->doc())
0025     , m_view(view)
0026     , m_timeLine(new QTimeLine(250, this))
0027     , m_value(0.0)
0028 {
0029     m_text = view->view()->doc()->text(range);
0030 
0031     connect(m_timeLine, &QTimeLine::valueChanged, this, &KateTextAnimation::nextFrame);
0032     connect(m_timeLine, &QTimeLine::finished, this, &KateTextAnimation::deleteLater);
0033 
0034     m_timeLine->setEasingCurve(QEasingCurve::SineCurve);
0035     m_timeLine->start();
0036 
0037     QObject::connect(view, &KTextEditor::View::destroyed, m_timeLine, &QTimeLine::stop);
0038 }
0039 
0040 KateTextAnimation::~KateTextAnimation()
0041 {
0042     // if still running, we need to update the view a last time to avoid artifacts
0043     if (m_timeLine->state() == QTimeLine::Running) {
0044         m_timeLine->stop();
0045         nextFrame(0.0);
0046     }
0047 }
0048 
0049 QRectF KateTextAnimation::rectForText()
0050 {
0051     const QFontMetricsF fm = m_view->view()->renderer()->currentFontMetrics();
0052     const int lineHeight = m_view->view()->renderer()->lineHeight();
0053     QPoint pixelPos = m_view->cursorToCoordinate(m_range.start(), /*bool realCursor*/ true, /*bool includeBorder*/ false);
0054 
0055     if (pixelPos.x() == -1 || pixelPos.y() == -1) {
0056         return QRectF();
0057     } else {
0058         QRectF rect(pixelPos.x(), pixelPos.y(), fm.boundingRect(m_view->view()->doc()->text(m_range)).width(), lineHeight);
0059         const QPointF center = rect.center();
0060         const qreal factor = 1.0 + 0.5 * m_value;
0061         rect.setWidth(rect.width() * factor);
0062         rect.setHeight(rect.height() * factor);
0063         rect.moveCenter(center);
0064         return rect;
0065     }
0066 }
0067 
0068 void KateTextAnimation::draw(QPainter &painter)
0069 {
0070     // could happen in corner cases: timeLine emitted finished(), but this object
0071     // is not yet deleted. Therefore, draw() might be called in paintEvent().
0072     if (m_timeLine->state() == QTimeLine::NotRunning) {
0073         return;
0074     }
0075 
0076     // get current rect and fill background
0077     QRectF rect = rectForText();
0078     painter.fillRect(rect, m_attribute->background());
0079 
0080     // scale font with animation
0081     QFont f = m_view->view()->renderer()->currentFont();
0082     f.setBold(m_attribute->fontBold());
0083     f.setPointSizeF(f.pointSizeF() * (1.0 + 0.5 * m_value));
0084     painter.setFont(f);
0085 
0086     painter.setPen(m_attribute->foreground().color());
0087     // finally draw contents on the view
0088     painter.drawText(rect, m_text);
0089 }
0090 
0091 void KateTextAnimation::nextFrame(qreal value)
0092 {
0093     // cache previous rect for update
0094     const QRectF prevRect = rectForText();
0095 
0096     m_value = value;
0097 
0098     // next rect is used to draw the text
0099     const QRectF nextRect = rectForText();
0100 
0101     // due to rounding errors, increase the rect 1px to avoid artifacts
0102     const QRect updateRect = nextRect.united(prevRect).adjusted(-1, -1, 1, 1).toRect();
0103 
0104     // request repaint
0105     m_view->update(updateRect);
0106 }
0107 
0108 #include "moc_katetextanimation.cpp"