From e7d88adcf7ee59d90bcae0b14aa0604ed1fce6ea 2012-03-23 15:43:04 From: sauimone Date: 2012-03-23 15:43:04 Subject: [PATCH] Scrolling logic to legend --- diff --git a/src/legendscrollbutton.cpp b/src/legendscrollbutton.cpp new file mode 100644 index 0000000..4a827eb --- /dev/null +++ b/src/legendscrollbutton.cpp @@ -0,0 +1,26 @@ +#include "legendscrollbutton_p.h" +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +LegendScrollButton::LegendScrollButton(ScrollButtonId id, QGraphicsItem *parent) + : QGraphicsPolygonItem(parent) + ,mId(id) +{ + setAcceptedMouseButtons(Qt::LeftButton); +} + +LegendScrollButton::ScrollButtonId LegendScrollButton::id() +{ + return mId; +} + +void LegendScrollButton::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + emit clicked(event); +} + +#include "moc_legendscrollbutton_p.cpp" + +QTCOMMERCIALCHART_END_NAMESPACE + diff --git a/src/legendscrollbutton_p.h b/src/legendscrollbutton_p.h new file mode 100644 index 0000000..f23f2a9 --- /dev/null +++ b/src/legendscrollbutton_p.h @@ -0,0 +1,38 @@ +#ifndef LEGENDSCROLLBUTTON_P_H +#define LEGENDSCROLLBUTTON_P_H + +#include +#include +#include + +QTCOMMERCIALCHART_BEGIN_NAMESPACE + +class LegendScrollButton : public QObject, public QGraphicsPolygonItem +{ + Q_OBJECT +public: + enum ScrollButtonId { + ScrollButtonIdLeft, + ScrollButtonIdRight, + ScrollButtonIdUp, + ScrollButtonIdDown + }; + + explicit LegendScrollButton(ScrollButtonId id, QGraphicsItem *parent = 0); + ScrollButtonId id(); + + virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); + +signals: + void clicked(QGraphicsSceneMouseEvent* event); + +public slots: + +private: + + ScrollButtonId mId; +}; + +QTCOMMERCIALCHART_END_NAMESPACE + +#endif // LEGENDSCROLLBUTTON_P_H diff --git a/src/qchart.cpp b/src/qchart.cpp index 82a1a56..82311f4 100644 --- a/src/qchart.cpp +++ b/src/qchart.cpp @@ -337,12 +337,44 @@ void QChart::updateLayout() // recalculate legend position if (m_legend) { if (m_legend->parentObject() == this) { - m_legend->setMaximumSize(rect.size()); - m_legend->setPos(rect.topLeft()); + updateLegendLayout(); } } } +void QChart::updateLegendLayout() +{ + QRectF plotRect = m_rect.adjusted(m_padding,m_padding, -m_padding, -m_padding); + QRectF legendRect; + + switch (m_legend->preferredLayout()) + { + case QLegend::PreferredLayoutTop:{ + legendRect = m_rect.adjusted(m_padding,0,-m_padding,-m_padding - plotRect.height()); + break; + } + case QLegend::PreferredLayoutBottom: { + legendRect = m_rect.adjusted(m_padding,m_padding + plotRect.height(),-m_padding,0); + break; + } + case QLegend::PreferredLayoutLeft: { + legendRect = m_rect.adjusted(0,m_padding,-m_padding - plotRect.width(),-m_padding); + break; + } + case QLegend::PreferredLayoutRight: { + legendRect = m_rect.adjusted(m_padding + plotRect.width(),m_padding,0,-m_padding); + break; + } + default: { + legendRect = plotRect; + break; + } + } + + m_legend->setMaximumSize(legendRect.size()); + m_legend->setPos(legendRect.topLeft()); +} + int QChart::padding() const { diff --git a/src/qchart.h b/src/qchart.h index a880eb3..a5d352c 100644 --- a/src/qchart.h +++ b/src/qchart.h @@ -101,6 +101,7 @@ private: void setBackgroundPadding(int padding); void setBackgroundDiameter(int diameter); void updateLayout(); + void updateLegendLayout(); private: Q_DISABLE_COPY(QChart) diff --git a/src/qlegend.cpp b/src/qlegend.cpp index 7efeb92..0100f4d 100644 --- a/src/qlegend.cpp +++ b/src/qlegend.cpp @@ -2,6 +2,7 @@ #include "qlegend.h" #include "qseries.h" #include "legendmarker_p.h" +#include "legendscrollbutton_p.h" #include "qxyseries.h" #include "qlineseries.h" #include "qareaseries.h" @@ -27,10 +28,23 @@ QLegend::QLegend(QGraphicsItem *parent) ,mSize(0,0) ,mMinimumSize(50,20) // TODO: magic numbers ,mMaximumSize(150,100) - ,m_brush(Qt::darkGray) // TODO: from theme? - ,mPreferredLayout(QLegend::PreferredLayoutVertical) + ,m_brush(Qt::darkGray) // TODO: from theme? + ,mPreferredLayout(QLegend::PreferredLayoutTop) + ,mFirstMarker(0) + ,mMargin(5) { - setVisible(false); +// setVisible(false); + + mScrollButtonLeft = new LegendScrollButton(LegendScrollButton::ScrollButtonIdLeft, this); + mScrollButtonRight = new LegendScrollButton(LegendScrollButton::ScrollButtonIdRight, this); + mScrollButtonUp = new LegendScrollButton(LegendScrollButton::ScrollButtonIdUp, this); + mScrollButtonDown = new LegendScrollButton(LegendScrollButton::ScrollButtonIdDown, this); + + connect(mScrollButtonLeft,SIGNAL(clicked(QGraphicsSceneMouseEvent*)),this,SLOT(handleScrollButtonClicked(QGraphicsSceneMouseEvent*))); + connect(mScrollButtonRight,SIGNAL(clicked(QGraphicsSceneMouseEvent*)),this,SLOT(handleScrollButtonClicked(QGraphicsSceneMouseEvent*))); + connect(mScrollButtonUp,SIGNAL(clicked(QGraphicsSceneMouseEvent*)),this,SLOT(handleScrollButtonClicked(QGraphicsSceneMouseEvent*))); + connect(mScrollButtonDown,SIGNAL(clicked(QGraphicsSceneMouseEvent*)),this,SLOT(handleScrollButtonClicked(QGraphicsSceneMouseEvent*))); + setZValue(ChartPresenter::LegendZValue); } @@ -39,7 +53,7 @@ void QLegend::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q Q_UNUSED(option) Q_UNUSED(widget) - painter->setOpacity(0.5); + painter->setOpacity(0.8); painter->setPen(m_pen); painter->setBrush(m_brush); painter->drawRect(boundingRect()); @@ -79,7 +93,12 @@ QPen QLegend::pen() const void QLegend::setPreferredLayout(QLegend::PreferredLayout preferred) { mPreferredLayout = preferred; - layoutChanged(); + updateLayout(); +} + +QLegend::PreferredLayout QLegend::preferredLayout() const +{ + return mPreferredLayout; } QSizeF QLegend::maximumSize() const @@ -90,7 +109,7 @@ QSizeF QLegend::maximumSize() const void QLegend::setMaximumSize(const QSizeF size) { mMaximumSize = size; - layoutChanged(); + updateLayout(); } void QLegend::setSize(const QSizeF size) @@ -107,7 +126,7 @@ void QLegend::setSize(const QSizeF size) void QLegend::setPos(const QPointF &pos) { mPos = pos; - layoutChanged(); + updateLayout(); } void QLegend::handleSeriesAdded(QSeries* series, Domain* domain) @@ -116,7 +135,7 @@ void QLegend::handleSeriesAdded(QSeries* series, Domain* domain) createMarkers(series); connectSeries(series); - layoutChanged(); + updateLayout(); } void QLegend::handleSeriesRemoved(QSeries* series) @@ -133,7 +152,7 @@ void QLegend::handleSeriesRemoved(QSeries* series) deleteMarkers(series); } - layoutChanged(); + updateLayout(); } void QLegend::handleAdded(QList slices) @@ -150,7 +169,7 @@ void QLegend::handleAdded(QList slices) mMarkers.append(marker); childItems().append(marker); } - layoutChanged(); + updateLayout(); } void QLegend::handleRemoved(QList slices) @@ -166,7 +185,36 @@ void QLegend::handleMarkerDestroyed() // TODO: what if more than one markers are destroyed and we update layout after first one? LegendMarker* m = static_cast (sender()); mMarkers.removeOne(m); - layoutChanged(); + updateLayout(); +} + +void QLegend::handleScrollButtonClicked(QGraphicsSceneMouseEvent *event) +{ + Q_UNUSED(event); // Maybe later somethin happens with right click... + + // TODO: detect sender object. scroll to appropiate direction. + LegendScrollButton* scrollButton = static_cast (sender()); + Q_ASSERT(scrollButton); + + switch (scrollButton->id()) { + case LegendScrollButton::ScrollButtonIdLeft: + case LegendScrollButton::ScrollButtonIdUp: { + // Lower limit is same in these cases + mFirstMarker--; + checkMarkerBounds(); + break; + } + case LegendScrollButton::ScrollButtonIdRight: + case LegendScrollButton::ScrollButtonIdDown: { + mFirstMarker++; + checkMarkerBounds(); + break; + } + default: { + break; + } + } + updateLayout(); } void QLegend::connectSeries(QSeries *series) @@ -371,111 +419,158 @@ void QLegend::deleteMarkers(QSeries *series) } } -void QLegend::layoutChanged() +void QLegend::updateLayout() { // Calculate layout for markers and text if (mMarkers.count() <= 0) { // Nothing to do return; } + checkMarkerBounds(); // Find out widest item. - qreal itemMaxWidth = 0; - qreal itemMaxHeight = 0; - foreach (LegendMarker* m, mMarkers) { - if (m->boundingRect().width() > itemMaxWidth) { - itemMaxWidth = m->boundingRect().width(); - } - if (m->boundingRect().height() > itemMaxHeight) { - itemMaxHeight = m->boundingRect().height(); - } - } + QSizeF markerMaxSize = maximumMarkerSize(); - int maxHorizontalItems = boundingRect().width() / itemMaxWidth; - int maxVerticalItems = boundingRect().height() / itemMaxHeight; + // Use max height as scroll button size + rescaleScrollButtons(QSize(markerMaxSize.height() ,markerMaxSize.height())); - if (mMarkers.count() > maxHorizontalItems * maxVerticalItems) { - // TODO: overlapping layout - //qDebug() << "Warning. Not enough space to layout all legend items properly."; - } - - qreal margin = 5; qreal totalWidth = 0; qreal totalHeight = 0; switch (mPreferredLayout) { - case QLegend::PreferredLayoutHorizontal: { - /* - qreal xStep = mMaximumSize.width() / (mMarkers.count()+1); - if (xStep > itemMaxWidth) { - xStep = itemMaxWidth; - } - qreal yStep = mMaximumSize.height() / (mMarkers.count()+1); - if (yStep > itemMaxHeight) { - yStep = itemMaxHeight; - }*/ - qreal xStep = itemMaxWidth; - qreal yStep = itemMaxHeight; - qreal x = mPos.x() + margin; - qreal y = mPos.y() + margin; - int row = 1; + // Both cases organise items horizontally + case QLegend::PreferredLayoutBottom: + case QLegend::PreferredLayoutTop: { + + qreal xStep = markerMaxSize.width(); + qreal x = mPos.x() + mMargin; + qreal y = mPos.y() + mMargin; int column = 0; - int maxRows = 1; int maxColumns = 1; - foreach (LegendMarker* m, mMarkers) { - maxRows = row; - m->setPos(x,y); - x += xStep; - column++; - if (column > maxColumns) { - maxColumns = column; - } - if ((x + itemMaxWidth + margin*2) > (mPos.x() + mMaximumSize.width())) { - x = mPos.x() + margin; - y += yStep; - row++; - column = 0; + qreal scrollButtonPadding = 0; + + // Set correct visibility for scroll scrollbuttons + if (scrollButtonsVisible()) { + mScrollButtonLeft->setVisible(true); + mScrollButtonRight->setVisible(true); + totalWidth += (mScrollButtonLeft->boundingRect().width() + mMargin) * 2; // scrollbuttons visible, so add their width to total width + x += mScrollButtonLeft->boundingRect().width() + mMargin; // start position changes by scrollbutton width + scrollButtonPadding = mScrollButtonLeft->boundingRect().width(); + } else { + mScrollButtonLeft->setVisible(false); + mScrollButtonRight->setVisible(false); + } + mScrollButtonUp->setVisible(false); + mScrollButtonDown->setVisible(false); + + for (int i=0; isetVisible(false); + } else { + if ((x + xStep + scrollButtonPadding) > (mPos.x() + mMaximumSize.width())) { + // This marker would go outside legend rect. + m->setVisible(false); + } else { + // This marker is ok + m->setVisible(true); + m->setPos(x,y); + x += xStep; + column++; + } } + maxColumns = column; } - totalWidth = maxColumns * itemMaxWidth + margin * 2; - totalHeight = maxRows * itemMaxHeight + margin * 2; + + mScrollButtonLeft->setPos(mPos.x() + mMargin, y); + mScrollButtonRight->setPos(x+mMargin,y); + + totalWidth += maxColumns * markerMaxSize.width() + mMargin * 2; + totalHeight = markerMaxSize.height() + mMargin * 2; + break; } - case QLegend::PreferredLayoutVertical: { - /* - qreal xStep = mMaximumSize.width() / (mMarkers.count()+1); - if (xStep > itemMaxWidth) { - xStep = itemMaxWidth; + // Both cases organize items vertically + case QLegend::PreferredLayoutLeft: + case QLegend::PreferredLayoutRight: { + qreal yStep = markerMaxSize.height(); + qreal x = mPos.x() + mMargin; + qreal y = mPos.y() + mMargin; + int row = 1; + int maxRows = 1; + qreal scrollButtonPadding = 0; + + // Set correct visibility for scroll scrollbuttons + if (scrollButtonsVisible()) { + mScrollButtonUp->setVisible(true); + mScrollButtonDown->setVisible(true); + totalHeight += (mScrollButtonUp->boundingRect().height() + mMargin) * 2; // scrollbuttons visible, so add their height to total height + y += mScrollButtonUp->boundingRect().height() + mMargin; // start position changes by scrollbutton height + scrollButtonPadding = mScrollButtonUp->boundingRect().height(); + } else { + mScrollButtonUp->setVisible(false); + mScrollButtonDown->setVisible(false); } - qreal yStep = mMaximumSize.height() / (mMarkers.count()+1); - if (yStep > itemMaxHeight) { - yStep = itemMaxHeight; - }*/ - qreal xStep = itemMaxWidth; - qreal yStep = itemMaxHeight; - qreal x = mPos.x() + margin; - qreal y = mPos.y() + margin; + mScrollButtonLeft->setVisible(false); + mScrollButtonRight->setVisible(false); + + for (int i=0; isetVisible(false); + } else { + if ((y + yStep + scrollButtonPadding) > (mPos.y() + mMaximumSize.height())) { + // This marker would go outside legend rect. + m->setVisible(false); + } else { + // This marker is ok + m->setVisible(true); + m->setPos(x,y); + y += yStep; + row++; + } + } + maxRows = row; + } + + mScrollButtonUp->setPos(mPos.x() + mMargin, mPos.y() + mMargin); + mScrollButtonDown->setPos(mPos.x() + mMargin, y + mMargin); + + totalWidth += markerMaxSize.width() + mMargin * 2; + totalHeight = maxRows * markerMaxSize.height() + mMargin * 4 + scrollButtonPadding; // TODO: check this + + /* + qreal yStep = markerMaxSize.height(); + qreal x = mPos.x() + mMargin; + qreal y = mPos.y() + mMargin; int row = 0; - int column = 1; int maxRows = 1; int maxColumns = 1; - foreach (LegendMarker* m, mMarkers) { - maxColumns = column; - m->setPos(x,y); - y += yStep; - row++; - if (row > maxRows) { - maxRows = row; - } - if ((y + itemMaxHeight + margin*2) > (mPos.y() + mMaximumSize.height())) { - y = mPos.y() + margin; - x += xStep; - column++; - row = 0; + for (int i=0; isetVisible(false); + } else { + if ((y + markerMaxSize.height() + mMargin*2) > (mPos.y() + mMaximumSize.height())) { + // This marker would go outside legend rect. + m->setVisible(false); + } else { + // This marker is ok + m->setVisible(true); + maxRows = row; + m->setPos(x,y); + y += yStep; + row++; + } } + maxRows = row; } - totalWidth = maxColumns * itemMaxWidth + margin * 2; - totalHeight = maxRows * itemMaxHeight + margin * 2; + totalWidth = maxColumns * markerMaxSize.width() + mMargin * 2; + totalHeight = maxRows * markerMaxSize.height() + mMargin * 2; + */ break; } default: { @@ -485,6 +580,81 @@ void QLegend::layoutChanged() mSize.setWidth(totalWidth); mSize.setHeight(totalHeight); + + update(); +} + +void QLegend::rescaleScrollButtons(const QSize &size) +{ + QPolygonF left; + left << QPointF(size.width(),0) << QPointF(0,size.height()/2) << QPointF(size.width(),size.height()); + QPolygonF right; + right << QPointF(0,0) << QPointF(size.width(),size.height()/2) << QPointF(0,size.height()); + QPolygonF up; + up << QPointF(0,size.height()) << QPointF(size.width()/2,0) << QPointF(size.width(),size.height()); + QPolygonF down; + down << QPointF(0,0) << QPointF(size.width()/2,size.height()) << QPointF(size.width(),0); + + mScrollButtonLeft->setPolygon(left); + mScrollButtonRight->setPolygon(right); + mScrollButtonUp->setPolygon(up); + mScrollButtonDown->setPolygon(down); +} + +QSizeF QLegend::maximumMarkerSize() +{ + QSizeF max(0,0); + foreach (LegendMarker* m, mMarkers) { + if (m->boundingRect().width() > max.width()) { + max.setWidth(m->boundingRect().width()); + } + if (m->boundingRect().height() > max.height()) { + max.setHeight(m->boundingRect().height()); + } + } + return max; +} + +void QLegend::checkMarkerBounds() +{ + if ((mPreferredLayout == QLegend::PreferredLayoutLeft) || (mPreferredLayout == QLegend::PreferredLayoutRight)) { + // Bounds limited by height + int max; + if (scrollButtonsVisible()) { + max = (mMaximumSize.height() - mScrollButtonLeft->boundingRect().height() * 2 - mMargin*2) / maximumMarkerSize().height(); + } else { + max = mMaximumSize.height() / maximumMarkerSize().height(); + } + + if (mFirstMarker > mMarkers.count() - max) { + mFirstMarker = mMarkers.count() - max; + } + } else { + // Bounds limited by width + int max; + if (scrollButtonsVisible()) { + max = (mMaximumSize.width() - mScrollButtonLeft->boundingRect().width() * 2 - mMargin*2) / maximumMarkerSize().width(); + } else { + max = mMaximumSize.width() / maximumMarkerSize().width(); + } + + if (mFirstMarker > mMarkers.count() - max) { + mFirstMarker = mMarkers.count() - max; + } + } + + if (mFirstMarker < 0) { + mFirstMarker = 0; + } +} + +bool QLegend::scrollButtonsVisible() +{ + // Just a helper to clarify, what the magic below means :) + if ((mPreferredLayout == QLegend::PreferredLayoutTop) || (mPreferredLayout == QLegend::PreferredLayoutBottom)) { + return (maximumMarkerSize().width() * mMarkers.count() + mMargin * 3 > mMaximumSize.width()); + } + return (maximumMarkerSize().height() * mMarkers.count() + mMargin * 3 > mMaximumSize.height()); } #include "moc_qlegend.cpp" diff --git a/src/qlegend.h b/src/qlegend.h index 99ede81..1048618 100644 --- a/src/qlegend.h +++ b/src/qlegend.h @@ -14,6 +14,7 @@ class QXYSeries; class QBarSet; class QBarSeries; class QPieSeries; +class LegendScrollButton; class QTCOMMERCIALCHART_EXPORT QLegend : public QGraphicsObject { @@ -21,8 +22,10 @@ class QTCOMMERCIALCHART_EXPORT QLegend : public QGraphicsObject public: enum PreferredLayout { - PreferredLayoutHorizontal, - PreferredLayoutVertical + PreferredLayoutTop, + PreferredLayoutBottom, + PreferredLayoutLeft, + PreferredLayoutRight, }; explicit QLegend(QGraphicsItem *parent = 0); @@ -37,6 +40,7 @@ public: QPen pen() const; void setPreferredLayout(QLegend::PreferredLayout preferred); + QLegend::PreferredLayout preferredLayout() const; QSizeF maximumSize() const; void setMaximumSize(const QSizeF size); @@ -56,6 +60,7 @@ public slots: void handleAdded(QList slices); void handleRemoved(QList slices); void handleMarkerDestroyed(); + void handleScrollButtonClicked(QGraphicsSceneMouseEvent* event); private: // PIMPL ---> @@ -66,8 +71,13 @@ private: void appendMarkers(QBarSeries* series); void appendMarkers(QPieSeries* series); void deleteMarkers(QSeries* series); - void layoutChanged(); - // <--- PIMPL +// void layoutChanged(); // This tries to fit all items to legend + void updateLayout(); // New version of layout. Fits items only to row or column and adds scrollbars. + void rescaleScrollButtons(const QSize& size); + QSizeF maximumMarkerSize(); + void checkMarkerBounds(); + bool scrollButtonsVisible(); +// void updateScrollButtonsLayout(); QPointF mPos; QSizeF mSize; @@ -79,6 +89,16 @@ private: QBrush m_brush; QPen m_pen; QLegend::PreferredLayout mPreferredLayout; + + int mFirstMarker; + + LegendScrollButton* mScrollButtonLeft; + LegendScrollButton* mScrollButtonRight; + LegendScrollButton* mScrollButtonUp; + LegendScrollButton* mScrollButtonDown; + + qreal mMargin; + // <--- PIMPL }; QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/src.pro b/src/src.pro index 7b67c2d..8ce0561 100644 --- a/src/src.pro +++ b/src/src.pro @@ -17,6 +17,7 @@ SOURCES += \ $$PWD/qseries.cpp \ $$PWD/qlegend.cpp \ $$PWD/legendmarker.cpp \ + $$PWD/legendscrollbutton.cpp \ $$PWD/chartbackground.cpp \ $$PWD/chart.cpp PRIVATE_HEADERS += \ @@ -26,6 +27,7 @@ PRIVATE_HEADERS += \ $$PWD/charttheme_p.h \ $$PWD/domain_p.h \ $$PWD/legendmarker_p.h \ + $$PWD/legendscrollbutton_p.h \ $$PWD/chartbackground_p.h \ $$PWD/chart_p.h PUBLIC_HEADERS += \