還有一些擴展接口沒有添加,有興趣的同學可以自己添加
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();
}