Allow user to drag/resize tiles on homewindow.

This commit is contained in:
Mark Liversedge
2011-01-03 19:47:37 +00:00
parent 0ef63a51a4
commit b6aacd264d
4 changed files with 358 additions and 29 deletions

View File

@@ -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;
}
}

View File

@@ -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);
};

View File

@@ -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;
}

View File

@@ -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