1 | /* |
2 | SPDX-FileCopyrightText: 2009 Sebastian Trueg <trueg@kde.org> |
3 | |
4 | SPDX-License-Identifier: LGPL-2.1-or-later |
5 | */ |
6 | |
7 | #include "kpixmapsequenceoverlaypainter.h" |
8 | #include "kpixmapsequence.h" |
9 | |
10 | #include <QCoreApplication> |
11 | #include <QEvent> |
12 | #include <QPainter> |
13 | #include <QPointer> |
14 | #include <QRect> |
15 | #include <QTimer> |
16 | #include <QWidget> |
17 | |
18 | class KPixmapSequenceOverlayPainterPrivate |
19 | { |
20 | public: |
21 | void init(KPixmapSequenceOverlayPainter *p); |
22 | void timeout(); |
23 | void paintFrame(); |
24 | |
25 | KPixmapSequence &sequence(); |
26 | |
27 | QRect pixmapRect(); |
28 | |
29 | KPixmapSequence m_sequence; |
30 | QPointer<QWidget> m_widget; |
31 | Qt::Alignment m_alignment; |
32 | QPoint m_offset; |
33 | QRect m_rect; |
34 | |
35 | QTimer m_timer; |
36 | int m_counter; |
37 | |
38 | bool m_started; |
39 | |
40 | KPixmapSequenceOverlayPainter *q; |
41 | }; |
42 | |
43 | void KPixmapSequenceOverlayPainterPrivate::init(KPixmapSequenceOverlayPainter *p) |
44 | { |
45 | q = p; |
46 | m_widget = nullptr; |
47 | m_alignment = Qt::AlignCenter; |
48 | m_started = false; |
49 | q->setInterval(200); |
50 | QObject::connect(sender: &m_timer, signal: &QTimer::timeout, context: q, slot: [this]() { |
51 | timeout(); |
52 | }); |
53 | } |
54 | |
55 | void KPixmapSequenceOverlayPainterPrivate::timeout() |
56 | { |
57 | if (sequence().isEmpty()) { |
58 | return; |
59 | } |
60 | ++m_counter; |
61 | m_counter %= sequence().frameCount(); |
62 | if (m_widget) { |
63 | m_widget->update(pixmapRect()); |
64 | } |
65 | } |
66 | |
67 | void KPixmapSequenceOverlayPainterPrivate::paintFrame() |
68 | { |
69 | if (m_counter >= sequence().frameCount()) { |
70 | return; |
71 | } |
72 | QPainter p(m_widget); |
73 | p.drawPixmap(targetRect: pixmapRect(), pixmap: sequence().frameAt(index: m_counter), sourceRect: QRect(QPoint(0, 0), sequence().frameSize())); |
74 | } |
75 | |
76 | KPixmapSequence &KPixmapSequenceOverlayPainterPrivate::sequence() |
77 | { |
78 | return m_sequence; |
79 | } |
80 | |
81 | QRect KPixmapSequenceOverlayPainterPrivate::pixmapRect() |
82 | { |
83 | QRect rect(m_rect); |
84 | if (!rect.isValid()) { |
85 | rect = m_widget->rect(); |
86 | } |
87 | |
88 | QPoint pos(rect.topLeft()); |
89 | if (m_alignment & Qt::AlignHCenter) { |
90 | pos.setX(rect.center().x() - (sequence().frameSize().width() / 2)); |
91 | } else if (m_alignment & Qt::AlignRight) { |
92 | pos.setX(rect.right() - sequence().frameSize().width()); |
93 | } |
94 | |
95 | if (m_alignment & Qt::AlignVCenter) { |
96 | pos.setY(rect.center().y() - (sequence().frameSize().height() / 2)); |
97 | } else if (m_alignment & Qt::AlignBottom) { |
98 | pos.setY(rect.bottom() - sequence().frameSize().height()); |
99 | } |
100 | |
101 | pos += m_offset; |
102 | |
103 | return QRect(pos, sequence().frameSize()); |
104 | } |
105 | |
106 | KPixmapSequenceOverlayPainter::KPixmapSequenceOverlayPainter(QObject *parent) |
107 | : QObject(parent) |
108 | , d(new KPixmapSequenceOverlayPainterPrivate) |
109 | { |
110 | d->init(p: this); |
111 | } |
112 | |
113 | KPixmapSequenceOverlayPainter::KPixmapSequenceOverlayPainter(const KPixmapSequence &seq, QObject *parent) |
114 | : QObject(parent) |
115 | , d(new KPixmapSequenceOverlayPainterPrivate) |
116 | { |
117 | d->init(p: this); |
118 | d->m_sequence = seq; |
119 | } |
120 | |
121 | KPixmapSequenceOverlayPainter::~KPixmapSequenceOverlayPainter() |
122 | { |
123 | stop(); |
124 | } |
125 | |
126 | KPixmapSequence KPixmapSequenceOverlayPainter::sequence() const |
127 | { |
128 | return d->sequence(); |
129 | } |
130 | |
131 | int KPixmapSequenceOverlayPainter::interval() const |
132 | { |
133 | return d->m_timer.interval(); |
134 | } |
135 | |
136 | QRect KPixmapSequenceOverlayPainter::rect() const |
137 | { |
138 | if (d->m_rect.isValid()) { |
139 | return d->m_rect; |
140 | } else if (d->m_widget) { |
141 | return d->m_widget->rect(); |
142 | } else { |
143 | return QRect(); |
144 | } |
145 | } |
146 | |
147 | Qt::Alignment KPixmapSequenceOverlayPainter::alignment() const |
148 | { |
149 | return d->m_alignment; |
150 | } |
151 | |
152 | QPoint KPixmapSequenceOverlayPainter::offset() const |
153 | { |
154 | return d->m_offset; |
155 | } |
156 | |
157 | void KPixmapSequenceOverlayPainter::setSequence(const KPixmapSequence &seq) |
158 | { |
159 | bool restart = d->m_started; |
160 | stop(); |
161 | d->m_sequence = seq; |
162 | if (restart) { |
163 | start(); |
164 | } |
165 | } |
166 | |
167 | void KPixmapSequenceOverlayPainter::setInterval(int msecs) |
168 | { |
169 | d->m_timer.setInterval(msecs); |
170 | } |
171 | |
172 | void KPixmapSequenceOverlayPainter::setWidget(QWidget *w) |
173 | { |
174 | stop(); |
175 | d->m_widget = w; |
176 | } |
177 | |
178 | void KPixmapSequenceOverlayPainter::setRect(const QRect &rect) |
179 | { |
180 | bool restart = d->m_started; |
181 | stop(); |
182 | d->m_rect = rect; |
183 | if (restart) { |
184 | start(); |
185 | } |
186 | } |
187 | |
188 | void KPixmapSequenceOverlayPainter::setAlignment(Qt::Alignment align) |
189 | { |
190 | bool restart = d->m_started; |
191 | stop(); |
192 | d->m_alignment = align; |
193 | if (restart) { |
194 | start(); |
195 | } |
196 | } |
197 | |
198 | void KPixmapSequenceOverlayPainter::setOffset(const QPoint &offset) |
199 | { |
200 | bool restart = d->m_started; |
201 | stop(); |
202 | d->m_offset = offset; |
203 | if (restart) { |
204 | start(); |
205 | } |
206 | } |
207 | |
208 | void KPixmapSequenceOverlayPainter::start() |
209 | { |
210 | if (d->m_widget) { |
211 | stop(); |
212 | |
213 | d->m_counter = 0; |
214 | d->m_started = true; |
215 | d->m_widget->installEventFilter(filterObj: this); |
216 | if (d->m_widget->isVisible()) { |
217 | d->m_timer.start(); |
218 | d->m_widget->update(d->pixmapRect()); |
219 | } |
220 | } |
221 | } |
222 | |
223 | void KPixmapSequenceOverlayPainter::stop() |
224 | { |
225 | d->m_timer.stop(); |
226 | if (d->m_widget && d->m_started) { |
227 | d->m_started = false; |
228 | d->m_widget->removeEventFilter(obj: this); |
229 | d->m_widget->update(d->pixmapRect()); |
230 | } |
231 | } |
232 | |
233 | bool KPixmapSequenceOverlayPainter::eventFilter(QObject *obj, QEvent *event) |
234 | { |
235 | if (obj == d->m_widget) { |
236 | switch (event->type()) { |
237 | case QEvent::Paint: |
238 | // make sure we paint after everyone else including other event filters |
239 | obj->removeEventFilter(obj: this); // don't recourse... |
240 | QCoreApplication::sendEvent(receiver: obj, event); |
241 | d->paintFrame(); |
242 | obj->installEventFilter(filterObj: this); // catch on... |
243 | return true; |
244 | break; |
245 | case QEvent::Hide: |
246 | d->m_timer.stop(); |
247 | break; |
248 | case QEvent::Show: |
249 | if (d->m_started) { |
250 | d->m_timer.start(); |
251 | d->m_widget->update(d->pixmapRect()); |
252 | } |
253 | break; |
254 | default: |
255 | break; |
256 | } |
257 | } |
258 | |
259 | return false; |
260 | } |
261 | |
262 | #include "moc_kpixmapsequenceoverlaypainter.cpp" |
263 | |