Qt 无边框窗口自定义标题栏

实现了自定义标题栏的最小化,最大化(自适应任务栏),关闭,拉伸,拖曳。less

由于不想引入其余资源,因此按钮,背景都是系统提供的。
this

效果图:3d



直接上代码。rest

用法:code

#include "widget.h"
#include <QApplication>
#include "CustomFrame.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget *w = new Widget();
    CustomFrame frame(w, "CustomFrame");
    frame.show();

    return a.exec();
}

#pragma once

#include <QFrame>

class QToolButton;
class CustomFrame : public QFrame
{
	Q_OBJECT
public:
	explicit CustomFrame(QWidget *contentWidget, const QString &title);

public slots:
	void slotShowSmall();
	void slotShowMaxRestore();

protected:
	void mousePressEvent(QMouseEvent *);
	void mouseMoveEvent(QMouseEvent *);
    void mouseReleaseEvent(QMouseEvent *);
	bool nativeEvent(const QByteArray & eventType, void * message, long * result);
	void paintEvent(QPaintEvent *);

private:
	bool isMax_;
	bool isPress_;
	QPoint startPos_;
	QPoint clickPos_;
	QWidget *contentWidget_;
    QPixmap maxPixmap_;
    QPixmap restorePixmap_;
    QToolButton *maxButton_;
};


#include "CustomFrame.h"
#include <QtWidgets>

static const int TITLE_HEIGHT = 30;
static const int FRAME_BORDER = 2;
CustomFrame::CustomFrame(QWidget *contentWidget, const QString &title)
	: contentWidget_(contentWidget)
{
	this->setWindowFlags(Qt::FramelessWindowHint);
	this->setAttribute(Qt::WA_TranslucentBackground);
	this->setMouseTracking(true);

	isMax_ = false;
	isPress_ = false;

	QLabel *logoLabel = new QLabel();
    QPixmap logoPixmap = this->style()->standardPixmap(QStyle::SP_TitleBarMenuButton);
	logoLabel->setPixmap(logoPixmap);
	logoLabel->setFixedSize(16, 16);
	logoLabel->setScaledContents(true);

	QLabel *titleLabel = new QLabel();
	titleLabel->setText(title);
	QFont titleFont = titleLabel->font();
	titleFont.setBold(true);
	titleLabel->setFont(titleFont);
	titleLabel->setObjectName("whiteLabel");

    QToolButton *minButton = new QToolButton();
    QPixmap minPixmap = this->style()->standardPixmap(QStyle::SP_TitleBarMinButton);
    minButton->setIcon(minPixmap);
	connect(minButton, SIGNAL(clicked()), this, SLOT(slotShowSmall()));

    maxButton_ = new QToolButton();
    maxPixmap_ = this->style()->standardPixmap(QStyle::SP_TitleBarMaxButton);
    restorePixmap_ = this->style()->standardPixmap(QStyle::SP_TitleBarNormalButton);
    maxButton_->setIcon(maxPixmap_);
    connect(maxButton_, SIGNAL(clicked()), this, SLOT(slotShowMaxRestore()));

    QToolButton *closeButton = new QToolButton();
    QPixmap closePixmap = this->style()->standardPixmap(QStyle::SP_TitleBarCloseButton);
    closeButton->setIcon(closePixmap);
	connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));

	QHBoxLayout *titleLayout = new QHBoxLayout();
	titleLayout->addWidget(logoLabel);
	titleLayout->addWidget(titleLabel);
	titleLabel->setContentsMargins(5, 0, 0, 0);
	titleLayout->addStretch();
	titleLayout->addWidget(minButton, 0, Qt::AlignTop);
    titleLayout->addWidget(maxButton_, 0, Qt::AlignTop);
	titleLayout->addWidget(closeButton, 0, Qt::AlignTop);
	titleLayout->setSpacing(0);
	titleLayout->setContentsMargins(5, 0, 0, 0);
	
	QWidget *titleWidget = new QWidget();
	titleWidget->setLayout(titleLayout);
	titleWidget->installEventFilter(0);

	QVBoxLayout *mainLayout = new QVBoxLayout();
	mainLayout->addWidget(titleWidget);
	mainLayout->addWidget(contentWidget_);
	mainLayout->setSpacing(0);
	mainLayout->setMargin(5);
	this->setLayout(mainLayout);
}

void CustomFrame::slotShowSmall()
{
	this->showMinimized();
}

void CustomFrame::slotShowMaxRestore()
{
	if (isMax_) {
		this->showNormal();
        maxButton_->setIcon(maxPixmap_);
	} else {
		this->showMaximized();
        maxButton_->setIcon(restorePixmap_);
	}
	isMax_ = !isMax_;
}

void CustomFrame::mousePressEvent(QMouseEvent *e)
{
	startPos_ = e->globalPos();
	clickPos_ = e->pos();

	if (e->button() == Qt::LeftButton) {
		if (e->type() == QEvent::MouseButtonPress) {
			isPress_ = true;
		} else if (e->type() == QEvent::MouseButtonDblClick && e->pos().y() <= TITLE_HEIGHT) {
			this->slotShowMaxRestore();
		}
	}
}

void CustomFrame::mouseMoveEvent(QMouseEvent *e)
{
	if (isMax_ || !isPress_) {
		return;
	}
	this->move(e->globalPos() - clickPos_);
}

void CustomFrame::mouseReleaseEvent(QMouseEvent *)
{
	isPress_ = false;
}

bool CustomFrame::nativeEvent(const QByteArray & eventType, void * message, long * result)
{
	Q_UNUSED(eventType);
    const int HIT_BORDER = 5;
    const MSG *msg=static_cast<MSG*>(message);
    if(msg->message == WM_NCHITTEST) {
        int xPos = ((int)(short)LOWORD(msg->lParam)) - this->frameGeometry().x();
        int yPos = ((int)(short)HIWORD(msg->lParam)) - this->frameGeometry().y();
        if(this->childAt(xPos,yPos) == 0) {
            *result = HTCAPTION;
        } else {
            return false;
        }
        if(xPos > 0 && xPos < HIT_BORDER) {
            *result = HTLEFT;
        }
        if(xPos > (this->width() - HIT_BORDER) && xPos < (this->width() - 0)) {
            *result = HTRIGHT;
        }
        if(yPos > 0 && yPos < HIT_BORDER) {
            *result = HTTOP;
        }
        if(yPos > (this->height() - HIT_BORDER) && yPos < (this->height() - 0)) {
            *result = HTBOTTOM;
        }
        if(xPos > 0 && xPos < HIT_BORDER && yPos > 0 && yPos < HIT_BORDER) {
            *result = HTTOPLEFT;
        }
        if(xPos > (this->width() - HIT_BORDER) && xPos < (this->width() - 0) && yPos > 0 && yPos < HIT_BORDER) {
            *result = HTTOPRIGHT;
        }
        if(xPos > 0 && xPos < HIT_BORDER && yPos > (this->height() - HIT_BORDER) && yPos < (this->height() - 0)) {
            *result = HTBOTTOMLEFT;
        }
        if(xPos > (this->width() - HIT_BORDER) && xPos < (this->width() - 0) && yPos > (this->height() - HIT_BORDER) && yPos < (this->height() - 0)) {
            *result = HTBOTTOMRIGHT;
        }
        return true;
    }
    return false;
}

void CustomFrame::paintEvent(QPaintEvent *e)
{
	int border = FRAME_BORDER;
	if (this->isMaximized()) {
		border = 0;
	}

	QPainter painter(this);
	QPainterPath painterPath;
	painterPath.setFillRule(Qt::WindingFill);
	painterPath.addRect(border, border, this->width()-2*border, this->height()-2*border);
	painter.setRenderHint(QPainter::Antialiasing, true);
	painter.fillPath(painterPath, QBrush(Qt::white));
	QColor color(200, 200, 200);
	for (int i=0; i<border; i++) {
		color.setAlpha((i+1)*30);
		painter.setPen(color);
		painter.drawRect(border-i, border-i, this->width()-(border-i)*2, this->height()-(border-i)*2);
	}

	painter.setPen(Qt::NoPen);
	painter.setBrush(Qt::white);
    // 这里能够在资源中指定一张标题背景图片
    //painter.drawPixmap(QRect(border, border, this->width()-2*border, this->height()-2*border), QPixmap(DEFAULT_SKIN));
	painter.drawRect(QRect(border, TITLE_HEIGHT, this->width()-2*border, this->height()-TITLE_HEIGHT-border));

	QFrame::paintEvent(e);
}