1 /***************************************************************************
2 * Copyright 2010 Stefan Majewsky <majewsky@gmx.net> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU Library General Public License *
6 * version 2 as published by the Free Software Foundation *
7 * *
8 * This program is distributed in the hope that it will be useful, *
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
11 * GNU Library General Public License for more details. *
12 * *
13 * You should have received a copy of the GNU Library General Public *
14 * License along with this program; if not, write to the *
15 * Free Software Foundation, Inc., *
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
17 ***************************************************************************/
18
19 #include "board.h"
20 #include "spriteobjectitem.h"
21
22 #include <QBasicTimer>
23 #include <QTimerEvent>
24 #include <QApplication>
25 #include <QGraphicsScene>
26
27 struct Tagaro::Board::Private
28 {
29 Tagaro::Board* m_board;
30
31 Qt::Alignment m_alignment;
32 QSizeF m_logicalSize, m_size;
33 qreal m_physicalSizeFactor;
34 QPointF m_renderSizeFactor;
35
36 QList<Tagaro::SpriteObjectItem*> m_items;
37 QList<QGraphicsItem*> m_pendingItems;
38 QBasicTimer m_pendingItemsTimer;
39
40 void _k_update();
41 inline void update(Tagaro::SpriteObjectItem* item);
42 void _k_updateItem();
43
PrivateTagaro::Board::Private44 Private(Tagaro::Board* board) : m_board(board), m_alignment(Qt::AlignCenter), m_logicalSize(1, 1), m_size(1, 1), m_physicalSizeFactor(1), m_renderSizeFactor(1, 1) {}
45 };
46
Board(QGraphicsItem * parent)47 Tagaro::Board::Board(QGraphicsItem* parent)
48 : QGraphicsObject(parent)
49 , d(new Private(this))
50 {
51 setFlag(QGraphicsItem::ItemHasNoContents); //so do not call paint()
52 if (parent)
53 {
54 d->_k_update();
55 }
56 }
57
~Board()58 Tagaro::Board::~Board()
59 {
60 delete d;
61 }
62
logicalSize() const63 QSizeF Tagaro::Board::logicalSize() const
64 {
65 return d->m_logicalSize;
66 }
67
setLogicalSize(const QSizeF & size)68 void Tagaro::Board::setLogicalSize(const QSizeF& size)
69 {
70 if (size.isValid() && d->m_logicalSize != size)
71 {
72 d->m_logicalSize = size;
73 d->_k_update();
74 }
75 }
76
size() const77 QSizeF Tagaro::Board::size() const
78 {
79 return d->m_size;
80 }
81
setSize(const QSizeF & size)82 void Tagaro::Board::setSize(const QSizeF& size)
83 {
84 if (size.isValid() && d->m_size != size)
85 {
86 d->m_size = size;
87 d->m_alignment = Qt::Alignment();
88 d->_k_update();
89 }
90 }
91
physicalSizeFactor() const92 qreal Tagaro::Board::physicalSizeFactor() const
93 {
94 return d->m_physicalSizeFactor;
95 }
96
setPhysicalSizeFactor(qreal physicalSizeFactor)97 void Tagaro::Board::setPhysicalSizeFactor(qreal physicalSizeFactor)
98 {
99 if (physicalSizeFactor > 0.0 && d->m_physicalSizeFactor != physicalSizeFactor)
100 {
101 d->m_physicalSizeFactor = physicalSizeFactor;
102 d->_k_update();
103 }
104 }
105
alignment() const106 Qt::Alignment Tagaro::Board::alignment() const
107 {
108 return d->m_alignment;
109 }
110
setAlignment(Qt::Alignment alignment)111 void Tagaro::Board::setAlignment(Qt::Alignment alignment)
112 {
113 //filter Qt::AlignJustify which is not interpreted by this class
114 static const Qt::Alignment respectedFlags = (Qt::Alignment) ((Qt::AlignHorizontal_Mask & ~Qt::AlignJustify) | Qt::AlignVertical_Mask);
115 alignment &= respectedFlags;
116 if (d->m_alignment != alignment)
117 {
118 d->m_alignment = alignment;
119 d->_k_update();
120 }
121 }
122
_k_update()123 void Tagaro::Board::Private::_k_update()
124 {
125 //determine physical size
126 if (m_alignment)
127 {
128 //determine base rect (the rect into which we will be layouting)
129 QRectF baseRect;
130 QGraphicsItem* parentItem = m_board->parentItem();
131 if (parentItem)
132 {
133 baseRect = parentItem->boundingRect();
134 }
135 else
136 {
137 QGraphicsScene* scene = m_board->scene();
138 baseRect = scene ? scene->sceneRect() : QRectF(QPointF(), m_size);
139 }
140 //keep aspect ratio
141 const qreal scaleX = baseRect.width() / m_logicalSize.width();
142 const qreal scaleY = baseRect.height() / m_logicalSize.height();
143 m_size = qMin(scaleX, scaleY) * m_logicalSize;
144 QRectF physicalRect(baseRect.topLeft(), m_size);
145 //horizontal alignment (constructor of physicalRect aligns on left)
146 const bool hReverse = !(m_alignment & Qt::AlignAbsolute) && QApplication::isRightToLeft();
147 if (m_alignment & Qt::AlignHCenter)
148 {
149 const qreal dx = (baseRect.width() - m_size.width()) / 2;
150 physicalRect.translate(dx, 0);
151 }
152 else if (m_alignment & Qt::AlignRight || ((m_alignment & Qt::AlignLeft) && hReverse))
153 {
154 physicalRect.moveRight(baseRect.right());
155 }
156 //vertical alignment (constructor of physicalRect aligns on top)
157 if (m_alignment & Qt::AlignVCenter)
158 {
159 const qreal dy = (baseRect.height() - m_size.height()) / 2;
160 physicalRect.translate(0, dy);
161 }
162 else if (m_alignment & Qt::AlignBottom)
163 {
164 physicalRect.moveBottom(baseRect.bottom());
165 }
166 m_board->setPos(physicalRect.topLeft());
167 }
168 //update own transform and calculate renderSizeFactor
169 m_renderSizeFactor.setX(m_size.width() / m_logicalSize.width());
170 m_renderSizeFactor.setY(m_size.height() / m_logicalSize.height());
171 m_board->setTransform(QTransform::fromScale(m_renderSizeFactor.x(), m_renderSizeFactor.y()));
172 m_renderSizeFactor *= m_physicalSizeFactor;
173 //update items
174 QList<Tagaro::SpriteObjectItem*>::const_iterator it1 = m_items.constBegin(), it2 = m_items.constEnd();
175 for (; it1 != it2; ++it1)
176 update(*it1);
177 }
178
_k_updateItem()179 void Tagaro::Board::Private::_k_updateItem()
180 {
181 Tagaro::SpriteObjectItem* item = qobject_cast<Tagaro::SpriteObjectItem*>(m_board->sender());
182 if (item)
183 {
184 update(item);
185 }
186 }
187
update(Tagaro::SpriteObjectItem * item)188 void Tagaro::Board::Private::update(Tagaro::SpriteObjectItem* item)
189 {
190 QSizeF size = item->size();
191 size.rwidth() *= m_renderSizeFactor.x();
192 size.rheight() *= m_renderSizeFactor.y();
193 item->setRenderSize(size.toSize());
194 }
195
boundingRect() const196 QRectF Tagaro::Board::boundingRect() const
197 {
198 return QRectF(QPointF(), d->m_size);
199 }
200
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)201 void Tagaro::Board::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
202 {
203 Q_UNUSED(painter) Q_UNUSED(option) Q_UNUSED(widget)
204 }
205
itemChange(QGraphicsItem::GraphicsItemChange change,const QVariant & value)206 QVariant Tagaro::Board::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant& value)
207 {
208 if (change == ItemChildRemovedChange)
209 {
210 QGraphicsItem* item = value.value<QGraphicsItem*>();
211 d->m_pendingItems.removeAll(item);
212 QGraphicsObject* object = item->toGraphicsObject();
213 Tagaro::SpriteObjectItem* objectItem = qobject_cast<Tagaro::SpriteObjectItem*>(object);
214 if (objectItem)
215 {
216 disconnect(objectItem, nullptr, this, nullptr);
217 d->m_items.removeAll(objectItem);
218 }
219 }
220 else if (change == ItemChildAddedChange)
221 {
222 //check if this is a Tagaro::SpriteObjectItem
223 QGraphicsItem* item = value.value<QGraphicsItem*>();
224 QGraphicsObject* object = item->toGraphicsObject();
225 Tagaro::SpriteObjectItem* objectItem = qobject_cast<Tagaro::SpriteObjectItem*>(object);
226 if (objectItem)
227 {
228 d->m_items << objectItem;
229 connect(objectItem, SIGNAL(sizeChanged(QSizeF)), SLOT(_k_updateItem()));
230 d->update(objectItem);
231 }
232 //if we cannot cast to Tagaro::SpriteObjectItem, it might be that the item is
233 //not fully constructed -> check again later
234 if (item && (!object || (object && object->metaObject()->className() != QByteArray("QGraphicsObject"))))
235 {
236 d->m_pendingItems << item;
237 if (!d->m_pendingItemsTimer.isActive())
238 {
239 d->m_pendingItemsTimer.start(0, this);
240 }
241 }
242 }
243 else if (change == ItemSceneChange)
244 {
245 QGraphicsScene* scene = value.value<QGraphicsScene*>();
246 if (scene)
247 {
248 disconnect(scene, nullptr, this, nullptr);
249 }
250 }
251 else if (change == ItemSceneHasChanged)
252 {
253 QGraphicsScene* scene = value.value<QGraphicsScene*>();
254 if (scene)
255 {
256 d->_k_update();
257 connect(scene, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(_k_update()));
258 }
259 }
260 return QGraphicsObject::itemChange(change, value);
261 }
262
timerEvent(QTimerEvent * event)263 void Tagaro::Board::timerEvent(QTimerEvent* event)
264 {
265 if (event->timerId() == d->m_pendingItemsTimer.timerId())
266 {
267 d->m_pendingItemsTimer.stop();
268 //process pending ItemChildAddedChanges
269 QList<QGraphicsItem*>::const_iterator it1 = d->m_pendingItems.constBegin(), it2 = d->m_pendingItems.constEnd();
270 for (; it1 != it2; ++it1)
271 {
272 QGraphicsItem* item = *it1;
273 QGraphicsObject* object = item->toGraphicsObject();
274 Tagaro::SpriteObjectItem* objectItem = qobject_cast<Tagaro::SpriteObjectItem*>(object);
275 if (objectItem)
276 {
277 d->m_items << objectItem;
278 connect(objectItem, SIGNAL(sizeChanged(QSizeF)), SLOT(_k_updateItem()));
279 d->update(objectItem);
280 }
281 }
282 d->m_pendingItems.clear();
283 }
284 else
285 {
286 QGraphicsObject::timerEvent(event);
287 }
288 }
289
290 #include "moc_board.cpp"
291