File indexing completed on 2025-01-12 03:46:22

0001 /***************************************************************************
0002  *   Copyright 2008, 2009, 2010 Stefan Majewsky <majewsky@gmx.net>
0003  *
0004  *   This program is free software; you can redistribute it and/or
0005  *   modify it under the terms of the GNU General Public
0006  *   License as published by the Free Software Foundation; either
0007  *   version 2 of the License, or (at your option) any later version.
0008  *
0009  *   This program is distributed in the hope that it will be useful,
0010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *   GNU General Public License for more details.
0013  *
0014  *   You should have received a copy of the GNU General Public License
0015  *   along with this program; if not, write to the Free Software
0016  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
0017  ***************************************************************************/
0018 
0019 #include "overlay.h"
0020 #include "canvasitem.h"
0021 #include "game.h"
0022 #include "shape.h"
0023 #include "utils-animateditem.h"
0024 
0025 #include <QGraphicsSceneEvent>
0026 #include <QPen>
0027 
0028 const qreal Kolf::Overlay::MinimumObjectDimension = 10;
0029 
0030 static const qreal OverlayHandleSize = 5;
0031 static const QPen HandlePen(Qt::blue);
0032 static const QBrush ActiveHandleBrush(QColor::fromHsv(240, 200, 255, 128));
0033 static const QBrush NormalHandleBrush(QColor::fromHsv(240, 255, 255, 128));
0034 
0035 //BEGIN Kolf::OverlayHandle
0036 
0037 Kolf::OverlayHandle::OverlayHandle(Kolf::OverlayHandle::Shape shape, QGraphicsItem* parent)
0038     : QGraphicsPathItem(parent)
0039 {
0040     setAcceptHoverEvents(true);
0041     setAcceptedMouseButtons(Qt::LeftButton);
0042     setPen(HandlePen);
0043     setBrush(NormalHandleBrush);
0044     //create shape
0045     QPainterPath path;
0046     switch (shape)
0047     {
0048         case SquareShape:
0049             path.addRect(QRectF(-OverlayHandleSize, -OverlayHandleSize, 2 * OverlayHandleSize, 2 * OverlayHandleSize));
0050             break;
0051         case CircleShape:
0052             path.addEllipse(QPointF(), OverlayHandleSize, OverlayHandleSize);
0053             break;
0054         case TriangleShape:
0055             path.moveTo(QPointF(OverlayHandleSize, 0.0));
0056             path.lineTo(QPointF(-OverlayHandleSize, OverlayHandleSize));
0057             path.lineTo(QPointF(-OverlayHandleSize, -OverlayHandleSize));
0058             path.closeSubpath();
0059             break;
0060     }
0061     setPath(path);
0062 }
0063 
0064 void Kolf::OverlayHandle::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
0065 {
0066     event->accept();
0067     setBrush(ActiveHandleBrush);
0068 }
0069 
0070 void Kolf::OverlayHandle::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
0071 {
0072     event->accept();
0073     setBrush(NormalHandleBrush);
0074 }
0075 
0076 void Kolf::OverlayHandle::mousePressEvent(QGraphicsSceneMouseEvent* event)
0077 {
0078     if (event->button() == Qt::LeftButton)
0079     {
0080         event->accept();
0081         Q_EMIT moveStarted();
0082     }
0083 }
0084 
0085 void Kolf::OverlayHandle::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
0086 {
0087     if (event->buttons() & Qt::LeftButton)
0088     {
0089         event->accept();
0090         Q_EMIT moveRequest(event->scenePos());
0091     }
0092 }
0093 
0094 void Kolf::OverlayHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
0095 {
0096     if (event->button() == Qt::LeftButton)
0097     {
0098         event->accept();
0099         Q_EMIT moveEnded();
0100     }
0101 }
0102 
0103 //END Kolf::OverlayHandle
0104 //BEGIN Kolf::OverlayAreaItem
0105 
0106 Kolf::OverlayAreaItem::OverlayAreaItem(Features features, QGraphicsItem* parent)
0107     : QGraphicsPathItem(parent)
0108     , m_features(features)
0109 {
0110     if (m_features & (Clickable | Draggable))
0111         setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
0112     if (m_features & Draggable)
0113         setCursor(Qt::OpenHandCursor);
0114     if (m_features & Hoverable)
0115         setAcceptHoverEvents(true);
0116     //start invisible
0117     setPen(Qt::NoPen);
0118     setBrush(Qt::transparent);
0119 }
0120 
0121 void Kolf::OverlayAreaItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
0122 {
0123     if (m_features & Hoverable)
0124     {
0125         event->accept();
0126         Q_EMIT hoverEntered();
0127     }
0128     else
0129         QGraphicsPathItem::hoverEnterEvent(event);
0130 }
0131 
0132 void Kolf::OverlayAreaItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
0133 {
0134     if (m_features & Hoverable)
0135     {
0136         event->accept();
0137         Q_EMIT hoverLeft();
0138     }
0139     else
0140         QGraphicsPathItem::hoverLeaveEvent(event);
0141 }
0142 
0143 void Kolf::OverlayAreaItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
0144 {
0145     if (m_features & Clickable)
0146     {
0147         event->accept();
0148         Q_EMIT clicked(event->button());
0149     }
0150     if (m_features & Draggable)
0151     {
0152         event->accept();
0153         setCursor(Qt::ClosedHandCursor);
0154     }
0155     if (!event->isAccepted())
0156         QGraphicsPathItem::mousePressEvent(event);
0157 }
0158 
0159 void Kolf::OverlayAreaItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
0160 {
0161     if (m_features & Draggable)
0162         Q_EMIT dragged(event->scenePos() - event->lastScenePos());
0163     else
0164         QGraphicsItem::mouseMoveEvent(event);
0165 }
0166 
0167 void Kolf::OverlayAreaItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
0168 {
0169     if (m_features & Draggable)
0170         setCursor(Qt::OpenHandCursor);
0171     else
0172         QGraphicsItem::mouseReleaseEvent(event);
0173 }
0174 
0175 //END Kolf::OverlayAreaItem
0176 //BEGIN Kolf::Overlay
0177 
0178 Kolf::Overlay::Overlay(CanvasItem* citem, QGraphicsItem* qitem, bool hack_addQitemShapeToOutlines)
0179     : QGraphicsItem(qitem)
0180     , m_citem(citem), m_qitem(qitem)
0181     , m_state(Kolf::Overlay::Passive)
0182     , m_addQitemShapeToOutlines(hack_addQitemShapeToOutlines)
0183     , m_activatorItem(new Kolf::OverlayAreaItem(Kolf::OverlayAreaItem::Clickable | Kolf::OverlayAreaItem::Hoverable, this))
0184     , m_interactorAnimator(new Utils::AnimatedItem(this))
0185     , m_interactorItem(new Kolf::OverlayAreaItem(Kolf::OverlayAreaItem::Clickable | Kolf::OverlayAreaItem::Draggable, m_interactorAnimator))
0186     , m_handleAnimator(new Utils::AnimatedItem(this))
0187 {
0188     //overlays have to be shown explicitly
0189     hide();
0190     //overlays themselves do not have mouse interaction, and do not paint anything itself
0191     setAcceptedMouseButtons(Qt::NoButton);
0192     setFlag(QGraphicsItem::ItemHasNoContents);
0193     //initialize activator area item
0194     m_activatorItem->setZValue(1);
0195     connect(m_activatorItem, &Kolf::OverlayAreaItem::hoverEntered, this, &Overlay::activatorEntered);
0196     connect(m_activatorItem, &Kolf::OverlayAreaItem::hoverLeft, this, &Overlay::activatorLeft);
0197     connect(m_activatorItem, &Kolf::OverlayAreaItem::clicked, this, &Overlay::activatorClicked);
0198     //initialize interactor area item
0199     m_interactorAnimator->setZValue(2);
0200     m_interactorAnimator->setOpacity(0); //not visible at first
0201     m_interactorItem->setBrush(Qt::green);
0202     connect(m_interactorItem, &Kolf::OverlayAreaItem::clicked, this, &Overlay::activatorClicked);
0203     connect(m_interactorItem, &Kolf::OverlayAreaItem::dragged, this, &Overlay::interactorDragged);
0204     //initialize handle manager
0205     m_handleAnimator->setZValue(3);
0206     m_handleAnimator->setHideWhenInvisible(true);
0207     m_handleAnimator->setOpacity(0); //not visible at first
0208     //apply passive state - we need to change to some other state to prevent the setState method from returning early
0209     m_state = Active;
0210     setState(Passive);
0211 }
0212 
0213 CanvasItem* Kolf::Overlay::citem() const
0214 {
0215     return m_citem;
0216 }
0217 
0218 QGraphicsItem* Kolf::Overlay::qitem() const
0219 {
0220     return m_qitem;
0221 }
0222 
0223 void Kolf::Overlay::update()
0224 {
0225     //update geometry outlines
0226     QPainterPath activationOutline, interactionOutline;
0227         const auto shapes = m_citem->shapes();
0228     for (Kolf::Shape* shape : shapes) {
0229         activationOutline.addPath(shape->activationOutline());
0230         interactionOutline.addPath(shape->interactionOutline());
0231     }
0232     //HACK for Kolf::Shape
0233     if (m_addQitemShapeToOutlines)
0234     {
0235         const QPainterPath shape = m_qitem->shape();
0236         activationOutline.addPath(shape);
0237         interactionOutline.addPath(shape);
0238     }
0239     activationOutline.setFillRule(Qt::WindingFill);
0240     interactionOutline.setFillRule(Qt::WindingFill);
0241     m_activatorItem->setPath(activationOutline);
0242     m_interactorItem->setPath(interactionOutline);
0243 }
0244 
0245 void Kolf::Overlay::addHandle(QGraphicsItem* handle)
0246 {
0247     handle->setParentItem(m_handleAnimator);
0248     handle->show();
0249 }
0250 
0251 Kolf::Overlay::State Kolf::Overlay::state() const
0252 {
0253     return m_state;
0254 }
0255 
0256 void Kolf::Overlay::setState(Kolf::Overlay::State state)
0257 {
0258     if (m_state == state)
0259         return;
0260     m_state = state;
0261     //apply new inner properties
0262     switch (state)
0263     {
0264         case Kolf::Overlay::Passive:
0265             m_interactorAnimator->setOpacityAnimated(0.0);
0266             break;
0267         case Kolf::Overlay::Hovered:
0268             m_interactorAnimator->setOpacityAnimated(0.3);
0269             break;
0270         case Kolf::Overlay::Active:
0271             m_interactorAnimator->setOpacityAnimated(0.6);
0272             break;
0273     }
0274     m_handleAnimator->setOpacityAnimated(state == Active ? 1.0 : 0.0);
0275     //propagate changes
0276     Q_EMIT stateChanged();
0277     if (state == Kolf::Overlay::Active)
0278     {
0279         KolfGame* game = m_citem->game;
0280         if (game)
0281             game->setSelectedItem(m_citem);
0282     }
0283 }
0284 
0285 void Kolf::Overlay::activatorEntered()
0286 {
0287     if (m_state == Kolf::Overlay::Passive)
0288         setState(Kolf::Overlay::Hovered);
0289 }
0290 
0291 void Kolf::Overlay::activatorLeft()
0292 {
0293     if (m_state == Kolf::Overlay::Hovered)
0294         setState(Kolf::Overlay::Passive);
0295 }
0296 
0297 void Kolf::Overlay::activatorClicked(int button)
0298 {
0299     Q_UNUSED(button)
0300     setState(Kolf::Overlay::Active);
0301 }
0302 
0303 void Kolf::Overlay::interactorDragged(const QPointF& distance)
0304 {
0305     if (m_state == Kolf::Overlay::Active)
0306         m_citem->moveBy(distance.x(), distance.y());
0307 }
0308 
0309 //default implementation for QGraphicsItem
0310 
0311 QRectF Kolf::Overlay::boundingRect() const
0312 {
0313     return QRectF();
0314 }
0315 
0316 void Kolf::Overlay::paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*)
0317 {
0318 }
0319 
0320 //END Kolf::Overlay
0321 
0322 #include "moc_overlay.cpp"