diff --git a/0snap/frameless.gif b/0snap/frameless.gif new file mode 100644 index 0000000..fe07065 Binary files /dev/null and b/0snap/frameless.gif differ diff --git a/QWidgetDemo.pro b/QWidgetDemo.pro index 4dadbac..d511f2e 100644 --- a/QWidgetDemo.pro +++ b/QWidgetDemo.pro @@ -1,6 +1,8 @@ TEMPLATE = subdirs #定义了ordered表示子项目按照添加的顺序来编译 CONFIG += ordered + +#挨个添加子项目 SUBDIRS += lightbutton #高亮按钮控件 SUBDIRS += movewidget #通用控件移动类 SUBDIRS += flatui #模仿flatui类 @@ -35,7 +37,9 @@ SUBDIRS += imageswitch #图片开关控件 SUBDIRS += netserver #网络中转服务器 SUBDIRS += base64 #图片文字base64互换 SUBDIRS += smoothcurve #平滑曲线 +SUBDIRS += frameless #无边框方案支持win、linux、mac等 +#限定windows系统加载下面的项目 win32 { SUBDIRS += ffmpegdemo #视频流播放ffmpeg内核 SUBDIRS += vlcdemo #视频流播放vlc内核 @@ -46,8 +50,10 @@ SUBDIRS += miniblink #miniblink示例 #如果你电脑对应的Qt版本有webkit或者webengine组件可以自行打开 #SUBDIRS += echartgauge #echart仪表盘含交互支持webkit及webengine -lessThan(QT_MAJOR_VERSION, 4) { -#SUBDIRS += designer #QtDesigner4源码 +#这个项目依赖QtDesigner模块,一般在arm上默认没有这个模块需要自行编译 +#所以arm上如果没有QtDesigner模块不要加载下面这个子项目 +lessThan(QT_MAJOR_VERSION, 5) { +SUBDIRS += designer #QtDesigner4源码 } #qwt项目需要等官方适配了qwt组件才能适配 diff --git a/README.md b/README.md index 3fca264..6ce02bd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ 1. **可以选择打开QWidgetDemo.pro一次性编译所有的,也可以进入到目录下打开pro进行编译。** 2. **如果发现有些子项目没有加载请打开QWidgetDemo.pro仔细看里面的注释。** 3. **编译好的可执行文件在源码同级目录下的bin目录。** -4. **亲测Qt4.6到Qt6.1所有版本,亲测win、linux、mac、uos等系统。** +4. **亲测Qt4.6到Qt6.2所有版本,亲测win、linux、mac、uos等系统。** | 编号 | 文件夹 | 描述 | | ------ | ------ | ------ | @@ -49,6 +49,7 @@ | 41 | miniblink | miniblink示例 | | 42 | base64 | 图片文字base64互换 | | 43 | smoothcurve | 平滑曲线 | +| 44 | frameless | 地表最强最简单无边框方案 | ### 二、学习群 1. **Qt交流大会群 853086607(雨田哥)** @@ -99,4 +100,5 @@ ![avatar](https://gitee.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/designer.png) ![avatar](https://gitee.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/miniblink.jpg) ![avatar](https://gitee.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/base64.png) -![avatar](https://gitee.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/smoothcurve.gif) \ No newline at end of file +![avatar](https://gitee.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/smoothcurve.gif) +![avatar](https://gitee.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/frameless.gif) \ No newline at end of file diff --git a/core_common/appinit.cpp b/core_common/appinit.cpp new file mode 100644 index 0000000..ab7f9df --- /dev/null +++ b/core_common/appinit.cpp @@ -0,0 +1,56 @@ +#include "appinit.h" +#include "qmutex.h" +#include "qapplication.h" +#include "qevent.h" +#include "qwidget.h" + +QScopedPointer AppInit::self; +AppInit *AppInit::Instance() +{ + if (self.isNull()) { + static QMutex mutex; + QMutexLocker locker(&mutex); + if (self.isNull()) { + self.reset(new AppInit); + } + } + + return self.data(); +} + +AppInit::AppInit(QObject *parent) : QObject(parent) +{ +} + +bool AppInit::eventFilter(QObject *watched, QEvent *event) +{ + QWidget *w = (QWidget *)watched; + if (!w->property("canMove").toBool()) { + return QObject::eventFilter(watched, event); + } + + static QPoint mousePoint; + static bool mousePressed = false; + + QMouseEvent *mouseEvent = static_cast(event); + if (mouseEvent->type() == QEvent::MouseButtonPress) { + if (mouseEvent->button() == Qt::LeftButton) { + mousePressed = true; + mousePoint = mouseEvent->globalPos() - w->pos(); + } + } else if (mouseEvent->type() == QEvent::MouseButtonRelease) { + mousePressed = false; + } else if (mouseEvent->type() == QEvent::MouseMove) { + if (mousePressed) { + w->move(mouseEvent->globalPos() - mousePoint); + return true; + } + } + + return QObject::eventFilter(watched, event); +} + +void AppInit::start() +{ + qApp->installEventFilter(this); +} diff --git a/core_common/appinit.h b/core_common/appinit.h new file mode 100644 index 0000000..467ad84 --- /dev/null +++ b/core_common/appinit.h @@ -0,0 +1,23 @@ +#ifndef APPINIT_H +#define APPINIT_H + +#include + +class AppInit : public QObject +{ + Q_OBJECT +public: + static AppInit *Instance(); + explicit AppInit(QObject *parent = 0); + +protected: + bool eventFilter(QObject *watched, QEvent *event); + +private: + static QScopedPointer self; + +public slots: + void start(); +}; + +#endif // APPINIT_H diff --git a/core_common/common.qrc b/core_common/common.qrc new file mode 100644 index 0000000..827aaf3 --- /dev/null +++ b/core_common/common.qrc @@ -0,0 +1,5 @@ + + + image/fontawesome-webfont.ttf + + diff --git a/core_common/core_common.pri b/core_common/core_common.pri new file mode 100644 index 0000000..9183b24 --- /dev/null +++ b/core_common/core_common.pri @@ -0,0 +1,34 @@ +#指定编译产生的文件分门别类放到对应目录 +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj + +#指定编译生成的可执行文件放到源码上一级目录下的bin目录 +!android { +!wasm { +DESTDIR = $$PWD/../bin +}} + +#把所有警告都关掉眼不见为净 +CONFIG += warn_off +#开启大资源支持 +CONFIG += resources_big +#开启后会将打印信息用控制台输出 +#CONFIG += console + +#引入全志H3芯片依赖 +include ($$PWD/h3.pri) + +HEADERS += \ + $$PWD/appinit.h \ + $$PWD/iconhelper.h \ + $$PWD/quihelper.h + +SOURCES += \ + $$PWD/appinit.cpp \ + $$PWD/iconhelper.cpp \ + $$PWD/quihelper.cpp + +RESOURCES += \ + $$PWD/common.qrc diff --git a/core_common/h3.pri b/core_common/h3.pri new file mode 100644 index 0000000..d976a3e --- /dev/null +++ b/core_common/h3.pri @@ -0,0 +1,6 @@ +unix:!macx { +contains(DEFINES, arma7) { +INCLUDEPATH += /usr/local/openssl-1.0.2m-h3-gcc-4.9.2/include +LIBS += -L/usr/local/openssl-1.0.2m-h3-gcc-4.9.2/lib -lssl -lcrypto +LIBS += -L/usr/local/h3_rootfsv -lXdmcp +}} diff --git a/core_common/iconhelper.cpp b/core_common/iconhelper.cpp new file mode 100644 index 0000000..e9df4ff --- /dev/null +++ b/core_common/iconhelper.cpp @@ -0,0 +1,351 @@ +#include "iconhelper.h" + +IconHelper *IconHelper::iconFontAliBaBa = 0; +IconHelper *IconHelper::iconFontAwesome = 0; +void IconHelper::initFont() +{ + static bool isInit = false; + if (!isInit) { + isInit = true; + if (iconFontAliBaBa == 0) { + iconFontAliBaBa = new IconHelper(":/image/iconfont.ttf", "iconfont"); + } + if (iconFontAwesome == 0) { + iconFontAwesome = new IconHelper(":/image/fontawesome-webfont.ttf", "FontAwesome"); + } + } +} + +void IconHelper::setIcon(QLabel *lab, int icon, quint32 size) +{ + initFont(); + + //自动根据不同的字体的值选择对应的类,fontawesome 0xf开头 iconfont 0xe开头 + if (icon > 0xe000 && icon < 0xf000) { + iconFontAliBaBa->setIcon1(lab, icon, size); + } else if (icon > 0xf000) { + iconFontAwesome->setIcon1(lab, icon, size); + } +} + +void IconHelper::setIcon(QAbstractButton *btn, int icon, quint32 size) +{ + initFont(); + + //自动根据不同的字体的值选择对应的类,fontawesome 0xf开头 iconfont 0xe开头 + if (icon > 0xe000 && icon < 0xf000) { + iconFontAliBaBa->setIcon1(btn, icon, size); + } else if (icon > 0xf000) { + iconFontAwesome->setIcon1(btn, icon, size); + } +} + +void IconHelper::setPixmap(QAbstractButton *btn, const QColor &color, int icon, quint32 size, + quint32 width, quint32 height, int flags) +{ + initFont(); + + //自动根据不同的字体的值选择对应的类,fontawesome 0xf开头 iconfont 0xe开头 + if (icon > 0xe000 && icon < 0xf000) { + iconFontAliBaBa->setPixmap1(btn, color, icon, size, width, height, flags); + } else if (icon > 0xf000) { + iconFontAwesome->setPixmap1(btn, color, icon, size, width, height, flags); + } +} + +QPixmap IconHelper::getPixmap(const QColor &color, int icon, quint32 size, + quint32 width, quint32 height, int flags) +{ + initFont(); + + //自动根据不同的字体的值选择对应的类,fontawesome 0xf开头 iconfont 0xe开头 + QPixmap pix; + if (icon > 0xe000 && icon < 0xf000) { + pix = iconFontAliBaBa->getPixmap1(color, icon, size, width, height, flags); + } else if (icon > 0xf000) { + pix = iconFontAwesome->getPixmap1(color, icon, size, width, height, flags); + } + return pix; +} + +void IconHelper::setStyle(QWidget *widget, QList btns, + QList icons, const IconHelper::StyleColor &styleColor) +{ + initFont(); + + //自动根据不同的字体的值选择对应的类,fontawesome 0xf开头 iconfont 0xe开头 + int icon = icons.first(); + if (icon > 0xe000 && icon < 0xf000) { + iconFontAliBaBa->setStyle1(widget, btns, icons, styleColor); + } else if (icon > 0xf000) { + iconFontAwesome->setStyle1(widget, btns, icons, styleColor); + } +} + +void IconHelper::setStyle(QWidget *widget, QList btns, + QList icons, const IconHelper::StyleColor &styleColor) +{ + initFont(); + + //自动根据不同的字体的值选择对应的类,fontawesome 0xf开头 iconfont 0xe开头 + int icon = icons.first(); + if (icon > 0xe000 && icon < 0xf000) { + iconFontAliBaBa->setStyle1(widget, btns, icons, styleColor); + } else if (icon > 0xf000) { + iconFontAwesome->setStyle1(widget, btns, icons, styleColor); + } +} + +void IconHelper::setStyle(QWidget *widget, QList btns, + QList icons, const IconHelper::StyleColor &styleColor) +{ + initFont(); + + //自动根据不同的字体的值选择对应的类,fontawesome 0xf开头 iconfont 0xe开头 + int icon = icons.first(); + if (icon > 0xe000 && icon < 0xf000) { + iconFontAliBaBa->setStyle1(widget, btns, icons, styleColor); + } else if (icon > 0xf000) { + iconFontAwesome->setStyle1(widget, btns, icons, styleColor); + } +} + +IconHelper::IconHelper(const QString &fontFile, const QString &fontName, QObject *parent) : QObject(parent) +{ + //判断图形字体是否存在,不存在则加入 + QFontDatabase fontDb; + if (!fontDb.families().contains(fontName)) { + int fontId = fontDb.addApplicationFont(fontFile); + QStringList listName = fontDb.applicationFontFamilies(fontId); + if (listName.count() == 0) { + qDebug() << QString("load %1 error").arg(fontName); + } + } + + if (fontDb.families().contains(fontName)) { + iconFont = QFont(fontName); +#if (QT_VERSION >= QT_VERSION_CHECK(4,8,0)) + iconFont.setHintingPreference(QFont::PreferNoHinting); +#endif + } +} + +bool IconHelper::eventFilter(QObject *watched, QEvent *event) +{ + //根据不同的 + if (watched->inherits("QAbstractButton")) { + QAbstractButton *btn = (QAbstractButton *)watched; + int index = btns.indexOf(btn); + if (index >= 0) { + //不同的事件设置不同的图标,同时区分选中的和没有选中的 + if (btn->isChecked()) { + if (event->type() == QEvent::MouseButtonPress) { + QMouseEvent *mouseEvent = (QMouseEvent *)event; + if (mouseEvent->button() == Qt::LeftButton) { + btn->setIcon(QIcon(pixChecked.at(index))); + } + } else if (event->type() == QEvent::Enter) { + btn->setIcon(QIcon(pixChecked.at(index))); + } else if (event->type() == QEvent::Leave) { + btn->setIcon(QIcon(pixChecked.at(index))); + } + } else { + if (event->type() == QEvent::MouseButtonPress) { + QMouseEvent *mouseEvent = (QMouseEvent *)event; + if (mouseEvent->button() == Qt::LeftButton) { + btn->setIcon(QIcon(pixPressed.at(index))); + } + } else if (event->type() == QEvent::Enter) { + btn->setIcon(QIcon(pixHover.at(index))); + } else if (event->type() == QEvent::Leave) { + btn->setIcon(QIcon(pixNormal.at(index))); + } + } + } + } + + return QObject::eventFilter(watched, event); +} + +void IconHelper::toggled(bool checked) +{ + //选中和不选中设置不同的图标 + QAbstractButton *btn = (QAbstractButton *)sender(); + int index = btns.indexOf(btn); + if (checked) { + btn->setIcon(QIcon(pixChecked.at(index))); + } else { + btn->setIcon(QIcon(pixNormal.at(index))); + } +} + +void IconHelper::setIcon1(QLabel *lab, int icon, quint32 size) +{ + iconFont.setPixelSize(size); + lab->setFont(iconFont); + lab->setText((QChar)icon); +} + +void IconHelper::setIcon1(QAbstractButton *btn, int icon, quint32 size) +{ + iconFont.setPixelSize(size); + btn->setFont(iconFont); + btn->setText((QChar)icon); +} + +void IconHelper::setPixmap1(QAbstractButton *btn, const QColor &color, int icon, quint32 size, + quint32 width, quint32 height, int flags) +{ + btn->setIcon(getPixmap1(color, icon, size, width, height, flags)); +} + +QPixmap IconHelper::getPixmap1(const QColor &color, int icon, quint32 size, + quint32 width, quint32 height, int flags) +{ + //主动绘制图形字体到图片 + QPixmap pix(width, height); + pix.fill(Qt::transparent); + + QPainter painter; + painter.begin(&pix); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.setPen(color); + + iconFont.setPixelSize(size); + painter.setFont(iconFont); + painter.drawText(pix.rect(), flags, (QChar)icon); + painter.end(); + return pix; +} + +void IconHelper::setStyle1(QWidget *widget, QList btns, QList icons, const IconHelper::StyleColor &styleColor) +{ + QList list; + foreach (QPushButton *btn, btns) { + list << btn; + } + + setStyle(widget, list, icons, styleColor); +} + +void IconHelper::setStyle1(QWidget *widget, QList btns, QList icons, const IconHelper::StyleColor &styleColor) +{ + QList list; + foreach (QToolButton *btn, btns) { + list << btn; + } + + setStyle(widget, list, icons, styleColor); +} + +void IconHelper::setStyle1(QWidget *widget, QList btns, QList icons, const IconHelper::StyleColor &styleColor) +{ + int btnCount = btns.count(); + int iconCount = icons.count(); + if (btnCount <= 0 || iconCount <= 0 || btnCount != iconCount) { + return; + } + + QString position = styleColor.position; + quint32 iconSize = styleColor.iconSize; + quint32 iconWidth = styleColor.iconWidth; + quint32 iconHeight = styleColor.iconHeight; + quint32 borderWidth = styleColor.borderWidth; + + //根据不同的位置计算边框 + QString strBorder; + if (position == "top") { + strBorder = QString("border-width:%1px 0px 0px 0px;padding-top:%1px;padding-bottom:%2px;") + .arg(borderWidth).arg(borderWidth * 2); + } else if (position == "right") { + strBorder = QString("border-width:0px %1px 0px 0px;padding-right:%1px;padding-left:%2px;") + .arg(borderWidth).arg(borderWidth * 2); + } else if (position == "bottom") { + strBorder = QString("border-width:0px 0px %1px 0px;padding-bottom:%1px;padding-top:%2px;") + .arg(borderWidth).arg(borderWidth * 2); + } else if (position == "left") { + strBorder = QString("border-width:0px 0px 0px %1px;padding-left:%1px;padding-right:%2px;") + .arg(borderWidth).arg(borderWidth * 2); + } + + //如果图标是左侧显示则需要让没有选中的按钮左侧也有加深的边框,颜色为背景颜色 + QStringList qss; + if (styleColor.textBesideIcon) { + qss << QString("QWidget[flag=\"%1\"] QAbstractButton{border-style:solid;border-radius:0px;%2border-color:%3;color:%4;background:%5;}") + .arg(position).arg(strBorder).arg(styleColor.normalBgColor).arg(styleColor.normalTextColor).arg(styleColor.normalBgColor); + } else { + qss << QString("QWidget[flag=\"%1\"] QAbstractButton{border-style:none;border-radius:0px;padding:5px;color:%2;background:%3;}") + .arg(position).arg(styleColor.normalTextColor).arg(styleColor.normalBgColor); + } + + //悬停+按下+选中 + qss << QString("QWidget[flag=\"%1\"] QAbstractButton:hover{border-style:solid;%2border-color:%3;color:%4;background:%5;}") + .arg(position).arg(strBorder).arg(styleColor.borderColor).arg(styleColor.hoverTextColor).arg(styleColor.hoverBgColor); + qss << QString("QWidget[flag=\"%1\"] QAbstractButton:pressed{border-style:solid;%2border-color:%3;color:%4;background:%5;}") + .arg(position).arg(strBorder).arg(styleColor.borderColor).arg(styleColor.pressedTextColor).arg(styleColor.pressedBgColor); + qss << QString("QWidget[flag=\"%1\"] QAbstractButton:checked{border-style:solid;%2border-color:%3;color:%4;background:%5;}") + .arg(position).arg(strBorder).arg(styleColor.borderColor).arg(styleColor.checkedTextColor).arg(styleColor.checkedBgColor); + + //窗体背景颜色+按钮背景颜色 + qss << QString("QWidget#%1{background:%2;}") + .arg(widget->objectName()).arg(styleColor.normalBgColor); + qss << QString("QWidget>QAbstractButton{border-width:0px;background-color:%1;color:%2;}") + .arg(styleColor.normalBgColor).arg(styleColor.normalTextColor); + qss << QString("QWidget>QAbstractButton:hover{background-color:%1;color:%2;}") + .arg(styleColor.hoverBgColor).arg(styleColor.hoverTextColor); + qss << QString("QWidget>QAbstractButton:pressed{background-color:%1;color:%2;}") + .arg(styleColor.pressedBgColor).arg(styleColor.pressedTextColor); + qss << QString("QWidget>QAbstractButton:checked{background-color:%1;color:%2;}") + .arg(styleColor.checkedBgColor).arg(styleColor.checkedTextColor); + + //设置样式表 + widget->setStyleSheet(qss.join("")); + + //可能会重复调用设置所以先要移除上一次的 + for (int i = 0; i < btnCount; i++) { + for (int j = 0; j < this->btns.count(); j++) { + if (this->btns.at(j) == btns.at(i)) { + disconnect(btns.at(i), SIGNAL(toggled(bool)), this, SLOT(toggled(bool))); + this->btns.at(j)->removeEventFilter(this); + this->btns.removeAt(j); + this->pixNormal.removeAt(j); + this->pixHover.removeAt(j); + this->pixPressed.removeAt(j); + this->pixChecked.removeAt(j); + break; + } + } + } + + //存储对应按钮对象,方便鼠标移上去的时候切换图片 + int checkedIndex = -1; + for (int i = 0; i < btnCount; i++) { + int icon = icons.at(i); + QPixmap pixNormal = getPixmap1(styleColor.normalTextColor, icon, iconSize, iconWidth, iconHeight); + QPixmap pixHover = getPixmap1(styleColor.hoverTextColor, icon, iconSize, iconWidth, iconHeight); + QPixmap pixPressed = getPixmap1(styleColor.pressedTextColor, icon, iconSize, iconWidth, iconHeight); + QPixmap pixChecked = getPixmap1(styleColor.checkedTextColor, icon, iconSize, iconWidth, iconHeight); + + //记住最后选中的按钮 + QAbstractButton *btn = btns.at(i); + if (btn->isChecked()) { + checkedIndex = i; + } + + btn->setIcon(QIcon(pixNormal)); + btn->setIconSize(QSize(iconWidth, iconHeight)); + btn->installEventFilter(this); + connect(btn, SIGNAL(toggled(bool)), this, SLOT(toggled(bool))); + + this->btns << btn; + this->pixNormal << pixNormal; + this->pixHover << pixHover; + this->pixPressed << pixPressed; + this->pixChecked << pixChecked; + } + + //主动触发一下选中的按钮 + if (checkedIndex >= 0) { + QMetaObject::invokeMethod(btns.at(checkedIndex), "toggled", Q_ARG(bool, true)); + } +} diff --git a/core_common/iconhelper.h b/core_common/iconhelper.h new file mode 100644 index 0000000..22c930d --- /dev/null +++ b/core_common/iconhelper.h @@ -0,0 +1,148 @@ +#ifndef ICONHELPER_H +#define ICONHELPER_H + +#include +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) +#include +#endif + +/** + * 超级图形字体类 作者:feiyangqingyun(QQ:517216493) 2016-11-23 + * 1. 可传入多种图形字体文件 + * 2. 可设置 QLabel+QAbstractButton 图形字体 + * 3. 可设置按钮图标 + * 4. 可获取指定尺寸的图形字体图片 + * 5. 超级导航栏样式设置,带图标和效果切换+悬停颜色+按下颜色+选中颜色 + */ + +#ifdef quc +class Q_DECL_EXPORT IconHelper : public QObject +#else +class IconHelper : public QObject +#endif + +{ + Q_OBJECT + +public: + //样式颜色结构体 + struct StyleColor { + QString position; //位置 left right top bottom + bool textBesideIcon; //文字在图标左侧 + + quint32 iconSize; //图标字体尺寸 + quint32 iconWidth; //图标图片宽度 + quint32 iconHeight; //图标图片高度 + + quint32 borderWidth; //边框宽度 + QString borderColor; //边框颜色 + + QString normalBgColor; //正常背景颜色 + QString normalTextColor; //正常文字颜色 + QString hoverBgColor; //悬停背景颜色 + QString hoverTextColor; //悬停文字颜色 + QString pressedBgColor; //按下背景颜色 + QString pressedTextColor; //按下文字颜色 + QString checkedBgColor; //选中背景颜色 + QString checkedTextColor; //选中文字颜色 + + StyleColor() { + position = "left"; + textBesideIcon = false; + + iconSize = 12; + iconWidth = 15; + iconHeight = 15; + + borderWidth = 3; + borderColor = "#029FEA"; + + normalBgColor = "#292F38"; + normalTextColor = "#54626F"; + hoverBgColor = "#40444D"; + hoverTextColor = "#FDFDFD"; + pressedBgColor = "#404244"; + pressedTextColor = "#FDFDFD"; + checkedBgColor = "#44494F"; + checkedTextColor = "#FDFDFD"; + } + + //设置常规颜色 普通状态+加深状态 + void setColor(const QString &normalBgColor, + const QString &normalTextColor, + const QString &darkBgColor, + const QString &darkTextColor) { + this->normalBgColor = normalBgColor; + this->normalTextColor = normalTextColor; + this->hoverBgColor = darkBgColor; + this->hoverTextColor = darkTextColor; + this->pressedBgColor = darkBgColor; + this->pressedTextColor = darkTextColor; + this->checkedBgColor = darkBgColor; + this->checkedTextColor = darkTextColor; + } + }; + + //阿里巴巴图形字体类 + static IconHelper *iconFontAliBaBa; + //FontAwesome图形字体类 + static IconHelper *iconFontAwesome; + //初始化图形字体 + static void initFont(); + + static void setIcon(QLabel *lab, int icon, quint32 size = 12); + static void setIcon(QAbstractButton *btn, int icon, quint32 size = 12); + + static void setPixmap(QAbstractButton *btn, const QColor &color, + int icon, quint32 size = 12, + quint32 width = 15, quint32 height = 15, + int flags = Qt::AlignCenter); + static QPixmap getPixmap(const QColor &color, int icon, quint32 size = 12, + quint32 width = 15, quint32 height = 15, + int flags = Qt::AlignCenter); + + static void setStyle(QWidget *widget, QList btns, QList icons, const StyleColor &styleColor); + static void setStyle(QWidget *widget, QList btns, QList icons, const StyleColor &styleColor); + static void setStyle(QWidget *widget, QList btns, QList icons, const StyleColor &styleColor); + + //默认构造函数,传入字体文件+字体名称 + explicit IconHelper(const QString &fontFile, const QString &fontName, QObject *parent = 0); + +protected: + bool eventFilter(QObject *watched, QEvent *event); + +private: + QFont iconFont; //图形字体 + QList btns; //按钮队列 + QList pixNormal; //正常图片队列 + QList pixHover; //悬停图片队列 + QList pixPressed; //按下图片队列 + QList pixChecked; //选中图片队列 + +private slots: + //按钮选中状态切换处理 + void toggled(bool checked); + +public: + //设置图形字体到标签 + void setIcon1(QLabel *lab, int icon, quint32 size = 12); + //设置图形字体到按钮 + void setIcon1(QAbstractButton *btn, int icon, quint32 size = 12); + + //设置图形字体到图标 + void setPixmap1(QAbstractButton *btn, const QColor &color, + int icon, quint32 size = 12, + quint32 width = 15, quint32 height = 15, + int flags = Qt::AlignCenter); + //获取指定图形字体,可以指定文字大小,图片宽高,文字对齐 + QPixmap getPixmap1(const QColor &color, int icon, quint32 size = 12, + quint32 width = 15, quint32 height = 15, + int flags = Qt::AlignCenter); + + //指定导航面板样式,带图标和效果切换+悬停颜色+按下颜色+选中颜色 + void setStyle1(QWidget *widget, QList btns, QList icons, const StyleColor &styleColor); + void setStyle1(QWidget *widget, QList btns, QList icons, const StyleColor &styleColor); + void setStyle1(QWidget *widget, QList btns, QList icons, const StyleColor &styleColor); +}; + +#endif // ICONHELPER_H diff --git a/core_common/image/fontawesome-webfont.ttf b/core_common/image/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/core_common/image/fontawesome-webfont.ttf differ diff --git a/core_common/quihelper.cpp b/core_common/quihelper.cpp new file mode 100644 index 0000000..ffaafce --- /dev/null +++ b/core_common/quihelper.cpp @@ -0,0 +1,374 @@ +#include "quihelper.h" + +int QUIHelper::getScreenIndex() +{ + //需要对多个屏幕进行处理 + int screenIndex = 0; +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + int screenCount = qApp->screens().count(); +#else + int screenCount = qApp->desktop()->screenCount(); +#endif + + if (screenCount > 1) { + //找到当前鼠标所在屏幕 + QPoint pos = QCursor::pos(); + for (int i = 0; i < screenCount; ++i) { +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + if (qApp->screens().at(i)->geometry().contains(pos)) { +#else + if (qApp->desktop()->screenGeometry(i).contains(pos)) { +#endif + screenIndex = i; + break; + } + } + } + return screenIndex; +} + +QRect QUIHelper::getScreenRect(bool available) +{ + QRect rect; + int screenIndex = QUIHelper::getScreenIndex(); + if (available) { +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + rect = qApp->screens().at(screenIndex)->availableGeometry(); +#else + rect = qApp->desktop()->availableGeometry(screenIndex); +#endif + } else { +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + rect = qApp->screens().at(screenIndex)->geometry(); +#else + rect = qApp->desktop()->screenGeometry(screenIndex); +#endif + } + return rect; +} + +int QUIHelper::deskWidth() +{ + return getScreenRect().width(); +} + +int QUIHelper::deskHeight() +{ + return getScreenRect().height(); +} + +QWidget *QUIHelper::centerBaseForm = 0; +void QUIHelper::setFormInCenter(QWidget *form) +{ + int formWidth = form->width(); + int formHeight = form->height(); + + //如果=0表示采用系统桌面屏幕为参照 + QRect rect; + if (centerBaseForm == 0) { + rect = getScreenRect(); + } else { + rect = centerBaseForm->geometry(); + } + + int deskWidth = rect.width(); + int deskHeight = rect.height(); + QPoint movePoint(deskWidth / 2 - formWidth / 2 + rect.x(), deskHeight / 2 - formHeight / 2 + rect.y()); + form->move(movePoint); +} + +QString QUIHelper::appName() +{ + //没有必要每次都获取,只有当变量为空时才去获取一次 + static QString name; + if (name.isEmpty()) { + name = qApp->applicationFilePath(); + //下面的方法主要为了过滤安卓的路径 lib程序名_armeabi-v7a + QStringList list = name.split("/"); + name = list.at(list.count() - 1).split(".").at(0); + } + + return name; +} + +QString QUIHelper::appPath() +{ +#ifdef Q_OS_ANDROID + //return QString("/sdcard/Android/%1").arg(appName()); + return QString("/storage/emulated/0/%1").arg(appName()); +#else + return qApp->applicationDirPath(); +#endif +} + +QString QUIHelper::getUuid() +{ + QString uuid = QUuid::createUuid().toString(); + uuid.replace("{", ""); + uuid.replace("}", ""); + return uuid; +} + +void QUIHelper::initRand() +{ + //初始化随机数种子 + QTime t = QTime::currentTime(); + srand(t.msec() + t.second() * 1000); +} + +void QUIHelper::newDir(const QString &dirName) +{ + QString strDir = dirName; + + //如果路径中包含斜杠字符则说明是绝对路径 + //linux系统路径字符带有 / windows系统 路径字符带有 :/ + if (!strDir.startsWith("/") && !strDir.contains(":/")) { + strDir = QString("%1/%2").arg(QUIHelper::appPath()).arg(strDir); + } + + QDir dir(strDir); + if (!dir.exists()) { + dir.mkpath(strDir); + } +} + +void QUIHelper::sleep(int msec) +{ + if (msec > 0) { +#if (QT_VERSION < QT_VERSION_CHECK(5,7,0)) + QTime endTime = QTime::currentTime().addMSecs(msec); + while (QTime::currentTime() < endTime) { + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + } +#else + QThread::msleep(msec); +#endif + } +} + +void QUIHelper::setStyle() +{ + //打印下所有内置风格的名字 + qDebug() << "Qt内置的样式" << QStyleFactory::keys(); +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + qApp->setStyle(QStyleFactory::create("Fusion")); +#else + qApp->setStyle(QStyleFactory::create("Cleanlooks")); +#endif + //qApp->setPalette(QPalette("#FFFFFF")); +} + +void QUIHelper::setFont(int fontSize) +{ + QFont font; + font.setFamily("MicroSoft Yahei"); +#ifdef Q_OS_ANDROID + font.setPixelSize(15); +#elif __arm__ + font.setPixelSize(25); +#else + font.setPixelSize(fontSize); +#endif + +#ifndef rk3399 +#if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) + qApp->setFont(font); +#endif +#endif +} + +void QUIHelper::setCode(bool utf8) +{ +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#if _MSC_VER + QTextCodec *codec = QTextCodec::codecForName("gbk"); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); +#endif + QTextCodec::setCodecForLocale(codec); + QTextCodec::setCodecForCStrings(codec); + QTextCodec::setCodecForTr(codec); +#else + //如果想要控制台打印信息中文正常就注释掉这个设置 + if (utf8) { + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + QTextCodec::setCodecForLocale(codec); + } +#endif +} + +void QUIHelper::setTranslator(const QString &qmFile) +{ + QTranslator *translator = new QTranslator(qApp); + translator->load(qmFile); + qApp->installTranslator(translator); +} + +int QUIHelper::showMessageBox(const QString &info, int type, int closeSec, bool exec) +{ + int result = 0; + if (type == 0) { + showMessageBoxInfo(info, closeSec, exec); + } else if (type == 1) { + showMessageBoxError(info, closeSec, exec); + } else if (type == 2) { + result = showMessageBoxQuestion(info); + } + + return result; +} + +void QUIHelper::showMessageBoxInfo(const QString &info, int closeSec, bool exec) +{ + QMessageBox box(QMessageBox::Information, "提示", info); + box.setStandardButtons(QMessageBox::Yes); + box.setButtonText(QMessageBox::Yes, QString("确 定")); + box.exec(); + //QMessageBox::information(0, "提示", info, QMessageBox::Yes); +} + +void QUIHelper::showMessageBoxError(const QString &info, int closeSec, bool exec) +{ + QMessageBox box(QMessageBox::Critical, "错误", info); + box.setStandardButtons(QMessageBox::Yes); + box.setButtonText(QMessageBox::Yes, QString("确 定")); + box.exec(); + //QMessageBox::critical(0, "错误", info, QMessageBox::Yes); +} + +int QUIHelper::showMessageBoxQuestion(const QString &info) +{ + QMessageBox box(QMessageBox::Question, "询问", info); + box.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + box.setButtonText(QMessageBox::Yes, QString("确 定")); + box.setButtonText(QMessageBox::No, QString("取 消")); + return box.exec(); + //return QMessageBox::question(0, "询问", info, QMessageBox::Yes | QMessageBox::No); +} + +QString QUIHelper::getXorEncryptDecrypt(const QString &value, char key) +{ + //矫正范围外的数据 + if (key < 0 || key >= 127) { + key = 127; + } + + QString result = value; + int count = result.count(); + for (int i = 0; i < count; i++) { + result[i] = QChar(result.at(i).toLatin1() ^ key); + } + return result; +} + +uchar QUIHelper::getOrCode(const QByteArray &data) +{ + int len = data.length(); + uchar result = 0; + for (int i = 0; i < len; i++) { + result ^= data.at(i); + } + + return result; +} + +uchar QUIHelper::getCheckCode(const QByteArray &data) +{ + int len = data.length(); + uchar temp = 0; + for (uchar i = 0; i < len; i++) { + temp += data.at(i); + } + + return temp % 256; +} + +void QUIHelper::initTableView(QTableView *tableView, int rowHeight, bool headVisible, bool edit, bool stretchLast) +{ + //取消自动换行 + tableView->setWordWrap(false); + //超出文本不显示省略号 + tableView->setTextElideMode(Qt::ElideNone); + //奇数偶数行颜色交替 + tableView->setAlternatingRowColors(false); + //垂直表头是否可见 + tableView->verticalHeader()->setVisible(headVisible); + //选中一行表头是否加粗 + tableView->horizontalHeader()->setHighlightSections(false); + //最后一行拉伸填充 + tableView->horizontalHeader()->setStretchLastSection(stretchLast); + //行标题最小宽度尺寸 + tableView->horizontalHeader()->setMinimumSectionSize(0); + //行标题最小高度,等同于和默认行高一致 + tableView->horizontalHeader()->setFixedHeight(rowHeight); + //默认行高 + tableView->verticalHeader()->setDefaultSectionSize(rowHeight); + //选中时一行整体选中 + tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + //只允许选择单个 + tableView->setSelectionMode(QAbstractItemView::SingleSelection); + + //表头不可单击 +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + tableView->horizontalHeader()->setSectionsClickable(false); +#else + tableView->horizontalHeader()->setClickable(false); +#endif + + //鼠标按下即进入编辑模式 + if (edit) { + tableView->setEditTriggers(QAbstractItemView::CurrentChanged | QAbstractItemView::DoubleClicked); + } else { + tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); + } +} + +void QUIHelper::openFile(const QString &fileName, const QString &msg) +{ +#ifdef __arm__ + return; +#endif + if (fileName.isEmpty()) { + return; + } + if (QUIHelper::showMessageBoxQuestion(msg + "成功!确定现在就打开吗?") == QMessageBox::Yes) { + QString url = QString("file:///%1").arg(fileName); + QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode)); + } +} + +bool QUIHelper::checkIniFile(const QString &iniFile) +{ + //如果配置文件大小为0,则以初始值继续运行,并生成配置文件 + QFile file(iniFile); + if (file.size() == 0) { + return false; + } + + //如果配置文件不完整,则以初始值继续运行,并生成配置文件 + if (file.open(QFile::ReadOnly)) { + bool ok = true; + while (!file.atEnd()) { + QString line = file.readLine(); + line.replace("\r", ""); + line.replace("\n", ""); + QStringList list = line.split("="); + + if (list.count() == 2) { + if (list.at(1) == "") { + qDebug() << "ini node no value" << list.at(0); + ok = false; + break; + } + } + } + + if (!ok) { + return false; + } + } else { + return false; + } + + return true; +} diff --git a/core_common/quihelper.h b/core_common/quihelper.h new file mode 100644 index 0000000..1ce6711 --- /dev/null +++ b/core_common/quihelper.h @@ -0,0 +1,65 @@ +#ifndef QUIHELPER2_H +#define QUIHELPER2_H + +#include "head.h" + +class QUIHelper +{ +public: + //获取当前鼠标所在屏幕索引+尺寸 + static int getScreenIndex(); + static QRect getScreenRect(bool available = true); + + //获取桌面宽度高度+居中显示 + static int deskWidth(); + static int deskHeight(); + + //居中显示窗体 + //定义标志位指定是以桌面为参照还是主程序界面为参照 + static QWidget *centerBaseForm; + static void setFormInCenter(QWidget *form); + + //程序文件名称+当前所在路径 + static QString appName(); + static QString appPath(); + + //获取uuid+初始化随机数种子+新建目录+延时 + static QString getUuid(); + static void initRand(); + static void newDir(const QString &dirName); + static void sleep(int msec); + + //设置样式+字体+编码+居中+翻译 + static void setStyle(); + static void setFont(int fontSize = 12); + static void setCode(bool utf8 = true); + static void setTranslator(const QString &qmFile = ":/image/qt_zh_CN.qm"); + + //弹出框 + static int showMessageBox(const QString &info, int type = 0, int closeSec = 0, bool exec = false); + //弹出消息框 + static void showMessageBoxInfo(const QString &info, int closeSec = 0, bool exec = false); + //弹出错误框 + static void showMessageBoxError(const QString &info, int closeSec = 0, bool exec = false); + //弹出询问框 + static int showMessageBoxQuestion(const QString &info); + + //异或加密-只支持字符,如果是中文需要将其转换base64编码 + static QString getXorEncryptDecrypt(const QString &value, char key); + //异或校验 + static uchar getOrCode(const QByteArray &data); + //计算校验码 + static uchar getCheckCode(const QByteArray &data); + + //初始化表格 + static void initTableView(QTableView *tableView, int rowHeight = 25, + bool headVisible = false, bool edit = false, + bool stretchLast = true); + //打开文件带提示框 + static void openFile(const QString &fileName, const QString &msg); + + //检查ini配置文件 + static bool checkIniFile(const QString &iniFile); +}; + +#endif // QUIHELPER2_H diff --git a/core_frameless/core_frameless.pri b/core_frameless/core_frameless.pri new file mode 100644 index 0000000..3662227 --- /dev/null +++ b/core_frameless/core_frameless.pri @@ -0,0 +1,9 @@ +HEADERS += \ + $$PWD/framelessdialog.h \ + $$PWD/framelessmainwindow.h \ + $$PWD/framelesswidget.h + +SOURCES += \ + $$PWD/framelessdialog.cpp \ + $$PWD/framelessmainwindow.cpp \ + $$PWD/framelesswidget.cpp diff --git a/core_frameless/framelessdialog.cpp b/core_frameless/framelessdialog.cpp new file mode 100644 index 0000000..3c63413 --- /dev/null +++ b/core_frameless/framelessdialog.cpp @@ -0,0 +1,370 @@ +#include "framelessdialog.h" +#include "qdatetime.h" +#include "qevent.h" +#include "qdebug.h" + +#ifdef Q_OS_WIN +#include "windows.h" +#pragma comment (lib,"user32.lib") +#endif + +#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) + +FramelessDialog::FramelessDialog(QWidget *parent) : QDialog(parent) +{ + padding = 8; + moveEnable = true; + resizeEnable = true; + + mousePressed = false; + mousePoint = QPoint(0, 0); + mouseRect = QRect(0, 0, 0, 0); + + for (int i = 0; i < 8; ++i) { + pressedArea << false; + pressedRect << QRect(0, 0, 0, 0); + } + + isMin = false; + flags = this->windowFlags(); + titleBar = 0; + + //设置背景透明 官方在5.3以后才彻底修复 WA_TranslucentBackground+FramelessWindowHint 并存不绘制的BUG +#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0)) + this->setAttribute(Qt::WA_TranslucentBackground); +#endif + //设置无边框属性 + this->setWindowFlags(flags | Qt::FramelessWindowHint); + //安装事件过滤器识别拖动 + this->installEventFilter(this); + + //设置属性产生win窗体效果,移动到边缘半屏或者最大化等 + //设置以后会产生标题栏需要在下面拦截消息重新去掉 +#ifdef Q_OS_WIN + HWND hwnd = (HWND)this->winId(); + DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); + ::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION); +#endif +} + +void FramelessDialog::showEvent(QShowEvent *event) +{ + //解决有时候窗体重新显示的时候假死不刷新的BUG + setAttribute(Qt::WA_Mapped); + QDialog::showEvent(event); +} + +void FramelessDialog::doWindowStateChange(QEvent *event) +{ + //非最大化才能移动和拖动大小 + if (windowState() == Qt::WindowNoState) { + moveEnable = true; + resizeEnable = true; + } else { + moveEnable = false; + resizeEnable = false; + } + + //发出最大化最小化等改变事件,以便界面上更改对应的信息比如右上角图标和文字 + emit windowStateChange(!moveEnable); + + //解决mac系统上无边框最小化失效的BUG +#ifdef Q_OS_MACOS + if (windowState() & Qt::WindowMinimized) { + isMin = true; + } else { + if (isMin) { + //设置无边框属性 + this->setWindowFlags(flags | Qt::FramelessWindowHint); + this->setVisible(true); + isMin = false; + } + } +#endif +} + +void FramelessDialog::doResizeEvent(QEvent *event) +{ + //非win系统的无边框拉伸,win系统上已经采用了nativeEvent来处理拉伸 + //为何不统一用计算的方式因为在win上用这个方式往左拉伸会发抖妹的 +#ifndef Q_OS_WIN + if (event->type() == QEvent::Resize) { + //重新计算八个描点的区域,描点区域的作用还有就是计算鼠标坐标是否在某一个区域内 + int width = this->width(); + int height = this->height(); + + //左侧描点区域 + pressedRect[0] = QRect(0, padding, padding, height - padding * 2); + //右侧描点区域 + pressedRect[1] = QRect(width - padding, padding, padding, height - padding * 2); + //上侧描点区域 + pressedRect[2] = QRect(padding, 0, width - padding * 2, padding); + //下侧描点区域 + pressedRect[3] = QRect(padding, height - padding, width - padding * 2, padding); + + //左上角描点区域 + pressedRect[4] = QRect(0, 0, padding, padding); + //右上角描点区域 + pressedRect[5] = QRect(width - padding, 0, padding, padding); + //左下角描点区域 + pressedRect[6] = QRect(0, height - padding, padding, padding); + //右下角描点区域 + pressedRect[7] = QRect(width - padding, height - padding, padding, padding); + } else if (event->type() == QEvent::HoverMove) { + //设置对应鼠标形状,这个必须放在这里而不是下面,因为可以在鼠标没有按下的时候识别 + QHoverEvent *hoverEvent = (QHoverEvent *)event; + QPoint point = hoverEvent->pos(); + if (resizeEnable) { + if (pressedRect.at(0).contains(point)) { + this->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(1).contains(point)) { + this->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(2).contains(point)) { + this->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(3).contains(point)) { + this->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(4).contains(point)) { + this->setCursor(Qt::SizeFDiagCursor); + } else if (pressedRect.at(5).contains(point)) { + this->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(6).contains(point)) { + this->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(7).contains(point)) { + this->setCursor(Qt::SizeFDiagCursor); + } else { + this->setCursor(Qt::ArrowCursor); + } + } + + //根据当前鼠标位置,计算XY轴移动了多少 + int offsetX = point.x() - mousePoint.x(); + int offsetY = point.y() - mousePoint.y(); + + //根据按下处的位置判断是否是移动控件还是拉伸控件 + if (moveEnable && mousePressed) { + this->move(this->x() + offsetX, this->y() + offsetY); + } + + if (resizeEnable) { + int rectX = mouseRect.x(); + int rectY = mouseRect.y(); + int rectW = mouseRect.width(); + int rectH = mouseRect.height(); + + if (pressedArea.at(0)) { + int resizeW = this->width() - offsetX; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, rectY, resizeW, rectH); + } + } else if (pressedArea.at(1)) { + this->setGeometry(rectX, rectY, rectW + offsetX, rectH); + } else if (pressedArea.at(2)) { + int resizeH = this->height() - offsetY; + if (this->minimumHeight() <= resizeH) { + this->setGeometry(rectX, this->y() + offsetY, rectW, resizeH); + } + } else if (pressedArea.at(3)) { + this->setGeometry(rectX, rectY, rectW, rectH + offsetY); + } else if (pressedArea.at(4)) { + int resizeW = this->width() - offsetX; + int resizeH = this->height() - offsetY; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, this->y(), resizeW, resizeH); + } + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(5)) { + int resizeW = rectW + offsetX; + int resizeH = this->height() - offsetY; + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(6)) { + int resizeW = this->width() - offsetX; + int resizeH = rectH + offsetY; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, this->y(), resizeW, resizeH); + } + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y(), resizeW, resizeH); + } + } else if (pressedArea.at(7)) { + int resizeW = rectW + offsetX; + int resizeH = rectH + offsetY; + this->setGeometry(this->x(), this->y(), resizeW, resizeH); + } + } + } else if (event->type() == QEvent::MouseButtonPress) { + //记住鼠标按下的坐标+窗体区域 + QMouseEvent *mouseEvent = (QMouseEvent *)event; + mousePoint = mouseEvent->pos(); + mouseRect = this->geometry(); + + //判断按下的手柄的区域位置 + if (pressedRect.at(0).contains(mousePoint)) { + pressedArea[0] = true; + } else if (pressedRect.at(1).contains(mousePoint)) { + pressedArea[1] = true; + } else if (pressedRect.at(2).contains(mousePoint)) { + pressedArea[2] = true; + } else if (pressedRect.at(3).contains(mousePoint)) { + pressedArea[3] = true; + } else if (pressedRect.at(4).contains(mousePoint)) { + pressedArea[4] = true; + } else if (pressedRect.at(5).contains(mousePoint)) { + pressedArea[5] = true; + } else if (pressedRect.at(6).contains(mousePoint)) { + pressedArea[6] = true; + } else if (pressedRect.at(7).contains(mousePoint)) { + pressedArea[7] = true; + } else { + mousePressed = true; + } + } else if (event->type() == QEvent::MouseMove) { + //改成用HoverMove识别 + } else if (event->type() == QEvent::MouseButtonRelease) { + //恢复所有 + this->setCursor(Qt::ArrowCursor); + mousePressed = false; + for (int i = 0; i < 8; ++i) { + pressedArea[i] = false; + } + } +#endif +} + +bool FramelessDialog::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == this) { + if (event->type() == QEvent::WindowStateChange) { + doWindowStateChange(event); + } else { + doResizeEvent(event); + } + } else if (watched == titleBar) { + //双击标题栏发出双击信号给主界面 + //下面的 *result = HTCAPTION; 标志位也会自动识别双击标题栏 +#ifndef Q_OS_WIN + if (event->type() == QEvent::MouseButtonDblClick) { + emit titleDblClick(); + } else if (event->type() == QEvent::NonClientAreaMouseButtonDblClick) { + emit titleDblClick(); + } +#endif + } + + return QDialog::eventFilter(watched, event); +} + +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) +bool FramelessDialog::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) +#else +bool FramelessDialog::nativeEvent(const QByteArray &eventType, void *message, long *result) +#endif +{ + if (eventType == "windows_generic_MSG") { +#ifdef Q_OS_WIN + MSG *msg = static_cast(message); + //qDebug() << TIMEMS << "nativeEvent" << msg->wParam << msg->message; + + //不同的消息类型和参数进行不同的处理 + if (msg->message == WM_NCCALCSIZE) { + *result = 0; + return true; + } else if (msg->message == WM_NCHITTEST) { + //计算鼠标对应的屏幕坐标 + long x = LOWORD(msg->lParam); + long y = HIWORD(msg->lParam); + QPoint pos = mapFromGlobal(QPoint(x, y)); + + //判断当前鼠标位置在哪个区域 + bool left = pos.x() < padding; + bool right = pos.x() > width() - padding; + bool top = pos.y() < padding; + bool bottom = pos.y() > height() - padding; + + //鼠标移动到四个角,这个消息是当鼠标移动或者有鼠标键按下时候发出的 + *result = 0; + if (resizeEnable) { + if (left && top) { + *result = HTTOPLEFT; + } else if (left && bottom) { + *result = HTBOTTOMLEFT; + } else if (right && top) { + *result = HTTOPRIGHT; + } else if (right && bottom) { + *result = HTBOTTOMRIGHT; + } else if (left) { + *result = HTLEFT; + } else if (right) { + *result = HTRIGHT; + } else if (top) { + *result = HTTOP; + } else if (bottom) { + *result = HTBOTTOM; + } + } + + //先处理掉拉伸 + if (0 != *result) { + return true; + } + + //识别标题栏拖动产生半屏全屏效果 + if (titleBar != 0 && titleBar->rect().contains(pos)) { + QWidget *child = titleBar->childAt(pos); + if (!child) { + *result = HTCAPTION; + return true; + } + } + } else if (msg->wParam == PBT_APMSUSPEND && msg->message == WM_POWERBROADCAST) { + //系统休眠的时候自动最小化可以规避程序可能出现的问题 + this->showMinimized(); + } else if (msg->wParam == PBT_APMRESUMEAUTOMATIC) { + //休眠唤醒后自动打开 + this->showNormal(); + } +#endif + } else if (eventType == "NSEvent") { +#ifdef Q_OS_MACOS +#endif + } else if (eventType == "xcb_generic_event_t") { +#ifdef Q_OS_LINUX +#endif + } + return false; +} + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#ifdef Q_OS_WIN +bool FramelessDialog::winEvent(MSG *message, long *result) +{ + return nativeEvent("windows_generic_MSG", message, result); +} +#endif +#endif + +void FramelessDialog::setPadding(int padding) +{ + this->padding = padding; +} + +void FramelessDialog::setMoveEnable(bool moveEnable) +{ + this->moveEnable = moveEnable; +} + +void FramelessDialog::setResizeEnable(bool resizeEnable) +{ + this->resizeEnable = resizeEnable; +} + +void FramelessDialog::setTitleBar(QWidget *titleBar) +{ + this->titleBar = titleBar; + this->titleBar->installEventFilter(this); +} + + diff --git a/core_frameless/framelessdialog.h b/core_frameless/framelessdialog.h new file mode 100644 index 0000000..675d353 --- /dev/null +++ b/core_frameless/framelessdialog.h @@ -0,0 +1,73 @@ +#ifndef FRAMELESSDIALOG_H +#define FRAMELESSDIALOG_H + +#include + +class FramelessDialog : public QDialog +{ + Q_OBJECT +public: + explicit FramelessDialog(QWidget *parent = 0); + +protected: + //窗体显示的时候触发 + void showEvent(QShowEvent *event); + + //事件过滤器识别拖动拉伸等 + void doWindowStateChange(QEvent *event); + void doResizeEvent(QEvent *event); + bool eventFilter(QObject *watched, QEvent *event); + + //拦截系统事件用于修复系统休眠后唤醒程序的BUG +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) + bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result); +#else + bool nativeEvent(const QByteArray &eventType, void *message, long *result); +#endif + + //Qt4的写法 +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#ifdef Q_OS_WIN + bool winEvent(MSG *message, long *result); +#endif +#endif + +private: + //边距+可移动+可拉伸 + int padding; + bool moveEnable; + bool resizeEnable; + + //标题栏控件 + QWidget *titleBar; + + //鼠标是否按下+按下坐标+按下时窗体区域 + bool mousePressed; + QPoint mousePoint; + QRect mouseRect; + + //鼠标是否按下某个区域+按下区域的大小 + //依次为 左侧+右侧+上侧+下侧+左上侧+右上侧+左下侧+右下侧 + QList pressedArea; + QList pressedRect; + + //记录是否最小化 + bool isMin; + //存储窗体默认的属性 + Qt::WindowFlags flags; + +public: + //设置边距+可拖动+可拉伸 + void setPadding(int padding); + void setMoveEnable(bool moveEnable); + void setResizeEnable(bool resizeEnable); + + //设置标题栏控件 + void setTitleBar(QWidget *titleBar); + +signals: + void titleDblClick(); + void windowStateChange(bool max); +}; + +#endif // FRAMELESSDIALOG_H diff --git a/core_frameless/framelessmainwindow.cpp b/core_frameless/framelessmainwindow.cpp new file mode 100644 index 0000000..e23b030 --- /dev/null +++ b/core_frameless/framelessmainwindow.cpp @@ -0,0 +1,368 @@ +#include "framelessmainwindow.h" +#include "qdatetime.h" +#include "qevent.h" +#include "qdebug.h" + +#ifdef Q_OS_WIN +#include "windows.h" +#pragma comment (lib,"user32.lib") +#endif + +#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) + +FramelessMainWindow::FramelessMainWindow(QWidget *parent) : QMainWindow(parent) +{ + padding = 8; + moveEnable = true; + resizeEnable = true; + + mousePressed = false; + mousePoint = QPoint(0, 0); + mouseRect = QRect(0, 0, 0, 0); + + for (int i = 0; i < 8; ++i) { + pressedArea << false; + pressedRect << QRect(0, 0, 0, 0); + } + + isMin = false; + flags = this->windowFlags(); + titleBar = 0; + + //设置背景透明 官方在5.3以后才彻底修复 WA_TranslucentBackground+FramelessWindowHint 并存不绘制的BUG +#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0)) + this->setAttribute(Qt::WA_TranslucentBackground); +#endif + //设置无边框属性 + this->setWindowFlags(flags | Qt::FramelessWindowHint); + //安装事件过滤器识别拖动 + this->installEventFilter(this); + + //设置属性产生win窗体效果,移动到边缘半屏或者最大化等 + //设置以后会产生标题栏,需要在下面拦截消息WM_NCCALCSIZE重新去掉 +#ifdef Q_OS_WIN + HWND hwnd = (HWND)this->winId(); + DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); + ::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION); +#endif +} + +void FramelessMainWindow::showEvent(QShowEvent *event) +{ + //解决有时候窗体重新显示的时候假死不刷新的BUG + setAttribute(Qt::WA_Mapped); + QMainWindow::showEvent(event); +} + +void FramelessMainWindow::doWindowStateChange(QEvent *event) +{ + //非最大化才能移动和拖动大小 + if (windowState() == Qt::WindowNoState) { + moveEnable = true; + resizeEnable = true; + } else { + moveEnable = false; + resizeEnable = false; + } + + //发出最大化最小化等改变事件,以便界面上更改对应的信息比如右上角图标和文字 + emit windowStateChange(!moveEnable); + + //解决mac系统上无边框最小化失效的BUG +#ifdef Q_OS_MACOS + if (windowState() & Qt::WindowMinimized) { + isMin = true; + } else { + if (isMin) { + //设置无边框属性 + this->setWindowFlags(flags | Qt::FramelessWindowHint); + this->setVisible(true); + isMin = false; + } + } +#endif +} + +void FramelessMainWindow::doResizeEvent(QEvent *event) +{ + //非win系统的无边框拉伸,win系统上已经采用了nativeEvent来处理拉伸 + //为何不统一用计算的方式因为在win上用这个方式往左拉伸会发抖妹的 +#ifndef Q_OS_WIN + if (event->type() == QEvent::Resize) { + //重新计算八个描点的区域,描点区域的作用还有就是计算鼠标坐标是否在某一个区域内 + int width = this->width(); + int height = this->height(); + + //左侧描点区域 + pressedRect[0] = QRect(0, padding, padding, height - padding * 2); + //右侧描点区域 + pressedRect[1] = QRect(width - padding, padding, padding, height - padding * 2); + //上侧描点区域 + pressedRect[2] = QRect(padding, 0, width - padding * 2, padding); + //下侧描点区域 + pressedRect[3] = QRect(padding, height - padding, width - padding * 2, padding); + + //左上角描点区域 + pressedRect[4] = QRect(0, 0, padding, padding); + //右上角描点区域 + pressedRect[5] = QRect(width - padding, 0, padding, padding); + //左下角描点区域 + pressedRect[6] = QRect(0, height - padding, padding, padding); + //右下角描点区域 + pressedRect[7] = QRect(width - padding, height - padding, padding, padding); + } else if (event->type() == QEvent::HoverMove) { + //设置对应鼠标形状,这个必须放在这里而不是下面,因为可以在鼠标没有按下的时候识别 + QHoverEvent *hoverEvent = (QHoverEvent *)event; + QPoint point = hoverEvent->pos(); + if (resizeEnable) { + if (pressedRect.at(0).contains(point)) { + this->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(1).contains(point)) { + this->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(2).contains(point)) { + this->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(3).contains(point)) { + this->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(4).contains(point)) { + this->setCursor(Qt::SizeFDiagCursor); + } else if (pressedRect.at(5).contains(point)) { + this->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(6).contains(point)) { + this->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(7).contains(point)) { + this->setCursor(Qt::SizeFDiagCursor); + } else { + this->setCursor(Qt::ArrowCursor); + } + } + + //根据当前鼠标位置,计算XY轴移动了多少 + int offsetX = point.x() - mousePoint.x(); + int offsetY = point.y() - mousePoint.y(); + + //根据按下处的位置判断是否是移动控件还是拉伸控件 + if (moveEnable && mousePressed) { + this->move(this->x() + offsetX, this->y() + offsetY); + } + + if (resizeEnable) { + int rectX = mouseRect.x(); + int rectY = mouseRect.y(); + int rectW = mouseRect.width(); + int rectH = mouseRect.height(); + + if (pressedArea.at(0)) { + int resizeW = this->width() - offsetX; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, rectY, resizeW, rectH); + } + } else if (pressedArea.at(1)) { + this->setGeometry(rectX, rectY, rectW + offsetX, rectH); + } else if (pressedArea.at(2)) { + int resizeH = this->height() - offsetY; + if (this->minimumHeight() <= resizeH) { + this->setGeometry(rectX, this->y() + offsetY, rectW, resizeH); + } + } else if (pressedArea.at(3)) { + this->setGeometry(rectX, rectY, rectW, rectH + offsetY); + } else if (pressedArea.at(4)) { + int resizeW = this->width() - offsetX; + int resizeH = this->height() - offsetY; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, this->y(), resizeW, resizeH); + } + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(5)) { + int resizeW = rectW + offsetX; + int resizeH = this->height() - offsetY; + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(6)) { + int resizeW = this->width() - offsetX; + int resizeH = rectH + offsetY; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, this->y(), resizeW, resizeH); + } + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y(), resizeW, resizeH); + } + } else if (pressedArea.at(7)) { + int resizeW = rectW + offsetX; + int resizeH = rectH + offsetY; + this->setGeometry(this->x(), this->y(), resizeW, resizeH); + } + } + } else if (event->type() == QEvent::MouseButtonPress) { + //记住鼠标按下的坐标+窗体区域 + QMouseEvent *mouseEvent = (QMouseEvent *)event; + mousePoint = mouseEvent->pos(); + mouseRect = this->geometry(); + + //判断按下的手柄的区域位置 + if (pressedRect.at(0).contains(mousePoint)) { + pressedArea[0] = true; + } else if (pressedRect.at(1).contains(mousePoint)) { + pressedArea[1] = true; + } else if (pressedRect.at(2).contains(mousePoint)) { + pressedArea[2] = true; + } else if (pressedRect.at(3).contains(mousePoint)) { + pressedArea[3] = true; + } else if (pressedRect.at(4).contains(mousePoint)) { + pressedArea[4] = true; + } else if (pressedRect.at(5).contains(mousePoint)) { + pressedArea[5] = true; + } else if (pressedRect.at(6).contains(mousePoint)) { + pressedArea[6] = true; + } else if (pressedRect.at(7).contains(mousePoint)) { + pressedArea[7] = true; + } else { + mousePressed = true; + } + } else if (event->type() == QEvent::MouseMove) { + //改成用HoverMove识别 + } else if (event->type() == QEvent::MouseButtonRelease) { + //恢复所有 + this->setCursor(Qt::ArrowCursor); + mousePressed = false; + for (int i = 0; i < 8; ++i) { + pressedArea[i] = false; + } + } +#endif +} + +bool FramelessMainWindow::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == this) { + if (event->type() == QEvent::WindowStateChange) { + doWindowStateChange(event); + } else { + doResizeEvent(event); + } + } else if (watched == titleBar) { + //双击标题栏发出双击信号给主界面 + //下面的 *result = HTCAPTION; 标志位也会自动识别双击标题栏 +#ifndef Q_OS_WIN + if (event->type() == QEvent::MouseButtonDblClick) { + emit titleDblClick(); + } else if (event->type() == QEvent::NonClientAreaMouseButtonDblClick) { + emit titleDblClick(); + } +#endif + } + + return QMainWindow::eventFilter(watched, event); +} + +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) +bool FramelessMainWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) +#else +bool FramelessMainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) +#endif +{ + if (eventType == "windows_generic_MSG") { +#ifdef Q_OS_WIN + MSG *msg = static_cast(message); + //qDebug() << TIMEMS << "nativeEvent" << msg->wParam << msg->message; + + //不同的消息类型和参数进行不同的处理 + if (msg->message == WM_NCCALCSIZE) { + *result = 0; + return true; + } else if (msg->message == WM_NCHITTEST) { + //计算鼠标对应的屏幕坐标 + long x = LOWORD(msg->lParam); + long y = HIWORD(msg->lParam); + QPoint pos = mapFromGlobal(QPoint(x, y)); + + //判断当前鼠标位置在哪个区域 + bool left = pos.x() < padding; + bool right = pos.x() > width() - padding; + bool top = pos.y() < padding; + bool bottom = pos.y() > height() - padding; + + //鼠标移动到四个角,这个消息是当鼠标移动或者有鼠标键按下时候发出的 + *result = 0; + if (resizeEnable) { + if (left && top) { + *result = HTTOPLEFT; + } else if (left && bottom) { + *result = HTBOTTOMLEFT; + } else if (right && top) { + *result = HTTOPRIGHT; + } else if (right && bottom) { + *result = HTBOTTOMRIGHT; + } else if (left) { + *result = HTLEFT; + } else if (right) { + *result = HTRIGHT; + } else if (top) { + *result = HTTOP; + } else if (bottom) { + *result = HTBOTTOM; + } + } + + //先处理掉拉伸 + if (0 != *result) { + return true; + } + + //识别标题栏拖动产生半屏全屏效果 + if (titleBar != 0 && titleBar->rect().contains(pos)) { + QWidget *child = titleBar->childAt(pos); + if (!child) { + *result = HTCAPTION; + return true; + } + } + } else if (msg->wParam == PBT_APMSUSPEND && msg->message == WM_POWERBROADCAST) { + //系统休眠的时候自动最小化可以规避程序可能出现的问题 + this->showMinimized(); + } else if (msg->wParam == PBT_APMRESUMEAUTOMATIC) { + //休眠唤醒后自动打开 + this->showNormal(); + } +#endif + } else if (eventType == "NSEvent") { +#ifdef Q_OS_MACOS +#endif + } else if (eventType == "xcb_generic_event_t") { +#ifdef Q_OS_LINUX +#endif + } + return false; +} + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#ifdef Q_OS_WIN +bool FramelessMainWindow::winEvent(MSG *message, long *result) +{ + return nativeEvent("windows_generic_MSG", message, result); +} +#endif +#endif + +void FramelessMainWindow::setPadding(int padding) +{ + this->padding = padding; +} + +void FramelessMainWindow::setMoveEnable(bool moveEnable) +{ + this->moveEnable = moveEnable; +} + +void FramelessMainWindow::setResizeEnable(bool resizeEnable) +{ + this->resizeEnable = resizeEnable; +} + +void FramelessMainWindow::setTitleBar(QWidget *titleBar) +{ + this->titleBar = titleBar; + this->titleBar->installEventFilter(this); +} diff --git a/core_frameless/framelessmainwindow.h b/core_frameless/framelessmainwindow.h new file mode 100644 index 0000000..ba80f4e --- /dev/null +++ b/core_frameless/framelessmainwindow.h @@ -0,0 +1,73 @@ +#ifndef FRAMELESSMAINWINDOW_H +#define FRAMELESSMAINWINDOW_H + +#include + +class FramelessMainWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit FramelessMainWindow(QWidget *parent = 0); + +protected: + //窗体显示的时候触发 + void showEvent(QShowEvent *event); + + //事件过滤器识别拖动拉伸等 + void doWindowStateChange(QEvent *event); + void doResizeEvent(QEvent *event); + bool eventFilter(QObject *watched, QEvent *event); + + //拦截系统事件用于修复系统休眠后唤醒程序的BUG +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) + bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result); +#else + bool nativeEvent(const QByteArray &eventType, void *message, long *result); +#endif + + //Qt4的写法 +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#ifdef Q_OS_WIN + bool winEvent(MSG *message, long *result); +#endif +#endif + +private: + //边距+可移动+可拉伸 + int padding; + bool moveEnable; + bool resizeEnable; + + //标题栏控件 + QWidget *titleBar; + + //鼠标是否按下+按下坐标+按下时窗体区域 + bool mousePressed; + QPoint mousePoint; + QRect mouseRect; + + //鼠标是否按下某个区域+按下区域的大小 + //依次为 左侧+右侧+上侧+下侧+左上侧+右上侧+左下侧+右下侧 + QList pressedArea; + QList pressedRect; + + //记录是否最小化 + bool isMin; + //存储窗体默认的属性 + Qt::WindowFlags flags; + +public: + //设置边距+可拖动+可拉伸 + void setPadding(int padding); + void setMoveEnable(bool moveEnable); + void setResizeEnable(bool resizeEnable); + + //设置标题栏控件 + void setTitleBar(QWidget *titleBar); + +signals: + void titleDblClick(); + void windowStateChange(bool max); +}; + +#endif // FRAMELESSMAINWINDOW_H diff --git a/core_frameless/framelesswidget.cpp b/core_frameless/framelesswidget.cpp new file mode 100644 index 0000000..2acb4ee --- /dev/null +++ b/core_frameless/framelesswidget.cpp @@ -0,0 +1,370 @@ +#include "framelesswidget.h" +#include "qdatetime.h" +#include "qevent.h" +#include "qdebug.h" + +#ifdef Q_OS_WIN +#include "windows.h" +#pragma comment (lib,"user32.lib") +#endif + +#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) + +FramelessWidget::FramelessWidget(QWidget *parent) : QWidget(parent) +{ + padding = 8; + moveEnable = true; + resizeEnable = true; + + mousePressed = false; + mousePoint = QPoint(0, 0); + mouseRect = QRect(0, 0, 0, 0); + + for (int i = 0; i < 8; ++i) { + pressedArea << false; + pressedRect << QRect(0, 0, 0, 0); + } + + isMin = false; + flags = this->windowFlags(); + titleBar = 0; + + //设置背景透明 官方在5.3以后才彻底修复 WA_TranslucentBackground+FramelessWindowHint 并存不绘制的BUG +#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0)) + this->setAttribute(Qt::WA_TranslucentBackground); +#endif + //设置无边框属性 + this->setWindowFlags(flags | Qt::FramelessWindowHint); + //安装事件过滤器识别拖动 + this->installEventFilter(this); + + //设置属性产生win窗体效果,移动到边缘半屏或者最大化等 + //设置以后会产生标题栏需要在下面拦截消息重新去掉 +#ifdef Q_OS_WIN + HWND hwnd = (HWND)this->winId(); + DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); + ::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION); +#endif +} + +void FramelessWidget::showEvent(QShowEvent *event) +{ + //解决有时候窗体重新显示的时候假死不刷新的BUG + setAttribute(Qt::WA_Mapped); + QWidget::showEvent(event); +} + +void FramelessWidget::doWindowStateChange(QEvent *event) +{ + //非最大化才能移动和拖动大小 + if (windowState() == Qt::WindowNoState) { + moveEnable = true; + resizeEnable = true; + } else { + moveEnable = false; + resizeEnable = false; + } + + //发出最大化最小化等改变事件,以便界面上更改对应的信息比如右上角图标和文字 + emit windowStateChange(!moveEnable); + + //解决mac系统上无边框最小化失效的BUG +#ifdef Q_OS_MACOS + if (windowState() & Qt::WindowMinimized) { + isMin = true; + } else { + if (isMin) { + //设置无边框属性 + this->setWindowFlags(flags | Qt::FramelessWindowHint); + this->setVisible(true); + isMin = false; + } + } +#endif +} + +void FramelessWidget::doResizeEvent(QEvent *event) +{ + //非win系统的无边框拉伸,win系统上已经采用了nativeEvent来处理拉伸 + //为何不统一用计算的方式因为在win上用这个方式往左拉伸会发抖妹的 +#ifndef Q_OS_WIN + if (event->type() == QEvent::Resize) { + //重新计算八个描点的区域,描点区域的作用还有就是计算鼠标坐标是否在某一个区域内 + int width = this->width(); + int height = this->height(); + + //左侧描点区域 + pressedRect[0] = QRect(0, padding, padding, height - padding * 2); + //右侧描点区域 + pressedRect[1] = QRect(width - padding, padding, padding, height - padding * 2); + //上侧描点区域 + pressedRect[2] = QRect(padding, 0, width - padding * 2, padding); + //下侧描点区域 + pressedRect[3] = QRect(padding, height - padding, width - padding * 2, padding); + + //左上角描点区域 + pressedRect[4] = QRect(0, 0, padding, padding); + //右上角描点区域 + pressedRect[5] = QRect(width - padding, 0, padding, padding); + //左下角描点区域 + pressedRect[6] = QRect(0, height - padding, padding, padding); + //右下角描点区域 + pressedRect[7] = QRect(width - padding, height - padding, padding, padding); + } else if (event->type() == QEvent::HoverMove) { + //设置对应鼠标形状,这个必须放在这里而不是下面,因为可以在鼠标没有按下的时候识别 + QHoverEvent *hoverEvent = (QHoverEvent *)event; + QPoint point = hoverEvent->pos(); + if (resizeEnable) { + if (pressedRect.at(0).contains(point)) { + this->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(1).contains(point)) { + this->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(2).contains(point)) { + this->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(3).contains(point)) { + this->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(4).contains(point)) { + this->setCursor(Qt::SizeFDiagCursor); + } else if (pressedRect.at(5).contains(point)) { + this->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(6).contains(point)) { + this->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(7).contains(point)) { + this->setCursor(Qt::SizeFDiagCursor); + } else { + this->setCursor(Qt::ArrowCursor); + } + } + + //根据当前鼠标位置,计算XY轴移动了多少 + int offsetX = point.x() - mousePoint.x(); + int offsetY = point.y() - mousePoint.y(); + + //根据按下处的位置判断是否是移动控件还是拉伸控件 + if (moveEnable && mousePressed) { + this->move(this->x() + offsetX, this->y() + offsetY); + } + + if (resizeEnable) { + int rectX = mouseRect.x(); + int rectY = mouseRect.y(); + int rectW = mouseRect.width(); + int rectH = mouseRect.height(); + + if (pressedArea.at(0)) { + int resizeW = this->width() - offsetX; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, rectY, resizeW, rectH); + } + } else if (pressedArea.at(1)) { + this->setGeometry(rectX, rectY, rectW + offsetX, rectH); + } else if (pressedArea.at(2)) { + int resizeH = this->height() - offsetY; + if (this->minimumHeight() <= resizeH) { + this->setGeometry(rectX, this->y() + offsetY, rectW, resizeH); + } + } else if (pressedArea.at(3)) { + this->setGeometry(rectX, rectY, rectW, rectH + offsetY); + } else if (pressedArea.at(4)) { + int resizeW = this->width() - offsetX; + int resizeH = this->height() - offsetY; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, this->y(), resizeW, resizeH); + } + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(5)) { + int resizeW = rectW + offsetX; + int resizeH = this->height() - offsetY; + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(6)) { + int resizeW = this->width() - offsetX; + int resizeH = rectH + offsetY; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, this->y(), resizeW, resizeH); + } + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y(), resizeW, resizeH); + } + } else if (pressedArea.at(7)) { + int resizeW = rectW + offsetX; + int resizeH = rectH + offsetY; + this->setGeometry(this->x(), this->y(), resizeW, resizeH); + } + } + } else if (event->type() == QEvent::MouseButtonPress) { + //记住鼠标按下的坐标+窗体区域 + QMouseEvent *mouseEvent = (QMouseEvent *)event; + mousePoint = mouseEvent->pos(); + mouseRect = this->geometry(); + + //判断按下的手柄的区域位置 + if (pressedRect.at(0).contains(mousePoint)) { + pressedArea[0] = true; + } else if (pressedRect.at(1).contains(mousePoint)) { + pressedArea[1] = true; + } else if (pressedRect.at(2).contains(mousePoint)) { + pressedArea[2] = true; + } else if (pressedRect.at(3).contains(mousePoint)) { + pressedArea[3] = true; + } else if (pressedRect.at(4).contains(mousePoint)) { + pressedArea[4] = true; + } else if (pressedRect.at(5).contains(mousePoint)) { + pressedArea[5] = true; + } else if (pressedRect.at(6).contains(mousePoint)) { + pressedArea[6] = true; + } else if (pressedRect.at(7).contains(mousePoint)) { + pressedArea[7] = true; + } else { + mousePressed = true; + } + } else if (event->type() == QEvent::MouseMove) { + //改成用HoverMove识别 + } else if (event->type() == QEvent::MouseButtonRelease) { + //恢复所有 + this->setCursor(Qt::ArrowCursor); + mousePressed = false; + for (int i = 0; i < 8; ++i) { + pressedArea[i] = false; + } + } +#endif +} + +bool FramelessWidget::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == this) { + if (event->type() == QEvent::WindowStateChange) { + doWindowStateChange(event); + } else { + doResizeEvent(event); + } + } else if (watched == titleBar) { + //双击标题栏发出双击信号给主界面 + //下面的 *result = HTCAPTION; 标志位也会自动识别双击标题栏 +#ifndef Q_OS_WIN + if (event->type() == QEvent::MouseButtonDblClick) { + emit titleDblClick(); + } else if (event->type() == QEvent::NonClientAreaMouseButtonDblClick) { + emit titleDblClick(); + } +#endif + } + + return QWidget::eventFilter(watched, event); +} + +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) +bool FramelessWidget::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) +#else +bool FramelessWidget::nativeEvent(const QByteArray &eventType, void *message, long *result) +#endif +{ + if (eventType == "windows_generic_MSG") { +#ifdef Q_OS_WIN + MSG *msg = static_cast(message); + //qDebug() << TIMEMS << "nativeEvent" << msg->wParam << msg->message; + + //不同的消息类型和参数进行不同的处理 + if (msg->message == WM_NCCALCSIZE) { + *result = 0; + return true; + } else if (msg->message == WM_NCHITTEST) { + //计算鼠标对应的屏幕坐标 + long x = LOWORD(msg->lParam); + long y = HIWORD(msg->lParam); + QPoint pos = mapFromGlobal(QPoint(x, y)); + + //判断当前鼠标位置在哪个区域 + bool left = pos.x() < padding; + bool right = pos.x() > width() - padding; + bool top = pos.y() < padding; + bool bottom = pos.y() > height() - padding; + + //鼠标移动到四个角,这个消息是当鼠标移动或者有鼠标键按下时候发出的 + *result = 0; + if (resizeEnable) { + if (left && top) { + *result = HTTOPLEFT; + } else if (left && bottom) { + *result = HTBOTTOMLEFT; + } else if (right && top) { + *result = HTTOPRIGHT; + } else if (right && bottom) { + *result = HTBOTTOMRIGHT; + } else if (left) { + *result = HTLEFT; + } else if (right) { + *result = HTRIGHT; + } else if (top) { + *result = HTTOP; + } else if (bottom) { + *result = HTBOTTOM; + } + } + + //先处理掉拉伸 + if (0 != *result) { + return true; + } + + //识别标题栏拖动产生半屏全屏效果 + if (titleBar != 0 && titleBar->rect().contains(pos)) { + QWidget *child = titleBar->childAt(pos); + if (!child) { + *result = HTCAPTION; + return true; + } + } + } else if (msg->wParam == PBT_APMSUSPEND && msg->message == WM_POWERBROADCAST) { + //系统休眠的时候自动最小化可以规避程序可能出现的问题 + this->showMinimized(); + } else if (msg->wParam == PBT_APMRESUMEAUTOMATIC) { + //休眠唤醒后自动打开 + this->showNormal(); + } +#endif + } else if (eventType == "NSEvent") { +#ifdef Q_OS_MACOS +#endif + } else if (eventType == "xcb_generic_event_t") { +#ifdef Q_OS_LINUX +#endif + } + return false; +} + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#ifdef Q_OS_WIN +bool FramelessWidget::winEvent(MSG *message, long *result) +{ + return nativeEvent("windows_generic_MSG", message, result); +} +#endif +#endif + +void FramelessWidget::setPadding(int padding) +{ + this->padding = padding; +} + +void FramelessWidget::setMoveEnable(bool moveEnable) +{ + this->moveEnable = moveEnable; +} + +void FramelessWidget::setResizeEnable(bool resizeEnable) +{ + this->resizeEnable = resizeEnable; +} + +void FramelessWidget::setTitleBar(QWidget *titleBar) +{ + this->titleBar = titleBar; + this->titleBar->installEventFilter(this); +} + + diff --git a/core_frameless/framelesswidget.h b/core_frameless/framelesswidget.h new file mode 100644 index 0000000..80d036b --- /dev/null +++ b/core_frameless/framelesswidget.h @@ -0,0 +1,73 @@ +#ifndef FRAMELESSWIDGET_H +#define FRAMELESSWIDGET_H + +#include + +class FramelessWidget : public QWidget +{ + Q_OBJECT +public: + explicit FramelessWidget(QWidget *parent = 0); + +protected: + //窗体显示的时候触发 + void showEvent(QShowEvent *event); + + //事件过滤器识别拖动拉伸等 + void doWindowStateChange(QEvent *event); + void doResizeEvent(QEvent *event); + bool eventFilter(QObject *watched, QEvent *event); + + //拦截系统事件用于修复系统休眠后唤醒程序的BUG +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) + bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result); +#else + bool nativeEvent(const QByteArray &eventType, void *message, long *result); +#endif + + //Qt4的写法 +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#ifdef Q_OS_WIN + bool winEvent(MSG *message, long *result); +#endif +#endif + +private: + //边距+可移动+可拉伸 + int padding; + bool moveEnable; + bool resizeEnable; + + //标题栏控件 + QWidget *titleBar; + + //鼠标是否按下+按下坐标+按下时窗体区域 + bool mousePressed; + QPoint mousePoint; + QRect mouseRect; + + //鼠标是否按下某个区域+按下区域的大小 + //依次为 左侧+右侧+上侧+下侧+左上侧+右上侧+左下侧+右下侧 + QList pressedArea; + QList pressedRect; + + //记录是否最小化 + bool isMin; + //存储窗体默认的属性 + Qt::WindowFlags flags; + +public: + //设置边距+可拖动+可拉伸 + void setPadding(int padding); + void setMoveEnable(bool moveEnable); + void setResizeEnable(bool resizeEnable); + + //设置标题栏控件 + void setTitleBar(QWidget *titleBar); + +signals: + void titleDblClick(); + void windowStateChange(bool max); +}; + +#endif // FRAMELESSWIDGET_H diff --git a/frameless/form/dialog.cpp b/frameless/form/dialog.cpp new file mode 100644 index 0000000..3cd7272 --- /dev/null +++ b/frameless/form/dialog.cpp @@ -0,0 +1,69 @@ +#include "dialog.h" +#include "ui_dialog.h" +#include "head.h" + +Dialog::Dialog(QWidget *parent) : FramelessDialog(parent), ui(new Ui::Dialog) +{ + ui->setupUi(this); + this->initForm(); +} + +Dialog::~Dialog() +{ + delete ui; +} + +void Dialog::initForm() +{ + //设置标题栏控件 + ui->labTitle->setText("无边框窗体示例-支持win、linux、mac等系统 (QQ: 517216493 WX: feiyangqingyun)"); + this->setWindowTitle(ui->labTitle->text()); + this->setTitleBar(ui->labTitle); + + //关联信号 + connect(this, SIGNAL(titleDblClick()), this, SLOT(titleDblClick())); + connect(this, SIGNAL(windowStateChange(bool)), this, SLOT(windowStateChange(bool))); + + //设置样式表 + QStringList list; + list << "#titleBar{background:#BBBBBB;}"; + list << "#titleBar{border-top-left-radius:8px;border-top-right-radius:8px;}"; + list << "#widgetMain{border:2px solid #BBBBBB;background:#FFFFFF;}"; + //list << "#widgetMain{border-bottom-left-radius:8px;border-bottom-right-radius:8px;}"; + this->setStyleSheet(list.join("")); +} + +void Dialog::titleDblClick() +{ + on_btnMenu_Max_clicked(); +} + +void Dialog::windowStateChange(bool max) +{ + ui->btnMenu_Max->setText(max ? "还原" : "最大"); +} + +void Dialog::on_btnMenu_Min_clicked() +{ +#ifdef Q_OS_MACOS + this->setWindowFlags(this->windowFlags() & ~Qt::FramelessWindowHint); +#endif + this->showMinimized(); +} + +void Dialog::on_btnMenu_Max_clicked() +{ + if (this->isMaximized()) { + this->showNormal(); + ui->btnMenu_Max->setText("最大"); + } else { + this->showMaximized(); + ui->btnMenu_Max->setText("还原"); + } +} + +void Dialog::on_btnMenu_Close_clicked() +{ + this->close(); +} + diff --git a/frameless/form/dialog.h b/frameless/form/dialog.h new file mode 100644 index 0000000..d87d2af --- /dev/null +++ b/frameless/form/dialog.h @@ -0,0 +1,32 @@ +#ifndef DIALOG_H +#define DIALOG_H + +#include "framelessdialog.h" + +namespace Ui { +class Dialog; +} + +class Dialog : public FramelessDialog +{ + Q_OBJECT + +public: + explicit Dialog(QWidget *parent = 0); + ~Dialog(); + +private: + Ui::Dialog *ui; + +private slots: + void initForm(); + void titleDblClick(); + void windowStateChange(bool max); + +private slots: + void on_btnMenu_Min_clicked(); + void on_btnMenu_Max_clicked(); + void on_btnMenu_Close_clicked(); +}; + +#endif // DIALOG_H diff --git a/frameless/form/dialog.ui b/frameless/form/dialog.ui new file mode 100644 index 0000000..6c7df10 --- /dev/null +++ b/frameless/form/dialog.ui @@ -0,0 +1,215 @@ + + + Dialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 0 + + + 0 + + + 6 + + + 0 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 16777215 + 28 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + ArrowCursor + + + Qt::NoFocus + + + 最小化 + + + 最小 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Qt::NoFocus + + + 最大 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + ArrowCursor + + + Qt::NoFocus + + + 关闭 + + + 关闭 + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + diff --git a/frameless/form/form.pri b/frameless/form/form.pri new file mode 100644 index 0000000..2307635 --- /dev/null +++ b/frameless/form/form.pri @@ -0,0 +1,14 @@ +FORMS += \ + $$PWD/dialog.ui \ + $$PWD/mainwindow.ui \ + $$PWD/widget.ui + +HEADERS += \ + $$PWD/dialog.h \ + $$PWD/mainwindow.h \ + $$PWD/widget.h + +SOURCES += \ + $$PWD/dialog.cpp \ + $$PWD/mainwindow.cpp \ + $$PWD/widget.cpp diff --git a/frameless/form/mainwindow.cpp b/frameless/form/mainwindow.cpp new file mode 100644 index 0000000..2f09f60 --- /dev/null +++ b/frameless/form/mainwindow.cpp @@ -0,0 +1,68 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "head.h" + +MainWindow::MainWindow(QWidget *parent) : FramelessMainWindow(parent), ui(new Ui::MainWindow) +{ + ui->setupUi(this); + this->initForm(); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::initForm() +{ + //设置标题栏控件 + ui->labTitle->setText("无边框窗体示例-支持win、linux、mac等系统 (QQ: 517216493 WX: feiyangqingyun)"); + this->setWindowTitle(ui->labTitle->text()); + this->setTitleBar(ui->labTitle); + + //关联信号 + connect(this, SIGNAL(titleDblClick()), this, SLOT(titleDblClick())); + connect(this, SIGNAL(windowStateChange(bool)), this, SLOT(windowStateChange(bool))); + + //设置样式表 + QStringList list; + list << "#titleBar{background:#BBBBBB;}"; + list << "#titleBar{border-top-left-radius:8px;border-top-right-radius:8px;}"; + list << "#widgetMain{border:2px solid #BBBBBB;background:#FFFFFF;}"; + //list << "#widgetMain{border-bottom-left-radius:8px;border-bottom-right-radius:8px;}"; + this->setStyleSheet(list.join("")); +} + +void MainWindow::titleDblClick() +{ + on_btnMenu_Max_clicked(); +} + +void MainWindow::windowStateChange(bool max) +{ + ui->btnMenu_Max->setText(max ? "还原" : "最大"); +} + +void MainWindow::on_btnMenu_Min_clicked() +{ +#ifdef Q_OS_MACOS + this->setWindowFlags(this->windowFlags() & ~Qt::FramelessWindowHint); +#endif + this->showMinimized(); +} + +void MainWindow::on_btnMenu_Max_clicked() +{ + if (this->isMaximized()) { + this->showNormal(); + ui->btnMenu_Max->setText("最大"); + } else { + this->showMaximized(); + ui->btnMenu_Max->setText("还原"); + } +} + +void MainWindow::on_btnMenu_Close_clicked() +{ + this->close(); +} diff --git a/frameless/form/mainwindow.h b/frameless/form/mainwindow.h new file mode 100644 index 0000000..676af4f --- /dev/null +++ b/frameless/form/mainwindow.h @@ -0,0 +1,32 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "framelessmainwindow.h" + +namespace Ui { +class MainWindow; +} + +class MainWindow : public FramelessMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private: + Ui::MainWindow *ui; + +private slots: + void initForm(); + void titleDblClick(); + void windowStateChange(bool max); + +private slots: + void on_btnMenu_Min_clicked(); + void on_btnMenu_Max_clicked(); + void on_btnMenu_Close_clicked(); +}; + +#endif // MAINWINDOW_H diff --git a/frameless/form/mainwindow.ui b/frameless/form/mainwindow.ui new file mode 100644 index 0000000..b4b93c1 --- /dev/null +++ b/frameless/form/mainwindow.ui @@ -0,0 +1,217 @@ + + + MainWindow + + + + 0 + 0 + 400 + 300 + + + + MainWindow + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 0 + + + 0 + + + 6 + + + 0 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 16777215 + 28 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + ArrowCursor + + + Qt::NoFocus + + + 最小化 + + + 最小 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Qt::NoFocus + + + 最大 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + ArrowCursor + + + Qt::NoFocus + + + 关闭 + + + 关闭 + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + diff --git a/frameless/form/widget.cpp b/frameless/form/widget.cpp new file mode 100644 index 0000000..57c2342 --- /dev/null +++ b/frameless/form/widget.cpp @@ -0,0 +1,68 @@ +#include "widget.h" +#include "ui_widget.h" +#include "head.h" + +Widget::Widget(QWidget *parent) : FramelessWidget(parent), ui(new Ui::Widget) +{ + ui->setupUi(this); + this->initForm(); +} + +Widget::~Widget() +{ + delete ui; +} + +void Widget::initForm() +{ + //设置标题栏控件 + ui->labTitle->setText("无边框窗体示例-支持win、linux、mac等系统 (QQ: 517216493 WX: feiyangqingyun)"); + this->setWindowTitle(ui->labTitle->text()); + this->setTitleBar(ui->labTitle); + + //关联信号 + connect(this, SIGNAL(titleDblClick()), this, SLOT(titleDblClick())); + connect(this, SIGNAL(windowStateChange(bool)), this, SLOT(windowStateChange(bool))); + + //设置样式表 + QStringList list; + list << "#titleBar{background:#BBBBBB;}"; + list << "#titleBar{border-top-left-radius:8px;border-top-right-radius:8px;}"; + list << "#widgetMain{border:2px solid #BBBBBB;background:#FFFFFF;}"; + //list << "#widgetMain{border-bottom-left-radius:8px;border-bottom-right-radius:8px;}"; + this->setStyleSheet(list.join("")); +} + +void Widget::titleDblClick() +{ + on_btnMenu_Max_clicked(); +} + +void Widget::windowStateChange(bool max) +{ + ui->btnMenu_Max->setText(max ? "还原" : "最大"); +} + +void Widget::on_btnMenu_Min_clicked() +{ +#ifdef Q_OS_MACOS + this->setWindowFlags(this->windowFlags() & ~Qt::FramelessWindowHint); +#endif + this->showMinimized(); +} + +void Widget::on_btnMenu_Max_clicked() +{ + if (this->isMaximized()) { + this->showNormal(); + ui->btnMenu_Max->setText("最大"); + } else { + this->showMaximized(); + ui->btnMenu_Max->setText("还原"); + } +} + +void Widget::on_btnMenu_Close_clicked() +{ + this->close(); +} diff --git a/frameless/form/widget.h b/frameless/form/widget.h new file mode 100644 index 0000000..39e3a64 --- /dev/null +++ b/frameless/form/widget.h @@ -0,0 +1,32 @@ +#ifndef WIDGET_H +#define WIDGET_H + +#include "framelesswidget.h" + +namespace Ui { +class Widget; +} + +class Widget : public FramelessWidget +{ + Q_OBJECT + +public: + explicit Widget(QWidget *parent = 0); + ~Widget(); + +private: + Ui::Widget *ui; + +private slots: + void initForm(); + void titleDblClick(); + void windowStateChange(bool max); + +private slots: + void on_btnMenu_Min_clicked(); + void on_btnMenu_Max_clicked(); + void on_btnMenu_Close_clicked(); +}; + +#endif // WIDGET_H diff --git a/frameless/form/widget.ui b/frameless/form/widget.ui new file mode 100644 index 0000000..ebe649d --- /dev/null +++ b/frameless/form/widget.ui @@ -0,0 +1,215 @@ + + + Widget + + + + 0 + 0 + 400 + 300 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 0 + + + 0 + + + 6 + + + 0 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 16777215 + 28 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + ArrowCursor + + + Qt::NoFocus + + + 最小化 + + + 最小 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Qt::NoFocus + + + 最大 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + ArrowCursor + + + Qt::NoFocus + + + 关闭 + + + 关闭 + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + diff --git a/frameless/frameless.pro b/frameless/frameless.pro new file mode 100644 index 0000000..8679857 --- /dev/null +++ b/frameless/frameless.pro @@ -0,0 +1,21 @@ +QT += core gui +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 5): QT += core5compat + +TARGET = frameless +TEMPLATE = app +DESTDIR = $$PWD/../bin + +CONFIG += warn_off +HEADERS += head.h +SOURCES += main.cpp + +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/form +include ($$PWD/form/form.pri) + +INCLUDEPATH += $$PWD/../core_frameless +include ($$PWD/../core_frameless/core_frameless.pri) + +INCLUDEPATH += $$PWD/../core_common +include ($$PWD/../core_common/core_common.pri) diff --git a/frameless/head.h b/frameless/head.h new file mode 100644 index 0000000..304849c --- /dev/null +++ b/frameless/head.h @@ -0,0 +1,15 @@ +#include +#include + +#if (QT_VERSION > QT_VERSION_CHECK(5,0,0)) +#include +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) +#include +#endif + +#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) +#define DATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")) +#pragma execution_character_set("utf-8") + diff --git a/frameless/main.cpp b/frameless/main.cpp new file mode 100644 index 0000000..993784f --- /dev/null +++ b/frameless/main.cpp @@ -0,0 +1,29 @@ +#include "quihelper.h" +#include "mainwindow.h" +#include "widget.h" +#include "dialog.h" + +int main(int argc, char *argv[]) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor); +#endif + QApplication a(argc, argv); + + QUIHelper::setStyle(); + QUIHelper::setFont(15); + QUIHelper::setCode(); + + MainWindow w; + //Widget w; + //Dialog w; + +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + w.resize(800, 600); +#else + w.resize(800, 550); +#endif + QUIHelper::setFormInCenter(&w); + w.show(); + return a.exec(); +} diff --git a/frameless/readme.md b/frameless/readme.md new file mode 100644 index 0000000..fdec90e --- /dev/null +++ b/frameless/readme.md @@ -0,0 +1,50 @@ +### 一 前言 +不知道各位程序员有没有遇到过这样一种困惑,好不容易在开源网站找到了类似的想要的项目代码,结果down下来一编译,我勒个去,几百个错误,根本没法用,熟悉的人还好可以直接阅读代码进行修改(有些只要做小改动就能正常编译,有些可能需要大刀阔斧),可是对于绝大部分的初学者来说绝对是噩梦,连把代码看下去的勇气都没有,我们没有任何权利和理由去责怪开原作者,只是期望各位能够在项目开源的同时,将开源项目完善好、测试好,最起码要把项目中依赖的遗漏的文件一起打包好,或者作出必要的说明,比如对应的开发编译版本要求,以来的文件去哪里下载。很多优秀的项目就毁在这个地方,没人完善和维护(可能因为没有耐心,也没有持续的收入来源,所以干的没劲,作者甚至转行送外卖了)。只有解决了这个痛点才能使得对应的开源项目持续发光发热。 + +本人自从学习Qt开发以来,开源过至少上百个项目(大部分早期开源的目前不在开源主页,有空会全部整理好重新发布),我要是说在国内Qt界开源的项目数量和质量排第十的话,没人敢说排第一、第二、第三...到第九。关于无边框界面方案,网上也有不少的优秀的开源的例子,99%都存在以下几个问题,针对以上问题,解决这些痛点,借助自己刚好有多个操作系统、几十个Qt版本的开发测试环境,特意完善了这个无边框类。 + +- 无法正常编译,缺少文件,作者真粗心,自己都没测试过。 +- 只限定了部分特定的版本才能编译。 +- 只解决了单个问题,比如无边框拖动,没有系统特性拉到左侧右侧半屏、顶部最大化。 +- 代码赶鸭子上架,复制粘贴的一坨坨,毫无章法。 +- 代码就是给作者自己用的,放上去就是给个参考,管他那么多。 +- 往左侧拉动抖动的厉害。 +- mac系统上不能最小化。 +- 不能同时支持win、linux、mac三种主流操作系统。 + +### 二 功能特点 +1. 同时支持Qt4-Qt6,亲测Qt4.7到Qt6.2以及后续版本。 +2. 同时支持mingw、msvc、gcc等。 +3. 同时支持windows、linux、mac。 +4. 同时支持QMainWindow、QWidget、QDialog。 +5. 使用方法极其简单,只需要将继承类修改即可。 +6. 自动识别双击标题栏响应。 +7. 无边框拉伸在windows下不抖动。 +8. 在windows下具有移动到边缘半屏、移动到顶部全屏特性。 +9. 解决mac系统上无边框最小化最大化失效的BUG。 +10. 解决系统休眠后再次启动程序懵逼的BUG。 +11. 解决有时候窗体重新显示的时候假死不刷新的BUG。 +12. 轻量级,1个代码文件,核心代码行数不到300行。 +13. 注释详细,示例完美,非常适合阅读和学习。 +14. 开源开箱即用,保证任意Qt版本可正常编译运行,无需任何调整。 + +### 三 效果图 +#### 1 windows +![avatar](https://gitee.com/feiyangqingyun/QWidgetDemo/raw/master/frameless/snap/win.gif) + +#### 2 ubuntu +![avatar](https://gitee.com/feiyangqingyun/QWidgetDemo/raw/master/frameless/snap/ubuntu.gif) + +#### 3 uos +![avatar](https://gitee.com/feiyangqingyun/QWidgetDemo/raw/master/frameless/snap/uos.gif) + +#### 4 kylin +![avatar](https://gitee.com/feiyangqingyun/QWidgetDemo/raw/master/frameless/snap/kylin.gif) + +#### 5 mac +![avatar](https://gitee.com/feiyangqingyun/QWidgetDemo/raw/master/frameless/snap/mac.gif) + +### 四 特别说明 +1. 点赞、评论、留言、转发、发代码。 +2. 国内站点:[https://gitee.com/feiyangqingyun/QWidgetDemo](https://gitee.com/feiyangqingyun/QWidgetDemo) +3. 国际站点:[https://github.com/feiyangqingyun/QWidgetDemo](https://github.com/feiyangqingyun/QWidgetDemo) \ No newline at end of file diff --git a/frameless/snap/kylin.gif b/frameless/snap/kylin.gif new file mode 100644 index 0000000..459dd53 Binary files /dev/null and b/frameless/snap/kylin.gif differ diff --git a/frameless/snap/mac.gif b/frameless/snap/mac.gif new file mode 100644 index 0000000..fdfd54b Binary files /dev/null and b/frameless/snap/mac.gif differ diff --git a/frameless/snap/ubuntu.gif b/frameless/snap/ubuntu.gif new file mode 100644 index 0000000..38a2c7b Binary files /dev/null and b/frameless/snap/ubuntu.gif differ diff --git a/frameless/snap/uos.gif b/frameless/snap/uos.gif new file mode 100644 index 0000000..991f76c Binary files /dev/null and b/frameless/snap/uos.gif differ diff --git a/frameless/snap/win.gif b/frameless/snap/win.gif new file mode 100644 index 0000000..fe07065 Binary files /dev/null and b/frameless/snap/win.gif differ