说明
我们知道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