legendlayout.cpp
408 lines
| 13.2 KiB
| text/x-c
|
CppLexer
Michal Klocek
|
r1535 | /**************************************************************************** | ||
** | ||||
Miikka Heikkinen
|
r2432 | ** Copyright (C) 2013 Digia Plc | ||
Michal Klocek
|
r1535 | ** All rights reserved. | ||
** For any questions to Digia, please use contact form at http://qt.digia.com | ||||
** | ||||
Miikka Heikkinen
|
r2574 | ** This file is part of the Qt Enterprise Charts Add-on. | ||
Michal Klocek
|
r1535 | ** | ||
** $QT_BEGIN_LICENSE$ | ||||
Miikka Heikkinen
|
r2574 | ** Licensees holding valid Qt Enterprise licenses may use this file in | ||
** accordance with the Qt Enterprise License Agreement provided with the | ||||
Michal Klocek
|
r1535 | ** Software or, alternatively, in accordance with the terms contained in | ||
** a written agreement between you and Digia. | ||||
** | ||||
** If you have questions regarding the use of this file, please use | ||||
** contact form at http://qt.digia.com | ||||
** $QT_END_LICENSE$ | ||||
** | ||||
****************************************************************************/ | ||||
Michal Klocek
|
r1534 | #include "legendlayout_p.h" | ||
#include "chartpresenter_p.h" | ||||
#include "qlegend_p.h" | ||||
Miikka Heikkinen
|
r2483 | #include "abstractchartlayout_p.h" | ||
Michal Klocek
|
r1535 | |||
sauimone
|
r2168 | #include "qlegendmarker_p.h" | ||
#include "legendmarkeritem_p.h" | ||||
#include "qlegendmarker.h" | ||||
Michal Klocek
|
r1534 | QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||
Jani Honkonen
|
r2104 | LegendLayout::LegendLayout(QLegend *legend) | ||
sauimone
|
r2196 | : m_legend(legend), | ||
m_offsetX(0), | ||||
m_offsetY(0) | ||||
Michal Klocek
|
r1534 | { | ||
} | ||||
LegendLayout::~LegendLayout() | ||||
{ | ||||
} | ||||
void LegendLayout::setOffset(qreal x, qreal y) | ||||
{ | ||||
bool scrollHorizontal = true; | ||||
Jani Honkonen
|
r2097 | switch (m_legend->alignment()) { | ||
case Qt::AlignTop: | ||||
case Qt::AlignBottom: | ||||
scrollHorizontal = true; | ||||
break; | ||||
case Qt::AlignLeft: | ||||
case Qt::AlignRight: | ||||
scrollHorizontal = false; | ||||
break; | ||||
Michal Klocek
|
r1534 | } | ||
// If detached, the scrolling direction is vertical instead of horizontal and vice versa. | ||||
Jani Honkonen
|
r2097 | if (!m_legend->isAttachedToChart()) | ||
Michal Klocek
|
r1534 | scrollHorizontal = !scrollHorizontal; | ||
QRectF boundingRect = geometry(); | ||||
Michal Klocek
|
r1965 | qreal left, top, right, bottom; | ||
getContentsMargins(&left, &top, &right, &bottom); | ||||
Jani Honkonen
|
r2097 | boundingRect.adjust(left, top, -right, -bottom); | ||
Michal Klocek
|
r1534 | |||
// Limit offset between m_minOffset and m_maxOffset | ||||
if (scrollHorizontal) { | ||||
Jani Honkonen
|
r2097 | if (m_width <= boundingRect.width()) | ||
return; | ||||
Michal Klocek
|
r1534 | |||
if (x != m_offsetX) { | ||||
m_offsetX = qBound(m_minOffsetX, x, m_maxOffsetX); | ||||
Jani Honkonen
|
r2097 | m_legend->d_ptr->items()->setPos(-m_offsetX, boundingRect.top()); | ||
Michal Klocek
|
r1534 | } | ||
Jani Honkonen
|
r2097 | } else { | ||
if (m_height <= boundingRect.height()) | ||||
return; | ||||
Michal Klocek
|
r1534 | |||
if (y != m_offsetY) { | ||||
m_offsetY = qBound(m_minOffsetY, y, m_maxOffsetY); | ||||
Jani Honkonen
|
r2097 | m_legend->d_ptr->items()->setPos(boundingRect.left(), -m_offsetY); | ||
Michal Klocek
|
r1534 | } | ||
} | ||||
} | ||||
QPointF LegendLayout::offset() const | ||||
{ | ||||
Jani Honkonen
|
r2097 | return QPointF(m_offsetX, m_offsetY); | ||
Michal Klocek
|
r1534 | } | ||
Michal Klocek
|
r2088 | void LegendLayout::invalidate() | ||
{ | ||||
QGraphicsLayout::invalidate(); | ||||
Jani Honkonen
|
r2097 | if (m_legend->isAttachedToChart()) | ||
Michal Klocek
|
r2088 | m_legend->d_ptr->m_presenter->layout()->invalidate(); | ||
} | ||||
Jani Honkonen
|
r2104 | void LegendLayout::setGeometry(const QRectF &rect) | ||
Michal Klocek
|
r1534 | { | ||
Michal Klocek
|
r1837 | m_legend->d_ptr->items()->setVisible(m_legend->isVisible()); | ||
Michal Klocek
|
r1534 | |||
Michal Klocek
|
r2082 | QGraphicsLayout::setGeometry(rect); | ||
Michal Klocek
|
r1837 | |||
Jani Honkonen
|
r2097 | if (m_legend->isAttachedToChart()) | ||
Michal Klocek
|
r1534 | setAttachedGeometry(rect); | ||
Jani Honkonen
|
r2097 | else | ||
Michal Klocek
|
r1534 | setDettachedGeometry(rect); | ||
} | ||||
Jani Honkonen
|
r2104 | void LegendLayout::setAttachedGeometry(const QRectF &rect) | ||
Michal Klocek
|
r1534 | { | ||
Jani Honkonen
|
r2097 | if (!rect.isValid()) | ||
return; | ||||
Michal Klocek
|
r1534 | |||
sauimone
|
r2196 | qreal oldOffsetX = m_offsetX; | ||
qreal oldOffsetY = m_offsetY; | ||||
Jani Honkonen
|
r2097 | m_offsetX = 0; | ||
m_offsetY = 0; | ||||
Michal Klocek
|
r1534 | |||
Jani Honkonen
|
r2097 | QSizeF size(0, 0); | ||
Michal Klocek
|
r1534 | |||
sauimone
|
r2185 | if (m_legend->d_ptr->markers().isEmpty()) { | ||
Jani Honkonen
|
r2097 | return; | ||
sauimone
|
r2170 | } | ||
Jani Honkonen
|
r2097 | |||
m_width = 0; | ||||
m_height = 0; | ||||
Michal Klocek
|
r1534 | |||
Michal Klocek
|
r1965 | qreal left, top, right, bottom; | ||
getContentsMargins(&left, &top, &right, &bottom); | ||||
Jani Honkonen
|
r2097 | QRectF geometry = rect.adjusted(left, top, -right, -bottom); | ||
Michal Klocek
|
r1534 | |||
sauimone
|
r2168 | switch(m_legend->alignment()) { | ||
Jani Honkonen
|
r2097 | case Qt::AlignTop: | ||
case Qt::AlignBottom: { | ||||
sauimone
|
r2168 | QPointF point(0,0); | ||
sauimone
|
r2193 | foreach (QLegendMarker *marker, m_legend->d_ptr->markers()) { | ||
LegendMarkerItem *item = marker->d_ptr->item(); | ||||
sauimone
|
r2170 | if (item->isVisible()) { | ||
sauimone
|
r2168 | item->setGeometry(geometry); | ||
item->setPos(point.x(),geometry.height()/2 - item->boundingRect().height()/2); | ||||
sauimone
|
r2193 | const QRectF &rect = item->boundingRect(); | ||
sauimone
|
r2168 | size = size.expandedTo(rect.size()); | ||
qreal w = rect.width(); | ||||
m_width+=w; | ||||
point.setX(point.x() + w); | ||||
} | ||||
Michal Klocek
|
r1534 | } | ||
sauimone
|
r2168 | if (m_width < geometry.width()) | ||
m_legend->d_ptr->items()->setPos(geometry.width() / 2 - m_width / 2, geometry.top()); | ||||
else | ||||
m_legend->d_ptr->items()->setPos(geometry.topLeft()); | ||||
m_height = size.height(); | ||||
Michal Klocek
|
r1534 | } | ||
sauimone
|
r2168 | break; | ||
Jani Honkonen
|
r2097 | case Qt::AlignLeft: | ||
case Qt::AlignRight: { | ||||
sauimone
|
r2168 | QPointF point(0,0); | ||
sauimone
|
r2193 | foreach (QLegendMarker *marker, m_legend->d_ptr->markers()) { | ||
LegendMarkerItem *item = marker->d_ptr->item(); | ||||
sauimone
|
r2170 | if (item->isVisible()) { | ||
sauimone
|
r2168 | item->setGeometry(geometry); | ||
item->setPos(point); | ||||
sauimone
|
r2193 | const QRectF &rect = item->boundingRect(); | ||
sauimone
|
r2168 | qreal h = rect.height(); | ||
size = size.expandedTo(rect.size()); | ||||
m_height+=h; | ||||
point.setY(point.y() + h); | ||||
} | ||||
} | ||||
if (m_height < geometry.height()) | ||||
m_legend->d_ptr->items()->setPos(geometry.left(), geometry.height() / 2 - m_height / 2); | ||||
else | ||||
m_legend->d_ptr->items()->setPos(geometry.topLeft()); | ||||
m_width = size.width(); | ||||
break; | ||||
Michal Klocek
|
r1534 | } | ||
} | ||||
Jani Honkonen
|
r2097 | |||
Michal Klocek
|
r1965 | m_minOffsetX = -left; | ||
m_minOffsetY = - top; | ||||
m_maxOffsetX = m_width - geometry.width() - right; | ||||
m_maxOffsetY = m_height - geometry.height() - bottom; | ||||
sauimone
|
r2196 | |||
setOffset(oldOffsetX, oldOffsetY); | ||||
Michal Klocek
|
r1534 | } | ||
Jani Honkonen
|
r2104 | void LegendLayout::setDettachedGeometry(const QRectF &rect) | ||
Michal Klocek
|
r1534 | { | ||
Jani Honkonen
|
r2097 | if (!rect.isValid()) | ||
return; | ||||
Michal Klocek
|
r1538 | |||
Michal Klocek
|
r1534 | // Detached layout is different. | ||
// In detached mode legend may have multiple rows and columns, so layout calculations | ||||
// differ a log from attached mode. | ||||
// Also the scrolling logic is bit different. | ||||
sauimone
|
r2196 | qreal oldOffsetX = m_offsetX; | ||
qreal oldOffsetY = m_offsetY; | ||||
Jani Honkonen
|
r2097 | m_offsetX = 0; | ||
m_offsetY = 0; | ||||
Michal Klocek
|
r1534 | |||
Michal Klocek
|
r1965 | qreal left, top, right, bottom; | ||
getContentsMargins(&left, &top, &right, &bottom); | ||||
Jani Honkonen
|
r2097 | QRectF geometry = rect.adjusted(left, top, -right, -bottom); | ||
Michal Klocek
|
r1965 | |||
Jani Honkonen
|
r2097 | QSizeF size(0, 0); | ||
Michal Klocek
|
r1534 | |||
sauimone
|
r2185 | QList<QLegendMarker *> markers = m_legend->d_ptr->markers(); | ||
Michal Klocek
|
r1534 | |||
Jani Honkonen
|
r2097 | if (markers.isEmpty()) | ||
return; | ||||
Michal Klocek
|
r1534 | |||
switch (m_legend->alignment()) { | ||||
Jani Honkonen
|
r2097 | case Qt::AlignTop: { | ||
Jani Honkonen
|
r2131 | QPointF point(0, 0); | ||
m_width = 0; | ||||
m_height = 0; | ||||
for (int i = 0; i < markers.count(); i++) { | ||||
sauimone
|
r2170 | LegendMarkerItem *item = markers.at(i)->d_ptr->item(); | ||
if (item->isVisible()) { | ||||
item->setGeometry(geometry); | ||||
item->setPos(point.x(),point.y()); | ||||
sauimone
|
r2193 | const QRectF &boundingRect = item->boundingRect(); | ||
sauimone
|
r2170 | qreal w = boundingRect.width(); | ||
qreal h = boundingRect.height(); | ||||
m_width = qMax(m_width,w); | ||||
m_height = qMax(m_height,h); | ||||
point.setX(point.x() + w); | ||||
if (point.x() + w > geometry.left() + geometry.width() - right) { | ||||
// Next item would go off rect. | ||||
point.setX(0); | ||||
point.setY(point.y() + h); | ||||
if (i+1 < markers.count()) { | ||||
Jani Honkonen
|
r2131 | m_height += h; | ||
Michal Klocek
|
r1534 | } | ||
} | ||||
} | ||||
} | ||||
Jani Honkonen
|
r2131 | m_legend->d_ptr->items()->setPos(geometry.topLeft()); | ||
m_minOffsetX = -left; | ||||
m_minOffsetY = -top; | ||||
m_maxOffsetX = m_width - geometry.width() - right; | ||||
m_maxOffsetY = m_height - geometry.height() - bottom; | ||||
} | ||||
break; | ||||
Jani Honkonen
|
r2097 | case Qt::AlignBottom: { | ||
Jani Honkonen
|
r2131 | QPointF point(0, geometry.height()); | ||
m_width = 0; | ||||
m_height = 0; | ||||
for (int i = 0; i < markers.count(); i++) { | ||||
sauimone
|
r2170 | LegendMarkerItem *item = markers.at(i)->d_ptr->item(); | ||
if (item->isVisible()) { | ||||
item->setGeometry(geometry); | ||||
sauimone
|
r2193 | const QRectF &boundingRect = item->boundingRect(); | ||
sauimone
|
r2170 | qreal w = boundingRect.width(); | ||
qreal h = boundingRect.height(); | ||||
m_width = qMax(m_width,w); | ||||
m_height = qMax(m_height,h); | ||||
item->setPos(point.x(),point.y() - h); | ||||
point.setX(point.x() + w); | ||||
if (point.x() + w > geometry.left() + geometry.width() - right) { | ||||
// Next item would go off rect. | ||||
point.setX(0); | ||||
point.setY(point.y() - h); | ||||
if (i+1 < markers.count()) { | ||||
Jani Honkonen
|
r2131 | m_height += h; | ||
Michal Klocek
|
r1534 | } | ||
} | ||||
} | ||||
} | ||||
sauimone
|
r2171 | m_legend->d_ptr->items()->setPos(geometry.topLeft()); | ||
Jani Honkonen
|
r2131 | |||
sauimone
|
r2171 | m_minOffsetX = -left; | ||
m_minOffsetY = -m_height + geometry.height() - top; | ||||
m_maxOffsetX = m_width - geometry.width() - right; | ||||
m_maxOffsetY = -bottom; | ||||
Jani Honkonen
|
r2131 | } | ||
break; | ||||
Jani Honkonen
|
r2097 | case Qt::AlignLeft: { | ||
Jani Honkonen
|
r2131 | QPointF point(0, 0); | ||
m_width = 0; | ||||
m_height = 0; | ||||
qreal maxWidth = 0; | ||||
for (int i = 0; i < markers.count(); i++) { | ||||
sauimone
|
r2170 | LegendMarkerItem *item = markers.at(i)->d_ptr->item(); | ||
if (item->isVisible()) { | ||||
item->setGeometry(geometry); | ||||
sauimone
|
r2193 | const QRectF &boundingRect = item->boundingRect(); | ||
Jani Honkonen
|
r2131 | qreal w = boundingRect.width(); | ||
qreal h = boundingRect.height(); | ||||
sauimone
|
r2170 | m_height = qMax(m_height,h); | ||
maxWidth = qMax(maxWidth,w); | ||||
item->setPos(point.x(),point.y()); | ||||
Jani Honkonen
|
r2131 | point.setY(point.y() + h); | ||
if (point.y() + h > geometry.bottom() - bottom) { | ||||
// Next item would go off rect. | ||||
point.setX(point.x() + maxWidth); | ||||
point.setY(0); | ||||
sauimone
|
r2170 | if (i+1 < markers.count()) { | ||
Jani Honkonen
|
r2131 | m_width += maxWidth; | ||
maxWidth = 0; | ||||
Michal Klocek
|
r1534 | } | ||
} | ||||
} | ||||
} | ||||
sauimone
|
r2171 | m_width += maxWidth; | ||
m_legend->d_ptr->items()->setPos(geometry.topLeft()); | ||||
Jani Honkonen
|
r2131 | |||
sauimone
|
r2171 | m_minOffsetX = -left; | ||
m_minOffsetY = -top; | ||||
m_maxOffsetX = m_width - geometry.width() - right; | ||||
m_maxOffsetY = m_height - geometry.height() - bottom; | ||||
Jani Honkonen
|
r2131 | } | ||
break; | ||||
Jani Honkonen
|
r2097 | case Qt::AlignRight: { | ||
Jani Honkonen
|
r2131 | QPointF point(geometry.width(), 0); | ||
m_width = 0; | ||||
m_height = 0; | ||||
qreal maxWidth = 0; | ||||
for (int i = 0; i < markers.count(); i++) { | ||||
sauimone
|
r2170 | LegendMarkerItem *item = markers.at(i)->d_ptr->item(); | ||
if (item->isVisible()) { | ||||
item->setGeometry(geometry); | ||||
sauimone
|
r2193 | const QRectF &boundingRect = item->boundingRect(); | ||
sauimone
|
r2170 | qreal w = boundingRect.width(); | ||
qreal h = boundingRect.height(); | ||||
m_height = qMax(m_height,h); | ||||
maxWidth = qMax(maxWidth,w); | ||||
item->setPos(point.x() - w,point.y()); | ||||
point.setY(point.y() + h); | ||||
if (point.y() + h > geometry.bottom()-bottom) { | ||||
Jani Honkonen
|
r2131 | // Next item would go off rect. | ||
point.setX(point.x() - maxWidth); | ||||
point.setY(0); | ||||
sauimone
|
r2170 | if (i+1 < markers.count()) { | ||
Jani Honkonen
|
r2131 | m_width += maxWidth; | ||
maxWidth = 0; | ||||
Michal Klocek
|
r1534 | } | ||
} | ||||
} | ||||
} | ||||
sauimone
|
r2171 | m_width += maxWidth; | ||
m_legend->d_ptr->items()->setPos(geometry.topLeft()); | ||||
Jani Honkonen
|
r2131 | |||
sauimone
|
r2171 | m_minOffsetX = - m_width + geometry.width() - left; | ||
m_minOffsetY = -top; | ||||
m_maxOffsetX = - right; | ||||
m_maxOffsetY = m_height - geometry.height() - bottom; | ||||
Jani Honkonen
|
r2131 | } | ||
break; | ||||
Jani Honkonen
|
r2097 | default: | ||
Michal Klocek
|
r1534 | break; | ||
} | ||||
sauimone
|
r2196 | setOffset(oldOffsetX, oldOffsetY); | ||
Michal Klocek
|
r1534 | } | ||
Jani Honkonen
|
r2104 | QSizeF LegendLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const | ||
Michal Klocek
|
r1534 | { | ||
sauimone
|
r2168 | QSizeF size(0, 0); | ||
qreal left, top, right, bottom; | ||||
getContentsMargins(&left, &top, &right, &bottom); | ||||
if(constraint.isValid()) { | ||||
sauimone
|
r2193 | foreach(QLegendMarker *marker, m_legend->d_ptr->markers()) { | ||
sauimone
|
r2170 | LegendMarkerItem *item = marker->d_ptr->item(); | ||
sauimone
|
r2168 | size = size.expandedTo(item->effectiveSizeHint(which)); | ||
} | ||||
size = size.boundedTo(constraint); | ||||
} | ||||
else if (constraint.width() >= 0) { | ||||
qreal width = 0; | ||||
qreal height = 0; | ||||
sauimone
|
r2193 | foreach(QLegendMarker *marker, m_legend->d_ptr->markers()) { | ||
sauimone
|
r2170 | LegendMarkerItem *item = marker->d_ptr->item(); | ||
sauimone
|
r2168 | width+=item->effectiveSizeHint(which).width(); | ||
height=qMax(height,item->effectiveSizeHint(which).height()); | ||||
} | ||||
size = QSizeF(qMin(constraint.width(),width), height); | ||||
} | ||||
else if (constraint.height() >= 0) { | ||||
qreal width = 0; | ||||
qreal height = 0; | ||||
sauimone
|
r2193 | foreach(QLegendMarker *marker, m_legend->d_ptr->markers()) { | ||
sauimone
|
r2170 | LegendMarkerItem *item = marker->d_ptr->item(); | ||
sauimone
|
r2168 | width=qMax(width,item->effectiveSizeHint(which).width()); | ||
height+=height,item->effectiveSizeHint(which).height(); | ||||
} | ||||
size = QSizeF(width,qMin(constraint.height(),height)); | ||||
} | ||||
else { | ||||
sauimone
|
r2193 | foreach(QLegendMarker *marker, m_legend->d_ptr->markers()) { | ||
sauimone
|
r2170 | LegendMarkerItem *item = marker->d_ptr->item(); | ||
sauimone
|
r2168 | size = size.expandedTo(item->effectiveSizeHint(which)); | ||
} | ||||
} | ||||
size += QSize(left + right, top + bottom); | ||||
return size; | ||||
Michal Klocek
|
r1534 | } | ||
QTCOMMERCIALCHART_END_NAMESPACE | ||||