1/*
2 SPDX-FileCopyrightText: 2013-2018 Dominik Haumann <dhaumann@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "katetextanimation.h"
8
9#include "katerenderer.h"
10#include "kateview.h"
11#include "kateviewinternal.h"
12#include <ktexteditor/document.h>
13
14#include <QPainter>
15#include <QPointF>
16#include <QRect>
17#include <QSizeF>
18#include <QTimeLine>
19
20KateTextAnimation::KateTextAnimation(KTextEditor::Range range, KTextEditor::Attribute::Ptr attribute, KateViewInternal *view)
21 : QObject(view)
22 , m_range(range)
23 , m_text(view->view()->document()->text(range))
24 , m_attribute(std::move(attribute))
25 , m_doc(view->view()->doc())
26 , m_view(view)
27 , m_timeLine(new QTimeLine(250, this))
28 , m_value(0.0)
29{
30 connect(sender: m_timeLine, signal: &QTimeLine::valueChanged, context: this, slot: &KateTextAnimation::nextFrame);
31 connect(sender: m_timeLine, signal: &QTimeLine::finished, context: this, slot: &KateTextAnimation::deleteLater);
32
33 m_timeLine->setEasingCurve(QEasingCurve::SineCurve);
34 m_timeLine->start();
35
36 QObject::connect(sender: view, signal: &KTextEditor::View::destroyed, context: m_timeLine, slot: &QTimeLine::stop);
37}
38
39KateTextAnimation::~KateTextAnimation()
40{
41 // if still running, we need to update the view a last time to avoid artifacts
42 if (m_timeLine->state() == QTimeLine::Running) {
43 m_timeLine->stop();
44 nextFrame(value: 0.0);
45 }
46}
47
48QRectF KateTextAnimation::rectForText()
49{
50 const QFontMetricsF fm = m_view->view()->renderer()->currentFontMetrics();
51 const int lineHeight = m_view->view()->renderer()->lineHeight();
52 QPoint pixelPos = m_view->cursorToCoordinate(cursor: m_range.start(), /*bool realCursor*/ realCursor: true, /*bool includeBorder*/ includeBorder: false);
53
54 if (pixelPos.x() == -1 || pixelPos.y() == -1) {
55 return QRectF();
56 } else {
57 QRectF rect(pixelPos.x(), pixelPos.y(), fm.boundingRect(string: m_view->view()->document()->text(range: m_range)).width(), lineHeight);
58 const QPointF center = rect.center();
59 const qreal factor = 1.0 + 0.5 * m_value;
60 rect.setWidth(rect.width() * factor);
61 rect.setHeight(rect.height() * factor);
62 rect.moveCenter(p: center);
63 return rect;
64 }
65}
66
67void KateTextAnimation::draw(QPainter &painter)
68{
69 // could happen in corner cases: timeLine emitted finished(), but this object
70 // is not yet deleted. Therefore, draw() might be called in paintEvent().
71 if (m_timeLine->state() == QTimeLine::NotRunning) {
72 return;
73 }
74
75 // get current rect and fill background
76 QRectF rect = rectForText();
77 painter.fillRect(rect, m_attribute->background());
78
79 // scale font with animation
80 QFont f = m_view->view()->renderer()->currentFont();
81 f.setBold(m_attribute->fontBold());
82 f.setPointSizeF(f.pointSizeF() * (1.0 + 0.5 * m_value));
83 painter.setFont(f);
84
85 painter.setPen(m_attribute->foreground().color());
86 // finally draw contents on the view
87 painter.drawText(r: rect, text: m_text);
88}
89
90void KateTextAnimation::nextFrame(qreal value)
91{
92 // cache previous rect for update
93 const QRectF prevRect = rectForText();
94
95 m_value = value;
96
97 // next rect is used to draw the text
98 const QRectF nextRect = rectForText();
99
100 // due to rounding errors, increase the rect 1px to avoid artifacts
101 const QRect updateRect = nextRect.united(r: prevRect).adjusted(xp1: -1, yp1: -1, xp2: 1, yp2: 1).toRect();
102
103 // request repaint
104 m_view->update(updateRect);
105}
106

source code of ktexteditor/src/view/katetextanimation.cpp