File indexing completed on 2024-05-12 04:06:23
0001 /* 0002 SPDX-FileCopyrightText: 2009, 2010 Stefan Majewsky <majewsky@gmx.net> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "constraintvisualizer.h" 0008 #include "scene.h" 0009 0010 #include <QPropertyAnimation> 0011 #include <QCursor> 0012 #include "palapeli_debug.h" // IDW test. 0013 0014 Palapeli::ConstraintVisualizer::ConstraintVisualizer(Palapeli::Scene* scene) 0015 : m_scene(scene) 0016 , m_active(false) 0017 , m_shadowItems(SideCount) 0018 , m_handleItems(HandleCount) 0019 , m_sceneRect(QRectF()) 0020 , m_animator(new QPropertyAnimation(this, "opacity", this)) 0021 , m_isStopped(true) 0022 , m_thickness(5.0) 0023 { 0024 // All QGraphicsRectItems have null size until the first update(). 0025 setOpacity(0.3); 0026 // Create shadow items. These are outside the puzzle table. 0027 QColor rectColor(Qt::black); 0028 // IDW test. rectColor.setAlpha(80); 0029 rectColor.setAlpha(40); // Outer area is paler. 0030 for (int i = 0; i < SideCount; ++i) 0031 { 0032 m_shadowItems[i] = new QGraphicsRectItem(this); 0033 m_shadowItems[i]->setPen(Qt::NoPen); 0034 m_shadowItems[i]->setBrush(rectColor); 0035 } 0036 // Create handle items. These are the edges and corners of the table. 0037 // IDW test. rectColor.setAlpha(rectColor.alpha() / 2); 0038 rectColor.setAlpha(rectColor.alpha() * 2); // Table edge is darker. 0039 Qt::CursorShape shapes[] = { Qt::SizeHorCursor, Qt::SizeFDiagCursor, 0040 Qt::SizeVerCursor, Qt::SizeBDiagCursor }; 0041 for (int i = 0; i < HandleCount; ++i) 0042 { 0043 m_handleItems[i] = new QGraphicsRectItem(this); 0044 m_handleItems[i]->setPen(Qt::NoPen); 0045 m_handleItems[i]->setBrush(rectColor); 0046 m_handleItems[i]->setCursor(shapes[i % 4]); 0047 } 0048 //more initialization 0049 QObject::setParent(scene); //delete myself automatically when the scene is destroyed 0050 } 0051 0052 void Palapeli::ConstraintVisualizer::start (const QRectF& sceneRect, 0053 const int thickness) 0054 { 0055 // Puzzle loading nearly finished: add resize handles and shadow areas. 0056 if (!m_isStopped) { 0057 return; // Duplicate call. 0058 } 0059 m_thickness = thickness; 0060 this->update(sceneRect); 0061 m_scene->addItem(this); 0062 setZValue(-1); 0063 0064 // NOTE: The QueuedConnection is necessary because setSceneRect() sends 0065 // out the sceneRectChanged() signal before it disables automatic 0066 // growing of the scene rect. If the connection was direct, we could 0067 // thus enter an infinite loop when the constraint visualizer enlarges 0068 // itself in reaction to the changed sceneRect, thereby changing the 0069 // autogrowing sceneRect again. 0070 connect(m_scene, &Palapeli::Scene::sceneRectChanged, this, &ConstraintVisualizer::update, Qt::QueuedConnection); 0071 m_isStopped = false; 0072 } 0073 0074 void Palapeli::ConstraintVisualizer::stop() 0075 { 0076 if (m_isStopped) { 0077 return; // Starting first loadPuzzle(): nothing to do. 0078 } 0079 m_scene->removeItem(this); 0080 disconnect(m_scene, &QGraphicsScene::sceneRectChanged, this, &ConstraintVisualizer::update); 0081 m_sceneRect = QRectF(); 0082 m_isStopped = true; 0083 } 0084 0085 bool Palapeli::ConstraintVisualizer::isActive() const 0086 { 0087 return m_active; 0088 } 0089 0090 void Palapeli::ConstraintVisualizer::setActive(bool active) 0091 { 0092 if (m_active == active) 0093 return; 0094 m_active = active; 0095 const qreal targetOpacity = active ? 1.0 : 0.3; 0096 m_animator->setDuration(150 * qAbs(targetOpacity - opacity())); 0097 m_animator->setStartValue(opacity()); 0098 m_animator->setEndValue(targetOpacity); 0099 m_animator->start(); 0100 } 0101 0102 void Palapeli::ConstraintVisualizer::update(const QRectF& sceneRect) 0103 { 0104 if (m_sceneRect == sceneRect) 0105 return; 0106 // Make sure the ConstraintVisualizer stays outside the pieces' area. 0107 QRectF minimumRect = m_scene->extPiecesBoundingRect(); 0108 m_sceneRect = sceneRect; 0109 if(!sceneRect.contains(minimumRect)) { 0110 // IDW TODO - Works and seems safe, 0111 // but it may be better for interactor to check. 0112 m_sceneRect = minimumRect; 0113 m_scene->setSceneRect(minimumRect); 0114 } 0115 // Find a fictional viewport we want to cover (except for scene rect). 0116 const qreal viewportRectSizeFactor = 10; 0117 QRectF viewportRect = m_sceneRect; 0118 viewportRect.setSize(viewportRectSizeFactor * m_sceneRect.size()); 0119 viewportRect.moveCenter(m_sceneRect.center()); 0120 // The shadow areas are the areas outside the puzzle table. 0121 //adjust left shadow area 0122 QRectF itemRect = viewportRect; 0123 itemRect.setRight(m_sceneRect.left()); 0124 m_shadowItems[LeftSide]->setRect(itemRect); 0125 //adjust right shadow area 0126 itemRect = viewportRect; 0127 itemRect.setLeft(m_sceneRect.right()); 0128 m_shadowItems[RightSide]->setRect(itemRect); 0129 //adjust top shadow area 0130 itemRect = viewportRect; 0131 itemRect.setBottom(m_sceneRect.top()); 0132 itemRect.setLeft(m_sceneRect.left()); //do not overlap left area... 0133 itemRect.setRight(m_sceneRect.right()); //..and right area 0134 m_shadowItems[TopSide]->setRect(itemRect); 0135 //adjust bottom shadow area 0136 itemRect = viewportRect; 0137 itemRect.setTop(m_sceneRect.bottom()); 0138 itemRect.setLeft(m_sceneRect.left()); //same as above 0139 itemRect.setRight(m_sceneRect.right()); 0140 m_shadowItems[BottomSide]->setRect(itemRect); 0141 // 0142 // The handles are the draggable borders of the puzzle table. 0143 //adjust edge handles 0144 // IDW test.QRectF handleRect(QPointF(), handleSize); 0145 QRectF handleRect(QPointF(), QSizeF(m_thickness, m_thickness)); 0146 handleRect.moveTopLeft(m_sceneRect.topLeft()); 0147 m_handleItems[TopLeftHandle]->setRect(handleRect); 0148 handleRect.moveTopRight(m_sceneRect.topRight()); 0149 m_handleItems[TopRightHandle]->setRect(handleRect); 0150 handleRect.moveBottomLeft(m_sceneRect.bottomLeft()); 0151 m_handleItems[BottomLeftHandle]->setRect(handleRect); 0152 handleRect.moveBottomRight(m_sceneRect.bottomRight()); 0153 m_handleItems[BottomRightHandle]->setRect(handleRect); 0154 //adjust top/bottom handles 0155 // IDW test. handleRect.setSize(QSizeF(m_sceneRect.width() - 2 * handleSize.width(), handleSize.height())); 0156 handleRect.setSize(QSizeF(m_sceneRect.width() - 2 * m_thickness, 0157 m_thickness)); 0158 handleRect.moveCenter(m_sceneRect.center()); 0159 handleRect.moveTop(m_sceneRect.top()); 0160 m_handleItems[TopHandle]->setRect(handleRect); 0161 handleRect.moveBottom(m_sceneRect.bottom()); 0162 m_handleItems[BottomHandle]->setRect(handleRect); 0163 //adjust left/right handles 0164 // IDW test. handleRect.setSize(QSizeF(handleSize.width(), m_sceneRect.height() - 2 * handleSize.height())); 0165 handleRect.setSize(QSizeF(m_thickness, 0166 m_sceneRect.height() - 2 * m_thickness)); 0167 handleRect.moveCenter(m_sceneRect.center()); 0168 handleRect.moveLeft(m_sceneRect.left()); 0169 m_handleItems[LeftHandle]->setRect(handleRect); 0170 handleRect.moveRight(m_sceneRect.right()); 0171 m_handleItems[RightHandle]->setRect(handleRect); 0172 } 0173 0174 #include "moc_constraintvisualizer.cpp"