summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKeith Kyzivat <keith.kyzivat@qt.io>2021-04-29 19:40:15 -0400
committerKeith Kyzivat <keith.kyzivat@qt.io>2021-06-01 13:10:10 -0400
commit7f3ad23dc6e3703cc1c2cdfa926cefb412b027bb (patch)
tree1d64a7519f6a102a8fceb09230692fc2de756a45 /src
parent1a0063ed1523ca7511e1801957c377b5bf40af27 (diff)
Add move and resize to QLegend
This feature adds the ability to move and resize the chart legend when it is not attached to the chart. It does not put a separate frame around the legend, but instead takes control of mouse messages when the mouse cursor is between the content area and the border of the legend. The user can drag the legend around when they click and drag the top edge of a detached legend. A visual rectangle is painted when the user positions the cursor at the top of a detached legend. This area identifies the region they can use to drag the legend around. If the mouse pointer is moved beyond the edge of the chart, it will attach to the edge that the mouse moved off of. The legend remains bound to the area of the chart, even if dragging would have moved an edge of the legend beyond the edge of the chart. An attached legend can be detached by double clicking it. Task-number: QTBUG-93477 Change-Id: I5adff45802ba326004f2ffebe27357c8f8b138d0 Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/charts/CMakeLists.txt1
-rw-r--r--src/charts/legend/legendmarkeritem.cpp8
-rw-r--r--src/charts/legend/legendmoveresizehandler.cpp287
-rw-r--r--src/charts/legend/legendmoveresizehandler_p.h103
-rw-r--r--src/charts/legend/legendscroller.cpp167
-rw-r--r--src/charts/legend/legendscroller_p.h15
-rw-r--r--src/charts/legend/qlegend.cpp56
-rw-r--r--src/charts/legend/qlegend.h7
-rw-r--r--src/charts/legend/qlegend_p.h6
9 files changed, 629 insertions, 21 deletions
diff --git a/src/charts/CMakeLists.txt b/src/charts/CMakeLists.txt
index 16fe06c5..3eedc51f 100644
--- a/src/charts/CMakeLists.txt
+++ b/src/charts/CMakeLists.txt
@@ -65,6 +65,7 @@ qt_internal_add_module(Charts
legend/legendlayout.cpp legend/legendlayout_p.h
legend/legendmarkeritem.cpp legend/legendmarkeritem_p.h
legend/legendscroller.cpp legend/legendscroller_p.h
+ legend/legendmoveresizehandler.cpp legend/legendmoveresizehandler_p.h
legend/qlegend.cpp legend/qlegend.h legend/qlegend_p.h
legend/qlegendmarker.cpp legend/qlegendmarker.h legend/qlegendmarker_p.h
legend/qxylegendmarker.cpp legend/qxylegendmarker.h legend/qxylegendmarker_p.h
diff --git a/src/charts/legend/legendmarkeritem.cpp b/src/charts/legend/legendmarkeritem.cpp
index 9dec8196..e01af41b 100644
--- a/src/charts/legend/legendmarkeritem.cpp
+++ b/src/charts/legend/legendmarkeritem.cpp
@@ -261,16 +261,20 @@ QSizeF LegendMarkerItem::sizeHint(Qt::SizeHint which, const QSizeF& constraint)
void LegendMarkerItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
- Q_UNUSED(event);
+ event->setAccepted(false);
m_hovering = true;
emit m_marker->q_ptr->hovered(true);
+
+ QGraphicsObject::hoverEnterEvent(event);
}
void LegendMarkerItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
- Q_UNUSED(event);
+ event->setAccepted(false);
m_hovering = false;
emit m_marker->q_ptr->hovered(false);
+
+ QGraphicsObject::hoverLeaveEvent(event);
}
QString LegendMarkerItem::displayedLabel() const
diff --git a/src/charts/legend/legendmoveresizehandler.cpp b/src/charts/legend/legendmoveresizehandler.cpp
new file mode 100644
index 00000000..24711b4c
--- /dev/null
+++ b/src/charts/legend/legendmoveresizehandler.cpp
@@ -0,0 +1,287 @@
+/****************************************************************************
+**
+** 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 <QtGui/QCursor>
+#include <QtWidgets/QGraphicsSceneMouseEvent>
+#include <QtWidgets/QGraphicsSceneHoverEvent>
+#include <QtWidgets/QGraphicsScene>
+#include <QtCharts/QLegendMarker>
+#include <QtCharts/QChart>
+#include <private/legendlayout_p.h>
+#include <private/legendmoveresizehandler_p.h>
+
+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<QGraphicsItem*> 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
diff --git a/src/charts/legend/legendmoveresizehandler_p.h b/src/charts/legend/legendmoveresizehandler_p.h
new file mode 100644
index 00000000..feae804f
--- /dev/null
+++ b/src/charts/legend/legendmoveresizehandler_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt Chart API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef LEGENDMOVERESIZEHANDLER_P_H
+#define LEGENDMOVERESIZEHANDLER_P_H
+
+#include <QtCharts/QChartGlobal>
+#include <QtCore/QPointF>
+#include <QtCharts/qlegend.h>
+#include <QtCharts/private/qlegend_p.h>
+#include <QtCharts/private/qchartglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsSceneMouseEvent;
+class QGraphicsSceneHoverEvent;
+
+class Q_CHARTS_PRIVATE_EXPORT LegendMoveResizeHandler
+{
+public:
+ LegendMoveResizeHandler(QLegend *legend);
+ ~LegendMoveResizeHandler();
+
+ inline bool shouldShowMoveHint() const { return m_mode == MousePosition::Top; }
+ void reset();
+
+ void handleMousePressEvent(QGraphicsSceneMouseEvent *event);
+ void handleMouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ void handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ void handleHoverEnterEvent(QGraphicsSceneHoverEvent *event);
+ void handleHoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ void handleHoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+
+private:
+ enum class Action {
+ Idle = 0x00,
+ Hovered = 0x01,
+ Pressed = 0x02,
+ Moving = 0x04,
+ Resizing= 0x08
+ };
+
+ enum class MousePosition {
+ Nowhere,
+ TopLeft,
+ BottomRight,
+ BottomLeft,
+ TopRight,
+ Top,
+ Bottom,
+ Left,
+ Right
+ };
+
+ void setMouseCursor();
+ void setMouseCursor(MousePosition mpos);
+ void determineMousePosition(QPointF fromPoint);
+
+ QLegend *m_legend{nullptr};
+ QPointF m_moveOffset{0.0,0.0};
+ Action m_action{Action::Idle};
+ MousePosition m_mode{MousePosition::Nowhere};
+ qreal m_reattachThreshold{0.0};
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/charts/legend/legendscroller.cpp b/src/charts/legend/legendscroller.cpp
index 013b874d..1b174b55 100644
--- a/src/charts/legend/legendscroller.cpp
+++ b/src/charts/legend/legendscroller.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -27,18 +27,41 @@
**
****************************************************************************/
-#include <QtCore/QDebug>
#include <QtWidgets/QGraphicsSceneMouseEvent>
+#include <QtWidgets/QGraphicsSceneHoverEvent>
#include <QtWidgets/QGraphicsScene>
+#include <QtWidgets/QStylePainter>
+#include <QtWidgets/QStyleOption>
#include <QtCharts/QLegendMarker>
+#include <QtCharts/QChart>
#include <private/qlegendmarker_p.h>
+#include <private/legendlayout_p.h>
#include <private/legendmarkeritem_p.h>
#include <private/legendscroller_p.h>
+#include <private/legendmoveresizehandler_p.h>
QT_BEGIN_NAMESPACE
LegendScroller::LegendScroller(QChart *chart) : QLegend(chart)
{
+ connect(this, &QLegend::interactiveChanged, this, &LegendScroller::handleInteractiveChanged);
+}
+
+void LegendScroller::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ QLegend::paint(painter, option, widget);
+
+ if (!isInteractive())
+ return;
+
+ if (!isAttachedToChart() && d_ptr->m_resizer->shouldShowMoveHint()) {
+ QStyle *s = this->style();
+ QStylePainter stylePainter;
+ QStyleOptionRubberBand rubberBandOption;
+ QRectF rubberBandRectF(0, 0, geometry().width(), d_ptr->m_layout->contentsRect().y());
+ rubberBandOption.rect = rubberBandRectF.toRect();
+ s->drawControl(QStyle::CE_RubberBand, &rubberBandOption, painter);
+ }
}
void LegendScroller::setOffset(const QPointF &point)
@@ -53,28 +76,148 @@ QPointF LegendScroller::offset() const
void LegendScroller::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
- Scroller::handleMousePressEvent(event);
+ if (!isInteractive() || isAttachedToChart()) {
+ Scroller::handleMousePressEvent(event);
+ return;
+ }
+
+ m_forwardMouseEvents = false;
+ m_forwardHoverEvents = false;
+
+ QRectF contentRect = geometry();
+ qreal left, top, right, bottom;
+ d_ptr->m_layout->getContentsMargins(&left, &top, &right, &bottom);
+ contentRect.adjust(left, top, -right, -bottom);
+ contentRect.moveTo(left, top);
+ QPointF pt = event->pos();
+
+ if (contentRect.contains(pt)) {
+ Scroller::handleMousePressEvent(event);
+ } else {
+ d_ptr->m_resizer->handleMousePressEvent(event);
+ updateForResizerChange();
+ m_forwardMouseEvents = event->isAccepted();
+ }
}
void LegendScroller::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
- Scroller::handleMouseMoveEvent(event);
+ if (m_forwardMouseEvents) {
+ d_ptr->m_resizer->handleMouseMoveEvent(event);
+ updateForResizerChange();
+ } else {
+ Scroller::handleMouseMoveEvent(event);
+ }
}
void LegendScroller::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
- Scroller::handleMouseReleaseEvent(event);
+ if (m_forwardMouseEvents) {
+ d_ptr->m_resizer->handleMouseReleaseEvent(event);
+ updateForResizerChange();
+ m_forwardMouseEvents = false;
+ } else {
+ Scroller::handleMouseReleaseEvent(event);
- if (!event->isAccepted()) {
- QList<QGraphicsItem *> items = scene()->items(event->scenePos());
+ if (!event->isAccepted()) {
+ const QList<QGraphicsItem *> items = scene()->items(event->scenePos());
- foreach (QGraphicsItem *i, items) {
- if (d_ptr->m_markerHash.contains(i)) {
- QLegendMarker *marker = d_ptr->m_markerHash.value(i);
- emit marker->clicked();
+ for (const auto i : items) {
+ if (d_ptr->m_markerHash.contains(i)) {
+ QLegendMarker *marker = d_ptr->m_markerHash.value(i);
+ emit marker->clicked();
+ }
}
+ event->accept();
+ }
+ }
+}
+
+void LegendScroller::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *)
+{
+ if (isInteractive() && isAttachedToChart())
+ detachFromChart();
+}
+
+void LegendScroller::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
+{
+ if (isInteractive() && !isAttachedToChart()) {
+ m_forwardHoverEvents = true;
+ d_ptr->m_resizer->handleHoverEnterEvent(event);
+ updateForResizerChange();
+ } else {
+ QLegend::hoverEnterEvent(event);
+ }
+}
+
+void LegendScroller::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ if (!isInteractive() || isAttachedToChart()) {
+ QLegend::hoverMoveEvent(event);
+ return;
+ }
+
+ bool stopForwarding = d_ptr->m_layout->contentsRect().contains(event->pos());
+ if (stopForwarding) {
+ if (m_forwardHoverEvents) {
+ // the event position is no longer over the border
+ // frame, so send a leave. This will set the
+ // mouse cursor back to an arrow.
+ m_forwardHoverEvents = false;
+ d_ptr->m_resizer->handleHoverLeaveEvent(event);
+ updateForResizerChange();
+ }
+ } else {
+ if (!m_forwardHoverEvents) {
+ // if we're not already forwarding events, and we
+ // shouldn't stop forwarding, then we should
+ // start forwarding. This would happen if the event
+ // position was inside the layout's content rect
+ // on the _previous_ move event, and now the position
+ // is on the border frame. So, this fakes a hover
+ // enter from the _inside_ (which we otherwise
+ // would not get).
+ m_forwardHoverEvents = true;
+ d_ptr->m_resizer->handleHoverEnterEvent(event);
+ updateForResizerChange();
}
- event->accept();
+ }
+
+ if (m_forwardHoverEvents) {
+ d_ptr->m_resizer->handleHoverMoveEvent(event);
+ updateForResizerChange();
+ } else {
+ QLegend::hoverMoveEvent(event);
+ }
+}
+
+void LegendScroller::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ if (m_forwardHoverEvents) {
+ m_forwardHoverEvents = false;
+ d_ptr->m_resizer->handleHoverLeaveEvent(event);
+ updateForResizerChange();
+ } else {
+ QLegend::hoverLeaveEvent(event);
+ }
+}
+
+void LegendScroller::handleInteractiveChanged(bool interactive)
+{
+ setAcceptHoverEvents(interactive);
+ m_cachedShouldShowMoveEvents = d_ptr->m_resizer->shouldShowMoveHint();
+ m_forwardMouseEvents = false;
+ m_forwardHoverEvents = false;
+ d_ptr->m_resizer->reset();
+ update();
+}
+
+void LegendScroller::updateForResizerChange()
+{
+ LegendMoveResizeHandler* resizer = d_ptr->m_resizer;
+ if (resizer->shouldShowMoveHint() != m_cachedShouldShowMoveEvents) {
+ m_cachedShouldShowMoveEvents = resizer->shouldShowMoveHint();
+ update();
}
}
diff --git a/src/charts/legend/legendscroller_p.h b/src/charts/legend/legendscroller_p.h
index a22f5088..27fc718d 100644
--- a/src/charts/legend/legendscroller_p.h
+++ b/src/charts/legend/legendscroller_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -54,6 +54,7 @@ class Q_CHARTS_PRIVATE_EXPORT LegendScroller: public QLegend, public Scroller
public:
LegendScroller(QChart *chart);
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
void setOffset(const QPointF &point) override;
QPointF offset() const override;
@@ -61,6 +62,18 @@ public:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
+ void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
+ void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
+ void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
+
+private:
+ void handleInteractiveChanged(bool interactive);
+ void updateForResizerChange();
+
+ bool m_forwardMouseEvents{false};
+ bool m_forwardHoverEvents{false};
+ bool m_cachedShouldShowMoveEvents{false};
};
QT_END_NAMESPACE
diff --git a/src/charts/legend/qlegend.cpp b/src/charts/legend/qlegend.cpp
index 90fd3458..4c481641 100644
--- a/src/charts/legend/qlegend.cpp
+++ b/src/charts/legend/qlegend.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -38,6 +38,7 @@
#include <QtCharts/QLegendMarker>
#include <private/qlegendmarker_p.h>
#include <private/legendmarkeritem_p.h>
+#include <private/legendmoveresizehandler_p.h>
#include <private/chartdataset_p.h>
#include <QtGui/QPainter>
#include <QtGui/QPen>
@@ -309,8 +310,20 @@ void QLegend::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q
painter->setOpacity(opacity());
painter->setPen(d_ptr->m_pen);
painter->setBrush(d_ptr->m_brush);
- painter->drawRoundedRect(rect(), d_ptr->roundness(rect().width()), d_ptr->roundness(rect().height()),
- Qt::RelativeSize);
+ painter->drawRoundedRect(rect(), d_ptr->roundness(rect().width()),
+ d_ptr->roundness(rect().height()), Qt::RelativeSize);
+
+ // Draw a border slightly larger than the contents rectangle
+ // of the layout so we don't incur overlap. The border gives the
+ // impression of a move/resize frame around the legend to the user
+ // and demarcates its boundaries.
+ if (!isAttachedToChart()) {
+ QRectF frameRect = d_ptr->m_layout->contentsRect();
+ frameRect.adjust(-1., -1., 1., 1.);
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRoundedRect(frameRect, d_ptr->roundness(rect().width()),
+ d_ptr->roundness(rect().height()), Qt::RelativeSize);
+ }
}
@@ -570,6 +583,39 @@ void QLegend::setShowToolTips(bool show)
}
}
+/*!
+ Returns whether the legend can be dragged or resized using a mouse when it is detached.
+
+ \sa QLegend::setInteractive()
+ \since 6.2
+*/
+
+bool QLegend::isInteractive() const
+{
+ return d_ptr->m_interactive;
+}
+
+/*!
+ When \a interactive is \c true and the legend is detached, the legend is able to be moved and
+ resized with a mouse in a similar way to a window.
+
+ The legend will automatically attach to an edge of the chart by dragging it off of that edge.
+ Double clicking an attached legend will detach it.
+ This is \c false by default.
+
+ \sa QLegend::isInteractive()
+ \since 6.2
+*/
+
+void QLegend::setInteractive(bool interactive)
+{
+ if (d_ptr->m_interactive != interactive) {
+ d_ptr->m_interactive = interactive;
+ update();
+ emit interactiveChanged(interactive);
+ }
+}
+
QLegend::MarkerShape QLegend::markerShape() const
{
return d_ptr->m_markerShape;
@@ -615,6 +661,7 @@ QLegendPrivate::QLegendPrivate(ChartPresenter *presenter, QChart *chart, QLegend
: q_ptr(q),
m_presenter(presenter),
m_layout(new LegendLayout(q)),
+ m_resizer(new LegendMoveResizeHandler(q)),
m_chart(chart),
m_items(new QGraphicsItemGroup(q)),
m_alignment(Qt::AlignTop),
@@ -626,6 +673,7 @@ QLegendPrivate::QLegendPrivate(ChartPresenter *presenter, QChart *chart, QLegend
m_backgroundVisible(false),
m_reverseMarkers(false),
m_showToolTips(false),
+ m_interactive(false),
m_markerShape(QLegend::MarkerShapeRectangle)
{
m_items->setHandlesChildEvents(false);
@@ -633,7 +681,7 @@ QLegendPrivate::QLegendPrivate(ChartPresenter *presenter, QChart *chart, QLegend
QLegendPrivate::~QLegendPrivate()
{
-
+ delete m_resizer;
}
void QLegendPrivate::setOffset(const QPointF &offset)
diff --git a/src/charts/legend/qlegend.h b/src/charts/legend/qlegend.h
index 5f36cfd3..b3ea96ec 100644
--- a/src/charts/legend/qlegend.h
+++ b/src/charts/legend/qlegend.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -111,6 +111,9 @@ public:
bool showToolTips() const;
void setShowToolTips(bool show);
+ bool isInteractive() const;
+ void setInteractive(bool interactive);
+
MarkerShape markerShape() const;
void setMarkerShape(MarkerShape shape);
@@ -128,12 +131,14 @@ Q_SIGNALS:
void showToolTipsChanged(bool showToolTips);
void markerShapeChanged(MarkerShape shape);
Q_REVISION(6, 2) void attachedToChartChanged(bool attachedToChart);
+ void interactiveChanged(bool interactive);
private:
QScopedPointer<QLegendPrivate> d_ptr;
Q_DISABLE_COPY(QLegend)
friend class LegendScroller;
friend class LegendLayout;
+ friend class LegendMoveResizeHandler;
friend class ChartLayout;
friend class LegendMarkerItem;
friend class QLegendMarkerPrivate;
diff --git a/src/charts/legend/qlegend_p.h b/src/charts/legend/qlegend_p.h
index 0e367150..ae1b2611 100644
--- a/src/charts/legend/qlegend_p.h
+++ b/src/charts/legend/qlegend_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -48,6 +48,7 @@ class QChart;
class ChartPresenter;
class QAbstractSeries;
class LegendLayout;
+class LegendMoveResizeHandler;
class QLegendMarker;
class Q_CHARTS_PRIVATE_EXPORT QLegendPrivate : public QObject
@@ -88,6 +89,7 @@ private:
QLegend *q_ptr;
ChartPresenter *m_presenter;
LegendLayout *m_layout;
+ LegendMoveResizeHandler *m_resizer;
QChart *m_chart;
QGraphicsItemGroup *m_items;
Qt::Alignment m_alignment;
@@ -101,6 +103,7 @@ private:
bool m_backgroundVisible;
bool m_reverseMarkers;
bool m_showToolTips;
+ bool m_interactive;
QLegend::MarkerShape m_markerShape;
QList<QLegendMarker *> m_markers;
@@ -113,6 +116,7 @@ private:
friend class LegendLayout;
friend class QLegendMarkerPrivate;
friend class LegendScroller;
+ friend class LegendMoveResizeHandler;
};
QT_END_NAMESPACE