File indexing completed on 2024-04-28 07:46:50

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