Workout Editor Smart Guides

.. just a simple indicator in the x-axis for now
   when points are selected. may extend to the y-axis
   as well.

.. lots more to come on smart guides but needs a lot
   of thought and play time.
This commit is contained in:
Mark Liversedge
2016-01-04 22:05:01 +00:00
parent 5ae5da1b0f
commit f971ccff4b
4 changed files with 145 additions and 0 deletions

View File

@@ -393,6 +393,126 @@ WWMMPCurve::paint(QPainter *painter)
}
}
void
WWSmartGuide::paint(QPainter *painter)
{
QPointF tl(-1,-1);
QPointF br(-1,-1);
// get the boundary of the selection
int selected=0;
foreach(WWPoint *p, workoutWidget()->points()) {
// don't show guides whilst selecting, too noisy
if (!p->selected) continue;
selected++;
// top left
if (tl.x() == -1 || tl.x() > p->x) tl.setX(p->x);
if (tl.y() == -1 || tl.y() < p->y) tl.setY(p->y);
// bottom right
if (br.x() == -1 || br.x() < p->x) br.setX(p->x);
if (br.y() == -1 || br.y() > p->y) br.setY(p->y);
}
// set the boundary
QRectF boundary (workoutWidget()->transform(tl.x(), tl.y()),
workoutWidget()->transform(br.x(), br.y()));
if (selected > 0) {
// for now just paint the boundary tics on the x-axis
QPen linePen(GColor(CPLOTMARKER));
linePen.setWidthF(0);
painter->setPen(linePen);
QRectF bottom = workoutWidget()->bottom();
// top line width indicators
painter->drawLine(boundary.bottomLeft().x(), workoutWidget()->bottom().y(),
boundary.bottomLeft().x(), workoutWidget()->bottom().y()+bottom.height());
painter->drawLine(boundary.bottomRight().x(), bottom.y(),
boundary.bottomRight().x(), bottom.y()+bottom.height());
// now the text - but only if we have a gap!
if (br.x() > tl.x()) {
QFontMetrics fontMetrics(workoutWidget()->markerFont);
painter->setFont(workoutWidget()->markerFont);
// paint time at very bottom of bottom in the middle
// of the elongated tic marks
QString text = time_to_string(br.x()-tl.x());
QRect bound = fontMetrics.boundingRect(text);
QPoint here(boundary.center().x()-(bound.width()/2), bottom.bottom());
painter->drawText(here, text);
}
// find next and previous points, so we can mark them too
// this is useful when shifting left and right
int prev=-1, next=-1;
for(int i=0; i < workoutWidget()->points().count(); i++) {
WWPoint *p = workoutWidget()->points()[i];
if (p->x < tl.x()) prev=i; // to left
if (p->x > br.x()) {
next=i;
break;
}
}
// in seconds
int left = prev >= 0 ? workoutWidget()->points()[prev]->x : 0;
int right = next >= 0 ? workoutWidget()->points()[next]->x : workoutWidget()->maxX();
// in plot coordinates
QPointF leftpx = workoutWidget()->transform(left,0);
QPointF rightpx = workoutWidget()->transform(right,0);
leftpx.setY(bottom.y());
rightpx.setY(bottom.y());
// left indicator
painter->drawLine(leftpx, leftpx + QPointF(0, bottom.height()));
painter->drawLine(rightpx, rightpx + QPointF(0, bottom.height()));
// now the left text - but only if we have a gap!
if (left < tl.x()) {
QFontMetrics fontMetrics(workoutWidget()->markerFont);
painter->setFont(workoutWidget()->markerFont);
// paint time at very bottom of bottom in the middle
// of the elongated tic marks
QString text = time_to_string(tl.x() - left);
QRect bound = fontMetrics.boundingRect(text);
QPointF here(leftpx.x() + ((boundary.left() - leftpx.x())/2) - (bound.width()/2), bottom.bottom());
painter->drawText(here, text);
}
// now the right text - but only if we have a gap!
if (right > br.x()) {
QFontMetrics fontMetrics(workoutWidget()->markerFont);
painter->setFont(workoutWidget()->markerFont);
// paint time at very bottom of bottom in the middle
// of the elongated tic marks
QString text = time_to_string(right - br.x());
QRect bound = fontMetrics.boundingRect(text);
QPointF here(boundary.right() + ((rightpx.x()-boundary.right())/2) - (bound.width()/2), bottom.bottom());
painter->drawText(here, text);
}
#if 0 // doesn't add much
// side line width indicators
painter->drawLine(boundary.topLeft()-QPointF(10,0), boundary.topLeft()-QPointF(30,0));
painter->drawLine(boundary.bottomLeft()-QPointF(10,0), boundary.bottomLeft()-QPointF(30,0));
painter->drawLine(boundary.bottomLeft()-QPointF(20,0), boundary.topLeft()-QPointF(20,0));
#endif
}
}
//
// COMMANDS
//

View File

@@ -40,6 +40,7 @@
#define GCWW_BCURSOR 7 // interval "block" cursor
#define GCWW_BRECT 8 // block selection
#define GCWW_MMPCURVE 9
#define GCWW_SGUIDE 10 // smart guides appear as we drag
//
// ITEMS
@@ -163,6 +164,25 @@ class WWRect : public WorkoutWidgetItem {
};
// flash up guide lines when things align when the user is creating
// moving or dragging points or blocks around
class WWSmartGuide : public WorkoutWidgetItem {
public:
WWSmartGuide(WorkoutWidget *w) : WorkoutWidgetItem(w) { w->addItem(this); }
// Reimplement in children
int type() { return GCWW_SGUIDE; }
void paint(QPainter *painter);
// locate me on the parent widget in paint coordinates
// should not be selectable (!!)
QRectF bounding() { return QRectF(); }
};
// highlight as we hover over blocks
class WWBlockCursor : public WorkoutWidgetItem {
public:

View File

@@ -58,6 +58,9 @@ WorkoutWindow::WorkoutWindow(Context *context) :
// selection tool
rect = new WWRect(workout);
// guides always on top!
guide = new WWSmartGuide(workout);
// setup the toolbar
toolbar = new QToolBar(this);
toolbar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);

View File

@@ -44,6 +44,7 @@ class WWRect;
class WWBlockCursor;
class WWBlockSelection;
class WWMMPCurve;
class WWSmartGuide;
class WorkoutWindow : public GcWindow
{
@@ -87,6 +88,7 @@ class WorkoutWindow : public GcWindow
WWBlockCursor *bcursor;
WWBlockSelection *brect;
WWMMPCurve *mmp;
WWSmartGuide *guide;
bool active;
};