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