mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Basic Navigation Model
.. back and forward buttons to navigate between views and selections. .. currently limited to just rides, date ranges and views. .. next step is to enable click to select from trends overviews to allow users to drill down from the season overview into activities and back again. .. part of the shift from searching through lists to analyse data to exploring data visually with drill down and click through. .. the buttons are very basic and there is no way to explore the history / recently viewed items etc. these will come later. Fixes #3529
This commit is contained in:
@@ -106,7 +106,7 @@ ChartBar::ChartBar(Context *context) : QWidget(context->mainWindow), context(con
|
||||
menuButton->setStyleSheet("QToolButton { border: none; padding: 0px; }");
|
||||
menuButton->setAutoFillBackground(false);
|
||||
menuButton->setFixedSize(20*dpiXFactor,20*dpiYFactor);
|
||||
menuButton->setIcon(iconFromPNG(":images/sidebar/extra.png"));
|
||||
menuButton->setIcon(iconFromPNG(":images/sidebar/plus.png"));
|
||||
menuButton->setIconSize(QSize(10*dpiXFactor,10*dpiYFactor));
|
||||
menuButton->setFocusPolicy(Qt::NoFocus);
|
||||
mlayout->addWidget(menuButton);
|
||||
@@ -119,13 +119,12 @@ ChartBar::ChartBar(Context *context) : QWidget(context->mainWindow), context(con
|
||||
connect(menuMapper, SIGNAL(mapped(int)), this, SLOT(triggerContextMenu(int)));
|
||||
|
||||
barMenu = new QMenu("Add");
|
||||
chartMenu = barMenu->addMenu(tr("Add Chart"));
|
||||
chartMenu = barMenu->addMenu(tr("New "));
|
||||
|
||||
barMenu->addAction(tr("Import Chart..."), context->mainWindow, SLOT(importChart()));
|
||||
barMenu->addAction(tr("Import ..."), context->mainWindow, SLOT(importChart()));
|
||||
|
||||
#ifdef GC_HAS_CLOUD_DB
|
||||
barMenu->addAction(tr("Upload Chart..."), context->mainWindow, SLOT(exportChartToCloudDB()));
|
||||
barMenu->addAction(tr("Download Chart..."), context->mainWindow, SLOT(addChartFromCloudDB()));
|
||||
barMenu->addAction(tr("Download ..."), context->mainWindow, SLOT(addChartFromCloudDB()));
|
||||
#endif
|
||||
// menu
|
||||
connect(menuButton, SIGNAL(clicked()), this, SLOT(menuPopup()));
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Athlete.h"
|
||||
|
||||
#include "RideMetric.h"
|
||||
#include "NavigationModel.h"
|
||||
#include "UserMetricSettings.h"
|
||||
#include "UserMetricParser.h"
|
||||
#include "DataFilter.h"
|
||||
|
||||
@@ -68,6 +68,7 @@ class Context;
|
||||
class Athlete;
|
||||
class MainWindow;
|
||||
class Tab;
|
||||
class NavigationModel;
|
||||
|
||||
class Context : public QObject
|
||||
{
|
||||
@@ -78,6 +79,7 @@ class Context : public QObject
|
||||
~Context();
|
||||
|
||||
// mainwindow state
|
||||
NavigationModel *nav;
|
||||
int viewIndex;
|
||||
bool showSidebar, showLowbar, showToolbar, showTabbar;
|
||||
int style;
|
||||
@@ -169,6 +171,8 @@ class Context : public QObject
|
||||
void notifyStop() { emit stop(); }
|
||||
void notifySeek(long x) { emit seek(x); }
|
||||
|
||||
// date range selection
|
||||
void notifyDateRangeChanged(DateRange x) { dr_=x; emit dateRangeSelected(x); }
|
||||
void notifyWorkoutsChanged() { emit workoutsChanged(); }
|
||||
void notifyVideoSyncChanged() { emit VideoSyncChanged(); }
|
||||
|
||||
@@ -243,6 +247,7 @@ class Context : public QObject
|
||||
void autoDownloadEnd();
|
||||
void autoDownloadProgress(QString, double, int, int);
|
||||
|
||||
void dateRangeSelected(DateRange);
|
||||
void rideSelected(RideItem*);
|
||||
|
||||
// we added/deleted/changed an item
|
||||
|
||||
@@ -135,11 +135,16 @@ class RideCache : public QObject
|
||||
friend class ::LTMPlot; // get weekly performances
|
||||
friend class ::Banister; // get weekly performances
|
||||
friend class ::Leaf; // get weekly performances
|
||||
friend class ::RideItem; // adds to deletelist in destructor
|
||||
friend class ::NavigationModel; // checks deletelist during redo/undo
|
||||
|
||||
Context *context;
|
||||
QDir directory, plannedDirectory;
|
||||
|
||||
QVector<RideItem*> rides_, reverse_, delete_;
|
||||
// rides and reverse are the main lists
|
||||
// delete_ is a list of items to garbage collect (delete later)
|
||||
// deletelist is a list of items that no longer exist (deleted)
|
||||
QVector<RideItem*> rides_, reverse_, delete_, deletelist;
|
||||
RideCacheModel *model_;
|
||||
bool exiting;
|
||||
double progress_; // percent
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include "RideItem.h"
|
||||
#include "RideCache.h"
|
||||
#include "RideMetric.h"
|
||||
#include "RideFile.h"
|
||||
#include "RideFileCache.h"
|
||||
@@ -238,6 +239,9 @@ RideFile *RideItem::ride(bool open)
|
||||
|
||||
RideItem::~RideItem()
|
||||
{
|
||||
// add to the deleted list
|
||||
if (context->athlete->rideCache) context->athlete->rideCache->deletelist << this;
|
||||
|
||||
//qDebug()<<"deleting:"<<fileName;
|
||||
if (isOpen()) close();
|
||||
if (fileCache_) delete fileCache_;
|
||||
|
||||
@@ -39,8 +39,6 @@ class Context;
|
||||
class UserData;
|
||||
class ComparePane;
|
||||
|
||||
Q_DECLARE_METATYPE(RideItem*)
|
||||
|
||||
class RideItem : public QObject
|
||||
{
|
||||
|
||||
@@ -222,4 +220,7 @@ class RideItem : public QObject
|
||||
void updateIntervals();
|
||||
};
|
||||
|
||||
Q_DECLARE_OPAQUE_POINTER(RideItem*);
|
||||
Q_DECLARE_METATYPE(RideItem*)
|
||||
|
||||
#endif // _GC_RideItem_h
|
||||
|
||||
@@ -183,6 +183,7 @@ DateRange::DateRange(const DateRange &other) : QObject()
|
||||
to=other.to;
|
||||
name=other.name;
|
||||
color=other.color;
|
||||
id = other.id;
|
||||
valid = from.isValid() && to.isValid();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#define _TimeUtils_h
|
||||
|
||||
#include <QDate>
|
||||
#include <QUuid>
|
||||
#include <QDateEdit>
|
||||
#include <QColor>
|
||||
#include <QDateTime>
|
||||
@@ -46,9 +47,19 @@ class DateRange : QObject
|
||||
DateRange(const DateRange& other);
|
||||
DateRange(QDate from = QDate(), QDate to = QDate(), QString name ="", QColor=QColor(127,127,127));
|
||||
DateRange& operator=(const DateRange &);
|
||||
bool operator!=(const DateRange&other) {
|
||||
if (other.from != from || other.to != to || other.name != name) return true;
|
||||
return false;
|
||||
}
|
||||
bool operator==(const DateRange&other) {
|
||||
if (other.from == from && other.to == to && other.name == name) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
QDate from, to;
|
||||
QString name;
|
||||
QColor color; // used by R code only
|
||||
QUuid id;
|
||||
|
||||
// does this date fall in the range selection ?
|
||||
bool pass(QDate date) {
|
||||
|
||||
@@ -323,6 +323,33 @@ LTMSidebar::configChanged(qint32)
|
||||
* Selections Made
|
||||
*----------------------------------------------------------------------*/
|
||||
|
||||
void
|
||||
LTMSidebar::selectDateRange(DateRange dr)
|
||||
{
|
||||
for(int i=0; i<seasons->seasons.count(); i++) {
|
||||
Season s = seasons->seasons.at(i);
|
||||
if (s.start == dr.from && s.end == dr.to && s.name == dr.name) {
|
||||
// bingo
|
||||
dateRangeTree->selectionModel()->clearSelection(); // clear selection
|
||||
allDateRanges->child(i)->setSelected(true); // select ours
|
||||
return;
|
||||
} else {
|
||||
QStringList names=dr.name.split("/");
|
||||
if (names.count() == 2 && s.name == names.at(0)) {
|
||||
for (int j=0; j<s.phases.count(); j++) {
|
||||
Phase p = s.phases.at(j);
|
||||
if (p.start == dr.from && p.end == dr.to && p.name == names.at(1)) {
|
||||
// bingo
|
||||
dateRangeTree->selectionModel()->clearSelection(); // clear selection
|
||||
allDateRanges->child(i)->child(j)->setSelected(true); // select ours
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LTMSidebar::dateRangeTreeWidgetSelectionChanged()
|
||||
{
|
||||
|
||||
@@ -55,6 +55,7 @@ class LTMSidebar : public QWidget
|
||||
public slots:
|
||||
|
||||
// date range selection and editing
|
||||
void selectDateRange(DateRange);
|
||||
void dateRangeTreeWidgetSelectionChanged();
|
||||
void dateRangePopup(QPoint);
|
||||
void dateRangePopup();
|
||||
|
||||
@@ -271,8 +271,26 @@ MainWindow::MainWindow(const QDir &home)
|
||||
lowbarIcon = iconFromPNG(":images/mac/lowbar.png");
|
||||
tabbedIcon = iconFromPNG(":images/mac/tabbed.png");
|
||||
tiledIcon = iconFromPNG(":images/mac/tiled.png");
|
||||
backIcon = iconFromPNG(":images/mac/back.png");
|
||||
forwardIcon = iconFromPNG(":images/mac/forward.png");
|
||||
QSize isize(19 *dpiXFactor,19 *dpiYFactor);
|
||||
|
||||
back = new QPushButton(this);
|
||||
back->setIcon(backIcon);
|
||||
back->setFixedHeight(24 *dpiYFactor);
|
||||
back->setFixedWidth(32 *dpiYFactor);
|
||||
back->setIconSize(isize);
|
||||
back->setStyle(toolStyle);
|
||||
connect(back, SIGNAL(clicked(bool)), this, SIGNAL(backClicked()));
|
||||
|
||||
forward = new QPushButton(this);
|
||||
forward->setIcon(forwardIcon);
|
||||
forward->setFixedHeight(24 *dpiYFactor);
|
||||
forward->setFixedWidth(32 *dpiYFactor);
|
||||
forward->setIconSize(isize);
|
||||
forward->setStyle(toolStyle);
|
||||
connect(forward, SIGNAL(clicked(bool)), this, SIGNAL(forwardClicked()));
|
||||
|
||||
lowbar = new QPushButton(this);
|
||||
lowbar->setIcon(lowbarIcon);
|
||||
lowbar->setFixedHeight(24 *dpiYFactor);
|
||||
@@ -324,11 +342,19 @@ MainWindow::MainWindow(const QDir &home)
|
||||
searchBox->setStyle(toolStyle);
|
||||
searchBox->setFixedWidth(400 * dpiXFactor);
|
||||
searchBox->setFixedHeight(28 * dpiYFactor);
|
||||
|
||||
QWidget *space = new QWidget(this);
|
||||
space->setAutoFillBackground(false);
|
||||
space->setFixedWidth(5 * dpiYFactor);
|
||||
|
||||
head->addWidget(space);
|
||||
head->addWidget(back);
|
||||
head->addWidget(forward);
|
||||
head->addStretch();
|
||||
head->addWidget(sidelist);
|
||||
head->addWidget(lowbar);
|
||||
head->addWidget(styleSelector);
|
||||
head->setFixedHeight(searchBox->height() + (10 *dpiXFactor));
|
||||
head->setFixedHeight(searchBox->height() + (16 *dpiXFactor));
|
||||
|
||||
connect(searchBox, SIGNAL(searchResults(QStringList)), this, SLOT(setFilter(QStringList)));
|
||||
connect(searchBox, SIGNAL(searchClear()), this, SLOT(clearFilter()));
|
||||
@@ -2199,6 +2225,9 @@ MainWindow::configChanged(qint32)
|
||||
searchBox->setStyleSheet(QString("QLineEdit { background: %1; color: %2; }").arg(GColor(CCHROME).name()).arg(GCColor::invertColor(GColor(CCHROME)).name()));
|
||||
|
||||
#endif
|
||||
QString buttonstyle = QString("QPushButton { border: none; background-color: %1; }").arg(CCHROME);
|
||||
back->setStyleSheet(buttonstyle);
|
||||
forward->setStyleSheet(buttonstyle);
|
||||
|
||||
// All platforms
|
||||
QPalette tabbarPalette;
|
||||
|
||||
@@ -93,6 +93,7 @@ class MainWindow : public QMainWindow
|
||||
|
||||
// currently selected tab
|
||||
Tab *athleteTab() { return currentTab; }
|
||||
NewSideBar *newSidebar() { return sidebar; }
|
||||
|
||||
protected:
|
||||
|
||||
@@ -112,6 +113,10 @@ class MainWindow : public QMainWindow
|
||||
void setSplash(bool first=false);
|
||||
void clearSplash();
|
||||
|
||||
signals:
|
||||
void backClicked();
|
||||
void forwardClicked();
|
||||
|
||||
public slots:
|
||||
|
||||
bool eventFilter(QObject*,QEvent*);
|
||||
@@ -273,11 +278,12 @@ class MainWindow : public QMainWindow
|
||||
|
||||
// Not on Mac so use other types
|
||||
QPushButton *sidelist, *lowbar;
|
||||
QPushButton *back, *forward;
|
||||
QtSegmentControl *styleSelector;
|
||||
GcToolBar *head;
|
||||
|
||||
// the icons
|
||||
QIcon sidebarIcon, lowbarIcon, tabbedIcon, tiledIcon;
|
||||
QIcon backIcon, forwardIcon, sidebarIcon, lowbarIcon, tabbedIcon, tiledIcon;
|
||||
|
||||
// tab bar (that supports swtitching on drag and drop)
|
||||
DragBar *tabbar;
|
||||
|
||||
209
src/Gui/NavigationModel.cpp
Normal file
209
src/Gui/NavigationModel.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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 "NavigationModel.h"
|
||||
|
||||
#include "RideCache.h"
|
||||
// a little too intertwined with these two,
|
||||
// probably needs refactoring out at some point.
|
||||
#include "LTMSidebar.h"
|
||||
#include "NewSideBar.h"
|
||||
#include "MainWindow.h"
|
||||
|
||||
|
||||
NavigationModel::NavigationModel(Tab *tab) : tab(tab), block(false), viewinit(false), iteminit(false), drinit(false)
|
||||
{
|
||||
connect(tab, SIGNAL(viewChanged(int)), this, SLOT(viewChanged(int)));
|
||||
connect(tab, SIGNAL(rideItemSelected(RideItem*)), this, SLOT(rideChanged(RideItem*)));
|
||||
connect(static_cast<HomeView*>(tab->view(0))->sidebar, SIGNAL(dateRangeChanged(DateRange)), this, SLOT(dateChanged(DateRange)));
|
||||
connect(tab->context->mainWindow, SIGNAL(backClicked()), this, SLOT(back()));
|
||||
connect(tab->context->mainWindow, SIGNAL(forwardClicked()), this, SLOT(forward()));
|
||||
|
||||
stackpointer=-1;
|
||||
}
|
||||
|
||||
NavigationModel::~NavigationModel()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NavigationModel::addToStack(NavigationEvent &event)
|
||||
{
|
||||
if (stackpointer == -1) {
|
||||
// empty
|
||||
stack.clear();
|
||||
stack << event;
|
||||
stackpointer++;
|
||||
|
||||
} else if (stackpointer == (stack.count()-1)) {
|
||||
|
||||
// end, just append
|
||||
stack << event;
|
||||
stackpointer++;
|
||||
|
||||
} else if (stackpointer < (stack.count()-1)) {
|
||||
|
||||
// middle, truncate
|
||||
stack.remove(stackpointer+1, stack.count() - (stackpointer+1));
|
||||
stack << event;
|
||||
stackpointer++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NavigationModel::rideChanged(RideItem *x)
|
||||
{
|
||||
if (block) return;
|
||||
|
||||
// initial values
|
||||
if (iteminit == false) {
|
||||
iteminit = true;
|
||||
item = x;
|
||||
return;
|
||||
}
|
||||
|
||||
if (item != x) {
|
||||
|
||||
// add to recent
|
||||
if (!recent.contains(x)) recent << x;
|
||||
|
||||
// add to the stack -- truncate if needed
|
||||
NavigationEvent add(NavigationEvent::RIDE, x);
|
||||
add.before.setValue(item);
|
||||
addToStack(add);
|
||||
|
||||
item = x;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NavigationModel::dateChanged(DateRange x)
|
||||
{
|
||||
if (block) return;
|
||||
|
||||
// initial values
|
||||
if (drinit == false) {
|
||||
drinit = true;
|
||||
dr = x;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dr != x) {
|
||||
|
||||
// add to the stack
|
||||
NavigationEvent add(NavigationEvent::DATERANGE, x);
|
||||
add.before.setValue(dr);
|
||||
addToStack(add);
|
||||
|
||||
dr = x;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NavigationModel::viewChanged(int x)
|
||||
{
|
||||
if (block) return;
|
||||
|
||||
// initial values
|
||||
if (viewinit == false) {
|
||||
viewinit = true;
|
||||
view = x;
|
||||
return;
|
||||
}
|
||||
|
||||
if (view != x) {
|
||||
|
||||
// add to the stack
|
||||
NavigationEvent add(NavigationEvent::VIEW, x);
|
||||
add.before.setValue(view);
|
||||
addToStack(add);
|
||||
|
||||
view = x;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NavigationModel::action(bool redo, NavigationEvent event)
|
||||
{
|
||||
block = true; // don't observe events during redo/undo
|
||||
|
||||
switch(event.type) {
|
||||
case NavigationEvent::VIEW:
|
||||
{
|
||||
int view = redo ? event.after.toInt() : event.before.toInt();
|
||||
tab->selectView(view);
|
||||
|
||||
// new side bar uses a different id, which will
|
||||
// eventually be refactored to be the only id
|
||||
// but for now we need to map this
|
||||
int id=0;
|
||||
switch(view) {
|
||||
case 0: id=2; break; // trends
|
||||
case 1: id=3; break; // analysis
|
||||
case 2: id=0; break; // diary
|
||||
case 3: id=5; break; // train
|
||||
}
|
||||
tab->context->mainWindow->newSidebar()->setItemSelected(id, true);
|
||||
}
|
||||
break;
|
||||
|
||||
case NavigationEvent::RIDE:
|
||||
{
|
||||
RideItem *item = redo ? event.after.value<RideItem*>() : event.before.value<RideItem*>();
|
||||
|
||||
// don't select deleted rides (!!)
|
||||
if (!tab->context->athlete->rideCache->deletelist.contains(item))
|
||||
tab->context->athlete->selectRideFile(item->fileName);
|
||||
}
|
||||
break;
|
||||
|
||||
case NavigationEvent::DATERANGE:
|
||||
{
|
||||
DateRange dr = redo ? event.after.value<DateRange>() : event.before.value<DateRange>();
|
||||
static_cast<HomeView*>(tab->view(0))->sidebar->selectDateRange(dr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
block = false;
|
||||
}
|
||||
|
||||
void
|
||||
NavigationModel::back()
|
||||
{
|
||||
// are we the current tab?
|
||||
if (tab->context->mainWindow->athleteTab() == tab) {
|
||||
if (stackpointer >= 0) {
|
||||
action(false, stack[stackpointer]);
|
||||
stackpointer--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NavigationModel::forward()
|
||||
{
|
||||
// are we the current tab?
|
||||
if (tab->context->mainWindow->athleteTab() == tab) {
|
||||
if ((stackpointer+1) < stack.count() && stack.count()) {
|
||||
stackpointer++;
|
||||
action(true, stack[stackpointer]);
|
||||
}
|
||||
}
|
||||
}
|
||||
94
src/Gui/NavigationModel.h
Normal file
94
src/Gui/NavigationModel.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 <QObject>
|
||||
#include <QUuid>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
#include "Tab.h"
|
||||
#include "TimeUtils.h"
|
||||
#include "Context.h"
|
||||
#include "Athlete.h"
|
||||
#include "RideItem.h"
|
||||
|
||||
class NavigationEvent {
|
||||
|
||||
public:
|
||||
|
||||
// events we observe and replay is a limited subset mostly
|
||||
// about changing views, dates and ride selections
|
||||
enum NavigationEventType { VIEW=0, TAB, RIDE, DATERANGE, FILTER } type;
|
||||
|
||||
NavigationEvent(NavigationEventType t, int v) : type(t) { after.setValue(v); }
|
||||
NavigationEvent(NavigationEventType t, DateRange v) : type(t) { after.setValue(v); }
|
||||
NavigationEvent(NavigationEventType t, RideItem* v) : type(t) { after.setValue(v); }
|
||||
NavigationEvent(NavigationEventType t, QString v) : type(t) { after.setValue(v); }
|
||||
|
||||
// before is updated by the navigation model
|
||||
QVariant before, after;
|
||||
};
|
||||
|
||||
//
|
||||
// Navigation is for the athlete tab -- not mainwindow.
|
||||
// Remember: multiple athletes can be opened at once.
|
||||
//
|
||||
class NavigationModel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
NavigationModel(Tab *tab);
|
||||
~NavigationModel();
|
||||
|
||||
// keep a track of the rides we've looked
|
||||
// at recently-- might want to quick link
|
||||
// to these in the future ...
|
||||
QVector<RideItem*> recent;
|
||||
|
||||
public slots:
|
||||
|
||||
void viewChanged(int);
|
||||
void rideChanged(RideItem*);
|
||||
void dateChanged(DateRange dr);
|
||||
|
||||
void back();
|
||||
void forward();
|
||||
void action(bool redo, NavigationEvent); // redo/undo the event
|
||||
|
||||
private:
|
||||
|
||||
Tab *tab;
|
||||
|
||||
// block observer when undo/redo is active
|
||||
bool block;
|
||||
|
||||
// current state, before an event arrives
|
||||
int view; // which view is selected
|
||||
DateRange dr;
|
||||
RideItem *item;
|
||||
|
||||
// have we seen first values for:
|
||||
bool viewinit, drinit, iteminit;
|
||||
|
||||
// the stack
|
||||
void addToStack(NavigationEvent&); // truncates stack if needed
|
||||
int stackpointer;
|
||||
QVector<NavigationEvent> stack;
|
||||
};
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "IntervalTreeView.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Colors.h"
|
||||
#include "NavigationModel.h"
|
||||
|
||||
#include <QPaintEvent>
|
||||
|
||||
@@ -88,6 +89,12 @@ Tab::Tab(Context *context) : QWidget(context->mainWindow), context(context)
|
||||
chartSettings->setMaximumHeight(600);
|
||||
chartSettings->hide();
|
||||
|
||||
// navigation model after main items as it uses the observer
|
||||
// pattern on views etc, so they need to be created first
|
||||
// but we need to get setup before ride selection happens
|
||||
// below, so we can observe the iniital ride select
|
||||
nav = new NavigationModel(this);
|
||||
|
||||
// cpx aggregate cache check
|
||||
connect(context,SIGNAL(rideSelected(RideItem*)), this, SLOT(rideSelected(RideItem*)));
|
||||
|
||||
@@ -100,9 +107,11 @@ Tab::Tab(Context *context) : QWidget(context->mainWindow), context(context)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise just the latest
|
||||
if (context->currentRideItem() == NULL && context->athlete->rideCache->rides().count() != 0)
|
||||
if (context->currentRideItem() == NULL && context->athlete->rideCache->rides().count() != 0) {
|
||||
context->athlete->selectRideFile(context->athlete->rideCache->rides().last()->fileName);
|
||||
}
|
||||
}
|
||||
|
||||
Tab::~Tab()
|
||||
@@ -112,6 +121,7 @@ Tab::~Tab()
|
||||
delete trainView;
|
||||
delete diaryView;
|
||||
delete views;
|
||||
delete nav;
|
||||
}
|
||||
|
||||
RideNavigator *
|
||||
@@ -174,6 +184,8 @@ Tab::view(int index)
|
||||
void
|
||||
Tab::selectView(int index)
|
||||
{
|
||||
emit viewChanged(index);
|
||||
|
||||
// first we deselect the current
|
||||
view(views->currentIndex())->setSelected(false);
|
||||
|
||||
@@ -187,6 +199,8 @@ Tab::selectView(int index)
|
||||
void
|
||||
Tab::rideSelected(RideItem*)
|
||||
{
|
||||
emit rideItemSelected(context->ride);
|
||||
|
||||
// update the ride property on all widgets
|
||||
// to let them know they need to replot new
|
||||
// selected ride (now the tree is up to date)
|
||||
|
||||
@@ -26,6 +26,7 @@ class RideNavigator;
|
||||
class MainWindow;
|
||||
class ProgressLine;
|
||||
class QPaintEvent;
|
||||
class NavigationModel;
|
||||
|
||||
class Tab: public QWidget
|
||||
{
|
||||
@@ -41,15 +42,21 @@ class Tab: public QWidget
|
||||
int currentView() { return views->currentIndex(); }
|
||||
TabView *view(int index);
|
||||
|
||||
NavigationModel *nav; // back/forward for this tab
|
||||
RideNavigator *rideNavigator(); // to get logical headings
|
||||
|
||||
protected:
|
||||
|
||||
friend class ::MainWindow;
|
||||
friend class ::NavigationModel;
|
||||
Context *context;
|
||||
|
||||
signals:
|
||||
|
||||
void viewChanged(int);
|
||||
void rideItemSelected(RideItem*);
|
||||
void dateRangeSelected(DateRange);
|
||||
|
||||
public slots:
|
||||
|
||||
void rideSelected(RideItem*);
|
||||
|
||||
@@ -117,7 +117,7 @@ DiaryView::setRide(RideItem*ride)
|
||||
void
|
||||
DiaryView::dateRangeChanged(DateRange dr)
|
||||
{
|
||||
context->dr_ = dr;
|
||||
//context->notifyDateRangeChanged(dr); // diary view deprecated and not part of navigation model
|
||||
page()->setProperty("dateRange", QVariant::fromValue<DateRange>(dr));
|
||||
}
|
||||
|
||||
@@ -130,18 +130,18 @@ DiaryView::isBlank()
|
||||
|
||||
HomeView::HomeView(Context *context, QStackedWidget *controls) : TabView(context, VIEW_HOME)
|
||||
{
|
||||
LTMSidebar *s = new LTMSidebar(context);
|
||||
sidebar = new LTMSidebar(context);
|
||||
HomeWindow *h = new HomeWindow(context, "home", "Trends");
|
||||
controls->addWidget(h->controls());
|
||||
controls->setCurrentIndex(0);
|
||||
BlankStateHomePage *b = new BlankStateHomePage(context);
|
||||
|
||||
setSidebar(s);
|
||||
setSidebar(sidebar);
|
||||
setPage(h);
|
||||
setBlank(b);
|
||||
setBottom(new ComparePane(context, this, ComparePane::season));
|
||||
|
||||
connect(s, SIGNAL(dateRangeChanged(DateRange)), this, SLOT(dateRangeChanged(DateRange)));
|
||||
connect(sidebar, SIGNAL(dateRangeChanged(DateRange)), this, SLOT(dateRangeChanged(DateRange)));
|
||||
connect(this, SIGNAL(onSelectionChanged()), this, SLOT(justSelected()));
|
||||
connect(bottomSplitter(), SIGNAL(compareChanged(bool)), this, SLOT(compareChanged(bool)));
|
||||
connect(bottomSplitter(), SIGNAL(compareClear()), bottom(), SLOT(clear()));
|
||||
@@ -161,7 +161,8 @@ HomeView::compareChanged(bool state)
|
||||
void
|
||||
HomeView::dateRangeChanged(DateRange dr)
|
||||
{
|
||||
context->dr_ = dr;
|
||||
emit dateChanged(dr);
|
||||
context->notifyDateRangeChanged(dr);
|
||||
page()->setProperty("dateRange", QVariant::fromValue<DateRange>(dr));
|
||||
}
|
||||
bool
|
||||
@@ -176,7 +177,7 @@ HomeView::justSelected()
|
||||
{
|
||||
if (isSelected()) {
|
||||
// force date range refresh
|
||||
static_cast<LTMSidebar*>(sidebar())->dateRangeTreeWidgetSelectionChanged();
|
||||
static_cast<LTMSidebar*>(sidebar)->dateRangeTreeWidgetSelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ private slots:
|
||||
void onAutoHideChanged(bool enabled);
|
||||
};
|
||||
|
||||
class LTMSidebar;
|
||||
class HomeView : public TabView
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -101,6 +102,10 @@ class HomeView : public TabView
|
||||
HomeView(Context *context, QStackedWidget *controls);
|
||||
~HomeView();
|
||||
|
||||
LTMSidebar *sidebar;
|
||||
|
||||
signals:
|
||||
void dateChanged(DateRange);
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
@@ -139,6 +139,8 @@
|
||||
<file>images/toolbar/popbutton.png</file>
|
||||
<file>images/toolbar/flipbutton.png</file>
|
||||
<file>images/splashscreen.png</file>
|
||||
<file>images/mac/back.png</file>
|
||||
<file>images/mac/forward.png</file>
|
||||
<file>images/mac/compose.png</file>
|
||||
<file>images/mac/download.png</file>
|
||||
<file>images/mac/share.png</file>
|
||||
|
||||
BIN
src/Resources/images/mac/back.png
Normal file
BIN
src/Resources/images/mac/back.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
BIN
src/Resources/images/mac/forward.png
Normal file
BIN
src/Resources/images/mac/forward.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
@@ -723,7 +723,7 @@ HEADERS += Gui/AboutDialog.h Gui/AddIntervalDialog.h Gui/AnalysisSidebar.h Gui/C
|
||||
Gui/SaveDialogs.h Gui/SearchBox.h Gui/SearchFilterBox.h Gui/SolveCPDialog.h Gui/Tab.h Gui/TabView.h Gui/ToolsRhoEstimator.h \
|
||||
Gui/Views.h Gui/BatchExportDialog.h Gui/DownloadRideDialog.h Gui/ManualRideDialog.h Gui/NewMainWindow.h Gui/NewSideBar.h \
|
||||
Gui/MergeActivityWizard.h Gui/RideImportWizard.h Gui/SplitActivityWizard.h Gui/SolverDisplay.h Gui/MetricSelect.h \
|
||||
Gui/AddChartWizard.h
|
||||
Gui/AddChartWizard.h Gui/NavigationModel.h
|
||||
|
||||
# metrics and models
|
||||
HEADERS += Metrics/Banister.h Metrics/CPSolver.h Metrics/Estimator.h Metrics/ExtendedCriticalPower.h Metrics/HrZones.h Metrics/PaceZones.h \
|
||||
@@ -818,7 +818,7 @@ SOURCES += Gui/AboutDialog.cpp Gui/AddIntervalDialog.cpp Gui/AnalysisSidebar.cpp
|
||||
Gui/SearchBox.cpp Gui/SearchFilterBox.cpp Gui/SolveCPDialog.cpp Gui/Tab.cpp Gui/TabView.cpp Gui/ToolsRhoEstimator.cpp Gui/Views.cpp \
|
||||
Gui/BatchExportDialog.cpp Gui/DownloadRideDialog.cpp Gui/ManualRideDialog.cpp Gui/EditUserMetricDialog.cpp Gui/NewMainWindow.cpp Gui/NewSideBar.cpp \
|
||||
Gui/MergeActivityWizard.cpp Gui/RideImportWizard.cpp Gui/SplitActivityWizard.cpp Gui/SolverDisplay.cpp Gui/MetricSelect.cpp \
|
||||
Gui/AddChartWizard.cpp
|
||||
Gui/AddChartWizard.cpp Gui/NavigationModel.cpp
|
||||
|
||||
## Models and Metrics
|
||||
SOURCES += Metrics/aBikeScore.cpp Metrics/aCoggan.cpp Metrics/AerobicDecoupling.cpp Metrics/Banister.cpp Metrics/BasicRideMetrics.cpp \
|
||||
|
||||
Reference in New Issue
Block a user