From 11d5822bba436be0201774c570f67783f6ff88e2 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Thu, 2 Mar 2017 23:12:48 -0500 Subject: A bit more work on the new window frames. Now the minimum size policies are respected, and the border extents have been standardized. --- src-qt5/core/libLumina/RootSubWindow.cpp | 102 ++++++++++++++++++++++++------- src-qt5/core/libLumina/RootSubWindow.h | 6 +- 2 files changed, 85 insertions(+), 23 deletions(-) (limited to 'src-qt5/core/libLumina') diff --git a/src-qt5/core/libLumina/RootSubWindow.cpp b/src-qt5/core/libLumina/RootSubWindow.cpp index 9769c922..7ec25a71 100644 --- a/src-qt5/core/libLumina/RootSubWindow.cpp +++ b/src-qt5/core/libLumina/RootSubWindow.cpp @@ -10,6 +10,8 @@ #include #include +#define WIN_BORDER 3 + // === PUBLIC === RootSubWindow::RootSubWindow(QWidget *root, NativeWindow *win) : QFrame(root){ this->setAttribute(Qt::WA_DeleteOnClose); @@ -22,7 +24,6 @@ RootSubWindow::RootSubWindow(QWidget *root, NativeWindow *win) : QFrame(root){ LoadProperties( QList< NativeWindow::Property>() << NativeWindow::Title << NativeWindow::Icon \ << NativeWindow::MinSize << NativeWindow::MaxSize << NativeWindow::Size ); //Hookup the signals/slots - connect(this, SIGNAL(aboutToActivate()), this, SLOT(aboutToActivate()) ); connect(WIN, SIGNAL(PropertyChanged(NativeWindow::Property, QVariant)), this, SLOT(propertyChanged(NativeWindow::Property, QVariant))); //Make sure the visibily property only gets loaded after it is added to the root area propertyChanged(NativeWindow::Visible, WIN->property(NativeWindow::Visible)); @@ -39,26 +40,26 @@ WId RootSubWindow::id(){ // === PRIVATE === RootSubWindow::ModState RootSubWindow::getStateAtPoint(QPoint pt, bool setoffset){ //Note: pt should be in widget-relative coordinates, not global - if(!WinWidget->geometry().contains(pt)){ + if(!WinWidget->geometry().contains(pt) && !titleBar->geometry().contains(pt)){ //above the frame itself - need to figure out which quadrant it is in (8-directions) - if(pt.y() < 3){ + if(pt.y() < WIN_BORDER){ //One of the top options - if(pt.x() < 3){ + if(pt.x() < WIN_BORDER){ if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()); } //difference from top-left corner return ResizeTopLeft; - }else if(pt.x() > (this->width()-3)){ + }else if(pt.x() > (this->width()-WIN_BORDER)){ if(setoffset){ offset.setX(this->width()-pt.x()); 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(pt.y() > (this->height()-3) ){ + }else if(pt.y() > (this->height()-WIN_BORDER) ){ //One of the bottom options - if(pt.x() < 3){ + if(pt.x() < WIN_BORDER){ if(setoffset){ offset.setX(pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-left corner return ResizeBottomLeft; - }else if(pt.x() > (this->width()-3)){ + }else if(pt.x() > (this->width()-WIN_BORDER)){ if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-right corner return ResizeBottomRight; }else{ @@ -67,10 +68,10 @@ RootSubWindow::ModState RootSubWindow::getStateAtPoint(QPoint pt, bool setoffset } }else{ //One of the side options - if(pt.x() < 3){ + if(pt.x() < WIN_BORDER){ if(setoffset){ offset.setX(pt.x()); offset.setY(0); } //difference from left edge (Y does not matter) return ResizeLeft; - }else if(pt.x() > (this->width()-3) ){ + }else if(pt.x() > (this->width()-WIN_BORDER) ){ if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(0); } //difference from right edge (Y does not matter) return ResizeRight; }else{ @@ -82,6 +83,7 @@ RootSubWindow::ModState RootSubWindow::getStateAtPoint(QPoint pt, bool setoffset } void RootSubWindow::setMouseCursor(ModState state, bool override){ + if(currentCursor==state && !override){ return; } //nothing to change Qt::CursorShape shape; switch(state){ case Normal: @@ -118,6 +120,7 @@ void RootSubWindow::setMouseCursor(ModState state, bool override){ if(override){ QApplication::setOverrideCursor(QCursor(shape)); }else{ + currentCursor = state; this->setCursor(shape); } } @@ -128,14 +131,19 @@ void RootSubWindow::initWindowFrame(){ closeB = new QToolButton(this); maxB = new QToolButton(this); minB = new QToolButton(this); + otherB = new QToolButton(this); titleLabel = new QLabel(this); titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); otherM = new QMenu(this); //menu of other actions + otherB->setMenu(otherM); + otherB->setPopupMode(QToolButton::InstantPopup); + otherB->setAutoRaise(true); connect(closeB, SIGNAL(clicked()), this, SLOT(triggerClose()) ); connect(maxB, SIGNAL(clicked()), this, SLOT(toggleMaximize()) ); connect(minB, SIGNAL(clicked()), this, SLOT(toggleMinimize()) ); //Now assemble the frame layout based on the current settings this->setLayout(mainLayout); + titleBar->addWidget(otherB); titleBar->addWidget(titleLabel); titleBar->addWidget(minB); titleBar->addWidget(maxB); @@ -150,9 +158,9 @@ void RootSubWindow::initWindowFrame(){ maxB->setObjectName("Button_Maximize"); otherM->setObjectName("Menu_Actions"); titleLabel->setObjectName("Label_Title"); - this->setStyleSheet("QWidget#WindowFrame{background-color: black;} QWidget#Label_Title{background-color: darkgrey; color: white; }"); + this->setStyleSheet("QWidget#WindowFrame{background-color: darkblue;} QWidget#Label_Title{background-color: transparent; color: white; }"); //And adjust the margins - mainLayout->setContentsMargins(4,4,4,4); + mainLayout->setContentsMargins(WIN_BORDER,WIN_BORDER,WIN_BORDER,WIN_BORDER); mainLayout->setSpacing(0); titleBar->setSpacing(1); titleBar->setContentsMargins(0,0,0,0); @@ -194,7 +202,18 @@ void RootSubWindow::activate(){ //Mouse Interactivity void RootSubWindow::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(titleBar->geometry().center()); + QCursor::setPos(curpt); + } + //Calculate the offset + activeState = Move; + offset = this->mapFromGlobal(curpt); + setMouseCursor(activeState, true); //this one is an override cursor + //Also need to capture the mouse + this->grabMouse(); } void RootSubWindow::startResizing(){ @@ -204,10 +223,6 @@ void RootSubWindow::startResizing(){ // === PRIVATE SLOTS === -void RootSubWindow::aboutToActivate(){ - -} - void RootSubWindow::propertyChanged(NativeWindow::Property prop, QVariant val){ if(val.isNull()){ return; } //not the same as a default/empty value - the property has just not been set yet qDebug() << "Set Window Property:" << prop << val; @@ -220,7 +235,7 @@ void RootSubWindow::propertyChanged(NativeWindow::Property prop, QVariant val){ titleLabel->setText(val.toString()); break; case NativeWindow::Icon: - //this->setWindowIcon(val.value< QIcon>()); + otherB->setIcon(val.value< QIcon>()); break; case NativeWindow::Size: WinWidget->resize(val.toSize()); @@ -244,7 +259,7 @@ void RootSubWindow::propertyChanged(NativeWindow::Property prop, QVariant val){ // === PROTECTED === void RootSubWindow::mousePressEvent(QMouseEvent *ev){ - qDebug() << "Frame Mouse Press Event"; + //qDebug() << "Frame Mouse Press Event"; offset.setX(0); offset.setY(0); if(activeState != Normal){ return; } // do nothing - already in a state of grabbed mouse this->activate(); @@ -265,41 +280,78 @@ void RootSubWindow::mouseMoveEvent(QMouseEvent *ev){ ev->accept(); if(activeState == Normal){ setMouseCursor( getStateAtPoint(ev->pos()) ); //just update the mouse cursor - }else{ //Currently in a modification state QRect geom = this->geometry(); + QSize minsize(WinWidget->minimumSize().width() + (2*WIN_BORDER), WinWidget->minimumSize().height()+(2*WIN_BORDER)+titleBar->geometry().size().height()); 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); - break; + 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; } + this->setGeometry(geom); } } @@ -315,4 +367,12 @@ void RootSubWindow::mouseReleaseEvent(QMouseEvent *ev){ activeState = Normal; QApplication::restoreOverrideCursor(); setMouseCursor( getStateAtPoint(ev->pos()) ); + if(QWidget::mouseGrabber() == this){ this->releaseMouse(); } +} + +void RootSubWindow::leaveEvent(QEvent *ev){ + QFrame::leaveEvent(ev); + if(activeState == Normal){ + setMouseCursor(Normal); + } } diff --git a/src-qt5/core/libLumina/RootSubWindow.h b/src-qt5/core/libLumina/RootSubWindow.h index fcf0fdae..12feaea6 100644 --- a/src-qt5/core/libLumina/RootSubWindow.h +++ b/src-qt5/core/libLumina/RootSubWindow.h @@ -20,6 +20,7 @@ #include #include + class RootSubWindow : public QFrame{ Q_OBJECT public: @@ -32,6 +33,7 @@ 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 @@ -42,7 +44,7 @@ private: bool closing; //Title bar objects QBoxLayout *titleBar, *mainLayout; - QToolButton *closeB, *maxB, *minB; + QToolButton *closeB, *maxB, *minB, *otherB; QLabel *titleLabel; QMenu *otherM; //menu of other actions void initWindowFrame(); @@ -64,13 +66,13 @@ public slots: void startResizing(); private slots: - void aboutToActivate(); void propertyChanged(NativeWindow::Property, QVariant); protected: void mousePressEvent(QMouseEvent*); void mouseMoveEvent(QMouseEvent*); void mouseReleaseEvent(QMouseEvent*); + void leaveEvent(QEvent *ev); }; -- cgit