1/***************************************************************************
2 * Copyright 2008, 2009, 2010 Stefan Majewsky <majewsky@gmx.net>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 ***************************************************************************/
18
19#include "overlay.h"
20#include "canvasitem.h"
21#include "game.h"
22#include "shape.h"
23#include "utils-animateditem.h"
24
25#include <QGraphicsSceneEvent>
26#include <QPen>
27#include <QtCore/qmath.h>
28
29const qreal Kolf::Overlay::MinimumObjectDimension = 10;
30
31static const qreal OverlayHandleSize = 5;
32static const QPen HandlePen(Qt::blue);
33static const QBrush ActiveHandleBrush(QColor::fromHsv(240, 200, 255, 128));
34static const QBrush NormalHandleBrush(QColor::fromHsv(240, 255, 255, 128));
35
36//BEGIN Kolf::OverlayHandle
37
38Kolf::OverlayHandle::OverlayHandle(Kolf::OverlayHandle::Shape shape, QGraphicsItem* parent)
39 : QGraphicsPathItem(parent)
40{
41 setAcceptHoverEvents(true);
42 setAcceptedMouseButtons(Qt::LeftButton);
43 setPen(HandlePen);
44 setBrush(NormalHandleBrush);
45 //create shape
46 QPainterPath path;
47 switch (shape)
48 {
49 case SquareShape:
50 path.addRect(QRectF(-OverlayHandleSize, -OverlayHandleSize, 2 * OverlayHandleSize, 2 * OverlayHandleSize));
51 break;
52 case CircleShape:
53 path.addEllipse(QPointF(), OverlayHandleSize, OverlayHandleSize);
54 break;
55 case TriangleShape:
56 path.moveTo(QPointF(OverlayHandleSize, 0.0));
57 path.lineTo(QPointF(-OverlayHandleSize, OverlayHandleSize));
58 path.lineTo(QPointF(-OverlayHandleSize, -OverlayHandleSize));
59 path.closeSubpath();
60 break;
61 }
62 setPath(path);
63}
64
65void Kolf::OverlayHandle::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
66{
67 event->accept();
68 setBrush(ActiveHandleBrush);
69}
70
71void Kolf::OverlayHandle::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
72{
73 event->accept();
74 setBrush(NormalHandleBrush);
75}
76
77void Kolf::OverlayHandle::mousePressEvent(QGraphicsSceneMouseEvent* event)
78{
79 if (event->button() == Qt::LeftButton)
80 {
81 event->accept();
82 emit moveStarted();
83 }
84}
85
86void Kolf::OverlayHandle::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
87{
88 if (event->buttons() & Qt::LeftButton)
89 {
90 event->accept();
91 emit moveRequest(event->scenePos());
92 }
93}
94
95void Kolf::OverlayHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
96{
97 if (event->button() == Qt::LeftButton)
98 {
99 event->accept();
100 emit moveEnded();
101 }
102}
103
104//END Kolf::OverlayHandle
105//BEGIN Kolf::OverlayAreaItem
106
107Kolf::OverlayAreaItem::OverlayAreaItem(Features features, QGraphicsItem* parent)
108 : QGraphicsPathItem(parent)
109 , m_features(features)
110{
111 if (m_features & (Clickable | Draggable))
112 setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
113 if (m_features & Draggable)
114 setCursor(Qt::OpenHandCursor);
115 if (m_features & Hoverable)
116 setAcceptHoverEvents(true);
117 //start invisible
118 setPen(Qt::NoPen);
119 setBrush(Qt::transparent);
120}
121
122void Kolf::OverlayAreaItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
123{
124 if (m_features & Hoverable)
125 {
126 event->accept();
127 emit hoverEntered();
128 }
129 else
130 QGraphicsPathItem::hoverEnterEvent(event);
131}
132
133void Kolf::OverlayAreaItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
134{
135 if (m_features & Hoverable)
136 {
137 event->accept();
138 emit hoverLeft();
139 }
140 else
141 QGraphicsPathItem::hoverLeaveEvent(event);
142}
143
144void Kolf::OverlayAreaItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
145{
146 if (m_features & Clickable)
147 {
148 event->accept();
149 emit clicked(event->button());
150 }
151 if (m_features & Draggable)
152 {
153 event->accept();
154 setCursor(Qt::ClosedHandCursor);
155 }
156 if (!event->isAccepted())
157 QGraphicsPathItem::mousePressEvent(event);
158}
159
160void Kolf::OverlayAreaItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
161{
162 if (m_features & Draggable)
163 emit dragged(event->scenePos() - event->lastScenePos());
164 else
165 QGraphicsItem::mouseMoveEvent(event);
166}
167
168void Kolf::OverlayAreaItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
169{
170 if (m_features & Draggable)
171 setCursor(Qt::OpenHandCursor);
172 else
173 QGraphicsItem::mouseReleaseEvent(event);
174}
175
176//END Kolf::OverlayAreaItem
177//BEGIN Kolf::Overlay
178
179Kolf::Overlay::Overlay(CanvasItem* citem, QGraphicsItem* qitem, bool hack_addQitemShapeToOutlines)
180 : QGraphicsItem(qitem)
181 , m_citem(citem), m_qitem(qitem)
182 , m_state(Kolf::Overlay::Passive)
183 , m_addQitemShapeToOutlines(hack_addQitemShapeToOutlines)
184 , m_activatorItem(new Kolf::OverlayAreaItem(Kolf::OverlayAreaItem::Clickable | Kolf::OverlayAreaItem::Hoverable, this))
185 , m_interactorAnimator(new Utils::AnimatedItem(this))
186 , m_interactorItem(new Kolf::OverlayAreaItem(Kolf::OverlayAreaItem::Clickable | Kolf::OverlayAreaItem::Draggable, m_interactorAnimator))
187 , m_handleAnimator(new Utils::AnimatedItem(this))
188{
189 //overlays have to be shown explicitly
190 hide();
191 //overlays themselves do not have mouse interaction, and do not paint anything itself
192 setAcceptedMouseButtons(0);
193 setFlag(QGraphicsItem::ItemHasNoContents);
194 //initialize activator area item
195 m_activatorItem->setZValue(1);
196 connect(m_activatorItem, SIGNAL(hoverEntered()), this, SLOT(activatorEntered()));
197 connect(m_activatorItem, SIGNAL(hoverLeft()), this, SLOT(activatorLeft()));
198 connect(m_activatorItem, SIGNAL(clicked(int)), this, SLOT(activatorClicked(int)));
199 //initialize interactor area item
200 m_interactorAnimator->setZValue(2);
201 m_interactorAnimator->setOpacity(0); //not visible at first
202 m_interactorItem->setBrush(Qt::green);
203 connect(m_interactorItem, SIGNAL(clicked(int)), this, SLOT(activatorClicked(int))); //note that interactor item is over activator in hovered mode
204 connect(m_interactorItem, SIGNAL(dragged(QPointF)), this, SLOT(interactorDragged(QPointF)));
205 //initialize handle manager
206 m_handleAnimator->setZValue(3);
207 m_handleAnimator->setHideWhenInvisible(true);
208 m_handleAnimator->setOpacity(0); //not visible at first
209 //apply passive state - we need to change to some other state to prevent the setState method from returning early
210 m_state = Active;
211 setState(Passive);
212}
213
214CanvasItem* Kolf::Overlay::citem() const
215{
216 return m_citem;
217}
218
219QGraphicsItem* Kolf::Overlay::qitem() const
220{
221 return m_qitem;
222}
223
224void Kolf::Overlay::update()
225{
226 //update geometry outlines
227 QPainterPath activationOutline, interactionOutline;
228 foreach (Kolf::Shape* shape, m_citem->shapes())
229 {
230 activationOutline.addPath(shape->activationOutline());
231 interactionOutline.addPath(shape->interactionOutline());
232 }
233 //HACK for Kolf::Shape
234 if (m_addQitemShapeToOutlines)
235 {
236 const QPainterPath shape = m_qitem->shape();
237 activationOutline.addPath(shape);
238 interactionOutline.addPath(shape);
239 }
240 activationOutline.setFillRule(Qt::WindingFill);
241 interactionOutline.setFillRule(Qt::WindingFill);
242 m_activatorItem->setPath(activationOutline);
243 m_interactorItem->setPath(interactionOutline);
244}
245
246void Kolf::Overlay::addHandle(QGraphicsItem* handle)
247{
248 handle->setParentItem(m_handleAnimator);
249 handle->show();
250}
251
252Kolf::Overlay::State Kolf::Overlay::state() const
253{
254 return m_state;
255}
256
257void Kolf::Overlay::setState(Kolf::Overlay::State state)
258{
259 if (m_state == state)
260 return;
261 m_state = state;
262 //apply new inner properties
263 switch (state)
264 {
265 case Kolf::Overlay::Passive:
266 m_interactorAnimator->setOpacityAnimated(0.0);
267 break;
268 case Kolf::Overlay::Hovered:
269 m_interactorAnimator->setOpacityAnimated(0.3);
270 break;
271 case Kolf::Overlay::Active:
272 m_interactorAnimator->setOpacityAnimated(0.6);
273 break;
274 }
275 m_handleAnimator->setOpacityAnimated(state == Active ? 1.0 : 0.0);
276 //propagate changes
277 emit stateChanged();
278 if (state == Kolf::Overlay::Active)
279 {
280 KolfGame* game = m_citem->game;
281 if (game)
282 game->setSelectedItem(m_citem);
283 }
284}
285
286void Kolf::Overlay::activatorEntered()
287{
288 if (m_state == Kolf::Overlay::Passive)
289 setState(Kolf::Overlay::Hovered);
290}
291
292void Kolf::Overlay::activatorLeft()
293{
294 if (m_state == Kolf::Overlay::Hovered)
295 setState(Kolf::Overlay::Passive);
296}
297
298void Kolf::Overlay::activatorClicked(int button)
299{
300 Q_UNUSED(button)
301 setState(Kolf::Overlay::Active);
302}
303
304void Kolf::Overlay::interactorDragged(const QPointF& distance)
305{
306 if (m_state == Kolf::Overlay::Active)
307 m_citem->moveBy(distance.x(), distance.y());
308}
309
310//default implementation for QGraphicsItem
311
312QRectF Kolf::Overlay::boundingRect() const
313{
314 return QRectF();
315}
316
317void Kolf::Overlay::paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*)
318{
319}
320
321//END Kolf::Overlay
322
323#include "overlay.moc"
324