/**************************************************************************** ** ** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Charts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE LegendMoveResizeHandler::LegendMoveResizeHandler(QLegend *legend) : m_legend(legend) { m_legend->setAcceptHoverEvents(true); m_legend->setCursor(Qt::ArrowCursor); } LegendMoveResizeHandler::~LegendMoveResizeHandler() { } void LegendMoveResizeHandler::reset() { m_action = Action::Idle; setMouseCursor(MousePosition::Nowhere); } void LegendMoveResizeHandler::handleMousePressEvent(QGraphicsSceneMouseEvent *event) { determineMousePosition(event->pos()); m_moveOffset = event->pos(); m_action = Action::Pressed; // Since the legend does not have a proper frame, // with a title bar, we have replaced the "top" from // being a resize bar to being a move area. This // is evident to the user as the mouse cursor // changes to an open hand while over the top // in hover mode, and a closed hand when the mouse // button is pressed. if (m_legend->isAttachedToChart()) { m_action = Action::Idle; } else if (m_mode == MousePosition::Top) { m_action = Action::Moving; setMouseCursor(); } else if (m_mode != MousePosition::Nowhere) { m_action = Action::Resizing; } } void LegendMoveResizeHandler::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event) { QRectF geom = m_legend->geometry(); QMarginsF reattachMargins{m_reattachThreshold, m_reattachThreshold, m_reattachThreshold, m_reattachThreshold}; QRectF dragArea = m_legend->parentWidget()->geometry() - reattachMargins; if (m_action == Action::Moving) { bool reattach = true; QPointF toPoint = event->scenePos() - m_moveOffset; if (event->scenePos().x() <= dragArea.left()) m_legend->setAlignment(Qt::AlignLeft); else if (event->scenePos().x() >= dragArea.right()) m_legend->setAlignment(Qt::AlignRight); else if (event->scenePos().y() <= dragArea.top()) m_legend->setAlignment(Qt::AlignTop); else if (event->scenePos().y() >= dragArea.bottom()) m_legend->setAlignment(Qt::AlignBottom); else reattach = false; QRectF potentialGeom(geom); potentialGeom.moveTopLeft(toPoint); if (potentialGeom.left() <= dragArea.left()) toPoint.setX(dragArea.left()); else if (potentialGeom.right() >= dragArea.right()) toPoint.setX(dragArea.right() - geom.width()); if (potentialGeom.top() <= dragArea.top()) toPoint.setY(dragArea.top()); else if (potentialGeom.bottom() >= dragArea.bottom()) toPoint.setY(dragArea.bottom() - geom.height()); // Set the geometry to be the new desired position corrected to // be within the bounds of the dragArea. geom.moveTopLeft(toPoint); if (geom != m_legend->geometry()) m_legend->setGeometry(geom); if (reattach && !m_legend->isAttachedToChart()) { m_action = Action::Idle; m_mode = MousePosition::Nowhere; setMouseCursor(); m_legend->attachToChart(); } } else if (m_action == Action::Resizing) { QPointF trackPoint = event->scenePos(); QRectF boundRect = m_legend->parentWidget()->geometry(); if (trackPoint.x() <= boundRect.left()) trackPoint.rx() = boundRect.left() + 1; else if (trackPoint.x() >= boundRect.right()) trackPoint.rx() = boundRect.right() - 1; if (trackPoint.y() <= boundRect.top()) trackPoint.ry() = boundRect.top() + 1; else if (trackPoint.y() >= boundRect.bottom()) trackPoint.ry() = boundRect.bottom(); switch (m_mode) { case MousePosition::TopLeft: geom = QRectF(trackPoint, geom.bottomRight()); break; case MousePosition::BottomRight: geom = QRectF(geom.topLeft(), trackPoint); break; case MousePosition::BottomLeft: geom = QRectF(QPointF(trackPoint.x(), geom.y()), QPointF(geom.right(), trackPoint.y())); break; case MousePosition::TopRight: geom = QRectF(QPointF(geom.x(), trackPoint.y()), QPoint(trackPoint.x(), geom.bottom())); break; case MousePosition::Bottom: geom = QRectF(geom.topLeft(), QPointF(geom.right(), trackPoint.y())); break; case MousePosition::Left: geom = QRectF(QPointF(trackPoint.x(), geom.top()), geom.bottomRight()); break; case MousePosition::Right: geom = QRectF(geom.topLeft(), QPointF(trackPoint.x(), geom.bottom())); break; default: break; } geom = QRectF(geom.topLeft(), geom.size().expandedTo(m_legend->d_ptr->m_layout->minimumSize()) .boundedTo((boundRect & geom).size())); if (geom.size() != m_legend->geometry().size()) m_legend->setGeometry(geom); } } void LegendMoveResizeHandler::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event) { m_action = Action::Idle; determineMousePosition(event->pos()); setMouseCursor(); } void LegendMoveResizeHandler::handleHoverEnterEvent(QGraphicsSceneHoverEvent *event) { if (!m_legend->isAttachedToChart()) { determineMousePosition(event->pos()); setMouseCursor(); } m_action = Action::Hovered; } void LegendMoveResizeHandler::handleHoverMoveEvent(QGraphicsSceneHoverEvent *event) { if (!m_legend->isAttachedToChart()) { determineMousePosition(event->pos()); setMouseCursor(); } m_action = Action::Hovered; } void LegendMoveResizeHandler::handleHoverLeaveEvent(QGraphicsSceneHoverEvent *) { if (!m_legend->isAttachedToChart()) { m_mode = MousePosition::Nowhere; setMouseCursor(); } m_action = Action::Idle; } void LegendMoveResizeHandler::setMouseCursor() { setMouseCursor(m_mode); } void LegendMoveResizeHandler::setMouseCursor(MousePosition mpos) { #ifdef QT_NO_CURSOR Q_UNUSED(mpos); return; #else const QList items = m_legend->d_ptr->m_items->childItems(); for (const auto item : items) { if (!item->hasCursor()) item->setCursor(Qt::ArrowCursor); } switch (mpos) { case MousePosition::TopLeft: case MousePosition::BottomRight: m_legend->setCursor(Qt::SizeFDiagCursor); break; case MousePosition::BottomLeft: case MousePosition::TopRight: m_legend->setCursor(Qt::SizeBDiagCursor); break; case MousePosition::Top: if (m_action == Action::Moving) m_legend->setCursor(Qt::ClosedHandCursor); else m_legend->setCursor(Qt::OpenHandCursor); break; case MousePosition::Bottom: m_legend->setCursor(Qt::SizeVerCursor); break; case MousePosition::Left: case MousePosition::Right: m_legend->setCursor(Qt::SizeHorCursor); break; case MousePosition::Nowhere: m_legend->setCursor(Qt::ArrowCursor); break; } #endif } void LegendMoveResizeHandler::determineMousePosition(QPointF fromPoint) { QRectF contentRect = m_legend->d_ptr->m_layout->contentsRect(); if (fromPoint.x() <= contentRect.left()) { if (fromPoint.y() <= contentRect.top()) m_mode = MousePosition::TopLeft; else if (fromPoint.y() >= contentRect.bottom()) m_mode = MousePosition::BottomLeft; else m_mode = MousePosition::Left; } else if (fromPoint.x() > contentRect.left() && fromPoint.x() < contentRect.right()) { if (fromPoint.y() <= contentRect.top()) m_mode = MousePosition::Top; else if (fromPoint.y() >= contentRect.bottom()) m_mode = MousePosition::Bottom; else // This catches a corner case where the mouse // position y is inside of the content rect m_mode = MousePosition::Nowhere; } else if (fromPoint.x() >= contentRect.left()) { if (fromPoint.y() <= contentRect.top()) m_mode = MousePosition::TopRight; else if (fromPoint.y() >= contentRect.bottom()) m_mode = MousePosition::BottomRight; else m_mode = MousePosition::Right; } else { m_mode = MousePosition::Nowhere; } } QT_END_NAMESPACE