diff --git a/.gitignore b/.gitignore index ce9d573..a1e9d95 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,4 @@ Thumbs.db *.bin *.run .qmake.conf +*.res diff --git a/examples/examples.pro b/examples/examples.pro index 4559212..79e9525 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -1,4 +1,4 @@ TEMPLATE = subdirs CONFIG -= ordered -qtHaveModule(widgets): SUBDIRS += widget +qtHaveModule(widgets): SUBDIRS += widget qmainwindow qtHaveModule(quick): SUBDIRS += quick diff --git a/examples/images/button_close_black.svg b/examples/images/button_close_black.svg index 0743e48..6f1f03b 100644 --- a/examples/images/button_close_black.svg +++ b/examples/images/button_close_black.svg @@ -1,5 +1,5 @@ - + diff --git a/examples/images/button_close_white.svg b/examples/images/button_close_white.svg index 66e6bb2..b69095a 100644 --- a/examples/images/button_close_white.svg +++ b/examples/images/button_close_white.svg @@ -1,5 +1,5 @@ - + diff --git a/examples/qmainwindow/MainWindow.ui b/examples/qmainwindow/MainWindow.ui new file mode 100644 index 0000000..f20bcc4 --- /dev/null +++ b/examples/qmainwindow/MainWindow.ui @@ -0,0 +1,293 @@ + + + MainWindow + + + + 0 + 0 + 518 + 362 + + + + MainWindow + + + + ../windows.ico../windows.ico + + + + + + + PushButton + + + + + + + + Nouvelle ligne + + + + + Nouvelle ligne + + + + + Nouvelle ligne + + + + + Nouvelle ligne + + + + + Nouvelle ligne + + + + + Nouvelle ligne + + + + + Nouvelle colonne + + + + + Nouvelle colonne + + + + + Nouvelle colonne + + + + + Nouvelle colonne + + + + + Nouvelle colonne + + + + + Nouvelle colonne + + + + + Nouvelle colonne + + + + + Nouvelle colonne + + + + + Nouvelle colonne + + + + + + + + + + 0 + 0 + 518 + 25 + + + + + 0 + 25 + + + + + Menu 1 + + + + + + + Menu 2 + + + + + Menu 3 + + + + + + + + + Dock 1 + + + 1 + + + + + + + PushButton + + + + + + + PushButton + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Dock 2 + + + 1 + + + + + 0 + + + 0 + + + + + RadioButton + + + + + + + RadioButton + + + + + + + + + + Qt::Vertical + + + + 20 + 89 + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + toolBar_2 + + + TopToolBarArea + + + false + + + + + toolBar_3 + + + BottomToolBarArea + + + false + + + + + + + Action 1 + + + + + Action 2 + + + + + Item 1 + + + + + Item 2 + + + + + + diff --git a/examples/qmainwindow/TitleBar.ui b/examples/qmainwindow/TitleBar.ui new file mode 100644 index 0000000..7d67e93 --- /dev/null +++ b/examples/qmainwindow/TitleBar.ui @@ -0,0 +1,288 @@ + + + TitleBar + + + + 0 + 0 + 552 + 31 + + + + + 0 + 0 + + + + + 0 + 31 + + + + + 16777215 + 31 + + + + QMenuBar { + background-color: transparent; + height: 23px; +} + +#iconButton, #minimizeButton, #maximizeButton, #closeButton { + border-style: none; + background-color: transparent; +} + +#minimizeButton:hover, #maximizeButton:hover { + background-color: #80c7c7c7; +} + +#minimizeButton:pressed, #maximizeButton:pressed { + background-color: #80808080; +} + +#closeButton:hover { + background-color: #e81123; + icon: url(:/images/button_close_white.svg); +} + +#closeButton:pressed { + background-color: #8c0a15; +} + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + 30 + 30 + + + + Qt::NoFocus + + + + 24 + 24 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + Segoe UI + 9 + + + + WIndow Title + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 45 + 30 + + + + + 45 + 30 + + + + Qt::NoFocus + + + Qt::NoContextMenu + + + Minimize + + + + :/images/button_minimize_black.svg:/images/button_minimize_black.svg + + + + 45 + 30 + + + + + + + + + 0 + 0 + + + + + 45 + 30 + + + + + 45 + 30 + + + + Qt::NoFocus + + + Qt::NoContextMenu + + + Maximize + + + + :/images/button_maximize_black.svg + :/images/button_restore_black.svg:/images/button_maximize_black.svg + + + + 45 + 30 + + + + true + + + + + + + + 0 + 0 + + + + + 45 + 30 + + + + + 45 + 30 + + + + Qt::NoFocus + + + Qt::NoContextMenu + + + Close + + + + :/images/button_close_black.svg:/images/button_close_black.svg + + + + 45 + 30 + + + + + + + + + + + + diff --git a/examples/qmainwindow/main.cpp b/examples/qmainwindow/main.cpp new file mode 100644 index 0000000..761c57c --- /dev/null +++ b/examples/qmainwindow/main.cpp @@ -0,0 +1,136 @@ +/* + * MIT License + * + * Copyright (C) 2021 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "../../framelesswindowsmanager.h" +#include "../../qtacrylicmainwindow.h" +#include "ui_MainWindow.h" +#include "ui_TitleBar.h" +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif + +int main(int argc, char *argv[]) +{ + QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + // High DPI scaling is enabled by default from Qt 6 +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + // Windows: we are using the manifest file to get maximum compatibility + // because some APIs are not supprted on old systems such as Windows 7 + // and Windows 8. And once we have set the DPI awareness level in the + // manifest file, any attemptation to try to change it through API will + // fail. In other words, Qt won't be able to enable or disable high DPI + // scaling or change the DPI awareness level once we have set it in the + // manifest file. So the following two lines are uesless actually (However, + // they are still useful on other platforms). + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + // Don't round the scale factor. + // This will break QWidget applications because they can't render correctly. + // Qt Quick applications won't have this issue. + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); +#endif +#endif + + QApplication application(argc, argv); + + QtAcrylicMainWindow *mainWindow = new QtAcrylicMainWindow(NULL, Qt::WindowFlags()); + mainWindow->setAcrylicEnabled(true); + + Ui::MainWindow appMainWindow; + appMainWindow.setupUi(mainWindow); + + QWidget *widget = new QWidget; + Ui::TitleBar titleBarWidget; + titleBarWidget.setupUi(widget); + + QMenuBar *menuBar = mainWindow->menuBar(); + titleBarWidget.horizontalLayout->insertWidget(1, menuBar); + + mainWindow->setMenuWidget(widget); + + QObject::connect(mainWindow, + &QMainWindow::windowIconChanged, + titleBarWidget.iconButton, + &QPushButton::setIcon); + QObject::connect(mainWindow, + &QMainWindow::windowTitleChanged, + titleBarWidget.titleLabel, + &QLabel::setText); + QObject::connect(titleBarWidget.closeButton, + &QPushButton::clicked, + mainWindow, + &QMainWindow::close); + QObject::connect(titleBarWidget.minimizeButton, + &QPushButton::clicked, + mainWindow, + &QMainWindow::showMinimized); + QObject::connect(titleBarWidget.maximizeButton, + &QPushButton::clicked, + [mainWindow, titleBarWidget]() { + if (mainWindow->isMaximized()) { + mainWindow->showNormal(); + } else { + mainWindow->showMaximized(); + } + }); + + QStyleOption option; + option.initFrom(mainWindow); + const QIcon icon = mainWindow->style()->standardIcon(QStyle::SP_ComputerIcon, &option); + mainWindow->setWindowIcon(icon); + mainWindow->setWindowTitle(QObject::tr("Hello, World!")); + + mainWindow->createWinId(); // Qt's internal function, make sure it's a top level window. + const QWindow *win = mainWindow->windowHandle(); + + QObject::connect(mainWindow, + &QtAcrylicMainWindow::windowStateChanged, + [mainWindow, titleBarWidget]() { + titleBarWidget.maximizeButton->setChecked(mainWindow->isMaximized()); + titleBarWidget.maximizeButton->setToolTip(mainWindow->isMaximized() ? QObject::tr("Restore") : QObject::tr("Maximize")); + }); + + QObject::connect(titleBarWidget.iconButton, + &QPushButton::clicked, + mainWindow, + &QtAcrylicMainWindow::displaySystemMenu); + + FramelessWindowsManager::addIgnoreObject(win, titleBarWidget.iconButton); + FramelessWindowsManager::addIgnoreObject(win, titleBarWidget.minimizeButton); + FramelessWindowsManager::addIgnoreObject(win, titleBarWidget.maximizeButton); + FramelessWindowsManager::addIgnoreObject(win, titleBarWidget.closeButton); + FramelessWindowsManager::addIgnoreObject(win, appMainWindow.menubar); + + mainWindow->resize(800, 600); + Q_EMIT mainWindow->windowStateChanged(); + + mainWindow->show(); + + return QApplication::exec(); +} diff --git a/examples/qmainwindow/qmainwindow.pro b/examples/qmainwindow/qmainwindow.pro new file mode 100644 index 0000000..777e72d --- /dev/null +++ b/examples/qmainwindow/qmainwindow.pro @@ -0,0 +1,6 @@ +TARGET = QMainWindow +TEMPLATE = app +QT += widgets +SOURCES += main.cpp +FORMS += TitleBar.ui MainWindow.ui +include($$PWD/../common.pri) diff --git a/lib.pro b/lib.pro index ab3d1ef..d3a17d7 100644 --- a/lib.pro +++ b/lib.pro @@ -24,8 +24,10 @@ SOURCES += \ qtacryliceffecthelper.cpp qtHaveModule(widgets) { QT += widgets - HEADERS += qtacrylicwidget.h - SOURCES += qtacrylicwidget.cpp + HEADERS += qtacrylicwidget.h \ + qtacrylicmainwindow.h + SOURCES += qtacrylicwidget.cpp \ + qtacrylicmainwindow.cpp } qtHaveModule(quick) { QT += quick diff --git a/qtacrylicmainwindow.cpp b/qtacrylicmainwindow.cpp new file mode 100644 index 0000000..58a1b17 --- /dev/null +++ b/qtacrylicmainwindow.cpp @@ -0,0 +1,202 @@ +/* + * MIT License + * + * Copyright (C) 2021 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "qtacrylicmainwindow.h" +#include "utilities.h" +#include "framelesswindowsmanager.h" +#include +#include +#include + +QtAcrylicMainWindow::QtAcrylicMainWindow(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags) {} + +QtAcrylicMainWindow::~QtAcrylicMainWindow() = default; + +QColor QtAcrylicMainWindow::tintColor() const +{ + const QColor color = m_acrylicHelper.getTintColor(); + if (color.isValid() && (color != Qt::transparent)) { + return color; + } else { + return palette().color(backgroundRole()); + } +} + +void QtAcrylicMainWindow::setTintColor(const QColor &value) +{ + if (!value.isValid()) { + qWarning() << "Tint color not valid."; + return; + } + if (m_acrylicHelper.getTintColor() != value) { + m_acrylicHelper.setTintColor(value); + QPalette pal = palette(); + pal.setColor(backgroundRole(), m_acrylicHelper.getTintColor()); + setPalette(pal); + //m_acrylicHelper.updateAcrylicBrush(tintColor()); + update(); + Q_EMIT tintColorChanged(); + } +} + +qreal QtAcrylicMainWindow::tintOpacity() const +{ + return m_acrylicHelper.getTintOpacity(); +} + +void QtAcrylicMainWindow::setTintOpacity(const qreal value) +{ + if (m_acrylicHelper.getTintOpacity() != value) { + m_acrylicHelper.setTintOpacity(value); + m_acrylicHelper.updateAcrylicBrush(tintColor()); + update(); + Q_EMIT tintOpacityChanged(); + } +} + +qreal QtAcrylicMainWindow::noiseOpacity() const +{ + return m_acrylicHelper.getNoiseOpacity(); +} + +void QtAcrylicMainWindow::setNoiseOpacity(const qreal value) +{ + if (m_acrylicHelper.getNoiseOpacity() != value) { + m_acrylicHelper.setNoiseOpacity(value); + m_acrylicHelper.updateAcrylicBrush(tintColor()); + update(); + Q_EMIT noiseOpacityChanged(); + } +} + +bool QtAcrylicMainWindow::frameVisible() const +{ + return m_frameVisible; +} + +void QtAcrylicMainWindow::setFrameVisible(const bool value) +{ + if (m_frameVisible != value) { + m_frameVisible = value; + update(); + Q_EMIT frameVisibleChanged(); + } +} + +QColor QtAcrylicMainWindow::frameColor() const +{ + return m_acrylicHelper.getFrameColor(); +} + +void QtAcrylicMainWindow::setFrameColor(const QColor &value) +{ + if (m_acrylicHelper.getFrameColor() != value) { + m_acrylicHelper.setFrameColor(value); + update(); + Q_EMIT frameColorChanged(); + } +} + +qreal QtAcrylicMainWindow::frameThickness() const +{ + return m_acrylicHelper.getFrameThickness(); +} + +void QtAcrylicMainWindow::setFrameThickness(const qreal value) +{ + if (m_acrylicHelper.getFrameThickness() != value) { + m_acrylicHelper.setFrameThickness(value); + update(); + Q_EMIT frameThicknessChanged(); + } +} + +bool QtAcrylicMainWindow::acrylicEnabled() const +{ + return m_acrylicEnabled; +} + +void QtAcrylicMainWindow::setAcrylicEnabled(const bool value) +{ + if (m_acrylicEnabled != value) { + m_acrylicEnabled = value; + if (m_inited) { + Utilities::setBlurEffectEnabled(windowHandle(), m_acrylicEnabled); + } + setAutoFillBackground(!m_acrylicEnabled); + setAttribute(Qt::WA_NoSystemBackground, m_acrylicEnabled); + setAttribute(Qt::WA_OpaquePaintEvent, m_acrylicEnabled); + setBackgroundRole(m_acrylicEnabled ? QPalette::Base : QPalette::Window); + update(); + Q_EMIT acrylicEnabledChanged(); + } +} + +void QtAcrylicMainWindow::showEvent(QShowEvent *event) +{ + QMainWindow::showEvent(event); + updateContentMargin(); + if (!m_inited) { + FramelessWindowsManager::addWindow(windowHandle()); + m_acrylicHelper.install(windowHandle()); + m_acrylicHelper.updateAcrylicBrush(tintColor()); + connect(&m_acrylicHelper, &QtAcrylicEffectHelper::needsRepaint, this, qOverload<>(&QtAcrylicMainWindow::update)); + m_inited = true; + } +} + +void QtAcrylicMainWindow::updateContentMargin() +{ + const qreal m = isMaximized() ? 0.0 : 1.0 / devicePixelRatioF(); + setContentsMargins(m, m, m, m); +} + +void QtAcrylicMainWindow::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + const QRect rect = {0, 0, width(), height()}; + if (acrylicEnabled()) { + m_acrylicHelper.paintWindowBackground(&painter, rect); + } + if (frameVisible()) { + m_acrylicHelper.paintWindowFrame(&painter, rect); + } + QMainWindow::paintEvent(event); +} + +void QtAcrylicMainWindow::changeEvent(QEvent *event) +{ + if( event->type()==QEvent::WindowStateChange ) { + updateContentMargin(); + Q_EMIT windowStateChanged(); + } + QMainWindow::changeEvent(event); +} + +void QtAcrylicMainWindow::displaySystemMenu() +{ +#ifdef WIN32 + Utilities::displaySystemMenu(windowHandle()); +#endif +} \ No newline at end of file diff --git a/qtacrylicmainwindow.h b/qtacrylicmainwindow.h new file mode 100644 index 0000000..744969b --- /dev/null +++ b/qtacrylicmainwindow.h @@ -0,0 +1,92 @@ +/* + * MIT License + * + * Copyright (C) 2021 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "framelesshelper_global.h" +#include +#include "qtacryliceffecthelper.h" + +class FRAMELESSHELPER_EXPORT QtAcrylicMainWindow : public QMainWindow +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QtAcrylicMainWindow) + Q_PROPERTY(QColor tintColor READ tintColor WRITE setTintColor NOTIFY tintColorChanged) + Q_PROPERTY(qreal tintOpacity READ tintOpacity WRITE setTintOpacity NOTIFY tintOpacityChanged) + Q_PROPERTY(qreal noiseOpacity READ noiseOpacity WRITE setNoiseOpacity NOTIFY noiseOpacityChanged) + Q_PROPERTY(bool frameVisible READ frameVisible WRITE setFrameVisible NOTIFY frameVisibleChanged) + Q_PROPERTY(QColor frameColor READ frameColor WRITE setFrameColor NOTIFY frameColorChanged) + Q_PROPERTY(qreal frameThickness READ frameThickness WRITE setFrameThickness NOTIFY frameThicknessChanged) + Q_PROPERTY(bool acrylicEnabled READ acrylicEnabled WRITE setAcrylicEnabled NOTIFY acrylicEnabledChanged) + +public: + explicit QtAcrylicMainWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); + ~QtAcrylicMainWindow() override; + + QColor tintColor() const; + void setTintColor(const QColor &value); + + qreal tintOpacity() const; + void setTintOpacity(const qreal value); + + qreal noiseOpacity() const; + void setNoiseOpacity(const qreal value); + + bool frameVisible() const; + void setFrameVisible(const bool value); + + QColor frameColor() const; + void setFrameColor(const QColor &value); + + qreal frameThickness() const; + void setFrameThickness(const qreal value); + + bool acrylicEnabled() const; + void setAcrylicEnabled(const bool value); + +Q_SIGNALS: + void tintColorChanged(); + void tintOpacityChanged(); + void noiseOpacityChanged(); + void frameVisibleChanged(); + void frameColorChanged(); + void frameThicknessChanged(); + void acrylicEnabledChanged(); + void windowStateChanged(); + +public Q_SLOTS: + void displaySystemMenu(); + +protected: + void showEvent(QShowEvent *event) override; + void paintEvent(QPaintEvent *event) override; + void changeEvent(QEvent *event) override; + +private: + QtAcrylicEffectHelper m_acrylicHelper; + bool m_frameVisible = true; + bool m_acrylicEnabled = false; + bool m_inited = false; + void updateContentMargin(); +}; diff --git a/qtacrylicwidget.h b/qtacrylicwidget.h index 9849f59..f9a3271 100644 --- a/qtacrylicwidget.h +++ b/qtacrylicwidget.h @@ -73,6 +73,7 @@ Q_SIGNALS: void frameColorChanged(); void frameThicknessChanged(); void acrylicEnabledChanged(); + void windowStateChanged(); protected: void showEvent(QShowEvent *event) override;