在Qt中实现无边框窗口的鼠标事件处理,主要涉及窗口拖动和调整大小功能。以下是分步实现的代码示例:
1. 创建无边框窗口
首先,创建一个继承自QWidget的自定义窗口类,并设置无边框标志:
#include <QWidget>
#include <QMouseEvent>
class FramelessWindow : public QWidget {
Q_OBJECT
public:
FramelessWindow(QWidget *parent = nullptr) : QWidget(parent) {
setWindowFlags(Qt::FramelessWindowHint);
setMouseTracking(true); // 启用鼠标追踪
}
};
2. 处理窗口拖动
在标题栏区域按下鼠标左键时拖动窗口:
protected:
void mousePressEvent(QMouseEvent *event) override {
if (event->button() == Qt::LeftButton) {
if (isInDragRegion(event->pos())) {
m_dragging = true;
m_dragStartPos = event->globalPos();
m_windowPos = pos();
}
}
QWidget::mousePressEvent(event);
}
void mouseMoveEvent(QMouseEvent *event) override {
if (m_dragging) {
QPoint delta = event->globalPos() - m_dragStartPos;
move(m_windowPos + delta);
}
QWidget::mouseMoveEvent(event);
}
void mouseReleaseEvent(QMouseEvent *event) override {
if (event->button() == Qt::LeftButton) {
m_dragging = false;
}
QWidget::mouseReleaseEvent(event);
}
private:
bool m_dragging = false;
QPoint m_dragStartPos;
QPoint m_windowPos;
bool isInDragRegion(const QPoint &pos) const {
// 假设标题栏高度为30像素
return pos.y() < 30;
}
3. 实现调整窗口大小
检测边缘并调整窗口大小:
protected:
enum ResizeDirection { None, Left, Right, Top, Bottom, TopLeft, TopRight, BottomLeft, BottomRight };
ResizeDirection m_resizeDir = None;
void checkResizeRegion(const QPoint &pos) {
const int borderWidth = 5;
QRect rect = this->rect();
bool left = pos.x() <= borderWidth;
bool right = pos.x() >= rect.width() - borderWidth;
bool top = pos.y() <= borderWidth;
bool bottom = pos.y() >= rect.height() - borderWidth;
if (left && top) m_resizeDir = TopLeft;
else if (right && top) m_resizeDir = TopRight;
else if (left && bottom) m_resizeDir = BottomLeft;
else if (right && bottom) m_resizeDir = BottomRight;
else if (left) m_resizeDir = Left;
else if (right) m_resizeDir = Right;
else if (top) m_resizeDir = Top;
else if (bottom) m_resizeDir = Bottom;
else m_resizeDir = None;
}
void mousePressEvent(QMouseEvent *event) override {
// ... 原有拖动处理 ...
checkResizeRegion(event->pos());
if (m_resizeDir != None) {
m_resizing = true;
m_resizeStartPos = event->globalPos();
m_initialGeometry = geometry();
}
}
void handleResize(const QPoint &globalPos) {
QRect newGeometry = m_initialGeometry;
QPoint delta = globalPos - m_resizeStartPos;
switch (m_resizeDir) {
case Left:
newGeometry.setLeft(newGeometry.left() + delta.x());
break;
case Right:
newGeometry.setRight(newGeometry.right() + delta.x());
break;
case Top:
newGeometry.setTop(newGeometry.top() + delta.y());
break;
case Bottom:
newGeometry.setBottom(newGeometry.bottom() + delta.y());
break;
case TopLeft:
newGeometry.setTopLeft(newGeometry.topLeft() + delta);
break;
case TopRight:
newGeometry.setTopRight(newGeometry.topRight() + delta);
break;
case BottomLeft:
newGeometry.setBottomLeft(newGeometry.bottomLeft() + delta);
break;
case BottomRight:
newGeometry.setBottomRight(newGeometry.bottomRight() + delta);
break;
}
// 限制最小尺寸
newGeometry = newGeometry.normalized().adjusted(0, 0, 1, 1);
if (newGeometry.width() < minimumWidth() || newGeometry.height() < minimumHeight())
return;
setGeometry(newGeometry);
}
4. 更新光标形状
根据鼠标位置调整光标:
void updateCursorShape(const QPoint &pos) {
checkResizeRegion(pos);
switch (m_resizeDir) {
case Left:
case Right:
setCursor(Qt::SizeHorCursor);
break;
case Top:
case Bottom:
setCursor(Qt::SizeVerCursor);
break;
case TopLeft:
case BottomRight:
setCursor(Qt::SizeFDiagCursor);
break;
case TopRight:
case BottomLeft:
setCursor(Qt::SizeBDiagCursor);
break;
default:
unsetCursor();
}
}
5. 双击最大化
双击标题栏切换最大化:
void mouseDoubleClickEvent(QMouseEvent *event) override {
if (event->button() == Qt::LeftButton && isInDragRegion(event->pos())) {
toggleMaximize();
}
QWidget::mouseDoubleClickEvent(event);
}
void toggleMaximize() {
isMaximized() ? showNormal() : showMaximized();
}
完整示例代码
整合以上代码,完整的无边框窗口类如下:
#include <QWidget>
#include <QMouseEvent>
#include <QCursor>
class FramelessWindow : public QWidget {
Q_OBJECT
public:
FramelessWindow(QWidget *parent = nullptr) : QWidget(parent) {
setWindowFlags(Qt::FramelessWindowHint);
setMouseTracking(true);
}
protected:
void mousePressEvent(QMouseEvent *event) override {
if (event->button() == Qt::LeftButton) {
if (isInDragRegion(event->pos())) {
m_dragging = true;
m_dragStartPos = event->globalPos();
m_windowPos = pos();
} else {
checkResizeRegion(event->pos());
if (m_resizeDir != None) {
m_resizing = true;
m_resizeStartPos = event->globalPos();
m_initialGeometry = geometry();
}
}
}
QWidget::mousePressEvent(event);
}
void mouseMoveEvent(QMouseEvent *event) override {
if (!m_resizing && !m_dragging)
updateCursorShape(event->pos());
if (m_dragging) {
QPoint delta = event->globalPos() - m_dragStartPos;
move(m_windowPos + delta);
} else if (m_resizing) {
handleResize(event->globalPos());
}
QWidget::mouseMoveEvent(event);
}
void mouseReleaseEvent(QMouseEvent *event) override {
if (event->button() == Qt::LeftButton) {
m_dragging = false;
m_resizing = false;
m_resizeDir = None;
unsetCursor();
}
QWidget::mouseReleaseEvent(event);
}
void mouseDoubleClickEvent(QMouseEvent *event) override {
if (event->button() == Qt::LeftButton && isInDragRegion(event->pos())) {
toggleMaximize();
}
QWidget::mouseDoubleClickEvent(event);
}
private:
bool m_dragging = false;
QPoint m_dragStartPos;
QPoint m_windowPos;
bool m_resizing = false;
QPoint m_resizeStartPos;
QRect m_initialGeometry;
enum ResizeDirection {
None, Left, Right, Top, Bottom,
TopLeft, TopRight, BottomLeft, BottomRight
};
ResizeDirection m_resizeDir = None;
bool isInDragRegion(const QPoint &pos) const {
return pos.y() < 30; // 标题栏区域高度
}
void checkResizeRegion(const QPoint &pos) {
const int border = 5;
bool left = pos.x() <= border;
bool right = pos.x() >= width() - border;
bool top = pos.y() <= border;
bool bottom = pos.y() >= height() - border;
if (left && top) m_resizeDir = TopLeft;
else if (right && top) m_resizeDir = TopRight;
else if (left && bottom) m_resizeDir = BottomLeft;
else if (right && bottom) m_resizeDir = BottomRight;
else if (left) m_resizeDir = Left;
else if (right) m_resizeDir = Right;
else if (top) m_resizeDir = Top;
else if (bottom) m_resizeDir = Bottom;
else m_resizeDir = None;
}
void updateCursorShape(const QPoint &pos) {
checkResizeRegion(pos);
switch (m_resizeDir) {
case Left: case Right:
setCursor(Qt::SizeHorCursor); break;
case Top: case Bottom:
setCursor(Qt::SizeVerCursor); break;
case TopLeft: case BottomRight:
setCursor(Qt::SizeFDiagCursor); break;
case TopRight: case BottomLeft:
setCursor(Qt::SizeBDiagCursor); break;
default:
unsetCursor();
}
}
void handleResize(const QPoint &globalPos) {
QRect newGeo = m_initialGeometry;
int deltaX = globalPos.x() - m_resizeStartPos.x();
int deltaY = globalPos.y() - m_resizeStartPos.y();
switch (m_resizeDir) {
case Left:
newGeo.setLeft(newGeo.left() + deltaX);
break;
case Right:
newGeo.setRight(newGeo.right() + deltaX);
break;
case Top:
newGeo.setTop(newGeo.top() + deltaY);
break;
case Bottom:
newGeo.setBottom(newGeo.bottom() + deltaY);
break;
case TopLeft:
newGeo.setTopLeft(newGeo.topLeft() + QPoint(deltaX, deltaY));
break;
case TopRight:
newGeo.setTopRight(newGeo.topRight() + QPoint(deltaX, deltaY));
break;
case BottomLeft:
newGeo.setBottomLeft(newGeo.bottomLeft() + QPoint(deltaX, deltaY));
break;
case BottomRight:
newGeo.setBottomRight(newGeo.bottomRight() + QPoint(deltaX, deltaY));
break;
}
if (newGeo.width() < minimumWidth() || newGeo.height() < minimumHeight())
return;
setGeometry(newGeo);
}
void toggleMaximize() {
isMaximized() ? showNormal() : showMaximized();
}
};
注意事项
- 边缘检测:调整边缘检测的阈值(如
border = 5
)以适应不同需求。 - 最小尺寸:确保窗口不会调整到小于
minimumWidth
和minimumHeight
。 - 最大化处理:在最大化状态下可能需要禁用调整大小或特殊处理拖动行为。
- 性能优化:避免频繁的几何计算影响性能。
通过以上步骤,即可在Qt的无边框窗口中实现完整的鼠标交互功能。