From 2ee04838adf84df335e15cd89ffdfc077516efd1 Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Sun, 9 Jan 2011 18:47:02 +0000 Subject: [PATCH] HomeWindow chart move/drop The previous commit enabling tile 'bumping' as you move them around proved impractical with charts that largely filled the screen. This patch replaces this approach (original code is retained though) by using a 'cursor' to show where charts will be dropped / moved to and moving or dropping to this position. This means the layout code is complete, except for potentially adding support for multiple layouts (e.g. have saved layouts for different purposes like LTM charts for last 6 months or 28 days or an agenda view that shows past week next week calendar). --- src/GcWindowLayout.cpp | 6 ++ src/GcWindowLayout.h | 4 +- src/GoldenCheetah.cpp | 3 +- src/GoldenCheetah.h | 5 +- src/HomeWindow.cpp | 213 +++++++++++++++++++++++++++++++++++++++-- src/HomeWindow.h | 6 ++ 6 files changed, 225 insertions(+), 12 deletions(-) diff --git a/src/GcWindowLayout.cpp b/src/GcWindowLayout.cpp index 464c05bf3..f0d376533 100644 --- a/src/GcWindowLayout.cpp +++ b/src/GcWindowLayout.cpp @@ -43,6 +43,12 @@ void GcWindowLayout::addItem(QLayoutItem *item) itemList.append(item); } +void GcWindowLayout::insert(int index, QWidget *widget) +{ + addChildWidget(widget); + itemList.insert(index, new QWidgetItem(widget)); +} + int GcWindowLayout::horizontalSpacing() const { if (m_hSpace >= 0) { diff --git a/src/GcWindowLayout.h b/src/GcWindowLayout.h index f7e55688d..17e243f95 100644 --- a/src/GcWindowLayout.h +++ b/src/GcWindowLayout.h @@ -36,6 +36,8 @@ public: ~GcWindowLayout(); void addItem(QLayoutItem *item); + void insert(int index, QWidget *item); + int horizontalSpacing() const; int verticalSpacing() const; Qt::Orientations expandingDirections() const; @@ -49,8 +51,8 @@ public: QLayoutItem *takeAt(int index); private: - int doLayout(const QRect &rect, bool testOnly) const; int smartSpacing(QStyle::PixelMetric pm) const; + int doLayout(const QRect &rect, bool testOnly) const; QList itemList; int m_hSpace; diff --git a/src/GoldenCheetah.cpp b/src/GoldenCheetah.cpp index 0b3cf76aa..cf4036434 100644 --- a/src/GoldenCheetah.cpp +++ b/src/GoldenCheetah.cpp @@ -337,7 +337,8 @@ GcWindow::mouseMoveEvent(QMouseEvent *e) default: case Move : - move(oX + relx, oY + rely); + //move(oX + relx, oY + rely); + setCursor(Qt::DragMoveCursor); emit moving(this); break; diff --git a/src/GoldenCheetah.h b/src/GoldenCheetah.h index 0e4e6f5d4..44ab96f71 100644 --- a/src/GoldenCheetah.h +++ b/src/GoldenCheetah.h @@ -82,8 +82,6 @@ private: bool _resizable; bool _gripped; - // we paint a heading if there is space in the top margin - void paintEvent (QPaintEvent * event); enum drag { None, Close, Flip, Move, Left, Right, Top, Bottom, TLCorner, TRCorner, BLCorner, BRCorner }; typedef enum drag DragState; // state data for resizing tiles @@ -142,6 +140,9 @@ public: // for sorting... we look at x bool operator< (GcWindow right) const { return geometry().x() < right.geometry().x(); } + // we paint a heading if there is space in the top margin + void paintEvent (QPaintEvent * event); + // mouse actions -- resizing and dragging tiles bool eventFilter(QObject *object, QEvent *e); virtual void mousePressEvent(QMouseEvent *); diff --git a/src/HomeWindow.cpp b/src/HomeWindow.cpp index 417e24d9f..2993ac6ff 100644 --- a/src/HomeWindow.cpp +++ b/src/HomeWindow.cpp @@ -21,7 +21,7 @@ #include "LTMSettings.h" HomeWindow::HomeWindow(MainWindow *mainWindow) : - GcWindow(mainWindow), mainWindow(mainWindow), active(false), clicked(NULL) + GcWindow(mainWindow), mainWindow(mainWindow), active(false), clicked(NULL), dropPending(false), chartCursor(-2) { setInstanceName("Home Window"); setControls(new QStackedWidget(this)); @@ -98,8 +98,6 @@ HomeWindow::HomeWindow(MainWindow *mainWindow) : winWidget = new QWidget(this); winWidget->setContentsMargins(0,0,0,0); winWidget->setPalette(palette); - //tileWidget->setMouseTracking(true); - //tileWidget->installEventFilter(this); winFlow = new GcWindowLayout(winWidget, 0, 20, 20); winFlow->setContentsMargins(20,20,20,20); @@ -110,6 +108,8 @@ HomeWindow::HomeWindow(MainWindow *mainWindow) : winArea->setFrameStyle(QFrame::NoFrame); winArea->setContentsMargins(0,0,0,0); style->addWidget(winArea); + winWidget->installEventFilter(this); // to draw cursor + winWidget->setMouseTracking(true); // to draw cursor currentStyle=2; #ifdef Q_OS_MAC @@ -130,6 +130,10 @@ HomeWindow::HomeWindow(MainWindow *mainWindow) : #endif restoreState(); + + // watch drop operations + //setMouseTracking(true); + installEventFilter(this); } HomeWindow::~HomeWindow() @@ -270,8 +274,10 @@ HomeWindow::styleChanged(int id) void HomeWindow::dragEnterEvent(QDragEnterEvent *event) { - if (event->mimeData()->formats().contains("application/x-qabstractitemmodeldatalist")) + if (event->mimeData()->formats().contains("application/x-qabstractitemmodeldatalist")) { event->accept(); + dropPending = true; + } } void @@ -281,6 +287,8 @@ HomeWindow::dropEvent(QDropEvent *event) model.dropMimeData(event->mimeData(), Qt::CopyAction, 0,0, QModelIndex()); QString chart = model.data(model.index(0,0), Qt::DisplayRole).toString(); + dropPending = false; + // which one am i? for (int i = 0; GcWindows[i].id != GcWindowTypes::None; i++) { if (GcWindows[i].name == chart) { @@ -296,12 +304,21 @@ HomeWindow::dropEvent(QDropEvent *event) addChart(newone); newone->show(); } + + // before we return lets turn the cursor off + chartCursor = -2; + winWidget->repaint(); return; } } // nope not one of ours event->ignore(); + + // turn off cursor + chartCursor = -2; + winWidget->repaint(); + return; } @@ -317,7 +334,11 @@ HomeWindow::addChart(GcWindow* newone) QWidget *x = dynamic_cast(newone)->controls(); QWidget *c = (x != NULL) ? x : new QWidget(this); - m->addWidget(c); + + if (chartCursor >= 0) + m->insertWidget(chartCursor, c); + else + m->addWidget(c); // watch for enter events! newone->installEventFilter(this); @@ -372,12 +393,15 @@ HomeWindow::addChart(GcWindow* newone) else newone->setFixedHeight(newheight); newone->setContentsMargins(0,15,0,0); newone->setResizable(true); // we need to show on tab selection! - winFlow->addWidget(newone); + + if (chartCursor >= 0) winFlow->insert(chartCursor, newone); + else winFlow->addWidget(newone); } break; } // add to the list - charts.append(newone); + if (chartCursor >= 0) charts.insert(chartCursor, newone); + else charts.append(newone); newone->hide(); // watch for moves etc @@ -485,6 +509,29 @@ HomeWindow::resizeEvent(QResizeEvent *e) bool HomeWindow::eventFilter(QObject *object, QEvent *e) { + // we watch the mouse when + // dropping charts, to update + // the cursor position + // dropping and mouse move? + if (object == this) { + if (dropPending == true && e->type() == QEvent::DragMove) { + QPoint pos = winWidget->mapFromGlobal(QCursor::pos()); + winArea->ensureVisible(pos.x(), pos.y(), 20, 20); + chartCursor = pointTile(pos); + winWidget->repaint(); // show cursor + } + return false; + } + + // draw a cursor when winWidget paint event kicks off + if (object == winWidget) { + if (e->type() == QEvent::Paint) drawCursor(); + return false; + } + + // From here on, we're looking at events + // from the charts... + // is it a mouse enter event? if (e->type() == QEvent::Enter) { @@ -556,10 +603,108 @@ HomeWindow::eventFilter(QObject *object, QEvent *e) return false; } +// which tile to place before, -1 means append +int +HomeWindow::pointTile(QPoint pos) +{ + // find the window that is to the right of + // the drop point + + // is the cursor above a widget? + int i; + int here = -1; // chart number to the right of cursor... + for (i=0; igeometry().contains(pos)) { + here = i; + break; + } + } + + // is it to the right? + if (here == -1) { + + int toRight = pos.x(); + while (here == -1 && toRight < winArea->width()) { + toRight += 20; + QPoint right = QPoint(toRight, pos.y()); + for (i=0; igeometry().contains(right)) { + here = i; + break; + } + } + } + } + + // is it to the left? + if (here == -1) { + + int toLeft = pos.x(); + while (here == -1 && toLeft > 0) { + toLeft -= 20; + QPoint left = QPoint(toLeft, pos.y()); + for (i=0; igeometry().contains(left)) { + here = i+1; + break; + } + } + } + } + + // well it must be below, so if there is + // anything at y+20, use that, otherwise + // we can assume we are at the end and + // append to the list... + if (here == -1) { + QPoint below(20, pos.y()+20); + for (i=0; igeometry().contains(below)) { + here = i; + break; + } + } + } + + // all done + if (here == -1 || here >= charts.count()) return -1; + return here; +} + void HomeWindow::windowMoved(GcWindow*w) { + // now drop at the cursor + if (currentStyle == 2) { + // go find... + int before=0; + for (;before < charts.count(); before++) { + if (charts[before] == w) { + QWidget *m = winFlow->takeAt(before)->widget(); + QWidget *c = dynamic_cast(controls())->widget(before); + dynamic_cast(controls())->removeWidget(c); + QWidget *l = charts.takeAt(before); + if (chartCursor > before) chartCursor--; + if (chartCursor >= 0) { + dynamic_cast(controls())->insertWidget(chartCursor, c); + winFlow->insert(chartCursor, m); + charts.insert(chartCursor, dynamic_cast(l)); + } else { + dynamic_cast(controls())->addWidget(c); + winFlow->addWidget(m); + charts.append(dynamic_cast(l)); + } + break; + } + } + //winFlow->doLayout(winFlow->geometry(), false); + winFlow->update(); + chartCursor = -2; + winWidget->repaint(); + } + // remove the cursor + chartCursor = -2; // -2 means don't show } void @@ -570,10 +715,22 @@ HomeWindow::windowResized(GcWindow*w) void HomeWindow::windowMoving(GcWindow*w) { - // ensure the mouse pointer is visible + // ensure the mouse pointer is visible, scrolls + // as we get near to the margins... QPoint pos = winWidget->mapFromGlobal(QCursor::pos()); winArea->ensureVisible(pos.x(), pos.y(), 20, 20); + chartCursor = pointTile(pos); + winWidget->repaint(); // show cursor + +#if 0 + // code for bumping tiles movement + // disaobled for now in preference for + // a drag operation with a cursor that + // highlights where the window will be + // moved to... + winArea->ensureVisible(pos.x(), pos.y(), 20, 20); + // since the widget has moved, the mouse is no longer // in the same position relevant to the widget, so // move the mouse back to the same position relevant @@ -708,6 +865,7 @@ HomeWindow::windowMoving(GcWindow*w) } } } +#endif } void @@ -717,6 +875,45 @@ HomeWindow::windowResizing(GcWindow*w) winArea->ensureVisible(pos.x(), pos.y(), 20, 20); } +void +HomeWindow::drawCursor() +{ + if (chartCursor == -2) return; + + QPainter painter(winWidget); + + // lets draw to the left of the chart... + if (chartCursor > -1) { + + // background light gray for now? + QRect line(charts[chartCursor]->geometry().x()-12, + charts[chartCursor]->geometry().y(), + 4, + charts[chartCursor]->geometry().height()); + + painter.setPen(Qt::NoPen); + painter.setBrush(QBrush(Qt::white)); + painter.drawRect(line); + + } + + // lets draw at to the right of the + // last chart... + if (chartCursor == -1) { + + // background light gray for now? + QRect line(charts[charts.count()-1]->geometry().x() + charts[charts.count()-1]->width() + 8, + charts[charts.count()-1]->geometry().y(), + 4, + charts[charts.count()-1]->geometry().height()); + + painter.setPen(Qt::NoPen); + painter.setBrush(QBrush(Qt::white)); + painter.drawRect(line); + + } +} + GcWindowDialog::GcWindowDialog(GcWinID type, MainWindow *mainWindow) : mainWindow(mainWindow), type(type) { setAttribute(Qt::WA_DeleteOnClose); diff --git a/src/HomeWindow.h b/src/HomeWindow.h index eb2fbe6e4..7116ec750 100644 --- a/src/HomeWindow.h +++ b/src/HomeWindow.h @@ -76,10 +76,15 @@ class HomeWindow : public GcWindow void windowMoved(GcWindow*); void windowResized(GcWindow*); + // when moving tiles + int pointTile(QPoint pos); + void drawCursor(); + protected: MainWindow *mainWindow; bool active; // ignore gui signals when changing views GcWindow *clicked; // keep track of selected charts + bool dropPending; // top bar QLabel *title; @@ -105,6 +110,7 @@ class HomeWindow : public GcWindow // the charts! QList charts; + int chartCursor; };