diff options
3 files changed, 319 insertions, 1 deletions
diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeEmbedWidget.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeEmbedWidget.h index f9cc52b7..73f4e908 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeEmbedWidget.h +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeEmbedWidget.h @@ -40,9 +40,10 @@ public: ~NativeEmbedWidget(){} QWidget* widget(){ return embedW; } + QRect geometry(){ return embedW->geometry(); } public slots: - void activateWindow(){ _window->requestActivate(); } + void activateWindow(){ QTimer::singleShot(0, _window, SLOT(requestActivate())); } }; diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.cpp b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.cpp index b7909da1..3c4562c1 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.cpp +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.cpp @@ -13,6 +13,12 @@ NativeWindow::NativeWindow( NativeWindowObject *obj ) : QFrame(0, Qt::Window | Qt::FramelessWindowHint){ WIN = obj; createFrame(); + this->setMouseTracking(true); + //Setup the timer object to syncronize info + moveTimer = new QTimer(this); + moveTimer->setSingleShot(true); + moveTimer->setInterval(100); //1/10 second + connect(moveTimer, SIGNAL(timeout()), this, SLOT(syncGeom()) ); //WIN->addFrameWinID(this->winId()); } @@ -60,21 +66,157 @@ void NativeWindow::initProperties(){ container->activateWindow(); } +//Mouse Interactivity +void NativeWindow::startMoving(){ + //If the cursor is not over this window, move it to the center of the titlebar + QPoint curpt = QCursor::pos(); //global coords + if(!this->geometry().contains(curpt)){ + curpt = this->mapToGlobal(titleLabel->geometry().center()); + QCursor::setPos(curpt); + } + //Calculate the offset + activeState = Move; + offset = this->mapFromGlobal(curpt); + setMouseCursor(activeState, true); //this one is an override cursor + //WinWidget->pause(); + this->grabMouse(); +} + +void NativeWindow::startResizing(){ + activeState = getStateAtPoint( this->mapFromGlobal(QCursor::pos()), true); //also have it set the offset variable + setMouseCursor(activeState, true); //this one is an override cursor + //WinWidget->pause(); + this->grabMouse(); +} + // === PRIVATE === +NativeWindow::ModState NativeWindow::getStateAtPoint(QPoint pt, bool setoffset){ + //Note: pt should be in widget-relative coordinates, not global + QList<int> frame = WIN->property(NativeWindowObject::FrameExtents).value<QList<int> >(); + /*QList<int> : [Left, Right, Top, Bottom] in pixels */ + bool onLeft = (pt.x()<=frame[0]); + bool onRight = (pt.x() >= (this->width()-frame[1])) && pt.x() <= this->width(); + bool onTop = (pt.y() < titleLabel->y() ); //be careful about this one - the top frame gets split up and the title bar put in + bool onBottom = (pt.y() >= (this->height()-frame[3]) && (pt.y() <= this->height()) ); + if(onLeft || onRight || onTop || onBottom){ + //above the frame itself - need to figure out which quadrant it is in (8-directions) + if(onTop){ + //One of the top options + if(onLeft){ + if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()); } //difference from top-left corner + return ResizeTopLeft; + }else if(onRight){ + if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(pt.y()); } //difference from top-right corner + return ResizeTopRight; + }else{ + if(setoffset){ offset.setX(0); offset.setY(pt.y()); } //difference from top edge (X does not matter) + return ResizeTop; + } + }else if(onBottom){ + //One of the bottom options + if(onLeft){ + if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()-this->height()); } //difference from bottom-left corner + return ResizeBottomLeft; + }else if(onRight){ + if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(pt.y()-this->height()); } //difference from bottom-right corner + return ResizeBottomRight; + }else{ + if(setoffset){ offset.setX(0); offset.setY(pt.y()-this->height()); } //difference from bottom edge (X does not matter) + return ResizeBottom; + } + }else if(onLeft){ + //Left side options + /*if(pt.y() < this->height()/5){ + if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()); } //difference from top-left corner + return ResizeTopLeft; + }else if(pt.y() > (this->height()*4.0/5.0)){ + if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()-this->height()); } //difference from bottom-left corner + return ResizeBottomLeft; + }else{*/ + if(setoffset){ offset.setX(pt.x()); offset.setY(0); } //difference from left edge (Y does not matter) + return ResizeLeft; + //} + }else if(onRight){ + //Right side options + /*if(pt.y() < this->height()/5){ + if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(pt.y()); } //difference from top-right corner + return ResizeTopRight; + }else if(pt.y() > (this->height()*4.0/5.0)){ + if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(pt.y()-this->height()); } //difference from bottom-right corner + return ResizeBottomRight; + }else{*/ + if(setoffset){ offset.setX(pt.x()-this->width()); offset.setY(0); } //difference from right edge (Y does not matter) + return ResizeRight; + //} + }else{ + return Normal; + } + } + //if it gets this far just return normal + return Normal; +} + +void NativeWindow::setMouseCursor(ModState state, bool override){ + if(currentCursor==state && !override){ return; } //nothing to change + Qt::CursorShape shape; + switch(state){ + case Normal: + shape = Qt::ArrowCursor; + break; + case Move: + shape = Qt::SizeAllCursor; + break; + case ResizeTop: + shape = Qt::SizeVerCursor; + break; + case ResizeTopRight: + shape = Qt::SizeBDiagCursor; + break; + case ResizeRight: + shape = Qt::SizeHorCursor; + break; + case ResizeBottomRight: + shape = Qt::SizeFDiagCursor; + break; + case ResizeBottom: + shape = Qt::SizeVerCursor; + break; + case ResizeBottomLeft: + shape = Qt::SizeBDiagCursor; + break; + case ResizeLeft: + shape = Qt::SizeHorCursor; + break; + case ResizeTopLeft: + shape = Qt::SizeFDiagCursor; + break; + } + if(override){ + QApplication::setOverrideCursor(QCursor(shape)); + }else{ + currentCursor = state; + this->setCursor(shape); + } +} + void NativeWindow::createFrame(){ //Initialize the widgets closeB = new QToolButton(this); closeB->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); closeB->setAutoRaise(true); + closeB->setCursor(Qt::ArrowCursor); minB = new QToolButton(this); minB->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); minB->setAutoRaise(true); + minB->setCursor(Qt::ArrowCursor); maxB = new QToolButton(this); maxB->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); maxB->setAutoRaise(true); + maxB->setCursor(Qt::ArrowCursor); otherB = new QToolButton(this); otherB->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); otherB->setAutoRaise(true); + otherB->setCursor(Qt::ArrowCursor); vlayout = new QVBoxLayout(); vlayout->setSpacing(0); toolbarL = new QHBoxLayout(); @@ -85,6 +227,7 @@ void NativeWindow::createFrame(){ //vlayout.align titleLabel = new QLabel(this); titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + titleLabel->setCursor(Qt::ArrowCursor); //Now put the widgets in the right places toolbarL->addWidget(otherB); toolbarL->addWidget(titleLabel); @@ -169,3 +312,152 @@ void NativeWindow::syncGeom(){ this->setGeometry( geom ); } } + +// === PROTECTED === +void NativeWindow::mousePressEvent(QMouseEvent *ev){ + container->activateWindow(); + this->raise(); + QFrame::mousePressEvent(ev); + //qDebug() << "Frame Mouse Press Event"; + if(activeState != Normal){ return; } // do nothing - already in a state of grabbed mouse + offset.setX(0); offset.setY(0); + if(ev->button()==Qt::LeftButton){ + if(this->childAt(ev->pos())!=0){ + //Clicked on the titlebar + startMoving(); + }else{ + //Clicked on the frame somewhere + startResizing(); + } + } + +} + +void NativeWindow::mouseMoveEvent(QMouseEvent *ev){ + QFrame::mouseMoveEvent(ev); + if(activeState == Normal){ + setMouseCursor( getStateAtPoint(ev->pos()) ); //just update the mouse cursor + }else{ + //Currently in a modification state + QRect geom = this->geometry(); + QList<int> frame = WIN->property(NativeWindowObject::FrameExtents).value<QList<int> >(); + /*QList<int> : [Left, Right, Top, Bottom] in pixels */ + QSize minsize(container->widget()->minimumSize().width() + frame[0]+frame[1], container->widget()->minimumSize().height()+frame[2]+frame[3] ); + switch(activeState){ + case Move: + geom.moveTopLeft(ev->globalPos()-offset); //will not change size + break; + case ResizeTop: + geom.setTop(ev->globalPos().y()-offset.y()); + if(geom.size().height() < minsize.height()){ + geom.setTop(geom.y() - (minsize.height()-geom.size().height())); //reset back to min height + } + break; + case ResizeTopRight: + geom.setTopRight(ev->globalPos()-offset); + if(geom.size().height() < minsize.height()){ + geom.setTop(geom.y() - (minsize.height()-geom.size().height())); //reset back to min height + } + if(geom.size().width() < minsize.width()){ + geom.setRight(geom.x() + minsize.width()); //reset back to min width + } + break; + case ResizeRight: + geom.setRight(ev->globalPos().x()-offset.x()); + if(geom.size().width() < minsize.width()){ + geom.setRight(geom.x() + minsize.width()); //reset back to min width + } + break; + case ResizeBottomRight: + geom.setBottomRight(ev->globalPos()-offset); + if(geom.size().height() < minsize.height()){ + geom.setBottom(geom.y() + minsize.height()); //reset back to min height + } + if(geom.size().width() < minsize.width()){ + geom.setRight(geom.x() + minsize.width()); //reset back to min width + } + break; + case ResizeBottom: + geom.setBottom(ev->globalPos().y()-offset.y()); + if(geom.size().height() < minsize.height()){ + geom.setBottom(geom.y() + minsize.height()); //reset back to min height + } + break; + case ResizeBottomLeft: + geom.setBottomLeft(ev->globalPos()-offset); + if(geom.size().height() < minsize.height()){ + geom.setBottom(geom.y() + minsize.height()); //reset back to min height + } + if(geom.size().width() < minsize.width()){ + geom.setLeft(geom.x() - (minsize.width()-geom.size().width())); //reset back to min width + } + break; + case ResizeLeft: + geom.setLeft(ev->globalPos().x()-offset.x()); + if(geom.size().width() < minsize.width()){ + geom.setLeft(geom.x() - (minsize.width()-geom.size().width())); //reset back to min width + } + break; + case ResizeTopLeft: + geom.setTopLeft(ev->globalPos()-offset); + if(geom.size().height() < minsize.height()){ + geom.setTop(geom.y() - (minsize.height()-geom.size().height())); //reset back to min height + } + if(geom.size().width() < minsize.width()){ + geom.setLeft(geom.x() - (minsize.width()-geom.size().width())); //reset back to min width + } + break; + default: + break; + } + //if( (geom.width()%2==0 && geom.height()%2==0) || activeState==Move){ + //qDebug() << " Change Window:" << this->geometry() << geom; + if(activeState==Move){ this->setGeometry(geom); } + else{ + //qDebug() << " Change Window Dimensions:" << this->geometry() << geom; + //qDebug() << " - Mouse Pos:" << ev->globalPos() << ev->pos() << "Offset" << offset; + this->setGeometry(geom); + } + //} + } +} + +void NativeWindow::mouseReleaseEvent(QMouseEvent *ev){ + //Check for a right-click event + //qDebug() << "Frame Mouse Release Event"; + QFrame::mouseReleaseEvent(ev); + if( (activeState==Normal) && (titleLabel->geometry().contains(ev->pos())) && (ev->button()==Qt::RightButton) ){ + //container->raiseWindow();//need to ensure the native window is always on top of this frame but under the menu + otherB->emit clicked(); + return; + } + if(activeState!=Normal){ + //if(container->isPaused()){ container->resume(); } + activeState = Normal; + QApplication::restoreOverrideCursor(); + setMouseCursor( getStateAtPoint(ev->pos()) ); + } + if(QFrame::mouseGrabber() == this){ this->releaseMouse(); } + container->activateWindow(); + //QTimer::singleShot(0, container, SLOT(raiseWindow()) ); +} + +/*void NativeWindow::enterEvent(QEvent *ev){ + QFrame::enterEvent(ev); + container->raiseWindow(); +}*/ +void NativeWindow::leaveEvent(QEvent *ev){ + QFrame::leaveEvent(ev); + if(activeState == Normal){ + setMouseCursor(Normal); + } + //if(!QRect(QPoint(0,0),this->size()).contains( this->mapFromGlobal(QCursor::pos())) ){ container->lowerWindow(); } +} + +void NativeWindow::moveEvent(QMoveEvent *ev){ + //qDebug() << "Got Move Event:" << ev->pos() << container->geometry(); + QFrame::moveEvent(ev); + //if(!closing && !container->isPaused()){ + moveTimer->start(); + //} +} diff --git a/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.h b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.h index 47699ae6..20491014 100644 --- a/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.h +++ b/src-qt5/core/lumina-desktop-unified/src-desktop/src-widgets/NativeWindow.h @@ -21,8 +21,21 @@ public: public slots: void initProperties(); + //Mouse Interactivity + void startMoving(); + void startResizing(); private: + //Window status + enum ModState{Normal, Move, ResizeTop, ResizeTopRight, ResizeRight, ResizeBottomRight, ResizeBottom, ResizeBottomLeft, ResizeLeft, ResizeTopLeft}; + ModState activeState; + ModState currentCursor; + QPoint offset; //needed for movement calculations (offset from mouse click to movement point) + //Functions for getting/setting state + ModState getStateAtPoint(QPoint pt, bool setoffset = false); //generally used for mouse location detection + void setMouseCursor(ModState, bool override = false); //Update the mouse cursor based on state + QTimer *moveTimer; + //Core object NativeWindowObject *WIN; // Interface items @@ -49,6 +62,18 @@ private slots: void syncVisibility(bool init = false); void syncWinType(); void syncGeom(); + +protected: + void mousePressEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + void leaveEvent(QEvent *ev); + //void enterEvent(QEvent *ev); + void moveEvent(QMoveEvent *ev); + +signals: + void windowMoved(NativeWindowObject*); + }; #endif |