mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Allow user to drag/resize tiles on homewindow.
This commit is contained in:
@@ -21,6 +21,8 @@
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QEvent>
|
||||
#include <QMouseEvent>
|
||||
|
||||
QWidget *GcWindow::controls() const
|
||||
{
|
||||
@@ -65,33 +67,43 @@ void GcWindow::setRideItem(RideItem* x)
|
||||
emit rideItemChanged(_rideItem);
|
||||
}
|
||||
|
||||
int GcWindow::widthFactor() const
|
||||
double GcWindow::widthFactor() const
|
||||
{
|
||||
return _widthFactor;
|
||||
}
|
||||
|
||||
void GcWindow::setWidthFactor(int x)
|
||||
void GcWindow::setWidthFactor(double x)
|
||||
{
|
||||
_widthFactor = x;
|
||||
emit widthFactorChanged(x);
|
||||
}
|
||||
|
||||
int GcWindow::heightFactor() const
|
||||
double GcWindow::heightFactor() const
|
||||
{
|
||||
return _heightFactor;
|
||||
}
|
||||
|
||||
void GcWindow::setHeightFactor(int x)
|
||||
void GcWindow::setHeightFactor(double x)
|
||||
{
|
||||
_heightFactor = x;
|
||||
emit heightFactorChanged(x);
|
||||
}
|
||||
|
||||
void GcWindow::setResizable(bool x)
|
||||
{
|
||||
_resizable = x;
|
||||
}
|
||||
|
||||
bool GcWindow::resizable() const
|
||||
{
|
||||
return _resizable;
|
||||
}
|
||||
|
||||
GcWindow::GcWindow()
|
||||
{
|
||||
}
|
||||
|
||||
GcWindow::GcWindow(QWidget *parent) : QFrame(parent) {
|
||||
GcWindow::GcWindow(QWidget *parent) : QFrame(parent), dragState(None) {
|
||||
qRegisterMetaType<QWidget*>("controls");
|
||||
qRegisterMetaType<RideItem*>("ride");
|
||||
qRegisterMetaType<GcWinID>("type");
|
||||
@@ -100,6 +112,8 @@ GcWindow::GcWindow(QWidget *parent) : QFrame(parent) {
|
||||
setRideItem(NULL);
|
||||
setTitle("");
|
||||
setContentsMargins(0,0,0,0);
|
||||
setResizable(false);
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
GcWindow::~GcWindow()
|
||||
@@ -118,7 +132,7 @@ GcWindow::amVisible()
|
||||
|
||||
|
||||
void
|
||||
GcWindow::paintEvent(QPaintEvent * /*event*/)
|
||||
GcWindow::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
static QPixmap closeImage = QPixmap(":images/toolbar/popbutton.png");
|
||||
static QPixmap aluBar = QPixmap(":images/aluBar.png");
|
||||
@@ -181,3 +195,283 @@ GcWindow::paintEvent(QPaintEvent * /*event*/)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
* Drag and resize tiles
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
bool
|
||||
GcWindow::eventFilter(QObject *, QEvent *e)
|
||||
{
|
||||
if (!resizable()) return false;
|
||||
|
||||
// handle moving / resizing activity
|
||||
if (dragState != None) {
|
||||
switch (e->type()) {
|
||||
case QEvent::MouseMove:
|
||||
mouseMoveEvent((QMouseEvent*)e);
|
||||
return false;
|
||||
break;
|
||||
case QEvent::MouseButtonRelease:
|
||||
mouseReleaseEvent((QMouseEvent*)e);
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
GcWindow::mousePressEvent(QMouseEvent *e)
|
||||
{
|
||||
if (!resizable() || e->button() == Qt::NoButton || isHidden()) {
|
||||
setDragState(None);
|
||||
return;
|
||||
}
|
||||
|
||||
DragState h = spotHotSpot(e);
|
||||
|
||||
// is it on the close icon?
|
||||
if (h == Close) {
|
||||
setDragState(None);
|
||||
hide();
|
||||
//emit exit();
|
||||
return;
|
||||
} else if (h == Flip) {
|
||||
setDragState(None);
|
||||
//flip();
|
||||
}
|
||||
|
||||
// get current window state
|
||||
oWidth = width();
|
||||
oHeight = height();
|
||||
oWidthFactor = widthFactor();
|
||||
oHeightFactor = heightFactor();
|
||||
oX = pos().x();
|
||||
oY = pos().y();
|
||||
mX = e->globalX();
|
||||
mY = e->globalY();
|
||||
|
||||
setDragState(h); // set drag state then!
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void
|
||||
GcWindow::mouseReleaseEvent(QMouseEvent *)
|
||||
{
|
||||
setDragState(None);
|
||||
repaint();
|
||||
}
|
||||
|
||||
// for the mouse position, are we in a hotspot?
|
||||
// if so, what would the drag state become if we
|
||||
// clicked?
|
||||
GcWindow::DragState
|
||||
GcWindow::spotHotSpot(QMouseEvent *e)
|
||||
{
|
||||
// corner
|
||||
int corner = 9;
|
||||
int borderWidth = 3;
|
||||
|
||||
// account for offset XXX map to GcWindow geom
|
||||
int _y = e->y();
|
||||
int _x = e->x();
|
||||
int _height = height();
|
||||
int _width = width();
|
||||
|
||||
if (e->x() > (2 + width() - corner) && e->y() < corner) return (Close);
|
||||
else if (_x <= corner && _y <= corner) return (TLCorner);
|
||||
else if (_x >= (_width-corner) && _y <= corner) return (TRCorner);
|
||||
else if (_x <= corner && _y >= (_height-corner)) return (BLCorner);
|
||||
else if (_x >= (_width-corner) && _y >= (_height-corner)) return (BRCorner);
|
||||
else if (_x <= borderWidth) return (Left);
|
||||
else if (_x >= (_width-borderWidth)) return (Right);
|
||||
else if (_y <= borderWidth) return (Top);
|
||||
else if (_y >= (_height-borderWidth)) return (Bottom);
|
||||
else return (Move);
|
||||
}
|
||||
|
||||
void
|
||||
GcWindow::mouseMoveEvent(QMouseEvent *e)
|
||||
{
|
||||
if (!resizable()) return;
|
||||
|
||||
if (dragState == None) {
|
||||
// set the cursor shape
|
||||
setCursorShape(spotHotSpot(e));
|
||||
return;
|
||||
}
|
||||
|
||||
// work out the relative move x and y
|
||||
int relx = e->globalX() - mX;
|
||||
int rely = e->globalY() - mY;
|
||||
|
||||
switch (dragState) {
|
||||
|
||||
default:
|
||||
case Move :
|
||||
move(oX + relx, oY + rely);
|
||||
break;
|
||||
|
||||
case TLCorner :
|
||||
{
|
||||
int newWidth = oWidth - relx;
|
||||
int newHeight = oHeight - rely;
|
||||
|
||||
// need to move and resize
|
||||
if (newWidth > 30 && newHeight > 30) {
|
||||
move(oX + relx, oY + rely);
|
||||
setNewSize(newWidth, newHeight);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TRCorner :
|
||||
{
|
||||
int newWidth = oWidth + relx;
|
||||
int newHeight = oHeight - rely;
|
||||
|
||||
// need to move and resize if changes on y plane
|
||||
if (newWidth > 30 && newHeight > 30) {
|
||||
move(oX, oY + rely);
|
||||
setNewSize(newWidth, newHeight);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BLCorner :
|
||||
{
|
||||
int newWidth = oWidth - relx;
|
||||
int newHeight = oHeight + rely;
|
||||
|
||||
// need to move and resize
|
||||
if (newWidth > 30 && newHeight > 30) {
|
||||
move(oX + relx, oY);
|
||||
setNewSize(newWidth, newHeight);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BRCorner :
|
||||
{
|
||||
int newWidth = oWidth + relx;
|
||||
int newHeight = oHeight + rely;
|
||||
|
||||
// need to move and resize
|
||||
if (newWidth > 30 && newHeight > 30) {
|
||||
setNewSize(newWidth, newHeight);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Top :
|
||||
{
|
||||
int newHeight = oHeight - rely;
|
||||
|
||||
// need to move and resize
|
||||
if (newHeight > 30) {
|
||||
move (oX, oY + rely);
|
||||
setNewSize(oWidth, newHeight);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Bottom :
|
||||
{
|
||||
int newHeight = oHeight + rely;
|
||||
|
||||
// need to move and resize
|
||||
if (newHeight > 30) {
|
||||
setNewSize(oWidth, newHeight);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Left :
|
||||
{
|
||||
int newWidth = oWidth - relx;
|
||||
|
||||
// need to move and resize
|
||||
if (newWidth > 30) {
|
||||
move (oX + relx, oY);
|
||||
setNewSize(newWidth, oHeight);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Right :
|
||||
{
|
||||
int newWidth = oWidth + relx;
|
||||
|
||||
// need to move and resize
|
||||
if (newWidth > 30) {
|
||||
setNewSize(newWidth, oHeight);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//repaint();
|
||||
//QApplication::processEvents(); // flicker...
|
||||
}
|
||||
|
||||
void
|
||||
GcWindow::setNewSize(int w, int h)
|
||||
{
|
||||
// convert to height factor
|
||||
double newHF = (double(oHeight) * oHeightFactor) / double(h);
|
||||
double newWF = (double(oWidth) * oWidthFactor) / double(w);
|
||||
|
||||
// don't get too big!
|
||||
if (newHF < 1 || newWF < 1) return; // too big
|
||||
|
||||
// now apply
|
||||
setFixedSize(QSize(w,h));
|
||||
|
||||
// adjust factors
|
||||
setHeightFactor(newHF);
|
||||
setWidthFactor(newWF);
|
||||
}
|
||||
|
||||
void
|
||||
GcWindow::setDragState(DragState d)
|
||||
{
|
||||
dragState = d;
|
||||
setCursorShape(d);
|
||||
}
|
||||
|
||||
void
|
||||
GcWindow::setCursorShape(DragState d)
|
||||
{
|
||||
// set cursor
|
||||
switch (d) {
|
||||
case Bottom:
|
||||
case Top:
|
||||
setCursor(Qt::SizeVerCursor);
|
||||
break;
|
||||
case Left:
|
||||
case Right:
|
||||
setCursor(Qt::SizeHorCursor);
|
||||
break;
|
||||
case TLCorner:
|
||||
case BRCorner:
|
||||
setCursor(Qt::SizeFDiagCursor);
|
||||
break;
|
||||
case TRCorner:
|
||||
case BLCorner:
|
||||
setCursor(Qt::SizeBDiagCursor);
|
||||
break;
|
||||
case Move:
|
||||
//setCursor(Qt::OpenHandCursor); //XXX sub widgets don't set the cursor...
|
||||
setCursor(Qt::ArrowCursor);
|
||||
break;
|
||||
default:
|
||||
case Close:
|
||||
case None:
|
||||
setCursor(Qt::ArrowCursor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,26 +65,37 @@ private:
|
||||
Q_PROPERTY(RideItem* ride READ rideItem WRITE setRideItem NOTIFY rideItemChanged)
|
||||
|
||||
// geometry factor
|
||||
Q_PROPERTY(int widthFactor READ widthFactor WRITE setWidthFactor NOTIFY widthFactorChanged USER true);
|
||||
Q_PROPERTY(int heightFactor READ heightFactor WRITE setHeightFactor NOTIFY widthFactorChanged USER true);
|
||||
Q_PROPERTY(double widthFactor READ widthFactor WRITE setWidthFactor NOTIFY widthFactorChanged USER true);
|
||||
Q_PROPERTY(double heightFactor READ heightFactor WRITE setHeightFactor NOTIFY widthFactorChanged USER true);
|
||||
|
||||
// can be resized
|
||||
Q_PROPERTY(bool resizable READ resizable WRITE setResizable USER true);
|
||||
|
||||
QWidget *_controls;
|
||||
QString _title;
|
||||
QString _instanceName;
|
||||
RideItem *_rideItem;
|
||||
GcWinID _type;
|
||||
int _widthFactor;
|
||||
int _heightFactor;
|
||||
double _widthFactor;
|
||||
double _heightFactor;
|
||||
bool _resizable;
|
||||
|
||||
// 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
|
||||
DragState dragState;
|
||||
int oWidth, oHeight, oX, oY, mX, mY;
|
||||
double oHeightFactor, oWidthFactor;
|
||||
|
||||
|
||||
signals:
|
||||
void controlsChanged(QWidget*);
|
||||
void titleChanged(QString);
|
||||
void rideItemChanged(RideItem*);
|
||||
void heightFactorChanged(int);
|
||||
void widthFactorChanged(int);
|
||||
void heightFactorChanged(double);
|
||||
void widthFactorChanged(double);
|
||||
|
||||
public:
|
||||
|
||||
@@ -105,16 +116,30 @@ public:
|
||||
void setRideItem(RideItem *);
|
||||
RideItem *rideItem() const;
|
||||
|
||||
void setWidthFactor(int);
|
||||
int widthFactor() const;
|
||||
void setWidthFactor(double);
|
||||
double widthFactor() const;
|
||||
|
||||
void setHeightFactor(int);
|
||||
int heightFactor() const;
|
||||
void setHeightFactor(double);
|
||||
double heightFactor() const;
|
||||
|
||||
void setResizable(bool);
|
||||
bool resizable() const;
|
||||
|
||||
GcWinID type() const { return _type; }
|
||||
void setType(GcWinID x) { _type = x; } // only really used by the window registry
|
||||
|
||||
virtual bool amVisible();
|
||||
|
||||
// mouse actions -- resizing and dragging tiles
|
||||
bool eventFilter(QObject *object, QEvent *e);
|
||||
virtual void mousePressEvent(QMouseEvent *);
|
||||
virtual void mouseReleaseEvent(QMouseEvent *);
|
||||
virtual void mouseMoveEvent(QMouseEvent *);
|
||||
void setDragState(DragState);
|
||||
void setCursorShape(DragState);
|
||||
DragState spotHotSpot(QMouseEvent *);
|
||||
void setNewSize(int w, int h);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -238,16 +238,19 @@ HomeWindow::styleChanged(int id)
|
||||
case 0 : // they are tabs in a TabWidget
|
||||
tabbed->addTab(charts[i], charts[i]->property("title").toString());
|
||||
charts[i]->setContentsMargins(0,0,0,0);
|
||||
charts[i]->setResizable(false); // we need to show on tab selection!
|
||||
charts[i]->hide(); // we need to show on tab selection!
|
||||
break;
|
||||
case 1 : // they are lists in a GridLayout
|
||||
tileGrid->addWidget(charts[i], i,0);
|
||||
charts[i]->setContentsMargins(0,25,0,0);
|
||||
charts[i]->setResizable(false); // we need to show on tab selection!
|
||||
charts[i]->show();
|
||||
break;
|
||||
case 2 : // thet are in a FlowLayout
|
||||
winFlow->addWidget(charts[i]);
|
||||
charts[i]->setContentsMargins(0,15,0,0);
|
||||
charts[i]->setResizable(true); // we need to show on tab selection!
|
||||
charts[i]->show();
|
||||
default:
|
||||
break;
|
||||
@@ -327,6 +330,7 @@ HomeWindow::addChart(GcWindow* newone)
|
||||
|
||||
case 0 :
|
||||
newone->setContentsMargins(0,0,0,0);
|
||||
newone->setResizable(false); // we need to show on tab selection!
|
||||
tabbed->addTab(newone, newone->property("title").toString());
|
||||
break;
|
||||
case 1 :
|
||||
@@ -335,6 +339,7 @@ HomeWindow::addChart(GcWindow* newone)
|
||||
// set geometry
|
||||
newone->setFixedWidth((tileArea->width()-50));
|
||||
newone->setFixedHeight(newone->width() * 0.7);
|
||||
newone->setResizable(false); // we need to show on tab selection!
|
||||
int row = chartnum; // / 2;
|
||||
int column = 0; //chartnum % 2;
|
||||
newone->setContentsMargins(0,25,0,0);
|
||||
@@ -343,18 +348,18 @@ HomeWindow::addChart(GcWindow* newone)
|
||||
break;
|
||||
case 2 :
|
||||
{
|
||||
int heightFactor = newone->property("heightFactor").toInt();
|
||||
int widthFactor = newone->property("widthFactor").toInt();
|
||||
double heightFactor = newone->property("heightFactor").toDouble();
|
||||
double widthFactor = newone->property("widthFactor").toDouble();
|
||||
|
||||
// width of area minus content margins and spacing around each item
|
||||
// divided by the number of items
|
||||
|
||||
int newwidth = (winArea->width() - 20 /* scrollbar */
|
||||
double newwidth = (winArea->width() - 20 /* scrollbar */
|
||||
- 40 /* left and right marings */
|
||||
- ((widthFactor-1) * 20) /* internal spacing */
|
||||
) / widthFactor;
|
||||
|
||||
int newheight = (winArea->height()
|
||||
double newheight = (winArea->height()
|
||||
- 40 /* top and bottom marings */
|
||||
- ((heightFactor-1) * 20) /* internal spacing */
|
||||
) / heightFactor;
|
||||
@@ -366,6 +371,7 @@ HomeWindow::addChart(GcWindow* newone)
|
||||
if (newheight < minHeight) newheight = minHeight;
|
||||
else newone->setFixedHeight(newheight);
|
||||
newone->setContentsMargins(0,15,0,0);
|
||||
newone->setResizable(true); // we need to show on tab selection!
|
||||
winFlow->addWidget(newone);
|
||||
}
|
||||
break;
|
||||
@@ -422,7 +428,7 @@ HomeWindow::removeChart(int num)
|
||||
}
|
||||
|
||||
void
|
||||
HomeWindow::resizeEvent(QResizeEvent *)
|
||||
HomeWindow::resizeEvent(QResizeEvent *e)
|
||||
{
|
||||
foreach (GcWindow *x, charts) {
|
||||
|
||||
@@ -440,16 +446,16 @@ HomeWindow::resizeEvent(QResizeEvent *)
|
||||
|
||||
case 2 : // flow
|
||||
{
|
||||
int heightFactor = x->property("heightFactor").toInt();
|
||||
int widthFactor = x->property("widthFactor").toInt();
|
||||
double heightFactor = x->property("heightFactor").toDouble();
|
||||
double widthFactor = x->property("widthFactor").toDouble();
|
||||
|
||||
|
||||
int newwidth = (winArea->width() - 20 /* scrollbar */
|
||||
double newwidth = (winArea->width() - 20 /* scrollbar */
|
||||
- 40 /* left and right marings */
|
||||
- ((widthFactor-1) * 20) /* internal spacing */
|
||||
) / widthFactor;
|
||||
|
||||
int newheight = (winArea->height()
|
||||
double newheight = (winArea->height()
|
||||
- 40 /* top and bottom marings */
|
||||
- ((heightFactor-1) * 20) /* internal spacing */
|
||||
) / heightFactor;
|
||||
@@ -534,7 +540,7 @@ HomeWindow::eventFilter(QObject *object, QEvent *e)
|
||||
clicked = NULL;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -565,11 +571,11 @@ GcWindowDialog::GcWindowDialog(GcWinID type, MainWindow *mainWindow) : mainWindo
|
||||
chartLayout->addWidget(win);
|
||||
|
||||
controlLayout = new QFormLayout;
|
||||
height=new QSpinBox(this);
|
||||
height=new QDoubleSpinBox(this);
|
||||
height->setRange(1,10);
|
||||
height->setSingleStep(1);
|
||||
height->setValue(2);
|
||||
width=new QSpinBox(this);
|
||||
width=new QDoubleSpinBox(this);
|
||||
width->setRange(1,10);
|
||||
width->setSingleStep(1);
|
||||
width->setValue(2);
|
||||
@@ -625,6 +631,9 @@ GcWindowDialog::exec()
|
||||
} else return NULL;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
* Save and restore state (xxxx-layout.xml)
|
||||
*--------------------------------------------------------------------*/
|
||||
// static helper to protect special xml characters
|
||||
// ideally we would use XMLwriter to do this but
|
||||
// the file format is trivial and this implementation
|
||||
@@ -820,3 +829,4 @@ bool ViewParser::endDocument()
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ class GcWindowDialog : public QDialog
|
||||
QPushButton *ok, *cancel;
|
||||
GcWindow *win;
|
||||
QLineEdit *title;
|
||||
QSpinBox *height, *width;
|
||||
QDoubleSpinBox *height, *width;
|
||||
};
|
||||
|
||||
class ViewParser : public QXmlDefaultHandler
|
||||
|
||||
Reference in New Issue
Block a user