说明
我们知道QWidget等设置了this->setWindowFlags(Qt::FramelessWindowHint);
后无法移动和调整大小,但实际项目中是需要窗口能够调整大小的。所以以实现FrameLess弹窗调整大小需求,以此类推,移动窗口也就很简单了(这里没有实现)。
并且这里,还实现了QWidget的窗口阴影,因为FramelessWindow也没有窗口阴影。
代码
#ifndef SHADOWWIDGET_H
#define SHADOWWIDGET_H#include <QWidget>enum MouseDirection {DirectionLeft,DirectionRight,DirectionTop,DirectionBottom,DirectionTopLeft,DirectionTopRight,DirectionBottomLeft,DirectionBottomRight
};class ShadowWidget : public QWidget
{Q_OBJECT
public:explicit ShadowWidget(QWidget *parent = nullptr, QWidget *subWidget = nullptr);protected:bool event(QEvent *event) override;void paintEvent(QPaintEvent *event) override;void resizeEvent(QResizeEvent *event) override;void mousePressEvent(QMouseEvent *event) override;void mouseReleaseEvent(QMouseEvent *event) override;
// void mouseMoveEvent(QMouseEvent *event) override;void responseHoverMoveEvent(QHoverEvent *event);signals:public:uint32_t m_shadowWidthMargin;uint32_t m_shadowHeightMargin;uint32_t m_minWidth;uint32_t m_minHeight;uint32_t m_maxWidth;uint32_t m_maxHeight;QWidget *m_subWidget;private:QPixmap m_shadowPixmap;bool m_bCanDrag;bool m_bMousePress;QPoint m_globalPointPressed;QRect m_geoPressed;MouseDirection m_curMousePressMoveDirection;
};#endif // SHADOWWIDGET_H
#include "shadowwidget.h"#include <QPainter>
#include <QResizeEvent>
#include <QApplication>
#include <QScreen>
#include <QDebug>static QPixmap ninePatchScalePixmap(QPixmap *pix, int iHorzSplit, int iVertSplit, int DstWidth, int DstHeight)// 参考[1]
{int pixWidth = pix->width();int pixHeight = pix->height();QPixmap pix_1 = pix->copy(0, 0, iHorzSplit, iVertSplit);QPixmap pix_2 = pix->copy(iHorzSplit, 0, pixWidth - iHorzSplit * 2, iVertSplit);QPixmap pix_3 = pix->copy(pixWidth - iHorzSplit, 0, iHorzSplit, iVertSplit);QPixmap pix_4 = pix->copy(0, iVertSplit, iHorzSplit, pixHeight - iVertSplit * 2);QPixmap pix_5 = pix->copy(iHorzSplit, iVertSplit, pixWidth - iHorzSplit * 2, pixHeight - iVertSplit * 2);QPixmap pix_6 = pix->copy(pixWidth - iHorzSplit, iVertSplit, iHorzSplit, pixHeight - iVertSplit * 2);QPixmap pix_7 = pix->copy(0, pixHeight - iVertSplit, iHorzSplit, iVertSplit);QPixmap pix_8 = pix->copy(iHorzSplit, pixHeight - iVertSplit, pixWidth - iHorzSplit * 2, pixWidth - iHorzSplit * 2);QPixmap pix_9 = pix->copy(pixWidth - iHorzSplit, pixHeight - iVertSplit, iHorzSplit, iVertSplit);pix_2 = pix_2.scaled(DstWidth - iHorzSplit * 2, iVertSplit, Qt::IgnoreAspectRatio);//保持高度拉宽;pix_4 = pix_4.scaled(iHorzSplit, DstHeight - iVertSplit * 2, Qt::IgnoreAspectRatio);//保持宽度拉高;pix_5 = pix_5.scaled(DstWidth - iHorzSplit * 2, DstHeight - iVertSplit * 2, Qt::IgnoreAspectRatio);//宽高都缩放;pix_6 = pix_6.scaled(iHorzSplit, DstHeight - iVertSplit * 2, Qt::IgnoreAspectRatio);//保持宽度拉高;pix_8 = pix_8.scaled(DstWidth - iHorzSplit * 2, iVertSplit);//保持高度拉宽;QPixmap resultImg(DstWidth, DstHeight);// 需设置背景透明;resultImg.fill(Qt::transparent);QPainter* painter = new QPainter(&resultImg);if (!resultImg.isNull()) {painter->drawPixmap(0, 0, pix_1);painter->drawPixmap(iHorzSplit, 0, pix_2);painter->drawPixmap(DstWidth - iHorzSplit, 0, pix_3);painter->drawPixmap(0, iVertSplit, pix_4);painter->drawPixmap(iHorzSplit, iVertSplit, pix_5);painter->drawPixmap(DstWidth - iHorzSplit, iVertSplit, pix_6);painter->drawPixmap(0, DstHeight - iVertSplit, pix_7);painter->drawPixmap(iHorzSplit, DstHeight - iVertSplit, pix_8);painter->drawPixmap(DstWidth - iHorzSplit, DstHeight - iVertSplit, pix_9);painter->end();}return resultImg;
}ShadowWidget::ShadowWidget(QWidget *parent, QWidget *subWidget) :QWidget(parent),m_shadowWidthMargin(5),m_shadowHeightMargin(5),m_subWidget(subWidget),m_shadowPixmap(":resource/shadow.png"),m_bCanDrag(false),m_bMousePress(false),m_globalPointPressed(QPoint()),m_minWidth(120),m_minHeight(120),m_maxWidth(INT32_MAX),m_maxHeight(INT32_MAX)
{this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint);this->setAttribute(Qt::WA_TranslucentBackground, true);this->setAttribute(Qt::WA_Hover, true); // 开启Hover事件记录,替代mouseMoveEvent,但CPU代价更高
}bool ShadowWidget::event(QEvent *event)
{if (QEvent::HoverMove == event->type()){QHoverEvent *hoverEvent = static_cast<QHoverEvent *>(event);this->responseHoverMoveEvent(hoverEvent);event->accept();return true;}return QWidget::event(event);
}void ShadowWidget::paintEvent(QPaintEvent *event)
{QWidget::paintEvent(event);QPainter painter(this);painter.drawPixmap(this->rect(), m_shadowPixmap);
}void ShadowWidget::resizeEvent(QResizeEvent *event)
{// 重新生成geometry()大小的图片m_shadowPixmap = ::ninePatchScalePixmap(&m_shadowPixmap, 10, 10,event->size().width(), event->size().height());// 重置subWidget大小if (m_subWidget){QRect rect;rect.setTopLeft(QPoint(m_shadowWidthMargin, m_shadowWidthMargin));rect.setSize(QSize(this->geometry().width() - 2 * m_shadowWidthMargin,this->geometry().height() - 2 * m_shadowHeightMargin));m_subWidget->setGeometry(rect);}QWidget::resizeEvent(event);this->repaint();
}void ShadowWidget::mousePressEvent(QMouseEvent *event)
{if (m_bCanDrag){m_bMousePress = true;m_globalPointPressed = event->globalPos();m_geoPressed = this->geometry();}return QWidget::mousePressEvent(event);
}void ShadowWidget::mouseReleaseEvent(QMouseEvent *event)
{m_bMousePress = false;m_globalPointPressed = QPoint();m_geoPressed = QRect();return QWidget::mouseReleaseEvent(event);
}void ShadowWidget::responseHoverMoveEvent(QHoverEvent *event)
{QPoint pt = event->pos();// 可拖拽区域鼠标拖拽,调整窗口大小if (m_bMousePress){QPoint globalPt = QPoint(this->x() + pt.x(), this->y() + pt.y());double xDiff = globalPt.x() - m_globalPointPressed.x();double yDiff = globalPt.y() - m_globalPointPressed.y();QRect rect = m_geoPressed;if (m_curMousePressMoveDirection == DirectionLeft)rect.setX(rect.x() + xDiff);else if (m_curMousePressMoveDirection == DirectionRight)rect.setWidth(rect.width() + xDiff);else if (m_curMousePressMoveDirection == DirectionTop)rect.setY(rect.y() + yDiff);else if (m_curMousePressMoveDirection == DirectionBottom)rect.setHeight(rect.height() + yDiff);else if (m_curMousePressMoveDirection == DirectionTopLeft){rect.setX(rect.x() + xDiff);rect.setY(rect.y() + yDiff);}else if (m_curMousePressMoveDirection == DirectionTopRight){rect.setY(rect.y() + yDiff);rect.setWidth(rect.width() + xDiff);}else if (m_curMousePressMoveDirection == DirectionBottomLeft){rect.setHeight(rect.height() + yDiff);rect.setX(rect.x() + xDiff);}else if (m_curMousePressMoveDirection == DirectionBottomRight){rect.setHeight(rect.height() + yDiff);rect.setWidth(rect.width() + xDiff);}rect.setWidth(qMax<int>(qMin<int>(m_maxWidth, rect.width()), m_minWidth));rect.setHeight(qMax<int>(qMin<int>(m_maxHeight, rect.height()), m_minHeight));this->setGeometry(rect);return;}// 若鼠标在subWidget中则重置鼠标QRect subWidgetCannotDragRect = m_subWidget->geometry().adjusted(m_shadowWidthMargin,m_shadowHeightMargin,-m_shadowWidthMargin,-m_shadowHeightMargin);if (subWidgetCannotDragRect.contains(pt)){m_bCanDrag = false;QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));return;}m_bCanDrag = true;// 根据当前鼠标位置确定应该选择那种伸缩图标MouseDirection direction;if (pt.x() < 2 * m_shadowWidthMargin){direction = DirectionLeft;if (pt.y() < 2 * m_shadowHeightMargin)direction = DirectionTopLeft;else if ((this->height() - pt.y()) < 2 * m_shadowHeightMargin)direction = DirectionBottomLeft;}else if ((this->width() - pt.x()) < 2 * m_shadowWidthMargin){direction = DirectionRight;if (pt.y() < 2 * m_shadowHeightMargin)direction = DirectionTopRight;else if ((this->height() - pt.y()) < 2 * m_shadowHeightMargin)direction = DirectionBottomRight;}else if (pt.y() < 2 * m_shadowHeightMargin){direction = DirectionTop;if (pt.x() < 2 * m_shadowWidthMargin)direction = DirectionTopLeft;else if ((this->width() - pt.x()) < 2 * m_shadowWidthMargin)direction = DirectionTopRight;}else if ((this->height() - pt.y()) < 2 * m_shadowHeightMargin){direction = DirectionBottom;if (pt.x() < 2 * m_shadowWidthMargin)direction = DirectionBottomLeft;else if ((this->width() - pt.x()) < 2 * m_shadowWidthMargin)direction = DirectionBottomRight;}m_curMousePressMoveDirection = direction;switch (direction){case DirectionLeft:case DirectionRight:QApplication::setOverrideCursor(QCursor(Qt::SizeHorCursor));break;case DirectionTop:case DirectionBottom:QApplication::setOverrideCursor(QCursor(Qt::SizeVerCursor));break;case DirectionTopLeft:case DirectionBottomRight:QApplication::setOverrideCursor(QCursor(Qt::SizeFDiagCursor));break;case DirectionTopRight:case DirectionBottomLeft:QApplication::setOverrideCursor(QCursor(Qt::SizeBDiagCursor));break;}
}
解释
- m_minWidth、m_minHeight、m_maxWidth、m_maxHeight:防止窗口太大或太小。
- m_shadowWidthMargin、m_shadowHeightMargin:记录窗口阴影的宽和高。
- m_subWidget:用于中间放置主Widget(阴影弹窗作为后面的一个widget)。
- m_shadowPixmap:阴影图。
- m_bCanDrag:记录是否可调整大小。
- m_bMousePress:记录是否在可调整大小的情况下,鼠标按下了。
- m_globalPointPressed:用于记录按下时的全局坐标点。
- m_geoPressed:记录按下时,阴影窗口的geometry。
- m_curMousePressMoveDirection:记录拖拽方向。
- enum MouseDirection:标记窗口方向,共有8个方向。
参考:
1