mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-04-15 05:32:21 +00:00
Refactored the placement of items in the ChartSpace (#4549)
Fixes #4467: * Tiles are dragged to a column, inserted into the columns tile order * Placing tiles left or right of the existing columns creates a new column (up to a fixed limit) * As there is no concept of lines, the placement must * always respect the users choice of a column * never move a tile horizontally without user interaction * Shift-resizing a tile allows spanning multiple columns * Improved change detection * Preventing superfluous layouting requests and order increments * Added a visual indicator where the item will be placed when dropped * Fixed a glitch when creating new columns on the right, when dragging beyond a newly created column on the right, the visual indicator was hidden and pseudo changes where detected.
This commit is contained in:
committed by
GitHub
parent
62ef8abe42
commit
a30b96cc8a
@@ -609,6 +609,18 @@ ChartSpace::updateGeometry()
|
||||
} else animation->setEasingCurve(QEasingCurve(QEasingCurve::OutQuint));
|
||||
|
||||
group->addAnimation(animation);
|
||||
} else if (item.invisible) {
|
||||
if (landingzone == nullptr) {
|
||||
QColor bgColor = GColor(CCARDBACKGROUND);
|
||||
bgColor.setAlpha(200);
|
||||
QPen pen(bgColor);
|
||||
pen.setWidth(10 * dpiXFactor);
|
||||
bgColor.setAlpha(100);
|
||||
QBrush brush(bgColor);
|
||||
landingzone = scene->addRect(item.geometry, pen, brush);
|
||||
} else if (landingzone->rect() != item.geometry) {
|
||||
landingzone->setRect(item.geometry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1012,10 +1024,15 @@ ChartSpace::eventFilter(QObject *, QEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
} else if (event->type() == QEvent::GraphicsSceneMouseRelease) {
|
||||
} else if (event->type() == QEvent::GraphicsSceneMouseRelease) {
|
||||
|
||||
// stop dragging
|
||||
if (state == DRAG || state == YRESIZE || state == SPAN || state == XRESIZE) {
|
||||
if (landingzone != nullptr) {
|
||||
scene->removeItem(landingzone);
|
||||
delete landingzone;
|
||||
landingzone = nullptr;
|
||||
}
|
||||
|
||||
// we want this one
|
||||
event->accept();
|
||||
@@ -1114,126 +1131,161 @@ ChartSpace::eventFilter(QObject *, QEvent *event)
|
||||
}
|
||||
|
||||
} else if (state == DRAG && !scrolling) { // dragging?
|
||||
|
||||
// whilst mouse moves, only update geom when changed
|
||||
bool changed = false;
|
||||
|
||||
// move the ChartSpaceItem being dragged
|
||||
stateData.drag.item->setPos(pos.x()-stateData.drag.offx, pos.y()-stateData.drag.offy);
|
||||
stateData.drag.item->setPos(pos.x() - stateData.drag.offx, pos.y() - stateData.drag.offy);
|
||||
|
||||
// should I move?
|
||||
QList<QGraphicsItem *> overlaps;
|
||||
foreach(QGraphicsItem *p, scene->items(pos))
|
||||
if(items.contains(static_cast<ChartSpaceItem*>(p)))
|
||||
overlaps << p;
|
||||
|
||||
// we always overlap with ourself, so see if more
|
||||
if (overlaps.count() > 1) {
|
||||
|
||||
ChartSpaceItem *over = static_cast<ChartSpaceItem*>(overlaps[1]);
|
||||
if (pos.y()-over->geometry().y() > over->geometry().height()/2) {
|
||||
|
||||
// place below the one its over
|
||||
stateData.drag.item->column = over->column;
|
||||
stateData.drag.item->order = over->order+1;
|
||||
for(int i=items.indexOf(over); i< items.count(); i++) {
|
||||
if (i>=0 && items[i]->column == over->column && items[i]->order > over->order && items[i] != stateData.drag.item) {
|
||||
items[i]->order += 1;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// place above the one its over
|
||||
stateData.drag.item->column = over->column;
|
||||
stateData.drag.item->order = over->order;
|
||||
for(int i=0; i< items.count(); i++) {
|
||||
if (i>=0 && items[i]->column == over->column && items[i]->order >= (over->order) && items[i] != stateData.drag.item) {
|
||||
items[i]->order += 1;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// columns are now variable width
|
||||
// create a new column to the right?
|
||||
int x=SPACING;
|
||||
// Nothing to do when mouse is over the landingzone
|
||||
if ( landingzone == nullptr
|
||||
|| ! landingzone->contains(pos)) {
|
||||
// What is my target column? Because of spans we can't rely only on overlaps
|
||||
int x = SPACING;
|
||||
int targetcol = -1;
|
||||
for(int i=0; i<10; i++) {
|
||||
if (pos.x() > x && pos.x() < (x+columns[i]+SPACING)) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (pos.x() > x && pos.x() < (x + columns[i] + SPACING)) {
|
||||
targetcol = i;
|
||||
break;
|
||||
}
|
||||
x += columns[i]+SPACING;
|
||||
x += columns[i] + SPACING;
|
||||
}
|
||||
|
||||
if (items.last()->column < 9 && targetcol < 0) {
|
||||
// don't keep moving - if we're already alone in column 0 then no move is needed
|
||||
if (stateData.drag.item->column != 0 || (items.count()>1 && items[1]->column == 0)) {
|
||||
|
||||
// new col to left
|
||||
for(int i=0; i< items.count(); i++) items[i]->column += 1;
|
||||
stateData.drag.item->column = 0;
|
||||
stateData.drag.item->order = 0;
|
||||
|
||||
// shift columns widths to the right
|
||||
for(int i=9; i>0; i--) columns[i] = columns[i-1];
|
||||
columns[0] = stateData.drag.width;
|
||||
|
||||
changed = true;
|
||||
// Do we overlap?
|
||||
ChartSpaceItem *over = nullptr;
|
||||
for (QGraphicsItem *p : scene->items(pos)) {
|
||||
if (p != landingzone) {
|
||||
ChartSpaceItem *testItem = static_cast<ChartSpaceItem*>(p);
|
||||
if (items.contains(testItem) && testItem != stateData.drag.item && testItem->column == targetcol) {
|
||||
over = testItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (targetcol > 0 && targetcol <= 9 && columnCount(targetcol) == 0) {
|
||||
// Find
|
||||
// * the highest column in active use
|
||||
// * all items currently placed in targetcol (excluding the dragged item)
|
||||
// This excludes items "only" spanning over this column
|
||||
QList<ChartSpaceItem*> columnItems;
|
||||
int highestcol = -1;
|
||||
for (ChartSpaceItem *item : items) {
|
||||
if (item->column == targetcol && item != stateData.drag.item) {
|
||||
columnItems << item;
|
||||
}
|
||||
highestcol = std::max(highestcol, item->column + item->span - 1);
|
||||
}
|
||||
std::sort(columnItems.begin(), columnItems.end(), LayoutChartSpaceItem::LayoutChartSpaceItemSort);
|
||||
|
||||
// we are over an empty column
|
||||
stateData.drag.item->column = targetcol;
|
||||
stateData.drag.item->order = 0;
|
||||
|
||||
// make column width same as source width
|
||||
columns[stateData.drag.item->column] = stateData.drag.width;
|
||||
|
||||
changed = true;
|
||||
|
||||
} else if (items.last()->column < 9 && items.last() && items.last()->column < targetcol) {
|
||||
|
||||
// new col to the right
|
||||
stateData.drag.item->column = items.last()->column + 1;
|
||||
stateData.drag.item->order = 0;
|
||||
|
||||
// make column width same as source width
|
||||
columns[stateData.drag.item->column] = stateData.drag.width;
|
||||
|
||||
changed = true;
|
||||
// Correct targetcol if it exceeds highestcol with a (then) empty highestcol
|
||||
targetcol = std::min(targetcol, highestcol + 1);
|
||||
if ( highestcol >= 0
|
||||
&& targetcol > highestcol
|
||||
&& columnCount(highestcol) == 1
|
||||
&& stateData.drag.item->column == highestcol) {
|
||||
targetcol = highestcol;
|
||||
}
|
||||
|
||||
if (over != nullptr) {
|
||||
if (pos.y() - over->geometry().y() > over->geometry().height() / 2) {
|
||||
// place below the one its over
|
||||
if ( stateData.drag.item->column != over->column
|
||||
|| stateData.drag.item->order <= over->order) {
|
||||
stateData.drag.item->column = over->column;
|
||||
stateData.drag.item->order = over->order + 1;
|
||||
changed = true;
|
||||
for (int i = columnItems.indexOf(over); i < columnItems.count(); i++) {
|
||||
if ( columnItems[i]->order > over->order
|
||||
&& columnItems[i] != stateData.drag.item) {
|
||||
columnItems[i]->order += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// place above the one its over
|
||||
if ( stateData.drag.item->column != over->column
|
||||
|| stateData.drag.item->order >= over->order) {
|
||||
stateData.drag.item->column = over->column;
|
||||
stateData.drag.item->order = over->order;
|
||||
changed = true;
|
||||
for (ChartSpaceItem *colItem : columnItems) {
|
||||
if ( colItem->order >= (over->order)
|
||||
&& colItem != stateData.drag.item) {
|
||||
colItem->order += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (targetcol < 0) {
|
||||
// create a new column to the right?
|
||||
if (highestcol < 9 && columnCount(0) > 1) {
|
||||
// Drop as new first column
|
||||
// don't keep moving - if we're already alone in column 0 then no move is needed
|
||||
// new col to left
|
||||
for (ChartSpaceItem *item : items) {
|
||||
item->column += 1;
|
||||
}
|
||||
stateData.drag.item->column = 0;
|
||||
stateData.drag.item->order = 0;
|
||||
|
||||
// add to the end of the column
|
||||
int last = -1;
|
||||
for(int i=0; i<items.count() && items[i]->column <= targetcol; i++) {
|
||||
if (items[i]->column == targetcol) last=i;
|
||||
// shift columns widths to the right
|
||||
for (int i = 9; i > 0; i--) {
|
||||
columns[i] = columns[i - 1];
|
||||
}
|
||||
columns[0] = stateData.drag.width;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
} else if (targetcol <= 9) {
|
||||
if (columnItems.count() > 0) {
|
||||
if (pos.y() > columnItems.last()->geometry().bottom()) {
|
||||
// Append below last
|
||||
if ( stateData.drag.item->column != targetcol
|
||||
|| ( stateData.drag.item->column == targetcol
|
||||
&& stateData.drag.item->order <= columnItems.last()->order)) {
|
||||
stateData.drag.item->column = targetcol;
|
||||
stateData.drag.item->order = columnItems.last()->order + 1;
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
// Insert above item
|
||||
for (ChartSpaceItem *colItem : columnItems) {
|
||||
if (! changed && pos.y() < colItem->geometry().top()) {
|
||||
if ( stateData.drag.item->column != targetcol
|
||||
|| ( stateData.drag.item->column == targetcol
|
||||
&& stateData.drag.item->order >= colItem->order)) {
|
||||
stateData.drag.item->column = targetcol;
|
||||
stateData.drag.item->order = colItem->order;
|
||||
changed = true;
|
||||
}
|
||||
if (! changed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
++colItem->order;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Add as first item
|
||||
if ( stateData.drag.item->column != targetcol
|
||||
|| stateData.drag.item->order != 0) {
|
||||
stateData.drag.item->column = targetcol;
|
||||
stateData.drag.item->order = 0;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// so long as its been dragged below the last entry on the column !
|
||||
if (last >= 0 && pos.y() > items[last]->geometry().bottom()) {
|
||||
stateData.drag.item->column = targetcol;
|
||||
stateData.drag.item->order = items[last]->order+1;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
|
||||
if (changed || landingzone == nullptr) {
|
||||
// drop it down
|
||||
updateGeometry();
|
||||
updateView();
|
||||
}
|
||||
|
||||
} else if (state == YRESIZE) {
|
||||
|
||||
// resize in rows, so in 75px units
|
||||
|
||||
@@ -323,7 +323,8 @@ class ChartSpace : public QWidget
|
||||
|
||||
// content
|
||||
QVector<int> columns; // column widths
|
||||
QList<ChartSpaceItem*> items; // tiles
|
||||
QList<ChartSpaceItem*> items; // tiles
|
||||
QGraphicsRectItem *landingzone = nullptr; // marker where the dragged ChartSpaceItem will be placed
|
||||
|
||||
// state data
|
||||
bool yresizecursor; // is the cursor set to resize?
|
||||
|
||||
Reference in New Issue
Block a user