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"