mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Smart Layout in Tile View
The tile view will now layout the charts in a more efficient manner. It still attempts to run from top to bottom and then left to right when laying out charts, but now, if there is any space and the chart will fit in it, it will be moved. This means you can layout the view with smaller charts surrounding or to the left of right of a larger one. This is particularly useful when looking at say a large summary window with a smaller map and a route profile etc. It can be a little disconcerting however, since charts will suddenly 'disappear' when you resize them -- of course they don't disappear they just move to a space that can accomodate them. We could make 'smart' layouts optional if users get thrown by it. Lastly, fixed a bug when resizing charts -- if they move position when resizing then the delta (size change) is miscalculated. This is avoided now by ending any resize action when a chart moves. Fixes #453.
This commit is contained in:
@@ -19,6 +19,8 @@
|
||||
#include <QtGui>
|
||||
#include "GcWindowLayout.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
GcWindowLayout::GcWindowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
|
||||
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
|
||||
{
|
||||
@@ -123,42 +125,147 @@ QSize GcWindowLayout::minimumSize() const
|
||||
return size;
|
||||
}
|
||||
|
||||
// we sort the points to try from top to bottom
|
||||
// and then left to right since this is the sequence
|
||||
// we follow to layout the charts
|
||||
class QPointS : public QPoint
|
||||
{
|
||||
public:
|
||||
QPointS(int x, int y) : QPoint(x,y) {}
|
||||
|
||||
bool operator< (QPointS right) const {
|
||||
if (y() == right.y())
|
||||
return x() < right.x();
|
||||
else
|
||||
return y() < right.y();
|
||||
}
|
||||
};
|
||||
|
||||
int GcWindowLayout::doLayout(const QRect &rect, bool testOnly) const
|
||||
{
|
||||
// geometry for the layout
|
||||
int left, top, right, bottom;
|
||||
int spaceX = horizontalSpacing();
|
||||
int spaceY = verticalSpacing();
|
||||
getContentsMargins(&left, &top, &right, &bottom);
|
||||
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
|
||||
int x = effectiveRect.x();
|
||||
int y = effectiveRect.y();
|
||||
int lineHeight = 0;
|
||||
|
||||
QLayoutItem *item;
|
||||
foreach (item, itemList) {
|
||||
// the space we have to line up our charts
|
||||
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
|
||||
|
||||
// Charts already positioned are held here
|
||||
QList <QRect> placed;
|
||||
|
||||
// iterate through each chart, placing it in the
|
||||
// most optimal location top to bottom then
|
||||
// left to right (packed flow)
|
||||
foreach (QLayoutItem *item, itemList) {
|
||||
|
||||
// what widget is in this item
|
||||
QWidget *wid = item->widget();
|
||||
int spaceX = horizontalSpacing();
|
||||
|
||||
// handle when no spacing is set (always is for HomeWindow layouts)
|
||||
if (spaceX == -1)
|
||||
spaceX = wid->style()->layoutSpacing(
|
||||
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
|
||||
int spaceY = verticalSpacing();
|
||||
spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
|
||||
if (spaceY == -1)
|
||||
spaceY = wid->style()->layoutSpacing(
|
||||
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
|
||||
int nextX = x + item->sizeHint().width() + spaceX;
|
||||
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
|
||||
spaceY = wid->style()->layoutSpacing( QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
|
||||
|
||||
|
||||
// the location we are going to place
|
||||
int x=-1, y=-1;
|
||||
|
||||
if (placed.count() == 0) { // is first item!
|
||||
|
||||
x = effectiveRect.x();
|
||||
y = y + lineHeight + spaceY;
|
||||
nextX = x + item->sizeHint().width() + spaceX;
|
||||
lineHeight = 0;
|
||||
y = effectiveRect.y();
|
||||
|
||||
} else {
|
||||
|
||||
// get a list of all the locations that the chart could be placed at
|
||||
QList<QPointS> attempts;
|
||||
|
||||
// iterate through the charts and create a list of placement
|
||||
// points that we can attempt to put the current chart
|
||||
foreach(QRect here, placed) {
|
||||
|
||||
// always try to the right of another chart
|
||||
attempts.append(QPointS(here.x()+here.width()+spaceX, here.y()));
|
||||
|
||||
int ax = here.x();
|
||||
foreach(QRect there, placed)
|
||||
attempts.append(QPointS(ax, there.y() + there.height() + spaceY));
|
||||
}
|
||||
|
||||
// now sort the list in order of preference
|
||||
qSort(attempts);
|
||||
|
||||
// see if it fits!
|
||||
foreach(QPointS attempt, attempts) {
|
||||
|
||||
QRect mine(attempt.x(),
|
||||
attempt.y(),
|
||||
item->sizeHint().width()+spaceX,
|
||||
item->sizeHint().height()+spaceY);
|
||||
|
||||
// doesn't even fit in the boundary
|
||||
if (mine.x()+mine.width()-spaceX > effectiveRect.right()) continue;
|
||||
|
||||
|
||||
// lets assume it will fit and check if it overlaps with existing
|
||||
bool fits = true;
|
||||
foreach (QRect here, placed) {
|
||||
|
||||
// add spacing
|
||||
QRect bigger(here.x(), here.y(), here.width()+spaceY, here.height()+spaceX);
|
||||
|
||||
// overlaps?
|
||||
if (bigger.intersects(mine)) {
|
||||
fits = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// it fits!
|
||||
if (fits == true) {
|
||||
x = attempt.x();
|
||||
y = attempt.y();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!testOnly)
|
||||
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
|
||||
// its just too big ... fnarr fnarr
|
||||
// stick it at the bottom
|
||||
// this can happen when the layout is being called
|
||||
// to set geometry when width and height are being
|
||||
// derived
|
||||
if ((x < 0 || y < 0) && placed.count()) {
|
||||
int maxy=0;
|
||||
foreach(QRect geom, placed) {
|
||||
if (geom.y()+geom.height()+spaceY > maxy) {
|
||||
maxy = geom.y()+geom.height()+spaceY;
|
||||
}
|
||||
}
|
||||
x = effectiveRect.x();
|
||||
y = maxy;
|
||||
}
|
||||
|
||||
QRect placement(QPoint(x,y), item->sizeHint());
|
||||
placed.append(placement);
|
||||
|
||||
if (!testOnly) item->setGeometry(placement);
|
||||
|
||||
x = nextX;
|
||||
lineHeight = qMax(lineHeight, item->sizeHint().height());
|
||||
}
|
||||
return y + lineHeight - rect.y() + bottom;
|
||||
|
||||
// Return the length of the layout
|
||||
int maxy=0;
|
||||
foreach(QRect geom, placed) {
|
||||
if (geom.y()+geom.height()+spaceY > maxy) {
|
||||
maxy = geom.y()+geom.height()+spaceY;
|
||||
}
|
||||
}
|
||||
return maxy;
|
||||
}
|
||||
|
||||
int GcWindowLayout::smartSpacing(QStyle::PixelMetric pm) const
|
||||
{
|
||||
QObject *parent = this->parent();
|
||||
|
||||
@@ -113,6 +113,15 @@ bool GcWindow::resizable() const
|
||||
return _resizable;
|
||||
}
|
||||
|
||||
// if a window moves whilst it is being
|
||||
// resized we should let go, because the
|
||||
// geometry tends to warp to stupid or
|
||||
// tiny sizes as a result
|
||||
void GcWindow::moveEvent(QMoveEvent *)
|
||||
{
|
||||
if (dragState != Move) setDragState(None);
|
||||
}
|
||||
|
||||
void GcWindow::setGripped(bool x)
|
||||
{
|
||||
_gripped = x;
|
||||
|
||||
@@ -135,6 +135,7 @@ public:
|
||||
void setResizable(bool);
|
||||
bool resizable() const;
|
||||
|
||||
void moveEvent(QMoveEvent *); // we trap move events to ungrab during resize
|
||||
void setGripped(bool);
|
||||
bool gripped() const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user