diff options
-rw-r--r-- | src-qt5/core/libLumina/NativeWindow.cpp | 5 | ||||
-rw-r--r-- | src-qt5/core/libLumina/NativeWindow.h | 2 | ||||
-rw-r--r-- | src-qt5/core/libLumina/RootSubWindow.cpp | 258 | ||||
-rw-r--r-- | src-qt5/core/libLumina/RootSubWindow.h | 44 |
4 files changed, 275 insertions, 34 deletions
diff --git a/src-qt5/core/libLumina/NativeWindow.cpp b/src-qt5/core/libLumina/NativeWindow.cpp index bd42ecaa..bcdc30d0 100644 --- a/src-qt5/core/libLumina/NativeWindow.cpp +++ b/src-qt5/core/libLumina/NativeWindow.cpp @@ -35,3 +35,8 @@ void NativeWindow::setProperty(NativeWindow::Property prop, QVariant val){ hash.insert(prop, val); emit PropertyChanged(prop, val); } + +void NativeWindow::requestProperty(NativeWindow::Property prop, QVariant val){ + if(prop == NativeWindow::None){ return; } + emit RequestPropertyChange(winid, prop, val); +} diff --git a/src-qt5/core/libLumina/NativeWindow.h b/src-qt5/core/libLumina/NativeWindow.h index 59b955c3..03d7e27d 100644 --- a/src-qt5/core/libLumina/NativeWindow.h +++ b/src-qt5/core/libLumina/NativeWindow.h @@ -55,6 +55,7 @@ public: QVariant property(NativeWindow::Property); void setProperty(NativeWindow::Property, QVariant); + void requestProperty(NativeWindow::Property, QVariant); private: QHash <NativeWindow::Property, QVariant> hash; @@ -64,6 +65,7 @@ private: signals: //General Notifications void PropertyChanged(NativeWindow::Property, QVariant); + void RequestPropertyChange(WId, NativeWindow::Property, QVariant); void WindowClosed(WId); //Action Requests (not automatically emitted - typically used to ask the WM to do something) diff --git a/src-qt5/core/libLumina/RootSubWindow.cpp b/src-qt5/core/libLumina/RootSubWindow.cpp index e4c3c4ee..9769c922 100644 --- a/src-qt5/core/libLumina/RootSubWindow.cpp +++ b/src-qt5/core/libLumina/RootSubWindow.cpp @@ -6,22 +6,24 @@ //=========================================== #include "RootSubWindow.h" #include <QDebug> +#include <QApplication> +#include <QVBoxLayout> +#include <QVBoxLayout> // === PUBLIC === -RootSubWindow::RootSubWindow(QMdiArea *root, NativeWindow *win) : QMdiSubWindow(0){ +RootSubWindow::RootSubWindow(QWidget *root, NativeWindow *win) : QFrame(root){ this->setAttribute(Qt::WA_DeleteOnClose); + this->setMouseTracking(true); //Create the QWindow and QWidget containers for the window WIN = win; closing = false; WinWidget = QWidget::createWindowContainer( WIN->window(), this); - this->setWidget(WinWidget); + initWindowFrame(); 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))); - //Now add this window to the root QMdiArea - root->addSubWindow(this); //Make sure the visibily property only gets loaded after it is added to the root area propertyChanged(NativeWindow::Visible, WIN->property(NativeWindow::Visible)); } @@ -35,6 +37,127 @@ 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)){ + //above the frame itself - need to figure out which quadrant it is in (8-directions) + if(pt.y() < 3){ + //One of the top options + if(pt.x() < 3){ + if(setoffset){ offset.setX(pt.x()); offset.setY(pt.y()); } //difference from top-left corner + return ResizeTopLeft; + }else if(pt.x() > (this->width()-3)){ + 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) ){ + //One of the bottom options + if(pt.x() < 3){ + 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)){ + if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(this->height()-pt.y()); } //difference from bottom-right corner + return ResizeBottomRight; + }else{ + if(setoffset){ offset.setX(0); offset.setY(this->height() - pt.y()); } //difference from bottom edge (X does not matter) + return ResizeBottom; + } + }else{ + //One of the side options + if(pt.x() < 3){ + 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) ){ + if(setoffset){ offset.setX(this->width()-pt.x()); offset.setY(0); } //difference from right edge (Y does not matter) + return ResizeRight; + }else{ + return Normal; + } + } + } + return Normal; +} + +void RootSubWindow::setMouseCursor(ModState state, bool override){ + 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{ + this->setCursor(shape); + } +} + +void RootSubWindow::initWindowFrame(){ + mainLayout = new QVBoxLayout(this); + titleBar = new QHBoxLayout(this); + closeB = new QToolButton(this); + maxB = new QToolButton(this); + minB = new QToolButton(this); + titleLabel = new QLabel(this); + titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + otherM = new QMenu(this); //menu of other actions + 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(titleLabel); + titleBar->addWidget(minB); + titleBar->addWidget(maxB); + titleBar->addWidget(closeB); + WinWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + mainLayout->addLayout(titleBar); + mainLayout->addWidget(WinWidget); + //Now all the stylesheet options + this->setObjectName("WindowFrame"); + closeB->setObjectName("Button_Close"); + minB->setObjectName("Button_Minimize"); + 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; }"); + //And adjust the margins + mainLayout->setContentsMargins(4,4,4,4); + mainLayout->setSpacing(0); + titleBar->setSpacing(1); + titleBar->setContentsMargins(0,0,0,0); +} + void RootSubWindow::LoadProperties( QList< NativeWindow::Property> list){ for(int i=0; i<list.length(); i++){ propertyChanged( list[i], WIN->property(list[i]) ); @@ -48,19 +171,41 @@ void RootSubWindow::clientClosed(){ this->close(); } -void RootSubWindow::clientHidden(){ - qDebug() << "Client Hidden"; - this->hide(); +//Button Actions - public so they can be tied to key shortcuts and stuff as well +void RootSubWindow::toggleMinimize(){ + +} + +void RootSubWindow::toggleMaximize(){ + +} + +void RootSubWindow::triggerClose(){ + WIN->emit RequestClose(WIN->id()); +} + +void RootSubWindow::toggleSticky(){ + +} + +void RootSubWindow::activate(){ + WIN->emit RequestActivate(WIN->id()); +} + +//Mouse Interactivity +void RootSubWindow::startMoving(){ + } -void RootSubWindow::clientShown(){ - qDebug() << "Client Shown"; - this->show(); +void RootSubWindow::startResizing(){ + } + + // === PRIVATE SLOTS === void RootSubWindow::aboutToActivate(){ - WIN->emit RequestActivate(WIN->id()); + } void RootSubWindow::propertyChanged(NativeWindow::Property prop, QVariant val){ @@ -68,26 +213,26 @@ void RootSubWindow::propertyChanged(NativeWindow::Property prop, QVariant val){ qDebug() << "Set Window Property:" << prop << val; switch(prop){ case NativeWindow::Visible: - if(val.toBool()){ clientShown(); } - else{ clientHidden(); } + if(val.toBool()){ this->show(); } + else{ this->hide(); } break; case NativeWindow::Title: - this->setWindowTitle(val.toString()); + titleLabel->setText(val.toString()); break; case NativeWindow::Icon: - this->setWindowIcon(val.value< QIcon>()); + //this->setWindowIcon(val.value< QIcon>()); break; case NativeWindow::Size: - this->resize(val.toSize()); + WinWidget->resize(val.toSize()); break; case NativeWindow::MinSize: - this->setMinimumSize(val.toSize()); + WinWidget->setMinimumSize(val.toSize()); break; case NativeWindow::MaxSize: - this->setMaximumSize(val.toSize()); + WinWidget->setMaximumSize(val.toSize()); break; case NativeWindow::Active: - if(val.toBool()){ this->mdiArea()->setActiveSubWindow(this); } + WinWidget->setFocus(); break; /*case NativeWindow::WindowFlags: this->setWindowFlags( val.value< Qt::WindowFlags >() ); @@ -98,13 +243,76 @@ void RootSubWindow::propertyChanged(NativeWindow::Property prop, QVariant val){ } // === PROTECTED === -void RootSubWindow::closeEvent(QCloseEvent *ev){ - if(!closing){ - //qDebug() << "Close Window By Button:" << WIN->id(); - ev->ignore(); - WIN->emit RequestClose(WIN->id()); +void RootSubWindow::mousePressEvent(QMouseEvent *ev){ + 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(); + if(this->childAt(ev->pos())!=0){ + //Check for any non-left-click event and skip it + if(ev->button()!=Qt::LeftButton){ return; } + activeState = Move; + offset.setX(ev->pos().x()); offset.setY(ev->pos().y()); }else{ - QMdiSubWindow::closeEvent(ev); + //Clicked on the frame somewhere + activeState = getStateAtPoint(ev->pos(), true); //also have it set the offset variable } + setMouseCursor(activeState, true); //this one is an override cursor } + +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(); + switch(activeState){ + case Move: + geom.moveTopLeft(ev->globalPos()-offset); //will not change size + break; + case ResizeTop: + geom.setTop(ev->globalPos().y()-offset.y()); + break; + case ResizeTopRight: + geom.setTopRight(ev->globalPos()-offset); + break; + case ResizeRight: + geom.setRight(ev->globalPos().x()-offset.x()); + break; + case ResizeBottomRight: + geom.setBottomRight(ev->globalPos()-offset); + break; + case ResizeBottom: + geom.setBottom(ev->globalPos().y()-offset.y()); + break; + case ResizeBottomLeft: + geom.setBottomLeft(ev->globalPos()-offset); + break; + case ResizeLeft: + geom.setLeft(ev->globalPos().x()-offset.x()); + break; + case ResizeTopLeft: + geom.setTopLeft(ev->globalPos()-offset); + break; + default: + break; + } + this->setGeometry(geom); + } +} + +void RootSubWindow::mouseReleaseEvent(QMouseEvent *ev){ + //Check for a right-click event + qDebug() << "Frame Mouse Release Event"; + ev->accept(); + if( (activeState==Normal) && (titleBar->geometry().contains(ev->pos())) && (ev->button()==Qt::RightButton) ){ + otherM->popup(ev->globalPos()); + return; + } + activeState = Normal; + QApplication::restoreOverrideCursor(); + setMouseCursor( getStateAtPoint(ev->pos()) ); +} diff --git a/src-qt5/core/libLumina/RootSubWindow.h b/src-qt5/core/libLumina/RootSubWindow.h index c56f3c96..fcf0fdae 100644 --- a/src-qt5/core/libLumina/RootSubWindow.h +++ b/src-qt5/core/libLumina/RootSubWindow.h @@ -10,41 +10,67 @@ #ifndef _LUMINA_ROOT_WINDOW_SUB_WINDOW_H #define _LUMINA_ROOT_WINDOW_SUB_WINDOW_H -#include <QMdiArea> -#include <QMdiSubWindow> #include <QWindow> #include <QWidget> #include <QCloseEvent> - +#include <QFrame> +#include <QBoxLayout> +#include <QLabel> +#include <QToolButton> +#include <QMenu> #include <NativeWindow.h> -class RootSubWindow : public QMdiSubWindow{ +class RootSubWindow : public QFrame{ Q_OBJECT public: - RootSubWindow(QMdiArea *root, NativeWindow *win); + RootSubWindow(QWidget *root, NativeWindow *win); ~RootSubWindow(); WId id(); private: + //Window status + enum ModState{Normal, Move, ResizeTop, ResizeTopRight, ResizeRight, ResizeBottomRight, ResizeBottom, ResizeBottomLeft, ResizeLeft, ResizeTopLeft}; + ModState activeState; + 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 + //Native window embed objects NativeWindow *WIN; QWidget *WinWidget; bool closing; + //Title bar objects + QBoxLayout *titleBar, *mainLayout; + QToolButton *closeB, *maxB, *minB; + QLabel *titleLabel; + QMenu *otherM; //menu of other actions + void initWindowFrame(); void LoadProperties( QList< NativeWindow::Property> list); public slots: void clientClosed(); + + //Button Actions - public so they can be tied to key shortcuts and stuff as well + void toggleMinimize(); + void toggleMaximize(); + void triggerClose(); + void toggleSticky(); + void activate(); + + //Mouse Interactivity + void startMoving(); + void startResizing(); private slots: - void clientHidden(); - void clientShown(); void aboutToActivate(); void propertyChanged(NativeWindow::Property, QVariant); - protected: - void closeEvent(QCloseEvent*); + void mousePressEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); }; |