一個無邊框帶默認標題欄的widget

還有一些擴展接口沒有添加,有興趣的同學可以自己添加

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();
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章