/* * Copyright (c) 2020 Mark Liversedge (liversedge@gmail.com) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ChartSpace.h" #include "TabView.h" #include "Athlete.h" #include "RideCache.h" #include #include #include #include #include #include #include static QIcon grayConfig, whiteConfig, accentConfig; ChartSpace::ChartSpace(Context *context) : state(NONE), context(context), group(NULL), _viewY(0), yresizecursor(false), xresizecursor(false), block(false), scrolling(false), setscrollbar(false), lasty(-1) { setContentsMargins(0,0,0,0); // no longer a gc chart ... //setProperty("color", GColor(COVERVIEWBACKGROUND)); XXX?? //setProperty("nomenu", true); //setShowTitle(false); //setControls(NULL); QHBoxLayout *main = new QHBoxLayout; // add a view and scene and centre scene = new QGraphicsScene(this); view = new QGraphicsView(this); view->viewport()->setAttribute(Qt::WA_AcceptTouchEvents, false); // stops it stealing focus on mouseover scrollbar = new QScrollBar(Qt::Vertical, this); // how to move etc //view->setDragMode(QGraphicsView::ScrollHandDrag); view->setRenderHint(QPainter::Antialiasing, true); view->setFrameStyle(QFrame::NoFrame); view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); view->setScene(scene); // layout main->addWidget(view); main->addWidget(scrollbar); // all the widgets setLayout(main); // by default these are the column sizes (user can adjust) columns << 1200 << 1200 << 1200 << 1200 << 1200 << 1200 << 1200 << 1200 << 1200 << 1200; // for changing the view group = new QParallelAnimationGroup(this); viewchange = new QPropertyAnimation(this, "viewRect"); viewchange->setEasingCurve(QEasingCurve(QEasingCurve::OutQuint)); // for scrolling the view scroller = new QPropertyAnimation(this, "viewY"); scroller->setEasingCurve(QEasingCurve(QEasingCurve::Linear)); // watch the view for mouse events view->setMouseTracking(true); scene->installEventFilter(this); // once all widgets created we can connect the signals connect(context, SIGNAL(configChanged(qint32)), this, SLOT(configChanged(qint32))); connect(scroller, SIGNAL(finished()), this, SLOT(scrollFinished())); connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollbarMoved(int))); // set the widgets etc configChanged(CONFIG_APPEARANCE); // we're ready to plot, but not configured configured=false; stale=true; current=NULL; } // add the item void ChartSpace::addItem(int order, int column, int deep, ChartSpaceItem *item) { item->order= order; item->column = column; item->deep = deep; items.append(item); } // when a ride is selected we need to notify all the ChartSpaceItems void ChartSpace::rideSelected(RideItem *item) { // don't plot when we're not visible, unless we have nothing plotted yet if (!isVisible() && current != NULL && item != NULL) { stale=true; return; } // don't replot .. we already did this one if (current == item && stale == false) { return; } // profiling the code //QTime timer; //timer.start(); // ride item changed foreach(ChartSpaceItem *ChartSpaceItem, items) ChartSpaceItem->setData(item); // profiling the code //qDebug()<<"took:"<state != ChartSpace::NONE) return false; // repaint when mouse enters and leaves if (event->type() == QEvent::GraphicsSceneHoverLeave || event->type() == QEvent::GraphicsSceneHoverEnter) { // force repaint update(); scene()->update(); // repaint when in the corner } else if (event->type() == QEvent::GraphicsSceneHoverMove && inCorner() != incorner) { incorner = inCorner(); update(); scene()->update(); } return false; } bool ChartSpaceItem::inCorner() { QPoint vpos = parent->view->mapFromGlobal(QCursor::pos()); QPointF spos = parent->view->mapToScene(vpos); if (geometry().contains(spos.x(), spos.y())) { if (spos.y() - geometry().top() < (ROWHEIGHT+40) && geometry().width() - (spos.x() - geometry().x()) < (ROWHEIGHT+40)) return true; } return false; } bool ChartSpaceItem::underMouse() { QPoint vpos = parent->view->mapFromGlobal(QCursor::pos()); QPointF spos = parent->view->mapToScene(vpos); if (geometry().contains(spos.x(), spos.y())) return true; return false; } // ChartSpaceItems need to show they are in config mode void ChartSpaceItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget) { if (drag) painter->setBrush(QBrush(GColor(CPLOTMARKER))); else painter->setBrush(GColor(CCARDBACKGROUND)); QPainterPath path; path.addRoundedRect(QRectF(0,0,geometry().width(),geometry().height()), ROWHEIGHT/5, ROWHEIGHT/5); painter->setPen(Qt::NoPen); //painter->fillPath(path, brush.color()); painter->drawPath(path); painter->setPen(GColor(CPLOTGRID)); //XXXpainter->drawLine(QLineF(0,ROWHEIGHT*2,geometry().width(),ROWHEIGHT*2)); //painter->fillRect(QRectF(0,0,geometry().width()+1,geometry().height()+1), brush); //titlefont.setWeight(QFont::Bold); if (GCColor::luminance(GColor(CCARDBACKGROUND)) < 127) painter->setPen(QColor(200,200,200)); else painter->setPen(QColor(70,70,70)); painter->setFont(parent->titlefont); painter->drawText(QPointF(ROWHEIGHT /2.0f, QFontMetrics(parent->titlefont, parent->device()).height()), name); // only paint contents if not dragging if (drag) return; // not dragging so we can get to work painting the rest if (parent->state != ChartSpace::DRAG && underMouse()) { if (inCorner()) { // if hovering over the button show a background to indicate // that pressing a button is good QPainterPath path; path.addRoundedRect(QRectF(geometry().width()-40-ROWHEIGHT,0, ROWHEIGHT+40, ROWHEIGHT+40), ROWHEIGHT/5, ROWHEIGHT/5); painter->setPen(Qt::NoPen); QColor darkgray(GColor(CCARDBACKGROUND).lighter(200)); painter->setBrush(darkgray); painter->drawPath(path); painter->fillRect(QRectF(geometry().width()-40-ROWHEIGHT, 0, ROWHEIGHT+40-(ROWHEIGHT/5), ROWHEIGHT+40), QBrush(darkgray)); painter->fillRect(QRectF(geometry().width()-40-ROWHEIGHT, ROWHEIGHT/5, ROWHEIGHT+40, ROWHEIGHT+40-(ROWHEIGHT/5)), QBrush(darkgray)); // draw the config button and make it more obvious // when hovering over the card painter->drawPixmap(geometry().width()-20-(ROWHEIGHT*1), 20, ROWHEIGHT*1, ROWHEIGHT*1, accentConfig.pixmap(QSize(ROWHEIGHT*1, ROWHEIGHT*1))); } else { // hover on card - make it more obvious there is a config button painter->drawPixmap(geometry().width()-20-(ROWHEIGHT*1), 20, ROWHEIGHT*1, ROWHEIGHT*1, whiteConfig.pixmap(QSize(ROWHEIGHT*1, ROWHEIGHT*1))); } } else painter->drawPixmap(geometry().width()-20-(ROWHEIGHT*1), 20, ROWHEIGHT*1, ROWHEIGHT*1, grayConfig.pixmap(QSize(ROWHEIGHT*1, ROWHEIGHT*1))); itemPaint(painter, opt, widget); } static bool ChartSpaceItemSort(const ChartSpaceItem* left, const ChartSpaceItem* right) { return (left->column < right->column ? true : (left->column == right->column && left->order < right->order ? true : false)); } void ChartSpace::updateGeometry() { bool animated=false; // prevent a memory leak group->stop(); delete group; group = new QParallelAnimationGroup(this); // order the items to their positions qSort(items.begin(), items.end(), ChartSpaceItemSort); int y=SPACING; int maxy = y; int column=-1; int x=SPACING; // just set their geometry for now, no interaction for(int i=0; i