Qt 之无边框窗口移动到桌面边缘动画
说明
无边框窗口在移动到桌面边缘时不会自动全屏或者半屏,网上有些资料能实现全屏或者半屏,但是窗口变化过程中没有动画。所以我想实践下能不能实现windows原生的效果。
实现思路
以一个透明有边框的窗口进行变换为基准。需要用到继承QWidget, 自绘(paintEvent), 动画(QPropertyAnimation)等知识
透明窗口
MoveResizeWidget.h
目的是画出一个透明的 带边框的窗口
#include <QWidget>
#include <QPropertyAnimation>
class QMoveResizeWidget : public QWidget
{
Q_OBJECT
public:
//开始动画
void startAnimation(const QVariant& startV, const QVariant& endV);
//停止动画
void stopAnimation();
protected:
void paintEvent(QPaintEvent *event);
//显示事件
void showEvent(QShowEvent *event);
//隐藏事件
void hideEvent(QHideEvent *event);
private:
画阴影
void drawShadow(QPainter* painter);
public:
bool m_bHelpWidhetShow; //窗口是否显示
QRect m_endRect; //目标位置
private:
QPropertyAnimation* x_pAnimation; //动画框架
};
MoveResizeWidget.cpp
#include "MoveResizWidget.h"
#include <QPainter>
QMoveResizeWidget::QMoveResizeWidget(QWidget* parent /*= nullptr*/)
:QWidget(parent)
, x_pAnimation(nullptr)
{
//设置为无边框 工具
setWindowFlags(Qt::FramelessWindowHint | Qt::Tool);
//设置背景透明
setAttribute(Qt::WA_TranslucentBackground);
//默认不显示
m_bHelpWidhetShow = false;
//动画框架
x_pAnimation = new QPropertyAnimation(this, "geometry");
x_pAnimation->setDuration(100);
}
QMoveResizeWidget::~QBaseWidget()
{
}
void QMoveResizeWidget::startAnimation(const QVariant& startV, const QVariant& endV)
{
//将辅助窗口移动到发起窗口相同的位置
this->setGeometry(startV.toRect());
//存储目的位置
m_endRect = endV.toRect();
//将辅助窗口显示出来
show();
//设置动画的起止值
x_pAnimation->setStartValue(startV);
x_pAnimation->setEndValue(endV);
//开始动画
x_pAnimation->start();
}
void QMoveResizeWidget::stopAnimation()
{
//停止动画并隐藏辅助窗口
x_pAnimation->stop();
hide();
}
void QMoveResizeWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
//画图
QPainter painter(this);
//反锯齿
painter.setRenderHint(QPainter::Antialiasing);
//画阴影
drawShadow(&painter);
}
void QMoveResizeWidget::showEvent(QShowEvent *event)
{
Q_UNUSED(event);
m_bHelpWidhetShow = true;
}
void QMoveResizeWidget::hideEvent(QHideEvent *event)
{
Q_UNUSED(event);
m_bHelpWidhetShow = false;
}
void QMoveResizeWidget::drawShadow(QPainter* painter)
{
//画阴影
painter->save();
QPainterPath path;
path.setFillRule(Qt::WindingFill);
path.addRoundedRect(10, 10, this->width() - 20, this->height() - 20, 5, 5);
painter->setRenderHint(QPainter::Antialiasing, true);
// painter->fillPath(path, QBrush(Qt::red));
// QColor color(92, 93, 95, 50);
QColor color(Qt::red); //阴影颜色 这里为了显眼使用红色
int arr[10] = { 150, 120, 80, 50, 40, 30, 20, 10, 5, 5 };
for (int i = 0; i < 10; i++)
{
QPainterPath path;
path.setFillRule(Qt::WindingFill);
if (i == 5)
path.addRect(10 - i - 1, 10 - i - 1, this->width() - (10 - i) * 2, this->height() - (10 - i) * 2);
else
path.addRoundedRect(10 - i - 1, 10 - i - 1, this->width() - (10 - i) * 2, this->height() - (10 - i) * 2, 5, 5);
color.setAlpha(arr[i]);
painter->setPen(color);
painter->drawPath(path);
}
painter->restore();
}
帮助类
MoveResizeHelper.h
#include <QObject>
#include <QMap>
#include "MoveResizWidget.h"
class QMoveReSizeHeler : public QObject
{
Q_OBJECT
public:
QReSizeHeler(QObject *parent = nullptr);
~QReSizeHeler();
void activeOn(QWidget* widget);
void removeFrom(QWidget* widget);
bool eventFilter(QObject *watched, QEvent *event);
private:
QMoveResizeWidget* x_pBaseWidget;
QMap<QWidget*, bool> x_widgetMap;
};
MoveResizeHelper.cpp
#include "MoveResizeHelper.h"
QMoveResizeHelper::QMoveResizeHelper(QObject *parent /*= nullptr*/)
:QObject(parent)
{
x_pBaseWidget = new QMoveResizeWidget();
x_pBaseWidget ->hide();
}
QReSizeHeler::~QReSizeHeler()
{
if (x_pBaseWidget)
{
delete x_pBaseWidget;
x_pBaseWidget = nullptr;
}
}
void QMoveResizeHelper::activeOn(QWidget* widget)
{
x_widgetMap.insert(widget, false);
//安装事件监视器
widget->installEventFilter(this);
}
void QMoveResizeHelper::removeFrom(QWidget* widget)
{
x_widgetMap.remove(widget);
}
bool QMoveResizeHelper::eventFilter(QObject *watched, QEvent *event)
{
QWidget* _pWidget = (QWidget*)(watched);
if (x_widgetMap.contains(_pWidget))
{
if (event->type() == QEvent::MouseButtonPress)
{
x_widgetMap[_pWidget] = true;
}
else if (event->type() == QEvent::MouseMove)
{
if (x_widgetMap[_pWidget] == true)
{
QMouseEvent* _pEvent = (QMouseEvent*)(event);
QPoint _pos = _pEvent->globalPos();
QRect _rect = QApplication::desktop()->availableGeometry();
if (_rect.width() - _pos.x() < 10)
{
//移动到最右边
if (!x_pBaseWidget->m_bHelpWidhetShow)
{
//辅助窗口未显示, 开始动画
x_pBaseWidget->startAnimation(_pWidget->geometry(), QRect(_rect.width() / 2, 0, _rect.width() / 2, _rect.height()));
}
}
else if (_pos.x() < 10)
{
//移动到最左边
if (!x_pBaseWidget->m_bHelpWidhetShow)
{
x_pBaseWidget->startAnimation(_pWidget->geometry(), QRect(0, 0, _rect.width() / 2, _rect.height()));
}
}
else if (_pos.y() < 10)
{
//移动到顶部
if (!x_pBaseWidget->m_bHelpWidhetShow)
{
x_pBaseWidget->startAnimation(_pWidget->geometry(), QRect(0, 0, _rect.width(), _rect.height()));
}
}
else
{
//隐藏辅助窗口
x_pBaseWidget->hide();
}
}
}
else if (event->type() == QEvent::MouseButtonRelease)
{
x_widgetMap[_pWidget] = false;
if (x_pBaseWidget->m_bHelpWidhetShow)
{
//辅助窗口显示, 将窗口置为目标位置
_pWidget->setGeometry(x_pBaseWidget->m_endRect);
}
//停止动画
x_pBaseWidget->stopAnimation();
}
}
return QObject::eventFilter(watched, event);
}
效果