还有一些扩展接口没有添加,有兴趣的同学可以自己添加
1.WinTitleBarWidgetBase类声明
#ifndef WINTITLEBARWIDGETBASE_H
#define WINTITLEBARWIDGETBASE_H
#include <QWidget>
class QPushButton;
#define GRADIENTICON "QWidget{background-image: url(:/gradienticon.png);}"
#define MAXLOGOSTYLE "QPushButton {background: transparent;border-image:url(:/windowmax.png)} QPushButton:hover {background: transparent;border-image:url(:/windowmaxH.png)}"
#define REDUCELOGOSTYLE "QPushButton {background: transparent;border-image:url(:/windowreduce.png)} QPushButton:hover {background: transparent;border-image:url(:/windowreduceH.png)}"
/**
* @class WinTitleBarWidgetBase
* @brief 一个可扩展工具栏,默认最大、最小、关闭三个按钮(靠右侧)
*/
class WinTitleBarWidgetBase : public QWidget
{
Q_OBJECT
public:
explicit WinTitleBarWidgetBase(QWidget *pParent = NULL);
virtual ~WinTitleBarWidgetBase();
/**
* @brief 正常大小和最大化切换时,改变图标样式
* @param[in] style 最新样式
*/
void ChangeMaxIconStyle(const QString& style);
/**
* @brief 标题栏的可扩展区域(占据三个默认按钮剩余区域)
* @return 可扩展区域
*/
QWidget* getContentWidget()const;
protected:
/**
* @brief 重写过滤器事件
* @param[in] pObject 监控的对象
* @param[in] pEvent 发生的事件
*/
virtual bool eventFilter(QObject *pObject, QEvent *pEvent);
/**
* @brief 重写鼠标双击事件
* @param[in] pEvent 鼠标双击事件
*/
virtual void mouseDoubleClickEvent(QMouseEvent *pEvent);
virtual void mousePressEvent(QMouseEvent *e);
virtual void mouseMoveEvent(QMouseEvent *e);
virtual void mouseReleaseEvent(QMouseEvent *e);
signals:
/**
* @brief 当点击关闭按钮时发送
*/
void sigClose();
/**
* @brief 当点击最小化按钮时发送
*/
void sigShowMin();
/**
* @brief 当点击最大化按钮时发送
*/
void sigShowMax();
private:
bool m_bLeftButtonPress;
QPoint m_ptPress;
QPoint m_ptMove;
QPushButton *m_pCloseBtn; ///< 关闭按钮
QPushButton *m_pMinBtn; ///< 最小化按钮
QPushButton *m_pMaxBtn; ///< 最大化按钮
QWidget *m_pContentWidget;///< 可扩展区域
};
#endif
2.WinTitleBarWidgetBase类实现
#include <QEvent>
#include <QMouseEvent>
#include <QPushButton>
#include <QHBoxLayout>
#include "wintitlebarwidgetbase.h"
#define MINLOGOSTYLE "QPushButton {background: transparent;border-image:url(:/windowmin.png)} QPushButton:hover {background: transparent;border-image:url(:/windowminH.png)}"
#define CLOSELOGOSTYLE "QPushButton {background: transparent;border-image:url(:/windowclose.png)} QPushButton:hover {background: transparent;border-image:url(:/windowcloseH.png)}"
WinTitleBarWidgetBase::WinTitleBarWidgetBase(QWidget *pParent):
QWidget(pParent)
{
//大小策略,水平方向优先,垂直方向固定高度
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
setFixedHeight(25);
//通过点击获取焦点
setFocusPolicy(Qt::ClickFocus);
//背景色自动填充
setAutoFillBackground(true);
//鼠标形状箭头
setCursor(QCursor(Qt::ArrowCursor));
//跟踪鼠标移动事件
setMouseTracking(true);
//关闭按钮
m_pCloseBtn = new QPushButton(this);
m_pCloseBtn->setFixedSize(25,25);
m_pCloseBtn->setCursor(Qt::PointingHandCursor);
m_pCloseBtn->setStyleSheet(CLOSELOGOSTYLE);
m_pCloseBtn->installEventFilter(this);
//最小化按钮
m_pMinBtn = new QPushButton(this);
m_pMinBtn->setFixedSize(25,25);
m_pMinBtn->setCursor(Qt::PointingHandCursor);
m_pMinBtn->setStyleSheet(MINLOGOSTYLE);
m_pMinBtn->installEventFilter(this);
//最大化按钮
m_pMaxBtn = new QPushButton(this);
m_pMaxBtn->setFixedSize(25,25);
m_pMaxBtn->setCursor(Qt::PointingHandCursor);
m_pMaxBtn->setStyleSheet(MAXLOGOSTYLE);
m_pMaxBtn->installEventFilter(this);
//扩展区
m_pContentWidget = new QWidget(this);
m_pContentWidget->setAutoFillBackground(true);
m_pContentWidget->setStyleSheet("QWidget{background: transparent;}");
QHBoxLayout *pMainLayout = new QHBoxLayout(this);
pMainLayout->addWidget(m_pContentWidget);
pMainLayout->addWidget(m_pMinBtn);
pMainLayout->addWidget(m_pMaxBtn);
pMainLayout->addWidget(m_pCloseBtn);
pMainLayout->setSpacing(0);
pMainLayout->setContentsMargins(0,0,0,2);
m_ptPress = QPoint(0,0);
m_ptMove = QPoint(0,0);
m_bLeftButtonPress = false;
}
WinTitleBarWidgetBase::~WinTitleBarWidgetBase()
{
}
void WinTitleBarWidgetBase::ChangeMaxIconStyle(const QString &style)
{
m_pMaxBtn->setStyleSheet(style);
}
QWidget *WinTitleBarWidgetBase::getContentWidget() const
{
return m_pContentWidget;
}
bool WinTitleBarWidgetBase::eventFilter(QObject *pObject, QEvent *pEvent)
{
if(pEvent->type() == QEvent::MouseButtonPress)
{
if(pObject == m_pCloseBtn)
{
emit sigClose();
}
if(pObject == m_pMinBtn)
{
emit sigShowMin();
}
if(pObject == m_pMaxBtn)
{
emit sigShowMax();
}
}
return QWidget::eventFilter(pObject,pEvent);
}
void WinTitleBarWidgetBase::mouseDoubleClickEvent(QMouseEvent *pEvent)
{
emit sigShowMax();
QWidget::mouseDoubleClickEvent(pEvent);
}
void WinTitleBarWidgetBase::mousePressEvent(QMouseEvent *e)
{
const int expand = 5; //鼠标指针误差范围
if(e->button() == Qt::LeftButton)
{
if(e->y() <= expand ||
e->x() <= expand ||
rect().width()-(e->x()) <= expand)
{
e->ignore();
return;
}
m_ptPress = e->globalPos();
m_bLeftButtonPress = true;
}
e->ignore();
}
void WinTitleBarWidgetBase::mouseMoveEvent(QMouseEvent *e)
{
QWidget *pMainWidget = qobject_cast<QWidget *>(parent());
if(m_bLeftButtonPress && pMainWidget && !(pMainWidget->isMaximized()))
{
m_ptMove = e->globalPos();
pMainWidget->move(pMainWidget->pos()+m_ptMove-m_ptPress);
//重新设置m_ptPress;
m_ptPress = m_ptMove;
}
e->ignore();
}
void WinTitleBarWidgetBase::mouseReleaseEvent(QMouseEvent *e)
{
if(e->button() == Qt::LeftButton)
{
m_bLeftButtonPress = false;
}
e->ignore();
}
3.WinMainWidgetBase类声明
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class WinTitleBarWidgetBase;
/**
* @class WinMainWidgetBase
* @brief 无边框带默认标题栏的widget控件
*/
class WinMainWidgetBase : public QWidget
{
Q_OBJECT
public:
/**
* @enum Direction
* @brief 鼠标改变控件大小时拖动方向
*/
enum Direction
{
UP=0, ///< 向上
DOWN=1, ///< 向下
LEFT, ///< 向左
RIGHT, ///< 向右
LEFTTOP, ///< 左上
LEFTBOTTOM, ///< 左下
RIGHTBOTTOM,///< 右下
RIGHTTOP, ///< 右上
NONE ///< 无方向
};
explicit WinMainWidgetBase(QWidget *pParent = NULL);
virtual ~WinMainWidgetBase();
/**
* @brief 设置四周边框宽度
* @param[in] left 左边框宽度
* @param[in] top 上边框宽度
* @param[in] right 右边框宽度
* @param[in] bottom 下边框宽度
*/
void SetFrameWidth(int left, int top, int right, int bottom);
/**
* @brief 可扩展区域
*/
QWidget* GetContentWidget() const;
protected:
/**
* @brief 重写基类鼠标释放事件
* @param[in] event 鼠标释放事件
*/
virtual void mouseReleaseEvent(QMouseEvent *event);
/**
* @brief 重写基类鼠标移动事件
* @param[in] event 鼠标移动事件
*/
virtual void mouseMoveEvent(QMouseEvent *event);
/**
* @brief 重写基类鼠标按压事件
* @param[in] event 鼠标按压事件
*/
virtual void mousePressEvent(QMouseEvent *event);
/**
* @brief 确定拖拽方向和鼠标形状
* @param[in] cursorGlobalPoint 鼠标全局位置
*/
void region(const QPoint &cursorGlobalPoint);
private slots:
/**
* @brief 窗口正常和最大化显示
*/
void showMaxOrNormal();
private:
int m_leftBorderWidth; ///< 左边框宽度
int m_TopBorderWidth; ///< 上边框宽度
int m_rightBorderWidth; ///< 右边框宽度
int m_bottomBorderWidth; ///< 下边框宽度
bool m_isLeftPressDown; ///< 判断左键是否按下
Direction m_direction; ///< 窗口大小改变时,记录改变方向
QRect m_rectRestoreWindow;///< 储存窗口大小
QWidget *m_pContentWidget;///< 可扩展区域
WinTitleBarWidgetBase *m_pTitleBar;///< 默认标题栏
};
#endif // WIDGET_H
4.WinMainWidgetBase类实现
#include "winmainwidgetbase.h"
#include "wintitlebarwidgetbase.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QPainter>
#include <QMouseEvent>
#include <QVBoxLayout>
WinMainWidgetBase::WinMainWidgetBase(QWidget *pParent):
QWidget(pParent)
{
m_leftBorderWidth = 2;
m_TopBorderWidth = 2;
m_rightBorderWidth = 2;
m_bottomBorderWidth = 2;
m_isLeftPressDown = false;
m_direction = NONE;
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
setMouseTracking(true);
m_pTitleBar = new WinTitleBarWidgetBase(this);
m_pTitleBar->setStyleSheet(GRADIENTICON);;
connect(m_pTitleBar,&WinTitleBarWidgetBase::sigClose,this,&QWidget::close);
connect(m_pTitleBar,&WinTitleBarWidgetBase::sigShowMin,this,&QWidget::showMinimized);
connect(m_pTitleBar,&WinTitleBarWidgetBase::sigShowMax,this,&WinMainWidgetBase::showMaxOrNormal);
m_pContentWidget = new QWidget(this);
m_pContentWidget->setMouseTracking(true);
m_pContentWidget->setAutoFillBackground(true);
m_pContentWidget->setCursor(QCursor(Qt::ArrowCursor));
m_pContentWidget->setPalette(QPalette(QColor("grey")));
QVBoxLayout *pMainLayout = new QVBoxLayout(this);
pMainLayout->setSpacing(2);
pMainLayout->addWidget(m_pTitleBar);
pMainLayout->addWidget(m_pContentWidget);
//设置边框宽度
SetFrameWidth(m_leftBorderWidth,m_TopBorderWidth,m_rightBorderWidth,m_bottomBorderWidth);
setMinimumSize(600,500);
m_rectRestoreWindow = geometry();
}
WinMainWidgetBase::~WinMainWidgetBase()
{
}
void WinMainWidgetBase::SetFrameWidth(int left, int top, int right, int bottom)
{
if(0 == left && 0 == top && 0 == right && 0 == bottom)
{
layout()->setContentsMargins(left,top,right,bottom);
}
else
{
m_leftBorderWidth = left;
m_TopBorderWidth = top;
m_rightBorderWidth = right;
m_bottomBorderWidth = bottom;
layout()->setContentsMargins(left,top,right,bottom);
}
}
QWidget *WinMainWidgetBase::GetContentWidget() const
{
return m_pContentWidget;
}
void WinMainWidgetBase::region(const QPoint &cursorGlobalPoint)
{
const int PADDING = 2;
QRect rect = this->rect();
QPoint tl = mapToGlobal(rect.topLeft());
QPoint rb = mapToGlobal(rect.bottomRight());
int x = cursorGlobalPoint.x();
int y = cursorGlobalPoint.y();
if(tl.x() + PADDING >= x && tl.x() <= x && tl.y() + PADDING >= y && tl.y() <= y)
{
// 左上角
m_direction = LEFTTOP;
this->setCursor(QCursor(Qt::SizeFDiagCursor));
}
else if(x >= rb.x() - PADDING && x <= rb.x() && y >= rb.y() - PADDING && y <= rb.y())
{
// 右下角
m_direction = RIGHTBOTTOM;
this->setCursor(QCursor(Qt::SizeFDiagCursor));
}
else if(x <= tl.x() + PADDING && x >= tl.x() && y >= rb.y() - PADDING && y <= rb.y())
{
//左下角
m_direction = LEFTBOTTOM;
this->setCursor(QCursor(Qt::SizeBDiagCursor));
}
else if(x <= rb.x() && x >= rb.x() - PADDING && y >= tl.y() && y <= tl.y() + PADDING)
{
// 右上角
m_direction = RIGHTTOP;
this->setCursor(QCursor(Qt::SizeBDiagCursor));
}
else if(x <= tl.x() + PADDING && x >= tl.x())
{
// 左边
m_direction = LEFT;
this->setCursor(QCursor(Qt::SizeHorCursor));
}
else if( x <= rb.x() && x >= rb.x() - PADDING)
{
// 右边
m_direction = RIGHT;
this->setCursor(QCursor(Qt::SizeHorCursor));
}
else if(y >= tl.y() && y <= tl.y() + PADDING)
{
// 上边
m_direction = UP;
this->setCursor(QCursor(Qt::SizeVerCursor));
}
else if(y <= rb.y() && y >= rb.y() - PADDING)
{
// 下边
m_direction = DOWN;
this->setCursor(QCursor(Qt::SizeVerCursor));
}
else
{
// 默认
m_direction = NONE;
this->setCursor(QCursor(Qt::ArrowCursor));
}
}
void WinMainWidgetBase::showMaxOrNormal()
{
if(!isMaximized()) //当前窗口化时 实现最大化
{
m_rectRestoreWindow = geometry();
showMaximized();
m_pTitleBar->ChangeMaxIconStyle(REDUCELOGOSTYLE);
SetFrameWidth(0,0,0,0);
}
else //否则窗口化
{
showNormal();
m_rectRestoreWindow = geometry();
m_pTitleBar->ChangeMaxIconStyle(MAXLOGOSTYLE);
SetFrameWidth(m_leftBorderWidth,m_TopBorderWidth,m_rightBorderWidth,m_bottomBorderWidth);
}
}
void WinMainWidgetBase::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
m_isLeftPressDown = false;
if(m_direction != NONE)
{
m_direction = NONE;
this->releaseMouse();
this->setCursor(QCursor(Qt::ArrowCursor));
}
}
}
void WinMainWidgetBase::mousePressEvent(QMouseEvent *event)
{
switch(event->button())
{
case Qt::LeftButton:
m_isLeftPressDown = true;
if(m_direction != NONE)
{
this->mouseGrabber();
}
break;
default:
QWidget::mousePressEvent(event);
}
}
void WinMainWidgetBase::mouseMoveEvent(QMouseEvent *event)
{
QPoint gloPoint = event->globalPos();
QRect rect = this->rect();
QPoint tl = mapToGlobal(rect.topLeft());
QPoint rb = mapToGlobal(rect.bottomRight());
if(!m_isLeftPressDown && !isMaximized())
{
this->region(gloPoint);
}
else
{
if(m_direction != NONE)
{
QRect rMove(tl, rb);
switch(m_direction)
{
case LEFT:
{
if(rb.x() - gloPoint.x() <= this->minimumWidth())
{
rMove.setX(tl.x());
}
else
{
rMove.setX(gloPoint.x());
}
}
break;
case RIGHT:
{
rMove.setWidth(gloPoint.x() - tl.x());
}
break;
case UP:
{
if(rb.y() - gloPoint.y() <= this->minimumHeight())
{
rMove.setY(tl.y());
}
else
{
rMove.setTop(gloPoint.y());
}
}
break;
case DOWN:
{
rMove.setHeight(gloPoint.y() - tl.y());
}
break;
case LEFTTOP:
{
if(rb.x() - gloPoint.x() <= this->minimumWidth())
{
rMove.setX(tl.x());
}
else
{
rMove.setX(gloPoint.x());
}
if(rb.y() - gloPoint.y() <= this->minimumHeight())
{
rMove.setY(tl.y());
}
else
{
rMove.setY(gloPoint.y());
}
}
break;
case RIGHTTOP:
{
if(rb.y() - gloPoint.y() <= this->minimumHeight() ||
gloPoint.x() - tl.x() <= this->minimumWidth())
{
rMove.setY(tl.y());
}
else
{
rMove.setY(gloPoint.y());
}
rMove.setWidth(gloPoint.x() - tl.x());
}
break;
case LEFTBOTTOM:
{
if(rb.x() - gloPoint.x() <= this->minimumWidth() ||
gloPoint.y() - tl.y() <= this->minimumHeight())
{
rMove.setX(tl.x());
}
else
{
rMove.setX(gloPoint.x());
}
rMove.setHeight(gloPoint.y() - tl.y());
}
break;
case RIGHTBOTTOM:
{
rMove.setWidth(gloPoint.x() - tl.x());
rMove.setHeight(gloPoint.y() - tl.y());
}
break;
default:
break;
}
setGeometry(rMove);
}
if(m_direction == NONE && isMaximized()&& m_isLeftPressDown && event->y() <= 25)
{
showNormal();
int desktopWidth = qApp->desktop()->availableGeometry().width(); //全屏桌面宽度
float scale = (float)event->globalX()/desktopWidth;
int newX = event->globalX()-m_rectRestoreWindow.width()*scale;
setGeometry(newX, 0, m_rectRestoreWindow.width(), m_rectRestoreWindow.height());
m_pTitleBar->ChangeMaxIconStyle(MAXLOGOSTYLE);
SetFrameWidth(m_leftBorderWidth,m_TopBorderWidth,m_rightBorderWidth,m_bottomBorderWidth);
}
}
QWidget::mouseMoveEvent(event);
}
5.资源文件图片
6.pro文件
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = framelesswidget
TEMPLATE = app
DESTDIR = ./bin
SOURCES += main.cpp \
winmainwidgetbase.cpp \
wintitlebarwidgetbase.cpp
HEADERS += \
winmainwidgetbase.h \
wintitlebarwidgetbase.h
RESOURCES += \
resource.qrc
7.main.cpp
#include "winmainwidgetbase.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
WinMainWidgetBase w;
w.show();
return a.exec();
}