├── .gitignore ├── QtFramelessWindow.pro ├── README.md ├── libframelesswindow ├── borderimage.cpp ├── borderimage.h ├── cursorposcalculator.cpp ├── cursorposcalculator.h ├── framelesshelper.cpp ├── framelesshelper.h ├── framelesshelperprivate.h ├── framelesswindow.cpp ├── framelesswindow.h ├── framelesswindow_global.h ├── images.qrc ├── images │ ├── background │ │ ├── client-shadow.png │ │ └── client-shadow.psd │ ├── msgbox │ │ ├── msg-error-48x48.png │ │ ├── msg-info-48x48.png │ │ ├── msg-question-48x48.png │ │ ├── msg-success-48x48.png │ │ └── msg-warning-48x48.png │ └── titlebar │ │ ├── close.png │ │ ├── max.png │ │ ├── menu.png │ │ ├── min.png │ │ └── restore.png ├── libframelesswindow.pri ├── statebutton.cpp ├── statebutton.h ├── style.qrc ├── style │ ├── style_black.qss │ └── style_white.qss ├── titlebar.cpp ├── titlebar.h ├── widgetdata.cpp ├── widgetdata.h └── widgetshadow.h ├── libtest ├── libTest.pro ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── style.qrc ├── style.qss └── stylesheethelper.h └── preview_1.png /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | *.txt 13 | *.Debug 14 | *.Release 15 | 16 | # Qt-es 17 | 18 | /.qmake.cache 19 | /.qmake.stash 20 | *.pro.user 21 | *.pro.user.* 22 | *.qbs.user 23 | *.qbs.user.* 24 | *.moc 25 | moc_*.cpp 26 | moc_*.h 27 | qrc_*.cpp 28 | ui_*.h 29 | Makefile* 30 | *build-* 31 | 32 | # QtCreator 33 | 34 | *.autosave 35 | 36 | # QtCtreator Qml 37 | *.qmlproject.user 38 | *.qmlproject.user.* 39 | 40 | # QtCtreator CMake 41 | CMakeLists.txt.user* 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /QtFramelessWindow.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | 3 | # 当使用subdirs模板时,此选项指定应按给出目录的顺序处理列出的目录 4 | CONFIG += ordered 5 | 6 | SUBDIRS += libtest 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QtFramelessWindow 2 | Custom frameless window,dialog,messagebox Library / 自定义无边框窗体,对话框和提示框,并封装成库 3 | 4 | ![image](https://raw.githubusercontent.com/dnybz/QtFramelessWindow/master/preview_1.png) 5 | 6 | 使用方法: 7 | 8 | ```c++ 9 | #include "framelesswindow.h" 10 | #include 11 | #include 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | QApplication a(argc, argv); 16 | 17 | // FramelessWindow 18 | FramelessWindow *pWindow = new FramelessWindow(); 19 | QWidget *pCentralWidget = new QWidget(pWindow); 20 | pWindow->setCentralWidget(pCentralWidget); 21 | pWindow->show(); 22 | 23 | // FramelessDialog 24 | FramelessDialog *pDialog = new FramelessDialog(pWindow); 25 | QWidget *pDlgCentralWidget = new QWidget(pDialog); 26 | QPushButton *pButton = new QPushButton(pDlgCentralWidget); 27 | pDialog->setCentralWidget(pDlgCentralWidget); 28 | pDialog->setModal(true); 29 | pDialog->show(); 30 | 31 | // FramelessMessageBox 32 | FramelessMessageBox::showInformation(pWindow, QObject::tr("提示!"), QObject::tr("自定义提示框!")); 33 | return a.exec(); 34 | } 35 | ``` 36 | -------------------------------------------------------------------------------- /libframelesswindow/borderimage.cpp: -------------------------------------------------------------------------------- 1 | #include "borderimage.h" 2 | #include 3 | 4 | void BorderImage::setPixmap(const QString& url) 5 | { 6 | m_pixmapUrl = url; 7 | m_pixmap.load(url); 8 | } 9 | 10 | void BorderImage::setPixmap(const QPixmap& pixmap) 11 | { 12 | m_pixmap = pixmap; 13 | } 14 | 15 | void BorderImage::setBorder(const QString& border) 16 | { 17 | QTextStream qs((QString*)&border); 18 | int left, top, right, bottom; 19 | qs >> left >> top >> right >> bottom; 20 | setBorder(left, top, right, bottom); 21 | } 22 | 23 | void BorderImage::setBorder(const QMargins& border) 24 | { 25 | m_border = border; 26 | } 27 | 28 | void BorderImage::setBorder(int left, int top, int right, int bottom) 29 | { 30 | QMargins m(left, top, right, bottom); 31 | m_border = m; 32 | } 33 | 34 | void BorderImage::setMargin(const QString& border) 35 | { 36 | QTextStream qs((QString*)&border); 37 | int left, top, right, bottom; 38 | qs >> left >> top >> right >> bottom; 39 | setMargin(left, top, right, bottom); 40 | } 41 | 42 | void BorderImage::setMargin(const QMargins& border) 43 | { 44 | m_margin = border; 45 | } 46 | 47 | void BorderImage::setMargin(int left, int top, int right, int bottom) 48 | { 49 | QMargins m(left, top, right, bottom); 50 | m_margin = m; 51 | } 52 | 53 | void BorderImage::load(const QString& pixmap_url, const QString& border, const QString& margin) 54 | { 55 | setPixmap(pixmap_url); 56 | setBorder(border); 57 | setMargin(margin); 58 | } 59 | -------------------------------------------------------------------------------- /libframelesswindow/borderimage.h: -------------------------------------------------------------------------------- 1 | #ifndef BORDERIMAGE_H 2 | #define BORDERIMAGE_H 3 | 4 | #include 5 | #include 6 | 7 | class BorderImage 8 | { 9 | public: 10 | const QMargins& margin() const { return m_margin; } 11 | const QMargins& border() const { return m_border; } 12 | const QPixmap& pixmap() const { return m_pixmap; } 13 | const QString& pixmap_url() const { return m_pixmapUrl; } 14 | 15 | public: 16 | void setPixmap(const QString& url); 17 | void setPixmap(const QPixmap& pixmap); 18 | 19 | //order: left top right bottom 20 | void setBorder(const QString& border); 21 | void setBorder(const QMargins& border); 22 | void setBorder(int left, int top, int right, int bottom); 23 | 24 | void setMargin(const QString& border); 25 | void setMargin(const QMargins& border); 26 | void setMargin(int left, int top, int right, int bottom); 27 | 28 | void load(const QString& pixmap_url, const QString& border, const QString& margin); 29 | 30 | private: 31 | QMargins m_margin; 32 | QMargins m_border; 33 | QPixmap m_pixmap; 34 | QString m_pixmapUrl; 35 | }; 36 | 37 | #endif // BORDERIMAGE_H 38 | -------------------------------------------------------------------------------- /libframelesswindow/cursorposcalculator.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * cursorposcalculator.cpp 5 | * 计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角 6 | * 7 | */ 8 | 9 | #include "cursorposcalculator.h" 10 | #include 11 | #include 12 | 13 | int CursorPosCalculator::m_nBorderWidth = 5; 14 | int CursorPosCalculator::m_nTitleHeight = 30; 15 | 16 | CursorPosCalculator::CursorPosCalculator() 17 | { 18 | reset(); 19 | } 20 | 21 | void CursorPosCalculator::reset() 22 | { 23 | m_bOnEdges = false; 24 | m_bOnLeftEdge = false; 25 | m_bOnRightEdge = false; 26 | m_bOnTopEdge = false; 27 | m_bOnBottomEdge = false; 28 | m_bOnTopLeftEdge = false; 29 | m_bOnBottomLeftEdge = false; 30 | m_bOnTopRightEdge = false; 31 | m_bOnBottomRightEdge = false; 32 | } 33 | 34 | void CursorPosCalculator::recalculate(const QPoint &gMousePos, const QRect &frameRect) 35 | { 36 | int globalMouseX = gMousePos.x(); 37 | int globalMouseY = gMousePos.y(); 38 | 39 | int frameX = frameRect.x(); 40 | int frameY = frameRect.y(); 41 | 42 | int frameWidth = frameRect.width(); 43 | int frameHeight = frameRect.height(); 44 | 45 | m_bOnLeftEdge = (globalMouseX >= frameX && 46 | globalMouseX <= frameX + m_nBorderWidth); 47 | m_bOnRightEdge = (globalMouseX >= frameX + frameWidth - m_nBorderWidth && 48 | globalMouseX <= frameX + frameWidth); 49 | m_bOnTopEdge = (globalMouseY >= frameY && 50 | globalMouseY <= frameY + m_nBorderWidth); 51 | 52 | m_bOnBottomEdge = (globalMouseY >= frameY + frameHeight - m_nBorderWidth && 53 | globalMouseY <= frameY + frameHeight); 54 | 55 | m_bOnTopLeftEdge = m_bOnTopEdge && m_bOnLeftEdge; 56 | m_bOnBottomLeftEdge = m_bOnBottomEdge && m_bOnLeftEdge; 57 | m_bOnTopRightEdge = m_bOnTopEdge && m_bOnRightEdge; 58 | m_bOnBottomRightEdge = m_bOnBottomEdge && m_bOnRightEdge; 59 | 60 | m_bOnEdges = m_bOnLeftEdge || m_bOnRightEdge || m_bOnTopEdge || m_bOnBottomEdge; 61 | } 62 | -------------------------------------------------------------------------------- /libframelesswindow/cursorposcalculator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * cursorposcalculator.h 5 | * 计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角 6 | */ 7 | 8 | #ifndef CURSORPOSCALCULATOR_H 9 | #define CURSORPOSCALCULATOR_H 10 | 11 | /** 12 | * @brief The CursorPosCalculator class 13 | * 计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角 14 | */ 15 | class QPoint; 16 | class QRect; 17 | class CursorPosCalculator 18 | { 19 | public: 20 | explicit CursorPosCalculator(); 21 | 22 | void reset(); 23 | void recalculate(const QPoint &gMousePos, const QRect &frameRect); 24 | 25 | public: 26 | bool m_bOnEdges : true; 27 | bool m_bOnLeftEdge : true; 28 | bool m_bOnRightEdge : true; 29 | bool m_bOnTopEdge : true; 30 | bool m_bOnBottomEdge : true; 31 | bool m_bOnTopLeftEdge : true; 32 | bool m_bOnBottomLeftEdge : true; 33 | bool m_bOnTopRightEdge : true; 34 | bool m_bOnBottomRightEdge : true; 35 | 36 | static int m_nBorderWidth; 37 | static int m_nTitleHeight; 38 | }; 39 | 40 | #endif // CURSORPOSCALCULATOR_H 41 | -------------------------------------------------------------------------------- /libframelesswindow/framelesshelper.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * framelesshelper.cpp 5 | * 实现了FramelessHelper类。设置窗体是否可移动、缩放、橡皮筋等属性。 6 | * 7 | */ 8 | 9 | #include "framelesshelper.h" 10 | #include "framelesshelperprivate.h" 11 | #include "cursorposcalculator.h" 12 | #include 13 | #include 14 | 15 | FramelessHelper::FramelessHelper(QObject *parent) 16 | : QObject(parent) 17 | , d(new FramelessHelperPrivate()) 18 | { 19 | d->m_bWidgetMovable = true; 20 | d->m_bWidgetResizable = true; 21 | d->m_bRubberBandOnMove = false; 22 | d->m_bRubberBandOnResize = false; 23 | } 24 | 25 | FramelessHelper::~FramelessHelper() 26 | { 27 | QList keys = d->m_widgetDataHash.keys(); 28 | int size = keys.size(); 29 | for(int i = 0; i < size; ++i) { 30 | delete d->m_widgetDataHash.take(keys[i]); 31 | } 32 | 33 | delete d; 34 | } 35 | 36 | void FramelessHelper::activateOn(QWidget *topLevelWidget) 37 | { 38 | if(!d->m_widgetDataHash.contains(topLevelWidget)) { 39 | WidgetData *data = new WidgetData(d, topLevelWidget); 40 | d->m_widgetDataHash.insert(topLevelWidget, data); 41 | 42 | topLevelWidget->installEventFilter(this); 43 | } 44 | } 45 | 46 | void FramelessHelper::removeFrom(QWidget *topLevelWidget) 47 | { 48 | WidgetData *data = d->m_widgetDataHash.take(topLevelWidget); 49 | if(data) { 50 | topLevelWidget->removeEventFilter(this); 51 | delete data; 52 | } 53 | } 54 | 55 | void FramelessHelper::setWidgetMovable(bool movable) 56 | { 57 | d->m_bWidgetMovable = movable; 58 | } 59 | 60 | void FramelessHelper::setWidgetResizable(bool resizable) 61 | { 62 | d->m_bWidgetResizable = resizable; 63 | } 64 | 65 | void FramelessHelper::setRubberBandOnMove(bool movable) 66 | { 67 | d->m_bRubberBandOnMove = movable; 68 | QList list = d->m_widgetDataHash.values(); 69 | foreach(WidgetData *data, list) { 70 | data->updateRubberBandStatus(); 71 | } 72 | } 73 | 74 | void FramelessHelper::setRubberBandOnResize(bool resizable) 75 | { 76 | d->m_bRubberBandOnResize = resizable; 77 | QList list = d->m_widgetDataHash.values(); 78 | foreach(WidgetData *data, list) { 79 | data->updateRubberBandStatus(); 80 | } 81 | } 82 | 83 | void FramelessHelper::setBorderWidth(uint width) 84 | { 85 | if(width > 0) { 86 | CursorPosCalculator::m_nBorderWidth = width; 87 | } 88 | } 89 | 90 | void FramelessHelper::setTitleHeight(uint height) 91 | { 92 | if(height > 0) { 93 | CursorPosCalculator::m_nTitleHeight = height; 94 | } 95 | } 96 | 97 | bool FramelessHelper::widgetResizable() const 98 | { 99 | return d->m_bWidgetResizable; 100 | } 101 | 102 | bool FramelessHelper::widgetMoable() const 103 | { 104 | return d->m_bWidgetMovable; 105 | } 106 | 107 | bool FramelessHelper::rubberBandOnMove() const 108 | { 109 | return d->m_bRubberBandOnMove; 110 | } 111 | 112 | bool FramelessHelper::rubberBandOnResize() const 113 | { 114 | return d->m_bRubberBandOnResize; 115 | } 116 | 117 | uint FramelessHelper::borderWidth() const 118 | { 119 | return CursorPosCalculator::m_nBorderWidth; 120 | } 121 | 122 | uint FramelessHelper::titleHeight() const 123 | { 124 | return CursorPosCalculator::m_nTitleHeight; 125 | } 126 | 127 | bool FramelessHelper::eventFilter(QObject *watched, QEvent *event) 128 | { 129 | switch(event->type()) { 130 | case QEvent::MouseMove: 131 | case QEvent::HoverMove: 132 | case QEvent::MouseButtonPress: 133 | case QEvent::MouseButtonRelease: 134 | case QEvent::Leave: 135 | { 136 | WidgetData *data = d->m_widgetDataHash.value(static_cast(watched)); 137 | if(data) { 138 | data->handleWidgetEvent(event); 139 | return true; 140 | } 141 | } 142 | default: 143 | return QObject::eventFilter(watched, event); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /libframelesswindow/framelesshelper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * framelesshelper.h 5 | * FramelessHelper类。设置窗体是否可移动、缩放、橡皮筋等属性。 6 | * 7 | */ 8 | 9 | #ifndef FRAMELESSHELPER_H 10 | #define FRAMELESSHELPER_H 11 | 12 | #include 13 | #include 14 | #include "widgetdata.h" 15 | 16 | 17 | class QWdiget; 18 | class FramelessHelperPrivate; 19 | class FramelessHelper : public QObject 20 | { 21 | Q_OBJECT 22 | public: 23 | explicit FramelessHelper(QObject *parent = nullptr); 24 | ~FramelessHelper(); 25 | 26 | /** 27 | * @brief activateOn 28 | * 激活窗体 29 | * @param topLevelWidget 30 | * QWidget * 31 | */ 32 | void activateOn(QWidget *topLevelWidget); 33 | 34 | /** 35 | * @brief removeFrom 36 | * 移除窗体 37 | * @param topLevelWidget 38 | * QWidget * 39 | */ 40 | void removeFrom(QWidget *topLevelWidget); 41 | 42 | /** 43 | * @brief setWidgetMovable 44 | * 设置窗体移动 45 | * @param movable 46 | * bool 47 | */ 48 | void setWidgetMovable(bool movable); 49 | 50 | /** 51 | * @brief setWidgetResizable 52 | * 设置窗体缩放 53 | * @param resizable 54 | * bool 55 | */ 56 | 57 | void setWidgetResizable(bool resizable); 58 | /** 59 | * @brief setRubberBandOnMove 60 | * 设置橡皮筋移动 61 | * @param movable 62 | * bool 63 | */ 64 | void setRubberBandOnMove(bool movable); 65 | /** 66 | * @brief setRubberBandOnResize 67 | * 设置橡皮筋缩放 68 | * @param resizable 69 | * bool 70 | */ 71 | void setRubberBandOnResize(bool resizable); 72 | 73 | /** 74 | * @brief setBorderWidth 75 | * 设置边框的宽度 76 | * @param width 77 | * unsigned int 78 | */ 79 | void setBorderWidth(uint width); 80 | 81 | /** 82 | * @brief setTitleHeight 83 | * 设置标题栏高度 84 | * @param height 85 | * unsigned int 86 | */ 87 | void setTitleHeight(uint height); 88 | 89 | bool widgetResizable() const; 90 | bool widgetMoable() const; 91 | bool rubberBandOnMove() const; 92 | bool rubberBandOnResize() const; 93 | uint borderWidth() const; 94 | uint titleHeight() const; 95 | 96 | protected: 97 | virtual bool eventFilter(QObject *watched, QEvent *event); 98 | 99 | private: 100 | FramelessHelperPrivate *d; 101 | }; 102 | 103 | #endif // FRAMELESSHELPER_H 104 | -------------------------------------------------------------------------------- /libframelesswindow/framelesshelperprivate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * framelesshelperprivate.h 5 | * 存储界面对应的数据集合,以及是否可移动、可缩放属性。 6 | * 7 | */ 8 | 9 | #ifndef FRAMELESSHELPERPRIVATE_H 10 | #define FRAMELESSHELPERPRIVATE_H 11 | 12 | #include "widgetdata.h" 13 | #include 14 | #include 15 | /** 16 | * @brief The FramelessHelperPrivate class 17 | * 存储界面对应的数据集合,以及是否可移动、可缩放属性 18 | */ 19 | class FramelessHelperPrivate 20 | { 21 | public: 22 | QHash m_widgetDataHash; 23 | bool m_bWidgetMovable : true; 24 | bool m_bWidgetResizable : true; 25 | bool m_bRubberBandOnResize : true; 26 | bool m_bRubberBandOnMove : true; 27 | }; 28 | 29 | #endif // FRAMELESSHELPERPRIVATE_H 30 | -------------------------------------------------------------------------------- /libframelesswindow/framelesswindow.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * framelesswindow.cpp 5 | * 实现了FramelessWindow、FramlessDialog、FramelessMessageBox 6 | * 7 | */ 8 | 9 | #include "framelesswindow.h" 10 | #include "framelesshelper.h" 11 | #include "titlebar.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | FramelessDialog::FramelessDialog(QWidget *parent) 30 | : WidgetShadow(parent) 31 | { 32 | 33 | resize(400, 300); 34 | setObjectName("framelessDialog"); 35 | setWindowTitle("FramelessDialog"); 36 | // 设置最大和还原按钮不可见 37 | setMinimumVisible(false); 38 | setMaximumVisible(false); 39 | } 40 | 41 | FramelessMessageBox::FramelessMessageBox(QWidget *parent, const QString &title, const QString &text, 42 | QMessageBox::StandardButtons buttons, 43 | QMessageBox::StandardButton defaultButton) 44 | : FramelessDialog(parent) 45 | { 46 | setObjectName("framelessMessagBox"); 47 | setMinimumVisible(false); 48 | setMaximumVisible(false); 49 | setWidgetResizable(false); 50 | setWindowTitle(title); 51 | setMinimumSize(300, 150); 52 | 53 | m_pButtonBox = new QDialogButtonBox(this); 54 | m_pButtonBox->setStandardButtons(QDialogButtonBox::StandardButtons(int(buttons))); 55 | setDefaultButton(defaultButton); 56 | 57 | QPushButton *pOkButton = m_pButtonBox->button(QDialogButtonBox::Ok); 58 | if(pOkButton != Q_NULLPTR) { 59 | pOkButton->setObjectName("pOkButton"); 60 | } 61 | 62 | QPushButton *pYesButton = m_pButtonBox->button(QDialogButtonBox::Yes); 63 | if(pYesButton != Q_NULLPTR) { 64 | pYesButton->setObjectName("pYesButton"); 65 | } 66 | 67 | QPushButton *pNoButton = m_pButtonBox->button(QDialogButtonBox::No); 68 | if(pNoButton != Q_NULLPTR) { 69 | pNoButton->setObjectName("pNoButton"); 70 | } 71 | 72 | QPushButton *pCloseButton = m_pButtonBox->button(QDialogButtonBox::Close); 73 | if(pCloseButton != Q_NULLPTR) { 74 | pCloseButton->setObjectName("pCloseButton"); 75 | } 76 | 77 | QPushButton *pCancelButton = m_pButtonBox->button(QDialogButtonBox::Cancel); 78 | if(pCancelButton != Q_NULLPTR) { 79 | pCancelButton->setObjectName("pCancelButton"); 80 | } 81 | 82 | m_pIconLabel = new QLabel(this); 83 | m_pLabel = new QLabel(this); 84 | 85 | QPixmap pixmap(":/images/msgbox/msg-info-48x48.png"); 86 | m_pIconLabel->setPixmap(pixmap); 87 | m_pIconLabel->setFixedSize(35, 35); 88 | m_pIconLabel->setScaledContents(true); 89 | m_pIconLabel->setObjectName("m_pIconLabel"); 90 | 91 | m_pLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); 92 | m_pLabel->setObjectName("messageTextLabel"); 93 | m_pLabel->setOpenExternalLinks(true); 94 | m_pLabel->setText(text); 95 | 96 | m_pGridLayout = new QGridLayout(); 97 | m_pGridLayout->addWidget(m_pIconLabel, 0, 0, 2, 1, Qt::AlignTop); 98 | m_pGridLayout->addWidget(m_pLabel, 0, 1, 2, 1); 99 | m_pGridLayout->addWidget(m_pButtonBox, m_pGridLayout->rowCount(), 0, 1, m_pGridLayout->columnCount()); 100 | m_pGridLayout->setSizeConstraint(QLayout::SetNoConstraint); 101 | m_pGridLayout->setHorizontalSpacing(10); 102 | m_pGridLayout->setVerticalSpacing(10); 103 | m_pGridLayout->setContentsMargins(10, 10, 10, 10); 104 | m_pFrameLessWindowLayout->addLayout(m_pGridLayout); 105 | 106 | translateUI(); 107 | 108 | connect(m_pButtonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(onButtonClicked(QAbstractButton*))); 109 | 110 | // 计算文字宽度 111 | QFont wordfont; 112 | wordfont.setFamily(this->font().defaultFamily()); 113 | wordfont.setPointSize(this->font().pointSize()); 114 | QFontMetrics fm(wordfont); 115 | QRect rect = fm.boundingRect(text); 116 | 117 | resize(rect.width() + 60, 130); 118 | } 119 | 120 | FramelessMessageBox::~FramelessMessageBox() 121 | { 122 | 123 | } 124 | 125 | QAbstractButton *FramelessMessageBox::clickedButton() const 126 | { 127 | return m_pClickedButton; 128 | } 129 | 130 | QMessageBox::StandardButton FramelessMessageBox::standardButton(QAbstractButton *button) const 131 | { 132 | return (QMessageBox::StandardButton)m_pButtonBox->standardButton(button); 133 | } 134 | 135 | void FramelessMessageBox::setDefaultButton(QPushButton *button) 136 | { 137 | if(!m_pButtonBox->buttons().contains(button)) 138 | return; 139 | 140 | m_pDefaultButton = button; 141 | button->setDefault(true); 142 | button->setFocus(); 143 | } 144 | 145 | void FramelessMessageBox::setDefaultButton(QMessageBox::StandardButton button) 146 | { 147 | setDefaultButton(m_pButtonBox->button(QDialogButtonBox::StandardButton(button))); 148 | } 149 | 150 | void FramelessMessageBox::setTitle(const QString &title) 151 | { 152 | setWindowTitle(title); 153 | } 154 | 155 | void FramelessMessageBox::setText(const QString &text) 156 | { 157 | m_pLabel->setText(text); 158 | } 159 | 160 | void FramelessMessageBox::setIcon(const QString &icon) 161 | { 162 | m_pIconLabel->setPixmap(QPixmap(icon)); 163 | } 164 | 165 | void FramelessMessageBox::hideInfoIcon() 166 | { 167 | //隐藏Icon 168 | m_pIconLabel->setVisible(false); 169 | //重新设置文字位置 170 | m_pGridLayout->addWidget(m_pLabel, 0, 0, 2, 1, Qt::AlignTop); 171 | } 172 | 173 | void FramelessMessageBox::addWidget(QWidget *pWidget) 174 | { 175 | m_pLabel->hide(); 176 | m_pGridLayout->addWidget(pWidget, 0, 1, 2, 1); 177 | } 178 | 179 | QMessageBox::StandardButton FramelessMessageBox::showMessageBox(QWidget *parent, 180 | const QString &title, 181 | const QString &text, 182 | QMessageBox::StandardButtons buttons, 183 | IconType messageType, 184 | QMessageBox::StandardButton defaultButton) 185 | { 186 | FramelessMessageBox msgBox(parent, title, text, buttons, defaultButton); 187 | msgBox.hideTitleBarIcon(); 188 | 189 | switch(messageType) { 190 | case MSG_NOICON: 191 | msgBox.hideInfoIcon(); 192 | break; 193 | case MSG_INFORMATION: 194 | msgBox.setIcon(":/images/msgbox/msg-info-48x48.png"); 195 | break; 196 | case MSG_WARNNING: 197 | msgBox.setIcon(":/images/msgbox/msg-warning-48x48.png"); 198 | break; 199 | case MSG_QUESTION: 200 | msgBox.setIcon(":/images/msgbox/msg-question-48x48.png"); 201 | break; 202 | case MSG_ERROR: 203 | msgBox.setIcon(":/images/msgbox/msg-error-48x48.png"); 204 | break; 205 | case MSG_SUCCESS: 206 | msgBox.setIcon(":/images/msgbox/msg-success-48x48.png"); 207 | break; 208 | default: 209 | break; 210 | } 211 | 212 | if(msgBox.exec() == -1) 213 | return QMessageBox::Cancel; 214 | return msgBox.standardButton(msgBox.clickedButton()); 215 | } 216 | 217 | QMessageBox::StandardButton FramelessMessageBox::showInformation(QWidget *parent, 218 | const QString &title, 219 | const QString &text, 220 | QMessageBox::StandardButtons buttons, 221 | QMessageBox::StandardButton defaultButton) 222 | { 223 | FramelessMessageBox msgBox(parent, title, text, buttons, defaultButton); 224 | msgBox.setIcon(":/images/msgbox/msg-info-48x48.png"); 225 | if(msgBox.exec() == -1) 226 | return QMessageBox::Cancel; 227 | return msgBox.standardButton(msgBox.clickedButton()); 228 | } 229 | 230 | QMessageBox::StandardButton FramelessMessageBox::showError(QWidget *parent, 231 | const QString &title, 232 | const QString &text, 233 | QMessageBox::StandardButtons buttons, 234 | QMessageBox::StandardButton defaultButton) 235 | { 236 | FramelessMessageBox msgBox(parent, title, text, buttons, defaultButton); 237 | msgBox.setIcon(":/images/msgbox/msg-error-48x48.png"); 238 | if(msgBox.exec() == -1) 239 | return QMessageBox::Cancel; 240 | return msgBox.standardButton(msgBox.clickedButton()); 241 | } 242 | 243 | QMessageBox::StandardButton FramelessMessageBox::showSuccess(QWidget *parent, 244 | const QString &title, 245 | const QString &text, 246 | QMessageBox::StandardButtons buttons, 247 | QMessageBox::StandardButton defaultButton) 248 | { 249 | FramelessMessageBox msgBox(parent, title, text, buttons, defaultButton); 250 | msgBox.setIcon(":/images/msgbox/msg-success-48x48.png"); 251 | if(msgBox.exec() == -1) 252 | return QMessageBox::Cancel; 253 | return msgBox.standardButton(msgBox.clickedButton()); 254 | } 255 | 256 | QMessageBox::StandardButton FramelessMessageBox::showQuestion(QWidget *parent, 257 | const QString &title, 258 | const QString &text, 259 | QMessageBox::StandardButtons buttons, 260 | QMessageBox::StandardButton defaultButton) 261 | { 262 | FramelessMessageBox msgBox(parent, title, text, buttons, defaultButton); 263 | msgBox.setIcon(":/images/msgbox/msg-question-48x48.png"); 264 | if(msgBox.exec() == -1) 265 | return QMessageBox::Cancel; 266 | return msgBox.standardButton(msgBox.clickedButton()); 267 | } 268 | 269 | QMessageBox::StandardButton FramelessMessageBox::showWarning(QWidget *parent, 270 | const QString &title, 271 | const QString &text, 272 | QMessageBox::StandardButtons buttons, 273 | QMessageBox::StandardButton defaultButton) 274 | { 275 | FramelessMessageBox msgBox(parent, title, text, buttons, defaultButton); 276 | msgBox.setIcon(":/images/msgbox/msg-warning-48x48.png"); 277 | if(msgBox.exec() == -1) 278 | return QMessageBox::Cancel; 279 | return msgBox.standardButton(msgBox.clickedButton()); 280 | } 281 | 282 | QMessageBox::StandardButton FramelessMessageBox::showCritical(QWidget *parent, 283 | const QString &title, 284 | const QString &text, 285 | QMessageBox::StandardButtons buttons, 286 | QMessageBox::StandardButton defaultButton) 287 | { 288 | FramelessMessageBox msgBox(parent, title, text, buttons, defaultButton); 289 | msgBox.setIcon(":/images/msgbox/msg-warning-48x48.png"); 290 | if(msgBox.exec() == -1) 291 | return QMessageBox::Cancel; 292 | return msgBox.standardButton(msgBox.clickedButton()); 293 | } 294 | 295 | QMessageBox::StandardButton FramelessMessageBox::showCheckBoxQuestion(QWidget *parent, 296 | const QString &title, 297 | const QString &text, 298 | QMessageBox::StandardButtons buttons, 299 | QMessageBox::StandardButton defaultButton) 300 | { 301 | FramelessMessageBox msgBox(parent, title, text, buttons, defaultButton); 302 | msgBox.setIcon(":/images/msgbox/msg-question-48x48.png"); 303 | 304 | QCheckBox *pCheckBox = new QCheckBox(&msgBox); 305 | pCheckBox->setText(text); 306 | msgBox.addWidget(pCheckBox); 307 | if(msgBox.exec() == -1) 308 | return QMessageBox::Cancel; 309 | 310 | QMessageBox::StandardButton standardButton = msgBox.standardButton(msgBox.clickedButton()); 311 | if(standardButton == QMessageBox::Yes) { 312 | return pCheckBox->isChecked() ? QMessageBox::Yes : QMessageBox::No; 313 | } 314 | return QMessageBox::Cancel; 315 | } 316 | 317 | void FramelessMessageBox::changeEvent(QEvent *event) 318 | { 319 | switch(event->type()) { 320 | case QEvent::LanguageChange: 321 | translateUI(); 322 | break; 323 | 324 | default: 325 | FramelessDialog::changeEvent(event); 326 | } 327 | } 328 | 329 | void FramelessMessageBox::onButtonClicked(QAbstractButton *button) 330 | { 331 | m_pClickedButton = button; 332 | done(execReturnCode(button)); 333 | } 334 | 335 | void FramelessMessageBox::translateUI() 336 | { 337 | QPushButton *pYesButton = m_pButtonBox->button(QDialogButtonBox::Yes); 338 | if(pYesButton != NULL) 339 | pYesButton->setText(tr("Yes")); 340 | 341 | QPushButton *pNoButton = m_pButtonBox->button(QDialogButtonBox::No); 342 | if(pNoButton != NULL) 343 | pNoButton->setText(tr("No")); 344 | 345 | QPushButton *pOkButton = m_pButtonBox->button(QDialogButtonBox::Ok); 346 | if(pOkButton != NULL) 347 | pOkButton->setText(tr("Ok")); 348 | 349 | QPushButton *pCancelButton = m_pButtonBox->button(QDialogButtonBox::Cancel); 350 | if(pCancelButton != NULL) 351 | pCancelButton->setText(tr("Cancel")); 352 | } 353 | 354 | int FramelessMessageBox::execReturnCode(QAbstractButton *button) 355 | { 356 | int nResult = m_pButtonBox->standardButton(button); 357 | return nResult; 358 | } 359 | -------------------------------------------------------------------------------- /libframelesswindow/framelesswindow.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * framelesswindow.h 5 | * 定义了FramelessWindow、FramlessDialog、FramelessMessageBox 6 | * 7 | */ 8 | 9 | #ifndef FRAMELESSWINDOW_H 10 | #define FRAMELESSWINDOW_H 11 | 12 | #include "framelesswindow_global.h" 13 | #include "borderimage.h" 14 | #include "widgetshadow.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | typedef WidgetShadow FramelessWindow; 25 | 26 | /** 27 | * @brief The FramelessDialog class 28 | * 无边框自定义对话框 29 | */ 30 | class FRAMELESSWINDOWSHARED_EXPORT FramelessDialog : public WidgetShadow 31 | { 32 | Q_OBJECT 33 | 34 | public: 35 | FramelessDialog(QWidget *parent = nullptr); 36 | 37 | }; 38 | 39 | 40 | class QLabel; 41 | class QGridLayout; 42 | class QDialogButtonBox; 43 | class QHBoxLayout; 44 | class QAbstractButton; 45 | /** 46 | * @brief The FramelessMessageBox class 47 | * 无边框自定义提示框 48 | */ 49 | class FRAMELESSWINDOWSHARED_EXPORT FramelessMessageBox : public FramelessDialog 50 | { 51 | Q_OBJECT 52 | 53 | public: 54 | FramelessMessageBox(QWidget *parent = nullptr, const QString &title = tr("Tip"), const QString &text = "", 55 | QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::Ok); 56 | 57 | ~FramelessMessageBox(); 58 | 59 | enum IconType { 60 | MSG_NOICON = 0, // 无图标 61 | MSG_INFORMATION, // 提示信息 62 | MSG_WARNNING, // 提示警告 63 | MSG_QUESTION, // 提示询问 64 | MSG_ERROR, // 提示错误 65 | MSG_SUCCESS, // 提示成功 66 | }; 67 | 68 | QAbstractButton *clickedButton() const; 69 | QMessageBox::StandardButton standardButton(QAbstractButton *button) const; 70 | /** 71 | * @brief setDefaultButton 72 | * 设置默认按钮 73 | * @param button 74 | */ 75 | void setDefaultButton(QPushButton *button); 76 | void setDefaultButton(QMessageBox::StandardButton button); 77 | /** 78 | * @brief setTitle 79 | * 设置窗体标题 80 | * @param title 81 | */ 82 | void setTitle(const QString &title); 83 | /** 84 | * @brief setText 85 | * @note 设置提示信息 86 | * @param text 87 | */ 88 | void setText(const QString &text); 89 | /** 90 | * @brief setIcon 91 | * @note 设置窗体图标 92 | * @param icon 93 | */ 94 | void setIcon(const QString &icon); 95 | 96 | /** 97 | * @brief hideInfoIcon 98 | * @note 隐藏icon 99 | */ 100 | void hideInfoIcon(); 101 | 102 | /** 103 | * @brief addWidget 104 | * 添加控件-替换提示信息所在的QLabel 105 | * @param pWidget 106 | */ 107 | void addWidget(QWidget *pWidget); 108 | 109 | static QMessageBox::StandardButton showMessageBox(QWidget *parent, const QString &title, 110 | const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, 111 | IconType messageType = MSG_NOICON, 112 | QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 113 | 114 | static QMessageBox::StandardButton showInformation(QWidget *parent, const QString &title, 115 | const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, 116 | QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 117 | static QMessageBox::StandardButton showError(QWidget *parent, const QString &title, 118 | const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, 119 | QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 120 | static QMessageBox::StandardButton showSuccess(QWidget *parent, const QString &title, 121 | const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, 122 | QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 123 | static QMessageBox::StandardButton showQuestion(QWidget *parent, const QString &title, 124 | const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, 125 | QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 126 | static QMessageBox::StandardButton showWarning(QWidget *parent, const QString &title, 127 | const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, 128 | QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 129 | static QMessageBox::StandardButton showCritical(QWidget *parent, const QString &title, 130 | const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, 131 | QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 132 | static QMessageBox::StandardButton showCheckBoxQuestion(QWidget *parent, const QString &title, 133 | const QString &text, QMessageBox::StandardButtons buttons, 134 | QMessageBox::StandardButton defaultButton); 135 | 136 | 137 | protected: 138 | /** 139 | * @brief changeEvent 140 | * 多语言翻译 141 | * @param event 142 | */ 143 | void changeEvent(QEvent *event); 144 | 145 | private slots: 146 | void onButtonClicked(QAbstractButton *button); 147 | 148 | private: 149 | void translateUI(); 150 | int execReturnCode(QAbstractButton *button); 151 | 152 | private: 153 | QLabel *m_pIconLabel; 154 | QLabel *m_pLabel; 155 | QGridLayout *m_pGridLayout; 156 | QDialogButtonBox *m_pButtonBox; 157 | QAbstractButton *m_pClickedButton; 158 | QAbstractButton *m_pDefaultButton; 159 | QHBoxLayout *m_pLayout; 160 | }; 161 | 162 | #endif // FRAMELESSWINDOW_H 163 | -------------------------------------------------------------------------------- /libframelesswindow/framelesswindow_global.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * framelesswindow_global.h 5 | * 6 | */ 7 | 8 | #ifndef FRAMELESSWINDOW_GLOBAL_H 9 | #define FRAMELESSWINDOW_GLOBAL_H 10 | 11 | #include 12 | 13 | #if defined(FRAMELESSWINDOW_LIBRARY) 14 | # define FRAMELESSWINDOWSHARED_EXPORT Q_DECL_EXPORT 15 | #else 16 | # define FRAMELESSWINDOWSHARED_EXPORT Q_DECL_IMPORT 17 | #endif 18 | 19 | #endif // FRAMELESSWINDOW_GLOBAL_H 20 | -------------------------------------------------------------------------------- /libframelesswindow/images.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/titlebar/close.png 4 | images/titlebar/max.png 5 | images/titlebar/menu.png 6 | images/titlebar/min.png 7 | images/titlebar/restore.png 8 | images/background/client-shadow.png 9 | images/msgbox/msg-error-48x48.png 10 | images/msgbox/msg-info-48x48.png 11 | images/msgbox/msg-question-48x48.png 12 | images/msgbox/msg-success-48x48.png 13 | images/msgbox/msg-warning-48x48.png 14 | 15 | 16 | -------------------------------------------------------------------------------- /libframelesswindow/images/background/client-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/libframelesswindow/images/background/client-shadow.png -------------------------------------------------------------------------------- /libframelesswindow/images/background/client-shadow.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/libframelesswindow/images/background/client-shadow.psd -------------------------------------------------------------------------------- /libframelesswindow/images/msgbox/msg-error-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/libframelesswindow/images/msgbox/msg-error-48x48.png -------------------------------------------------------------------------------- /libframelesswindow/images/msgbox/msg-info-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/libframelesswindow/images/msgbox/msg-info-48x48.png -------------------------------------------------------------------------------- /libframelesswindow/images/msgbox/msg-question-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/libframelesswindow/images/msgbox/msg-question-48x48.png -------------------------------------------------------------------------------- /libframelesswindow/images/msgbox/msg-success-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/libframelesswindow/images/msgbox/msg-success-48x48.png -------------------------------------------------------------------------------- /libframelesswindow/images/msgbox/msg-warning-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/libframelesswindow/images/msgbox/msg-warning-48x48.png -------------------------------------------------------------------------------- /libframelesswindow/images/titlebar/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/libframelesswindow/images/titlebar/close.png -------------------------------------------------------------------------------- /libframelesswindow/images/titlebar/max.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/libframelesswindow/images/titlebar/max.png -------------------------------------------------------------------------------- /libframelesswindow/images/titlebar/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/libframelesswindow/images/titlebar/menu.png -------------------------------------------------------------------------------- /libframelesswindow/images/titlebar/min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/libframelesswindow/images/titlebar/min.png -------------------------------------------------------------------------------- /libframelesswindow/images/titlebar/restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/libframelesswindow/images/titlebar/restore.png -------------------------------------------------------------------------------- /libframelesswindow/libframelesswindow.pri: -------------------------------------------------------------------------------- 1 | INCLUDEPATH += $$PWD 2 | DEPENDPATH += $$PWD 3 | 4 | DEFINES += FRAMELESSWINDOW_LIBRARY 5 | 6 | HEADERS += \ 7 | $$PWD/cursorposcalculator.h \ 8 | $$PWD/framelesshelper.h \ 9 | $$PWD/framelesshelperprivate.h \ 10 | $$PWD/framelesswindow.h \ 11 | $$PWD/framelesswindow_global.h \ 12 | $$PWD/widgetdata.h \ 13 | $$PWD/titlebar.h \ 14 | $$PWD/borderimage.h \ 15 | $$PWD/statebutton.h \ 16 | $$PWD/widgetshadow.h 17 | 18 | SOURCES += \ 19 | $$PWD/cursorposcalculator.cpp \ 20 | $$PWD/framelesshelper.cpp \ 21 | $$PWD/framelesswindow.cpp \ 22 | $$PWD/widgetdata.cpp \ 23 | $$PWD/titlebar.cpp \ 24 | $$PWD/borderimage.cpp \ 25 | $$PWD/statebutton.cpp 26 | 27 | RESOURCES += \ 28 | $$PWD/images.qrc \ 29 | $$PWD/style.qrc 30 | -------------------------------------------------------------------------------- /libframelesswindow/statebutton.cpp: -------------------------------------------------------------------------------- 1 |  2 | #include "statebutton.h" 3 | #include 4 | #include 5 | #include 6 | 7 | StateButton::StateButton(QWidget *parent) 8 | :QPushButton(parent) 9 | ,m_status(NORMAL) 10 | ,m_mousePressed(false) 11 | ,m_pixmapType(NONE) 12 | { 13 | setStyleSheet("QPushButton{background:transparent;border:none;}"); 14 | } 15 | 16 | StateButton::~StateButton() 17 | { 18 | } 19 | 20 | void StateButton::loadPixmap(const QString& pic_name, int state_count) 21 | { 22 | m_pixmapType = FOREGROUND; 23 | m_pixmap.load(pic_name); 24 | m_stateCount = state_count; 25 | m_width = m_pixmap.width()/state_count; 26 | m_height = m_pixmap.height(); 27 | setFixedSize(m_width, m_height); 28 | } 29 | 30 | void StateButton::setPixmap(const QPixmap& pixmap, int state_count) 31 | { 32 | m_pixmapType = FOREGROUND; 33 | m_pixmap = pixmap; 34 | m_stateCount = state_count; 35 | m_width = m_pixmap.width()/state_count; 36 | m_height = m_pixmap.height(); 37 | setFixedSize(m_width, m_height); 38 | } 39 | 40 | void StateButton::loadBackground(const QString& pic_name, int state_count/*=4*/) 41 | { 42 | loadPixmap(pic_name, state_count); 43 | m_pixmapType = BACKGROUND; 44 | } 45 | 46 | void StateButton::setBackground(const QPixmap& pixmap, int state_count/*=4*/) 47 | { 48 | setPixmap(pixmap, state_count); 49 | m_pixmapType = BACKGROUND; 50 | } 51 | 52 | void StateButton::enterEvent(QEvent *e) 53 | { 54 | m_status = HOVER; 55 | update(); 56 | } 57 | 58 | void StateButton::mousePressEvent(QMouseEvent *event) 59 | { 60 | if(event->button() == Qt::LeftButton) { 61 | m_mousePressed = true; 62 | m_status = PRESSED; 63 | update(); 64 | } 65 | 66 | QPushButton::mousePressEvent(event); 67 | } 68 | 69 | void StateButton::mouseReleaseEvent(QMouseEvent *event) 70 | { 71 | if(m_mousePressed) { 72 | m_mousePressed = false; 73 | 74 | if(this->rect().contains(event->pos())) { 75 | //if (isCheckable()) 76 | //{ 77 | // setChecked(!isChecked()); 78 | //} 79 | //emit clicked(); 80 | 81 | m_status = HOVER; 82 | } else { 83 | m_status = NORMAL; 84 | } 85 | 86 | update(); 87 | } 88 | 89 | QPushButton::mouseReleaseEvent(event); 90 | } 91 | 92 | void StateButton::leaveEvent(QEvent *e) 93 | { 94 | m_status = NORMAL; 95 | update(); 96 | QPushButton::leaveEvent(e); 97 | } 98 | 99 | void StateButton::paintEvent(QPaintEvent *e) 100 | { 101 | if(m_pixmapType == BACKGROUND) { 102 | paint_pixmap(); 103 | } 104 | 105 | // QPushButton::paintEvent(e); 106 | QStylePainter p(this); 107 | QStyleOptionButton option; 108 | initStyleOption(&option); 109 | if(option.state & QStyle::State_HasFocus) { 110 | option.state ^= QStyle::State_HasFocus; //去除焦点框 111 | option.state |= QStyle::State_MouseOver; 112 | } 113 | p.drawControl(QStyle::CE_PushButton, option); 114 | 115 | if(m_pixmapType == FOREGROUND) { 116 | paint_pixmap(); 117 | } 118 | } 119 | 120 | void StateButton::paint_pixmap() 121 | { 122 | QPainter painter(this); 123 | 124 | //根据状态显示图片 125 | ButtonStatus status = m_status; 126 | if(!isEnabled()) { 127 | status = DISABLED; 128 | } else if(isChecked()) { 129 | status = CHECKED; 130 | //没有checked图片,用hover代替 131 | if(status >= m_stateCount) { 132 | status = HOVER; 133 | } 134 | } 135 | 136 | if(status >= m_stateCount) { 137 | status = NORMAL; 138 | } 139 | 140 | painter.drawPixmap(rect(), m_pixmap.copy(m_width * status, 0, m_width, m_height)); 141 | } 142 | ////////////////////////////////////////////////////////////////////////// 143 | 144 | 145 | IconTextButton::IconTextButton(QWidget *parent) 146 | :QPushButton(parent) 147 | { 148 | } 149 | 150 | IconTextButton::~IconTextButton() 151 | { 152 | } 153 | 154 | void IconTextButton::paintEvent(QPaintEvent *e) 155 | { 156 | QStyleOptionButton opt; 157 | initStyleOption(&opt); 158 | //opt.text = ""; 159 | //opt.icon = QIcon(); 160 | //opt.state &= ~QStyle::State_HasFocus; //去除焦点框 161 | 162 | QStylePainter p(this); 163 | //绘制QSS描述的如border-image等 164 | p.drawControl(QStyle::CE_PushButtonBevel, opt); 165 | 166 | QRect r = rect(); //style()->subElementRect ( QStyle::SE_PushButtonContents, &opt, this); 167 | 168 | int v_space = 2; 169 | int x = r.left() + (r.width() - opt.iconSize.width()) / 2; 170 | int h = opt.iconSize.height() + v_space + opt.fontMetrics.height(); 171 | int y = r.top() + (r.height() - h) / 2; 172 | 173 | //y -= 2; 174 | 175 | p.drawPixmap(x, y, opt.icon.pixmap(opt.iconSize)); 176 | 177 | y += opt.iconSize.height() + v_space; 178 | y += opt.fontMetrics.ascent(); //加这个值的原因:The y-position is used as the baseline of the font. 179 | x = r.left() + (r.width() - opt.fontMetrics.width(opt.text)) / 2; 180 | p.drawText(x, y, opt.text); 181 | } 182 | 183 | TextButton::TextButton(QWidget *parent) 184 | :QPushButton(parent) 185 | { 186 | this->setFlat(true); 187 | this->setStyleSheet("QPushButton{background: transparent;}"); 188 | } 189 | 190 | TextButton::~TextButton() 191 | { 192 | 193 | } 194 | -------------------------------------------------------------------------------- /libframelesswindow/statebutton.h: -------------------------------------------------------------------------------- 1 |  2 | #ifndef _STATE_BUTTON_H_ 3 | #define _STATE_BUTTON_H_ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | //a pixmap have 5 picture which state is normal\hover\pressed\disabled\checked 10 | //this button does not draw focus rect 11 | class StateButton : public QPushButton 12 | { 13 | Q_OBJECT 14 | public: 15 | explicit StateButton(QWidget *parent = 0); 16 | ~StateButton(); 17 | 18 | public: 19 | void loadPixmap(const QString& pic_name, int state_count=4); 20 | void setPixmap(const QPixmap& pixmap, int state_count=4); 21 | 22 | void loadBackground(const QString& pic_name, int state_count=4); 23 | void setBackground(const QPixmap& pixmap, int state_count=4); 24 | 25 | protected: 26 | void enterEvent(QEvent *); 27 | void leaveEvent(QEvent *); 28 | void mousePressEvent(QMouseEvent *event); 29 | void mouseReleaseEvent(QMouseEvent *event); 30 | void paintEvent(QPaintEvent *); 31 | 32 | private: 33 | void paint_pixmap(); 34 | 35 | private: 36 | //枚举按钮的几种状态 37 | enum ButtonStatus {NORMAL, HOVER, PRESSED, DISABLED, CHECKED}; 38 | //pximap_位图类型, 仅能选择其一. 如果两者都需要,请选择BACKGROUND + QPushButton::setIcon 39 | enum PixmapType {NONE, FOREGROUND, BACKGROUND}; 40 | 41 | QPixmap m_pixmap; //图片 42 | PixmapType m_pixmapType; 43 | int m_stateCount; //图片有几种状态(几张子图) 44 | ButtonStatus m_status; //当前状态 45 | int m_width; //按钮宽度 46 | int m_height; //按钮高度 47 | bool m_mousePressed; //鼠标左键是否按下 48 | }; 49 | 50 | //按钮的图标在上面,文字在下面 51 | class IconTextButton: public QPushButton 52 | { 53 | public: 54 | IconTextButton(QWidget *parent); 55 | ~IconTextButton(); 56 | 57 | protected: 58 | void paintEvent(QPaintEvent *e); 59 | }; 60 | 61 | class TextButton: public QPushButton 62 | { 63 | Q_OBJECT 64 | public: 65 | TextButton(QWidget *parent = 0); 66 | ~TextButton(); 67 | }; 68 | 69 | #endif // 70 | -------------------------------------------------------------------------------- /libframelesswindow/style.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | style/style_black.qss 4 | style/style_white.qss 5 | 6 | 7 | -------------------------------------------------------------------------------- /libframelesswindow/style/style_black.qss: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库/测试程序 3 | * 4 | * style.qss 5 | * qss全局文件,设置界面的配色主题等。 6 | * 7 | */ 8 | 9 | 10 | /********** 无边框主窗体 **********/ 11 | FramelessWindow#framelessWindow { 12 | background-color: #323232; 13 | } 14 | /********************************/ 15 | 16 | 17 | /*********** 标题栏 **************/ 18 | QPushButton#minimizeButton { 19 | border: none; 20 | image: url(:/images/minimizeBtnWhite_16.png); 21 | } 22 | QPushButton#minimizeButton:hover { 23 | background: #505050; 24 | } 25 | 26 | QPushButton#maximizeButton { 27 | border: none; 28 | image: url(:/images/restoreWhite_16.png); 29 | } 30 | QPushButton#maximizeButton:hover { 31 | background: #505050; 32 | } 33 | QPushButton#maximizeButton[maximizeProperty=restore] { 34 | image: url(:/images/restoreWhite_16.png); 35 | } 36 | QPushButton#maximizeButton[maximizeProperty=maximize] { 37 | image: url(:/images/maximizeBtnWhite_16.png); 38 | } 39 | 40 | QPushButton#closeButton { 41 | border: none; 42 | image: url(:/images/closeBtnWhite_16.png); 43 | } 44 | QPushButton#closeButton:hover { 45 | background: #505050; 46 | } 47 | 48 | QPushButton#minimizeButton:pressed { 49 | background: #C8C8C8; 50 | image: url(:/images/minimizeBtnBlack_16.png); 51 | } 52 | QPushButton#maximizeButton:pressed { 53 | background: #C8C8C8; 54 | image: url(:/images/restoreBlack_16.png); 55 | } 56 | QPushButton#closeButton:pressed { 57 | background: #C8C8C8; 58 | image: url(:/images/closeBtnBlack_16.png); 59 | } 60 | QLabel#titleLabel { 61 | color: white; 62 | } 63 | /********************************/ 64 | 65 | /******* 自定义无边框提示框 *******/ 66 | FramelessMessageBox#framelessMessagBox { 67 | background-color: #404040; 68 | } 69 | QLabel#messageTextLabel { 70 | color: white; 71 | font-family: "Microsoft Yahei"; 72 | font-size: 14pt; 73 | } 74 | QPushButton#yesButton { 75 | background-color: red; 76 | } 77 | /********************************/ 78 | 79 | /******* 自定义无边框对话框 *******/ 80 | FramelessDialog#framelessDialog { 81 | background-color: #2E2E2E; 82 | } 83 | /********************************/ 84 | 85 | -------------------------------------------------------------------------------- /libframelesswindow/style/style_white.qss: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库/测试程序 3 | * 4 | * style.qss 5 | * qss全局文件,设置界面的配色主题等。 6 | * 7 | */ 8 | 9 | 10 | /********** 无边框主窗体 **********/ 11 | QWidget#framelessWindow { 12 | background-color: white; 13 | border-style: solid; 14 | border-width: 1px; 15 | border-color: green; 16 | } 17 | /********************************/ 18 | 19 | 20 | /*********** 标题栏 **************/ 21 | QPushButton#minimizeButton { 22 | border: none; 23 | image: url(:/images/minimizeBtnWhite_16.png); 24 | } 25 | QPushButton#minimizeButton:hover { 26 | background: #505050; 27 | } 28 | 29 | QPushButton#maximizeButton { 30 | border: none; 31 | image: url(:/images/restoreWhite_16.png); 32 | } 33 | QPushButton#maximizeButton:hover { 34 | background: #505050; 35 | } 36 | QPushButton#maximizeButton[maximizeProperty=restore] { 37 | image: url(:/images/restoreWhite_16.png); 38 | } 39 | QPushButton#maximizeButton[maximizeProperty=maximize] { 40 | image: url(:/images/maximizeBtnWhite_16.png); 41 | } 42 | 43 | QPushButton#closeButton { 44 | border: none; 45 | image: url(:/images/closeBtnWhite_16.png); 46 | } 47 | QPushButton#closeButton:hover { 48 | background: #505050; 49 | } 50 | 51 | QPushButton#minimizeButton:pressed { 52 | background: #C8C8C8; 53 | image: url(:/images/minimizeBtnBlack_16.png); 54 | } 55 | QPushButton#maximizeButton:pressed { 56 | background: #C8C8C8; 57 | image: url(:/images/restoreBlack_16.png); 58 | } 59 | QPushButton#closeButton:pressed { 60 | background: #C8C8C8; 61 | image: url(:/images/closeBtnBlack_16.png); 62 | } 63 | QLabel#titleLabel { 64 | color: white; 65 | } 66 | /********************************/ 67 | 68 | /******* 自定义无边框提示框 *******/ 69 | FramelessMessageBox#framelessMessagBox { 70 | background-color: white; 71 | border-style: solid; 72 | border-width: 1px; 73 | border-color: green; 74 | } 75 | QLabel#messageTextLabel { 76 | color: white; 77 | font-family: "Microsoft Yahei"; 78 | font-size: 14pt; 79 | } 80 | QPushButton#yesButton { 81 | background-color: red; 82 | } 83 | /********************************/ 84 | 85 | /******* 自定义无边框对话框 *******/ 86 | FramelessDialog#framelessDialog { 87 | background-color: white; 88 | border-style: solid; 89 | border-width: 1px; 90 | border-color: green; 91 | } 92 | /********************************/ 93 | 94 | -------------------------------------------------------------------------------- /libframelesswindow/titlebar.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * titlebar.cpp 5 | * 自定义窗体的标题栏。 6 | * 7 | */ 8 | 9 | #include "titlebar.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | TitleBar::TitleBar(QWidget *parent) 20 | : QWidget(parent) 21 | , m_bMaximizeDisabled(false) 22 | { 23 | setFixedHeight(30); 24 | 25 | m_pIconLabel = new QLabel(this); 26 | m_pTitleLabel = new QLabel(this); 27 | m_pMinimizeButton = new StateButton(this); 28 | m_pMinimizeButton->setPixmap(QPixmap(":/images/titlebar/min.png")); 29 | m_pMaximizeButton = new StateButton(this); 30 | m_pMaximizeButton->setPixmap(QPixmap(":/images/titlebar/max.png")); 31 | m_pCloseButton = new StateButton(this); 32 | m_pCloseButton->setPixmap(QPixmap(":/images/titlebar/close.png")); 33 | 34 | m_pIconLabel->setFixedSize(20, 20); 35 | m_pIconLabel->setScaledContents(true); 36 | 37 | m_pTitleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 38 | 39 | m_pTitleLabel->setObjectName("titleLabel"); 40 | m_pMinimizeButton->setObjectName("minimizeButton"); 41 | m_pMaximizeButton->setObjectName("maximizeButton"); 42 | m_pCloseButton->setObjectName("closeButton"); 43 | 44 | m_pMinimizeButton->setToolTip("Minimize"); 45 | m_pMaximizeButton->setToolTip("Maximize"); 46 | m_pCloseButton->setToolTip("Close"); 47 | 48 | m_pMainLayout = new QHBoxLayout(this); 49 | m_pMainLayout->addSpacing(5); 50 | m_pMainLayout->addWidget(m_pIconLabel); 51 | m_pMainLayout->addSpacing(5); 52 | m_pMainLayout->addWidget(m_pTitleLabel); 53 | m_pMainLayout->addStretch(); 54 | m_pMainLayout->addWidget(m_pMinimizeButton); 55 | m_pMainLayout->addWidget(m_pMaximizeButton); 56 | m_pMainLayout->addWidget(m_pCloseButton); 57 | m_pMainLayout->setSpacing(0); 58 | m_pMainLayout->setMargin(0); 59 | 60 | setLayout(m_pMainLayout); 61 | 62 | connect(m_pMinimizeButton, SIGNAL(clicked(bool)), this, SLOT(onClicked())); 63 | connect(m_pMaximizeButton, SIGNAL(clicked(bool)), this, SLOT(onClicked())); 64 | connect(m_pCloseButton, SIGNAL(clicked(bool)), this, SLOT(onClicked())); 65 | } 66 | TitleBar::~TitleBar() 67 | { 68 | 69 | } 70 | 71 | void TitleBar::setMinimumVisible(bool minimum) 72 | { 73 | if(!minimum) m_pMinimizeButton->hide(); 74 | } 75 | 76 | void TitleBar::setMaximumVisible(bool maximum) 77 | { 78 | if(!maximum) m_pMaximizeButton->hide(); 79 | } 80 | 81 | void TitleBar::setMaximizeDisabled() 82 | { 83 | m_bMaximizeDisabled = true; 84 | } 85 | 86 | void TitleBar::hideTitleIcon() 87 | { 88 | m_pIconLabel->setVisible(false); 89 | } 90 | 91 | void TitleBar::mouseDoubleClickEvent(QMouseEvent *event) 92 | { 93 | Q_UNUSED(event); 94 | if(m_bMaximizeDisabled) { 95 | return; 96 | } 97 | 98 | emit m_pMaximizeButton->clicked(); 99 | } 100 | 101 | //void TitleBar::mousePressEvent(QMouseEvent *event) 102 | //{ 103 | // if (ReleaseCapture()) 104 | // { 105 | // QWidget *pWindow = this->window(); 106 | // if (pWindow->isTopLevel()) 107 | // { 108 | // SendMessage(HWND(pWindow->winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0); 109 | // } 110 | // } 111 | // event->ignore(); 112 | //} 113 | 114 | bool TitleBar::eventFilter(QObject *obj, QEvent *event) 115 | { 116 | switch(event->type()) { 117 | case QEvent::WindowTitleChange: 118 | { 119 | QWidget *pWidget = qobject_cast(obj); 120 | if(pWidget) { 121 | m_pTitleLabel->setText(pWidget->windowTitle()); 122 | return true; 123 | } 124 | } 125 | case QEvent::WindowIconChange: 126 | { 127 | QWidget *pWidget = qobject_cast(obj); 128 | if(pWidget) { 129 | QIcon icon = pWidget->windowIcon(); 130 | m_pIconLabel->setPixmap(icon.pixmap(m_pIconLabel->size())); 131 | return true; 132 | } 133 | } 134 | case QEvent::Move: 135 | case QEvent::WindowStateChange: 136 | case QEvent::Resize: 137 | updateMaximize(); 138 | return true; 139 | default: 140 | return QWidget::eventFilter(obj, event); 141 | } 142 | } 143 | 144 | void TitleBar::onClicked() 145 | { 146 | QPushButton *pButton = qobject_cast(sender()); 147 | QWidget *pWindow = this->window(); 148 | if(pWindow->isTopLevel()) { 149 | if(pButton == m_pMinimizeButton) { 150 | pWindow->showMinimized(); 151 | } else if(pButton == m_pMaximizeButton) { 152 | if(pWindow->isMaximized()) { 153 | pWindow->showNormal(); 154 | m_pMaximizeButton->setPixmap(QPixmap(":/images/titlebar/max.png")); 155 | } else { 156 | pWindow->showMaximized(); 157 | window()->setGeometry(QApplication::desktop()->availableGeometry()); 158 | m_pMaximizeButton->setPixmap(QPixmap(":/images/titlebar/restore.png")); 159 | } 160 | } else if(pButton == m_pCloseButton) { 161 | pWindow->close(); 162 | } 163 | } 164 | } 165 | 166 | void TitleBar::updateMaximize() 167 | { 168 | QWidget *pWindow = this->window(); 169 | if(pWindow->isTopLevel()) { 170 | bool bMaximize = pWindow->isMaximized(); 171 | if(bMaximize) { 172 | m_pMaximizeButton->setToolTip(tr("Restore")); 173 | m_pMaximizeButton->setProperty("maximizeProperty", "restore"); 174 | } else { 175 | m_pMaximizeButton->setProperty("maximizeProperty", "maximize"); 176 | m_pMaximizeButton->setToolTip(tr("Maximize")); 177 | } 178 | 179 | m_pMaximizeButton->setStyle(QApplication::style()); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /libframelesswindow/titlebar.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * titlebar.h 5 | * 自定义窗体的标题栏。 6 | * 7 | */ 8 | 9 | #ifndef TITLEBAR_H 10 | #define TITLEBAR_H 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | class QLabel; 17 | class QPushButton; 18 | class TitleBar : public QWidget 19 | { 20 | Q_OBJECT 21 | public: 22 | explicit TitleBar(QWidget *parent = nullptr); 23 | ~TitleBar(); 24 | 25 | void setMinimumVisible(bool minimum); 26 | void setMaximumVisible(bool maximum); 27 | void setMaximizeDisabled(); 28 | void hideTitleIcon(); 29 | 30 | protected: 31 | /** 32 | * @brief mouseDoubleClickEvent 33 | * 双击标题栏进行界面的最大化/还原 34 | * @param event 35 | * QMouseEvent * 36 | */ 37 | virtual void mouseDoubleClickEvent(QMouseEvent *event); 38 | 39 | // 进行鼠界面的拖动 40 | // virtual void mousePressEvent(QMouseEvent *event); 41 | 42 | /** 43 | * @brief eventFilter 44 | * @note 设置界面标题与图标 45 | * @param obj 46 | * @param event 47 | * @return 48 | * bool 49 | */ 50 | virtual bool eventFilter(QObject *obj, QEvent *event); 51 | 52 | 53 | private slots: 54 | /** 55 | * @brief onClicked 56 | * @note 进行最小化、最大化/还原、关闭操作 57 | */ 58 | void onClicked(); 59 | 60 | private: 61 | /** 62 | * @brief updateMaximize 63 | * @note 最大化/还原 64 | */ 65 | void updateMaximize(); 66 | 67 | private: 68 | QHBoxLayout *m_pMainLayout; 69 | QLabel *m_pIconLabel; 70 | QLabel *m_pTitleLabel; 71 | StateButton *m_pMinimizeButton; 72 | StateButton *m_pMaximizeButton; 73 | StateButton *m_pCloseButton; 74 | bool m_bMaximizeDisabled; 75 | }; 76 | 77 | #endif // TITLEBAR_H 78 | -------------------------------------------------------------------------------- /libframelesswindow/widgetdata.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * widgetdata.cpp 5 | * 处理鼠标事件。 6 | * 7 | */ 8 | 9 | #include "widgetdata.h" 10 | #include "framelesshelperprivate.h" 11 | #include "cursorposcalculator.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | WidgetData::WidgetData(FramelessHelperPrivate *_d, QWidget *pTopLevelWidget) 20 | { 21 | d = _d; 22 | m_pWidget = pTopLevelWidget; 23 | m_bLeftButtonPressed = false; 24 | m_bCursorShapeChanged = false; 25 | m_bLeftButtonTitlePressed = false; 26 | m_pRubberBand = NULL; 27 | 28 | m_windowFlags = m_pWidget->windowFlags(); 29 | m_pWidget->setMouseTracking(true); 30 | m_pWidget->setAttribute(Qt::WA_Hover, true); 31 | 32 | updateRubberBandStatus(); 33 | } 34 | 35 | WidgetData::~WidgetData() 36 | { 37 | m_pWidget->setMouseTracking(false); 38 | m_pWidget->setWindowFlags(m_windowFlags); 39 | m_pWidget->setAttribute(Qt::WA_Hover, false); 40 | 41 | delete m_pRubberBand; 42 | m_pRubberBand = NULL; 43 | } 44 | 45 | QWidget *WidgetData::widget() 46 | { 47 | return m_pWidget; 48 | } 49 | 50 | void WidgetData::handleWidgetEvent(QEvent *event) 51 | { 52 | switch(event->type()) { 53 | case QEvent::MouseButtonPress: 54 | handleMousePressEvent(static_cast(event)); 55 | break; 56 | 57 | case QEvent::MouseButtonRelease: 58 | handleMouseReleaseEvent(static_cast(event)); 59 | break; 60 | 61 | case QEvent::MouseMove: 62 | handleMouseMoveEvent(static_cast(event)); 63 | break; 64 | 65 | case QEvent::Leave: 66 | handleLeaveEvent(static_cast(event)); 67 | break; 68 | 69 | case QEvent::HoverMove: 70 | handleHoverMoveEvent(static_cast(event)); 71 | break; 72 | 73 | default: 74 | break; 75 | } 76 | } 77 | 78 | void WidgetData::updateRubberBandStatus() 79 | { 80 | if(d->m_bRubberBandOnMove || d->m_bRubberBandOnResize) { 81 | if(NULL == m_pRubberBand) 82 | m_pRubberBand = new QRubberBand(QRubberBand::Rectangle); 83 | } else { 84 | delete m_pRubberBand; 85 | m_pRubberBand = NULL; 86 | } 87 | } 88 | 89 | void WidgetData::handleMousePressEvent(QMouseEvent *event) 90 | { 91 | if(event->button() == Qt::LeftButton) { 92 | m_bLeftButtonPressed = true; 93 | m_bLeftButtonTitlePressed = event->pos().y() < m_moveMousePos.m_nTitleHeight; 94 | 95 | QRect frameRect = m_pWidget->frameGeometry(); 96 | m_pressedMousePos.recalculate(event->globalPos(), frameRect); 97 | 98 | m_ptDragPos = event->globalPos() - frameRect.topLeft(); 99 | m_dLeftScale = double(m_ptDragPos.x()) / double(frameRect.width()); 100 | m_nRightLength = frameRect.width() - m_ptDragPos.x(); 101 | 102 | if(m_pressedMousePos.m_bOnEdges) { 103 | if(d->m_bRubberBandOnResize) { 104 | m_pRubberBand->setGeometry(frameRect); 105 | m_pRubberBand->show(); 106 | } 107 | } else if(d->m_bRubberBandOnMove) { 108 | m_pRubberBand->setGeometry(frameRect); 109 | m_pRubberBand->show(); 110 | } 111 | } 112 | } 113 | 114 | void WidgetData::handleMouseReleaseEvent(QMouseEvent *event) 115 | { 116 | if(event->button() == Qt::LeftButton) { 117 | m_bLeftButtonPressed = false; 118 | m_bLeftButtonTitlePressed = false; 119 | m_pressedMousePos.reset(); 120 | if(m_pRubberBand && m_pRubberBand->isVisible()) { 121 | m_pRubberBand->hide(); 122 | m_pWidget->setGeometry(m_pRubberBand->geometry()); 123 | } 124 | } 125 | } 126 | 127 | void WidgetData::handleMouseMoveEvent(QMouseEvent *event) 128 | { 129 | if(m_bLeftButtonPressed) { 130 | if(d->m_bWidgetResizable && m_pressedMousePos.m_bOnEdges) { 131 | resizeWidget(event->globalPos()); 132 | } else if(d->m_bWidgetMovable && m_bLeftButtonTitlePressed) { 133 | moveWidget(event->globalPos()); 134 | } 135 | } else if(d->m_bWidgetResizable) { 136 | updateCursorShape(event->globalPos()); 137 | } 138 | } 139 | 140 | void WidgetData::handleLeaveEvent(QMouseEvent *event) 141 | { 142 | Q_UNUSED(event) 143 | if(!m_bLeftButtonPressed) { 144 | m_pWidget->unsetCursor(); 145 | } 146 | } 147 | 148 | void WidgetData::handleHoverMoveEvent(QMouseEvent *event) 149 | { 150 | if(d->m_bWidgetResizable) { 151 | updateCursorShape(m_pWidget->mapToGlobal(event->pos())); 152 | } 153 | } 154 | 155 | void WidgetData::updateCursorShape(const QPoint &gMousePos) 156 | { 157 | if(m_pWidget->isFullScreen() || m_pWidget->isMaximized()) { 158 | if(m_bCursorShapeChanged) { 159 | m_pWidget->unsetCursor(); 160 | } 161 | return; 162 | } 163 | 164 | m_moveMousePos.recalculate(gMousePos, m_pWidget->frameGeometry()); 165 | 166 | if(m_moveMousePos.m_bOnTopLeftEdge || m_moveMousePos.m_bOnBottomRightEdge) { 167 | m_pWidget->setCursor(Qt::SizeFDiagCursor); 168 | m_bCursorShapeChanged = true; 169 | } else if(m_moveMousePos.m_bOnTopRightEdge || m_moveMousePos.m_bOnBottomLeftEdge) { 170 | m_pWidget->setCursor(Qt::SizeBDiagCursor); 171 | m_bCursorShapeChanged = true; 172 | } else if(m_moveMousePos.m_bOnLeftEdge || m_moveMousePos.m_bOnRightEdge) { 173 | m_pWidget->setCursor(Qt::SizeHorCursor); 174 | m_bCursorShapeChanged = true; 175 | } else if(m_moveMousePos.m_bOnTopEdge || m_moveMousePos.m_bOnBottomEdge) { 176 | m_pWidget->setCursor(Qt::SizeVerCursor); 177 | m_bCursorShapeChanged = true; 178 | } else { 179 | if(m_bCursorShapeChanged) { 180 | m_pWidget->unsetCursor(); 181 | m_bCursorShapeChanged = false; 182 | } 183 | } 184 | } 185 | 186 | void WidgetData::resizeWidget(const QPoint &gMousePos) 187 | { 188 | QRect origRect; 189 | 190 | if(d->m_bRubberBandOnResize) 191 | origRect = m_pRubberBand->frameGeometry(); 192 | else 193 | origRect = m_pWidget->frameGeometry(); 194 | 195 | int left = origRect.left(); 196 | int top = origRect.top(); 197 | int right = origRect.right(); 198 | int bottom = origRect.bottom(); 199 | origRect.getCoords(&left, &top, &right, &bottom); 200 | 201 | int minWidth = m_pWidget->minimumWidth(); 202 | int minHeight = m_pWidget->minimumHeight(); 203 | 204 | if(m_pressedMousePos.m_bOnTopLeftEdge) { 205 | left = gMousePos.x(); 206 | top = gMousePos.y(); 207 | } else if(m_pressedMousePos.m_bOnBottomLeftEdge) { 208 | left = gMousePos.x(); 209 | bottom = gMousePos.y(); 210 | } else if(m_pressedMousePos.m_bOnTopRightEdge) { 211 | right = gMousePos.x(); 212 | top = gMousePos.y(); 213 | } else if(m_pressedMousePos.m_bOnBottomRightEdge) { 214 | right = gMousePos.x(); 215 | bottom = gMousePos.y(); 216 | } else if(m_pressedMousePos.m_bOnLeftEdge) { 217 | left = gMousePos.x(); 218 | } else if(m_pressedMousePos.m_bOnRightEdge) { 219 | right = gMousePos.x(); 220 | } else if(m_pressedMousePos.m_bOnTopEdge) { 221 | top = gMousePos.y(); 222 | } else if(m_pressedMousePos.m_bOnBottomEdge) { 223 | bottom = gMousePos.y(); 224 | } 225 | 226 | QRect newRect(QPoint(left, top), QPoint(right, bottom)); 227 | 228 | if(newRect.isValid()) { 229 | if(minWidth > newRect.width()) { 230 | if(left != origRect.left()) 231 | newRect.setLeft(origRect.left()); 232 | else 233 | newRect.setRight(origRect.right()); 234 | } 235 | if(minHeight > newRect.height()) { 236 | if(top != origRect.top()) 237 | newRect.setTop(origRect.top()); 238 | else 239 | newRect.setBottom(origRect.bottom()); 240 | } 241 | 242 | if(d->m_bRubberBandOnResize) { 243 | m_pRubberBand->setGeometry(newRect); 244 | } else { 245 | m_pWidget->setGeometry(newRect); 246 | } 247 | } 248 | } 249 | 250 | void WidgetData::moveWidget(const QPoint &gMousePos) 251 | { 252 | if(d->m_bRubberBandOnMove) { 253 | m_pRubberBand->move(gMousePos - m_ptDragPos); 254 | } else { 255 | // 如果全屏时移动窗口,窗口按点击位置还原 256 | if(m_pWidget->isMaximized() || m_pWidget->isFullScreen()) { 257 | if(m_dLeftScale <= 0.3) { } 258 | else if(m_dLeftScale > 0.3 && m_dLeftScale < 0.7) { 259 | m_ptDragPos.setX(m_pWidget->normalGeometry().width() * m_dLeftScale); 260 | } else if(m_dLeftScale >= 0.7) { 261 | m_ptDragPos.setX(m_pWidget->normalGeometry().width() - m_nRightLength); 262 | } 263 | 264 | m_pWidget->setGeometry(0, 0, m_pWidget->normalGeometry().width(), m_pWidget->normalGeometry().height()); 265 | } 266 | m_pWidget->move(gMousePos - m_ptDragPos); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /libframelesswindow/widgetdata.h: -------------------------------------------------------------------------------- 1 | #ifndef WIDGETDATA_H 2 | #define WIDGETDATA_H 3 | #include 4 | #include 5 | #include "cursorposcalculator.h" 6 | 7 | class FramelessHelperPrivate; 8 | class CursorPosCalculator; 9 | class QWidget; 10 | class QEvent; 11 | class QMouseEvent; 12 | class QRubberBand; 13 | class QPoint; 14 | 15 | class WidgetData 16 | { 17 | public: 18 | explicit WidgetData(FramelessHelperPrivate *_d, QWidget *pTopLevelWidget); 19 | ~WidgetData(); 20 | 21 | QWidget *widget(); 22 | // 处理鼠标事件-划过、按下、释放、移动 23 | void handleWidgetEvent(QEvent *event); 24 | // 更新橡皮筋状态 25 | void updateRubberBandStatus(); 26 | 27 | private: 28 | // 处理鼠标按下 29 | void handleMousePressEvent(QMouseEvent *event); 30 | // 处理鼠标释放 31 | void handleMouseReleaseEvent(QMouseEvent *event); 32 | // 处理鼠标移动 33 | void handleMouseMoveEvent(QMouseEvent *event); 34 | // 处理鼠标离开 35 | void handleLeaveEvent(QMouseEvent *event); 36 | // 处理鼠标进入 37 | void handleHoverMoveEvent(QMouseEvent *event); 38 | 39 | // 更新鼠标样式 40 | void updateCursorShape(const QPoint &gMousePos); 41 | // 重置窗口大小 42 | void resizeWidget(const QPoint &gMousePos); 43 | // 移动窗体 44 | void moveWidget(const QPoint &gMousePos); 45 | 46 | private: 47 | FramelessHelperPrivate *d; 48 | QRubberBand *m_pRubberBand; 49 | QWidget *m_pWidget; 50 | QPoint m_ptDragPos; 51 | double m_dLeftScale; // 鼠标位置距离最窗口最左边的距离占整个宽度的比例 52 | int m_nRightLength; // 鼠标位置距离最窗口最右边的距离 53 | CursorPosCalculator m_pressedMousePos; 54 | CursorPosCalculator m_moveMousePos; 55 | bool m_bLeftButtonPressed; 56 | bool m_bLeftButtonTitlePressed; 57 | bool m_bCursorShapeChanged; 58 | Qt::WindowFlags m_windowFlags; 59 | }; 60 | 61 | #endif // WIDGETDATA_H 62 | -------------------------------------------------------------------------------- /libframelesswindow/widgetshadow.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义窗口阴影 3 | * 4 | * widgetshadow.h 5 | * 6 | */ 7 | 8 | #ifndef WIDGETSHADOW_H 9 | #define WIDGETSHADOW_H 10 | 11 | #include "framelesswindow_global.h" 12 | #include "borderimage.h" 13 | #include "framelesshelper.h" 14 | #include "titlebar.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | template 26 | class WidgetShadow : public T 27 | { 28 | public: 29 | typedef typename WidgetShadow BaseClass; 30 | 31 | WidgetShadow(QWidget *parent = nullptr) 32 | :T(parent) 33 | , m_pHelper(Q_NULLPTR) 34 | , m_pTitleBar(Q_NULLPTR) 35 | , m_pMainWindow(Q_NULLPTR) 36 | , m_pMainLayout(Q_NULLPTR) 37 | , m_pFrameLessWindowLayout(Q_NULLPTR) 38 | , m_pCentralWdiget(new QWidget(this)) 39 | , m_clientDrawType(kTopLeftToBottomRight) 40 | , m_redrawPixmap(true) 41 | , m_drawedPixmap(Q_NULLPTR) 42 | { 43 | 44 | resize(800, 600); 45 | setWindowTitle("FramelessWindow"); 46 | 47 | //设置默认背景色为白色 48 | setClientColor(QColor(255, 255, 255)); 49 | //设置默认阴影边框 50 | m_borderImage.load(":/images/background/client-shadow.png", "8 8 8 8", "8 8 8 8"); 51 | 52 | m_pMainWindow = new QWidget(this); 53 | m_pMainWindow->setObjectName("framelessWindow"); 54 | m_pMainLayout = new QVBoxLayout(this); 55 | m_pMainLayout->addWidget(m_pMainWindow); 56 | m_pMainLayout->setContentsMargins(m_borderImage.margin()); 57 | 58 | setWindowFlags(Qt::FramelessWindowHint | windowFlags()); 59 | setAttribute(Qt::WA_TranslucentBackground); 60 | 61 | m_pTitleBar = new TitleBar(this); 62 | installEventFilter(m_pTitleBar);//标题栏不注册事件,注册本窗口把事件转发到标题栏 63 | setTitleHeight(m_pTitleBar->height()); 64 | 65 | m_pFrameLessWindowLayout = new QVBoxLayout(m_pMainWindow); 66 | m_pFrameLessWindowLayout->addWidget(m_pTitleBar); 67 | m_pFrameLessWindowLayout->addWidget(m_pCentralWdiget, 1); 68 | m_pFrameLessWindowLayout->setSpacing(0); 69 | m_pFrameLessWindowLayout->setContentsMargins(0, 0, 0, 0); 70 | 71 | m_pHelper = new FramelessHelper(this); 72 | m_pHelper->activateOn(this); //激活当前窗体 73 | 74 | //设置边框宽度 75 | m_pHelper->setBorderWidth((m_borderImage.margin().top()+m_borderImage.margin().left()+m_borderImage.margin().right()+m_borderImage.margin().bottom())/4); 76 | 77 | setWidgetMovalbe(); 78 | setWidgetResizable(); 79 | setRubberBandOnMove(false); 80 | setRubberBandOnResize(false); 81 | } 82 | 83 | ~WidgetShadow() 84 | { 85 | qDeleteAll(m_alphaCache); 86 | m_alphaCache.clear(); 87 | 88 | if(m_drawedPixmap) { 89 | delete m_drawedPixmap; 90 | m_drawedPixmap = NULL; 91 | } 92 | } 93 | 94 | enum ClientDrawType { 95 | kTopLeftToBottomRight = 1, //左下到右下 96 | kTopRightToBottomLeft //右上到左下 97 | }; 98 | 99 | /** 100 | * @brief setStyleSheetFile 101 | * @note 设置QSS样式文件 102 | * @param file 103 | */ 104 | void setStyleSheetFile(const QString &file) 105 | { 106 | QFile qss(file); 107 | qss.open(QFile::ReadOnly); 108 | this->setStyleSheet(qss.readAll()); 109 | qss.close(); 110 | } 111 | 112 | /** 113 | * @brief setTitleHeight 114 | * @note 设置标题栏高度,如果设置了窗口可移动,拖动标题栏可以移动窗体 115 | * @param h 116 | * @note 标题栏的高度,默认是25 117 | */ 118 | void setTitleHeight(int h = 25) 119 | { 120 | m_pHelper->setTitleHeight(h); 121 | } 122 | 123 | /** 124 | * @brief setWidgetMovalbe 125 | * @note 设置窗口是否可移动,默认可移动 126 | * @param movable 127 | */ 128 | void setWidgetMovalbe(bool movable = true) 129 | { 130 | m_pHelper->setWidgetMovable(movable); 131 | } 132 | 133 | /** 134 | * @brief setWidgetResizable 135 | * @note 设置窗口是否可缩放,默认是可以进行缩放 136 | * @param resizable 137 | */ 138 | void setWidgetResizable(bool resizable = true) 139 | { 140 | m_pHelper->setWidgetResizable(resizable); 141 | } 142 | 143 | /** 144 | * @brief setMinimumVisible 145 | * @note 设置窗口标题栏最小化按钮是否可见 146 | * @param vislble 147 | */ 148 | void setMinimumVisible(bool vislble = true) 149 | { 150 | m_pTitleBar->setMinimumVisible(vislble); 151 | } 152 | 153 | /** 154 | * @brief setMaximumVisible 155 | * @note 设置窗口标题栏最大化或还原按钮是否可见 156 | * @param visible 157 | */ 158 | void setMaximumVisible(bool visible = true) 159 | { 160 | m_pTitleBar->setMaximumVisible(visible); 161 | } 162 | 163 | /** 164 | * @brief setRubberBandOnMove 165 | * @note 设置窗口缩放时橡皮筋是否可移动,默认是可移动 166 | * @param rubber 167 | */ 168 | void setRubberBandOnMove(bool move = true) 169 | { 170 | m_pHelper->setRubberBandOnMove(move); 171 | } 172 | 173 | /** 174 | * @brief setRubberBandOnResize 175 | * @note 设置窗口缩放时橡皮筋是否可缩放,默认可缩放 176 | * @param resize 177 | */ 178 | void setRubberBandOnResize(bool resize = true) 179 | { 180 | m_pHelper->setRubberBandOnResize(resize); 181 | } 182 | 183 | /** 184 | * @brief setCentralWidget 185 | * @note 设置中心界面 186 | * @param w 187 | * QWidget * 188 | */ 189 | void setCentralWidget(QWidget *w) 190 | { 191 | m_pCentralWdiget->deleteLater(); 192 | m_pCentralWdiget = w; 193 | m_pFrameLessWindowLayout->addWidget(w, 1); 194 | } 195 | 196 | 197 | /** 198 | * @brief setBorderImage 199 | * @param image 200 | * @note 设置边框图片或阴影 201 | */ 202 | void setBorderImage(const QString &image, const QMargins& border = {8,8,8,8}, const QMargins& margin = {6,6,6,6}) 203 | { 204 | m_borderImage.setPixmap(image); 205 | m_borderImage.setBorder(border); 206 | m_borderImage.setMargin(margin); 207 | update(); 208 | } 209 | 210 | /** 211 | * @brief setClientImage 212 | * @param file 213 | * @note 设置窗口背景片 214 | */ 215 | void setClientImage(const QString &image) 216 | { 217 | m_clientPixmap.load(image); 218 | update(); 219 | } 220 | 221 | /** 222 | * @brief setClientColor 223 | * @param color 224 | * @note 设置背景颜色 225 | */ 226 | void setClientColor(const QColor &color) 227 | { 228 | //画一个1*1大小的纯色图片 229 | QPixmap pixmap(1,1); //作为绘图设备 230 | QPainter painter(&pixmap); //创建一直画笔 231 | painter.fillRect(0,0,1,1, color); 232 | m_clientPixmap = pixmap; 233 | 234 | update(); 235 | } 236 | 237 | /** 238 | * @brief clientDrawType 239 | * @note 客户区背景绘制方式 240 | * @return 241 | */ 242 | inline ClientDrawType clientDrawType() const 243 | { 244 | return m_clientDrawType; 245 | } 246 | void setClientDrawType(ClientDrawType type) 247 | { 248 | m_clientDrawType = type; 249 | } 250 | 251 | /** 252 | * @brief hideTitleBar 253 | * @note 不显示标题栏 254 | */ 255 | void hideTitleBar() 256 | { 257 | m_pFrameLessWindowLayout->removeWidget(m_pTitleBar); 258 | m_pTitleBar->deleteLater(); 259 | m_pTitleBar = Q_NULLPTR; 260 | } 261 | 262 | /** 263 | * @brief hideTitleBarIcon 264 | * @note 隐藏标题栏icon 265 | */ 266 | void hideTitleBarIcon() 267 | { 268 | m_pTitleBar->hideTitleIcon(); 269 | } 270 | 271 | /** 272 | * @brief 除去边框后的客户区rect 273 | * @return 274 | */ 275 | QRect clientRect() const 276 | { 277 | QRect clientRect(rect()); 278 | if(!isMaximized()) { 279 | const QMargins& m = m_borderImage.margin(); 280 | clientRect.adjust(m.left(), m.top(), -m.right(), -m.bottom()); 281 | } 282 | 283 | return clientRect; 284 | } 285 | 286 | /** 287 | * @brief 画背景图, 左上角画原始图,右下角拉伸 288 | * @param painter 289 | * @param rect 290 | * @param img 291 | */ 292 | void drawTopLeft(QPainter *painter, const QRect& rect, const QPixmap& pixmap) 293 | { 294 | //图片比要画的区域大,剪辑 295 | int width = qMin(pixmap.width(), rect.width()); 296 | int height = qMin(pixmap.height(), rect.height()); 297 | 298 | painter->drawPixmap(rect.left(), rect.top(), pixmap, 0, 0, width, height); 299 | 300 | //图片宽度比要画的区域小,拉伸宽度 301 | if(width < rect.width()) { 302 | QRect dst(rect.left() + width, rect.top(), rect.width() - width, height/*r.height()*/); 303 | QRect src(width - 1, 0, 1, height); 304 | painter->drawPixmap(dst, pixmap, src); 305 | } 306 | 307 | //图片高度比要画的区域小,拉伸高度 308 | if(height < rect.height()) { 309 | QRect dst(rect.left(), rect.top() + height, width/*r.width()*/, rect.height() - height); 310 | QRect src(0, height - 1, width, 1); 311 | painter->drawPixmap(dst, pixmap, src); 312 | } 313 | 314 | //图片高度、宽度都比要画的区域小,拉伸最右下角 315 | if(width < rect.width() && height < rect.height()) { 316 | QRect dst(rect.left() + width, rect.top() + height, rect.width() - width, rect.height() - height); 317 | QRect src(width - 1, height - 1, 1, 1); //用最后一个点拉伸 318 | painter->drawPixmap(dst, pixmap, src); 319 | } 320 | } 321 | 322 | /** 323 | * @brief 画背景图, 右上角画原始图,左下角拉伸 324 | * @param painter 325 | * @param rect 326 | * @param img 327 | */ 328 | void drawTopRight(QPainter *painter, const QRect& rect, const QPixmap& pixmap) 329 | { 330 | int width = qMin(pixmap.width(), rect.width()); 331 | int height = qMin(rect.height(), pixmap.height()); 332 | 333 | //左上, 图的左边往左拉伸 334 | if(rect.width() > pixmap.width()) { 335 | QRect src(0, 0, 1, height); 336 | 337 | QRect dst(rect.left(), rect.top(), rect.width() - pixmap.width(), height); 338 | painter->drawPixmap(dst, pixmap, src); 339 | } 340 | 341 | //右上() 342 | { 343 | QRect src(pixmap.width() - width, 0, width, height); 344 | QRect dst(rect.left() + rect.width() - width, rect.top(), width, height); 345 | painter->drawPixmap(dst, pixmap, src); 346 | } 347 | 348 | //左下(拉伸) 349 | if(rect.width() > pixmap.width() && rect.height() > pixmap.height()) { 350 | QRect src(0, height - 1, 1, 1); 351 | QRect dst(0, height, rect.width() - pixmap.width(), rect.height() - pixmap.height()); 352 | painter->drawPixmap(dst, pixmap, src); 353 | } 354 | 355 | //右下(拉伸), 图的下边往下拉伸 356 | if(rect.height() > pixmap.height()) { 357 | QRect src(pixmap.width() - width, height - 1, width, 1); 358 | QRect dst(rect.left() + rect.width() - width, height, width, rect.height() - pixmap.height()); 359 | painter->drawPixmap(dst, pixmap, src); 360 | } 361 | } 362 | 363 | protected: 364 | virtual void resizeEvent(QResizeEvent *event) 365 | { 366 | if(event->size() == event->oldSize()) { 367 | return; 368 | } 369 | 370 | m_redrawPixmap = true; 371 | 372 | //判断是否最大化 373 | //这里没有使用 isMaximized因为有时候不准确 374 | if(QApplication::desktop()->availableGeometry().width() == geometry().width() && \ 375 | QApplication::desktop()->availableGeometry().height() == geometry().height()) { 376 | //无圆角,并禁止改变窗口大小 377 | this->clearMask(); 378 | //最大化后,无边框无边距 379 | m_pMainLayout->setContentsMargins(0, 0, 0, 0); 380 | return; 381 | 382 | } else { 383 | //恢复窗口的边框边距 384 | if(m_pMainLayout->contentsMargins().isNull()) { 385 | m_pMainLayout->setContentsMargins(m_borderImage.margin()); 386 | } 387 | } 388 | 389 | #if 1 390 | //圆角窗口 391 | QBitmap pixmap(event->size()); //生成一张位图 392 | QPainter painter(&pixmap); //QPainter用于在位图上绘画 393 | // 圆角平滑 394 | painter.setRenderHints(QPainter::Antialiasing, true); 395 | 396 | //填充位图矩形框(用白色填充) 397 | QRect r = this->rect(); 398 | painter.fillRect(r, Qt::color0); 399 | painter.setBrush(Qt::color1); 400 | //在位图上画圆角矩形(用黑色填充) 401 | painter.drawRoundedRect(r, 0, 0); 402 | painter.end(); 403 | 404 | //使用setmask过滤即可 405 | setMask(pixmap); 406 | #endif 407 | } 408 | 409 | virtual void paintEvent(QPaintEvent *event) 410 | { 411 | if(m_redrawPixmap || !m_drawedPixmap) { 412 | m_redrawPixmap = false; 413 | qDeleteAll(m_alphaCache); 414 | m_alphaCache.clear(); 415 | 416 | QRect rect = this->rect(); 417 | 418 | delete m_drawedPixmap; //it's safe to delete null 419 | m_drawedPixmap = new QPixmap(rect.width(), rect.height()); 420 | m_drawedPixmap->fill(Qt::transparent);//Qt::black 421 | 422 | QPainter painter(m_drawedPixmap); 423 | painter.setRenderHint(QPainter::Antialiasing, true); 424 | 425 | //边框背景图 426 | const QMargins &m = m_borderImage.border(); 427 | if(isMaximized()) { //最大化后,无边框 428 | //考虑只有一个borderimage作为背景的情况,这种情况最大化后就需要用borderimage去掉边框后作为背景图 429 | //把rect放大正好使边框看不见. 430 | rect.adjust(-m.left(), -m.top(), m.right(), m.bottom()); 431 | } 432 | qDrawBorderPixmap(&painter, rect, m, m_borderImage.pixmap()); 433 | 434 | //客户区图 435 | const QPixmap& bmp = m_clientPixmap; 436 | if(!bmp.isNull()) { 437 | 438 | rect = clientRect(); 439 | 440 | QPixmap pixmap(rect.size()); 441 | { 442 | QPainter p(&pixmap); 443 | //1-从左上固定,右下拉伸;2-右上固定,左下拉伸 444 | switch(clientDrawType()) { 445 | case kTopLeftToBottomRight: 446 | drawTopLeft(&p, pixmap.rect(), bmp); 447 | break; 448 | case kTopRightToBottomLeft: 449 | drawTopRight(&p, pixmap.rect(), bmp); 450 | break; 451 | default: 452 | p.drawPixmap(pixmap.rect(), bmp); 453 | break; 454 | } 455 | } 456 | 457 | painter.setCompositionMode(QPainter::CompositionMode_DestinationOver);//CompositionMode_DestinationAtop,CompositionMode_SoftLight,CompositionMode_Multiply 458 | painter.drawPixmap(rect.left(), rect.top(), pixmap); 459 | } 460 | } 461 | 462 | QPainter painter(this); 463 | painter.drawPixmap(0, 0, *m_drawedPixmap); 464 | } 465 | 466 | protected: 467 | FramelessHelper *m_pHelper; 468 | TitleBar *m_pTitleBar; 469 | QWidget *m_pMainWindow; 470 | QVBoxLayout *m_pMainLayout; 471 | QVBoxLayout *m_pFrameLessWindowLayout; 472 | QWidget *m_pCentralWdiget; 473 | bool m_redrawPixmap; //是否需要重新创建客户区图像 474 | QPixmap *m_drawedPixmap; //画好的背景图像 475 | QHash m_alphaCache; //保存子控件alpha透明后的背景图 476 | QPixmap m_clientPixmap; //背景图片 477 | ClientDrawType m_clientDrawType; //背景图片绘制方式 478 | BorderImage m_borderImage; //阴影边框 479 | }; 480 | 481 | #endif // WIDGETSHADOW_H 482 | -------------------------------------------------------------------------------- /libtest/libTest.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | TARGET = libTest 4 | 5 | #DEFINES += HAVE_WINDOW_AERO 6 | 7 | include(../libFramelessWindow/libFramelessWindow.pri) 8 | 9 | DESTDIR = $$PROJECT_BINDIR 10 | 11 | unix:QMAKE_RPATHDIR += $$PROJECT_LIBDIR 12 | 13 | QT += widgets 14 | 15 | SOURCES += \ 16 | main.cpp \ 17 | mainwindow.cpp \ 18 | 19 | HEADERS += \ 20 | mainwindow.h \ 21 | stylesheethelper.h \ 22 | 23 | RESOURCES += \ 24 | style.qrc 25 | 26 | -------------------------------------------------------------------------------- /libtest/main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * main.cpp 5 | * 测试入口函数。 6 | * 7 | * FlyWM_ 8 | * GitHub: https://github.com/FlyWM 9 | * CSDN: https://blog.csdn.net/a844651990 10 | * 11 | */ 12 | 13 | #include 14 | #include "mainwindow.h" 15 | #include "stylesheethelper.h" 16 | 17 | int main(int argc, char *argv[]) 18 | { 19 | QApplication a(argc, argv); 20 | 21 | StyleSheetHelper::setStyle(":/style.qss"); 22 | 23 | MainWindow w; 24 | w.show(); 25 | 26 | return a.exec(); 27 | } 28 | -------------------------------------------------------------------------------- /libtest/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库/测试程序 3 | * 4 | * mainwindow.cpp 5 | * 测试窗体。 6 | * 7 | */ 8 | 9 | #include "mainwindow.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | MainWindow::MainWindow(QWidget *parent) 16 | : FramelessWindow(parent) 17 | { 18 | setWindowTitle(tr("Custom Frameless Window")); 19 | setRubberBandOnMove(false); 20 | hideTitleBar(); 21 | 22 | setStyleSheetFile(":/style/style_white.qss"); 23 | 24 | QWidget *pMainWidget = new QWidget(this); 25 | 26 | QWidget *leftWidget = new QWidget(this); 27 | leftWidget->setFixedWidth(64); 28 | leftWidget->setStyleSheet("background-color:#03A9F4;"); 29 | 30 | QWidget *rightWidget = new QWidget(this); 31 | rightWidget->setStyleSheet("background-color:#EEEEEE;"); 32 | 33 | TitleBar *titleBar = new TitleBar(this); 34 | 35 | QWidget *rigthContext = new QWidget(this); 36 | rigthContext->setStyleSheet("background-color:#FFFFFF;"); 37 | 38 | QVBoxLayout *rightLayout = new QVBoxLayout(rightWidget); 39 | rightLayout->setMargin(0); 40 | rightLayout->setSpacing(0); 41 | rightLayout->addWidget(titleBar); 42 | rightLayout->addWidget(rigthContext); 43 | rightLayout->addStretch(); 44 | 45 | 46 | QHBoxLayout *pLayout = new QHBoxLayout(pMainWidget); 47 | pLayout->addWidget(leftWidget); 48 | pLayout->addWidget(rightWidget); 49 | pLayout->setMargin(0); 50 | pLayout->setSpacing(0); 51 | 52 | setCentralWidget(pMainWidget); 53 | 54 | } 55 | -------------------------------------------------------------------------------- /libtest/mainwindow.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * mainwindow.h 5 | * 测试窗体。 6 | * 7 | * FlyWM_ 8 | * GitHub: https://github.com/FlyWM 9 | * CSDN: https://blog.csdn.net/a844651990 10 | * 11 | */ 12 | 13 | 14 | #ifndef MAINWINDOW_H 15 | #define MAINWINDOW_H 16 | 17 | #include 18 | #include 19 | 20 | class MainWindow : public FramelessWindow 21 | { 22 | public: 23 | MainWindow(QWidget *parent = nullptr); 24 | 25 | 26 | 27 | }; 28 | 29 | #endif // MAINWINDOW_H 30 | -------------------------------------------------------------------------------- /libtest/style.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | style.qss 4 | 5 | 6 | -------------------------------------------------------------------------------- /libtest/style.qss: -------------------------------------------------------------------------------- 1 | QPushButton#openDlgBtn { 2 | background: lightgreen; 3 | border: none; 4 | } 5 | -------------------------------------------------------------------------------- /libtest/stylesheethelper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义无边框窗体、对话框和提示框并封装成库 3 | * 4 | * stylesheethelper.h 5 | * 加载qss文件。 6 | * 7 | * FlyWM_ 8 | * GitHub: https://github.com/FlyWM 9 | * CSDN: https://blog.csdn.net/a844651990 10 | * 11 | */ 12 | 13 | #ifndef LSSTYLESHEETHELPER_H 14 | #define LSSTYLESHEETHELPER_H 15 | 16 | #include 17 | #include 18 | 19 | class StyleSheetHelper 20 | { 21 | public: 22 | static void setStyle(const QString &qssFile) 23 | { 24 | QFile qss(qssFile); 25 | qss.open(QFile::ReadOnly); 26 | qApp->setStyleSheet(qss.readAll()); 27 | qss.close(); 28 | } 29 | }; 30 | 31 | #endif // LSSTYLESHEETHELPER_H 32 | -------------------------------------------------------------------------------- /preview_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangqiangcc/QtFramelessWindow/bdf1a0e49bb7ddf15da62f09a61b6da14cb5c383/preview_1.png --------------------------------------------------------------------------------