File indexing completed on 2024-05-12 03:48:20
0001 /* 0002 File : ResizeItem.cpp 0003 Project : LabPlot 0004 Description : Item allowing to resize worksheet elements with the mouse 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2021 Alexander Semke <alexander.semke@web.de> 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "ResizeItem.h" 0011 #include "backend/worksheet/WorksheetElementContainer.h" 0012 #include <QBrush> 0013 #include <QCursor> 0014 0015 ResizeItem::HandleItem::HandleItem(int position, ResizeItem* parent) 0016 : QGraphicsRectItem(-10, -10, 20, 20, parent) 0017 , m_position(position) 0018 , m_parent(parent) { 0019 setBrush(QBrush(Qt::red)); 0020 setFlag(ItemIsMovable); 0021 setFlag(ItemSendsGeometryChanges); 0022 setAcceptHoverEvents(true); 0023 } 0024 0025 int ResizeItem::HandleItem::position() const { 0026 return m_position; 0027 } 0028 0029 QVariant ResizeItem::HandleItem::itemChange(GraphicsItemChange change, const QVariant& value) { 0030 QVariant newValue = value; 0031 0032 if (change == ItemPositionChange) 0033 newValue = restrictPosition(value.toPointF()); 0034 else if (change == ItemPositionHasChanged) { 0035 QPointF pos = value.toPointF(); 0036 0037 switch (m_position) { 0038 case TopLeft: 0039 m_parent->setTopLeft(pos); 0040 break; 0041 case Top: 0042 m_parent->setTop(pos.y()); 0043 break; 0044 case TopRight: 0045 m_parent->setTopRight(pos); 0046 break; 0047 case Right: 0048 m_parent->setRight(pos.x()); 0049 break; 0050 case BottomRight: 0051 m_parent->setBottomRight(pos); 0052 break; 0053 case Bottom: 0054 m_parent->setBottom(pos.y()); 0055 break; 0056 case BottomLeft: 0057 m_parent->setBottomLeft(pos); 0058 break; 0059 case Left: 0060 m_parent->setLeft(pos.x()); 0061 break; 0062 } 0063 } 0064 0065 return newValue; 0066 } 0067 0068 void ResizeItem::HandleItem::mousePressEvent(QGraphicsSceneMouseEvent*) { 0069 m_parent->container()->setUndoAware(false); 0070 m_oldRect = m_parent->container()->rect(); 0071 } 0072 0073 void ResizeItem::HandleItem::mouseReleaseEvent(QGraphicsSceneMouseEvent*) { 0074 m_parent->container()->setUndoAware(true); 0075 0076 // the container has already the current rect set, we just need 0077 // to pass the previous rect so the undo-step can be done properly 0078 m_parent->container()->setPrevRect(m_oldRect); 0079 } 0080 0081 void ResizeItem::HandleItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event) { 0082 // HACK: make the parent container/plot non-movable otherwise 0083 // the move event doesn't reach HandleItem. Better solution? 0084 m_parent->container()->graphicsItem()->setFlag(ItemIsMovable, false); 0085 0086 switch (m_position) { 0087 case TopLeft: 0088 setCursor(Qt::SizeFDiagCursor); 0089 break; 0090 case Top: 0091 setCursor(Qt::SizeVerCursor); 0092 break; 0093 case TopRight: 0094 setCursor(Qt::SizeBDiagCursor); 0095 break; 0096 case Right: 0097 setCursor(Qt::SizeHorCursor); 0098 break; 0099 case BottomRight: 0100 setCursor(Qt::SizeFDiagCursor); 0101 break; 0102 case Bottom: 0103 setCursor(Qt::SizeVerCursor); 0104 break; 0105 case BottomLeft: 0106 setCursor(Qt::SizeBDiagCursor); 0107 break; 0108 case Left: 0109 setCursor(Qt::SizeHorCursor); 0110 break; 0111 } 0112 QGraphicsItem::hoverLeaveEvent(event); 0113 } 0114 0115 void ResizeItem::HandleItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) { 0116 m_parent->container()->graphicsItem()->setFlag(ItemIsMovable, true); 0117 QGraphicsItem::hoverLeaveEvent(event); 0118 } 0119 0120 QPointF ResizeItem::HandleItem::restrictPosition(const QPointF& pos) { 0121 QPointF newPos = this->pos(); 0122 0123 if (m_position & Top || m_position & Bottom) 0124 newPos.setY(pos.y()); 0125 0126 if (m_position & Left || m_position & Right) 0127 newPos.setX(pos.x()); 0128 0129 if (m_position & Top && newPos.y() > m_parent->m_rect.bottom()) 0130 newPos.setY(m_parent->m_rect.bottom()); 0131 else if (m_position & Bottom && newPos.y() < m_parent->m_rect.top()) 0132 newPos.setY(m_parent->m_rect.top()); 0133 0134 if (m_position & Left && newPos.x() > m_parent->m_rect.right()) 0135 newPos.setX(m_parent->m_rect.right()); 0136 else if (m_position & Right && newPos.x() < m_parent->m_rect.left()) 0137 newPos.setX(m_parent->m_rect.left()); 0138 0139 return newPos; 0140 } 0141 0142 ResizeItem::ResizeItem(WorksheetElementContainer* container) 0143 : QGraphicsItem(container->graphicsItem()) 0144 , m_container(container) { 0145 m_handleItems.append(new HandleItem(TopLeft, this)); 0146 m_handleItems.append(new HandleItem(Top, this)); 0147 m_handleItems.append(new HandleItem(TopRight, this)); 0148 m_handleItems.append(new HandleItem(Right, this)); 0149 m_handleItems.append(new HandleItem(BottomRight, this)); 0150 m_handleItems.append(new HandleItem(Bottom, this)); 0151 m_handleItems.append(new HandleItem(BottomLeft, this)); 0152 m_handleItems.append(new HandleItem(Left, this)); 0153 } 0154 0155 ResizeItem::~ResizeItem() = default; 0156 0157 void ResizeItem::setRect(QRectF rect) { 0158 prepareGeometryChange(); 0159 m_rect = mapRectFromScene(rect); 0160 updateHandleItemPositions(); 0161 } 0162 0163 QRectF ResizeItem::boundingRect() const { 0164 return m_rect; 0165 } 0166 0167 WorksheetElementContainer* ResizeItem::container() { 0168 return m_container; 0169 } 0170 0171 void ResizeItem::paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) { 0172 } 0173 0174 #define IMPL_SET_FN(TYPE, POS) \ 0175 void ResizeItem::set##POS(TYPE v) { \ 0176 m_rect.set##POS(v); \ 0177 m_container->setRect(mapRectToScene(m_rect)); \ 0178 } 0179 0180 IMPL_SET_FN(qreal, Top) 0181 IMPL_SET_FN(qreal, Right) 0182 IMPL_SET_FN(qreal, Bottom) 0183 IMPL_SET_FN(qreal, Left) 0184 IMPL_SET_FN(const QPointF&, TopLeft) 0185 IMPL_SET_FN(const QPointF&, TopRight) 0186 IMPL_SET_FN(const QPointF&, BottomRight) 0187 IMPL_SET_FN(const QPointF&, BottomLeft) 0188 0189 void ResizeItem::updateHandleItemPositions() { 0190 for (auto* item : m_handleItems) { 0191 item->setFlag(ItemSendsGeometryChanges, false); 0192 0193 switch (item->position()) { 0194 case TopLeft: 0195 item->setPos(m_rect.topLeft()); 0196 break; 0197 case Top: 0198 item->setPos(m_rect.left() + m_rect.width() / 2 - 1, m_rect.top()); 0199 break; 0200 case TopRight: 0201 item->setPos(m_rect.topRight()); 0202 break; 0203 case Right: 0204 item->setPos(m_rect.right(), m_rect.top() + m_rect.height() / 2 - 1); 0205 break; 0206 case BottomRight: 0207 item->setPos(m_rect.bottomRight()); 0208 break; 0209 case Bottom: 0210 item->setPos(m_rect.left() + m_rect.width() / 2 - 1, m_rect.bottom()); 0211 break; 0212 case BottomLeft: 0213 item->setPos(m_rect.bottomLeft()); 0214 break; 0215 case Left: 0216 item->setPos(m_rect.left(), m_rect.top() + m_rect.height() / 2 - 1); 0217 break; 0218 } 0219 0220 item->setFlag(ItemSendsGeometryChanges, true); 0221 } 0222 }