![]() |
![]() |
很重要--轉載聲明
- 本站文章無特別說明,皆爲原創,版權全部,轉載時請用連接的方式,給出原文出處。同時寫上原做者:朝十晚八 or Twowords
- 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時經過修改本文達到有利於轉載者的目的。
最近項目須要實現windows下橡皮筋的效果,因此對此作了一些瞭解,特此記錄。html
首先windows系統是支持橡皮筋效果的,須要使用win32方 法:SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, showFullWindow, NULL, 0);showFullWindow是一個變量,若是須要windows默認支持橡皮筋則須要傳遞參數false,不然傳遞參數true,若是使用 windows默認的橡皮筋縮放,效果如圖1所示,會產生一個矩形框,不論是窗口移動仍是放大縮小,都會對該矩形框做用,而後當鼠標彈起時,真實窗口才會 移動或者放大縮小。若是不使用橡皮筋拖拽的方式,那麼窗口就是實時的拖拽。windows
圖1 windows橡皮筋less
在使用Qt窗口時,若是須要支持windows系統這種方式的拖拽,不可以使用setGeometry該函數來移動或者放大縮小窗口,而須要 重寫QWidget::nativeEvent這個方法,該方法是在消息進入qt事件循環以前調用的,也就是說該方法會在mouseEvent等方法以前 調用,nativeEvent方法的實現請看另外一篇文章qt 拖拽 修改大小,不過在我使用的過程當中,使用了HTCAPTION這個屬性後,原始窗口的雙擊放大事件被屏蔽掉了,到如今緣由未搞清。在qt 拖拽 修改大小這篇文字中提到的bug,我用迂迴的方式解決了,那就是使用Qt::WindowSystemMenuHint屬性,可是窗口的放大和縮小使用另外一種方式解決。ide
下面就是我使用代理的方式來支持窗口拖拽,因爲該代理代碼行數過多,我只寫下重點的部分,該代理代碼我也是從別人那兒拷貝的,後來根據我本身的理解和項目需求添加了一些東西。函數
代理頭文件佈局
1 #ifndef NC_FRAMELESS_HELPER_H 2 3 #define NC_FRAMELESS_HELPER_H 4 5 #include 6 7 #include 8 9 #include 10 11 #include "commonControls/include/commoncontrols_global.h" 12 13 class WidgetResizeHandlerImpl; 14 15 class CRubberBand : public QRubberBand 16 17 { 18 19 public: 20 21 CRubberBand(QRubberBand::Shape s, QWidget * parent = nullptr); 22 23 ~CRubberBand(); 24 25 protected: 26 27 virtual void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; 28 29 virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; 30 31 void changeEvent(QEvent *) Q_DECL_OVERRIDE; 32 33 void showEvent(QShowEvent *) Q_DECL_OVERRIDE; 34 35 void moveEvent(QMoveEvent *) Q_DECL_OVERRIDE; 36 37 private: 38 39 }; 40 41 //鼠標狀態,能夠獲取鼠標當前和目標窗口的關係 42 43 class CursorPosCalculator 44 45 { 46 47 public: 48 49 CursorPosCalculator(){ reset(); } 50 51 void reset(); 52 53 void recalculate(const QPoint& globalMousePos, const QRect& frameRect); 54 55 public: 56 57 bool onEdges; 58 59 bool onLeftEdge; 60 61 bool onRightEdge; 62 63 bool onTopEdge; 64 65 bool onBottomEdge; 66 67 bool onTopLeftEdge; 68 69 bool onBottomLeftEdge; 70 71 bool onTopRightEdge; 72 73 bool onBottomRightEdge; 74 75 static int mBorderWidth; 76 77 }; 78 79 //真正的處理操做類 80 81 class WidgetData 82 83 { 84 85 public: 86 87 WidgetData(WidgetResizeHandlerImpl * d, QWidget* topLevelWidget); 88 89 ~WidgetData(); 90 91 QWidget * widget(); 92 93 void handleWidgetEvent(QEvent * event);//處理指定窗口事件入口函數 94 95 void updateRubberBandStatus(); 96 97 private: 98 99 void updateCursorShape(const QPoint& globalMousePos); 100 101 void resizeWidget(const QPoint& globalMousePos); 102 103 void moveWidget(const QPoint& globalMousePos); 104 105 void handleMousePressEvent(QMouseEvent* event); 106 107 void handleMouseReleaseEvent(QMouseEvent* event); 108 109 void handleMouseMoveEvent(QMouseEvent* event); 110 111 void handleLeaveEvent(QEvent* event); 112 113 void handleHoverMoveEvent(QHoverEvent* event); 114 115 private: 116 117 bool mLeftButtonPressed = false; 118 119 bool mCursorShapeChanged = false; 120 121 Qt::WindowFlags mWindowFlags; 122 123 QPoint mDragPos;//拖拽位置起點 124 125 QWidget * mWidget = nullptr;//被代理的窗口指針 126 127 CRubberBand * mRubberBand = nullptr;//橡膠類,支持橡膠操做 128 129 CursorPosCalculator mPressedMousePos;//鼠標按下時光標信息 130 131 CursorPosCalculator mMoveMousePos;//鼠標移動時光標信息 132 133 WidgetResizeHandlerImpl * d_ptr; 134 135 }; 136 137 ///說明:當QWidget設置了Qt::FramelessWindowHint屬性時,能夠藉助該類完成:拖拽+窗口大小更改 138 139 class COMMONCONTROLS_EXPORT WidgetResizeHandler : public QObject 140 141 { 142 143 public: 144 145 explicit WidgetResizeHandler(QObject* parent = 0); 146 147 ~WidgetResizeHandler(); 148 149 public: 150 151 void activateOn(QWidget * topLevelWidget);//添加topLevelWidget事件代理 152 153 void removeFrom(QWidget * topLevelWidget);//移除topLevelWidget事件代理 154 155 Qt::CursorShape CursorShape(QWidget * widget); 156 157 //窗口移動 default:true 158 159 void setWidgetMovable(bool movable); 160 161 bool isWidgetMovable(); 162 163 //大小可變 default:true 164 165 void setWidgetResizable(bool resizable); 166 167 bool isWidgetResizable(); 168 169 // 橡膠式窗口移動 default:false 170 171 void useRubberBandOnMove(bool use); 172 173 bool isUsingRubberBandOnMove(); 174 175 //橡膠式修改大小 default:false 176 177 void useRubberBandOnResize(bool use); 178 179 bool isUsingRubberBandOnResisze(); 180 181 void setBorderWidth(int newBorderWidth); 182 183 int borderWidth(); 184 185 //局部可移動 186 187 void useLocalMoveabled(bool use); 188 189 void addLocalWidget(QWidget *); 190 191 protected: 192 193 virtual bool eventFilter(QObject * obj, QEvent * event) Q_DECL_OVERRIDE;//????????????????????????????????????? 194 195 private: 196 197 WidgetResizeHandlerImpl * d_ptr; 198 199 }; 200 201 #endif // NC_FRAMELESS_HELPER_H
在須要代理的類中聲明WidgetResizeHandler對象,而後使用activateOn方法把須要代理的窗口添加到代理,注意被代 理的窗口須要含有Qt::Window屬性明也就是須要時頂層窗口,若是對於一個複雜的窗口進行代理時,可能會出現一些意向不到的問題,好比:一、 QLabel接受富文本時,代理拿不到鼠標彈起事件,QToolButton對象不放到佈局時,代理也拿不到鼠標彈起事件,這會致使代理不能正常使用,因 此我對該代理進行了修改,添加了useLocalMoveabled接口,容許代理只對局部窗口進行移動,這樣是解決了我前邊提到的兩個問題。若是仔細看 應該也能看到個人代理也是使用setGeometry方法來拖拽窗口的,那麼和我以前談論的橡皮筋方式就有出入了,這個時候重點纔來了,哈哈哈,繼續往下 看,頭文件中有個類CRubberBand,他是繼承自QRubberBand,該類就模擬了一個橡皮筋的過程,只是qt提供的類接口有限,有一些寫過很 難達到,好比說我要實現一些複雜的代理界面,那麼咱們就只能本身繪製了,我經過從新實現paintEvent函數,對該類畫了一個灰色的矩形框,代碼如 下:post
QPainter p(this);this
p.setPen(QPen(QColor(102, 102, 102), 4));spa
QRect rect = this->rect().adjusted(2, 2, -3, -3);3d
p.drawRect(rect);
若是照着我我上邊所說的流程走,就會發現除了一個矩形框以外還會有一個背景色填充,這個時候就奇怪了,咱們paintEvent並無畫背景 啊,呵呵呵,只須要在構造函數里加上這句話便可setAttribute(Qt::WA_NoSystemBackground),效果如圖2所示。
圖2 定製橡皮筋
下邊我添加一些代理部分代碼
一、CRubberBand構造函數
1 CRubberBand::CRubberBand(QRubberBand::Shape s, QWidget * parent) :QRubberBand(QRubberBand::Rectangle, parent) 2 3 { 4 5 setAttribute(Qt::WA_TranslucentBackground); 6 7 #ifndef Q_DEAD_CODE_FROM_QT4_WIN 8 9 setAttribute(Qt::WA_NoSystemBackground); 10 11 #endif //Q_DEAD_CODE_FROM_QT4_WIN 12 13 setWindowFlags(windowFlags() | Qt::FramelessWindowHint); 14 15 }
1 WidgetData::WidgetData(WidgetResizeHandlerImpl * d, QWidget * topLevelWidget) 2 3 { 4 5 this->d_ptr = d; 6 7 mWidget = topLevelWidget; 8 9 mWidget->setMouseTracking(true); 10 11 mWindowFlags = mWidget->windowFlags(); 12 13 //mWindowFlags |= Qt::CustomizeWindowHint | Qt::FramelessWindowHint; 14 15 mWindowFlags |= Qt::FramelessWindowHint; 16 17 mWidget->setWindowFlags(mWindowFlags); 18 19 //mWidget->setWindowFlags( Qt::Popup | Qt::CustomizeWindowHint|Qt::FramelessWindowHint ); 20 21 //Bug fix, mouse move events does not propagate from child widgets. 22 23 //so need the hover events. 24 25 mWidget->setAttribute(Qt::WA_Hover); 26 27 updateRubberBandStatus(); 28 29 bool visible = mWidget->isVisible();//防止非widget被代理 30 31 mWidget->setVisible(visible); 32 33 }
1 void WidgetData::handleWidgetEvent(QEvent * event) 2 3 { 4 5 switch (event->type()) 6 7 { 8 9 case QEvent::MouseButtonPress: 10 11 handleMousePressEvent(static_cast(event)); 12 13 break; 14 15 case QEvent::MouseButtonRelease: 16 17 handleMouseReleaseEvent(static_cast(event)); 18 19 break; 20 21 case QEvent::MouseMove: 22 23 handleMouseMoveEvent(static_cast(event)); 24 25 break; 26 27 case QEvent::Leave: 28 29 handleLeaveEvent(event); 30 31 break; 32 33 case QEvent::HoverMove: 34 35 handleHoverMoveEvent(static_cast(event)); 36 37 break; 38 39 } 40 41 }
1 void WidgetData::handleMousePressEvent(QMouseEvent * event) 2 3 { 4 5 if (event->button() == Qt::LeftButton) 6 7 { 8 9 mLeftButtonPressed = true; 10 11 QRect frameRect = mWidget->frameGeometry(); 12 13 mPressedMousePos.recalculate(event->globalPos(), frameRect); 14 15 mDragPos = event->globalPos() - frameRect.topLeft(); 16 17 if (mPressedMousePos.onEdges) 18 19 { 20 21 if (d_ptr->mUseRubberBandOnResize) 22 23 { 24 25 mRubberBand->setGeometry(frameRect); 26 27 //mRubberBand->show(); 28 29 } 30 31 } 32 33 else if (d_ptr->mUseRubberBandOnMove) 34 35 { 36 37 mRubberBand->setGeometry(frameRect); 38 39 //mRubberBand->show(); 40 41 } 42 43 if (d_ptr->mLocalOnMove)//啓用局部拖拽功能後 須要處理不在指定範圍內的拖拽,並過濾掉 44 45 { 46 47 bool canMove = false; 48 49 for (int i = 0; i < d_ptr->mLocalWidget.size(); ++i) 50 51 { 52 53 if (d_ptr->mLocalWidget[i]->rect().contains(d_ptr->mLocalWidget[i]->mapFromGlobal(event->globalPos()))) 54 55 { 56 57 canMove = true; 58 59 break; 60 61 } 62 63 } 64 65 if (canMove == false && mPressedMousePos.onEdges == false) 66 67 { 68 69 mLeftButtonPressed = false; 70 71 } 72 73 } 74 75 } 76 77 } 78 79 void WidgetData::handleMouseReleaseEvent(QMouseEvent* event) 80 81 { 82 83 if (event->button() == Qt::LeftButton) 84 85 { 86 87 d_ptr->mCanMoveFlag = false; 88 89 mLeftButtonPressed = false; 90 91 mPressedMousePos.reset(); 92 93 if (mRubberBand && mRubberBand->isVisible()) 94 95 { 96 97 mRubberBand->hide(); 98 99 mWidget->setGeometry(mRubberBand->geometry()); 100 101 } 102 103 } 104 105 } 106 107 void WidgetData::handleMouseMoveEvent(QMouseEvent* event) 108 109 { 110 111 if (mLeftButtonPressed) 112 113 { 114 115 if (d_ptr->mWidgetResizable && mPressedMousePos.onEdges) 116 117 { 118 119 resizeWidget(event->globalPos()); 120 121 } 122 123 else if (d_ptr->mWidgetMovable) 124 125 { 126 127 moveWidget(event->globalPos()); 128 129 } 130 131 } 132 133 else if (d_ptr->mWidgetResizable) 134 135 { 136 137 updateCursorShape(event->globalPos()); 138 139 } 140 141 } 142 143 void WidgetData::handleLeaveEvent(QEvent*) 144 145 { 146 147 if (!mLeftButtonPressed) 148 149 { 150 151 mWidget->unsetCursor(); 152 153 } 154 155 } 156 157 void WidgetData::handleHoverMoveEvent(QHoverEvent* event) 158 159 { 160 161 if (mLeftButtonPressed) 162 163 { 164 165 return; 166 167 } 168 169 if (d_ptr->mWidgetResizable) 170 171 { 172 173 updateCursorShape(mWidget->mapToGlobal(event->pos())); 174 175 } 176 177 }
五、更新鼠標狀態
1 void WidgetData::updateCursorShape(const QPoint & globalMousePos) 2 3 { 4 5 if (mWidget->isFullScreen() || mWidget->isMaximized()) 6 7 { 8 9 if (mCursorShapeChanged) 10 11 { 12 13 mWidget->unsetCursor(); 14 15 } 16 17 return; 18 19 } 20 21 mMoveMousePos.recalculate(globalMousePos, mWidget->frameGeometry()); 22 23 if (mMoveMousePos.onTopLeftEdge || mMoveMousePos.onBottomRightEdge) 24 25 { 26 27 mWidget->setCursor(Qt::SizeFDiagCursor); 28 29 mCursorShapeChanged = true; 30 31 } 32 33 else if (mMoveMousePos.onTopRightEdge || mMoveMousePos.onBottomLeftEdge) 34 35 { 36 37 mWidget->setCursor(Qt::SizeBDiagCursor); 38 39 mCursorShapeChanged = true; 40 41 } 42 43 else if (mMoveMousePos.onLeftEdge || mMoveMousePos.onRightEdge) 44 45 { 46 47 mWidget->setCursor(Qt::SizeHorCursor); 48 49 mCursorShapeChanged = true; 50 51 } 52 53 else if (mMoveMousePos.onTopEdge || mMoveMousePos.onBottomEdge) 54 55 { 56 57 mWidget->setCursor(Qt::SizeVerCursor); 58 59 mCursorShapeChanged = true; 60 61 } 62 63 else 64 65 { 66 67 if (mCursorShapeChanged)//修改鼠標狀態 68 69 { 70 71 mWidget->unsetCursor(); 72 73 mCursorShapeChanged = false; 74 75 } 76 77 } 78 79 }
1 void WidgetData::resizeWidget(const QPoint& globalMousePos) 2 3 { 4 5 QRect origRect; 6 7 if (d_ptr->mUseRubberBandOnResize) 8 9 { 10 11 origRect = mRubberBand->frameGeometry(); 12 13 } 14 15 else 16 17 { 18 19 origRect = mWidget->frameGeometry(); 20 21 } 22 23 int left = origRect.left(); 24 25 int top = origRect.top(); 26 27 int right = origRect.right(); 28 29 int bottom = origRect.bottom(); 30 31 origRect.getCoords(&left, &top, &right, &bottom); 32 33 int minWidth = mWidget->minimumWidth(); 34 35 int minHeight = mWidget->minimumHeight(); 36 37 if (mPressedMousePos.onTopLeftEdge) 38 39 { 40 41 left = globalMousePos.x(); 42 43 top = globalMousePos.y(); 44 45 } 46 47 else if (mPressedMousePos.onBottomLeftEdge) 48 49 { 50 51 left = globalMousePos.x(); 52 53 bottom = globalMousePos.y(); 54 55 } 56 57 else if (mPressedMousePos.onTopRightEdge) 58 59 { 60 61 right = globalMousePos.x(); 62 63 top = globalMousePos.y(); 64 65 } 66 67 else if (mPressedMousePos.onBottomRightEdge) 68 69 { 70 71 right = globalMousePos.x(); 72 73 bottom = globalMousePos.y(); 74 75 } 76 77 else if (mPressedMousePos.onLeftEdge) 78 79 { 80 81 left = globalMousePos.x(); 82 83 int max_width = mWidget->maximumWidth(); 84 85 if (right - left > max_width) 86 87 { 88 89 return; 90 91 } 92 93 } 94 95 else if (mPressedMousePos.onRightEdge) 96 97 { 98 99 right = globalMousePos.x(); 100 101 } 102 103 else if (mPressedMousePos.onTopEdge) 104 105 { 106 107 top = globalMousePos.y(); 108 109 } 110 111 else if (mPressedMousePos.onBottomEdge) 112 113 { 114 115 bottom = globalMousePos.y(); 116 117 } 118 119 QRect newRect(QPoint(left, top), QPoint(right, bottom)); 120 121 if (newRect.isValid()) 122 123 { 124 125 if (minWidth > newRect.width()) 126 127 { 128 129 //determine what has caused the width change. 130 131 if (left != origRect.left()) 132 133 newRect.setLeft(origRect.left()); 134 135 else 136 137 newRect.setRight(origRect.right()); 138 139 } 140 141 if (minHeight > newRect.height()) 142 143 { 144 145 //determine what has caused the height change. 146 147 if (top != origRect.top()) 148 149 newRect.setTop(origRect.top()); 150 151 else 152 153 newRect.setBottom(origRect.bottom()); 154 155 } 156 157 if (d_ptr->mUseRubberBandOnResize) 158 159 { 160 161 if (mRubberBand->isVisible() == false) 162 163 { 164 165 mRubberBand->show(); 166 167 } 168 169 mRubberBand->setGeometry(newRect); 170 171 } 172 173 else 174 175 { 176 177 //mWidget->setGeometry(newRect); 178 179 mWidget->move(newRect.topLeft()); 180 181 mWidget->resize(newRect.size()); 182 183 } 184 185 } 186 187 else 188 189 { 190 191 //qDebug() << "Calculated Rect is not valid" << newRect; 192 193 } 194 195 } 196 197 void WidgetData::moveWidget(const QPoint & globalMousePos) 198 199 { 200 201 bool canMove = false; 202 203 if (d_ptr->mLocalOnMove == true && d_ptr->mCanMoveFlag != true) 204 205 { 206 207 for (int i = 0; i < d_ptr->mLocalWidget.size(); ++i) 208 209 { 210 211 if (d_ptr->mLocalWidget[i]->rect().contains(d_ptr->mLocalWidget[i]->mapFromGlobal(globalMousePos))) 212 213 { 214 215 canMove = true; 216 217 d_ptr->mCanMoveFlag = true; 218 219 break; 220 221 } 222 223 } 224 225 } 226 227 else 228 229 { 230 231 canMove = true; 232 233 } 234 235 if (canMove) 236 237 { 238 239 if (d_ptr->mUseRubberBandOnMove) 240 241 { 242 243 if (mRubberBand->isVisible() == false) 244 245 { 246 247 mRubberBand->show(); 248 249 } 250 251 mRubberBand->move(globalMousePos - mDragPos); 252 253 } 254 255 else 256 257 { 258 259 mWidget->move(globalMousePos - mDragPos); 260 261 } 262 263 } 264 265 }