diff --git a/ArchitectureColoredPainting.sln b/ArchitectureColoredPainting.sln index c034bd5..dbfe560 100644 --- a/ArchitectureColoredPainting.sln +++ b/ArchitectureColoredPainting.sln @@ -4,11 +4,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 17.2.32519.379 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ArchitectureColoredPainting", "ArchitectureColoredPainting\ArchitectureColoredPainting.vcxproj", "{3FE96A33-2BB7-4686-A710-3EB8E3BBD709}" - ProjectSection(ProjectDependencies) = postProject - {B982E745-C0B1-46B3-A27B-743AF105F2D0} = {B982E745-C0B1-46B3-A27B-743AF105F2D0} - EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QGoodWindow", "QGoodWindow\QGoodWindow.vcxproj", "{B982E745-C0B1-46B3-A27B-743AF105F2D0}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FramelessHelperCore", "FramelessHelper\qmake\FramelessHelperCore.vcxproj", "{F1BC586E-F1ED-320C-899E-76CCB5660AE1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FramelessHelperWidgets", "FramelessHelper\qmake\FramelessHelperWidgets.vcxproj", "{61651EC8-A30E-3935-BD0C-A01AE3EE0AD9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -20,10 +19,14 @@ Global {3FE96A33-2BB7-4686-A710-3EB8E3BBD709}.Debug|x64.Build.0 = Debug|x64 {3FE96A33-2BB7-4686-A710-3EB8E3BBD709}.Release|x64.ActiveCfg = Release|x64 {3FE96A33-2BB7-4686-A710-3EB8E3BBD709}.Release|x64.Build.0 = Release|x64 - {B982E745-C0B1-46B3-A27B-743AF105F2D0}.Debug|x64.ActiveCfg = Debug|x64 - {B982E745-C0B1-46B3-A27B-743AF105F2D0}.Debug|x64.Build.0 = Debug|x64 - {B982E745-C0B1-46B3-A27B-743AF105F2D0}.Release|x64.ActiveCfg = Release|x64 - {B982E745-C0B1-46B3-A27B-743AF105F2D0}.Release|x64.Build.0 = Release|x64 + {F1BC586E-F1ED-320C-899E-76CCB5660AE1}.Debug|x64.ActiveCfg = Debug|x64 + {F1BC586E-F1ED-320C-899E-76CCB5660AE1}.Debug|x64.Build.0 = Debug|x64 + {F1BC586E-F1ED-320C-899E-76CCB5660AE1}.Release|x64.ActiveCfg = Release|x64 + {F1BC586E-F1ED-320C-899E-76CCB5660AE1}.Release|x64.Build.0 = Release|x64 + {61651EC8-A30E-3935-BD0C-A01AE3EE0AD9}.Debug|x64.ActiveCfg = Debug|x64 + {61651EC8-A30E-3935-BD0C-A01AE3EE0AD9}.Debug|x64.Build.0 = Debug|x64 + {61651EC8-A30E-3935-BD0C-A01AE3EE0AD9}.Release|x64.ActiveCfg = Release|x64 + {61651EC8-A30E-3935-BD0C-A01AE3EE0AD9}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj index f32e302..014d110 100644 --- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj +++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj @@ -68,7 +68,8 @@ stdcpp17 - $(SolutionDir)ArchitectureColoredPainting\src\Editor\RightBar;$(SolutionDir)ArchitectureColoredPainting\src\Editor\;$(SolutionDir)QGoodWindow;%(AdditionalIncludeDirectories) + $(SolutionDir)ArchitectureColoredPainting\src\Editor\RightBar;$(SolutionDir)ArchitectureColoredPainting\src\Editor\;$(SolutionDir)QGoodWindow;$(SolutionDir)FramelessHelper\include;$(SolutionDir)FramelessHelper\qmake\inc\core;$(SolutionDir)FramelessHelper\include\FramelessHelper\Core;%(AdditionalIncludeDirectories) + FRAMELESSHELPER_WIDGETS_STATIC;%(PreprocessorDefinitions) @@ -206,8 +207,8 @@ - - {b982e745-c0b1-46b3-a27b-743af105f2d0} + + {61651ec8-a30e-3935-bd0c-a01ae3ee0ad9} diff --git a/ArchitectureColoredPainting/FramelessWindow.ui b/ArchitectureColoredPainting/FramelessWindow.ui index 9660810..cf43485 100644 --- a/ArchitectureColoredPainting/FramelessWindow.ui +++ b/ArchitectureColoredPainting/FramelessWindow.ui @@ -38,7 +38,7 @@ false - #windowFrame{ background-color:palette(Window);} + @@ -56,108 +56,13 @@ 1 - - - - - 0 - 0 - - - - - 0 - 0 - - - - false - - - #windowTitlebar{border: 0px none palette(base); border-top-left-radius:5px; border-top-right-radius:5px; background-color:palette(shadow); height:20px;} - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 4 - 0 - - - - - 4 - 16777215 - - - - - - - - - 16 - 16 - - - - - 16 - 16 - - - - Qt::NoContextMenu - - - #icon {background-color:palette(shadow);} - - - - - - - - - - - - - - - - - - - - - false - #windowContent{ - border: 0px none palette(base); - border-radius:0px 0px 5px 5px; -} + @@ -184,25 +89,6 @@ - - - CaptionButton - QWidget -
CaptionButton.h
- 1 -
- - IconWidget - QWidget -
IconWidget.h
-
- - TitleWidget - QWidget -
TitleWidget.h
- 1 -
-
diff --git a/ArchitectureColoredPainting/RendererWidget.ui b/ArchitectureColoredPainting/RendererWidget.ui index fdebb92..c3a7631 100644 --- a/ArchitectureColoredPainting/RendererWidget.ui +++ b/ArchitectureColoredPainting/RendererWidget.ui @@ -13,7 +13,10 @@ RendererWidget - + + + 0 + 0 @@ -27,20 +30,74 @@ 0
- - - Qt::Vertical + + + QLayout::SetDefaultConstraint - - QSizePolicy::Fixed + + + + + 0 + 0 + + + + + 0 + 40 + + + + 鎵撳紑 + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + - + - 20 - 40 + 0 + 0 - + + + + 0 + 40 + + + + 鎵撳紑 + + + + diff --git a/ArchitectureColoredPainting/src/MainWindow.cpp b/ArchitectureColoredPainting/src/MainWindow.cpp index 589b6c6..c6cef40 100644 --- a/ArchitectureColoredPainting/src/MainWindow.cpp +++ b/ArchitectureColoredPainting/src/MainWindow.cpp @@ -3,98 +3,104 @@ #include "qslider.h" #include #include "NavigationBarWidget.h" +#include +#include +#include +#include #include +FRAMELESSHELPER_USE_NAMESPACE + inline void fontTheme() { - QFont defaultFont = qApp->font(); - defaultFont.setPointSize(defaultFont.pointSize() + 2); - qApp->setFont(defaultFont); + QFont defaultFont = qApp->font(); + defaultFont.setPointSize(defaultFont.pointSize() + 2); + qApp->setFont(defaultFont); } inline void setThemeStyleSheet(bool dark) { - QFile file(dark ? ":/darkstyle.qss" : ":/lightstyle.qss"); + QFile file(dark ? ":/darkstyle.qss" : ":/lightstyle.qss"); - if (!file.open(QFile::ReadOnly)) - return; + if (!file.open(QFile::ReadOnly)) + return; - const QString style_sheet = QLatin1String(file.readAll()); + const QString style_sheet = QLatin1String(file.readAll()); - file.close(); + file.close(); - qApp->setStyleSheet(style_sheet); + qApp->setStyleSheet(style_sheet); } inline void darkTheme() { - QPalette darkPalette = qApp->palette(); + QPalette darkPalette = qApp->palette(); - darkPalette.setColor(QPalette::Window, QColor(53, 53, 53)); - darkPalette.setColor(QPalette::WindowText, QColor(255, 255, 255)); - darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127)); - darkPalette.setColor(QPalette::Base, QColor(42, 42, 42)); - darkPalette.setColor(QPalette::AlternateBase, QColor(66, 66, 66)); - darkPalette.setColor(QPalette::ToolTipBase, QColor(255, 255, 255)); - darkPalette.setColor(QPalette::ToolTipText, QColor(255, 255, 255)); - darkPalette.setColor(QPalette::Text, QColor(255, 255, 255)); - darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127)); - darkPalette.setColor(QPalette::Dark, QColor(35, 35, 35)); - darkPalette.setColor(QPalette::Shadow, QColor(20, 20, 20)); - darkPalette.setColor(QPalette::Button, QColor(53, 53, 53)); - darkPalette.setColor(QPalette::ButtonText, QColor(255, 255, 255)); - darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127)); - darkPalette.setColor(QPalette::BrightText, QColor(255, 0, 0)); - darkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); - darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); - darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80)); - darkPalette.setColor(QPalette::HighlightedText, QColor(255, 255, 255)); - darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Window, QColor(53, 53, 53)); + darkPalette.setColor(QPalette::WindowText, QColor(255, 255, 255)); + darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Base, QColor(42, 42, 42)); + darkPalette.setColor(QPalette::AlternateBase, QColor(66, 66, 66)); + darkPalette.setColor(QPalette::ToolTipBase, QColor(255, 255, 255)); + darkPalette.setColor(QPalette::ToolTipText, QColor(255, 255, 255)); + darkPalette.setColor(QPalette::Text, QColor(255, 255, 255)); + darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::Dark, QColor(35, 35, 35)); + darkPalette.setColor(QPalette::Shadow, QColor(20, 20, 20)); + darkPalette.setColor(QPalette::Button, QColor(53, 53, 53)); + darkPalette.setColor(QPalette::ButtonText, QColor(255, 255, 255)); + darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127)); + darkPalette.setColor(QPalette::BrightText, QColor(255, 0, 0)); + darkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); + darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); + darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80)); + darkPalette.setColor(QPalette::HighlightedText, QColor(255, 255, 255)); + darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127)); - qApp->setPalette(darkPalette); + qApp->setPalette(darkPalette); - setThemeStyleSheet(true /*dark*/); + setThemeStyleSheet(true /*dark*/); } inline void lightTheme() { - QPalette lightPalette = qApp->palette(); + QPalette lightPalette = qApp->palette(); - lightPalette.setColor(QPalette::Window, QColor(240, 240, 240)); - lightPalette.setColor(QPalette::WindowText, QColor(0, 0, 0)); - lightPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(120, 120, 120)); - lightPalette.setColor(QPalette::Base, QColor(255, 255, 255)); - lightPalette.setColor(QPalette::AlternateBase, QColor(233, 231, 227)); - lightPalette.setColor(QPalette::ToolTipBase, QColor(255, 255, 220)); - lightPalette.setColor(QPalette::ToolTipText, QColor(0, 0, 0)); - lightPalette.setColor(QPalette::Text, QColor(0, 0, 0)); - lightPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(120, 120, 120)); - lightPalette.setColor(QPalette::Dark, QColor(160, 160, 160)); - lightPalette.setColor(QPalette::Shadow, QColor(105, 105, 105)); - lightPalette.setColor(QPalette::Button, QColor(240, 240, 240)); - lightPalette.setColor(QPalette::ButtonText, QColor(0, 0, 0)); - lightPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(120, 120, 120)); - lightPalette.setColor(QPalette::BrightText, QColor(0, 0, 255)); - lightPalette.setColor(QPalette::Link, QColor(51, 153, 255)); - lightPalette.setColor(QPalette::Highlight, QColor(0, 0, 255)); - lightPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(51, 153, 255)); - lightPalette.setColor(QPalette::HighlightedText, QColor(255, 255, 255)); - lightPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(255, 255, 255)); + lightPalette.setColor(QPalette::Window, QColor(240, 240, 240)); + lightPalette.setColor(QPalette::WindowText, QColor(0, 0, 0)); + lightPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(120, 120, 120)); + lightPalette.setColor(QPalette::Base, QColor(255, 255, 255)); + lightPalette.setColor(QPalette::AlternateBase, QColor(233, 231, 227)); + lightPalette.setColor(QPalette::ToolTipBase, QColor(255, 255, 220)); + lightPalette.setColor(QPalette::ToolTipText, QColor(0, 0, 0)); + lightPalette.setColor(QPalette::Text, QColor(0, 0, 0)); + lightPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(120, 120, 120)); + lightPalette.setColor(QPalette::Dark, QColor(160, 160, 160)); + lightPalette.setColor(QPalette::Shadow, QColor(105, 105, 105)); + lightPalette.setColor(QPalette::Button, QColor(240, 240, 240)); + lightPalette.setColor(QPalette::ButtonText, QColor(0, 0, 0)); + lightPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(120, 120, 120)); + lightPalette.setColor(QPalette::BrightText, QColor(0, 0, 255)); + lightPalette.setColor(QPalette::Link, QColor(51, 153, 255)); + lightPalette.setColor(QPalette::Highlight, QColor(0, 0, 255)); + lightPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(51, 153, 255)); + lightPalette.setColor(QPalette::HighlightedText, QColor(255, 255, 255)); + lightPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(255, 255, 255)); - qApp->setPalette(lightPalette); + qApp->setPalette(lightPalette); - setThemeStyleSheet(false /*dark*/); + setThemeStyleSheet(false /*dark*/); } FramelessWindow::FramelessWindow(QWidget* parent) : QWidget(parent), ui(new Ui::FramelessWindow) { - ui->setupUi(this); + ui->setupUi(this); } FramelessWindow::~FramelessWindow() { - delete ui; + delete ui; } CentralWidget::CentralWidget(QWidget* parent) : QMainWindow(parent) @@ -118,374 +124,61 @@ CentralWidget::~CentralWidget() } -MainWindow::MainWindow(QWidget* parent) - : QGoodWindow(parent) +MainWindow::MainWindow(QWidget* parent, const Qt::WindowFlags flags) { - - // if the system will draw borders or if this application must do that - m_draw_borders = !QGoodWindow::shouldBordersBeDrawnBySystem(); + m_titleBar = new StandardTitleBar(this); + m_titleBar->setWindowIconVisible(true); + constexpr int kTitleBarHeight = 28; + m_titleBar->setFixedHeight(kTitleBarHeight); + m_titleBar->minimizeButton()->setFixedHeight(kTitleBarHeight); + m_titleBar->maximizeButton()->setFixedHeight(kTitleBarHeight); + m_titleBar->closeButton()->setFixedHeight(kTitleBarHeight); + m_titleBar->setTitleFont(QFont(QString("Segoe UI, Microsoft YaHei UI"), 10)); + m_titleBar->chromePalette()->setTitleBarActiveBackgroundColor(QColor(0,0,0,0)); + m_titleBar->chromePalette()->setTitleBarInactiveBackgroundColor(QColor(0, 0, 0, 0)); - m_dark = QGoodWindow::isSystemThemeDark(); + m_window = new FramelessWindow(this); + m_central_widget = new CentralWidget(this); + m_window->ui->windowContent->layout()->addWidget(m_central_widget); - // create frameless window - m_window = new FramelessWindow(this); + setMenuWidget(m_titleBar); + setCentralWidget(m_window); - m_central_widget = new CentralWidget(m_window); + FramelessWidgetsHelper* helper = FramelessWidgetsHelper::get(this); + helper->setTitleBarWidget(m_titleBar); +#ifndef Q_OS_MACOS + helper->setSystemButton(m_titleBar->minimizeButton(), Global::SystemButtonType::Minimize); + helper->setSystemButton(m_titleBar->maximizeButton(), Global::SystemButtonType::Maximize); + helper->setSystemButton(m_titleBar->closeButton(), Global::SystemButtonType::Close); +#endif // Q_OS_MACOS - // add the mainwindow to our custom frameless window - m_window->ui->windowContent->layout()->addWidget(m_central_widget); + connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper]() { + helper->moveWindowToDesktopCenter(); - connect(this, &QGoodWindow::windowTitleChanged, this, [=](const QString& title) { - m_window->ui->titleWidget->setText(title); - }); - - connect(this, &QGoodWindow::windowIconChanged, this, [=](const QIcon& icon) { - m_window->ui->icon->setPixmap(icon.pixmap(16, 16)); - }); - int iconWidth = 30; - int captionButtonWidth = 45, captionButtonHeight = 30; - m_window->ui->icon->setFixedSize(iconWidth, 20); - m_window->ui->titleWidget->setFixedHeight(captionButtonHeight); - m_window->ui->minimizeButton->setFixedSize(captionButtonWidth, captionButtonHeight); - m_window->ui->maximizeButton->setFixedSize(captionButtonWidth, captionButtonHeight); - m_window->ui->restoreButton->setFixedSize(captionButtonWidth, captionButtonHeight); - m_window->ui->closeButton->setFixedSize(captionButtonWidth, captionButtonHeight); - - m_window->ui->minimizeButton->init(CaptionButton::IconType::Minimize); - m_window->ui->maximizeButton->init(CaptionButton::IconType::Maximize); - m_window->ui->restoreButton->init(CaptionButton::IconType::Restore); - m_window->ui->closeButton->init(CaptionButton::IconType::Close); - - connect(m_window->ui->minimizeButton, &CaptionButton::clicked, this, &MainWindow::showMinimized); - connect(m_window->ui->maximizeButton, &CaptionButton::clicked, this, &MainWindow::showMaximized); - connect(m_window->ui->restoreButton, &CaptionButton::clicked, this, &MainWindow::showNormal); - connect(m_window->ui->closeButton, &CaptionButton::clicked, this, &MainWindow::close); - - connect(this, &QGoodWindow::captionButtonStateChanged, this, &MainWindow::captionButtonStateChanged); + }); - setMargins(captionButtonHeight, iconWidth, 0, captionButtonWidth * 3); - - setCaptionButtonsHandled(true, Qt::TopRightCorner); - - // Overlap close with maximize and maximize with minimize - setCloseMask(QRect(captionButtonWidth * 2, 0, rightCaptionButtonsRect().width(), rightCaptionButtonsRect().height())); - setMaximizeMask(QRect(captionButtonWidth * 1, 0, rightCaptionButtonsRect().width(), rightCaptionButtonsRect().height())); - setMinimizeMask(QRect(0, 0, rightCaptionButtonsRect().width(), rightCaptionButtonsRect().height())); - - auto theme_change_func = [=] { - if (m_dark) - darkTheme(); - else - lightTheme(); - - //Icon color inverse of m_dark to contrast. - m_window->ui->minimizeButton->setIconMode(!m_dark); - m_window->ui->maximizeButton->setIconMode(!m_dark); - m_window->ui->restoreButton->setIconMode(!m_dark); - m_window->ui->closeButton->setIconMode(!m_dark); - }; - - QShortcut* shortcut1 = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_S), this); - - connect(shortcut1, &QShortcut::activated, this, [=] { - m_dark = !m_dark; - theme_change_func(); - }); - - connect(this, &QGoodWindow::systemThemeChanged, this, [=] { - m_dark = QGoodWindow::isSystemThemeDark(); - theme_change_func(); - }); - - theme_change_func(); + setWindowIcon(QIcon(":/images/icon.png")); + setWindowTitle("ArchitectureColoredPainting"); - setWindowIcon(QIcon(":/images/icon.png")); - setWindowTitle("ArchitectureColoredPainting"); + resize(m_central_widget->size()); + //setCentralWidget(m_window); - - resize(m_central_widget->size()); - setCentralWidget(m_window); - - move(QGuiApplication::primaryScreen()->availableGeometry().center() - rect().center()); + //move(QGuiApplication::primaryScreen()->availableGeometry().center() - rect().center()); } MainWindow::~MainWindow() {} -void MainWindow::styleWindow() -{ - bool window_active = isActiveWindow(); - bool window_no_state = windowState().testFlag(Qt::WindowNoState); - - //bool draw_borders = m_draw_borders; - bool draw_borders = false; - - if (window_active) - { - if (window_no_state) - { - if (draw_borders) - { - m_window->ui->windowTitlebar->setStyleSheet( - QStringLiteral("#windowTitlebar{border: 0px none palette(shadow);}")); - m_window->ui->windowFrame->setStyleSheet( - QStringLiteral("#windowFrame{border: 1px solid palette(highlight);" - "background-color: palette(Window);}")); - } - else - { - m_window->ui->windowTitlebar->setStyleSheet( - QStringLiteral("#windowTitlebar{border: 0px none palette(shadow);}")); - m_window->ui->windowFrame->setStyleSheet( - QStringLiteral("#windowFrame{border: 0px solid palette(highlight);" - "background-color: palette(Window);}")); - } - } - else - { - m_window->ui->windowTitlebar->setStyleSheet( - QStringLiteral("#windowTitlebar{border: 0px none palette(shadow);" - "background-color :palette(shadow); height:20px;}")); - m_window->ui->windowFrame->setStyleSheet( - QStringLiteral("#windowFrame{border: 0px none palette(dark);" - "background-color: palette(Window);}")); - } - } - else - { - if (window_no_state) - { - if (draw_borders) - { - m_window->ui->windowTitlebar->setStyleSheet( - QStringLiteral("#windowTitlebar{border: 0px none palette(shadow);" - "background-color: palette(dark); height:20px;}")); - m_window->ui->windowFrame->setStyleSheet( - QStringLiteral("#windowFrame{border: 1px solid #000000;" - "background-color: palette(Window);}")); - } - else - { - m_window->ui->windowTitlebar->setStyleSheet( - QStringLiteral("#windowTitlebar{border: 0px none palette(shadow);" - "background-color: palette(dark); height:20px;}")); - m_window->ui->windowFrame->setStyleSheet( - QStringLiteral("#windowFrame{border: 0px solid #000000;" - "background-color: palette(Window);}")); - } - } - else - { - m_window->ui->windowTitlebar->setStyleSheet( - QStringLiteral("#titlebarWidget{border: 0px none palette(shadow);" - "background-color: palette(dark); height: 20px;}")); - m_window->ui->windowFrame->setStyleSheet( - QStringLiteral("#windowFrame{border: 0px none palette(shadow);" - "background-color: palette(Window);}")); - } - } - - m_window->ui->icon->setActive(window_active); - - m_window->ui->titleWidget->setActive(window_active); - - if (!isMinimized()) - { - m_window->ui->maximizeButton->setVisible(window_no_state); - m_window->ui->restoreButton->setVisible(!window_no_state); - } - - m_window->ui->minimizeButton->setActive(window_active); - m_window->ui->maximizeButton->setActive(window_active); - m_window->ui->restoreButton->setActive(window_active); - m_window->ui->closeButton->setActive(window_active); -} - -void MainWindow::captionButtonStateChanged(const QGoodWindow::CaptionButtonState& state) -{ - switch (state) - { - // Hover enter - case QGoodWindow::CaptionButtonState::MinimizeHoverEnter: - { - m_window->ui->minimizeButton->setState(QEvent::HoverEnter); - - break; - } - case QGoodWindow::CaptionButtonState::MaximizeHoverEnter: - { - if (!isMaximized()) - m_window->ui->maximizeButton->setState(QEvent::HoverEnter); - else - m_window->ui->restoreButton->setState(QEvent::HoverEnter); - - break; - } - case QGoodWindow::CaptionButtonState::CloseHoverEnter: - { - m_window->ui->closeButton->setState(QEvent::HoverEnter); - - break; - } - // Hover leave - case QGoodWindow::CaptionButtonState::MinimizeHoverLeave: - { - m_window->ui->minimizeButton->setState(QEvent::HoverLeave); - - break; - } - case QGoodWindow::CaptionButtonState::MaximizeHoverLeave: - { - if (!isMaximized()) - m_window->ui->maximizeButton->setState(QEvent::HoverLeave); - else - m_window->ui->restoreButton->setState(QEvent::HoverLeave); - - break; - } - case QGoodWindow::CaptionButtonState::CloseHoverLeave: - { - m_window->ui->closeButton->setState(QEvent::HoverLeave); - - break; - } - // Mouse button press - case QGoodWindow::CaptionButtonState::MinimizePress: - { - m_window->ui->minimizeButton->setState(QEvent::MouseButtonPress); - - break; - } - case QGoodWindow::CaptionButtonState::MaximizePress: - { - if (!isMaximized()) - m_window->ui->maximizeButton->setState(QEvent::MouseButtonPress); - else - m_window->ui->restoreButton->setState(QEvent::MouseButtonPress); - - break; - } - case QGoodWindow::CaptionButtonState::ClosePress: - { - m_window->ui->closeButton->setState(QEvent::MouseButtonPress); - - break; - } - // Mouse button release - case QGoodWindow::CaptionButtonState::MinimizeRelease: - { - m_window->ui->minimizeButton->setState(QEvent::MouseButtonRelease); - - break; - } - case QGoodWindow::CaptionButtonState::MaximizeRelease: - { - if (!isMaximized()) - m_window->ui->maximizeButton->setState(QEvent::MouseButtonRelease); - else - m_window->ui->restoreButton->setState(QEvent::MouseButtonRelease); - - break; - } - case QGoodWindow::CaptionButtonState::CloseRelease: - { - m_window->ui->closeButton->setState(QEvent::MouseButtonRelease); - - break; - } - // Mouse button clicked - case QGoodWindow::CaptionButtonState::MinimizeClicked: - { - emit m_window->ui->minimizeButton->clicked(); - - break; - } - case QGoodWindow::CaptionButtonState::MaximizeClicked: - { - if (!isMaximized()) - emit m_window->ui->maximizeButton->clicked(); - else - emit m_window->ui->restoreButton->clicked(); - - break; - } - case QGoodWindow::CaptionButtonState::CloseClicked: - { - emit m_window->ui->closeButton->clicked(); - - break; - } - default: - break; - } -} - -bool MainWindow::event(QEvent* event) -{ - switch (event->type()) - { - case QEvent::Show: - case QEvent::WindowActivate: - case QEvent::WindowDeactivate: - case QEvent::WindowStateChange: - { - styleWindow(); - break; - } - case QEvent::StyleChange: - { - QColor active_color = qApp->palette().color(QPalette::WindowText); - QColor inactive_color = qApp->palette().color(QPalette::Disabled, QPalette::WindowText); - - m_window->ui->titleWidget->setTitleColor(active_color, inactive_color); - - break; - } - default: - break; - } - - return QGoodWindow::event(event); -} - -bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, long* result) -{ -#ifdef Q_OS_WIN - MSG* msg = static_cast(message); - - switch (msg->message) - { - case WM_THEMECHANGED: - case WM_DWMCOMPOSITIONCHANGED: - { - //Keep window theme on Windows theme change events. - QTimer::singleShot(1000, this, [=] { - if (m_dark) - darkTheme(); - else - lightTheme(); - }); - - break; - } - default: - break; - } - -#endif - return QGoodWindow::nativeEvent(eventType, message, result); -} - void MainWindow::closeEvent(QCloseEvent* event) { - /* int result = QMessageBox::question(this, "Close window", "Are you sure to close?"); + /* int result = QMessageBox::question(this, "Close window", "Are you sure to close?"); - if (result != QMessageBox::Yes) - event->ignore();*/ + if (result != QMessageBox::Yes) + event->ignore();*/ } diff --git a/ArchitectureColoredPainting/src/MainWindow.h b/ArchitectureColoredPainting/src/MainWindow.h index 1fdb6f2..cfcc888 100644 --- a/ArchitectureColoredPainting/src/MainWindow.h +++ b/ArchitectureColoredPainting/src/MainWindow.h @@ -4,8 +4,12 @@ #include "ui_MainWindow.h" #include "ui_FramelessWindow.h" -#define QGOODWINDOW -#include +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE +class StandardTitleBar; +FRAMELESSHELPER_END_NAMESPACE + class FramelessWindow : public QWidget { @@ -31,26 +35,21 @@ public: }; -class MainWindow : public QGoodWindow +class MainWindow : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessMainWindow) { Q_OBJECT + Q_DISABLE_COPY_MOVE(MainWindow) public: - explicit MainWindow(QWidget* parent = nullptr); - ~MainWindow(); + explicit MainWindow(QWidget * parent = nullptr, const Qt::WindowFlags flags = {}); + ~MainWindow() override; private: - //Functions - void styleWindow(); - void captionButtonStateChanged(const QGoodWindow::CaptionButtonState& state); - bool event(QEvent* event); - bool nativeEvent(const QByteArray& eventType, void* message, long* result); + FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar)* m_titleBar = nullptr; + Ui::MainWindowClass ui; + void closeEvent(QCloseEvent* event); - //Variables FramelessWindow* m_window; - CentralWidget* m_central_widget; - bool m_draw_borders; - bool m_dark; }; diff --git a/ArchitectureColoredPainting/src/Renderer/Light.cpp b/ArchitectureColoredPainting/src/Renderer/Light.cpp index 3bdd35f..257d70e 100644 --- a/ArchitectureColoredPainting/src/Renderer/Light.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Light.cpp @@ -83,19 +83,20 @@ QMatrix4x4 Light::getLightSpaceMatrix(const float nearPlane, const float farPlan minZ = std::min(minZ, -trf.z()); maxZ = std::max(maxZ, -trf.z()); } - for (const QVector3D& v : model->AABB) - { - const QVector4D trf = lightView * QVector4D(v, 1); - //qDebug() << v; - //qDebug() << trf; - //minX = std::min(minX, trf.x()); - //maxX = std::max(maxX, trf.x()); - //minY = std::min(minY, trf.y()); - //maxY = std::max(maxY, trf.y()); - minZ = std::min(minZ, -trf.z()); - //maxZ = std::max(maxZ, trf.z()); + if (model != nullptr) + for (const QVector3D& v : model->AABB) + { + const QVector4D trf = lightView * QVector4D(v, 1); + //qDebug() << v; + //qDebug() << trf; + //minX = std::min(minX, trf.x()); + //maxX = std::max(maxX, trf.x()); + //minY = std::min(minY, trf.y()); + //maxY = std::max(maxY, trf.y()); + minZ = std::min(minZ, -trf.z()); + //maxZ = std::max(maxZ, trf.z()); - } + } //qDebug() << minZ; // Tune this parameter according to the scene /* constexpr float zMult = 10.0f; diff --git a/ArchitectureColoredPainting/src/Renderer/Light.h b/ArchitectureColoredPainting/src/Renderer/Light.h index 2af104b..4177898 100644 --- a/ArchitectureColoredPainting/src/Renderer/Light.h +++ b/ArchitectureColoredPainting/src/Renderer/Light.h @@ -18,7 +18,7 @@ namespace Renderer std::vector shadowCascadeLevels; float blendRatio = 0.3; std::vector frustumSizes; - Model* model; + Model* model = nullptr; Light(Camera* camera); std::vector getFrustumCornersWorldSpace(const QMatrix4x4& projview); std::vector getFrustumCornersWorldSpace(const QMatrix4x4& proj, const QMatrix4x4& view); diff --git a/ArchitectureColoredPainting/src/Renderer/Mesh.cpp b/ArchitectureColoredPainting/src/Renderer/Mesh.cpp index 32652db..dfd39ec 100644 --- a/ArchitectureColoredPainting/src/Renderer/Mesh.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Mesh.cpp @@ -1,4 +1,5 @@ #include "Mesh.h" +#include using namespace Renderer; Mesh::Mesh(QOpenGLFunctions_4_5_Compatibility* glFunc, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* shadowProgram, aiMatrix4x4 model) : glFunc(glFunc) @@ -35,6 +36,7 @@ void Mesh::draw() QOpenGLVertexArrayObject::Binder bind(&VAO); shaderProgram->setUniformValue("model", model); + EBO.bind(); glFunc->glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); shaderProgram->release(); @@ -59,6 +61,7 @@ void Mesh::drawShadow() QOpenGLVertexArrayObject::Binder bind(&VAO); shadowProgram->setUniformValue("model", model); + EBO.bind(); glFunc->glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); shadowProgram->release(); diff --git a/ArchitectureColoredPainting/src/Renderer/Model.cpp b/ArchitectureColoredPainting/src/Renderer/Model.cpp index 20a3b56..cc8c128 100644 --- a/ArchitectureColoredPainting/src/Renderer/Model.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Model.cpp @@ -35,36 +35,15 @@ Model::Model(QString path, QOpenGLContext* context, QOpenGLShaderProgram* shader processNode(scene->mRootNode, scene); } -Model::Model(QString path, QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* paintingProgram, QOpenGLShaderProgram* shadowProgram, PaintingHelper* paintingHelper) +Model::Model(QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* paintingProgram, QOpenGLShaderProgram* shadowProgram, PaintingHelper* paintingHelper) : context(context) , glFunc(context->versionFunctions()) , shaderProgram(shaderProgram) , paintingProgram(paintingProgram) , shadowProgram(shadowProgram) , paintingHelper(paintingHelper) - , directory(path) { - Assimp::Importer importer; - const aiScene* scene = importer.ReadFile(directory.absolutePath().toUtf8(), aiProcess_Triangulate | aiProcess_FlipUVs); - if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) - { - qDebug() << "ERROR::ASSIMP::" << importer.GetErrorString() << endl; - return; - } - qDebug() << directory.absolutePath() << "Loaded Successfully"; - qDebug() << "NumMeshes: " << scene->mNumMeshes; - qDebug() << "NumMaterials: " << scene->mNumMaterials; - qDebug() << "NumTextures: " << scene->mNumTextures; - directory.cdUp(); - processNode(scene->mRootNode, scene); - AABB.push_back(QVector3D(minX, minY, minZ)); - AABB.push_back(QVector3D(minX, minY, maxZ)); - AABB.push_back(QVector3D(minX, maxY, minZ)); - AABB.push_back(QVector3D(minX, maxY, maxZ)); - AABB.push_back(QVector3D(maxX, minY, minZ)); - AABB.push_back(QVector3D(maxX, minY, maxZ)); - AABB.push_back(QVector3D(maxX, maxY, minZ)); - AABB.push_back(QVector3D(maxX, maxY, maxZ)); + } Model::~Model() //销毁对象 @@ -103,6 +82,32 @@ Model* Model::createModel(QString path, QOpenGLContext* context, QOpenGLShaderPr return new Model(path, context, shaderProgram); } +void Renderer::Model::loadModel(QString path) +{ + directory = path; + Assimp::Importer importer; + const aiScene* scene = importer.ReadFile(directory.absolutePath().toUtf8(), aiProcess_Triangulate | aiProcess_FlipUVs); + if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) + { + qDebug() << "ERROR::ASSIMP::" << importer.GetErrorString() << endl; + return; + } + qDebug() << directory.absolutePath() << "Loaded Successfully"; + qDebug() << "NumMeshes: " << scene->mNumMeshes; + qDebug() << "NumMaterials: " << scene->mNumMaterials; + qDebug() << "NumTextures: " << scene->mNumTextures; + directory.cdUp(); + processNode(scene->mRootNode, scene); + AABB.push_back(QVector3D(minX, minY, minZ)); + AABB.push_back(QVector3D(minX, minY, maxZ)); + AABB.push_back(QVector3D(minX, maxY, minZ)); + AABB.push_back(QVector3D(minX, maxY, maxZ)); + AABB.push_back(QVector3D(maxX, minY, minZ)); + AABB.push_back(QVector3D(maxX, minY, maxZ)); + AABB.push_back(QVector3D(maxX, maxY, minZ)); + AABB.push_back(QVector3D(maxX, maxY, maxZ)); +} + void Model::processNode(aiNode* node, const aiScene* scene, aiMatrix4x4 mat4) { @@ -334,9 +339,9 @@ GLuint Renderer::Model::loadPainting(std::string path) }; vector> style = { std::make_shared(std::vector{ - //strokeStyle - //stroke - 1, + //strokeStyle + //stroke + 1, //strokeWidth 0.01, //strokeEndType @@ -356,20 +361,20 @@ GLuint Renderer::Model::loadPainting(std::string path) //fillStyle //fill 0, - //fillType - 0, //单色 - //fillColorMetallicRoughness - 240 / 255., 220 / 255., 160 / 255., 0.996, 0.18, - }), - std::make_shared(std::vector{ - //fillStyle - //fill - 0, - //fillType - 0, //单色 - //fillColorMetallicRoughness - 24 / 255., 220 / 255., 16 / 255., 0., 0.18, - }), + //fillType + 0, //单色 + //fillColorMetallicRoughness + 240 / 255., 220 / 255., 160 / 255., 0.996, 0.18, + }), + std::make_shared(std::vector{ + //fillStyle + //fill + 0, + //fillType + 0, //单色 + //fillColorMetallicRoughness + 24 / 255., 220 / 255., 16 / 255., 0., 0.18, + }), }; vector> element = { std::make_shared(Element{ contour[0], style[1]}), diff --git a/ArchitectureColoredPainting/src/Renderer/Model.h b/ArchitectureColoredPainting/src/Renderer/Model.h index 51febb5..e5ec36c 100644 --- a/ArchitectureColoredPainting/src/Renderer/Model.h +++ b/ArchitectureColoredPainting/src/Renderer/Model.h @@ -16,7 +16,8 @@ namespace Renderer void drawShadow(); void destroy(); static Model* createModel(QString path, QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram); - Model(QString path, QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* paintingProgram, QOpenGLShaderProgram* shadowProgram, PaintingHelper* paintingHelper); + void loadModel(QString path); + Model(QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* paintingProgram, QOpenGLShaderProgram* shadowProgram, PaintingHelper* paintingHelper); private: Model(QString path, QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram); diff --git a/ArchitectureColoredPainting/src/Renderer/PaintingMesh.cpp b/ArchitectureColoredPainting/src/Renderer/PaintingMesh.cpp index 89ea5e6..0e56389 100644 --- a/ArchitectureColoredPainting/src/Renderer/PaintingMesh.cpp +++ b/ArchitectureColoredPainting/src/Renderer/PaintingMesh.cpp @@ -14,6 +14,7 @@ void PaintingMesh::draw() shaderProgram->bind(); QOpenGLVertexArrayObject::Binder bind(&VAO); shaderProgram->setUniformValue("model", model); + EBO.bind(); glFunc->glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); shaderProgram->release(); @@ -23,6 +24,7 @@ void PaintingMesh::drawShadow() shadowProgram->bind(); QOpenGLVertexArrayObject::Binder bind(&VAO); shadowProgram->setUniformValue("model", model); + EBO.bind(); glFunc->glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); shadowProgram->release(); } diff --git a/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.cpp b/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.cpp index efa688a..2270fe8 100644 --- a/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.cpp +++ b/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.cpp @@ -63,6 +63,13 @@ void RendererGLWidget::stopTimer() timerId = -1; } +bool loadingModel = false; + +void RendererGLWidget::setModel() +{ + loadingModel = true; +} + void RendererGLWidget::setMainLightPitch(float pitch) { //qDebug() << "pitch" << pitch; @@ -212,20 +219,9 @@ void RendererGLWidget::initializeGL() finalProgramPtr->release(); - - paintingHelper = new PaintingHelper(QOpenGLContext::currentContext()->versionFunctions()); - - model = new Model("Models/Sponza/Sponza.gltf", context(), modelProgramPtr, paintingProgramPtr, shadowProgramPtr, paintingHelper); - //model = new Model("E:\\3D Objects\\gallery_gltf\\gallery_gltf.gltf", context(), modelProgramPtr, paintingProgramPtr, shadowProgramPtr, paintingHelper); - light.model = model; - qDebug() << model->AABB; - - paintingHelper->allocateBuffers(); - paintingCompProgramPtr->bind(); - paintingHelper->bindPaintingBuffers(); - paintingCompProgramPtr->release(); - + model = new Model(context(), modelProgramPtr, paintingProgramPtr, shadowProgramPtr, paintingHelper); + quadVAO.create(); QOpenGLVertexArrayObject::Binder vaoBinder(&quadVAO); @@ -242,12 +238,11 @@ void RendererGLWidget::initializeGL() quadVBO.allocate(vertex, sizeof(vertex)); quadVBO.bind(); - QOpenGLFunctions_4_5_Compatibility* f = QOpenGLContext::currentContext()->versionFunctions(); - f->glEnableVertexAttribArray(0); - f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), nullptr); - f->glEnableVertexAttribArray(1); - f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), reinterpret_cast(3 * sizeof(GLfloat))); quadVBO.release(); @@ -257,6 +252,19 @@ void RendererGLWidget::initializeGL() void RendererGLWidget::paintGL() { + if (loadingModel) + { + model->loadModel("Models/Sponza/Sponza.gltf"); + //model = new Model("E:\\3D Objects\\gallery_gltf\\gallery_gltf.gltf", context(), modelProgramPtr, paintingProgramPtr, shadowProgramPtr, paintingHelper); + light.model = model; + qDebug() << model->AABB; + paintingHelper->allocateBuffers(); + paintingCompProgramPtr->bind(); + paintingHelper->bindPaintingBuffers(); + paintingCompProgramPtr->release(); + loadingModel = false; + } + light.lightDirection.setX(cos(qDegreesToRadians(sunPitch)) * cos(qDegreesToRadians(sunYaw))); light.lightDirection.setY(sin(qDegreesToRadians(sunPitch))); light.lightDirection.setZ(cos(qDegreesToRadians(sunPitch)) * sin(qDegreesToRadians(sunYaw))); @@ -275,7 +283,8 @@ void RendererGLWidget::paintGL() glViewport(0, 0, shadowMapResolution, shadowMapResolution); glClear(GL_DEPTH_BUFFER_BIT); //glCullFace(GL_FRONT); - model->drawShadow(); + if (model != nullptr) + model->drawShadow(); //glCullFace(GL_BACK); glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -296,7 +305,8 @@ void RendererGLWidget::paintGL() paintingProgramPtr->setUniformValue("projection", projection); paintingProgramPtr->setUniformValue("view", view); paintingProgramPtr->release(); - model->draw(); + if (model != nullptr) + model->draw(); fboPtr->release(); diff --git a/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.h b/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.h index c602d33..1b1caef 100644 --- a/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.h +++ b/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.h @@ -23,6 +23,7 @@ namespace Renderer void startTimer(); void stopTimer(); public slots: + void setModel(); void setMainLightPitch(float pitch); void setMainLightYaw(float yaw); protected: @@ -63,7 +64,7 @@ namespace Renderer GLuint lightSpaceMatricesUBO; QOpenGLBuffer quadVBO; QOpenGLVertexArrayObject quadVAO; - Model* model; + Model* model = nullptr; PaintingHelper* paintingHelper; }; } diff --git a/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp b/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp index e8cb926..5e295ea 100644 --- a/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp +++ b/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp @@ -11,6 +11,8 @@ RendererWidget::RendererWidget(QWidget *parent) ui.openGLWidget, &RendererGLWidget::setMainLightYaw); ui.horizontalSlider->setValue(105); ui.horizontalSlider_2->setValue(80); + QObject::connect(ui.openButton, &QPushButton::clicked, + ui.openGLWidget, &RendererGLWidget::setModel); } RendererWidget::~RendererWidget() diff --git a/ArchitectureColoredPainting/src/main.cpp b/ArchitectureColoredPainting/src/main.cpp index 1f9fe9e..f1449eb 100644 --- a/ArchitectureColoredPainting/src/main.cpp +++ b/ArchitectureColoredPainting/src/main.cpp @@ -2,6 +2,7 @@ #include "Renderer/Painting/CubicBezier.h" #include #include +#include using Renderer::CubicBezier; @@ -10,11 +11,18 @@ extern "C" _declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; } +FRAMELESSHELPER_USE_NAMESPACE + int main(int argc, char* argv[]) { - QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + //FramelessHelper::Widgets::initialize(); + FramelessHelper::Core::initialize(); + //QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); QApplication a(argc, argv); + FramelessHelper::Core::setApplicationOSThemeAware(); + FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); + FramelessConfig::instance()->set(Global::Option::CenterWindowBeforeShow); MainWindow w; w.show(); return a.exec(); diff --git a/FramelessHelper/.git_/HEAD b/FramelessHelper/.git_/HEAD new file mode 100644 index 0000000..b870d82 --- /dev/null +++ b/FramelessHelper/.git_/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/FramelessHelper/.git_/config b/FramelessHelper/.git_/config new file mode 100644 index 0000000..b8c9dcf --- /dev/null +++ b/FramelessHelper/.git_/config @@ -0,0 +1,16 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = false + logallrefupdates = true + symlinks = false + ignorecase = true +[remote "origin"] + url = https://github.com/wangwenx190/framelesshelper.git + fetch = +refs/heads/*:refs/remotes/origin/* +[branch "main"] + remote = origin + merge = refs/heads/main +[gui] + wmstate = normal + geometry = 1640x853+343+343 665 389 diff --git a/FramelessHelper/.git_/description b/FramelessHelper/.git_/description new file mode 100644 index 0000000..498b267 --- /dev/null +++ b/FramelessHelper/.git_/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/FramelessHelper/.git_/hooks/applypatch-msg.sample b/FramelessHelper/.git_/hooks/applypatch-msg.sample new file mode 100644 index 0000000..a5d7b84 --- /dev/null +++ b/FramelessHelper/.git_/hooks/applypatch-msg.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +commitmsg="$(git rev-parse --git-path hooks/commit-msg)" +test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} +: diff --git a/FramelessHelper/.git_/hooks/commit-msg.sample b/FramelessHelper/.git_/hooks/commit-msg.sample new file mode 100644 index 0000000..b58d118 --- /dev/null +++ b/FramelessHelper/.git_/hooks/commit-msg.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/FramelessHelper/.git_/hooks/fsmonitor-watchman.sample b/FramelessHelper/.git_/hooks/fsmonitor-watchman.sample new file mode 100644 index 0000000..14ed0aa --- /dev/null +++ b/FramelessHelper/.git_/hooks/fsmonitor-watchman.sample @@ -0,0 +1,173 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use IPC::Open2; + +# An example hook script to integrate Watchman +# (https://facebook.github.io/watchman/) with git to speed up detecting +# new and modified files. +# +# The hook is passed a version (currently 2) and last update token +# formatted as a string and outputs to stdout a new update token and +# all files that have been modified since the update token. Paths must +# be relative to the root of the working tree and separated by a single NUL. +# +# To enable this hook, rename this file to "query-watchman" and set +# 'git config core.fsmonitor .git/hooks/query-watchman' +# +my ($version, $last_update_token) = @ARGV; + +# Uncomment for debugging +# print STDERR "$0 $version $last_update_token\n"; + +# Check the hook interface version +if ($version ne 2) { + die "Unsupported query-fsmonitor hook version '$version'.\n" . + "Falling back to scanning...\n"; +} + +my $git_work_tree = get_working_dir(); + +my $retry = 1; + +my $json_pkg; +eval { + require JSON::XS; + $json_pkg = "JSON::XS"; + 1; +} or do { + require JSON::PP; + $json_pkg = "JSON::PP"; +}; + +launch_watchman(); + +sub launch_watchman { + my $o = watchman_query(); + if (is_work_tree_watched($o)) { + output_result($o->{clock}, @{$o->{files}}); + } +} + +sub output_result { + my ($clockid, @files) = @_; + + # Uncomment for debugging watchman output + # open (my $fh, ">", ".git/watchman-output.out"); + # binmode $fh, ":utf8"; + # print $fh "$clockid\n@files\n"; + # close $fh; + + binmode STDOUT, ":utf8"; + print $clockid; + print "\0"; + local $, = "\0"; + print @files; +} + +sub watchman_clock { + my $response = qx/watchman clock "$git_work_tree"/; + die "Failed to get clock id on '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + + return $json_pkg->new->utf8->decode($response); +} + +sub watchman_query { + my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty') + or die "open2() failed: $!\n" . + "Falling back to scanning...\n"; + + # In the query expression below we're asking for names of files that + # changed since $last_update_token but not from the .git folder. + # + # To accomplish this, we're using the "since" generator to use the + # recency index to select candidate nodes and "fields" to limit the + # output to file names only. Then we're using the "expression" term to + # further constrain the results. + if (substr($last_update_token, 0, 1) eq "c") { + $last_update_token = "\"$last_update_token\""; + } + my $query = <<" END"; + ["query", "$git_work_tree", { + "since": $last_update_token, + "fields": ["name"], + "expression": ["not", ["dirname", ".git"]] + }] + END + + # Uncomment for debugging the watchman query + # open (my $fh, ">", ".git/watchman-query.json"); + # print $fh $query; + # close $fh; + + print CHLD_IN $query; + close CHLD_IN; + my $response = do {local $/; }; + + # Uncomment for debugging the watch response + # open ($fh, ">", ".git/watchman-response.json"); + # print $fh $response; + # close $fh; + + die "Watchman: command returned no output.\n" . + "Falling back to scanning...\n" if $response eq ""; + die "Watchman: command returned invalid output: $response\n" . + "Falling back to scanning...\n" unless $response =~ /^\{/; + + return $json_pkg->new->utf8->decode($response); +} + +sub is_work_tree_watched { + my ($output) = @_; + my $error = $output->{error}; + if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) { + $retry--; + my $response = qx/watchman watch "$git_work_tree"/; + die "Failed to make watchman watch '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + $output = $json_pkg->new->utf8->decode($response); + $error = $output->{error}; + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + # Uncomment for debugging watchman output + # open (my $fh, ">", ".git/watchman-output.out"); + # close $fh; + + # Watchman will always return all files on the first query so + # return the fast "everything is dirty" flag to git and do the + # Watchman query just to get it over with now so we won't pay + # the cost in git to look up each individual file. + my $o = watchman_clock(); + $error = $output->{error}; + + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + output_result($o->{clock}, ("/")); + $last_update_token = $o->{clock}; + + eval { launch_watchman() }; + return 0; + } + + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + return 1; +} + +sub get_working_dir { + my $working_dir; + if ($^O =~ 'msys' || $^O =~ 'cygwin') { + $working_dir = Win32::GetCwd(); + $working_dir =~ tr/\\/\//; + } else { + require Cwd; + $working_dir = Cwd::cwd(); + } + + return $working_dir; +} diff --git a/FramelessHelper/.git_/hooks/post-update.sample b/FramelessHelper/.git_/hooks/post-update.sample new file mode 100644 index 0000000..ec17ec1 --- /dev/null +++ b/FramelessHelper/.git_/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info diff --git a/FramelessHelper/.git_/hooks/pre-applypatch.sample b/FramelessHelper/.git_/hooks/pre-applypatch.sample new file mode 100644 index 0000000..4142082 --- /dev/null +++ b/FramelessHelper/.git_/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +precommit="$(git rev-parse --git-path hooks/pre-commit)" +test -x "$precommit" && exec "$precommit" ${1+"$@"} +: diff --git a/FramelessHelper/.git_/hooks/pre-commit.sample b/FramelessHelper/.git_/hooks/pre-commit.sample new file mode 100644 index 0000000..e144712 --- /dev/null +++ b/FramelessHelper/.git_/hooks/pre-commit.sample @@ -0,0 +1,49 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=$(git hash-object -t tree /dev/null) +fi + +# If you want to allow non-ASCII filenames set this variable to true. +allownonascii=$(git config --type=bool hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ASCII filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + cat <<\EOF +Error: Attempt to add a non-ASCII file name. + +This can cause problems if you want to work with people on other platforms. + +To be portable it is advisable to rename the file. + +If you know what you are doing you can disable this check using: + + git config hooks.allownonascii true +EOF + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against -- diff --git a/FramelessHelper/.git_/hooks/pre-merge-commit.sample b/FramelessHelper/.git_/hooks/pre-merge-commit.sample new file mode 100644 index 0000000..399eab1 --- /dev/null +++ b/FramelessHelper/.git_/hooks/pre-merge-commit.sample @@ -0,0 +1,13 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git merge" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message to +# stderr if it wants to stop the merge commit. +# +# To enable this hook, rename this file to "pre-merge-commit". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" +: diff --git a/FramelessHelper/.git_/hooks/pre-push.sample b/FramelessHelper/.git_/hooks/pre-push.sample new file mode 100644 index 0000000..6187dbf --- /dev/null +++ b/FramelessHelper/.git_/hooks/pre-push.sample @@ -0,0 +1,53 @@ +#!/bin/sh + +# An example hook script to verify what is about to be pushed. Called by "git +# push" after it has checked the remote status, but before anything has been +# pushed. If this script exits with a non-zero status nothing will be pushed. +# +# This hook is called with the following parameters: +# +# $1 -- Name of the remote to which the push is being done +# $2 -- URL to which the push is being done +# +# If pushing without using a named remote those arguments will be equal. +# +# Information about the commits which are being pushed is supplied as lines to +# the standard input in the form: +# +# +# +# This sample shows how to prevent push of commits where the log message starts +# with "WIP" (work in progress). + +remote="$1" +url="$2" + +z40=0000000000000000000000000000000000000000 + +while read local_ref local_sha remote_ref remote_sha +do + if [ "$local_sha" = $z40 ] + then + # Handle delete + : + else + if [ "$remote_sha" = $z40 ] + then + # New branch, examine all commits + range="$local_sha" + else + # Update to existing branch, examine new commits + range="$remote_sha..$local_sha" + fi + + # Check for WIP commit + commit=`git rev-list -n 1 --grep '^WIP' "$range"` + if [ -n "$commit" ] + then + echo >&2 "Found WIP commit in $local_ref, not pushing" + exit 1 + fi + fi +done + +exit 0 diff --git a/FramelessHelper/.git_/hooks/pre-rebase.sample b/FramelessHelper/.git_/hooks/pre-rebase.sample new file mode 100644 index 0000000..6cbef5c --- /dev/null +++ b/FramelessHelper/.git_/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up to date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +<<\DOC_END + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". + +DOC_END diff --git a/FramelessHelper/.git_/hooks/pre-receive.sample b/FramelessHelper/.git_/hooks/pre-receive.sample new file mode 100644 index 0000000..a1fd29e --- /dev/null +++ b/FramelessHelper/.git_/hooks/pre-receive.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to make use of push options. +# The example simply echoes all push options that start with 'echoback=' +# and rejects all pushes when the "reject" push option is used. +# +# To enable this hook, rename this file to "pre-receive". + +if test -n "$GIT_PUSH_OPTION_COUNT" +then + i=0 + while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" + do + eval "value=\$GIT_PUSH_OPTION_$i" + case "$value" in + echoback=*) + echo "echo from the pre-receive-hook: ${value#*=}" >&2 + ;; + reject) + exit 1 + esac + i=$((i + 1)) + done +fi diff --git a/FramelessHelper/.git_/hooks/prepare-commit-msg.sample b/FramelessHelper/.git_/hooks/prepare-commit-msg.sample new file mode 100644 index 0000000..10fa14c --- /dev/null +++ b/FramelessHelper/.git_/hooks/prepare-commit-msg.sample @@ -0,0 +1,42 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first one removes the +# "# Please enter the commit message..." help message. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +COMMIT_MSG_FILE=$1 +COMMIT_SOURCE=$2 +SHA1=$3 + +/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" + +# case "$COMMIT_SOURCE,$SHA1" in +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; +# *) ;; +# esac + +# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" +# if test -z "$COMMIT_SOURCE" +# then +# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" +# fi diff --git a/FramelessHelper/.git_/hooks/update.sample b/FramelessHelper/.git_/hooks/update.sample new file mode 100644 index 0000000..5014c4b --- /dev/null +++ b/FramelessHelper/.git_/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to block unannotated tags from entering. +# Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --type=bool hooks.allowunannotated) +allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch) +denycreatebranch=$(git config --type=bool hooks.denycreatebranch) +allowdeletetag=$(git config --type=bool hooks.allowdeletetag) +allowmodifytag=$(git config --type=bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/FramelessHelper/.git_/index b/FramelessHelper/.git_/index new file mode 100644 index 0000000..494e3e0 Binary files /dev/null and b/FramelessHelper/.git_/index differ diff --git a/FramelessHelper/.git_/info/exclude b/FramelessHelper/.git_/info/exclude new file mode 100644 index 0000000..a5196d1 --- /dev/null +++ b/FramelessHelper/.git_/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/FramelessHelper/.git_/objects/pack/pack-61e4808dabd1fe389a00d5df97d0a1d82d56569c.idx b/FramelessHelper/.git_/objects/pack/pack-61e4808dabd1fe389a00d5df97d0a1d82d56569c.idx new file mode 100644 index 0000000..1b5c86a Binary files /dev/null and b/FramelessHelper/.git_/objects/pack/pack-61e4808dabd1fe389a00d5df97d0a1d82d56569c.idx differ diff --git a/FramelessHelper/.git_/objects/pack/pack-61e4808dabd1fe389a00d5df97d0a1d82d56569c.pack b/FramelessHelper/.git_/objects/pack/pack-61e4808dabd1fe389a00d5df97d0a1d82d56569c.pack new file mode 100644 index 0000000..cb7c0d2 Binary files /dev/null and b/FramelessHelper/.git_/objects/pack/pack-61e4808dabd1fe389a00d5df97d0a1d82d56569c.pack differ diff --git a/FramelessHelper/.git_/packed-refs b/FramelessHelper/.git_/packed-refs new file mode 100644 index 0000000..bfebd7c --- /dev/null +++ b/FramelessHelper/.git_/packed-refs @@ -0,0 +1,32 @@ +# pack-refs with: peeled fully-peeled sorted +a7aea71b474138986ec72605f91359d78adac234 refs/remotes/origin/1.x +2f629a9e5be1d377b34912816969cd081f8b7709 refs/remotes/origin/2.0 +1a3d1c82a1aaef372d30a7188809b887e22e3514 refs/remotes/origin/2.1 +d13d74783f91d3d3c9c3fe7b245b7deae4bba33a refs/remotes/origin/2.1.1 +45be647baf276932ebc37dab8cdefe14483c192a refs/remotes/origin/2.1.2 +ff1605ee7b46361bf9dc167597ff39d384b07afd refs/remotes/origin/2.1.3 +06653433d7a772e50d163df6dc4290e38a2a31dd refs/remotes/origin/2.1.4 +5754f2ec20751016d9bcb475285dde70eb0f54ad refs/remotes/origin/2.1.5 +a04fd53a38edd94e6d24a86d68976636d7b55359 refs/remotes/origin/2.1.6 +a89f19af96a478d2775964da31791a92cb210dfc refs/remotes/origin/2.1.7 +f4d20e5f062c4dc12756202a295460310be4bdfd refs/remotes/origin/altairwei/2.0 +a23438b02bbefd24f9277496475e89ae8af9f575 refs/remotes/origin/main +a0a9b8d108cf13795f9e8aa17a16cbcdbc50c250 refs/tags/2.0.0 +3c0209c979d5b6453e5ad79e9c41f6af0224480a refs/tags/2.0.1 +3d7576e06255c92de54465f7eadc631292d350a8 refs/tags/2.0.2 +fd2b3f5e84658f45e4319c4055a69b0e7a94ceda refs/tags/2.0.3 +1a3d1c82a1aaef372d30a7188809b887e22e3514 refs/tags/2.1.0 +d13d74783f91d3d3c9c3fe7b245b7deae4bba33a refs/tags/2.1.1 +45be647baf276932ebc37dab8cdefe14483c192a refs/tags/2.1.2 +ff1605ee7b46361bf9dc167597ff39d384b07afd refs/tags/2.1.3 +06653433d7a772e50d163df6dc4290e38a2a31dd refs/tags/2.1.4 +5754f2ec20751016d9bcb475285dde70eb0f54ad refs/tags/2.1.5 +a04fd53a38edd94e6d24a86d68976636d7b55359 refs/tags/2.1.6 +a89f19af96a478d2775964da31791a92cb210dfc refs/tags/2.1.7 +7a35b09a74b1836f92e256d2241583f283fa45e5 refs/tags/2.2.0 +f052a087dec97925661bdfe8f8bc5f02e68488cc refs/tags/2.2.1 +78638a3e2350955392498679cf0257b58b391aed refs/tags/2.3.0 +3842f4e353447e724ca049cf74e4dea01fce4ac4 refs/tags/2.3.1 +ddb2dcc7f70d44b767d065418eb582ac9ec56048 refs/tags/2.3.2 +524f65cc9bc1382333cd7acf0d31ff88cf6c30fd refs/tags/2.3.3 +9c08c69a4df401c318fccf801797c0c02b9abd68 refs/tags/2.3.4 diff --git a/FramelessHelper/.git_/refs/heads/main b/FramelessHelper/.git_/refs/heads/main new file mode 100644 index 0000000..2abc6ed --- /dev/null +++ b/FramelessHelper/.git_/refs/heads/main @@ -0,0 +1 @@ +a23438b02bbefd24f9277496475e89ae8af9f575 diff --git a/FramelessHelper/.git_/refs/remotes/origin/HEAD b/FramelessHelper/.git_/refs/remotes/origin/HEAD new file mode 100644 index 0000000..4b0a875 --- /dev/null +++ b/FramelessHelper/.git_/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +ref: refs/remotes/origin/main diff --git a/FramelessHelper/.github/workflows/ci.yml b/FramelessHelper/.github/workflows/ci.yml new file mode 100644 index 0000000..6d8839d --- /dev/null +++ b/FramelessHelper/.github/workflows/ci.yml @@ -0,0 +1,70 @@ +name: "CI: Build Test" + +on: + push: + branches: + - main + paths-ignore: + - "**.md" + - "**.png" + - "**.jpg" + - "**.jpeg" + - ".gitignore" + workflow_dispatch: + +jobs: + build: + name: Build + strategy: + matrix: + qt-version: [5.15.2, 6.4.1] + library-type: [shared, static] + platform: [windows-latest, ubuntu-latest, macos-latest] + include: + - platform: windows-latest + CC: cl + CXX: cl + - platform: ubuntu-latest + CC: gcc + CXX: g++ + - platform: macos-latest + CC: clang + CXX: clang++ + - library-type: shared + lib_type_flag: -DFRAMELESSHELPER_BUILD_STATIC=OFF + - library-type: static + lib_type_flag: -DFRAMELESSHELPER_BUILD_STATIC=ON + + runs-on: ${{ matrix.platform }} + + steps: + - name: Check out repository + uses: actions/checkout@v3 + + - name: Install Qt SDK + uses: jurplel/install-qt-action@v3 + with: + version: ${{ matrix.qt-version }} + cache: true + + - name: Set up Ninja + uses: seanmiddleditch/gha-setup-ninja@v3 + with: + version: 1.11.1 # Current latest version. + + - name: Set up MSVC environment + if: ${{ matrix.platform == 'windows-latest' }} + uses: ilammy/msvc-dev-cmd@v1 + + - name: Install Linux dependencies + if: ${{ matrix.platform == 'ubuntu-latest' }} + run: | + sudo apt install -y libgl1-mesa-dev libxcb1-dev libgtk-3-dev + + - name: Build library with CMake + run: | + mkdir ci-test-build + cd ci-test-build + cmake -DCMAKE_C_COMPILER=${{ matrix.CC }} -DCMAKE_CXX_COMPILER=${{ matrix.CXX }} -DCMAKE_INSTALL_PREFIX=../ci-test-install -DCMAKE_BUILD_TYPE=Release -DFRAMELESSHELPER_BUILD_EXAMPLES=ON ${{ matrix.lib_type_flag }} -GNinja .. + cmake --build . --target all --config Release --parallel + cmake --install . --config Release --strip diff --git a/FramelessHelper/.gitignore b/FramelessHelper/.gitignore new file mode 100644 index 0000000..129b761 --- /dev/null +++ b/FramelessHelper/.gitignore @@ -0,0 +1,84 @@ +# C++ objects and libs +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so +*.so.* +*.dll +*.dylib + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.qmlc +*.jsc +Makefile* +*build-* +*.qm +*.prl + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* + +# QtCreator 4.8< compilation database +compile_commands.json + +# QtCreator local machine specific files for imported projects +*creator.user* + +# My +[Bb]in/ +[Bb]in64/ +[Bb]uild*/ +*.7z +*.zip +*.rar +*.tar +*.gz +*.xz +*.exe +*.lib +*.pdb +*.ilk +*.exp +*.obj +build.user.bat +build.user.sh +user.conf +[Dd]oc/ +[Dd]ocs/ +Thumbs.db +*.rc +*.bin +*.run +.qmake.conf +*.res +.DS_Store +.vscode/ +.vs/ +.cmake.conf diff --git a/FramelessHelper/CMakeLists.txt b/FramelessHelper/CMakeLists.txt new file mode 100644 index 0000000..0cd3605 --- /dev/null +++ b/FramelessHelper/CMakeLists.txt @@ -0,0 +1,226 @@ +#[[ + MIT License + + Copyright (C) 2021-2023 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. +]] + +cmake_minimum_required(VERSION 3.20) + +project(FramelessHelper + VERSION 2.3.5.0 + DESCRIPTION "Cross-platform window customization framework for Qt Widgets and Qt Quick." + HOMEPAGE_URL "https://github.com/wangwenx190/framelesshelper/" + LANGUAGES CXX +) + +option(FRAMELESSHELPER_BUILD_STATIC "Build FramelessHelper as a static library." OFF) +option(FRAMELESSHELPER_BUILD_WIDGETS "Build FramelessHelper's Widgets module." ON) +option(FRAMELESSHELPER_BUILD_QUICK "Build FramelessHelper's Quick module." ON) +option(FRAMELESSHELPER_BUILD_EXAMPLES "Build FramelessHelper demo applications." OFF) +option(FRAMELESSHELPER_EXAMPLES_DEPLOYQT "Deploy the Qt framework after building the demo projects." OFF) +option(FRAMELESSHELPER_NO_DEBUG_OUTPUT "Suppress the debug messages from FramelessHelper." OFF) +option(FRAMELESSHELPER_NO_BUNDLE_RESOURCE "Do not bundle any resources within FramelessHelper." OFF) +option(FRAMELESSHELPER_NO_PRIVATE "Do not use any private functionalities from Qt." OFF) +option(FRAMELESSHELPER_ENABLE_VCLTL "MSVC only: link to the system MSVCRT/UCRT and get rid of API sets." OFF) +option(FRAMELESSHELPER_NO_PERMISSIVE_CHECKS "MSVC only: disable the additional permissive checks." OFF) +option(FRAMELESSHELPER_NO_INSTALL "Don't install any files." OFF) + +if(FRAMELESSHELPER_NO_BUNDLE_RESOURCE) + message(WARNING "Nothing will be embeded into FramelessHelper, the chrome buttons will have no icon.") +endif() + +if(FRAMELESSHELPER_ENABLE_VCLTL AND NOT MSVC) + message(WARNING "VC-LTL is only available for the MSVC toolchain.") +endif() + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +if(NOT DEFINED CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE) + # MinGW has many bugs when LTO is enabled, and they are all very + # hard to workaround, so just don't enable LTO at all for MinGW. + if(NOT (MINGW OR FRAMELESSHELPER_BUILD_STATIC)) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON) + endif() +endif() + +if(NOT DEFINED CMAKE_DEBUG_POSTFIX) + if(WIN32) + set(CMAKE_DEBUG_POSTFIX d) + else() + set(CMAKE_DEBUG_POSTFIX _debug) + endif() +endif() + +if(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") +endif() + +if(NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") +endif() + +if(NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") +endif() + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(PROJECT_VERSION_COMMIT "UNKNOWN") +# Get a git hash value. We do not want to use git command here +# because we don't want to make git a build-time dependency. +if(EXISTS "${PROJECT_SOURCE_DIR}/.git/HEAD") + file(READ "${PROJECT_SOURCE_DIR}/.git/HEAD" PROJECT_VERSION_COMMIT) + string(STRIP "${PROJECT_VERSION_COMMIT}" PROJECT_VERSION_COMMIT) + if(PROJECT_VERSION_COMMIT MATCHES "^ref: (.*)") + set(HEAD "${CMAKE_MATCH_1}") + if(EXISTS "${PROJECT_SOURCE_DIR}/.git/${HEAD}") + file(READ "${PROJECT_SOURCE_DIR}/.git/${HEAD}" PROJECT_VERSION_COMMIT) + string(STRIP "${PROJECT_VERSION_COMMIT}" PROJECT_VERSION_COMMIT) + else() + file(READ "${PROJECT_SOURCE_DIR}/.git/packed-refs" PACKED_REFS) + string(REGEX REPLACE ".*\n([0-9a-f]+) ${HEAD}\n.*" "\\1" PROJECT_VERSION_COMMIT "\n${PACKED_REFS}") + endif() + endif() +endif() + +set(PROJECT_COMPILE_DATETIME "UNKNOWN") +string(TIMESTAMP PROJECT_COMPILE_DATETIME UTC) + +if(MSVC) + string(REGEX REPLACE "[-|/]GR-? " " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + string(REGEX REPLACE "[-|/]EHs-?c-? " " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + string(REGEX REPLACE "[-|/]W[0|1|2|3|4|all] " " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + string(APPEND CMAKE_CXX_FLAGS " /GR /EHsc /W4 ") + set(CMAKE_RC_FLAGS "/c65001 /DWIN32 /nologo") + if(MSVC_VERSION GREATER_EQUAL 1920) # Visual Studio 2019 version 16.0 + string(REGEX REPLACE "[-|/]Ob[0|1|2|3] " " " CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) + string(APPEND CMAKE_CXX_FLAGS_RELEASE " /Ob3 ") + endif() + if(FRAMELESSHELPER_ENABLE_VCLTL) + include(src/core/VC-LTL.cmake) + if("x${SupportLTL}" STREQUAL "xtrue") + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" CACHE STRING "" FORCE) + endif() + endif() +endif() + +if(NOT DEFINED QML_IMPORT_PATH) + set(QML_IMPORT_PATH) +endif() +list(APPEND QML_IMPORT_PATH "${PROJECT_BINARY_DIR}/qml") +list(REMOVE_DUPLICATES QML_IMPORT_PATH) +set(QML_IMPORT_PATH ${QML_IMPORT_PATH} CACHE STRING "Qt Creator extra QML import paths" FORCE) + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Gui) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui) + +find_package(QT NAMES Qt6 Qt5 QUIET COMPONENTS Widgets Quick) +find_package(Qt${QT_VERSION_MAJOR} QUIET COMPONENTS Widgets Quick) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion +) + +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + NO_CHECK_REQUIRED_COMPONENTS_MACRO +) + +if(NOT FRAMELESSHELPER_NO_INSTALL) + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + ) +endif() + +if(TARGET Qt${QT_VERSION_MAJOR}::Core AND TARGET Qt${QT_VERSION_MAJOR}::Gui) + add_subdirectory(src) +endif() + +if(FRAMELESSHELPER_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +message("#######################################") +message("CMake version: ${CMAKE_VERSION}") +message("Host system: ${CMAKE_HOST_SYSTEM}") +message("Host processor: ${CMAKE_HOST_SYSTEM_PROCESSOR}") +#message("C compiler: ${CMAKE_C_COMPILER_ID} (${CMAKE_C_COMPILER})") # Currently we are not using any C compilers. +#message("C compiler version: ${CMAKE_C_COMPILER_VERSION}") +message("C++ compiler: ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER})") +message("C++ compiler version: ${CMAKE_CXX_COMPILER_VERSION}") +message("Linker: ${CMAKE_LINKER}") +message("Make program: ${CMAKE_MAKE_PROGRAM}") +message("Build type: ${CMAKE_BUILD_TYPE}") +message("Install prefix: ${CMAKE_INSTALL_PREFIX}") +message("#######################################") +set(__qt_inst_dir) +if(DEFINED Qt6_DIR) + set(__qt_inst_dir "${Qt6_DIR}") +else() + set(__qt_inst_dir "${Qt5_DIR}") +endif() +# /whatever/Qt/6.4.0/gcc_64/lib/cmake/Qt6 +set(__qt_inst_dir ${__qt_inst_dir}/../../..) +cmake_path(NORMAL_PATH __qt_inst_dir) +message("Qt install dir: ${__qt_inst_dir}") +message("Qt version: ${QT_VERSION}") +get_target_property(__qt_type Qt${QT_VERSION_MAJOR}::Core TYPE) +if(__qt_type STREQUAL "STATIC_LIBRARY") + set(__qt_type static) +else() + set(__qt_type shared) +endif() +message("Qt library type: ${__qt_type}") +message("#######################################") +message("FramelessHelper version: ${PROJECT_VERSION}") +message("FramelessHelper commit hash: ${PROJECT_VERSION_COMMIT}") +message("FramelessHelper configure date and time: ${PROJECT_COMPILE_DATETIME} (UTC)") +message("Build the static version of FramelessHelper: ${FRAMELESSHELPER_BUILD_STATIC}") +message("Build the FramelessHelper::Widgets module: ${FRAMELESSHELPER_BUILD_WIDGETS}") +message("Build the FramelessHelper::Quick module: ${FRAMELESSHELPER_BUILD_QUICK}") +message("Build the FramelessHelper demo applications: ${FRAMELESSHELPER_BUILD_EXAMPLES}") +message("Deploy Qt libraries after compilation: ${FRAMELESSHELPER_EXAMPLES_DEPLOYQT}") +message("Suppress debug messages from FramelessHelper: ${FRAMELESSHELPER_NO_DEBUG_OUTPUT}") +message("Do not bundle any resources within FramelessHelper: ${FRAMELESSHELPER_NO_BUNDLE_RESOURCE}") +message("Do not use any private functionalities from Qt: ${FRAMELESSHELPER_NO_PRIVATE}") +message("#######################################") diff --git a/FramelessHelper/FramelessHelperConfig.cmake.in b/FramelessHelper/FramelessHelperConfig.cmake.in new file mode 100644 index 0000000..677781c --- /dev/null +++ b/FramelessHelper/FramelessHelperConfig.cmake.in @@ -0,0 +1,35 @@ +#[[ + MIT License + + Copyright (C) 2021-2023 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. +]] + +@PACKAGE_INIT@ + +set(_@PROJECT_NAME@_supported_components Core Widgets Quick) + +foreach(_comp ${@PROJECT_NAME@_FIND_COMPONENTS}) + if(NOT _comp IN_LIST _@PROJECT_NAME@_supported_components) + set(@PROJECT_NAME@_FOUND FALSE) + set(@PROJECT_NAME@_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}") + endif() + include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@${_comp}Targets.cmake") +endforeach() diff --git a/QGoodWindow/QGoodWindow b/FramelessHelper/LICENSE similarity index 92% rename from QGoodWindow/QGoodWindow rename to FramelessHelper/LICENSE index 6bc1130..b6ae6bf 100644 --- a/QGoodWindow/QGoodWindow +++ b/FramelessHelper/LICENSE @@ -1,7 +1,6 @@ -/* -The MIT License (MIT) +MIT License -Copyright 漏 2021 Antonio Dias +Copyright (C) 2021-2023 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 @@ -20,6 +19,3 @@ 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 "src/qgoodwindow.h" diff --git a/FramelessHelper/README.md b/FramelessHelper/README.md new file mode 100644 index 0000000..be7adfd --- /dev/null +++ b/FramelessHelper/README.md @@ -0,0 +1,393 @@ +# FramelessHelper 2.x + +[![CI: Build Test](https://github.com/wangwenx190/framelesshelper/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/wangwenx190/framelesshelper/actions/workflows/ci.yml) + +Cross-platform window customization framework for Qt Widgets and Qt Quick. Supports Windows, Linux and macOS. + +## Join with Us :triangular_flag_on_post: + +You can join our [Discord channel](https://discord.gg/grrM4Tmesy) to communicate with us. You can share your findings, thoughts and ideas on improving / implementing FramelessHelper functionalities on more platforms and apps! + +## HELP WANTED! + +1. The current CMake package code is not complete and still has some small issues. Need some experienced CMake developers to help me improve it! +2. The current Linux/X11 implementation is not stable enough and still has some bugs. Need some experienced Linux developers to help me improve it! +3. The current macOS implementation is not stable enough and still has some bugs. Need some experienced macOS developers to help me improve it! + +## Roadmap + +- Common: Add cross-platform customizable system menu for both Qt Widgets and Qt Quick. Also supports both light and dark theme. +- Examples: Add QtWebEngine based demo projects for both Qt Widgets and Qt Quick. The whole user interface will be written in HTML instead of C++/QML. +- Examples: Add demo projects that emulate the classic appearance of UWP applications. They will have a backward button on the left side of the title bar and a search box in the middle of the title bar. And maybe a side bar on the left side to switch between different pages. +- Examples: Add demo projects that the main window is not resizable. +- Examples: Add demo projects that have transparent background and doesn't have rectangular window frame. +- Feature requests are welcome! + +## Highlights v2.4 + +- Widgets: Nested frameless windows are supported now! +- Linux: There have been many improvements to the Linux/X11 implementation! Most of them won't be directly visible to the user, but the code quality has been greatly improved. +- Routine bug fixes and internal refactorings. + +## Highlights v2.3 + +- Widgets & Quick: When you clicks the window icon, the system menu will now popup, and when you double clicks it, the window will be closed. This emulates the traditional window behavior on Windows. +- Widgets & Quick: The StandardSystemButton's icon size can't be changed due to I forgot to implement such funtionality, it's implemented now. +- Widgets: Added `FramelessDialog` class. You can use it in case a `QDialog` is preferred over a general `QWidget`. +- Widgets: The StandardSystemButton's size can't be changed due to a FramelessHelper bug, it's fixed now. +- Widgets: Added public interface to change the title label font (family, point size, style, etc...). +- Widgets: The window borders are now drawn by the newly introduced `WindowBorderPainter` class, and it's also exposed publicly, so you'll be able to change how we draw the window border easily. +- Quick: Added `WindowBorder` element. It's a cross-platform window border decorator, and can work without the `FramelessHelper` element. +- Quick: Added `FramelessApplicationWindow` element. It's a simple wrapper of the standard `ApplicationWindow` element, just removes the title bar and adds the window border. +- Windows: Added support for dark theme system menu. The system menu triggered by right-clicking on the title bar will now use the same theme with the current system theme, and will switch between light and dark theme automatically. +- macOS: Added support for old macOS versions and old Qt versions, in theory. +- Common: Internal code improvements & bug fixes. + +## Highlights v2.2 + +- Common: Added blur behind window feature for Windows (7\~11), Linux and macOS. On Windows 11 and macOS, FramelessHelper will make use of the blur effect provided by the OS to get the best appearance and performance, while on Windows 7\~10 and Linux, FramelessHelper will use a homemade blur effect to provide as much consistent experience as possible. +- Common: Added window icon support. It's now possible to set the window icon's image, size and visibility for the standard title bar control. +- Windows: If you are using Qt 6.4+, your Qt Widgets applications will now automatically switch to light/dark theme if the OS theme changes. It requires you are using the default palette provided by Qt. Qt Quick applications will not be affected. +- Linux: FramelessHelper is now theme-aware. If you change your OS theme, FramelessHelper will now emit the theme change signal and refresh it's internal palette. +- Build system: Implemented CMake package support. It's now possible to use `find_package` to find FramelessHelper. +- Build system: Implemented limited QMake support. FramelessHelper doesn't provide complete QMake project, to decrease the maintainance burden, but you can use the .pri files to directly embed FramelessHelper into your own application. +- Examples: Enabled blur behind window and round window corner by default. +- Common: Migrated to categorized logging output. You can now enable or disable some specific debug messages using QLoggingCategory. +- Common: Internal code improvements & bug fixes. + +## Highlights v2.1 + +- Windows: Added support for the snap layout feature introduced in Windows 11. +- Widgets: Redesigned the public interface, the use of FramelessHelper is now more elegant. +- Quick: Redesigned the public interface, the use of FramelessHelper is now more elegant. +- Common: Redesigned the standard title bar interface, it's now possible to customize it from outside. Previously there's no standard title bar in the widgets module, it's now added and exported. +- Doc: Add initial simple documentation to show how to use this library for both Qt Widgets and Qt Quick. +- Misc: Removed bundled Qt internal classes that are licensed under Commercial/GPL/LGPL. This library is now pure MIT licensed. +- Misc: Bug fixes and internal refactorings. + +## Highlights v2.0 + +- Windows: Gained the ability to only remove the title bar but preserve the window frame at the same time. +- Windows: The flicker and jitter during window resizing is completely gone. +- Windows: The system menu will be opened if you right-click on your custom title bar. +- Windows: Replaced Qt's original system menu with FramelessHelper's homemade one, which looks a lot better than the original one. +- Linux: Removed the limitation of the Qt version. The minimum supported version is lowered to Qt 5.6 now (previously was 5.15). +- macOS: Removed the limitation of the Qt version. The minimum supported version is lowered to Qt 5.6 now (previously was 5.15). +- macOS: The frameless window now supports native resizing. +- Common: Almost completely rewritten of the whole library, it's now a lot more easier to setup your own custom title bar than before. +- Common: Added many more helper functions to allow creating your own custom window easier. +- Misc: Reorganized the project structure to be more like a modern library, it's now a lot more friendly to the library users. +- Misc: Many bugs from the 1.x times are fixed (they were not fixable in 1.x due to technical reasons). + +## Screenshots + +### Windows + +![Light](./doc/win_light.png) + +![Dark](./doc/win_dark.png) + +### Linux + +![Light](./doc/linux_light.png) + +![Dark](./doc/linux_dark.png) + +### macOS + +![Light](./doc/mac_light.png) + +![Dark](./doc/mac_dark.png) + +## Use Cases + +### QVogenClient + +![QVogenClient](./doc/QVogenClient.png) + +Vogen editor using **QSynthesis** framework. Repository URL: . + +## Requiredments + +- Compiler: a modern compiler which supports C++17 at least. Tested on MSVC 2022 (Windows), GCC 11 (Linux) and Clang 13 (macOS). +- Qt version: using the latest stable version of Qt is highly recommended, the minimum supported version is Qt 5.6. However, if you are using some old Qt versions (such as older than 5.12), some features may not be available. Only Qt 5.15 and Qt 6 are actively tested. +- Qt modules: QtCore and QtGui for the core module; QtWidgets for the widgets module; QtQuick, QtQuickControls2 and QtQuickTemplates2 for the quick module. +- CMake & ninja: the newer, the better. Other build systems are not tested. + +## Supported Platforms + +- Windows: Windows 7, Windows 8, Windows 8.1, Windows 10, Windows 11 (only actively tested on Windows 10 & 11) +- Linux: any modern Linux distros should work, but only tested on Ubuntu 20.04 and Ubuntu 22.04 +- macOS: only tested on macOS 12.3 due to lack of Apple devices + +There are some additional restrictions for each platform, please refer to the _Platform notes_ section below. + +## Build + +```bash +git clone https://github.com/wangwenx190/framelesshelper.git +mkdir A_TEMP_DIR +cd A_TEMP_DIR +cmake -DCMAKE_PREFIX_PATH= -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE=Release -GNinja +cmake --build . --config Release --target all --parallel +cmake --install . --config Release --strip +``` + +Once the compilation and installation is done, you will be able to use the `find_package(FramelessHelper REQUIRED COMPONENTS Core Widgets Quick)` command to find and link to the FramelessHelper library. But before doing that, please make sure CMake knows where to find FramelessHelper, by passing the `CMAKE_PREFIX_PATH` variable to it. For example: `-DCMAKE_PREFIX_PATH=C:/my-cmake-packages;C:/my-toolchain;etc...`. Build FramelessHelper as a sub-directory of your CMake project is of course also supported. The supported FramelessHelper target names are `FramelessHelper::FramelessHelperCore`, `FramelessHelper::FramelessHelperWidgets` and `FramelessHelper::FramelessHelperQuick`. + +## Use + +### Qt Widgets + +To customize the window frame of a QWidget, you need to instantiate a `FramelessWidgetsHelper` object and then attach it to the widget's top level widget, and then `FramelessWidgetsHelper` will do all the rest work for you: the window frame will be removed automatically once it has been attached to the top level widget successfully. In theory you can instantiate multiple `FramelessWidgetsHelper` objects for a same widget, in this case there will be only one object that keeps functional, all other objects will become a wrapper of that one. But to make sure everything goes smoothly and normally, you should not do that in any case. The simplest way to instantiate a `FramelessWidgetsHelper` +object is to call the static method `FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)`. It will return the handle of the previously instantiated object if any, or it will instantiate a new object if it can't find one. It's safe to call this method multiple times for a same widget, it won't instantiate any new objects if there is one already. It also does not matter when and where you call that function as long as the top level widget is the same. The internally created objects will always be parented to the top level widget. Once you get the handle of the `FramelessWidgetsHelper` object, you can call `void FramelessWidgetsHelper::setContentExtendedIntoTitleBar(true)` to let it hide the default title bar provided by the operating system. In order to make sure `FramelessWidgetsHelper` can find the correct top level widget, you should call the `FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)` function on a widget which has a complete parent-chain whose root parent is the top level widget. To make the frameless window draggable, you should provide a homemade title bar widget yourself, the title bar widget doesn't need to be in rectangular shape, it also doesn't need to be placed on the first row of the window. Call `void FramelessWidgetsHelper::setTitleBarWidget(QWidget *)` to let `FramelessHelper` know what's your title bar widget. By default, all the widgets in the title bar area won't be responsible to any mouse and keyboard events due to they have been intercepted by FramelessHelper. To make them recover the responsible state, you should make them visible to hit test. Call `void FramelessWidgetsHelper::setHitTestVisible(QWidget* )` to do that. You can of course call it on a widget that is not inside the title bar at all, it won't have any effect though. Due to Qt's own limitations, you need to make sure your widget has a complete parent-chain whose root parent is the top level widget. Do not ever try to delete the `FramelessWidgetsHelper` object, it may still be monitoring and controlling your widget, and Qt will delete it for you automatically. No need to worry about memory leaks. + +There are also two classes called `FramelessWidget` and `FramelessMainWindow`, they are only simple wrappers of `FramelessWidgetsHelper`, which just saves the call of the `void FramelessWidgetsHelper::setContentExtendedIntoTitleBar(true)` function for you. You can absolutely use plain `QWidget` instead. + +#### Code Snippet + +First of all, call `void FramelessHelper::Widgets::initialize()` in your `main` function in a very early stage: + +```cpp +int main(int, char **) +{ + FramelessHelper::Widgets::initialize(); + // ... +} +``` + +Then hide the standard title bar provided by the OS: + +```cpp +MyWidget::MyWidget(QWidget *parent) : QWidget(parent) +{ + // You should do this early enough. + FramelessWidgetsHelper::get(this)->setContentExtendedIntoTitleBar(true); + // ... +} +``` + +Then let `FramelessHelper` know what should be the title bar: + +```cpp +void MyWidget::myFunction() +{ + // ... + FramelessWidgetsHelper::get(this)->setTitleBarWidget(m_myTitleBarWidget); + // ... +} +``` + +Then make some widgets inside your title bar visible to hit test: + +```cpp +void MyWidget::myFunction2() +{ + // ... + FramelessWidgetsHelper::get(this)->setHitTestVisible(m_someSearchBox); + FramelessWidgetsHelper::get(this)->setHitTestVisible(m_someButton); + FramelessWidgetsHelper::get(this)->setHitTestVisible(m_someMenuItem); + // ... +} +``` + +**IMPORTANT NOTE for Qt Widgets applications**: Some functionalities may only be available when `FramelessHelper` has finished the window customization process, such as changing window geometry/flags/state. In this case you can connect to the public `void ready()` signal of `FramelessHelper` to get the accurate time point and do your rest initialization process afterwards. + +### Qt Quick + +#### Code Snippet + +First of all, you should call `void FramelessHelper::Quick::initialize()` in your `main` function in a very early stage: + +```cpp +int main(int, char **) +{ + FramelessHelper::Quick::initialize(); + // ... +} +``` + +Then you need to register the custom types provided by `FramelessHelper` by calling `void FramelessHelper::Quick::registerTypes(QQmlEngine *)`, before the QML engine loads any QML documents: + +```cpp +int main(int, char **) +{ + // ... + QQmlApplicationEngine engine; + FramelessHelper::Quick::registerTypes(&engine); + // ... +} +``` + +Now you can write your QML documents. You should import `FramelessHelper` from the URI `org.wangwenx190.FramelessHelper`. You should specify a version number right after it if you are using Qt5: + +```qml +import org.wangwenx190.FramelessHelper 1.0 // You can use "auto" or omit the version number in Qt6. +``` + +And then you can use the attached properties from the QML type `FramelessHelper`: + +```qml +Window { + Item { + id: myTitleBar + Item { id: someControl1 } + Item { id: someControl2 } + Item { id: someControl3 } + Component.onCompleted: { + // Don't access FramelessHelper too early, otherwise it may not be able to find the root window! + FramelessHelper.titleBarItem = myTitleBar; + FramelessHelper.setHitTestVisible(someControl1); + FramelessHelper.setHitTestVisible(someControl2); + FramelessHelper.setHitTestVisible(someControl3); + } + } +} +``` + +It's the same with the `FramelessWidgetsHelper` interface, the QML type `FramelessHelper` will be instantiated only once for each `Window`, no matter when and where you use attached properties from it. However, due to the special design of the `FramelessHelper` type, you can also use it just like a normal QML type: + +```qml +Window { + Item { + id: myTitleBar + Item { id: someControl1 } + Item { id: someControl2 } + Item { id: someControl3 } + Component.onCompleted: { + framelessHelper.setHitTestVisible(someControl1); + framelessHelper.setHitTestVisible(someControl2); + framelessHelper.setHitTestVisible(someControl3); + } + } + FramelessHelper { + id: framelessHelper + titleBarItem: myTitleBar + } +} +``` + +In theory it's possible to instantiate multiple `FramelessHelper` objects for a same `Window`, in this case only one of them will keep functional, all other objects will become a wrapper of it, but doing so is not recommended and may cause unexpected behavior or bugs, so please avoid trying to do that in any case. + +If you find any of `FramelessHelper` functions have no effect after calling, the most possible reason is by the time you call the function/change the property of `FramelessHelper`, the root window has not finished its initialization process and thus `FramelessHelper` can't get the handle of it, so any action from the user will be ignored until the root window finished initialization. + +There's also a QML type called `FramelessWindow`, it's only a simple wrapper of `FramelessHelper`, you can absolutely use plain `Window` instead. + +**IMPORTANT NOTE for Qt Quick applications**: Some functionalities may only be available when `FramelessHelper` has finished the window customization process, such as changing window geometry/flags/state. In this case you can connect to the public `void ready()` signal of `FramelessHelper` to get the accurate time point and do your rest initialization process afterwards: + +```qml +Window { + FramelessHelper.onReady: { + // do something here ... + } +} +``` + +```qml +Window { + FramelessHelper { + onReady: { + // do something here ... + } + } +} +``` + +**IMPORTANT NOTE for all applications**: Once you called `QWidget::close()` or `Q(Quick)Window::close()`, Qt will release all the resources of the corresponding widget/window, and thus FramelessHelper's custom event handler will also be removed from them at the same time. However, this will make the title bar become unresponsible if you re-open the widget/window. The current workaround for this issue is to hide the widget/window instead of closing it, if you are going to show it again later. But if you have no plan to show the widget/window again after it has been closed, you don't need to do anything to workaround this issue. + +### More + +Please refer to the demo projects to see more detailed usages: [examples](./examples/) + +### Title Bar Design Guidance + +- Microsoft: +- KDE: +- GNOME: +- Apple: + +## Platform Notes + +### Windows + +- If DWM composition is disabled in some very rare cases (only possible on Windows 7), the top-left corner and top-right corner will appear in round shape. The round corners can be restored to square if you re-enable DWM composition. +- There's an OpenGL driver bug which will cause some frameless windows have a strange black bar right on top of your homemade title bar, and it also makes the controls in your windows shifted to the bottom-right corner for some pixels. It's a bug of your graphics card driver, specifically, your OpenGL driver, not FramelessHelper. There are some solutions provided by our users but some of them may not work in all conditions, you can pick one from them: + + Solution | Principle + -------- | --------- + Upgrade the graphics driver | Try to use a newer driver which may ship with the fix + Change the system theme to "Basic" (in contrary to "Windows Aero") | Let Windows use pure software rendering + If there are multiple graphics cards, use another one instead | Try to use a different driver which may don't have such bug at all + Upgrade the system to at least Windows 11 | Windows 11 redesigned the windowing system so the bug can no longer be triggered + Remove the `WS_THICKFRAME` and `WS_OVERLAPPED` styles from the window, and maybe also add the `WS_POPUP` style at the same time, and don't do anything inside the `WM_NCCALCSIZE` block (just return `false` directly or remove/comment out the whole block) | Try to mirror Qt's `FramelessWindowHint`'s behavior + Use `Qt::FramelessWindowHint` instead of doing the `WM_NCCALCSIZE` trick | Qt's rendering code path is totally different between these two solutions + Force Qt to use the ANGLE backend instead of the Desktop OpenGL | ANGLE will translate OpenGL directives into D3D ones + Force Qt to use pure software rendering instead of rendering through OpenGL | Qt is not using OpenGL at all + Force Qt to use the Mesa 3D libraries instead of normal OpenGL | Try to use a different OpenGL implementation + Use Direct3D/Vulkan/Metal instead of OpenGL | Just don't use the buggy OpenGL + + If you are lucky enough, one of them may fix the issue for you. If not, you may try to use multiple solutions together. **But I can't guarantee the issue can 100% be fixed.** +- Due to there are many sub-versions of Windows 10, it's highly recommended to use the latest version of Windows 10, at least **no older than Windows 10 1809**. If you try to use this framework on some very old Windows 10 versions such as 1507 or 1607, there may be some compatibility issues. Using this framework on Windows 7 is also supported but not recommended. To get the most stable behavior and the best appearance, you should use it on the latest version of Windows 10 or Windows 11. +- To make the snap layout work as expected, there are some additional rules for your homemade system buttons to follow: + - **Add a manifest file to your application. In the manifest file, you need to claim your application supports Windows 11 explicitly. This step is VERY VERY IMPORTANT. Without this step, the snap layout feature can't be enabled.** + - Make sure there are two public invokable functions (slot functions are always invokable): `void setHovered(bool)` and `void setPressed(bool)`. These two functions will be invoked by FramelessHelper when the button is being hovered or pressed. You should change the button's visual state inside these functions. If you need to show tooltips, you'll have to do it manually in these functions. + - Make sure there's a public signal: `void clicked()`. When the button is being clicked, that signal will be triggered by FramelessHelper. You should connect your event handler to that signal. + - For Qt Quick applications, for the C++ side, you need to inherit your button from the `QQuickAbstractButton` class, for the QML side, you need to inherit your button from the `Button` type (from the `QtQuick.Controls.Basic` module). They have all the invokable functions and signals we need, so no more extra work is needed. + - Don't forget to call `setSystemButton()` for each button to let FramelessHelper know which is the minimize/maximize/close button. + - System buttons will not be able to receive any actual mouse and keyboard events so there's no need to handle these events inside these buttons. That's also why we need to set the button's visual state manually. + - I know this is making everything complicated but unfortunately we can't avoid this mess if we need to support the snap layout feature. Snap layout is really only designed for the original standard window frame, so if we want to forcely support it without a standard window frame, many black magic will be needed. + +### Linux + +- FramelessHelper will force your application to use the _XCB_ platform plugin when running on Wayland. +- The resize area is inside of the window. + +### macOS + +- The frameless windows will appear in square corners instead of round corners (Qt Widgets applications only). +- The resize area is inside of the window. +- Some users reported that the window is not resizable on some old macOS versions. + +## FAQs + +### `When running on Win10, it seems the top border is missing? But the demo applications still have it?` + +`FramelessHelper` hides the system title bar by removing the whole top part of the window frame, including the top border. There's no way to only remove the system title bar but still preserve the top border at the same time, even Microsoft themself can't do that either. The exact reason is unknown to non-Microsoft developers, and I have no interest in digging into all the magic behind it. So you'll have to draw one manually yourself to pretend the top border is still there. You can retrieve it's height and color through official DWM APIs. Please refer to the documentation of [`DwmGetWindowAttribute()`](https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmgetwindowattribute) and [`DwmGetColorizationColor()`](https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmgetcolorizationcolor). The demo applications still have the top border because their main windows all inherit from `FramelessWidget` or `FramelessMainWindow`, which will draw the top border for you internally. As for Qt Quick, the QML type `FramelessWindow` will also draw the top border. + +### `When running on Wayland, dragging the title bar causes crash?` + +You need to force Qt to use the **XCB** QPA when running on Wayland. Try setting the environment variable `QT_QPA_PLATFORM` (case sensitive) to `xcb` (case sensitive) before instantiating any `Q(Gui)Application` instances. Or just call `void FramelessHelper::Widgets/Quick::initialize()` in your `main` function, this function will take care of it for you. + +### `I can see the black background during window resizing?` + +First of all, it's a Qt issue, not caused by FramelessHelper. And it should not be possible for Qt Widgets applications. It's a common issue for Qt Quick applications. Most of the time it's caused by D3D11/Vulkan/Metal because they are not good at dealing with texture resizing operations. If you really want to fix this issue, you can try to change Qt's RHI backend to **OpenGL** (be careful of the bug of your graphics card driver) or **Software** (if you don't care about performance). And please keep in mind that this issue is not fixable from outside of Qt. + +### `Can I preserve the window frame border even on Win7? How does Google Chrome/Microsoft Edge's installer achieve that?` + +Short answer: it's impossible. Full explaination: of course we can use the same technique we use on Win10 to remove the whole top part of the window and preserve the other three frame borders at the same time, but on Win10 we can bring the top border back, either by doing some black magic in the `WM_PAINT` handler or draw a thin frame border manually ourself, however, it's impossible to do this on Win7. I've tried it on Win7 already and sadly the result is the `WM_PAINT` trick won't work on Win7, and we also can't draw a frame border which looks very similar to the original one (a semi-transparent rectangle, blended with system's accent color and the visual content behind the window, also with some blur effect applied). But it seems Google Chrome/Microsoft Edge's installer have achieved what we wanted to do, how? Well, their installer is open source and I've read it's code already. They achieve that by overlapping two windows, one normal window on the bottom, another border-less window on the top to cover the bottom window's title bar. They draw their homemade title bar on the border-less window and use it to emulate the standard title bar's behavior. The original title bar provided by the system is still there, but it can't be seen by anyone just because it's covered by another window. I admit it's a good solution in such cases but for our library it's not appropriate because the code complexity will blow up. + +## License + +```text +MIT License + +Copyright (C) 2021-2023 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. +``` diff --git a/FramelessHelper/examples/CMakeLists.txt b/FramelessHelper/examples/CMakeLists.txt new file mode 100644 index 0000000..cc3ce56 --- /dev/null +++ b/FramelessHelper/examples/CMakeLists.txt @@ -0,0 +1,34 @@ +#[[ + MIT License + + Copyright (C) 2021-2023 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. +]] + +if(FRAMELESSHELPER_BUILD_WIDGETS AND TARGET Qt${QT_VERSION_MAJOR}::Widgets) + add_subdirectory(widget) + add_subdirectory(mainwindow) + #add_subdirectory(openglwidget) # Currently not functional. + add_subdirectory(dialog) +endif() + +if(FRAMELESSHELPER_BUILD_QUICK AND TARGET Qt${QT_VERSION_MAJOR}::Quick AND (${QT_VERSION_MAJOR} GREATER_EQUAL 6) AND (NOT FRAMELESSHELPER_NO_PRIVATE)) + add_subdirectory(quick) +endif() diff --git a/FramelessHelper/examples/dialog/CMakeLists.txt b/FramelessHelper/examples/dialog/CMakeLists.txt new file mode 100644 index 0000000..922d741 --- /dev/null +++ b/FramelessHelper/examples/dialog/CMakeLists.txt @@ -0,0 +1,55 @@ +#[[ + MIT License + + Copyright (C) 2021-2023 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. +]] + +set(SOURCES + ../shared/log.h + ../shared/log.cpp + ../shared/settings.h + ../shared/settings.cpp + dialog.h + dialog.cpp + main.cpp +) + +if(WIN32) + enable_language(RC) + list(APPEND SOURCES + ../shared/example.rc + ../shared/example.manifest + ) +endif() + +add_executable(Dialog ${SOURCES}) + +target_link_libraries(Dialog PRIVATE + Qt${QT_VERSION_MAJOR}::Widgets + FramelessHelper::Widgets +) + +include(../../src/core/cmakehelper.cmake) +setup_gui_app(Dialog) +setup_compile_params(Dialog) +if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT) + deploy_qt_runtime(Dialog) +endif() diff --git a/FramelessHelper/examples/dialog/dialog.cpp b/FramelessHelper/examples/dialog/dialog.cpp new file mode 100644 index 0000000..c006882 --- /dev/null +++ b/FramelessHelper/examples/dialog/dialog.cpp @@ -0,0 +1,151 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "dialog.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../shared/settings.h" + +extern template void Settings::set(const QString &, const QString &, const QRect &); +extern template void Settings::set(const QString &, const QString &, const qreal &); + +extern template QRect Settings::get(const QString &, const QString &); +extern template qreal Settings::get(const QString &, const QString &); + +FRAMELESSHELPER_USE_NAMESPACE + +using namespace Global; + +FRAMELESSHELPER_STRING_CONSTANT(Geometry) +FRAMELESSHELPER_STRING_CONSTANT(DevicePixelRatio) + +Dialog::Dialog(QWidget *parent) : FramelessDialog(parent) +{ + setupUi(); +} + +Dialog::~Dialog() = default; + +void Dialog::closeEvent(QCloseEvent *event) +{ + if (!parent()) { + Settings::set({}, kGeometry, geometry()); + Settings::set({}, kDevicePixelRatio, devicePixelRatioF()); + } + FramelessDialog::closeEvent(event); +} + +void Dialog::setupUi() +{ + setWindowTitle(tr("Qt Dialog demo")); + setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer)); + + titleBar = new StandardTitleBar(this); + titleBar->setWindowIconVisible(true); +#ifndef Q_OS_MACOS + titleBar->maximizeButton()->hide(); +#endif // Q_OS_MACOS + + label = new QLabel(tr("Find &what:")); + lineEdit = new QLineEdit; + label->setBuddy(lineEdit); + + caseCheckBox = new QCheckBox(tr("Match &case")); + fromStartCheckBox = new QCheckBox(tr("Search from &start")); + fromStartCheckBox->setChecked(true); + + findButton = new QPushButton(tr("&Find")); + findButton->setDefault(true); + connect(findButton, &QPushButton::clicked, this, [this](){ + const QString text = lineEdit->text(); + if (text.isEmpty()) { + QMessageBox::warning(this, tr("Warning"), tr("You didn't enter anything in the search box.")); + } else { + QMessageBox::information(this, tr("Result"), tr("You wanted to find: \"%1\".").arg(text)); + } + }); + + moreButton = new QPushButton(tr("&More")); + moreButton->setCheckable(true); + moreButton->setAutoDefault(false); + + extension = new QWidget; + + wholeWordsCheckBox = new QCheckBox(tr("&Whole words")); + backwardCheckBox = new QCheckBox(tr("Search &backward")); + searchSelectionCheckBox = new QCheckBox(tr("Search se&lection")); + + buttonBox = new QDialogButtonBox(Qt::Vertical); + buttonBox->addButton(findButton, QDialogButtonBox::ActionRole); + buttonBox->addButton(moreButton, QDialogButtonBox::ActionRole); + + connect(moreButton, &QPushButton::toggled, extension, &QWidget::setVisible); + + QVBoxLayout *extensionLayout = new QVBoxLayout; + extensionLayout->setContentsMargins(0, 0, 0, 0); + extensionLayout->addWidget(wholeWordsCheckBox); + extensionLayout->addWidget(backwardCheckBox); + extensionLayout->addWidget(searchSelectionCheckBox); + extension->setLayout(extensionLayout); + + QHBoxLayout *topLeftLayout = new QHBoxLayout; + topLeftLayout->addWidget(label); + topLeftLayout->addWidget(lineEdit); + + QVBoxLayout *leftLayout = new QVBoxLayout; + leftLayout->addLayout(topLeftLayout); + leftLayout->addWidget(caseCheckBox); + leftLayout->addWidget(fromStartCheckBox); + + QGridLayout *controlsLayout = new QGridLayout; + controlsLayout->setContentsMargins(11, 11, 11, 11); + controlsLayout->addLayout(leftLayout, 0, 0); + controlsLayout->addWidget(buttonBox, 0, 1); + controlsLayout->addWidget(extension, 1, 0, 1, 2); + controlsLayout->setRowStretch(2, 1); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSizeConstraint(QLayout::SetFixedSize); + mainLayout->addWidget(titleBar); + mainLayout->addLayout(controlsLayout); + + setLayout(mainLayout); + + extension->hide(); + + FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this); + helper->setTitleBarWidget(titleBar); +#ifndef Q_OS_MACOS + helper->setSystemButton(titleBar->minimizeButton(), SystemButtonType::Minimize); + helper->setSystemButton(titleBar->maximizeButton(), SystemButtonType::Maximize); + helper->setSystemButton(titleBar->closeButton(), SystemButtonType::Close); +#endif // Q_OS_MACOS + // Special hack to disable the overriding of the mouse cursor, it's totally different + // with making the window un-resizable: we still want the window be able to resize + // programatically, but we also want the user not able to resize the window manually. + // So apparently we can't use QWidget::setFixedWidth/Height/Size() here. + FramelessWidgetsHelperPrivate::get(helper)->setProperty(kDontOverrideCursorVar, true); + connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){ + const auto savedGeometry = Settings::get({}, kGeometry); + if (savedGeometry.isValid() && !parent()) { + const auto savedDpr = Settings::get({}, kDevicePixelRatio); + // Qt doesn't support dpr < 1. + const qreal oldDpr = std::max(savedDpr, qreal(1)); + const qreal scale = (devicePixelRatioF() / oldDpr); + setGeometry({savedGeometry.topLeft() * scale, savedGeometry.size() * scale}); + } else { + helper->moveWindowToDesktopCenter(); + } + }); +} diff --git a/FramelessHelper/examples/dialog/dialog.h b/FramelessHelper/examples/dialog/dialog.h new file mode 100644 index 0000000..651d2e9 --- /dev/null +++ b/FramelessHelper/examples/dialog/dialog.h @@ -0,0 +1,49 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#pragma once + +#include + +QT_BEGIN_NAMESPACE +class QCheckBox; +class QDialogButtonBox; +class QGroupBox; +class QLabel; +class QLineEdit; +class QPushButton; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE +class StandardTitleBar; +FRAMELESSHELPER_END_NAMESPACE + +class Dialog : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessDialog) +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(Dialog) + +public: + explicit Dialog(QWidget *parent = nullptr); + ~Dialog() override; + +protected: + void closeEvent(QCloseEvent *event) override; + +private: + void setupUi(); + +private: + FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar) *titleBar = nullptr; + QLabel *label = nullptr; + QLineEdit *lineEdit = nullptr; + QCheckBox *caseCheckBox = nullptr; + QCheckBox *fromStartCheckBox = nullptr; + QCheckBox *wholeWordsCheckBox = nullptr; + QCheckBox *searchSelectionCheckBox = nullptr; + QCheckBox *backwardCheckBox = nullptr; + QDialogButtonBox *buttonBox = nullptr; + QPushButton *findButton = nullptr; + QPushButton *moreButton = nullptr; + QWidget *extension = nullptr; +}; diff --git a/FramelessHelper/examples/dialog/main.cpp b/FramelessHelper/examples/dialog/main.cpp new file mode 100644 index 0000000..f88bcb6 --- /dev/null +++ b/FramelessHelper/examples/dialog/main.cpp @@ -0,0 +1,63 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 +#include +#include +#include "dialog.h" +#include "../shared/log.h" + +FRAMELESSHELPER_USE_NAMESPACE + +int main(int argc, char *argv[]) +{ + std::setlocale(LC_ALL, "en_US.UTF-8"); + + Log::setup(FRAMELESSHELPER_STRING_LITERAL("dialog")); + + // Not necessary, but better call this function, before the construction + // of any Q(Core|Gui)Application instances. + FramelessHelper::Widgets::initialize(); + +#if 0 + if (!qEnvironmentVariableIsSet("QT_WIDGETS_RHI")) { + qputenv("QT_WIDGETS_RHI", FRAMELESSHELPER_BYTEARRAY_LITERAL("1")); + } +#endif + + const auto application = std::make_unique(argc, argv); + + // Must be called after QGuiApplication has been constructed, we are using + // some private functions from QPA which won't be available until there's + // a QGuiApplication instance. + FramelessHelper::Core::setApplicationOSThemeAware(); + + FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); + FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial); + + const auto dialog = std::make_unique(); + dialog->show(); + + return QCoreApplication::exec(); +} diff --git a/FramelessHelper/examples/mainwindow/CMakeLists.txt b/FramelessHelper/examples/mainwindow/CMakeLists.txt new file mode 100644 index 0000000..fa4c7ee --- /dev/null +++ b/FramelessHelper/examples/mainwindow/CMakeLists.txt @@ -0,0 +1,60 @@ +#[[ + MIT License + + Copyright (C) 2021-2023 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. +]] + +set(SOURCES + ../shared/log.h + ../shared/log.cpp + ../shared/settings.h + ../shared/settings.cpp + ../dialog/dialog.h + ../dialog/dialog.cpp + ../widget/widget.h + ../widget/widget.cpp + mainwindow.ui + mainwindow.h + mainwindow.cpp + main.cpp +) + +if(WIN32) + enable_language(RC) + list(APPEND SOURCES + ../shared/example.rc + ../shared/example.manifest + ) +endif() + +add_executable(MainWindow ${SOURCES}) + +target_link_libraries(MainWindow PRIVATE + Qt${QT_VERSION_MAJOR}::Widgets + FramelessHelper::Widgets +) + +include(../../src/core/cmakehelper.cmake) +setup_gui_app(MainWindow) +setup_compile_params(MainWindow) +if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT) + deploy_qt_runtime(MainWindow) +endif() diff --git a/FramelessHelper/examples/mainwindow/main.cpp b/FramelessHelper/examples/mainwindow/main.cpp new file mode 100644 index 0000000..a1401da --- /dev/null +++ b/FramelessHelper/examples/mainwindow/main.cpp @@ -0,0 +1,63 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 +#include +#include +#include "mainwindow.h" +#include "../shared/log.h" + +FRAMELESSHELPER_USE_NAMESPACE + +int main(int argc, char *argv[]) +{ + std::setlocale(LC_ALL, "en_US.UTF-8"); + + Log::setup(FRAMELESSHELPER_STRING_LITERAL("mainwindow")); + + // Not necessary, but better call this function, before the construction + // of any Q(Core|Gui)Application instances. + FramelessHelper::Widgets::initialize(); + +#if 0 + if (!qEnvironmentVariableIsSet("QT_WIDGETS_RHI")) { + qputenv("QT_WIDGETS_RHI", FRAMELESSHELPER_BYTEARRAY_LITERAL("1")); + } +#endif + + const auto application = std::make_unique(argc, argv); + + // Must be called after QGuiApplication has been constructed, we are using + // some private functions from QPA which won't be available until there's + // a QGuiApplication instance. + FramelessHelper::Core::setApplicationOSThemeAware(); + + FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); + FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial); + + const auto mainWindow = std::make_unique(); + mainWindow->show(); + + return QCoreApplication::exec(); +} diff --git a/FramelessHelper/examples/mainwindow/mainwindow.cpp b/FramelessHelper/examples/mainwindow/mainwindow.cpp new file mode 100644 index 0000000..537f0f5 --- /dev/null +++ b/FramelessHelper/examples/mainwindow/mainwindow.cpp @@ -0,0 +1,139 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "mainwindow.h" +#include "ui_mainwindow.h" +#include +#include +#include +#include +#include +#include +#include "../shared/settings.h" +#include "../widget/widget.h" +#include "../dialog/dialog.h" + +extern template void Settings::set(const QString &, const QString &, const QRect &); +extern template void Settings::set(const QString &, const QString &, const qreal &); +extern template void Settings::set(const QString &, const QString &, const QByteArray &); + +extern template QRect Settings::get(const QString &, const QString &); +extern template qreal Settings::get(const QString &, const QString &); +extern template QByteArray Settings::get(const QString &, const QString &); + +FRAMELESSHELPER_USE_NAMESPACE + +using namespace Global; + +FRAMELESSHELPER_STRING_CONSTANT(Geometry) +FRAMELESSHELPER_STRING_CONSTANT(State) +FRAMELESSHELPER_STRING_CONSTANT(DevicePixelRatio) + +MainWindow::MainWindow(QWidget *parent, const Qt::WindowFlags flags) : FramelessMainWindow(parent, flags) +{ + initialize(); +} + +MainWindow::~MainWindow() = default; + +void MainWindow::closeEvent(QCloseEvent *event) +{ + if (!parent()) { + Settings::set({}, kGeometry, geometry()); + Settings::set({}, kState, saveState()); + Settings::set({}, kDevicePixelRatio, devicePixelRatioF()); + } + FramelessMainWindow::closeEvent(event); +} + +void MainWindow::initialize() +{ + m_titleBar = new StandardTitleBar(this); + m_titleBar->setTitleLabelAlignment(Qt::AlignCenter); + m_mainWindow = new Ui::MainWindow; + m_mainWindow->setupUi(this); + + QMenuBar * const mb = menuBar(); + mb->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); + mb->setStyleSheet(FRAMELESSHELPER_STRING_LITERAL(R"( +QMenuBar { + background-color: transparent; +} + +QMenuBar::item { + background: transparent; +} + +QMenuBar::item:selected { + background: #a8a8a8; +} + +QMenuBar::item:pressed { + background: #888888; +} + )")); + const auto titleBarLayout = static_cast(m_titleBar->layout()); + titleBarLayout->insertWidget(0, mb); + + // setMenuWidget(): make the menu widget become the first row of the window. + setMenuWidget(m_titleBar); + + FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this); + helper->setTitleBarWidget(m_titleBar); +#ifndef Q_OS_MACOS + helper->setSystemButton(m_titleBar->minimizeButton(), SystemButtonType::Minimize); + helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize); + helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close); +#endif // Q_OS_MACOS + helper->setHitTestVisible(mb); // IMPORTANT! + connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){ + const auto savedGeometry = Settings::get({}, kGeometry); + if (savedGeometry.isValid() && !parent()) { + const auto savedDpr = Settings::get({}, kDevicePixelRatio); + // Qt doesn't support dpr < 1. + const qreal oldDpr = std::max(savedDpr, qreal(1)); + const qreal scale = (devicePixelRatioF() / oldDpr); + setGeometry({savedGeometry.topLeft() * scale, savedGeometry.size() * scale}); + } else { + helper->moveWindowToDesktopCenter(); + } + const QByteArray savedState = Settings::get({}, kState); + if (!savedState.isEmpty() && !parent()) { + restoreState(savedState); + } + }); + + setWindowTitle(tr("FramelessHelper demo application - Qt MainWindow")); + setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer)); + connect(m_mainWindow->pushButton, &QPushButton::clicked, this, [this]{ + const auto dialog = new Dialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->exec(); + }); + connect(m_mainWindow->pushButton_2, &QPushButton::clicked, this, [this]{ + const auto widget = new Widget(this); + widget->setAttribute(Qt::WA_DeleteOnClose); + widget->show(); + }); +} diff --git a/FramelessHelper/examples/mainwindow/mainwindow.h b/FramelessHelper/examples/mainwindow/mainwindow.h new file mode 100644 index 0000000..10e99c5 --- /dev/null +++ b/FramelessHelper/examples/mainwindow/mainwindow.h @@ -0,0 +1,56 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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_BEGIN_NAMESPACE +class StandardTitleBar; +FRAMELESSHELPER_END_NAMESPACE + +namespace Ui +{ +class MainWindow; +} + +class MainWindow : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessMainWindow) +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(MainWindow) + +public: + explicit MainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {}); + ~MainWindow() override; + +protected: + void closeEvent(QCloseEvent *event) override; + +private: + void initialize(); + +private: + FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar) *m_titleBar = nullptr; + Ui::MainWindow *m_mainWindow = nullptr; +}; diff --git a/FramelessHelper/examples/mainwindow/mainwindow.ui b/FramelessHelper/examples/mainwindow/mainwindow.ui new file mode 100644 index 0000000..31b21df --- /dev/null +++ b/FramelessHelper/examples/mainwindow/mainwindow.ui @@ -0,0 +1,296 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + Hello, World! + + + + + + + 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 + + + + + + + <a href="https://www.google.com">https://www.google.com</a> + + + + + + + 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/FramelessHelper/examples/openglwidget/CMakeLists.txt b/FramelessHelper/examples/openglwidget/CMakeLists.txt new file mode 100644 index 0000000..a7311dc --- /dev/null +++ b/FramelessHelper/examples/openglwidget/CMakeLists.txt @@ -0,0 +1,75 @@ +#[[ + MIT License + + Copyright (C) 2021-2023 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. +]] + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS OpenGL) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS OpenGL) + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS OpenGLWidgets) + find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS OpenGLWidgets) +endif() + +set(SOURCES + ../shared/log.h + ../shared/log.cpp + ../shared/settings.h + ../shared/settings.cpp + images.qrc + logo.h + logo.cpp + glwidget.h + glwidget.cpp + mainwindow.h + mainwindow.cpp + main.cpp +) + +if(WIN32) + enable_language(RC) + list(APPEND SOURCES + ../shared/example.rc + ../shared/example.manifest + ) +endif() + +add_executable(OpenGLWidget ${SOURCES}) + +target_link_libraries(OpenGLWidget PRIVATE + Qt${QT_VERSION_MAJOR}::Widgets + Qt${QT_VERSION_MAJOR}::OpenGL + FramelessHelper::Widgets +) + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + target_link_libraries(OpenGLWidget PRIVATE + Qt${QT_VERSION_MAJOR}::OpenGLWidgets + ) +endif() + +include(../../src/core/cmakehelper.cmake) +setup_gui_app(OpenGLWidget) +setup_compile_params(OpenGLWidget) +if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT) + deploy_qt_runtime(OpenGLWidget) +endif() diff --git a/FramelessHelper/examples/openglwidget/glwidget.cpp b/FramelessHelper/examples/openglwidget/glwidget.cpp new file mode 100644 index 0000000..ae5285b --- /dev/null +++ b/FramelessHelper/examples/openglwidget/glwidget.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "glwidget.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GLWidget::GLWidget(QWidget *parent, Qt::WindowFlags f) : QOpenGLWidget(parent, f) +{ + m_world.setToIdentity(); + m_world.translate(0, 0, -1); + m_world.rotate(180, 1, 0, 0); + + QSequentialAnimationGroup *animGroup = new QSequentialAnimationGroup(this); + animGroup->setLoopCount(-1); + QPropertyAnimation *zAnim0 = new QPropertyAnimation(this, QByteArrayLiteral("z")); + zAnim0->setStartValue(1.5f); + zAnim0->setEndValue(10.0f); + zAnim0->setDuration(2000); + animGroup->addAnimation(zAnim0); + QPropertyAnimation *zAnim1 = new QPropertyAnimation(this, QByteArrayLiteral("z")); + zAnim1->setStartValue(10.0f); + zAnim1->setEndValue(50.0f); + zAnim1->setDuration(4000); + zAnim1->setEasingCurve(QEasingCurve::OutElastic); + animGroup->addAnimation(zAnim1); + QPropertyAnimation *zAnim2 = new QPropertyAnimation(this, QByteArrayLiteral("z")); + zAnim2->setStartValue(50.0f); + zAnim2->setEndValue(1.5f); + zAnim2->setDuration(2000); + animGroup->addAnimation(zAnim2); + animGroup->start(); + + QPropertyAnimation* rAnim = new QPropertyAnimation(this, QByteArrayLiteral("r")); + rAnim->setStartValue(0.0f); + rAnim->setEndValue(360.0f); + rAnim->setDuration(2000); + rAnim->setLoopCount(-1); + rAnim->start(); + + QTimer::singleShot(4000, this, &GLWidget::startSecondStage); +} + +GLWidget::~GLWidget() +{ + makeCurrent(); + delete m_texture; + delete m_program; + delete m_vbo; + delete m_vao; +} + +void GLWidget::startSecondStage() +{ + QPropertyAnimation* r2Anim = new QPropertyAnimation(this, QByteArrayLiteral("r2")); + r2Anim->setStartValue(0.0f); + r2Anim->setEndValue(360.0f); + r2Anim->setDuration(20000); + r2Anim->setLoopCount(-1); + r2Anim->start(); +} + +void GLWidget::setZ(float v) +{ + m_eye.setZ(v); + m_uniformsDirty = true; + update(); + Q_EMIT zChanged(); +} + +void GLWidget::setR(float v) +{ + m_r = v; + m_uniformsDirty = true; + update(); + Q_EMIT rChanged(); +} + +void GLWidget::setR2(float v) +{ + m_r2 = v; + m_uniformsDirty = true; + update(); + Q_EMIT r2Changed(); +} + +static constexpr const char vertexShaderSource[] = + "layout(location = 0) in vec4 vertex;\n" + "layout(location = 1) in vec3 normal;\n" + "out vec3 vert;\n" + "out vec3 vertNormal;\n" + "out vec3 color;\n" + "uniform mat4 projMatrix;\n" + "uniform mat4 camMatrix;\n" + "uniform mat4 worldMatrix;\n" + "uniform mat4 myMatrix;\n" + "uniform sampler2D sampler;\n" + "void main() {\n" + " ivec2 pos = ivec2(gl_InstanceID % 32, gl_InstanceID / 32);\n" + " vec2 t = vec2(float(-16 + pos.x) * 0.8, float(-18 + pos.y) * 0.6);\n" + " float val = 2.0 * length(texelFetch(sampler, pos, 0).rgb);\n" + " mat4 wm = myMatrix * mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, val, 1) * worldMatrix;\n" + " color = texelFetch(sampler, pos, 0).rgb * vec3(0.4, 1.0, 0.0);\n" + " vert = vec3(wm * vertex);\n" + " vertNormal = mat3(transpose(inverse(wm))) * normal;\n" + " gl_Position = projMatrix * camMatrix * wm * vertex;\n" + "}\n"; + +static constexpr const char fragmentShaderSource[] = + "in highp vec3 vert;\n" + "in highp vec3 vertNormal;\n" + "in highp vec3 color;\n" + "out highp vec4 fragColor;\n" + "uniform highp vec3 lightPos;\n" + "void main() {\n" + " highp vec3 L = normalize(lightPos - vert);\n" + " highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n" + " highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n" + " fragColor = vec4(col, 1.0);\n" + "}\n"; + +QByteArray versionedShaderCode(const char *src) +{ + QByteArray versionedSrc; + + if (QOpenGLContext::currentContext()->isOpenGLES()) + versionedSrc.append(QByteArrayLiteral("#version 300 es\n")); + else + versionedSrc.append(QByteArrayLiteral("#version 330\n")); + + versionedSrc.append(src); + return versionedSrc; +} + +void GLWidget::initializeGL() +{ + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + + QImage img(QStringLiteral(":/images/qtlogo.png")); + Q_ASSERT(!img.isNull()); + delete m_texture; + m_texture = new QOpenGLTexture(img.scaled(32, 36).mirrored()); + + delete m_program; + m_program = new QOpenGLShaderProgram; + // Prepend the correct version directive to the sources. The rest is the + // same, thanks to the common GLSL syntax. + m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, versionedShaderCode(vertexShaderSource)); + m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, versionedShaderCode(fragmentShaderSource)); + m_program->link(); + + m_projMatrixLoc = m_program->uniformLocation("projMatrix"); + m_camMatrixLoc = m_program->uniformLocation("camMatrix"); + m_worldMatrixLoc = m_program->uniformLocation("worldMatrix"); + m_myMatrixLoc = m_program->uniformLocation("myMatrix"); + m_lightPosLoc = m_program->uniformLocation("lightPos"); + + // Create a VAO. Not strictly required for ES 3, but it is for plain OpenGL. + delete m_vao; + m_vao = new QOpenGLVertexArrayObject; + if (m_vao->create()) + m_vao->bind(); + + m_program->bind(); + delete m_vbo; + m_vbo = new QOpenGLBuffer; + m_vbo->create(); + m_vbo->bind(); + m_vbo->allocate(m_logo.constData(), m_logo.count() * sizeof(GLfloat)); + f->glEnableVertexAttribArray(0); + f->glEnableVertexAttribArray(1); + f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), + nullptr); + f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), + reinterpret_cast(3 * sizeof(GLfloat))); + m_vbo->release(); + + f->glEnable(GL_DEPTH_TEST); + f->glEnable(GL_CULL_FACE); +} + +void GLWidget::resizeGL(int w, int h) +{ + m_proj.setToIdentity(); + m_proj.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f); + m_uniformsDirty = true; +} + +void GLWidget::paintGL() +{ + // Now use QOpenGLExtraFunctions instead of QOpenGLFunctions as we want to + // do more than what GL(ES) 2.0 offers. + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + + static constexpr const auto rgb = 240.0f / 255.0f; + f->glClearColor(rgb, rgb, rgb, 1); + f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + m_program->bind(); + m_texture->bind(); + + if (m_uniformsDirty) { + m_uniformsDirty = false; + QMatrix4x4 camera; + camera.lookAt(m_eye, m_eye + m_target, QVector3D(0, 1, 0)); + m_program->setUniformValue(m_projMatrixLoc, m_proj); + m_program->setUniformValue(m_camMatrixLoc, camera); + QMatrix4x4 wm = m_world; + wm.rotate(m_r, 1, 1, 0); + m_program->setUniformValue(m_worldMatrixLoc, wm); + QMatrix4x4 mm; + mm.setToIdentity(); + mm.rotate(-m_r2, 1, 0, 0); + m_program->setUniformValue(m_myMatrixLoc, mm); + m_program->setUniformValue(m_lightPosLoc, QVector3D(0, 0, 70)); + } + + // Now call a function introduced in OpenGL 3.1 / OpenGL ES 3.0. We + // requested a 3.3 or ES 3.0 context, so we know this will work. + f->glDrawArraysInstanced(GL_TRIANGLES, 0, m_logo.vertexCount(), 32 * 36); +} diff --git a/FramelessHelper/examples/openglwidget/glwidget.h b/FramelessHelper/examples/openglwidget/glwidget.h new file mode 100644 index 0000000..2443307 --- /dev/null +++ b/FramelessHelper/examples/openglwidget/glwidget.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include "logo.h" + +QT_BEGIN_NAMESPACE + +class QOpenGLTexture; +class QOpenGLShaderProgram; +class QOpenGLBuffer; +class QOpenGLVertexArrayObject; + +QT_END_NAMESPACE + +class GLWidget : public QOpenGLWidget +{ + Q_OBJECT + Q_PROPERTY(float z READ z WRITE setZ NOTIFY zChanged FINAL) + Q_PROPERTY(float r READ r WRITE setR NOTIFY rChanged FINAL) + Q_PROPERTY(float r2 READ r2 WRITE setR2 NOTIFY r2Changed FINAL) + +public: + explicit GLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = {}); + ~GLWidget() override; + + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + float z() const { return m_eye.z(); } + void setZ(float v); + + float r() const { return m_r; } + void setR(float v); + + float r2() const { return m_r2; } + void setR2(float v); + +private Q_SLOTS: + void startSecondStage(); + +Q_SIGNALS: + void zChanged(); + void rChanged(); + void r2Changed(); + +private: + QOpenGLTexture *m_texture = nullptr; + QOpenGLShaderProgram *m_program = nullptr; + QOpenGLBuffer *m_vbo = nullptr; + QOpenGLVertexArrayObject *m_vao = nullptr; + Logo m_logo; + int m_projMatrixLoc = 0; + int m_camMatrixLoc = 0; + int m_worldMatrixLoc = 0; + int m_myMatrixLoc = 0; + int m_lightPosLoc = 0; + QMatrix4x4 m_proj; + QMatrix4x4 m_world; + QVector3D m_eye; + QVector3D m_target = {0, 0, -1}; + bool m_uniformsDirty = true; + float m_r = 0; + float m_r2 = 0; +}; diff --git a/FramelessHelper/examples/openglwidget/images.qrc b/FramelessHelper/examples/openglwidget/images.qrc new file mode 100644 index 0000000..617c258 --- /dev/null +++ b/FramelessHelper/examples/openglwidget/images.qrc @@ -0,0 +1,5 @@ + + + images/qtlogo.png + + diff --git a/FramelessHelper/examples/openglwidget/images/qtlogo.png b/FramelessHelper/examples/openglwidget/images/qtlogo.png new file mode 100644 index 0000000..9cb2e01 Binary files /dev/null and b/FramelessHelper/examples/openglwidget/images/qtlogo.png differ diff --git a/FramelessHelper/examples/openglwidget/logo.cpp b/FramelessHelper/examples/openglwidget/logo.cpp new file mode 100644 index 0000000..6fcece1 --- /dev/null +++ b/FramelessHelper/examples/openglwidget/logo.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "logo.h" +#include + +Logo::Logo() +{ + m_data.resize(2500 * 6); + + const GLfloat x1 = +0.06f; + const GLfloat y1 = -0.14f; + const GLfloat x2 = +0.14f; + const GLfloat y2 = -0.06f; + const GLfloat x3 = +0.08f; + const GLfloat y3 = +0.00f; + const GLfloat x4 = +0.30f; + const GLfloat y4 = +0.22f; + + quad(x1, y1, x2, y2, y2, x2, y1, x1); + quad(x3, y3, x4, y4, y4, x4, y3, x3); + + extrude(x1, y1, x2, y2); + extrude(x2, y2, y2, x2); + extrude(y2, x2, y1, x1); + extrude(y1, x1, x1, y1); + extrude(x3, y3, x4, y4); + extrude(x4, y4, y4, x4); + extrude(y4, x4, y3, x3); + + const int NumSectors = 100; + + for (int i = 0; i < NumSectors; ++i) { + GLfloat angle = (i * 2 * M_PI) / NumSectors; + GLfloat angleSin = qSin(angle); + GLfloat angleCos = qCos(angle); + const GLfloat x5 = 0.30f * angleSin; + const GLfloat y5 = 0.30f * angleCos; + const GLfloat x6 = 0.20f * angleSin; + const GLfloat y6 = 0.20f * angleCos; + + angle = ((i + 1) * 2 * M_PI) / NumSectors; + angleSin = qSin(angle); + angleCos = qCos(angle); + const GLfloat x7 = 0.20f * angleSin; + const GLfloat y7 = 0.20f * angleCos; + const GLfloat x8 = 0.30f * angleSin; + const GLfloat y8 = 0.30f * angleCos; + + quad(x5, y5, x6, y6, x7, y7, x8, y8); + + extrude(x6, y6, x7, y7); + extrude(x8, y8, x5, y5); + } +} + +void Logo::add(const QVector3D &v, const QVector3D &n) +{ + GLfloat *p = m_data.data() + m_count; + *p++ = v.x(); + *p++ = v.y(); + *p++ = v.z(); + *p++ = n.x(); + *p++ = n.y(); + *p++ = n.z(); + m_count += 6; +} + +void Logo::quad(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3, GLfloat x4, GLfloat y4) +{ + QVector3D n = QVector3D::normal(QVector3D(x4 - x1, y4 - y1, 0.0f), QVector3D(x2 - x1, y2 - y1, 0.0f)); + + add(QVector3D(x1, y1, -0.05f), n); + add(QVector3D(x4, y4, -0.05f), n); + add(QVector3D(x2, y2, -0.05f), n); + + add(QVector3D(x3, y3, -0.05f), n); + add(QVector3D(x2, y2, -0.05f), n); + add(QVector3D(x4, y4, -0.05f), n); + + n = QVector3D::normal(QVector3D(x1 - x4, y1 - y4, 0.0f), QVector3D(x2 - x4, y2 - y4, 0.0f)); + + add(QVector3D(x4, y4, 0.05f), n); + add(QVector3D(x1, y1, 0.05f), n); + add(QVector3D(x2, y2, 0.05f), n); + + add(QVector3D(x2, y2, 0.05f), n); + add(QVector3D(x3, y3, 0.05f), n); + add(QVector3D(x4, y4, 0.05f), n); +} + +void Logo::extrude(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) +{ + QVector3D n = QVector3D::normal(QVector3D(0.0f, 0.0f, -0.1f), QVector3D(x2 - x1, y2 - y1, 0.0f)); + + add(QVector3D(x1, y1, +0.05f), n); + add(QVector3D(x1, y1, -0.05f), n); + add(QVector3D(x2, y2, +0.05f), n); + + add(QVector3D(x2, y2, -0.05f), n); + add(QVector3D(x2, y2, +0.05f), n); + add(QVector3D(x1, y1, -0.05f), n); +} diff --git a/FramelessHelper/examples/openglwidget/logo.h b/FramelessHelper/examples/openglwidget/logo.h new file mode 100644 index 0000000..61d5144 --- /dev/null +++ b/FramelessHelper/examples/openglwidget/logo.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include +#include +#include + +class Logo +{ +public: + Logo(); + const GLfloat *constData() const { return m_data.constData(); } + int count() const { return m_count; } + int vertexCount() const { return m_count / 6; } + +private: + void quad(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3, GLfloat x4, GLfloat y4); + void extrude(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); + void add(const QVector3D &v, const QVector3D &n); + + QVector m_data; + int m_count = 0; +}; diff --git a/FramelessHelper/examples/openglwidget/main.cpp b/FramelessHelper/examples/openglwidget/main.cpp new file mode 100644 index 0000000..d7a97f2 --- /dev/null +++ b/FramelessHelper/examples/openglwidget/main.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "mainwindow.h" +#include "../shared/log.h" + +// This example demonstrates easy, cross-platform usage of OpenGL ES 3.0 functions via +// QOpenGLExtraFunctions in an application that works identically on desktop platforms +// with OpenGL 3.3 and mobile/embedded devices with OpenGL ES 3.0. + +// The code is always the same, with the exception of two places: (1) the OpenGL context +// creation has to have a sufficiently high version number for the features that are in +// use, and (2) the shader code's version directive is different. + +FRAMELESSHELPER_USE_NAMESPACE + +int main(int argc, char *argv[]) +{ + std::setlocale(LC_ALL, "en_US.UTF-8"); + + Log::setup(FRAMELESSHELPER_STRING_LITERAL("openglwidget")); + + // Not necessary, but better call this function, before the construction + // of any Q(Core|Gui)Application instances. + FramelessHelper::Widgets::initialize(); + + const auto application = std::make_unique(argc, argv); + + // Must be called after QGuiApplication has been constructed, we are using + // some private functions from QPA which won't be available until there's + // a QGuiApplication instance. + FramelessHelper::Core::setApplicationOSThemeAware(); + + FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); + FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial); + + QSurfaceFormat fmt = {}; + fmt.setDepthBufferSize(24); + + // Request OpenGL 3.3 core or OpenGL ES 3.0. + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + qDebug() << "Requesting OpenGL 3.3 core context ..."; + fmt.setVersion(3, 3); + fmt.setProfile(QSurfaceFormat::CoreProfile); + } else { + qDebug() << "Requesting OpenGL ES 3.0 context ..."; + fmt.setVersion(3, 0); + } + + QSurfaceFormat::setDefaultFormat(fmt); + + const auto mainWindow = std::make_unique(); + mainWindow->show(); + + return QCoreApplication::exec(); +} diff --git a/FramelessHelper/examples/openglwidget/mainwindow.cpp b/FramelessHelper/examples/openglwidget/mainwindow.cpp new file mode 100644 index 0000000..dd274f4 --- /dev/null +++ b/FramelessHelper/examples/openglwidget/mainwindow.cpp @@ -0,0 +1,97 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "mainwindow.h" +#include "glwidget.h" +#include +#include +#include +#include +#include +#include "../shared/settings.h" + +extern template void Settings::set(const QString &, const QString &, const QRect &); +extern template void Settings::set(const QString &, const QString &, const qreal &); + +extern template QRect Settings::get(const QString &, const QString &); +extern template qreal Settings::get(const QString &, const QString &); + +FRAMELESSHELPER_USE_NAMESPACE + +using namespace Global; + +FRAMELESSHELPER_STRING_CONSTANT(Geometry) +FRAMELESSHELPER_STRING_CONSTANT(DevicePixelRatio) + +MainWindow::MainWindow(QWidget *parent) : FramelessWidget(parent) +{ + initialize(); +} + +MainWindow::~MainWindow() = default; + +void MainWindow::closeEvent(QCloseEvent *event) +{ + if (!parent()) { + Settings::set({}, kGeometry, geometry()); + Settings::set({}, kDevicePixelRatio, devicePixelRatioF()); + } + FramelessWidget::closeEvent(event); +} + +void MainWindow::initialize() +{ + setWindowTitle(tr("FramelessHelper demo application - QOpenGLWidget")); + setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer)); + resize(800, 600); + m_titleBar = new StandardTitleBar(this); + m_titleBar->setWindowIconVisible(true); + m_glWidget = new GLWidget(this); + const auto mainLayout = new QVBoxLayout(this); + mainLayout->setSpacing(0); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->addWidget(m_titleBar); + mainLayout->addWidget(m_glWidget); + setLayout(mainLayout); + + FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this); + helper->setTitleBarWidget(m_titleBar); +#ifndef Q_OS_MACOS + helper->setSystemButton(m_titleBar->minimizeButton(), SystemButtonType::Minimize); + helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize); + helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close); +#endif // Q_OS_MACOS + connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){ + const auto savedGeometry = Settings::get({}, kGeometry); + if (savedGeometry.isValid() && !parent()) { + const auto savedDpr = Settings::get({}, kDevicePixelRatio); + // Qt doesn't support dpr < 1. + const qreal oldDpr = std::max(savedDpr, qreal(1)); + const qreal scale = (devicePixelRatioF() / oldDpr); + setGeometry({savedGeometry.topLeft() * scale, savedGeometry.size() * scale}); + } else { + helper->moveWindowToDesktopCenter(); + } + }); +} diff --git a/FramelessHelper/examples/openglwidget/mainwindow.h b/FramelessHelper/examples/openglwidget/mainwindow.h new file mode 100644 index 0000000..6a0d2ee --- /dev/null +++ b/FramelessHelper/examples/openglwidget/mainwindow.h @@ -0,0 +1,53 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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_BEGIN_NAMESPACE +class StandardTitleBar; +FRAMELESSHELPER_END_NAMESPACE + +class GLWidget; + +class MainWindow : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget) +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(MainWindow) + +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow() override; + +protected: + void closeEvent(QCloseEvent *event) override; + +private: + void initialize(); + +private: + FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar) *m_titleBar = nullptr; + GLWidget *m_glWidget = nullptr; +}; diff --git a/FramelessHelper/examples/quick/CMakeLists.txt b/FramelessHelper/examples/quick/CMakeLists.txt new file mode 100644 index 0000000..e46b922 --- /dev/null +++ b/FramelessHelper/examples/quick/CMakeLists.txt @@ -0,0 +1,100 @@ +#[[ + MIT License + + Copyright (C) 2021-2023 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. +]] + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Quick) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Quick) + +set(SOURCES + ../shared/log.h + ../shared/log.cpp + ../shared/settings.h + ../shared/settings.cpp + main.cpp + quicksettings.h + quicksettings.cpp +) + +if(${QT_VERSION} VERSION_LESS 6.2) + list(APPEND SOURCES resources.qrc) +endif() + +if(WIN32) + enable_language(RC) + list(APPEND SOURCES + ../shared/example.rc + ../shared/example.manifest + ) +endif() + +add_executable(Quick ${SOURCES}) + +if(${QT_VERSION} VERSION_GREATER_EQUAL 6.2) + set(QML_SOURCES + "qml/Window.qml" + "qml/ApplicationWindow.qml" + "qml/HomePage.qml" + ) + set_source_files_properties(${QML_SOURCES} + PROPERTIES QT_DISCARD_FILE_CONTENTS TRUE + ) + qt_add_qml_module(Quick + URI Demo + VERSION 1.0 + IMPORT_PATH "${PROJECT_BINARY_DIR}/qml" + OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/qml/Demo" + RESOURCE_PREFIX "/" + NO_RESOURCE_TARGET_PATH + IMPORTS + QtQuick/auto + DEPENDENCIES + org.wangwenx190.FramelessHelper/auto + QML_FILES ${QML_SOURCES} + #ENABLE_TYPE_COMPILER + ) + qt_add_resources(Quick resources + PREFIX + "/" + FILES + "images/microsoft.svg" + ) +endif() + +target_link_libraries(Quick PRIVATE + Qt${QT_VERSION_MAJOR}::QuickPrivate + FramelessHelper::Quick +) + +target_compile_definitions(Quick PRIVATE + $<$,$>:QT_QML_DEBUG> +) + +include(../../src/core/cmakehelper.cmake) +setup_gui_app(Quick) +setup_compile_params(Quick) +if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT) + deploy_qt_runtime(Quick + QML_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" + #QML_IMPORT_DIR "${PROJECT_BINARY_DIR}/qml" # Some applications failed to start. + ) +endif() diff --git a/FramelessHelper/examples/quick/images/microsoft.png b/FramelessHelper/examples/quick/images/microsoft.png new file mode 100644 index 0000000..3cb3b5c Binary files /dev/null and b/FramelessHelper/examples/quick/images/microsoft.png differ diff --git a/FramelessHelper/examples/quick/images/microsoft.svg b/FramelessHelper/examples/quick/images/microsoft.svg new file mode 100644 index 0000000..ffdefbf --- /dev/null +++ b/FramelessHelper/examples/quick/images/microsoft.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/FramelessHelper/examples/quick/main.cpp b/FramelessHelper/examples/quick/main.cpp new file mode 100644 index 0000000..04208a7 --- /dev/null +++ b/FramelessHelper/examples/quick/main.cpp @@ -0,0 +1,163 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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. + */ + +#ifndef QMLTC_ENABLED +# define QMLTC_ENABLED 0 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "quicksettings.h" +#if QMLTC_ENABLED +# include +#endif +#include "../shared/log.h" + +FRAMELESSHELPER_USE_NAMESPACE + +static constexpr const bool IS_MACOS_HOST = +#ifdef Q_OS_MACOS + true +#else // !Q_OS_MACOS + false +#endif // Q_OS_MACOS + ; + +int main(int argc, char *argv[]) +{ + std::setlocale(LC_ALL, "en_US.UTF-8"); + + Log::setup(FRAMELESSHELPER_STRING_LITERAL("quick")); + + // Not necessary, but better call this function, before the construction + // of any Q(Core|Gui)Application instances. + FramelessHelper::Quick::initialize(); + + const auto application = std::make_unique(argc, argv); + + // Must be called after QGuiApplication has been constructed, we are using + // some private functions from QPA which won't be available until there's + // a QGuiApplication instance. + FramelessHelper::Core::setApplicationOSThemeAware(); + + FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); + FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial); + + // Enable QtRHI debug output if not explicitly requested by the user. + if (!qEnvironmentVariableIsSet("QSG_INFO")) { + qputenv("QSG_INFO", FRAMELESSHELPER_BYTEARRAY_LITERAL("1")); + } + + // Allow testing other RHI backends through environment variable. + if (!qEnvironmentVariableIsSet("QSG_RHI_BACKEND")) { + // This line is not relevant to FramelessHelper, we change + // the default RHI backend to "software" just because it's + // stable enough and have exactly the same behavior on all + // supported platforms. Other backends may behave differently + // on different platforms and graphics cards, so I think they + // are not suitable for our demonstration. +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QQuickWindow::setGraphicsApi(QSGRendererInterface::Software); +#elif (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software); +#endif + } + + if (!qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_STYLE")) { + // This line is not relevant to FramelessHelper, we change the default + // Qt Quick Controls theme to "Basic" (Qt6) or "Default" (Qt5) just + // because other themes will make our homemade system buttons look + // not good. This line has nothing to do with FramelessHelper itself. + qputenv("QT_QUICK_CONTROLS_STYLE", []() -> QByteArray { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + return FRAMELESSHELPER_BYTEARRAY_LITERAL("Basic"); +#else + return FRAMELESSHELPER_BYTEARRAY_LITERAL("Default"); +#endif + }()); + } + + // Enable some helpful debugging messages. + if (!qEnvironmentVariableIsSet("QML_IMPORT_TRACE")) { + qputenv("QML_IMPORT_TRACE", FRAMELESSHELPER_BYTEARRAY_LITERAL("1")); + } + + const auto engine = std::make_unique(); + + engine->rootContext()->setContextProperty( + FRAMELESSHELPER_STRING_LITERAL("$isMacOSHost"), QVariant(IS_MACOS_HOST)); + +#if (((QT_VERSION < QT_VERSION_CHECK(6, 2, 0)) || defined(QUICK_USE_QMAKE)) && !QMLTC_ENABLED) + // Don't forget to register our own custom QML types! + FramelessHelper::Quick::registerTypes(engine.get()); + + qmlRegisterSingletonType("Demo", 1, 0, "Settings", + [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { + Q_UNUSED(engine); + Q_UNUSED(scriptEngine); + return new QuickSettings; + }); +#endif + +#if !QMLTC_ENABLED + const QUrl mainUrl(FRAMELESSHELPER_STRING_LITERAL("qrc:///qml/HomePage.qml")); +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) + QObject::connect(engine.get(), &QQmlApplicationEngine::objectCreationFailed, qApp, + [](const QUrl &url){ + qCritical() << "The QML engine failed to create component:" << url; + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); +#elif !QMLTC_ENABLED + const QMetaObject::Connection connection = QObject::connect( + engine.get(), &QQmlApplicationEngine::objectCreated, &application, + [&mainUrl, &connection](QObject *object, const QUrl &url) { + if (url != mainUrl) { + return; + } + if (object) { + QObject::disconnect(connection); + } else { + QCoreApplication::exit(-1); + } + }, Qt::QueuedConnection); +#endif + +#if !QMLTC_ENABLED + engine->load(mainUrl); +#endif + +#if QMLTC_ENABLED + const auto homePage = std::make_unique(engine.get()); + homePage->show(); +#endif + + return QCoreApplication::exec(); +} diff --git a/FramelessHelper/examples/quick/qml/ApplicationWindow.qml b/FramelessHelper/examples/quick/qml/ApplicationWindow.qml new file mode 100644 index 0000000..4eed4cd --- /dev/null +++ b/FramelessHelper/examples/quick/qml/ApplicationWindow.qml @@ -0,0 +1,111 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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. + */ + +import QtQuick +import QtQuick.Controls.Basic +import org.wangwenx190.FramelessHelper +import Demo + +FramelessApplicationWindow { + id: window + objectName: "applicationWindow" + visible: false // Hide the window before we sets up it's correct size and position. + width: 800 + height: 600 + title: qsTr("FramelessHelper demo application - Qt Quick") + color: { + if (FramelessHelper.blurBehindWindowEnabled) { + return "transparent"; + } + if (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) { + return FramelessUtils.defaultSystemDarkColor; + } + return FramelessUtils.defaultSystemLightColor; + } + + Component.onDestruction: Settings.saveGeometry(window) + + FramelessHelper.onReady: { + // Let FramelessHelper know what's our homemade title bar, otherwise + // our window won't be draggable. + FramelessHelper.titleBarItem = titleBar; + if (!$isMacOSHost) { + // Make our own items visible to the hit test and on Windows, enable + // the snap layout feature (available since Windows 11). + FramelessHelper.setSystemButton(titleBar.minimizeButton, FramelessHelperConstants.Minimize); + FramelessHelper.setSystemButton(titleBar.maximizeButton, FramelessHelperConstants.Maximize); + FramelessHelper.setSystemButton(titleBar.closeButton, FramelessHelperConstants.Close); + } + if (!Settings.restoreGeometry(window)) { + FramelessHelper.moveWindowToDesktopCenter(); + } + // Finally, show the window after everything is setted. + window.visible = true; + } + + Shortcut { + sequences: [ StandardKey.Cancel, StandardKey.Close, StandardKey.Quit ] + onActivated: { + if (window.visibility === Window.FullScreen) { + window.toggleFullScreen(); + } else { + window.close(); + } + } + } + + Shortcut { + sequences: [ StandardKey.FullScreen, "ALT+RETURN" ] + onActivated: window.toggleFullScreen() + } + + Timer { + interval: 500 + running: true + repeat: true + onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss") + } + + Label { + id: timeLabel + anchors.centerIn: parent + font { + pointSize: 70 + bold: true + } + color: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) ? Qt.color("white") : Qt.color("black") + } + + StandardTitleBar { + id: titleBar + anchors { + top: parent.top + topMargin: window.visibility === Window.Windowed ? 1 : 0 + left: parent.left + right: parent.right + } + windowIcon: "qrc:///images/microsoft.svg" + windowIconVisible: true + } +} diff --git a/FramelessHelper/examples/quick/qml/HomePage.qml b/FramelessHelper/examples/quick/qml/HomePage.qml new file mode 100644 index 0000000..4127b2e --- /dev/null +++ b/FramelessHelper/examples/quick/qml/HomePage.qml @@ -0,0 +1,35 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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. + */ + +import QtQml + +QtObject { + property Window window: Window{} + property ApplicationWindow applicationWindow: ApplicationWindow{} + + Component.onCompleted: { + window.show(); + applicationWindow.show(); + } +} diff --git a/FramelessHelper/examples/quick/qml/Window.qml b/FramelessHelper/examples/quick/qml/Window.qml new file mode 100644 index 0000000..1bf98b3 --- /dev/null +++ b/FramelessHelper/examples/quick/qml/Window.qml @@ -0,0 +1,111 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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. + */ + +import QtQuick +import QtQuick.Controls.Basic +import org.wangwenx190.FramelessHelper +import Demo + +FramelessWindow { + id: window + objectName: "window" + visible: false // Hide the window before we sets up it's correct size and position. + width: 800 + height: 600 + title: qsTr("FramelessHelper demo application - Qt Quick") + color: { + if (FramelessHelper.blurBehindWindowEnabled) { + return "transparent"; + } + if (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) { + return FramelessUtils.defaultSystemDarkColor; + } + return FramelessUtils.defaultSystemLightColor; + } + + Component.onDestruction: Settings.saveGeometry(window) + + FramelessHelper.onReady: { + // Let FramelessHelper know what's our homemade title bar, otherwise + // our window won't be draggable. + FramelessHelper.titleBarItem = titleBar; + if (!$isMacOSHost) { + // Make our own items visible to the hit test and on Windows, enable + // the snap layout feature (available since Windows 11). + FramelessHelper.setSystemButton(titleBar.minimizeButton, FramelessHelperConstants.Minimize); + FramelessHelper.setSystemButton(titleBar.maximizeButton, FramelessHelperConstants.Maximize); + FramelessHelper.setSystemButton(titleBar.closeButton, FramelessHelperConstants.Close); + } + if (!Settings.restoreGeometry(window)) { + FramelessHelper.moveWindowToDesktopCenter(); + } + // Finally, show the window after everything is setted. + window.visible = true; + } + + Shortcut { + sequences: [ StandardKey.Cancel, StandardKey.Close, StandardKey.Quit ] + onActivated: { + if (window.visibility === Window.FullScreen) { + window.toggleFullScreen(); + } else { + window.close(); + } + } + } + + Shortcut { + sequences: [ StandardKey.FullScreen, "ALT+RETURN" ] + onActivated: window.toggleFullScreen() + } + + Timer { + interval: 500 + running: true + repeat: true + onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss") + } + + Label { + id: timeLabel + anchors.centerIn: parent + font { + pointSize: 70 + bold: true + } + color: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) ? Qt.color("white") : Qt.color("black") + } + + StandardTitleBar { + id: titleBar + anchors { + top: parent.top + topMargin: window.visibility === Window.Windowed ? 1 : 0 + left: parent.left + right: parent.right + } + windowIcon: "qrc:///images/microsoft.svg" + windowIconVisible: true + } +} diff --git a/FramelessHelper/examples/quick/quick.pro b/FramelessHelper/examples/quick/quick.pro new file mode 100644 index 0000000..57dc7cb --- /dev/null +++ b/FramelessHelper/examples/quick/quick.pro @@ -0,0 +1,18 @@ +TEMPLATE = app +TARGET = Quick +QT += qml quick quickcontrols2 +win32: CONFIG -= embed_manifest_exe +DEFINES += QUICK_USE_QMAKE +HEADERS += \ + ../shared/log.h \ + ../shared/settings.h \ + quicksettings.h +SOURCES += \ + ../shared/log.cpp \ + ../shared/settings.cpp \ + quicksettings.cpp \ + main.cpp +RESOURCES += resources.qrc +win32: RC_FILE = ../shared/example.rc +include(../../qmake/core.pri) +include(../../qmake/quick.pri) diff --git a/FramelessHelper/examples/quick/quicksettings.cpp b/FramelessHelper/examples/quick/quicksettings.cpp new file mode 100644 index 0000000..bf56ac6 --- /dev/null +++ b/FramelessHelper/examples/quick/quicksettings.cpp @@ -0,0 +1,71 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "quicksettings.h" +#include "../shared/settings.h" + +FRAMELESSHELPER_STRING_CONSTANT(Geometry) +FRAMELESSHELPER_STRING_CONSTANT(DevicePixelRatio) + +extern template void Settings::set(const QString &, const QString &, const QRect &); +extern template void Settings::set(const QString &, const QString &, const qreal &); + +extern template QRect Settings::get(const QString &, const QString &); +extern template qreal Settings::get(const QString &, const QString &); + +QuickSettings::QuickSettings(QObject *parent) : QObject(parent) +{ +} + +QuickSettings::~QuickSettings() = default; + +void QuickSettings::saveGeometry(QWindow *window) +{ + Q_ASSERT(window); + if (!window) { + return; + } + const QString objName = window->objectName(); + Settings::set(objName, kGeometry, window->geometry()); + Settings::set(objName, kDevicePixelRatio, window->devicePixelRatio()); +} + +bool QuickSettings::restoreGeometry(QWindow *window) +{ + Q_ASSERT(window); + if (!window) { + return false; + } + const QString objName = window->objectName(); + const auto savedGeometry = Settings::get(objName, kGeometry); + if (!savedGeometry.isValid()) { + return false; + } + const auto savedDpr = Settings::get(objName, kDevicePixelRatio); + // Qt doesn't support dpr < 1. + const qreal oldDpr = std::max(savedDpr, qreal(1)); + const qreal scale = (window->devicePixelRatio() / oldDpr); + window->setGeometry({savedGeometry.topLeft() * scale, savedGeometry.size() * scale}); + return true; +} diff --git a/FramelessHelper/examples/quick/quicksettings.h b/FramelessHelper/examples/quick/quicksettings.h new file mode 100644 index 0000000..f518eff --- /dev/null +++ b/FramelessHelper/examples/quick/quicksettings.h @@ -0,0 +1,48 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 +#include + +class QuickSettings : public QObject +{ + Q_OBJECT +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(Settings) +#endif +#ifdef QML_SINGLETON + QML_SINGLETON +#endif + Q_DISABLE_COPY_MOVE(QuickSettings) + +public: + explicit QuickSettings(QObject *parent = nullptr); + ~QuickSettings() override; + +public Q_SLOTS: + void saveGeometry(QWindow *window); + Q_NODISCARD bool restoreGeometry(QWindow *window); +}; diff --git a/FramelessHelper/examples/quick/resources.qrc b/FramelessHelper/examples/quick/resources.qrc new file mode 100644 index 0000000..f6da674 --- /dev/null +++ b/FramelessHelper/examples/quick/resources.qrc @@ -0,0 +1,8 @@ + + + images/microsoft.svg + qml/Window.qml + qml/ApplicationWindow.qml + qml/HomePage.qml + + diff --git a/FramelessHelper/examples/quick/scripts/d3d11.bat b/FramelessHelper/examples/quick/scripts/d3d11.bat new file mode 100644 index 0000000..b9fe601 --- /dev/null +++ b/FramelessHelper/examples/quick/scripts/d3d11.bat @@ -0,0 +1,28 @@ +:: MIT License +:: +:: Copyright (C) 2021-2023 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. + +@echo off +setlocal +set QSG_RHI_BACKEND=d3d11 +"%~dp0Quick.exe" +endlocal +exit /b 0 diff --git a/FramelessHelper/examples/quick/scripts/opengl.bat b/FramelessHelper/examples/quick/scripts/opengl.bat new file mode 100644 index 0000000..7946fa3 --- /dev/null +++ b/FramelessHelper/examples/quick/scripts/opengl.bat @@ -0,0 +1,28 @@ +:: MIT License +:: +:: Copyright (C) 2021-2023 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. + +@echo off +setlocal +set QSG_RHI_BACKEND=opengl +"%~dp0Quick.exe" +endlocal +exit /b 0 diff --git a/FramelessHelper/examples/quick/scripts/vulkan.bat b/FramelessHelper/examples/quick/scripts/vulkan.bat new file mode 100644 index 0000000..cb8d0c9 --- /dev/null +++ b/FramelessHelper/examples/quick/scripts/vulkan.bat @@ -0,0 +1,28 @@ +:: MIT License +:: +:: Copyright (C) 2021-2023 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. + +@echo off +setlocal +set QSG_RHI_BACKEND=vulkan +"%~dp0Quick.exe" +endlocal +exit /b 0 diff --git a/FramelessHelper/examples/shared/example.ico b/FramelessHelper/examples/shared/example.ico new file mode 100644 index 0000000..b3ec03b Binary files /dev/null and b/FramelessHelper/examples/shared/example.ico differ diff --git a/FramelessHelper/examples/shared/example.manifest b/FramelessHelper/examples/shared/example.manifest new file mode 100644 index 0000000..de6fb72 --- /dev/null +++ b/FramelessHelper/examples/shared/example.manifest @@ -0,0 +1,87 @@ + + + + + + + FramelessHelper Demo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True/PM + PerMonitorV2, PerMonitor + True + True + True + UTF-8 + SegmentHeap + + + diff --git a/FramelessHelper/examples/shared/log.cpp b/FramelessHelper/examples/shared/log.cpp new file mode 100644 index 0000000..fd4f396 --- /dev/null +++ b/FramelessHelper/examples/shared/log.cpp @@ -0,0 +1,94 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "log.h" +#include +#include +#include +#include +#include + +#ifndef QT_ENDL +# if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) +# define QT_ENDL Qt::endl +# else +# define QT_ENDL endl +# endif +#endif + +static QString g_app = {}; +static bool g_logError = false; + +static std::unique_ptr g_logFile = nullptr; +static std::unique_ptr g_logStream = nullptr; + +static inline void myMessageHandler(const QtMsgType type, const QMessageLogContext &context, const QString &message) +{ + if (message.isEmpty()) { + return; + } + const QString finalMessage = qFormatLogMessage(type, context, message).trimmed(); + if ((type == QtInfoMsg) || (type == QtDebugMsg)) { + std::cout << qUtf8Printable(finalMessage) << std::endl; + } else { + std::cerr << qUtf8Printable(finalMessage) << std::endl; + } + if (g_logError) { + return; + } + if (!g_logFile) { + g_logFile = std::make_unique(); + g_logFile->setFileName(FRAMELESSHELPER_STRING_LITERAL("debug-%1.log").arg(g_app)); + if (!g_logFile->open(QFile::WriteOnly | QFile::Text | QFile::Append)) { + std::cerr << "Can't open file to write: " << qUtf8Printable(g_logFile->errorString()) << std::endl; + g_logFile.reset(); + g_logError = true; + return; + } + } + if (!g_logStream) { + g_logStream = std::make_unique(); + g_logStream->setDevice(g_logFile.get()); + } + (*g_logStream) << finalMessage << QT_ENDL; +} + +void Log::setup(const QString &app) +{ + Q_ASSERT(!app.isEmpty()); + if (app.isEmpty()) { + return; + } + static bool once = false; + if (once) { + return; + } + once = true; + g_app = app; + qSetMessagePattern(FRAMELESSHELPER_STRING_LITERAL( + "[%{time yyyy/MM/dd hh:mm:ss.zzz}] <%{if-info}INFO%{endif}%{if-debug}DEBUG" + "%{endif}%{if-warning}WARNING%{endif}%{if-critical}CRITICAL%{endif}%{if-fatal}" + "FATAL%{endif}> %{if-category}%{category}: %{endif}%{message}")); + qInstallMessageHandler(myMessageHandler); +} diff --git a/FramelessHelper/examples/shared/log.h b/FramelessHelper/examples/shared/log.h new file mode 100644 index 0000000..f8730ed --- /dev/null +++ b/FramelessHelper/examples/shared/log.h @@ -0,0 +1,32 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 + +namespace Log +{ + void setup(const QString &app); +} // namespace Log diff --git a/FramelessHelper/examples/shared/settings.cpp b/FramelessHelper/examples/shared/settings.cpp new file mode 100644 index 0000000..7b62485 --- /dev/null +++ b/FramelessHelper/examples/shared/settings.cpp @@ -0,0 +1,83 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "settings.h" +#include +#include +#include +#include +#include + +static std::unique_ptr g_settings = nullptr; + +[[nodiscard]] static inline QSettings *appConfigFile() +{ + const QFileInfo fileInfo(QCoreApplication::applicationFilePath()); + const QString iniFileName = fileInfo.completeBaseName() + FRAMELESSHELPER_STRING_LITERAL(".ini"); + const QString iniFilePath = fileInfo.canonicalPath() + u'/' + iniFileName; + return new QSettings(iniFilePath, QSettings::IniFormat); +} + +[[nodiscard]] static inline QString appKey(const QString &id, const QString &key) +{ + Q_ASSERT(!key.isEmpty()); + if (key.isEmpty()) { + return {}; + } + return (id.isEmpty() ? key : (id + u'/' + key)); +} + +template +void Settings::set(const QString &id, const QString &key, const T &data) +{ + Q_ASSERT(!key.isEmpty()); + if (key.isEmpty()) { + return; + } + if (!g_settings) { + g_settings.reset(appConfigFile()); + } + g_settings->setValue(appKey(id, key), data); +} + +template +T Settings::get(const QString &id, const QString &key) +{ + Q_ASSERT(!key.isEmpty()); + if (key.isEmpty()) { + return T{}; + } + if (!g_settings) { + g_settings.reset(appConfigFile()); + } + return qvariant_cast(g_settings->value(appKey(id, key))); +} + +template void Settings::set(const QString &, const QString &, const QRect &); +template void Settings::set(const QString &, const QString &, const qreal &); +template void Settings::set(const QString &, const QString &, const QByteArray &); + +template QRect Settings::get(const QString &, const QString &); +template qreal Settings::get(const QString &, const QString &); +template QByteArray Settings::get(const QString &, const QString &); diff --git a/FramelessHelper/examples/shared/settings.h b/FramelessHelper/examples/shared/settings.h new file mode 100644 index 0000000..ef56db0 --- /dev/null +++ b/FramelessHelper/examples/shared/settings.h @@ -0,0 +1,36 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 + +namespace Settings +{ + template + void set(const QString &id, const QString &key, const T &data); + + template + [[nodiscard]] T get(const QString &id, const QString &key); +} // namespace Settings diff --git a/FramelessHelper/examples/widget/CMakeLists.txt b/FramelessHelper/examples/widget/CMakeLists.txt new file mode 100644 index 0000000..06376ad --- /dev/null +++ b/FramelessHelper/examples/widget/CMakeLists.txt @@ -0,0 +1,55 @@ +#[[ + MIT License + + Copyright (C) 2021-2023 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. +]] + +set(SOURCES + ../shared/log.h + ../shared/log.cpp + ../shared/settings.h + ../shared/settings.cpp + widget.h + widget.cpp + main.cpp +) + +if(WIN32) + enable_language(RC) + list(APPEND SOURCES + ../shared/example.rc + ../shared/example.manifest + ) +endif() + +add_executable(Widget ${SOURCES}) + +target_link_libraries(Widget PRIVATE + Qt${QT_VERSION_MAJOR}::Widgets + FramelessHelper::Widgets +) + +include(../../src/core/cmakehelper.cmake) +setup_gui_app(Widget) +setup_compile_params(Widget) +if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT) + deploy_qt_runtime(Widget) +endif() diff --git a/FramelessHelper/examples/widget/main.cpp b/FramelessHelper/examples/widget/main.cpp new file mode 100644 index 0000000..168dab7 --- /dev/null +++ b/FramelessHelper/examples/widget/main.cpp @@ -0,0 +1,68 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 +#include +#include +#include "widget.h" +#include "../shared/log.h" + +FRAMELESSHELPER_USE_NAMESPACE + +int main(int argc, char *argv[]) +{ + std::setlocale(LC_ALL, "en_US.UTF-8"); + + Log::setup(FRAMELESSHELPER_STRING_LITERAL("widget")); + + // Not necessary, but better call this function, before the construction + // of any Q(Core|Gui)Application instances. + FramelessHelper::Widgets::initialize(); + +#if 0 + if (!qEnvironmentVariableIsSet("QT_WIDGETS_RHI")) { + qputenv("QT_WIDGETS_RHI", FRAMELESSHELPER_BYTEARRAY_LITERAL("1")); + } +#endif + + const auto application = std::make_unique(argc, argv); + + // Must be called after QGuiApplication has been constructed, we are using + // some private functions from QPA which won't be available until there's + // a QGuiApplication instance. + FramelessHelper::Core::setApplicationOSThemeAware(); + + FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); + FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial); + + const auto window1 = std::make_unique(); + window1->setObjectName(FRAMELESSHELPER_STRING_LITERAL("window1")); + window1->show(); + + const auto window2 = std::make_unique(); + window2->setObjectName(FRAMELESSHELPER_STRING_LITERAL("window2")); + window2->show(); + + return QCoreApplication::exec(); +} diff --git a/FramelessHelper/examples/widget/widget.cpp b/FramelessHelper/examples/widget/widget.cpp new file mode 100644 index 0000000..fdf374a --- /dev/null +++ b/FramelessHelper/examples/widget/widget.cpp @@ -0,0 +1,164 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "widget.h" +#include +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +# include +#else +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "../shared/settings.h" + +extern template void Settings::set(const QString &, const QString &, const QRect &); +extern template void Settings::set(const QString &, const QString &, const qreal &); + +extern template QRect Settings::get(const QString &, const QString &); +extern template qreal Settings::get(const QString &, const QString &); + +FRAMELESSHELPER_USE_NAMESPACE + +using namespace Global; + +FRAMELESSHELPER_STRING_CONSTANT(Geometry) +FRAMELESSHELPER_STRING_CONSTANT(DevicePixelRatio) + +Widget::Widget(QWidget *parent) : FramelessWidget(parent) +{ + initialize(); + startTimer(500); + connect(FramelessManager::instance(), &FramelessManager::systemThemeChanged, this, &Widget::updateStyleSheet); +} + +Widget::~Widget() = default; + +void Widget::timerEvent(QTimerEvent *event) +{ + FramelessWidget::timerEvent(event); + if (m_clockLabel) { + m_clockLabel->setText(QTime::currentTime().toString(FRAMELESSHELPER_STRING_LITERAL("hh:mm:ss"))); + } +} + +void Widget::closeEvent(QCloseEvent *event) +{ + if (!parent()) { + const QString objName = objectName(); + Settings::set(objName, kGeometry, geometry()); + Settings::set(objName, kDevicePixelRatio, devicePixelRatioF()); + } + FramelessWidget::closeEvent(event); +} + +void Widget::initialize() +{ + setWindowTitle(tr("FramelessHelper demo application - Qt Widgets")); + setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer)); + resize(800, 600); + m_titleBar = new StandardTitleBar(this); + m_titleBar->setWindowIconVisible(true); + m_clockLabel = new QLabel(this); + m_clockLabel->setFrameShape(QFrame::NoFrame); + QFont clockFont = font(); + clockFont.setBold(true); + clockFont.setPointSize(70); + m_clockLabel->setFont(clockFont); + const auto contentLayout = new QHBoxLayout; + contentLayout->setContentsMargins(0, 0, 0, 0); + contentLayout->setSpacing(0); + contentLayout->addStretch(); + contentLayout->addWidget(m_clockLabel); + contentLayout->addStretch(); + const auto mainLayout = new QVBoxLayout(this); + mainLayout->setSpacing(0); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->addWidget(m_titleBar); + mainLayout->addLayout(contentLayout); + setLayout(mainLayout); + updateStyleSheet(); + + m_cancelShortcut = new QShortcut(this); + m_cancelShortcut->setKey(FRAMELESSHELPER_STRING_LITERAL("ESC")); + connect(m_cancelShortcut, &QShortcut::activated, this, [this](){ + if (isFullScreen()) { + Q_EMIT m_fullScreenShortcut->activated(); + } else { + close(); + } + }); + + m_fullScreenShortcut = new QShortcut(this); + m_fullScreenShortcut->setKey(FRAMELESSHELPER_STRING_LITERAL("ALT+RETURN")); + connect(m_fullScreenShortcut, &QShortcut::activated, this, [this](){ + if (isFullScreen()) { + setWindowState(windowState() & ~Qt::WindowFullScreen); + } else { + showFullScreen(); + } + }); + + FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this); + helper->setTitleBarWidget(m_titleBar); +#ifndef Q_OS_MACOS + helper->setSystemButton(m_titleBar->minimizeButton(), SystemButtonType::Minimize); + helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize); + helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close); +#endif // Q_OS_MACOS + connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){ + const QString objName = objectName(); + const auto savedGeometry = Settings::get(objName, kGeometry); + if (savedGeometry.isValid() && !parent()) { + const auto savedDpr = Settings::get(objName, kDevicePixelRatio); + // Qt doesn't support dpr < 1. + const qreal oldDpr = std::max(savedDpr, qreal(1)); + const qreal scale = (devicePixelRatioF() / oldDpr); + setGeometry({savedGeometry.topLeft() * scale, savedGeometry.size() * scale}); + } else { + helper->moveWindowToDesktopCenter(); + } + }); +} + +void Widget::updateStyleSheet() +{ + const bool dark = Utils::shouldAppsUseDarkMode(); + const QColor clockLabelTextColor = (dark ? kDefaultWhiteColor : kDefaultBlackColor); + m_clockLabel->setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("background-color: transparent; color: %1;") + .arg(clockLabelTextColor.name())); + if (FramelessWidgetsHelper::get(this)->isBlurBehindWindowEnabled()) { + setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("background-color: transparent;")); + } else { + const QColor windowBackgroundColor = (dark ? kDefaultSystemDarkColor : kDefaultSystemLightColor); + setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("background-color: %1;").arg(windowBackgroundColor.name())); + } + update(); +} diff --git a/FramelessHelper/examples/widget/widget.h b/FramelessHelper/examples/widget/widget.h new file mode 100644 index 0000000..1c984e0 --- /dev/null +++ b/FramelessHelper/examples/widget/widget.h @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 + +QT_BEGIN_NAMESPACE +class QLabel; +class QShortcut; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE +class StandardTitleBar; +FRAMELESSHELPER_END_NAMESPACE + +class Widget : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget) +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(Widget) + +public: + explicit Widget(QWidget *parent = nullptr); + ~Widget() override; + +protected: + void timerEvent(QTimerEvent *event) override; + void closeEvent(QCloseEvent *event) override; + +private: + void initialize(); + +private Q_SLOTS: + void updateStyleSheet(); + +private: + QLabel *m_clockLabel = nullptr; + FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar) *m_titleBar = nullptr; + QShortcut *m_fullScreenShortcut = nullptr; + QShortcut *m_cancelShortcut = nullptr; +}; diff --git a/FramelessHelper/examples/widget/widget.pro b/FramelessHelper/examples/widget/widget.pro new file mode 100644 index 0000000..d3124d4 --- /dev/null +++ b/FramelessHelper/examples/widget/widget.pro @@ -0,0 +1,17 @@ +TEMPLATE = app +TARGET = Widget +QT += widgets +win32: CONFIG -= embed_manifest_exe +DEFINES += WIDGET_USE_QMAKE +HEADERS += \ + ../shared/log.h \ + ../shared/settings.h \ + widget.h +SOURCES += \ + ../shared/log.cpp \ + ../shared/settings.cpp \ + widget.cpp \ + main.cpp +win32: RC_FILE = ../shared/example.rc +include(../../qmake/core.pri) +include(../../qmake/widgets.pri) diff --git a/FramelessHelper/include/FramelessHelper/Core/ChromePalette b/FramelessHelper/include/FramelessHelper/Core/ChromePalette new file mode 100644 index 0000000..de6bfff --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/ChromePalette @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Core/FramelessHelper_Linux b/FramelessHelper/include/FramelessHelper/Core/FramelessHelper_Linux new file mode 100644 index 0000000..de95d4e --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/FramelessHelper_Linux @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Core/FramelessHelper_Qt b/FramelessHelper/include/FramelessHelper/Core/FramelessHelper_Qt new file mode 100644 index 0000000..616e928 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/FramelessHelper_Qt @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Core/FramelessHelper_Win b/FramelessHelper/include/FramelessHelper/Core/FramelessHelper_Win new file mode 100644 index 0000000..3b164aa --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/FramelessHelper_Win @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Core/FramelessHelper_Windows b/FramelessHelper/include/FramelessHelper/Core/FramelessHelper_Windows new file mode 100644 index 0000000..631fbdd --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/FramelessHelper_Windows @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Core/FramelessManager b/FramelessHelper/include/FramelessHelper/Core/FramelessManager new file mode 100644 index 0000000..7785b35 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/FramelessManager @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Core/Global b/FramelessHelper/include/FramelessHelper/Core/Global new file mode 100644 index 0000000..aba4dba --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/Global @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Core/MicaMaterial b/FramelessHelper/include/FramelessHelper/Core/MicaMaterial new file mode 100644 index 0000000..296cdea --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/MicaMaterial @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Core/Utils b/FramelessHelper/include/FramelessHelper/Core/Utils new file mode 100644 index 0000000..4360d4c --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/Utils @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Core/WindowBorderPainter b/FramelessHelper/include/FramelessHelper/Core/WindowBorderPainter new file mode 100644 index 0000000..726fd0a --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/WindowBorderPainter @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Core/chromepalette.h b/FramelessHelper/include/FramelessHelper/Core/chromepalette.h new file mode 100644 index 0000000..46a2cae --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/chromepalette.h @@ -0,0 +1,129 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcChromePalette) + +class ChromePalettePrivate; + +class FRAMELESSHELPER_CORE_API ChromePalette : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(ChromePalette) + Q_DISABLE_COPY_MOVE(ChromePalette) + + Q_PROPERTY(QColor titleBarActiveBackgroundColor READ titleBarActiveBackgroundColor + WRITE setTitleBarActiveBackgroundColor RESET resetTitleBarActiveBackgroundColor + NOTIFY titleBarActiveBackgroundColorChanged FINAL) + Q_PROPERTY(QColor titleBarInactiveBackgroundColor READ titleBarInactiveBackgroundColor + WRITE setTitleBarInactiveBackgroundColor RESET resetTitleBarInactiveBackgroundColor + NOTIFY titleBarInactiveBackgroundColorChanged FINAL) + Q_PROPERTY(QColor titleBarActiveForegroundColor READ titleBarActiveForegroundColor + WRITE setTitleBarActiveForegroundColor RESET resetTitleBarActiveForegroundColor + NOTIFY titleBarActiveForegroundColorChanged FINAL) + Q_PROPERTY(QColor titleBarInactiveForegroundColor READ titleBarInactiveForegroundColor + WRITE setTitleBarInactiveForegroundColor RESET resetTitleBarInactiveForegroundColor + NOTIFY titleBarInactiveForegroundColorChanged FINAL) + Q_PROPERTY(QColor chromeButtonNormalColor READ chromeButtonNormalColor + WRITE setChromeButtonNormalColor RESET resetChromeButtonNormalColor + NOTIFY chromeButtonNormalColorChanged FINAL) + Q_PROPERTY(QColor chromeButtonHoverColor READ chromeButtonHoverColor + WRITE setChromeButtonHoverColor RESET resetChromeButtonHoverColor + NOTIFY chromeButtonHoverColorChanged FINAL) + Q_PROPERTY(QColor chromeButtonPressColor READ chromeButtonPressColor + WRITE setChromeButtonPressColor RESET resetChromeButtonPressColor + NOTIFY chromeButtonPressColorChanged FINAL) + Q_PROPERTY(QColor closeButtonNormalColor READ closeButtonNormalColor + WRITE setCloseButtonNormalColor RESET resetCloseButtonNormalColor + NOTIFY closeButtonNormalColorChanged FINAL) + Q_PROPERTY(QColor closeButtonHoverColor READ closeButtonHoverColor + WRITE setCloseButtonHoverColor RESET resetCloseButtonHoverColor + NOTIFY closeButtonHoverColorChanged FINAL) + Q_PROPERTY(QColor closeButtonPressColor READ closeButtonPressColor + WRITE setCloseButtonPressColor RESET resetCloseButtonPressColor + NOTIFY closeButtonPressColorChanged FINAL) + +public: + explicit ChromePalette(QObject *parent = nullptr); + ~ChromePalette() override; + + Q_NODISCARD QColor titleBarActiveBackgroundColor() const; + Q_NODISCARD QColor titleBarInactiveBackgroundColor() const; + Q_NODISCARD QColor titleBarActiveForegroundColor() const; + Q_NODISCARD QColor titleBarInactiveForegroundColor() const; + Q_NODISCARD QColor chromeButtonNormalColor() const; + Q_NODISCARD QColor chromeButtonHoverColor() const; + Q_NODISCARD QColor chromeButtonPressColor() const; + Q_NODISCARD QColor closeButtonNormalColor() const; + Q_NODISCARD QColor closeButtonHoverColor() const; + Q_NODISCARD QColor closeButtonPressColor() const; + +public Q_SLOTS: + void setTitleBarActiveBackgroundColor(const QColor &value); + void resetTitleBarActiveBackgroundColor(); + void setTitleBarInactiveBackgroundColor(const QColor &value); + void resetTitleBarInactiveBackgroundColor(); + void setTitleBarActiveForegroundColor(const QColor &value); + void resetTitleBarActiveForegroundColor(); + void setTitleBarInactiveForegroundColor(const QColor &value); + void resetTitleBarInactiveForegroundColor(); + void setChromeButtonNormalColor(const QColor &value); + void resetChromeButtonNormalColor(); + void setChromeButtonHoverColor(const QColor &value); + void resetChromeButtonHoverColor(); + void setChromeButtonPressColor(const QColor &value); + void resetChromeButtonPressColor(); + void setCloseButtonNormalColor(const QColor &value); + void resetCloseButtonNormalColor(); + void setCloseButtonHoverColor(const QColor &value); + void resetCloseButtonHoverColor(); + void setCloseButtonPressColor(const QColor &value); + void resetCloseButtonPressColor(); + +Q_SIGNALS: + void titleBarActiveBackgroundColorChanged(); + void titleBarInactiveBackgroundColorChanged(); + void titleBarActiveForegroundColorChanged(); + void titleBarInactiveForegroundColorChanged(); + void chromeButtonNormalColorChanged(); + void chromeButtonHoverColorChanged(); + void chromeButtonPressColorChanged(); + void closeButtonNormalColorChanged(); + void closeButtonHoverColorChanged(); + void closeButtonPressColorChanged(); + void titleBarColorChanged(); + void chromeButtonColorChanged(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(ChromePalette)) diff --git a/FramelessHelper/include/FramelessHelper/Core/framelesshelper_linux.h b/FramelessHelper/include/FramelessHelper/Core/framelesshelper_linux.h new file mode 100644 index 0000000..d1829d2 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/framelesshelper_linux.h @@ -0,0 +1,512 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 + +/* + * Copyright (C) 2001-2006 Bart Massey, Jamey Sharp, and Josh Triplett. + * All Rights Reserved. + * + * 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 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. + * + * Except as contained in this notice, the names of the authors or their + * institutions shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization from the authors. + */ + +// This one is not included in any public headers. +using Display = struct _XDisplay; + +#if __has_include() +# include +# define FRAMELESSHELPER_HAS_XCB +#else // !__has_include() + +using xcb_connection_t = struct xcb_connection_t; +using xcb_button_t = uint8_t; +using xcb_window_t = uint32_t; +using xcb_timestamp_t = uint32_t; +using xcb_atom_t = uint32_t; + +using xcb_generic_error_t = struct xcb_generic_error_t +{ + uint8_t response_type; + uint8_t error_code; + uint16_t sequence; + uint32_t resource_id; + uint16_t minor_code; + uint8_t major_code; + uint8_t pad0; + uint32_t pad[5]; + uint32_t full_sequence; +}; + +using xcb_client_message_data_t = union xcb_client_message_data_t +{ + uint8_t data8[20]; + uint16_t data16[10]; + uint32_t data32[5]; +}; + +using xcb_button_press_event_t = struct xcb_button_press_event_t +{ + uint8_t response_type; + xcb_button_t detail; + uint16_t sequence; + xcb_timestamp_t time; + xcb_window_t root; + xcb_window_t event; + xcb_window_t child; + int16_t root_x; + int16_t root_y; + int16_t event_x; + int16_t event_y; + uint16_t state; + uint8_t same_screen; + uint8_t pad0; +}; +using xcb_button_release_event_t = xcb_button_press_event_t; + +using xcb_void_cookie_t = struct xcb_void_cookie_t +{ + unsigned int sequence; +}; + +using xcb_intern_atom_cookie_t = struct xcb_intern_atom_cookie_t +{ + unsigned int sequence; +}; + +using xcb_intern_atom_reply_t = struct xcb_intern_atom_reply_t +{ + uint8_t response_type; + uint8_t pad0; + uint16_t sequence; + uint32_t length; + xcb_atom_t atom; +}; + +using xcb_client_message_event_t = struct xcb_client_message_event_t +{ + uint8_t response_type; + uint8_t format; + uint16_t sequence; + xcb_window_t window; + xcb_atom_t type; + xcb_client_message_data_t data; +}; + +using xcb_get_property_reply_t = struct xcb_get_property_reply_t +{ + uint8_t response_type; + uint8_t format; + uint16_t sequence; + uint32_t length; + xcb_atom_t type; + uint32_t bytes_after; + uint32_t value_len; + uint8_t pad0[12]; +}; + +using xcb_get_property_cookie_t = struct xcb_get_property_cookie_t +{ + unsigned int sequence; +}; + +using xcb_list_properties_cookie_t = struct xcb_list_properties_cookie_t +{ + unsigned int sequence; +}; + +using xcb_list_properties_reply_t = struct xcb_list_properties_reply_t +{ + uint8_t response_type; + uint8_t pad0; + uint16_t sequence; + uint32_t length; + uint16_t atoms_len; + uint8_t pad1[22]; +}; + +[[maybe_unused]] inline constexpr const auto XCB_NONE = 0; +[[maybe_unused]] inline constexpr const auto XCB_WINDOW_NONE = 0; +[[maybe_unused]] inline constexpr const auto XCB_CURRENT_TIME = 0; +[[maybe_unused]] inline constexpr const auto XCB_PROP_MODE_REPLACE = 0; +[[maybe_unused]] inline constexpr const auto XCB_ATOM_ATOM = 4; +[[maybe_unused]] inline constexpr const auto XCB_ATOM_CARDINAL = 6; +[[maybe_unused]] inline constexpr const auto XCB_ATOM_WINDOW = 33; +[[maybe_unused]] inline constexpr const auto XCB_BUTTON_INDEX_ANY = 0; +[[maybe_unused]] inline constexpr const auto XCB_BUTTON_INDEX_1 = 1; +[[maybe_unused]] inline constexpr const auto XCB_BUTTON_INDEX_2 = 2; +[[maybe_unused]] inline constexpr const auto XCB_BUTTON_INDEX_3 = 3; +[[maybe_unused]] inline constexpr const auto XCB_BUTTON_RELEASE = 5; +[[maybe_unused]] inline constexpr const auto XCB_CLIENT_MESSAGE = 33; +[[maybe_unused]] inline constexpr const auto XCB_EVENT_MASK_STRUCTURE_NOTIFY = 131072; +[[maybe_unused]] inline constexpr const auto XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT = 1048576; +[[maybe_unused]] inline constexpr const auto XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY = 524288; +#endif // __has_include() + +[[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0; +[[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOP = 1; +[[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2; +[[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_SIZE_RIGHT = 3; +[[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4; +[[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5; +[[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6; +[[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_SIZE_LEFT = 7; +[[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_MOVE = 8; +[[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_SIZE_KEYBOARD = 9; +[[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_MOVE_KEYBOARD = 10; +[[maybe_unused]] inline constexpr const auto _NET_WM_MOVERESIZE_CANCEL = 11; + +[[maybe_unused]] inline constexpr const char ATOM_NET_SUPPORTED[] = "_NET_SUPPORTED"; +[[maybe_unused]] inline constexpr const char ATOM_NET_WM_NAME[] = "_NET_WM_NAME"; +[[maybe_unused]] inline constexpr const char ATOM_NET_WM_MOVERESIZE[] = "_NET_WM_MOVERESIZE"; +[[maybe_unused]] inline constexpr const char ATOM_NET_SUPPORTING_WM_CHECK[] = "_NET_SUPPORTING_WM_CHECK"; +[[maybe_unused]] inline constexpr const char ATOM_NET_KDE_COMPOSITE_TOGGLING[] = "_NET_KDE_COMPOSITE_TOGGLING"; +[[maybe_unused]] inline constexpr const char ATOM_KDE_NET_WM_BLUR_BEHIND_REGION[] = "_KDE_NET_WM_BLUR_BEHIND_REGION"; +[[maybe_unused]] inline constexpr const char ATOM_GTK_SHOW_WINDOW_MENU[] = "_GTK_SHOW_WINDOW_MENU"; +[[maybe_unused]] inline constexpr const char ATOM_DEEPIN_NO_TITLEBAR[] = "_DEEPIN_NO_TITLEBAR"; +[[maybe_unused]] inline constexpr const char ATOM_DEEPIN_FORCE_DECORATE[] = "_DEEPIN_FORCE_DECORATE"; +[[maybe_unused]] inline constexpr const char ATOM_NET_WM_DEEPIN_BLUR_REGION_MASK[] = "_NET_WM_DEEPIN_BLUR_REGION_MASK"; +[[maybe_unused]] inline constexpr const char ATOM_NET_WM_DEEPIN_BLUR_REGION_ROUNDED[] = "_NET_WM_DEEPIN_BLUR_REGION_ROUNDED"; +[[maybe_unused]] inline constexpr const char ATOM_UTF8_STRING[] = "UTF8_STRING"; + +#ifndef FRAMELESSHELPER_HAS_XCB +extern "C" +{ + +FRAMELESSHELPER_CORE_API xcb_void_cookie_t +xcb_send_event( + xcb_connection_t *connection, + uint8_t propagate, + xcb_window_t destination, + uint32_t event_mask, + const char *event +); + +FRAMELESSHELPER_CORE_API int +xcb_flush( + xcb_connection_t *connection +); + +FRAMELESSHELPER_CORE_API xcb_intern_atom_cookie_t +xcb_intern_atom( + xcb_connection_t *connection, + uint8_t only_if_exists, + uint16_t name_len, + const char *name +); + +FRAMELESSHELPER_CORE_API xcb_intern_atom_reply_t * +xcb_intern_atom_reply( + xcb_connection_t *connection, + xcb_intern_atom_cookie_t cookie, + xcb_generic_error_t **error +); + +FRAMELESSHELPER_CORE_API xcb_void_cookie_t +xcb_ungrab_pointer( + xcb_connection_t *connection, + xcb_timestamp_t time +); + +FRAMELESSHELPER_CORE_API xcb_void_cookie_t +xcb_change_property( + xcb_connection_t *connection, + uint8_t mode, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint8_t format, + uint32_t data_len, + const void *data +); + +FRAMELESSHELPER_CORE_API xcb_void_cookie_t +xcb_delete_property_checked( + xcb_connection_t *connection, + xcb_window_t window, + xcb_atom_t property +); + +FRAMELESSHELPER_CORE_API xcb_get_property_cookie_t +xcb_get_property( + xcb_connection_t *connection, + uint8_t _delete, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint32_t long_offset, + uint32_t long_length +); + +FRAMELESSHELPER_CORE_API xcb_get_property_reply_t * +xcb_get_property_reply( + xcb_connection_t *connection, + xcb_get_property_cookie_t cookie, + xcb_generic_error_t **error +); + +FRAMELESSHELPER_CORE_API void * +xcb_get_property_value( + const xcb_get_property_reply_t *reply +); + +FRAMELESSHELPER_CORE_API int +xcb_get_property_value_length( + const xcb_get_property_reply_t *reply +); + +FRAMELESSHELPER_CORE_API xcb_list_properties_cookie_t +xcb_list_properties( + xcb_connection_t *connection, + xcb_window_t window +); + +FRAMELESSHELPER_CORE_API xcb_list_properties_reply_t * +xcb_list_properties_reply( + xcb_connection_t *connection, + xcb_list_properties_cookie_t cookie, + xcb_generic_error_t **error +); + +FRAMELESSHELPER_CORE_API int +xcb_list_properties_atoms_length( + const xcb_list_properties_reply_t *atom +); + +FRAMELESSHELPER_CORE_API xcb_atom_t * +xcb_list_properties_atoms( + const xcb_list_properties_reply_t *atom +); + +FRAMELESSHELPER_CORE_API xcb_get_property_cookie_t +xcb_get_property_unchecked( + xcb_connection_t *connection, + uint8_t _delete, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint32_t long_offset, + uint32_t long_length +); + +} // extern "C" +#endif // FRAMELESSHELPER_HAS_XCB + +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#if __has_include() +# undef signals // Workaround a compilation issue caused by GTK. +# include +# define FRAMELESSHELPER_HAS_GTK +#else // !__has_include() + +#define G_VALUE_INIT { 0, { { 0 } } } +#define g_signal_connect(instance, detailed_signal, c_handler, data) \ + g_signal_connect_data((instance), (detailed_signal), (c_handler), (data), nullptr, G_CONNECT_DEFAULT) + +using gint = int; +using glong = long; +using gshort = short; +using gboolean = int; +using gushort = unsigned short; +using guint = unsigned int; +using gulong = unsigned long; +using gfloat = float; +using gdouble = double; +using gchar = char; +using guchar = unsigned char; +using gchararray = char *; +using gpointer = void *; +using gint64 = signed long; +using guint64 = unsigned long; +using gsize = unsigned int; + +using GType = unsigned long; // TODO: or unsigned int? +using GValue = struct _GValue; +using GObject = struct _GObject; +using GClosure = struct _GClosure; +using GtkSettings = struct _GtkSettings; + +using GConnectFlags = enum GConnectFlags +{ + G_CONNECT_DEFAULT = 0, + G_CONNECT_AFTER = 1 << 0, + G_CONNECT_SWAPPED = 1 << 1 +}; + +using GCallback = void(*)(void); +using GClosureNotify = void(*)(gpointer data, GClosure *closure); + +struct _GValue +{ + GType g_type; + + union + { + gint v_int; + guint v_uint; + glong v_long; + gulong v_ulong; + gint64 v_int64; + guint64 v_uint64; + gfloat v_float; + gdouble v_double; + gpointer v_pointer; + } data[2]; +}; +#endif // __has_include() + +[[maybe_unused]] inline constexpr const char GTK_THEME_NAME_ENV_VAR[] = "GTK_THEME"; +[[maybe_unused]] inline constexpr const char GTK_THEME_NAME_PROP[] = "gtk-theme-name"; +[[maybe_unused]] inline constexpr const char GTK_THEME_PREFER_DARK_PROP[] = "gtk-application-prefer-dark-theme"; + +#ifndef FRAMELESSHELPER_HAS_GTK +extern "C" +{ + +FRAMELESSHELPER_CORE_API void +gtk_init( + int *argc, + char ***argv +); + +FRAMELESSHELPER_CORE_API GValue * +g_value_init( + GValue *value, + GType g_type +); + +FRAMELESSHELPER_CORE_API GValue * +g_value_reset( + GValue *value +); + +FRAMELESSHELPER_CORE_API void +g_value_unset( + GValue *value +); + +FRAMELESSHELPER_CORE_API gboolean +g_value_get_boolean( + const GValue *value +); + +FRAMELESSHELPER_CORE_API const gchar * +g_value_get_string( + const GValue *value +); + +FRAMELESSHELPER_CORE_API GtkSettings * +gtk_settings_get_default( + void +); + +FRAMELESSHELPER_CORE_API void +g_object_get_property( + GObject *object, + const gchar *property_name, + GValue *value +); + +FRAMELESSHELPER_CORE_API gulong +g_signal_connect_data( + gpointer instance, + const gchar *detailed_signal, + GCallback c_handler, + gpointer data, + GClosureNotify destroy_data, + GConnectFlags connect_flags +); + +FRAMELESSHELPER_CORE_API void +g_free( + gpointer mem +); + +FRAMELESSHELPER_CORE_API void +g_object_unref( + GObject *object +); + +FRAMELESSHELPER_CORE_API void +g_clear_object( + GObject **object_ptr +); + +} // extern "C" +#endif // FRAMELESSHELPER_HAS_GTK + +FRAMELESSHELPER_BEGIN_NAMESPACE +template +[[nodiscard]] T gtkSettings(const gchar *property); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +using x11_return_type = quint32; +#else // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +using x11_return_type = unsigned long; +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +FRAMELESSHELPER_END_NAMESPACE diff --git a/FramelessHelper/include/FramelessHelper/Core/framelesshelper_qt.h b/FramelessHelper/include/FramelessHelper/Core/framelesshelper_qt.h new file mode 100644 index 0000000..0d92733 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/framelesshelper_qt.h @@ -0,0 +1,51 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcFramelessHelperQt) + +class FRAMELESSHELPER_CORE_API FramelessHelperQt : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(FramelessHelperQt) + +public: + explicit FramelessHelperQt(QObject *parent = nullptr); + ~FramelessHelperQt() override; + + static void addWindow(const Global::SystemParameters ¶ms); + static void removeWindow(const WId windowId); + +protected: + Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessHelperQt)) diff --git a/FramelessHelper/include/FramelessHelper/Core/framelesshelper_win.h b/FramelessHelper/include/FramelessHelper/Core/framelesshelper_win.h new file mode 100644 index 0000000..37611d1 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/framelesshelper_win.h @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcFramelessHelperWin) + +class FRAMELESSHELPER_CORE_API FramelessHelperWin : public QAbstractNativeEventFilter +{ + Q_DISABLE_COPY_MOVE(FramelessHelperWin) + +public: + explicit FramelessHelperWin(); + ~FramelessHelperWin() override; + + static void addWindow(const Global::SystemParameters ¶ms); + static void removeWindow(const WId windowId); + + Q_NODISCARD bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessHelperWin)) diff --git a/FramelessHelper/include/FramelessHelper/Core/framelesshelper_windows.h b/FramelessHelper/include/FramelessHelper/Core/framelesshelper_windows.h new file mode 100644 index 0000000..abb98c0 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/framelesshelper_windows.h @@ -0,0 +1,714 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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. + */ + +// Copyright (C) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifndef WINRT_LEAN_AND_MEAN +# define WINRT_LEAN_AND_MEAN +#endif + +#ifndef UNICODE +# define UNICODE +#endif + +#ifndef _UNICODE +# define _UNICODE +#endif + +#ifndef _CRT_NON_CONFORMING_SWPRINTFS +# define _CRT_NON_CONFORMING_SWPRINTFS +#endif + +#ifndef _CRT_SECURE_NO_WARNINGS +# define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef NOMINMAX +# define NOMINMAX +#endif + +#if __has_include() +# include +#endif + +#ifndef _WIN32_WINNT_WIN10 +# define _WIN32_WINNT_WIN10 0x0A00 +#endif + +#ifndef NTDDI_WIN10_NI +# define NTDDI_WIN10_NI 0x0A00000C +#endif + +#ifndef WINVER +# define WINVER _WIN32_WINNT_WIN10 +#endif + +#ifndef _WIN32_WINNT +# define _WIN32_WINNT _WIN32_WINNT_WIN10 +#endif + +#ifndef NTDDI_VERSION +# define NTDDI_VERSION NTDDI_WIN10_NI +#endif + +#include +#include +#include +#include + +#include + +#ifndef WM_NCUAHDRAWCAPTION +# define WM_NCUAHDRAWCAPTION (0x00AE) +#endif + +#ifndef WM_NCUAHDRAWFRAME +# define WM_NCUAHDRAWFRAME (0x00AF) +#endif + +#ifndef WM_DWMCOMPOSITIONCHANGED +# define WM_DWMCOMPOSITIONCHANGED (0x031E) +#endif + +#ifndef WM_DWMCOLORIZATIONCOLORCHANGED +# define WM_DWMCOLORIZATIONCOLORCHANGED (0x0320) +#endif + +#ifndef WM_DPICHANGED +# define WM_DPICHANGED (0x02E0) +#endif + +#ifndef WM_GETDPISCALEDSIZE +# define WM_GETDPISCALEDSIZE (0x02E4) +#endif + +#ifndef SM_CXPADDEDBORDER +# define SM_CXPADDEDBORDER (92) +#endif + +#ifndef SM_CYPADDEDBORDER +# define SM_CYPADDEDBORDER SM_CXPADDEDBORDER +#endif + +#ifndef ABM_GETAUTOHIDEBAREX +# define ABM_GETAUTOHIDEBAREX (0x0000000b) +#endif + +#ifndef GET_X_LPARAM +# define GET_X_LPARAM(lp) (static_cast(static_cast(LOWORD(lp)))) +#endif + +#ifndef GET_Y_LPARAM +# define GET_Y_LPARAM(lp) (static_cast(static_cast(HIWORD(lp)))) +#endif + +#ifndef IsMinimized +# define IsMinimized(hwnd) (IsIconic(hwnd) != FALSE) +#endif + +#ifndef IsMaximized +# define IsMaximized(hwnd) (IsZoomed(hwnd) != FALSE) +#endif + +#ifndef MMSYSERR_NOERROR +# define MMSYSERR_NOERROR (0) +#endif + +#ifndef TIMERR_NOERROR +# define TIMERR_NOERROR (0) +#endif + +#ifndef WS_EX_NOREDIRECTIONBITMAP +# define WS_EX_NOREDIRECTIONBITMAP (0x00200000L) +#endif + +#ifndef USER_DEFAULT_SCREEN_DPI +# define USER_DEFAULT_SCREEN_DPI (96) +#endif + +DECLARE_HANDLE(_DPI_AWARENESS_CONTEXT); + +#ifndef _DPI_AWARENESS_CONTEXT_UNAWARE +# define _DPI_AWARENESS_CONTEXT_UNAWARE (reinterpret_cast<_DPI_AWARENESS_CONTEXT>(-1)) +#endif + +#ifndef _DPI_AWARENESS_CONTEXT_SYSTEM_AWARE +# define _DPI_AWARENESS_CONTEXT_SYSTEM_AWARE (reinterpret_cast<_DPI_AWARENESS_CONTEXT>(-2)) +#endif + +#ifndef _DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE +# define _DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (reinterpret_cast<_DPI_AWARENESS_CONTEXT>(-3)) +#endif + +#ifndef _DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 +# define _DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (reinterpret_cast<_DPI_AWARENESS_CONTEXT>(-4)) +#endif + +#ifndef _DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED +# define _DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED (reinterpret_cast<_DPI_AWARENESS_CONTEXT>(-5)) +#endif + +#ifndef HKEY_CLASSES_ROOT +# define HKEY_CLASSES_ROOT (reinterpret_cast(static_cast(static_cast(0x80000000)))) +#endif + +#ifndef HKEY_CURRENT_USER +# define HKEY_CURRENT_USER (reinterpret_cast(static_cast(static_cast(0x80000001)))) +#endif + +#ifndef HKEY_LOCAL_MACHINE +# define HKEY_LOCAL_MACHINE (reinterpret_cast(static_cast(static_cast(0x80000002)))) +#endif + +#ifndef HKEY_USERS +# define HKEY_USERS (reinterpret_cast(static_cast(static_cast(0x80000003)))) +#endif + +#ifndef HKEY_PERFORMANCE_DATA +# define HKEY_PERFORMANCE_DATA (reinterpret_cast(static_cast(static_cast(0x80000004)))) +#endif + +#ifndef HKEY_CURRENT_CONFIG +# define HKEY_CURRENT_CONFIG (reinterpret_cast(static_cast(static_cast(0x80000005)))) +#endif + +#ifndef HKEY_DYN_DATA +# define HKEY_DYN_DATA (reinterpret_cast(static_cast(static_cast(0x80000006)))) +#endif + +#ifndef HKEY_CURRENT_USER_LOCAL_SETTINGS +# define HKEY_CURRENT_USER_LOCAL_SETTINGS (reinterpret_cast(static_cast(static_cast(0x80000007)))) +#endif + +#ifndef HKEY_PERFORMANCE_TEXT +# define HKEY_PERFORMANCE_TEXT (reinterpret_cast(static_cast(static_cast(0x80000050)))) +#endif + +#ifndef HKEY_PERFORMANCE_NLSTEXT +# define HKEY_PERFORMANCE_NLSTEXT (reinterpret_cast(static_cast(static_cast(0x80000060)))) +#endif + +#ifndef _STATUS_SUCCESS +# define _STATUS_SUCCESS (static_cast<_NTSTATUS>(0x00000000L)) +#endif + +#ifndef WTNCA_NODRAWCAPTION +# define WTNCA_NODRAWCAPTION (0x00000001) // don't draw the window caption +#endif + +#ifndef WTNCA_NODRAWICON +# define WTNCA_NODRAWICON (0x00000002) // don't draw the system icon +#endif + +#ifndef WTNCA_NOSYSMENU +# define WTNCA_NOSYSMENU (0x00000004) // don't expose the system menu icon functionality +#endif + +#ifndef WTNCA_NOMIRRORHELP +# define WTNCA_NOMIRRORHELP (0x00000008) // don't mirror the question mark, even in RTL layout +#endif + +#ifndef WTNCA_VALIDBITS +# define WTNCA_VALIDBITS (WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON | WTNCA_NOSYSMENU | WTNCA_NOMIRRORHELP) +#endif + +#ifndef EXTERN_C +# define EXTERN_C extern "C" +#endif + +#ifndef EXTERN_C_START +# define EXTERN_C_START EXTERN_C { +#endif + +#ifndef EXTERN_C_END +# define EXTERN_C_END } +#endif + +using _NTSTATUS = LONG; + +using _MMRESULT = UINT; +using _TIMECAPS = struct _TIMECAPS +{ + UINT wPeriodMin; // minimum period supported + UINT wPeriodMax; // maximum period supported +}; +using _PTIMECAPS = _TIMECAPS *; + +using _PROCESS_DPI_AWARENESS = enum _PROCESS_DPI_AWARENESS +{ + _PROCESS_DPI_UNAWARE = 0, + _PROCESS_SYSTEM_DPI_AWARE = 1, + _PROCESS_PER_MONITOR_DPI_AWARE = 2, + _PROCESS_PER_MONITOR_DPI_AWARE_V2 = 3, + _PROCESS_DPI_UNAWARE_GDISCALED = 4 +}; + +using _MONITOR_DPI_TYPE = enum _MONITOR_DPI_TYPE +{ + _MDT_EFFECTIVE_DPI = 0, + _MDT_ANGULAR_DPI = 1, + _MDT_RAW_DPI = 2, + _MDT_DEFAULT = _MDT_EFFECTIVE_DPI +}; + +using _DEVICE_SCALE_FACTOR = enum _DEVICE_SCALE_FACTOR +{ + _DEVICE_SCALE_FACTOR_INVALID = 0, + _SCALE_100_PERCENT = 100, + _SCALE_120_PERCENT = 120, + _SCALE_125_PERCENT = 125, + _SCALE_140_PERCENT = 140, + _SCALE_150_PERCENT = 150, + _SCALE_160_PERCENT = 160, + _SCALE_175_PERCENT = 175, + _SCALE_180_PERCENT = 180, + _SCALE_200_PERCENT = 200, + _SCALE_225_PERCENT = 225, + _SCALE_250_PERCENT = 250, + _SCALE_300_PERCENT = 300, + _SCALE_350_PERCENT = 350, + _SCALE_400_PERCENT = 400, + _SCALE_450_PERCENT = 450, + _SCALE_500_PERCENT = 500 +}; + +using _DPI_AWARENESS = enum _DPI_AWARENESS +{ + _DPI_AWARENESS_INVALID = -1, + _DPI_AWARENESS_UNAWARE = 0, + _DPI_AWARENESS_SYSTEM_AWARE = 1, + _DPI_AWARENESS_PER_MONITOR_AWARE = 2, + _DPI_AWARENESS_PER_MONITOR_AWARE_V2 = 3, + _DPI_AWARENESS_UNAWARE_GDISCALED = 4 +}; + +using _DWMWINDOWATTRIBUTE = enum _DWMWINDOWATTRIBUTE +{ + _DWMWA_USE_HOSTBACKDROPBRUSH = 17, // [set] BOOL, Allows the use of host backdrop brushes for the window. + _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19, // Undocumented, the same with DWMWA_USE_IMMERSIVE_DARK_MODE, but available on systems before Win10 20H1. + _DWMWA_USE_IMMERSIVE_DARK_MODE = 20, // [set] BOOL, Allows a window to either use the accent color, or dark, according to the user Color Mode preferences. + _DWMWA_WINDOW_CORNER_PREFERENCE = 33, // [set] WINDOW_CORNER_PREFERENCE, Controls the policy that rounds top-level window corners + _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37, // [get] UINT, width of the visible border around a thick frame window + _DWMWA_SYSTEMBACKDROP_TYPE = 38, // [get, set] SYSTEMBACKDROP_TYPE, Controls the system-drawn backdrop material of a window, including behind the non-client area. + _DWMWA_MICA_EFFECT = 1029 // Undocumented, use this value to enable Mica material on Win11 21H2. You should use DWMWA_SYSTEMBACKDROP_TYPE instead on Win11 22H2 and newer. +}; + +using _DWM_WINDOW_CORNER_PREFERENCE = enum _DWM_WINDOW_CORNER_PREFERENCE +{ + _DWMWCP_DEFAULT = 0, // Let the system decide whether or not to round window corners + _DWMWCP_DONOTROUND = 1, // Never round window corners + _DWMWCP_ROUND = 2, // Round the corners if appropriate + _DWMWCP_ROUNDSMALL = 3 // Round the corners if appropriate, with a small radius +}; + +using _DWM_SYSTEMBACKDROP_TYPE = enum _DWM_SYSTEMBACKDROP_TYPE +{ + _DWMSBT_AUTO = 0, // [Default] Let DWM automatically decide the system-drawn backdrop for this window. + _DWMSBT_NONE = 1, // Do not draw any system backdrop. + _DWMSBT_MAINWINDOW = 2, /* Mica */ // Draw the backdrop material effect corresponding to a long-lived window. + _DWMSBT_TRANSIENTWINDOW = 3, /* Acrylic */ // Draw the backdrop material effect corresponding to a transient window. + _DWMSBT_TABBEDWINDOW = 4 /* Mica Alt */ // Draw the backdrop material effect corresponding to a window with a tabbed title bar. +}; + +using WINDOWCOMPOSITIONATTRIB = enum WINDOWCOMPOSITIONATTRIB +{ + WCA_UNDEFINED = 0, + WCA_NCRENDERING_ENABLED = 1, + WCA_NCRENDERING_POLICY = 2, + WCA_TRANSITIONS_FORCEDISABLED = 3, + WCA_ALLOW_NCPAINT = 4, + WCA_CAPTION_BUTTON_BOUNDS = 5, + WCA_NONCLIENT_RTL_LAYOUT = 6, + WCA_FORCE_ICONIC_REPRESENTATION = 7, + WCA_EXTENDED_FRAME_BOUNDS = 8, + WCA_HAS_ICONIC_BITMAP = 9, + WCA_THEME_ATTRIBUTES = 10, + WCA_NCRENDERING_EXILED = 11, + WCA_NCADORNMENTINFO = 12, + WCA_EXCLUDED_FROM_LIVEPREVIEW = 13, + WCA_VIDEO_OVERLAY_ACTIVE = 14, + WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15, + WCA_DISALLOW_PEEK = 16, + WCA_CLOAK = 17, + WCA_CLOAKED = 18, + WCA_ACCENT_POLICY = 19, + WCA_FREEZE_REPRESENTATION = 20, + WCA_EVER_UNCLOAKED = 21, + WCA_VISUAL_OWNER = 22, + WCA_HOLOGRAPHIC = 23, + WCA_EXCLUDED_FROM_DDA = 24, + WCA_PASSIVEUPDATEMODE = 25, + WCA_USEDARKMODECOLORS = 26, + WCA_CORNER_STYLE = 27, + WCA_PART_COLOR = 28, + WCA_DISABLE_MOVESIZE_FEEDBACK = 29, + WCA_LAST = 30 +}; + +using ACCENT_STATE = enum ACCENT_STATE +{ + ACCENT_DISABLED = 0, + ACCENT_ENABLE_GRADIENT = 1, + ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, + ACCENT_ENABLE_BLURBEHIND = 3, // Traditional DWM blur + ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803 + ACCENT_USE_HOST_BACKDROP = 5, // RS5 1809 + ACCENT_INVALID_STATE = 6 // Using this value will remove the window background +}; + +using ACCENT_POLICY = struct ACCENT_POLICY +{ + ACCENT_STATE State; + DWORD Flags; + DWORD GradientColor; // #AABBGGRR + DWORD AnimationId; +}; +using PACCENT_POLICY = ACCENT_POLICY *; + +using WINDOWCOMPOSITIONATTRIBDATA = struct WINDOWCOMPOSITIONATTRIBDATA +{ + WINDOWCOMPOSITIONATTRIB Attrib; + PVOID pvData; + DWORD cbData; +}; +using PWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA *; + +using _WINDOWTHEMEATTRIBUTETYPE = enum _WINDOWTHEMEATTRIBUTETYPE +{ + _WTA_NONCLIENT = 1 +}; + +using WTA_OPTIONS2 = struct WTA_OPTIONS2 +{ + DWORD dwFlags; // Values for each style option specified in the bitmask. + DWORD dwMask; // Bitmask for flags that are changing. +}; +using PWTA_OPTIONS2 = WTA_OPTIONS2 *; + +using IMMERSIVE_HC_CACHE_MODE = enum IMMERSIVE_HC_CACHE_MODE +{ + IHCM_USE_CACHED_VALUE = 0, + IHCM_REFRESH = 1 +}; + +using PREFERRED_APP_MODE = enum PREFERRED_APP_MODE +{ + PAM_DEFAULT = 0, // Default behavior on systems before Win10 1809. It indicates the application doesn't support dark mode at all. + PAM_AUTO = 1, // Available since Win10 1809, let system decide whether to enable dark mode or not. + PAM_DARK = 2, // Available since Win10 1903, force dark mode regardless of the system theme. + PAM_LIGHT = 3, // Available since Win10 1903, force light mode regardless of the system theme. + PAM_MAX = 4 +}; + +using GetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA); +using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA); +// Win10 1809 (10.0.17763) +using ShouldAppsUseDarkModePtr = BOOL(WINAPI *)(VOID); // Ordinal 132 +using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL); // Ordinal 133 +using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL); // Ordinal 135 +using FlushMenuThemesPtr = VOID(WINAPI *)(VOID); // Ordinal 136 +using RefreshImmersiveColorPolicyStatePtr = VOID(WINAPI *)(VOID); // Ordinal 104 +using IsDarkModeAllowedForWindowPtr = BOOL(WINAPI *)(HWND); // Ordinal 137 +using GetIsImmersiveColorUsingHighContrastPtr = BOOL(WINAPI *)(IMMERSIVE_HC_CACHE_MODE); // Ordinal 106 +using OpenNcThemeDataPtr = HTHEME(WINAPI *)(HWND, LPCWSTR); // Ordinal 49 +// Win10 1903 (10.0.18362) +using ShouldSystemUseDarkModePtr = BOOL(WINAPI *)(VOID); // Ordinal 138 +using SetPreferredAppModePtr = PREFERRED_APP_MODE(WINAPI *)(PREFERRED_APP_MODE); // Ordinal 135 +using IsDarkModeAllowedForAppPtr = BOOL(WINAPI *)(VOID); // Ordinal 139 + +EXTERN_C_START + +/////////////////////////////////////////////// +// Function prototypes + +_MMRESULT WINAPI +_timeGetDevCaps( + _PTIMECAPS ptc, + UINT cbtc +); + +_MMRESULT WINAPI +_timeBeginPeriod( + UINT uPeriod +); + +_MMRESULT WINAPI +_timeEndPeriod( + UINT uPeriod +); + +HRESULT WINAPI +_SetProcessDpiAwareness( + _PROCESS_DPI_AWARENESS value +); + +HRESULT WINAPI +_GetProcessDpiAwareness( + HANDLE hProcess, + _PROCESS_DPI_AWARENESS *value +); + +HRESULT WINAPI +_GetDpiForMonitor( + HMONITOR hMonitor, + _MONITOR_DPI_TYPE dpiType, + UINT *dpiX, + UINT *dpiY +); + +int WINAPI +_GetSystemMetricsForDpi( + int nIndex, + UINT dpi +); + +UINT WINAPI +_GetWindowDPI( + HWND hWnd +); + +UINT WINAPI +_GetDpiForWindow( + HWND hWnd +); + +UINT WINAPI +_GetDpiForSystem( + VOID +); + +UINT WINAPI +_GetSystemDpiForProcess( + HANDLE hProcess +); + +BOOL WINAPI +_SetProcessDpiAwarenessContext( + _DPI_AWARENESS_CONTEXT value +); + +BOOL WINAPI +_SetProcessDPIAware( + VOID +); + +HRESULT WINAPI +_GetScaleFactorForMonitor( + HMONITOR hMon, + _DEVICE_SCALE_FACTOR *pScale +); + +BOOL WINAPI +_EnableNonClientDpiScaling( + HWND hWnd +); + +_DPI_AWARENESS_CONTEXT WINAPI +_GetThreadDpiAwarenessContext( + VOID +); + +_DPI_AWARENESS_CONTEXT WINAPI +_GetWindowDpiAwarenessContext( + HWND hWnd +); + +_DPI_AWARENESS WINAPI +_GetAwarenessFromDpiAwarenessContext( + _DPI_AWARENESS_CONTEXT value +); + +_DPI_AWARENESS_CONTEXT WINAPI +_GetDpiAwarenessContextForProcess( + HANDLE hProcess +); + +BOOL WINAPI +_AreDpiAwarenessContextsEqual( + _DPI_AWARENESS_CONTEXT dpiContextA, + _DPI_AWARENESS_CONTEXT dpiContextB +); + +BOOL WINAPI +_EnableChildWindowDpiMessage( + HWND hWnd, + BOOL fEnable +); + +BOOL WINAPI +_EnablePerMonitorDialogScaling( + VOID +); + +int WINAPI +_GetDpiMetrics( + int nIndex, + UINT dpi +); + +BOOL WINAPI +_AdjustWindowRectExForDpi( + LPRECT lpRect, + DWORD dwStyle, + BOOL bMenu, + DWORD dwExStyle, + UINT dpi +); + +/////////////////////////////////////////////// +// API thunks + +FRAMELESSHELPER_CORE_API BOOL WINAPI +_GetWindowCompositionAttribute( + const HWND hWnd, + PWINDOWCOMPOSITIONATTRIBDATA pvData +); + +FRAMELESSHELPER_CORE_API BOOL WINAPI +_SetWindowCompositionAttribute( + const HWND hWnd, + PWINDOWCOMPOSITIONATTRIBDATA pvData +); + +FRAMELESSHELPER_CORE_API HRESULT WINAPI +_SetWindowThemeAttribute( + const HWND hWnd, + const _WINDOWTHEMEATTRIBUTETYPE attrib, + PVOID pvData, + const DWORD cbData +); + +FRAMELESSHELPER_CORE_API HRESULT WINAPI +_SetWindowThemeNonClientAttributes( + const HWND hWnd, + const DWORD dwMask, + const DWORD dwAttributes +); + +FRAMELESSHELPER_CORE_API BOOL WINAPI +_ShouldAppsUseDarkMode( + VOID +); + +FRAMELESSHELPER_CORE_API BOOL WINAPI +_AllowDarkModeForWindow( + const HWND hWnd, + const BOOL bAllow +); + +FRAMELESSHELPER_CORE_API BOOL WINAPI +_AllowDarkModeForApp( + const BOOL bAllow +); + +FRAMELESSHELPER_CORE_API VOID WINAPI +_FlushMenuThemes( + VOID +); + +FRAMELESSHELPER_CORE_API VOID WINAPI +_RefreshImmersiveColorPolicyState( + VOID +); + +FRAMELESSHELPER_CORE_API BOOL WINAPI +_IsDarkModeAllowedForWindow( + const HWND hWnd +); + +FRAMELESSHELPER_CORE_API BOOL WINAPI +_GetIsImmersiveColorUsingHighContrast( + const IMMERSIVE_HC_CACHE_MODE mode +); + +FRAMELESSHELPER_CORE_API HTHEME WINAPI +_OpenNcThemeData( + const HWND hWnd, + LPCWSTR pszClassList +); + +FRAMELESSHELPER_CORE_API BOOL WINAPI +_ShouldSystemUseDarkMode( + VOID +); + +FRAMELESSHELPER_CORE_API PREFERRED_APP_MODE WINAPI +_SetPreferredAppMode( + const PREFERRED_APP_MODE mode +); + +FRAMELESSHELPER_CORE_API BOOL WINAPI +_IsDarkModeAllowedForApp( + VOID +); + +FRAMELESSHELPER_CORE_API BOOL WINAPI +_EnableChildWindowDpiMessage2( + const HWND hWnd, + const BOOL fEnable +); + +FRAMELESSHELPER_CORE_API BOOL WINAPI +_EnablePerMonitorDialogScaling2( + VOID +); + +FRAMELESSHELPER_CORE_API UINT WINAPI +_GetDpiForWindow2( + const HWND hWnd +); + +FRAMELESSHELPER_CORE_API int WINAPI +_GetSystemMetricsForDpi2( + const int nIndex, + const UINT dpi +); + +FRAMELESSHELPER_CORE_API BOOL WINAPI +_AdjustWindowRectExForDpi2( + LPRECT lpRect, + const DWORD dwStyle, + const BOOL bMenu, + const DWORD dwExStyle, + const UINT dpi +); + +EXTERN_C_END + +[[maybe_unused]] inline constexpr const int kAutoHideTaskBarThickness = 2; // The thickness of an auto-hide taskbar in pixels. +[[maybe_unused]] inline constexpr const wchar_t kDwmRegistryKey[] = LR"(Software\Microsoft\Windows\DWM)"; +[[maybe_unused]] inline constexpr const wchar_t kPersonalizeRegistryKey[] = LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)"; +[[maybe_unused]] inline constexpr const wchar_t kThemeSettingChangeEventName[] = L"ImmersiveColorSet"; +[[maybe_unused]] inline constexpr const wchar_t kDwmColorKeyName[] = L"ColorPrevalence"; +[[maybe_unused]] inline constexpr const wchar_t kSystemDarkThemeResourceName[] = L"DarkMode_Explorer"; +[[maybe_unused]] inline constexpr const wchar_t kSystemLightThemeResourceName[] = L"Explorer"; +[[maybe_unused]] inline constexpr const wchar_t kDesktopRegistryKey[] = LR"(Control Panel\Desktop)"; +[[maybe_unused]] inline constexpr const wchar_t kDarkModePropertyName[] = L"UseImmersiveDarkModeColors"; diff --git a/FramelessHelper/include/FramelessHelper/Core/framelesshelpercore_global.h b/FramelessHelper/include/FramelessHelper/Core/framelesshelpercore_global.h new file mode 100644 index 0000000..71ad5d6 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/framelesshelpercore_global.h @@ -0,0 +1,647 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QScreen; +class QEvent; +class QEnterEvent; +QT_END_NAMESPACE + +#ifndef FRAMELESSHELPER_CORE_API +# ifdef FRAMELESSHELPER_CORE_STATIC +# define FRAMELESSHELPER_CORE_API +# else // FRAMELESSHELPER_CORE_STATIC +# ifdef FRAMELESSHELPER_CORE_LIBRARY +# define FRAMELESSHELPER_CORE_API Q_DECL_EXPORT +# else // FRAMELESSHELPER_CORE_LIBRARY +# define FRAMELESSHELPER_CORE_API Q_DECL_IMPORT +# endif // FRAMELESSHELPER_CORE_LIBRARY +# endif // FRAMELESSHELPER_CORE_STATIC +#endif + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINDOWS) +# define Q_OS_WINDOWS // Since 5.14 +#endif + +#ifndef Q_DISABLE_COPY_MOVE // Since 5.13 +# define Q_DISABLE_COPY_MOVE(Class) \ + Q_DISABLE_COPY(Class) \ + Class(Class &&) = delete; \ + Class &operator=(Class &&) = delete; +#endif + +#if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) + using QStringView = const QString &; +#endif + +#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) +# define qExchange(a, b) std::exchange(a, b) +# define Q_NAMESPACE_EXPORT(...) Q_NAMESPACE +#endif + +// MOC can't handle C++ attributes before 5.15. +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) +# define Q_NODISCARD [[nodiscard]] +# define Q_MAYBE_UNUSED [[maybe_unused]] +# define Q_CONSTEXPR2 constexpr +#else +# define Q_NODISCARD +# define Q_MAYBE_UNUSED +# define Q_CONSTEXPR2 +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + using QT_NATIVE_EVENT_RESULT_TYPE = qintptr; + using QT_ENTER_EVENT_TYPE = QEnterEvent; +#else + using QT_NATIVE_EVENT_RESULT_TYPE = long; + using QT_ENTER_EVENT_TYPE = QEvent; +#endif + +#ifndef Q_DECLARE_METATYPE2 +# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +# define Q_DECLARE_METATYPE2 Q_DECLARE_METATYPE +# else +# define Q_DECLARE_METATYPE2(Type) +# endif +#endif + +#ifndef QUtf8String +# define QUtf8String(str) QString::fromUtf8(str) +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) + using namespace Qt::Literals::StringLiterals; +#endif + +#ifndef FRAMELESSHELPER_BYTEARRAY_LITERAL +# if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) +# define FRAMELESSHELPER_BYTEARRAY_LITERAL(ba) ba##_ba +# elif (QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)) +# define FRAMELESSHELPER_BYTEARRAY_LITERAL(ba) ba##_qba +# else +# define FRAMELESSHELPER_BYTEARRAY_LITERAL(ba) QByteArrayLiteral(ba) +# endif +#endif + +#ifndef FRAMELESSHELPER_STRING_LITERAL +# if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) +# define FRAMELESSHELPER_STRING_LITERAL(str) u##str##_s +# elif (QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)) +# define FRAMELESSHELPER_STRING_LITERAL(str) u##str##_qs +# else +# define FRAMELESSHELPER_STRING_LITERAL(str) QStringLiteral(str) +# endif +#endif + +#ifndef Q_UNREACHABLE_RETURN // Since 6.5 +# define Q_UNREACHABLE_RETURN(...) \ + do { \ + Q_UNREACHABLE(); \ + return __VA_ARGS__; \ + } while (false) +#endif + +#ifndef FRAMELESSHELPER_BYTEARRAY_CONSTANT2 +# define FRAMELESSHELPER_BYTEARRAY_CONSTANT2(name, ba) \ + [[maybe_unused]] static const auto k##name = FRAMELESSHELPER_BYTEARRAY_LITERAL(ba); +#endif + +#ifndef FRAMELESSHELPER_STRING_CONSTANT2 +# define FRAMELESSHELPER_STRING_CONSTANT2(name, str) \ + [[maybe_unused]] static const auto k##name = FRAMELESSHELPER_STRING_LITERAL(str); +#endif + +#ifndef FRAMELESSHELPER_BYTEARRAY_CONSTANT +# define FRAMELESSHELPER_BYTEARRAY_CONSTANT(ba) FRAMELESSHELPER_BYTEARRAY_CONSTANT2(ba, #ba) +#endif + +#ifndef FRAMELESSHELPER_STRING_CONSTANT +# define FRAMELESSHELPER_STRING_CONSTANT(str) FRAMELESSHELPER_STRING_CONSTANT2(str, #str) +#endif + +#ifndef FRAMELESSHELPER_NAMESPACE +# define FRAMELESSHELPER_NAMESPACE wangwenx190::FramelessHelper +#endif + +#ifndef FRAMELESSHELPER_BEGIN_NAMESPACE +# define FRAMELESSHELPER_BEGIN_NAMESPACE namespace FRAMELESSHELPER_NAMESPACE { +#endif + +#ifndef FRAMELESSHELPER_END_NAMESPACE +# define FRAMELESSHELPER_END_NAMESPACE } +#endif + +#ifndef FRAMELESSHELPER_USE_NAMESPACE +# define FRAMELESSHELPER_USE_NAMESPACE using namespace FRAMELESSHELPER_NAMESPACE; +#endif + +#ifndef FRAMELESSHELPER_PREPEND_NAMESPACE +# define FRAMELESSHELPER_PREPEND_NAMESPACE(X) ::FRAMELESSHELPER_NAMESPACE::X +#endif + +#ifndef FRAMELESSHELPER_MAKE_VERSION +# define FRAMELESSHELPER_MAKE_VERSION(Major, Minor, Patch, Tweak) \ + ((((Major) & 0xff) << 24) | (((Minor) & 0xff) << 16) | (((Patch) & 0xff) << 8) | ((Tweak) & 0xff)) +#endif + +#ifndef FRAMELESSHELPER_EXTRACT_VERSION +# define FRAMELESSHELPER_EXTRACT_VERSION(Version, Major, Minor, Patch, Tweak) \ + { \ + (Major) = (((Version) & 0xff) >> 24); \ + (Minor) = (((Version) & 0xff) >> 16); \ + (Patch) = (((Version) & 0xff) >> 8); \ + (Tweak) = ((Version) & 0xff); \ + } +#endif + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcCoreGlobal) + +#include + +[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION = + FRAMELESSHELPER_MAKE_VERSION(FRAMELESSHELPER_VERSION_MAJOR, FRAMELESSHELPER_VERSION_MINOR, + FRAMELESSHELPER_VERSION_PATCH, FRAMELESSHELPER_VERSION_TWEAK); + +namespace Global +{ + +Q_NAMESPACE_EXPORT(FRAMELESSHELPER_CORE_API) + +[[maybe_unused]] inline constexpr const int kDefaultResizeBorderThickness = 8; +[[maybe_unused]] inline constexpr const int kDefaultCaptionHeight = 23; +[[maybe_unused]] inline constexpr const int kDefaultTitleBarHeight = 32; +[[maybe_unused]] inline constexpr const int kDefaultExtendedTitleBarHeight = 48; +[[maybe_unused]] inline constexpr const int kDefaultWindowFrameBorderThickness = 1; +[[maybe_unused]] inline constexpr const int kDefaultTitleBarFontPointSize = 11; +[[maybe_unused]] inline constexpr const int kDefaultTitleBarContentsMargin = 10; +[[maybe_unused]] inline constexpr const int kMacOSChromeButtonAreaWidth = 60; +[[maybe_unused]] inline constexpr const QSize kDefaultWindowIconSize = {16, 16}; +// We have to use "qRound()" here because "std::round()" is not constexpr, yet. +[[maybe_unused]] inline constexpr const QSize kDefaultSystemButtonSize = {qRound(qreal(kDefaultTitleBarHeight) * 1.5), kDefaultTitleBarHeight}; +[[maybe_unused]] inline constexpr const QSize kDefaultSystemButtonIconSize = kDefaultWindowIconSize; +[[maybe_unused]] inline constexpr const QSize kDefaultWindowSize = {160, 160}; // Value taken from QPA. + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) +# define kDefaultBlackColor QColorConstants::Black +# define kDefaultWhiteColor QColorConstants::White +# define kDefaultTransparentColor QColorConstants::Transparent +# define kDefaultDarkGrayColor QColorConstants::DarkGray +#else // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + [[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultBlackColor = {0, 0, 0}; // #000000 + [[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultWhiteColor = {255, 255, 255}; // #FFFFFF + [[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultTransparentColor = {0, 0, 0, 0}; + [[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultDarkGrayColor = {169, 169, 169}; // #A9A9A9 +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + +[[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultSystemLightColor = {240, 240, 240}; // #F0F0F0 +[[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultSystemDarkColor = {32, 32, 32}; // #202020 +[[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultFrameBorderActiveColor = {77, 77, 77}; // #4D4D4D +[[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultFrameBorderInactiveColorDark = {87, 89, 89}; // #575959 +[[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultFrameBorderInactiveColorLight = {166, 166, 166}; // #A6A6A6 +[[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultSystemButtonBackgroundColor = {204, 204, 204}; // #CCCCCC +[[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultSystemCloseButtonBackgroundColor = {232, 17, 35}; // #E81123 + +[[maybe_unused]] inline const QByteArray kDontOverrideCursorVar + = FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_DONT_OVERRIDE_CURSOR"); +[[maybe_unused]] inline const QByteArray kDontToggleMaximizeVar + = FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_DONT_TOGGLE_MAXIMIZE"); + +enum class Option +{ + UseCrossPlatformQtImplementation = 0, + ForceHideWindowFrameBorder = 1, + ForceShowWindowFrameBorder = 2, + DisableWindowsSnapLayout = 3, + WindowUseRoundCorners = 4, + CenterWindowBeforeShow = 5, + EnableBlurBehindWindow = 6, + ForceNonNativeBackgroundBlur = 7, + DisableLazyInitializationForMicaMaterial = 8 +}; +Q_ENUM_NS(Option) + +enum class SystemTheme +{ + Unknown = -1, + Light = 0, + Dark = 1, + HighContrast = 2 +}; +Q_ENUM_NS(SystemTheme) + +enum class SystemButtonType +{ + Unknown = -1, + WindowIcon = 0, + Help = 1, + Minimize = 2, + Maximize = 3, + Restore = 4, + Close = 5 +}; +Q_ENUM_NS(SystemButtonType) + +#ifdef Q_OS_WINDOWS +enum class DwmColorizationArea +{ + None = 0, + StartMenu_TaskBar_ActionCenter = 1, + TitleBar_WindowBorder = 2, + All = 3 +}; +Q_ENUM_NS(DwmColorizationArea) +#endif // Q_OS_WINDOWS + +enum class ButtonState +{ + Unspecified = -1, + Hovered = 0, + Pressed = 1, + Clicked = 2 +}; +Q_ENUM_NS(ButtonState) + +#ifdef Q_OS_WINDOWS +enum class WindowsVersion +{ + _2000 = 0, + _XP = 1, + _XP_64 = 2, + _WS_03 = _XP_64, // Windows Server 2003 + _Vista = 3, + _Vista_SP1 = 4, + _Vista_SP2 = 5, + _7 = 6, + _7_SP1 = 7, + _8 = 8, + _8_1 = 9, + _8_1_Update1 = 10, + _10_1507 = 11, + _10_1511 = 12, + _10_1607 = 13, + _10_1703 = 14, + _10_1709 = 15, + _10_1803 = 16, + _10_1809 = 17, + _10_1903 = 18, + _10_1909 = 19, + _10_2004 = 20, + _10_20H2 = 21, + _10_21H1 = 22, + _10_21H2 = 23, + _10_22H2 = 24, + _10 = _10_1507, + _11_21H2 = 25, + _11_22H2 = 26, + _11 = _11_21H2, + Latest = _11_22H2 +}; +Q_ENUM_NS(WindowsVersion) +#endif // Q_OS_WINDOWS + +enum class BlurMode +{ + Disable = 0, // Do not enable blur behind window + Default = 1, // Use platform default blur mode + Windows_Aero = 2, // Windows only, use the traditional DWM blur + Windows_Acrylic = 3, // Windows only, use the Acrylic blur + Windows_Mica = 4, // Windows only, use the Mica material + Windows_MicaAlt = 5 // Windows only, use the Mica Alt material +}; +Q_ENUM_NS(BlurMode) + +enum class WallpaperAspectStyle +{ + Fill = 0, // Keep aspect ratio to fill, expand/crop if necessary. + Fit = 1, // Keep aspect ratio to fill, but don't expand/crop. + Stretch = 2, // Ignore aspect ratio to fill. + Tile = 3, + Center = 4, + Span = 5 // ??? +}; +Q_ENUM_NS(WallpaperAspectStyle) + +#ifdef Q_OS_WINDOWS +enum class RegistryRootKey +{ + ClassesRoot = 0, + CurrentUser = 1, + LocalMachine = 2, + Users = 3, + PerformanceData = 4, + CurrentConfig = 5, + DynData = 6, + CurrentUserLocalSettings = 7, + PerformanceText = 8, + PerformanceNlsText = 9 +}; +Q_ENUM_NS(RegistryRootKey) +#endif // Q_OS_WINDOWS + +enum class WindowEdge : quint32 +{ + Left = 0x00000001, + Top = 0x00000002, + Right = 0x00000004, + Bottom = 0x00000008 +}; +Q_ENUM_NS(WindowEdge) +Q_DECLARE_FLAGS(WindowEdges, WindowEdge) +Q_FLAG_NS(WindowEdges) +Q_DECLARE_OPERATORS_FOR_FLAGS(WindowEdges) + +#ifdef Q_OS_WINDOWS +enum class DpiAwareness +{ + Unknown = -1, + Unaware = 0, + System = 1, + PerMonitor = 2, + PerMonitorVersion2 = 3, + Unaware_GdiScaled = 4 +}; +Q_ENUM_NS(DpiAwareness) +#endif // Q_OS_WINDOWS + +enum class WindowCornerStyle +{ + Default = 0, + Square = 1, + Round = 2 +}; +Q_ENUM_NS(WindowCornerStyle) + +struct VersionNumber +{ + int major = 0; + int minor = 0; + int patch = 0; + int tweak = 0; + + [[nodiscard]] friend constexpr bool operator==(const VersionNumber &lhs, const VersionNumber &rhs) noexcept + { + return ((lhs.major == rhs.major) && (lhs.minor == rhs.minor) && (lhs.patch == rhs.patch) && (lhs.tweak == rhs.tweak)); + } + + [[nodiscard]] friend constexpr bool operator!=(const VersionNumber &lhs, const VersionNumber &rhs) noexcept + { + return !operator==(lhs, rhs); + } + + [[nodiscard]] friend constexpr bool operator>(const VersionNumber &lhs, const VersionNumber &rhs) noexcept + { + if (operator==(lhs, rhs)) { + return false; + } + if (lhs.major > rhs.major) { + return true; + } + if (lhs.major < rhs.major) { + return false; + } + if (lhs.minor > rhs.minor) { + return true; + } + if (lhs.minor < rhs.minor) { + return false; + } + if (lhs.patch > rhs.patch) { + return true; + } + if (lhs.patch < rhs.patch) { + return false; + } + return (lhs.tweak > rhs.tweak); + } + + [[nodiscard]] friend constexpr bool operator<(const VersionNumber &lhs, const VersionNumber &rhs) noexcept + { + return (operator!=(lhs, rhs) && !operator>(lhs, rhs)); + } + + [[nodiscard]] friend constexpr bool operator>=(const VersionNumber &lhs, const VersionNumber &rhs) noexcept + { + return (operator>(lhs, rhs) || operator==(lhs, rhs)); + } + + [[nodiscard]] friend constexpr bool operator<=(const VersionNumber &lhs, const VersionNumber &rhs) noexcept + { + return (operator<(lhs, rhs) || operator==(lhs, rhs)); + } +}; + +using InitializeHookCallback = std::function; +using UninitializeHookCallback = std::function; + +using GetWindowFlagsCallback = std::function; +using SetWindowFlagsCallback = std::function; +using GetWindowSizeCallback = std::function; +using SetWindowSizeCallback = std::function; +using GetWindowPositionCallback = std::function; +using SetWindowPositionCallback = std::function; +using GetWindowScreenCallback = std::function; +using IsWindowFixedSizeCallback = std::function; +using SetWindowFixedSizeCallback = std::function; +using GetWindowStateCallback = std::function; +using SetWindowStateCallback = std::function; +using GetWindowHandleCallback = std::function; +using WindowToScreenCallback = std::function; +using ScreenToWindowCallback = std::function; +using IsInsideSystemButtonsCallback = std::function; +using IsInsideTitleBarDraggableAreaCallback = std::function; +using GetWindowDevicePixelRatioCallback = std::function; +using SetSystemButtonStateCallback = std::function; +using GetWindowIdCallback = std::function; +using ShouldIgnoreMouseEventsCallback = std::function; +using ShowSystemMenuCallback = std::function; +using SetPropertyCallback = std::function; +using GetPropertyCallback = std::function; +using SetCursorCallback = std::function; +using UnsetCursorCallback = std::function; +using GetWidgetHandleCallback = std::function; + +struct SystemParameters +{ + GetWindowFlagsCallback getWindowFlags = nullptr; + SetWindowFlagsCallback setWindowFlags = nullptr; + GetWindowSizeCallback getWindowSize = nullptr; + SetWindowSizeCallback setWindowSize = nullptr; + GetWindowPositionCallback getWindowPosition = nullptr; + SetWindowPositionCallback setWindowPosition = nullptr; + GetWindowScreenCallback getWindowScreen = nullptr; + IsWindowFixedSizeCallback isWindowFixedSize = nullptr; + SetWindowFixedSizeCallback setWindowFixedSize = nullptr; + GetWindowStateCallback getWindowState = nullptr; + SetWindowStateCallback setWindowState = nullptr; + GetWindowHandleCallback getWindowHandle = nullptr; + WindowToScreenCallback windowToScreen = nullptr; + ScreenToWindowCallback screenToWindow = nullptr; + IsInsideSystemButtonsCallback isInsideSystemButtons = nullptr; + IsInsideTitleBarDraggableAreaCallback isInsideTitleBarDraggableArea = nullptr; + GetWindowDevicePixelRatioCallback getWindowDevicePixelRatio = nullptr; + SetSystemButtonStateCallback setSystemButtonState = nullptr; + GetWindowIdCallback getWindowId = nullptr; + ShouldIgnoreMouseEventsCallback shouldIgnoreMouseEvents = nullptr; + ShowSystemMenuCallback showSystemMenu = nullptr; + SetPropertyCallback setProperty = nullptr; + GetPropertyCallback getProperty = nullptr; + SetCursorCallback setCursor = nullptr; + UnsetCursorCallback unsetCursor = nullptr; + GetWidgetHandleCallback getWidgetHandle = nullptr; + + [[nodiscard]] inline bool isValid() const + { + Q_ASSERT(getWindowFlags); + Q_ASSERT(setWindowFlags); + Q_ASSERT(getWindowSize); + Q_ASSERT(setWindowSize); + Q_ASSERT(getWindowPosition); + Q_ASSERT(setWindowPosition); + Q_ASSERT(getWindowScreen); + Q_ASSERT(isWindowFixedSize); + Q_ASSERT(setWindowFixedSize); + Q_ASSERT(getWindowState); + Q_ASSERT(setWindowState); + Q_ASSERT(getWindowHandle); + Q_ASSERT(windowToScreen); + Q_ASSERT(screenToWindow); + Q_ASSERT(isInsideSystemButtons); + Q_ASSERT(isInsideTitleBarDraggableArea); + Q_ASSERT(getWindowDevicePixelRatio); + Q_ASSERT(setSystemButtonState); + Q_ASSERT(getWindowId); + Q_ASSERT(shouldIgnoreMouseEvents); + Q_ASSERT(showSystemMenu); + Q_ASSERT(setProperty); + Q_ASSERT(getProperty); + Q_ASSERT(setCursor); + Q_ASSERT(unsetCursor); + Q_ASSERT(getWidgetHandle); + return (getWindowFlags && setWindowFlags && getWindowSize + && setWindowSize && getWindowPosition && setWindowPosition + && getWindowScreen && isWindowFixedSize && setWindowFixedSize + && getWindowState && setWindowState && getWindowHandle + && windowToScreen && screenToWindow && isInsideSystemButtons + && isInsideTitleBarDraggableArea && getWindowDevicePixelRatio + && setSystemButtonState && getWindowId && shouldIgnoreMouseEvents + && showSystemMenu && setProperty && getProperty && setCursor + && unsetCursor && getWidgetHandle); + } +}; + +struct VersionInfo +{ + int version = 0; + const char *version_str = nullptr; + const char *commit = nullptr; + const char *compileDateTime = nullptr; + const char *compiler = nullptr; + bool isDebug = false; + bool isStatic = false; +}; + +struct Dpi +{ + quint32 x = 0; + quint32 y = 0; +}; + +#ifdef Q_OS_WINDOWS +[[maybe_unused]] inline constexpr const VersionNumber WindowsVersions[] = +{ + { 5, 0, 2195}, // Windows 2000 + { 5, 1, 2600}, // Windows XP + { 5, 2, 3790}, // Windows XP x64 Edition or Windows Server 2003 + { 6, 0, 6000}, // Windows Vista + { 6, 0, 6001}, // Windows Vista with Service Pack 1 or Windows Server 2008 + { 6, 0, 6002}, // Windows Vista with Service Pack 2 + { 6, 1, 7600}, // Windows 7 or Windows Server 2008 R2 + { 6, 1, 7601}, // Windows 7 with Service Pack 1 or Windows Server 2008 R2 with Service Pack 1 + { 6, 2, 9200}, // Windows 8 or Windows Server 2012 + { 6, 3, 9200}, // Windows 8.1 or Windows Server 2012 R2 + { 6, 3, 9600}, // Windows 8.1 with Update 1 + {10, 0, 10240}, // Windows 10 Version 1507 (TH1) + {10, 0, 10586}, // Windows 10 Version 1511 (November Update) (TH2) + {10, 0, 14393}, // Windows 10 Version 1607 (Anniversary Update) (RS1) or Windows Server 2016 + {10, 0, 15063}, // Windows 10 Version 1703 (Creators Update) (RS2) + {10, 0, 16299}, // Windows 10 Version 1709 (Fall Creators Update) (RS3) + {10, 0, 17134}, // Windows 10 Version 1803 (April 2018 Update) (RS4) + {10, 0, 17763}, // Windows 10 Version 1809 (October 2018 Update) (RS5) or Windows Server 2019 + {10, 0, 18362}, // Windows 10 Version 1903 (May 2019 Update) (19H1) + {10, 0, 18363}, // Windows 10 Version 1909 (November 2019 Update) (19H2) + {10, 0, 19041}, // Windows 10 Version 2004 (May 2020 Update) (20H1) + {10, 0, 19042}, // Windows 10 Version 20H2 (October 2020 Update) (20H2) + {10, 0, 19043}, // Windows 10 Version 21H1 (May 2021 Update) (21H1) + {10, 0, 19044}, // Windows 10 Version 21H2 (November 2021 Update) (21H2) + {10, 0, 19045}, // Windows 10 Version 22H2 (October 2022 Update) (22H2) + {10, 0, 22000}, // Windows 11 Version 21H2 (21H2) + {10, 0, 22621} // Windows 11 Version 22H2 (October 2022 Update) (22H2) +}; +#endif // Q_OS_WINDOWS + +} // namespace Global + +namespace FramelessHelper::Core +{ +FRAMELESSHELPER_CORE_API void initialize(); +FRAMELESSHELPER_CORE_API void uninitialize(); +[[nodiscard]] FRAMELESSHELPER_CORE_API Global::VersionInfo version(); +FRAMELESSHELPER_CORE_API void registerInitializeHook(const Global::InitializeHookCallback &cb); +FRAMELESSHELPER_CORE_API void registerUninitializeHook(const Global::UninitializeHookCallback &cb); +FRAMELESSHELPER_CORE_API void setApplicationOSThemeAware(); +FRAMELESSHELPER_CORE_API void outputLogo(); +} // namespace FramelessHelper::Core + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber) +Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::SystemParameters) +Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionInfo) +Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Dpi); + +#ifndef QT_NO_DEBUG_STREAM +QT_BEGIN_NAMESPACE +FRAMELESSHELPER_CORE_API QDebug operator<<(QDebug, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber &); +FRAMELESSHELPER_CORE_API QDebug operator<<(QDebug, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionInfo &); +FRAMELESSHELPER_CORE_API QDebug operator<<(QDebug, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Dpi &); +QT_END_NAMESPACE +#endif // QT_NO_DEBUG_STREAM diff --git a/FramelessHelper/include/FramelessHelper/Core/framelessmanager.h b/FramelessHelper/include/FramelessHelper/Core/framelessmanager.h new file mode 100644 index 0000000..098cd74 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/framelessmanager.h @@ -0,0 +1,70 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcFramelessManager) + +class FramelessManagerPrivate; + +class FRAMELESSHELPER_CORE_API FramelessManager : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(FramelessManager) + Q_DISABLE_COPY_MOVE(FramelessManager) + Q_PROPERTY(Global::SystemTheme systemTheme READ systemTheme NOTIFY systemThemeChanged FINAL) + Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemThemeChanged FINAL) + Q_PROPERTY(QString wallpaper READ wallpaper NOTIFY wallpaperChanged FINAL) + Q_PROPERTY(Global::WallpaperAspectStyle wallpaperAspectStyle READ wallpaperAspectStyle NOTIFY wallpaperChanged FINAL) + +public: + explicit FramelessManager(QObject *parent = nullptr); + ~FramelessManager() override; + + Q_NODISCARD static FramelessManager *instance(); + + Q_NODISCARD Global::SystemTheme systemTheme() const; + Q_NODISCARD QColor systemAccentColor() const; + Q_NODISCARD QString wallpaper() const; + Q_NODISCARD Global::WallpaperAspectStyle wallpaperAspectStyle() const; + +public Q_SLOTS: + void addWindow(const Global::SystemParameters ¶ms); + void removeWindow(const WId windowId); + +Q_SIGNALS: + void systemThemeChanged(); + void wallpaperChanged(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessManager)) diff --git a/FramelessHelper/include/FramelessHelper/Core/micamaterial.h b/FramelessHelper/include/FramelessHelper/Core/micamaterial.h new file mode 100644 index 0000000..030763c --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/micamaterial.h @@ -0,0 +1,73 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcMicaMaterial) + +class MicaMaterialPrivate; + +class FRAMELESSHELPER_CORE_API MicaMaterial : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(MicaMaterial) + Q_DECLARE_PRIVATE(MicaMaterial) + + Q_PROPERTY(QColor tintColor READ tintColor WRITE setTintColor NOTIFY tintColorChanged FINAL) + Q_PROPERTY(qreal tintOpacity READ tintOpacity WRITE setTintOpacity NOTIFY tintOpacityChanged FINAL) + Q_PROPERTY(qreal noiseOpacity READ noiseOpacity WRITE setNoiseOpacity NOTIFY noiseOpacityChanged FINAL) + +public: + explicit MicaMaterial(QObject *parent = nullptr); + ~MicaMaterial() override; + + Q_NODISCARD QColor tintColor() const; + void setTintColor(const QColor &value); + + Q_NODISCARD qreal tintOpacity() const; + void setTintOpacity(const qreal value); + + Q_NODISCARD qreal noiseOpacity() const; + void setNoiseOpacity(const qreal value); + +public Q_SLOTS: + void paint(QPainter *painter, const QSize &size, const QPoint &pos); + +Q_SIGNALS: + void tintColorChanged(); + void tintOpacityChanged(); + void noiseOpacityChanged(); + void shouldRedraw(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(MicaMaterial)) diff --git a/FramelessHelper/include/FramelessHelper/Core/private/chromepalette_p.h b/FramelessHelper/include/FramelessHelper/Core/private/chromepalette_p.h new file mode 100644 index 0000000..585014f --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/private/chromepalette_p.h @@ -0,0 +1,77 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class ChromePalette; + +class FRAMELESSHELPER_CORE_API ChromePalettePrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(ChromePalette) + Q_DISABLE_COPY_MOVE(ChromePalettePrivate) + +public: + explicit ChromePalettePrivate(ChromePalette *q); + ~ChromePalettePrivate() override; + + Q_NODISCARD static ChromePalettePrivate *get(ChromePalette *q); + Q_NODISCARD static const ChromePalettePrivate *get(const ChromePalette *q); + +public Q_SLOTS: + void refresh(); + +private: + ChromePalette *q_ptr = nullptr; + // System-defined ones: + QColor titleBarActiveBackgroundColor_sys = {}; + QColor titleBarInactiveBackgroundColor_sys = {}; + QColor titleBarActiveForegroundColor_sys = {}; + QColor titleBarInactiveForegroundColor_sys = {}; + QColor chromeButtonNormalColor_sys = {}; + QColor chromeButtonHoverColor_sys = {}; + QColor chromeButtonPressColor_sys = {}; + QColor closeButtonNormalColor_sys = {}; + QColor closeButtonHoverColor_sys = {}; + QColor closeButtonPressColor_sys = {}; + // User-defined ones: + std::optional titleBarActiveBackgroundColor = std::nullopt; + std::optional titleBarInactiveBackgroundColor = std::nullopt; + std::optional titleBarActiveForegroundColor = std::nullopt; + std::optional titleBarInactiveForegroundColor = std::nullopt; + std::optional chromeButtonNormalColor = std::nullopt; + std::optional chromeButtonHoverColor = std::nullopt; + std::optional chromeButtonPressColor = std::nullopt; + std::optional closeButtonNormalColor = std::nullopt; + std::optional closeButtonHoverColor = std::nullopt; + std::optional closeButtonPressColor = std::nullopt; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(ChromePalettePrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Core/private/framelessconfig_p.h b/FramelessHelper/include/FramelessHelper/Core/private/framelessconfig_p.h new file mode 100644 index 0000000..a32a02e --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/private/framelessconfig_p.h @@ -0,0 +1,53 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FRAMELESSHELPER_CORE_API FramelessConfig : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(FramelessConfig) + +public: + explicit FramelessConfig(QObject *parent = nullptr); + ~FramelessConfig() override; + + Q_NODISCARD static FramelessConfig *instance(); + + void reload(const bool force = false); + + void set(const Global::Option option, const bool on = true); + Q_NODISCARD bool isSet(const Global::Option option) const; + + static void setLoadFromEnvironmentVariablesDisabled(const bool on = true); + static void setLoadFromConfigurationFileDisabled(const bool on = true); +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessConfig)) diff --git a/FramelessHelper/include/FramelessHelper/Core/private/framelessmanager_p.h b/FramelessHelper/include/FramelessHelper/Core/private/framelessmanager_p.h new file mode 100644 index 0000000..73ed72e --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/private/framelessmanager_p.h @@ -0,0 +1,78 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FramelessManager; + +class FRAMELESSHELPER_CORE_API FramelessManagerPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(FramelessManager) + Q_DISABLE_COPY_MOVE(FramelessManagerPrivate) + +public: + explicit FramelessManagerPrivate(FramelessManager *q); + ~FramelessManagerPrivate() override; + + Q_NODISCARD static FramelessManagerPrivate *get(FramelessManager *pub); + Q_NODISCARD static const FramelessManagerPrivate *get(const FramelessManager *pub); + + static void initializeIconFont(); + Q_NODISCARD static QFont getIconFont(); + + Q_NODISCARD Global::SystemTheme systemTheme() const; + Q_NODISCARD QColor systemAccentColor() const; + Q_NODISCARD QString wallpaper() const; + Q_NODISCARD Global::WallpaperAspectStyle wallpaperAspectStyle() const; + + static void addWindow(const Global::SystemParameters ¶ms); + static void removeWindow(const WId windowId); + + Q_INVOKABLE void notifySystemThemeHasChangedOrNot(); + Q_INVOKABLE void notifyWallpaperHasChangedOrNot(); + + Q_NODISCARD static bool usePureQtImplementation(); + +private: + void initialize(); + +private: + FramelessManager *q_ptr = nullptr; + Global::SystemTheme m_systemTheme = Global::SystemTheme::Unknown; + QColor m_accentColor = {}; +#ifdef Q_OS_WINDOWS + Global::DwmColorizationArea m_colorizationArea = Global::DwmColorizationArea::None; +#endif + QString m_wallpaper = {}; + Global::WallpaperAspectStyle m_wallpaperAspectStyle = Global::WallpaperAspectStyle::Fill; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessManagerPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Core/private/micamaterial_p.h b/FramelessHelper/include/FramelessHelper/Core/private/micamaterial_p.h new file mode 100644 index 0000000..2d7da94 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/private/micamaterial_p.h @@ -0,0 +1,67 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class MicaMaterial; + +class FRAMELESSHELPER_CORE_API MicaMaterialPrivate : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(MicaMaterialPrivate) + Q_DECLARE_PUBLIC(MicaMaterial) + +public: + explicit MicaMaterialPrivate(MicaMaterial *q); + ~MicaMaterialPrivate() override; + + Q_NODISCARD static MicaMaterialPrivate *get(MicaMaterial *q); + Q_NODISCARD static const MicaMaterialPrivate *get(const MicaMaterial *q); + +public Q_SLOTS: + void maybeGenerateBlurredWallpaper(const bool force = false); + void updateMaterialBrush(); + void paint(QPainter *painter, const QSize &size, const QPoint &pos); + +private: + void initialize(); + void prepareGraphicsResources(); + +private: + MicaMaterial *q_ptr = nullptr; + QColor tintColor = {}; + qreal tintOpacity = 0.0; + qreal noiseOpacity = 0.0; + QBrush micaBrush = {}; + bool initialized = false; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(MicaMaterialPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Core/private/registrykey_p.h b/FramelessHelper/include/FramelessHelper/Core/private/registrykey_p.h new file mode 100644 index 0000000..72ade24 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/private/registrykey_p.h @@ -0,0 +1,94 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" +#include + +#ifndef REGISTRYKEY_FORCE_QSETTINGS +# define REGISTRYKEY_FORCE_QSETTINGS (0) +#endif // REGISTRYKEY_FORCE_QSETTINGS + +#ifndef REGISTRYKEY_IMPL +# if ((QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) && !(REGISTRYKEY_FORCE_QSETTINGS) && !defined(FRAMELESSHELPER_CORE_NO_PRIVATE)) +# define REGISTRYKEY_IMPL (1) +# else // ((QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) || REGISTRYKEY_FORCE_QSETTINGS || defined(FRAMELESSHELPER_CORE_NO_PRIVATE)) +# define REGISTRYKEY_IMPL (2) +# endif // ((QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) && !REGISTRYKEY_FORCE_QSETTINGS && !defined(FRAMELESSHELPER_CORE_NO_PRIVATE)) +#endif // REGISTRYKEY_IMPL + +#ifndef REGISTRYKEY_QWINREGISTRYKEY +# define REGISTRYKEY_QWINREGISTRYKEY ((REGISTRYKEY_IMPL) == 1) +#endif // REGISTRYKEY_QWINREGISTRYKEY + +#ifndef REGISTRYKEY_QSETTINGS +# define REGISTRYKEY_QSETTINGS ((REGISTRYKEY_IMPL) == 2) +#endif // REGISTRYKEY_QSETTINGS + +QT_BEGIN_NAMESPACE +class QWinRegistryKey; +class QSettings; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FRAMELESSHELPER_CORE_API RegistryKey : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(RegistryKey) + +public: + explicit RegistryKey(const Global::RegistryRootKey root, const QString &key, QObject *parent = nullptr); + ~RegistryKey() override; + + Q_NODISCARD Global::RegistryRootKey rootKey() const; + Q_NODISCARD QString subKey() const; + + Q_NODISCARD bool isValid() const; + + Q_NODISCARD QVariant value(const QString &name) const; + template + Q_NODISCARD std::optional value(const QString &name) const + { + const QVariant var = value(name); + if (var.isValid()) { + return qvariant_cast(var); + } + return std::nullopt; + } + +private: + Global::RegistryRootKey m_rootKey = Global::RegistryRootKey::CurrentUser; + QString m_subKey = {}; +#if REGISTRYKEY_QWINREGISTRYKEY + QScopedPointer m_registryKey; +#else + QScopedPointer m_settings; +#endif +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(RegistryKey)) diff --git a/FramelessHelper/include/FramelessHelper/Core/private/sysapiloader_p.h b/FramelessHelper/include/FramelessHelper/Core/private/sysapiloader_p.h new file mode 100644 index 0000000..90112a7 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/private/sysapiloader_p.h @@ -0,0 +1,90 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FRAMELESSHELPER_CORE_API SysApiLoader : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(SysApiLoader) + +public: + explicit SysApiLoader(QObject *parent = nullptr); + ~SysApiLoader() override; + + Q_NODISCARD static SysApiLoader *instance(); + + Q_NODISCARD static QFunctionPointer resolve(const QString &library, const char *function); + Q_NODISCARD static QFunctionPointer resolve(const QString &library, const QByteArray &function); + Q_NODISCARD static QFunctionPointer resolve(const QString &library, const QString &function); + + Q_NODISCARD bool isAvailable(const QString &library, const QString &function); + + Q_NODISCARD QFunctionPointer get(const QString &function); + + template + Q_NODISCARD T get(const QString &function) + { + return reinterpret_cast(get(function)); + } +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(SysApiLoader)) + +#define API_AVAILABLE(lib, func) \ + (FRAMELESSHELPER_PREPEND_NAMESPACE(SysApiLoader)::instance()->isAvailable(k##lib, k##func)) + +#define API_CALL_FUNCTION(func, ...) \ + ((FRAMELESSHELPER_PREPEND_NAMESPACE(SysApiLoader)::instance()->get(k##func))(__VA_ARGS__)) + +#define API_CALL_FUNCTION2(func, type, ...) \ + ((FRAMELESSHELPER_PREPEND_NAMESPACE(SysApiLoader)::instance()->get(k##func))(__VA_ARGS__)) + +#define API_CALL_FUNCTION3(func, name, ...) \ + ((FRAMELESSHELPER_PREPEND_NAMESPACE(SysApiLoader)::instance()->get(k##name))(__VA_ARGS__)) + +#define API_CALL_FUNCTION4(func, ...) API_CALL_FUNCTION3(_##func, func, __VA_ARGS__) + +#define API_CALL_FUNCTION5(func, ...) API_CALL_FUNCTION3(func##2, func, __VA_ARGS__) + +#ifdef Q_OS_WINDOWS +# define API_USER_AVAILABLE(func) API_AVAILABLE(user32, func) +# define API_THEME_AVAILABLE(func) API_AVAILABLE(uxtheme, func) +# define API_DWM_AVAILABLE(func) API_AVAILABLE(dwmapi, func) +# define API_SHCORE_AVAILABLE(func) API_AVAILABLE(shcore, func) +# define API_WINMM_AVAILABLE(func) API_AVAILABLE(winmm, func) +# define API_D2D_AVAILABLE(func) API_AVAILABLE(d2d1, func) +# define API_NT_AVAILABLE(func) API_AVAILABLE(ntdll, func) +#endif // Q_OS_WINDOWS + +#ifdef Q_OS_LINUX +# define API_XCB_AVAILABLE(func) API_AVAILABLE(libxcb, func) +# define API_GTK_AVAILABLE(func) API_AVAILABLE(libgtk, func) +#endif // Q_OS_LINUX diff --git a/FramelessHelper/include/FramelessHelper/Core/private/windowborderpainter_p.h b/FramelessHelper/include/FramelessHelper/Core/private/windowborderpainter_p.h new file mode 100644 index 0000000..05f8677 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/private/windowborderpainter_p.h @@ -0,0 +1,66 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class WindowBorderPainter; + +class FRAMELESSHELPER_CORE_API WindowBorderPainterPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(WindowBorderPainter) + Q_DISABLE_COPY_MOVE(WindowBorderPainterPrivate) + +public: + explicit WindowBorderPainterPrivate(WindowBorderPainter *q); + ~WindowBorderPainterPrivate() override; + + Q_NODISCARD static WindowBorderPainterPrivate *get(WindowBorderPainter *q); + Q_NODISCARD static const WindowBorderPainterPrivate *get(const WindowBorderPainter *q); + + Q_NODISCARD static int getNativeBorderThickness(); + Q_NODISCARD static QColor getNativeBorderColor(const bool active); + Q_NODISCARD static Global::WindowEdges getNativeBorderEdges(); + +public Q_SLOTS: + void paint(QPainter *painter, const QSize &size, const bool active) const; + +private: + void initialize(); + +private: + WindowBorderPainter *q_ptr = nullptr; + std::optional m_thickness = std::nullopt; + std::optional m_edges = std::nullopt; + std::optional m_activeColor = std::nullopt; + std::optional m_inactiveColor = std::nullopt; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(WindowBorderPainterPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Core/private/winverhelper_p.h b/FramelessHelper/include/FramelessHelper/Core/private/winverhelper_p.h new file mode 100644 index 0000000..915a992 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/private/winverhelper_p.h @@ -0,0 +1,70 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +namespace WindowsVersionHelper +{ + +#define DECL(Name) [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin##Name##OrGreater(); + +DECL(2K) +DECL(XP) +DECL(XP64) +DECL(Vista) +DECL(VistaSP1) +DECL(VistaSP2) +DECL(7) +DECL(7SP1) +DECL(8) +DECL(8Point1) +DECL(8Point1Update1) +DECL(10) +DECL(10TH1) +DECL(10TH2) +DECL(10RS1) +DECL(10RS2) +DECL(10RS3) +DECL(10RS4) +DECL(10RS5) +DECL(1019H1) +DECL(1019H2) +DECL(1020H1) +DECL(1020H2) +DECL(1021H1) +DECL(1021H2) +DECL(1022H2) +DECL(11) +DECL(1121H2) +DECL(1122H2) + +#undef DECL + +} // namespace WindowsVersionHelper + +FRAMELESSHELPER_END_NAMESPACE diff --git a/FramelessHelper/include/FramelessHelper/Core/utils.h b/FramelessHelper/include/FramelessHelper/Core/utils.h new file mode 100644 index 0000000..33bdf2d --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/utils.h @@ -0,0 +1,185 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" +#ifdef Q_OS_LINUX +# include "framelesshelper_linux.h" +#endif // Q_OS_LINUX + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcUtilsCommon) +#ifdef Q_OS_WINDOWS + Q_DECLARE_LOGGING_CATEGORY(lcUtilsWin) +#elif defined(Q_OS_LINUX) + Q_DECLARE_LOGGING_CATEGORY(lcUtilsLinux) +#elif defined(Q_OS_MACOS) + Q_DECLARE_LOGGING_CATEGORY(lcUtilsMac) +#endif + +namespace Utils +{ + +[[nodiscard]] FRAMELESSHELPER_CORE_API + Qt::CursorShape calculateCursorShape(const QWindow *window, const QPoint &pos); +[[nodiscard]] FRAMELESSHELPER_CORE_API + Qt::Edges calculateWindowEdges(const QWindow *window, const QPoint &pos); +FRAMELESSHELPER_CORE_API void startSystemMove(QWindow *window, const QPoint &globalPos); +FRAMELESSHELPER_CORE_API void startSystemResize(QWindow *window, const Qt::Edges edges, const QPoint &globalPos); +[[nodiscard]] FRAMELESSHELPER_CORE_API QString getSystemButtonGlyph(const Global::SystemButtonType button); +[[nodiscard]] FRAMELESSHELPER_CORE_API QWindow *findWindow(const WId windowId); +FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter( + const Global::GetWindowScreenCallback &getWindowScreen, + const Global::GetWindowSizeCallback &getWindowSize, + const Global::SetWindowPositionCallback &setWindowPosition, + const bool considerTaskBar); +[[nodiscard]] FRAMELESSHELPER_CORE_API Global::SystemTheme getSystemTheme(); +[[nodiscard]] FRAMELESSHELPER_CORE_API Qt::WindowState windowStatesToWindowState( + const Qt::WindowStates states); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isThemeChangeEvent(const QEvent * const event); +[[nodiscard]] FRAMELESSHELPER_CORE_API QColor calculateSystemButtonBackgroundColor( + const Global::SystemButtonType button, const Global::ButtonState state); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode(); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isTitleBarColorized(); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool + setBlurBehindWindowEnabled(const WId windowId, const Global::BlurMode mode, const QColor &color); +[[nodiscard]] FRAMELESSHELPER_CORE_API QString getWallpaperFilePath(); +[[nodiscard]] FRAMELESSHELPER_CORE_API Global::WallpaperAspectStyle getWallpaperAspectStyle(); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isBlurBehindWindowSupported(); +FRAMELESSHELPER_CORE_API void registerThemeChangeNotification(); +[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getFrameBorderColor(const bool active); +[[nodiscard]] FRAMELESSHELPER_CORE_API qreal roundScaleFactor(const qreal factor); +[[nodiscard]] FRAMELESSHELPER_CORE_API int toNativePixels(const QWindow *window, const int value); +[[nodiscard]] FRAMELESSHELPER_CORE_API QPoint toNativePixels(const QWindow *window, const QPoint &point); +[[nodiscard]] FRAMELESSHELPER_CORE_API QSize toNativePixels(const QWindow *window, const QSize &size); +[[nodiscard]] FRAMELESSHELPER_CORE_API QRect toNativePixels(const QWindow *window, const QRect &rect); +[[nodiscard]] FRAMELESSHELPER_CORE_API int fromNativePixels(const QWindow *window, const int value); +[[nodiscard]] FRAMELESSHELPER_CORE_API QPoint fromNativePixels(const QWindow *window, const QPoint &point); +[[nodiscard]] FRAMELESSHELPER_CORE_API QSize fromNativePixels(const QWindow *window, const QSize &size); +[[nodiscard]] FRAMELESSHELPER_CORE_API QRect fromNativePixels(const QWindow *window, const QRect &rect); +[[nodiscard]] FRAMELESSHELPER_CORE_API int horizontalAdvance(const QFontMetrics &fm, const QString &str); + +#ifdef Q_OS_WINDOWS +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowsVersionOrGreater(const Global::WindowsVersion version); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isDwmCompositionEnabled(); +FRAMELESSHELPER_CORE_API void triggerFrameChange(const WId windowId); +FRAMELESSHELPER_CORE_API void updateWindowFrameMargins(const WId windowId, const bool reset); +FRAMELESSHELPER_CORE_API void updateInternalWindowFrameMargins(QWindow *window, const bool enable); +[[nodiscard]] FRAMELESSHELPER_CORE_API QString getSystemErrorMessage(const QString &function); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isFullScreen(const WId windowId); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowNoState(const WId windowId); +FRAMELESSHELPER_CORE_API void syncWmPaintWithDwm(); +FRAMELESSHELPER_CORE_API void showSystemMenu( + const WId windowId, + const QPoint &pos, + const bool selectFirstEntry, + const Global::IsWindowFixedSizeCallback &isWindowFixedSize); +[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getDwmColorizationColor(); +[[nodiscard]] FRAMELESSHELPER_CORE_API Global::DwmColorizationArea getDwmColorizationArea(); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isHighContrastModeEnabled(); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getPrimaryScreenDpi(const bool horizontal); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getWindowDpi(const WId windowId, const bool horizontal); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getResizeBorderThicknessForDpi + (const bool horizontal, const quint32 dpi); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getResizeBorderThickness(const WId windowId, + const bool horizontal, + const bool scaled); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getCaptionBarHeightForDpi(const quint32 dpi); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getCaptionBarHeight(const WId windowId, const bool scaled); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getTitleBarHeightForDpi(const quint32 dpi); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getTitleBarHeight(const WId windowId, const bool scaled); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getFrameBorderThicknessForDpi(const quint32 dpi); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getFrameBorderThickness(const WId windowId, + const bool scaled); +FRAMELESSHELPER_CORE_API void maybeFixupQtInternals(const WId windowId); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowFrameBorderVisible(); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isFrameBorderColorized(); +FRAMELESSHELPER_CORE_API void installSystemMenuHook( + const WId windowId, + const Global::IsWindowFixedSizeCallback &isWindowFixedSize, + const Global::IsInsideTitleBarDraggableAreaCallback &isInTitleBarArea, + const Global::GetWindowHandleCallback &getWindowHandle); +FRAMELESSHELPER_CORE_API void uninstallSystemMenuHook(const WId windowId); +FRAMELESSHELPER_CORE_API void setAeroSnappingEnabled(const WId windowId, const bool enable); +FRAMELESSHELPER_CORE_API void tryToEnableHighestDpiAwarenessLevel(); +FRAMELESSHELPER_CORE_API void updateGlobalWin32ControlsTheme(const WId windowId, const bool dark); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode_windows(); +FRAMELESSHELPER_CORE_API void setCornerStyleForWindow(const WId windowId, const Global::WindowCornerStyle style); +[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getDwmAccentColor(); +FRAMELESSHELPER_CORE_API void hideOriginalTitleBarElements + (const WId windowId, const bool disable = true); +FRAMELESSHELPER_CORE_API void setQtDarkModeAwareEnabled(const bool enable); +FRAMELESSHELPER_CORE_API void refreshWin32ThemeResources(const WId windowId, const bool dark); +FRAMELESSHELPER_CORE_API void enableNonClientAreaDpiScalingForWindow(const WId windowId); +[[nodiscard]] FRAMELESSHELPER_CORE_API + Global::DpiAwareness getDpiAwarenessForCurrentProcess(bool *highest = nullptr); +FRAMELESSHELPER_CORE_API void fixupChildWindowsDpiMessage(const WId windowId); +FRAMELESSHELPER_CORE_API void fixupDialogsDpiScaling(); +FRAMELESSHELPER_CORE_API void setDarkModeAllowedForApp(const bool allow = true); +FRAMELESSHELPER_CORE_API void bringWindowToFront(const WId windowId); +#endif // Q_OS_WINDOWS + +#ifdef Q_OS_LINUX +[[nodiscard]] FRAMELESSHELPER_CORE_API QScreen *x11_findScreenForVirtualDesktop + (const int virtualDesktopNumber); +[[nodiscard]] FRAMELESSHELPER_CORE_API x11_return_type x11_appRootWindow(const int screen); +[[nodiscard]] FRAMELESSHELPER_CORE_API int x11_appScreen(); +[[nodiscard]] FRAMELESSHELPER_CORE_API x11_return_type x11_appTime(); +[[nodiscard]] FRAMELESSHELPER_CORE_API x11_return_type x11_appUserTime(); +[[nodiscard]] FRAMELESSHELPER_CORE_API x11_return_type x11_getTimestamp(); +[[nodiscard]] FRAMELESSHELPER_CORE_API QByteArray x11_nextStartupId(); +[[nodiscard]] FRAMELESSHELPER_CORE_API Display *x11_display(); +[[nodiscard]] FRAMELESSHELPER_CORE_API xcb_connection_t *x11_connection(); +[[nodiscard]] FRAMELESSHELPER_CORE_API QByteArray getWindowProperty + (const WId windowId, const xcb_atom_t prop, const xcb_atom_t type, const quint32 data_len); +FRAMELESSHELPER_CORE_API void setWindowProperty + (const WId windowId, const xcb_atom_t prop, const xcb_atom_t type, + const void *data, const quint32 data_len, const uint8_t format); +FRAMELESSHELPER_CORE_API void clearWindowProperty(const WId windowId, const xcb_atom_t prop); +[[nodiscard]] FRAMELESSHELPER_CORE_API xcb_atom_t internAtom(const char *name); +[[nodiscard]] FRAMELESSHELPER_CORE_API QString getWindowManagerName(); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isSupportedByWindowManager(const xcb_atom_t atom); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isSupportedByRootWindow(const xcb_atom_t atom); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool tryHideSystemTitleBar(const WId windowId, const bool hide = true); +FRAMELESSHELPER_CORE_API void openSystemMenu(const WId windowId, const QPoint &globalPos); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode_linux(); +[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getWmThemeColor(); +FRAMELESSHELPER_CORE_API void sendMoveResizeMessage + (const WId windowId, const uint32_t action, const QPoint &globalPos, const Qt::MouseButton button = Qt::LeftButton); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isCustomDecorationSupported(); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool + setPlatformPropertiesForWindow(QWindow *window, const QVariantHash &props); +#endif // Q_OS_LINUX + +#ifdef Q_OS_MACOS +[[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode_macos(); +FRAMELESSHELPER_CORE_API void setSystemTitleBarVisible(const WId windowId, const bool visible); +[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getControlsAccentColor(); +FRAMELESSHELPER_CORE_API void removeWindowProxy(const WId windowId); +#endif // Q_OS_MACOS +} // namespace Utils + +FRAMELESSHELPER_END_NAMESPACE diff --git a/FramelessHelper/include/FramelessHelper/Core/windowborderpainter.h b/FramelessHelper/include/FramelessHelper/Core/windowborderpainter.h new file mode 100644 index 0000000..b9bb52c --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Core/windowborderpainter.h @@ -0,0 +1,86 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcWindowBorderPainter) + +class WindowBorderPainterPrivate; + +class FRAMELESSHELPER_CORE_API WindowBorderPainter : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(WindowBorderPainter) + Q_DECLARE_PRIVATE(WindowBorderPainter) + + Q_PROPERTY(int thickness READ thickness WRITE setThickness NOTIFY thicknessChanged FINAL) + Q_PROPERTY(Global::WindowEdges edges READ edges WRITE setEdges NOTIFY edgesChanged FINAL) + Q_PROPERTY(QColor activeColor READ activeColor WRITE setActiveColor NOTIFY activeColorChanged FINAL) + Q_PROPERTY(QColor inactiveColor READ inactiveColor WRITE setInactiveColor NOTIFY inactiveColorChanged FINAL) + + Q_PROPERTY(int nativeThickness READ nativeThickness NOTIFY nativeBorderChanged FINAL) + Q_PROPERTY(Global::WindowEdges nativeEdges READ nativeEdges NOTIFY nativeBorderChanged FINAL) + Q_PROPERTY(QColor nativeActiveColor READ nativeActiveColor NOTIFY nativeBorderChanged FINAL) + Q_PROPERTY(QColor nativeInactiveColor READ nativeInactiveColor NOTIFY nativeBorderChanged FINAL) + +public: + explicit WindowBorderPainter(QObject *parent = nullptr); + ~WindowBorderPainter() override; + + Q_NODISCARD int thickness() const; + Q_NODISCARD Global::WindowEdges edges() const; + Q_NODISCARD QColor activeColor() const; + Q_NODISCARD QColor inactiveColor() const; + + Q_NODISCARD int nativeThickness() const; + Q_NODISCARD Global::WindowEdges nativeEdges() const; + Q_NODISCARD QColor nativeActiveColor() const; + Q_NODISCARD QColor nativeInactiveColor() const; + +public Q_SLOTS: + void paint(QPainter *painter, const QSize &size, const bool active) const; + void setThickness(const int value); + void setEdges(const Global::WindowEdges value); + void setActiveColor(const QColor &value); + void setInactiveColor(const QColor &value); + +Q_SIGNALS: + void thicknessChanged(); + void edgesChanged(); + void activeColorChanged(); + void inactiveColorChanged(); + void nativeBorderChanged(); + void shouldRepaint(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(WindowBorderPainter)) diff --git a/FramelessHelper/include/FramelessHelper/Quick/FramelessQuickHelper b/FramelessHelper/include/FramelessHelper/Quick/FramelessQuickHelper new file mode 100644 index 0000000..1cbb62f --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/FramelessQuickHelper @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Quick/FramelessQuickModule b/FramelessHelper/include/FramelessHelper/Quick/FramelessQuickModule new file mode 100644 index 0000000..5027203 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/FramelessQuickModule @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Quick/FramelessQuickUtils b/FramelessHelper/include/FramelessHelper/Quick/FramelessQuickUtils new file mode 100644 index 0000000..4d9796e --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/FramelessQuickUtils @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Quick/Global b/FramelessHelper/include/FramelessHelper/Quick/Global new file mode 100644 index 0000000..c2dae33 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/Global @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Quick/QuickChromePalette b/FramelessHelper/include/FramelessHelper/Quick/QuickChromePalette new file mode 100644 index 0000000..1bafea7 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/QuickChromePalette @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Quick/QuickImageItem b/FramelessHelper/include/FramelessHelper/Quick/QuickImageItem new file mode 100644 index 0000000..10f9102 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/QuickImageItem @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Quick/QuickMicaMaterial b/FramelessHelper/include/FramelessHelper/Quick/QuickMicaMaterial new file mode 100644 index 0000000..24aa4be --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/QuickMicaMaterial @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Quick/QuickWindowBorder b/FramelessHelper/include/FramelessHelper/Quick/QuickWindowBorder new file mode 100644 index 0000000..db95f69 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/QuickWindowBorder @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Quick/framelesshelperquick_global.h b/FramelessHelper/include/FramelessHelper/Quick/framelesshelperquick_global.h new file mode 100644 index 0000000..d9f39a5 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/framelesshelperquick_global.h @@ -0,0 +1,157 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 +#include +#if __has_include() +# include +#endif + +#ifndef FRAMELESSHELPER_QUICK_API +# ifdef FRAMELESSHELPER_QUICK_STATIC +# define FRAMELESSHELPER_QUICK_API +# else // FRAMELESSHELPER_QUICK_STATIC +# ifdef FRAMELESSHELPER_QUICK_LIBRARY +# define FRAMELESSHELPER_QUICK_API Q_DECL_EXPORT +# else // FRAMELESSHELPER_QUICK_LIBRARY +# define FRAMELESSHELPER_QUICK_API Q_DECL_IMPORT +# endif // FRAMELESSHELPER_QUICK_LIBRARY +# endif // FRAMELESSHELPER_QUICK_STATIC +#endif // FRAMELESSHELPER_QUICK_API + +#ifndef FRAMELESSHELPER_QUICK_ENUM_VALUE +# define FRAMELESSHELPER_QUICK_ENUM_VALUE(Enum, Value) \ + Value = static_cast(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Enum::Value), +#endif + +#ifndef FRAMELESSHELPER_ENUM_CORE_TO_QUICK +# define FRAMELESSHELPER_ENUM_CORE_TO_QUICK(Enum, Value) \ + static_cast(static_cast(Value)) +#endif + +#ifndef FRAMELESSHELPER_ENUM_QUICK_TO_CORE +# define FRAMELESSHELPER_ENUM_QUICK_TO_CORE(Enum, Value) \ + static_cast(static_cast(Value)) +#endif + +#ifndef FRAMELESSHELPER_FLAGS_CORE_TO_QUICK +# define FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Enum, Value, In, Out) \ + if (In & FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Enum::Value) { \ + Out |= FRAMELESSHELPER_PREPEND_NAMESPACE(QuickGlobal)::Enum::Value; \ + } +#endif + +#ifndef FRAMELESSHELPER_FLAGS_QUICK_TO_CORE +# define FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Enum, Value, In, Out) \ + if (In & FRAMELESSHELPER_PREPEND_NAMESPACE(QuickGlobal)::Enum::Value) { \ + Out |= FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Enum::Value; \ + } +#endif + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQuickGlobal) + +[[maybe_unused]] inline constexpr const char FRAMELESSHELPER_QUICK_URI[] = "org.wangwenx190.FramelessHelper"; + +class FRAMELESSHELPER_QUICK_API QuickGlobal : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QuickGlobal) +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(FramelessHelperConstants) +#endif +#ifdef QML_UNCREATABLE + QML_UNCREATABLE("The FramelessHelperConstants namespace is not creatable, you can only use it to access it's enums.") +#endif + +public: + explicit QuickGlobal(QObject *parent = nullptr); + ~QuickGlobal() override; + + enum class SystemTheme + { + FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemTheme, Unknown) + FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemTheme, Light) + FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemTheme, Dark) + FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemTheme, HighContrast) + }; + Q_ENUM(SystemTheme) + + enum class SystemButtonType + { + FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, Unknown) + FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, WindowIcon) + FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, Help) + FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, Minimize) + FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, Maximize) + FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, Restore) + FRAMELESSHELPER_QUICK_ENUM_VALUE(SystemButtonType, Close) + }; + Q_ENUM(SystemButtonType) + + enum class ButtonState + { + FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Unspecified) + FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Hovered) + FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Pressed) + FRAMELESSHELPER_QUICK_ENUM_VALUE(ButtonState, Clicked) + }; + Q_ENUM(ButtonState) + + enum class BlurMode + { + FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Disable) + FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Default) + FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Windows_Aero) + FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Windows_Acrylic) + FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Windows_Mica) + FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Windows_MicaAlt) + }; + Q_ENUM(BlurMode) + + enum class WindowEdge : quint32 + { + FRAMELESSHELPER_QUICK_ENUM_VALUE(WindowEdge, Left) + FRAMELESSHELPER_QUICK_ENUM_VALUE(WindowEdge, Top) + FRAMELESSHELPER_QUICK_ENUM_VALUE(WindowEdge, Right) + FRAMELESSHELPER_QUICK_ENUM_VALUE(WindowEdge, Bottom) + }; + Q_ENUM(WindowEdge) + Q_DECLARE_FLAGS(WindowEdges, WindowEdge) + Q_FLAG(WindowEdges) +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QuickGlobal::WindowEdges) + +namespace FramelessHelper::Quick +{ +FRAMELESSHELPER_QUICK_API void initialize(); +FRAMELESSHELPER_QUICK_API void uninitialize(); +} // namespace FramelessHelper::Quick + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickGlobal)) diff --git a/FramelessHelper/include/FramelessHelper/Quick/framelessquickhelper.h b/FramelessHelper/include/FramelessHelper/Quick/framelessquickhelper.h new file mode 100644 index 0000000..ce775da --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/framelessquickhelper.h @@ -0,0 +1,111 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperquick_global.h" +#include +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcFramelessQuickHelper) + +class FramelessQuickHelperPrivate; +class QuickMicaMaterial; +class QuickWindowBorder; + +class FRAMELESSHELPER_QUICK_API FramelessQuickHelper : public QQuickItem +{ + Q_OBJECT +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(FramelessHelper) +#endif +#ifdef QML_ATTACHED + QML_ATTACHED(FramelessQuickHelper) +#endif + Q_DECLARE_PRIVATE(FramelessQuickHelper) + Q_DISABLE_COPY_MOVE(FramelessQuickHelper) + Q_PROPERTY(QQuickItem* titleBarItem READ titleBarItem WRITE setTitleBarItem NOTIFY titleBarItemChanged FINAL) + Q_PROPERTY(bool windowFixedSize READ isWindowFixedSize WRITE setWindowFixedSize NOTIFY windowFixedSizeChanged FINAL) + Q_PROPERTY(bool blurBehindWindowEnabled READ isBlurBehindWindowEnabled WRITE setBlurBehindWindowEnabled NOTIFY blurBehindWindowEnabledChanged FINAL) + Q_PROPERTY(QQuickWindow* window READ window NOTIFY windowChanged2 FINAL) + Q_PROPERTY(bool extendsContentIntoTitleBar READ isContentExtendedIntoTitleBar WRITE extendsContentIntoTitleBar NOTIFY extendsContentIntoTitleBarChanged FINAL) + +public: + explicit FramelessQuickHelper(QQuickItem *parent = nullptr); + ~FramelessQuickHelper() override; + + Q_NODISCARD static FramelessQuickHelper *get(QObject *object); + Q_NODISCARD static FramelessQuickHelper *qmlAttachedProperties(QObject *parentObject); + + Q_NODISCARD QQuickItem *titleBarItem() const; + Q_NODISCARD bool isWindowFixedSize() const; + Q_NODISCARD bool isBlurBehindWindowEnabled() const; + Q_NODISCARD bool isContentExtendedIntoTitleBar() const; + + Q_NODISCARD QuickMicaMaterial *micaMaterial() const; + Q_NODISCARD QuickWindowBorder *windowBorder() const; + +public Q_SLOTS: + void extendsContentIntoTitleBar(const bool value = true); + + void setTitleBarItem(QQuickItem *value); + void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType); + void setHitTestVisible(QQuickItem *item, const bool visible = true); + void setHitTestVisible_rect(const QRect &rect, const bool visible = true); + void setHitTestVisible_object(QObject *object, const bool visible = true); + void setHitTestVisible_item(QQuickItem *item, const bool visible = true); + + void showSystemMenu(const QPoint &pos); + void windowStartSystemMove2(const QPoint &pos); + void windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos); + + void moveWindowToDesktopCenter(); + void bringWindowToFront(); + void setWindowFixedSize(const bool value); + void setBlurBehindWindowEnabled(const bool value); + +protected: + void itemChange(const ItemChange change, const ItemChangeData &value) override; + void classBegin() override; + void componentComplete() override; + +Q_SIGNALS: + void extendsContentIntoTitleBarChanged(); + void titleBarItemChanged(); + void windowFixedSizeChanged(); + void blurBehindWindowEnabledChanged(); + void windowChanged2(); + void ready(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickHelper)) +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickHelper)) +QML_DECLARE_TYPEINFO(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickHelper), QML_HAS_ATTACHED_PROPERTIES) diff --git a/FramelessHelper/include/FramelessHelper/Quick/framelessquickmodule.h b/FramelessHelper/include/FramelessHelper/Quick/framelessquickmodule.h new file mode 100644 index 0000000..7d3523e --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/framelessquickmodule.h @@ -0,0 +1,42 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperquick_global.h" + +QT_BEGIN_NAMESPACE +class QQmlEngine; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQuickModule) + +namespace FramelessHelper::Quick +{ +FRAMELESSHELPER_QUICK_API void registerTypes(QQmlEngine *engine); +} // namespace FramelessHelper::Quick + +FRAMELESSHELPER_END_NAMESPACE diff --git a/FramelessHelper/include/FramelessHelper/Quick/framelessquickutils.h b/FramelessHelper/include/FramelessHelper/Quick/framelessquickutils.h new file mode 100644 index 0000000..34811ba --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/framelessquickutils.h @@ -0,0 +1,95 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperquick_global.h" +#include + +QT_BEGIN_NAMESPACE +class QQuickWindow; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcFramelessQuickUtils) + +class FRAMELESSHELPER_QUICK_API FramelessQuickUtils : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(FramelessQuickUtils) + Q_INTERFACES(QQmlParserStatus) +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(FramelessUtils) +#endif +#ifdef QML_SINGLETON + QML_SINGLETON +#endif + Q_PROPERTY(qreal titleBarHeight READ titleBarHeight CONSTANT FINAL) + Q_PROPERTY(bool frameBorderVisible READ frameBorderVisible CONSTANT FINAL) + Q_PROPERTY(qreal frameBorderThickness READ frameBorderThickness CONSTANT FINAL) + Q_PROPERTY(QuickGlobal::SystemTheme systemTheme READ systemTheme NOTIFY systemThemeChanged FINAL) + Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemAccentColorChanged FINAL) + Q_PROPERTY(bool titleBarColorized READ titleBarColorized NOTIFY titleBarColorizedChanged FINAL) + Q_PROPERTY(QColor defaultSystemLightColor READ defaultSystemLightColor CONSTANT FINAL) + Q_PROPERTY(QColor defaultSystemDarkColor READ defaultSystemDarkColor CONSTANT FINAL) + Q_PROPERTY(QSizeF defaultSystemButtonSize READ defaultSystemButtonSize CONSTANT FINAL) + Q_PROPERTY(QSizeF defaultSystemButtonIconSize READ defaultSystemButtonIconSize CONSTANT FINAL) + Q_PROPERTY(QColor defaultSystemButtonBackgroundColor READ defaultSystemButtonBackgroundColor CONSTANT FINAL) + Q_PROPERTY(QColor defaultSystemCloseButtonBackgroundColor READ defaultSystemCloseButtonBackgroundColor CONSTANT FINAL) + +public: + explicit FramelessQuickUtils(QObject *parent = nullptr); + ~FramelessQuickUtils() override; + + Q_NODISCARD qreal titleBarHeight() const; + Q_NODISCARD bool frameBorderVisible() const; + Q_NODISCARD qreal frameBorderThickness() const; + Q_NODISCARD QuickGlobal::SystemTheme systemTheme() const; + Q_NODISCARD QColor systemAccentColor() const; + Q_NODISCARD bool titleBarColorized() const; + Q_NODISCARD QColor defaultSystemLightColor() const; + Q_NODISCARD QColor defaultSystemDarkColor() const; + Q_NODISCARD QSizeF defaultSystemButtonSize() const; + Q_NODISCARD QSizeF defaultSystemButtonIconSize() const; + Q_NODISCARD QColor defaultSystemButtonBackgroundColor() const; + Q_NODISCARD QColor defaultSystemCloseButtonBackgroundColor() const; + + Q_NODISCARD Q_INVOKABLE QColor getSystemButtonBackgroundColor( + const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state); + +protected: + void classBegin() override; + void componentComplete() override; + +Q_SIGNALS: + void systemThemeChanged(); + void systemAccentColorChanged(); + void titleBarColorizedChanged(); +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickUtils)) +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickUtils)) diff --git a/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickapplicationwindow_p.h b/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickapplicationwindow_p.h new file mode 100644 index 0000000..8c52a27 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickapplicationwindow_p.h @@ -0,0 +1,88 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 + +#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE + +#include "framelesshelperquick_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FramelessQuickApplicationWindowPrivate; + +class FRAMELESSHELPER_QUICK_API FramelessQuickApplicationWindow : public QQuickApplicationWindow +{ + Q_OBJECT +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(FramelessApplicationWindow) +#endif + Q_DECLARE_PRIVATE(FramelessQuickApplicationWindow) + Q_DISABLE_COPY_MOVE(FramelessQuickApplicationWindow) + Q_PROPERTY(bool hidden READ isHidden NOTIFY hiddenChanged FINAL) + Q_PROPERTY(bool normal READ isNormal NOTIFY normalChanged FINAL) + Q_PROPERTY(bool minimized READ isMinimized NOTIFY minimizedChanged FINAL) + Q_PROPERTY(bool maximized READ isMaximized NOTIFY maximizedChanged FINAL) + Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL) + Q_PROPERTY(bool fullScreen READ isFullScreen NOTIFY fullScreenChanged FINAL) + +public: + explicit FramelessQuickApplicationWindow(QWindow *parent = nullptr); + ~FramelessQuickApplicationWindow() override; + + Q_NODISCARD bool isHidden() const; + Q_NODISCARD bool isNormal() const; + Q_NODISCARD bool isMinimized() const; + Q_NODISCARD bool isMaximized() const; + Q_NODISCARD bool isZoomed() const; + Q_NODISCARD bool isFullScreen() const; + +public Q_SLOTS: + void showMinimized2(); + void toggleMaximized(); + void toggleFullScreen(); + +protected: + void classBegin() override; + void componentComplete() override; + +Q_SIGNALS: + void hiddenChanged(); + void normalChanged(); + void minimizedChanged(); + void maximizedChanged(); + void zoomedChanged(); + void fullScreenChanged(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickApplicationWindow)) +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickApplicationWindow)) + +#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE diff --git a/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickapplicationwindow_p_p.h b/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickapplicationwindow_p_p.h new file mode 100644 index 0000000..dabba66 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickapplicationwindow_p_p.h @@ -0,0 +1,75 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 + +#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE + +#include "framelesshelperquick_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FramelessQuickApplicationWindow; +class QuickWindowBorder; + +class FRAMELESSHELPER_QUICK_API FramelessQuickApplicationWindowPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(FramelessQuickApplicationWindow) + Q_DISABLE_COPY_MOVE(FramelessQuickApplicationWindowPrivate) + +public: + explicit FramelessQuickApplicationWindowPrivate(FramelessQuickApplicationWindow *q); + ~FramelessQuickApplicationWindowPrivate() override; + + Q_NODISCARD static FramelessQuickApplicationWindowPrivate *get(FramelessQuickApplicationWindow *pub); + Q_NODISCARD static const FramelessQuickApplicationWindowPrivate *get(const FramelessQuickApplicationWindow *pub); + + Q_INVOKABLE Q_NODISCARD bool isHidden() const; + Q_INVOKABLE Q_NODISCARD bool isNormal() const; + Q_INVOKABLE Q_NODISCARD bool isMinimized() const; + Q_INVOKABLE Q_NODISCARD bool isMaximized() const; + Q_INVOKABLE Q_NODISCARD bool isZoomed() const; + Q_INVOKABLE Q_NODISCARD bool isFullScreen() const; + +public Q_SLOTS: + void showMinimized2(); + void toggleMaximized(); + void toggleFullScreen(); + +private: + void initialize(); + +private: + FramelessQuickApplicationWindow *q_ptr = nullptr; + QuickWindowBorder *m_windowBorder = nullptr; + QQuickWindow::Visibility m_savedVisibility = QQuickWindow::Windowed; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickApplicationWindowPrivate)) + +#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE diff --git a/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickhelper_p.h b/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickhelper_p.h new file mode 100644 index 0000000..8a02ea7 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickhelper_p.h @@ -0,0 +1,108 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperquick_global.h" + +QT_BEGIN_NAMESPACE +class QQuickItem; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FramelessQuickHelper; +class QuickMicaMaterial; +class QuickWindowBorder; +struct QuickHelperData; + +class FRAMELESSHELPER_QUICK_API FramelessQuickHelperPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(FramelessQuickHelper) + Q_DISABLE_COPY_MOVE(FramelessQuickHelperPrivate) + +public: + explicit FramelessQuickHelperPrivate(FramelessQuickHelper *q); + ~FramelessQuickHelperPrivate() override; + + Q_NODISCARD static FramelessQuickHelperPrivate *get(FramelessQuickHelper *pub); + Q_NODISCARD static const FramelessQuickHelperPrivate *get(const FramelessQuickHelper *pub); + + Q_NODISCARD bool isContentExtendedIntoTitleBar() const; + void extendsContentIntoTitleBar(const bool value); + + Q_NODISCARD QQuickItem *getTitleBarItem() const; + void setTitleBarItem(QQuickItem *value); + + void attach(); + void detach(); + void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType); + void setHitTestVisible(QQuickItem *item, const bool visible = true); + void setHitTestVisible(const QRect &rect, const bool visible = true); + void setHitTestVisible(QObject *object, const bool visible = true); + void showSystemMenu(const QPoint &pos); + void windowStartSystemMove2(const QPoint &pos); + void windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos); + + void moveWindowToDesktopCenter(); + void bringWindowToFront(); + + Q_NODISCARD bool isWindowFixedSize() const; + void setWindowFixedSize(const bool value); + + void emitSignalForAllInstances(const QByteArray &signal); + + Q_NODISCARD bool isBlurBehindWindowEnabled() const; + void setBlurBehindWindowEnabled(const bool value, const QColor &color); + + void setProperty(const QByteArray &name, const QVariant &value); + Q_NODISCARD QVariant getProperty(const QByteArray &name, const QVariant &defaultValue = {}); + + Q_NODISCARD QuickMicaMaterial *findOrCreateMicaMaterial() const; + Q_NODISCARD QuickWindowBorder *findOrCreateWindowBorder() const; + + Q_NODISCARD static FramelessQuickHelper *findOrCreateFramelessHelper(QObject *object); + +private: + Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const; + Q_NODISCARD bool isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const; + Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const; + Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const; + void setSystemButtonState(const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state); + Q_NODISCARD QuickHelperData getWindowData() const; + Q_NODISCARD QuickHelperData *getWindowDataMutable() const; + void rebindWindow(); + +private: + FramelessQuickHelper *q_ptr = nullptr; + QColor m_savedWindowBackgroundColor = {}; + bool m_blurBehindWindowEnabled = false; + std::optional m_extendIntoTitleBar = std::nullopt; + bool m_destroying = false; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickHelperPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickwindow_p.h b/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickwindow_p.h new file mode 100644 index 0000000..3643c22 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickwindow_p.h @@ -0,0 +1,88 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 + +#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE + +#include "framelesshelperquick_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FramelessQuickWindowPrivate; + +class FRAMELESSHELPER_QUICK_API FramelessQuickWindow : public QQuickWindowQmlImpl +{ + Q_OBJECT +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(FramelessWindow) +#endif + Q_DECLARE_PRIVATE(FramelessQuickWindow) + Q_DISABLE_COPY_MOVE(FramelessQuickWindow) + Q_PROPERTY(bool hidden READ isHidden NOTIFY hiddenChanged FINAL) + Q_PROPERTY(bool normal READ isNormal NOTIFY normalChanged FINAL) + Q_PROPERTY(bool minimized READ isMinimized NOTIFY minimizedChanged FINAL) + Q_PROPERTY(bool maximized READ isMaximized NOTIFY maximizedChanged FINAL) + Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL) + Q_PROPERTY(bool fullScreen READ isFullScreen NOTIFY fullScreenChanged FINAL) + +public: + explicit FramelessQuickWindow(QWindow *parent = nullptr); + ~FramelessQuickWindow() override; + + Q_NODISCARD bool isHidden() const; + Q_NODISCARD bool isNormal() const; + Q_NODISCARD bool isMinimized() const; + Q_NODISCARD bool isMaximized() const; + Q_NODISCARD bool isZoomed() const; + Q_NODISCARD bool isFullScreen() const; + +public Q_SLOTS: + void showMinimized2(); + void toggleMaximized(); + void toggleFullScreen(); + +protected: + void classBegin() override; + void componentComplete() override; + +Q_SIGNALS: + void hiddenChanged(); + void normalChanged(); + void minimizedChanged(); + void maximizedChanged(); + void zoomedChanged(); + void fullScreenChanged(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickWindow)) +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickWindow)) + +#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE diff --git a/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickwindow_p_p.h b/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickwindow_p_p.h new file mode 100644 index 0000000..6360557 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/private/framelessquickwindow_p_p.h @@ -0,0 +1,75 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 + +#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE + +#include "framelesshelperquick_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FramelessQuickWindow; +class QuickWindowBorder; + +class FRAMELESSHELPER_QUICK_API FramelessQuickWindowPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(FramelessQuickWindow) + Q_DISABLE_COPY_MOVE(FramelessQuickWindowPrivate) + +public: + explicit FramelessQuickWindowPrivate(FramelessQuickWindow *q); + ~FramelessQuickWindowPrivate() override; + + Q_NODISCARD static FramelessQuickWindowPrivate *get(FramelessQuickWindow *pub); + Q_NODISCARD static const FramelessQuickWindowPrivate *get(const FramelessQuickWindow *pub); + + Q_INVOKABLE Q_NODISCARD bool isHidden() const; + Q_INVOKABLE Q_NODISCARD bool isNormal() const; + Q_INVOKABLE Q_NODISCARD bool isMinimized() const; + Q_INVOKABLE Q_NODISCARD bool isMaximized() const; + Q_INVOKABLE Q_NODISCARD bool isZoomed() const; + Q_INVOKABLE Q_NODISCARD bool isFullScreen() const; + +public Q_SLOTS: + void showMinimized2(); + void toggleMaximized(); + void toggleFullScreen(); + +private: + void initialize(); + +private: + FramelessQuickWindow *q_ptr = nullptr; + QuickWindowBorder *m_windowBorder = nullptr; + QQuickWindow::Visibility m_savedVisibility = QQuickWindow::Windowed; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickWindowPrivate)) + +#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE diff --git a/FramelessHelper/include/FramelessHelper/Quick/private/quickimageitem_p.h b/FramelessHelper/include/FramelessHelper/Quick/private/quickimageitem_p.h new file mode 100644 index 0000000..204349d --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/private/quickimageitem_p.h @@ -0,0 +1,68 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperquick_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class QuickImageItem; + +class FRAMELESSHELPER_QUICK_API QuickImageItemPrivate : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QuickImageItemPrivate) + Q_DECLARE_PUBLIC(QuickImageItem) + +public: + explicit QuickImageItemPrivate(QuickImageItem *q); + ~QuickImageItemPrivate() override; + + Q_NODISCARD static QuickImageItemPrivate *get(QuickImageItem *q); + Q_NODISCARD static const QuickImageItemPrivate *get(const QuickImageItem *q); + + void paint(QPainter *painter) const; + + Q_NODISCARD QVariant source() const; + void setSource(const QVariant &value); + +private: + void initialize(); + void fromUrl(const QUrl &value, QPainter *painter) const; + void fromString(const QString &value, QPainter *painter) const; + void fromImage(const QImage &value, QPainter *painter) const; + void fromPixmap(const QPixmap &value, QPainter *painter) const; + void fromIcon(const QIcon &value, QPainter *painter) const; + Q_NODISCARD QRect paintArea() const; + +private: + QuickImageItem *q_ptr = nullptr; + QVariant m_source = {}; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickImageItemPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Quick/private/quickmicamaterial_p.h b/FramelessHelper/include/FramelessHelper/Quick/private/quickmicamaterial_p.h new file mode 100644 index 0000000..c9ae021 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/private/quickmicamaterial_p.h @@ -0,0 +1,64 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperquick_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class QuickMicaMaterial; +class WallpaperImageNode; + +class FRAMELESSHELPER_QUICK_API QuickMicaMaterialPrivate : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QuickMicaMaterialPrivate) + Q_DECLARE_PUBLIC(QuickMicaMaterial) + +public: + explicit QuickMicaMaterialPrivate(QuickMicaMaterial *q); + ~QuickMicaMaterialPrivate() override; + + Q_NODISCARD static QuickMicaMaterialPrivate *get(QuickMicaMaterial *q); + Q_NODISCARD static const QuickMicaMaterialPrivate *get(const QuickMicaMaterial *q); + +public Q_SLOTS: + void rebindWindow(); + void forceRegenerateWallpaperImageCache(); + void appendNode(WallpaperImageNode *node); + +private: + void initialize(); + +private: + QuickMicaMaterial *q_ptr = nullptr; + QMetaObject::Connection m_rootWindowXChangedConnection = {}; + QMetaObject::Connection m_rootWindowYChangedConnection = {}; + QList> m_nodes = {}; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickMicaMaterialPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Quick/private/quickstandardsystembutton_p.h b/FramelessHelper/include/FramelessHelper/Quick/private/quickstandardsystembutton_p.h new file mode 100644 index 0000000..7418d90 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/private/quickstandardsystembutton_p.h @@ -0,0 +1,116 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 + +#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE + +#include "framelesshelperquick_global.h" +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#include + +QT_BEGIN_NAMESPACE +class QQuickText; +class QQuickRectangle; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FRAMELESSHELPER_QUICK_API QuickStandardSystemButton : public QQuickButton +{ + Q_OBJECT +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(StandardSystemButton) +#endif + Q_DISABLE_COPY_MOVE(QuickStandardSystemButton) + Q_PROPERTY(QuickGlobal::SystemButtonType buttonType READ buttonType WRITE setButtonType NOTIFY buttonTypeChanged FINAL) + Q_PROPERTY(QString glyph READ glyph WRITE setGlyph NOTIFY glyphChanged FINAL) + Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setHoverColor NOTIFY hoverColorChanged FINAL) + Q_PROPERTY(QColor pressColor READ pressColor WRITE setPressColor NOTIFY pressColorChanged FINAL) + Q_PROPERTY(QColor normalColor READ normalColor WRITE setNormalColor NOTIFY normalColorChanged FINAL) + Q_PROPERTY(QColor activeForegroundColor READ activeForegroundColor WRITE setActiveForegroundColor NOTIFY activeForegroundColorChanged FINAL) + Q_PROPERTY(QColor inactiveForegroundColor READ inactiveForegroundColor WRITE setInactiveForegroundColor NOTIFY inactiveForegroundColorChanged FINAL) + Q_PROPERTY(qreal iconSize READ iconSize WRITE setIconSize NOTIFY iconSizeChanged FINAL) + +public: + explicit QuickStandardSystemButton(QQuickItem *parent = nullptr); + explicit QuickStandardSystemButton(const QuickGlobal::SystemButtonType type, QQuickItem *parent = nullptr); + ~QuickStandardSystemButton() override; + + Q_NODISCARD QuickGlobal::SystemButtonType buttonType() const; + Q_NODISCARD QString glyph() const; + Q_NODISCARD QColor normalColor() const; + Q_NODISCARD QColor hoverColor() const; + Q_NODISCARD QColor pressColor() const; + Q_NODISCARD QColor activeForegroundColor() const; + Q_NODISCARD QColor inactiveForegroundColor() const; + Q_NODISCARD qreal iconSize() const; + +public Q_SLOTS: + void updateColor(); + void setButtonType(const QuickGlobal::SystemButtonType type); + void setGlyph(const QString &value); + void setNormalColor(const QColor &value); + void setHoverColor(const QColor &value); + void setPressColor(const QColor &value); + void setActiveForegroundColor(const QColor &value); + void setInactiveForegroundColor(const QColor &value); + void setIconSize(const qreal value); + +protected: + void classBegin() override; + void componentComplete() override; + +private: + void initialize(); + +Q_SIGNALS: + void buttonTypeChanged(); + void glyphChanged(); + void normalColorChanged(); + void hoverColorChanged(); + void pressColorChanged(); + void activeForegroundColorChanged(); + void inactiveForegroundColorChanged(); + void iconSizeChanged(); + +private: + QQuickText *m_contentItem = nullptr; + QQuickRectangle *m_backgroundItem = nullptr; + QuickGlobal::SystemButtonType m_buttonType = QuickGlobal::SystemButtonType::Unknown; + QString m_glyph = {}; + QColor m_normalColor = {}; + QColor m_hoverColor = {}; + QColor m_pressColor = {}; + QColor m_activeForegroundColor = {}; + QColor m_inactiveForegroundColor = {}; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickStandardSystemButton)) +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickStandardSystemButton)) +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + +#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE diff --git a/FramelessHelper/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h b/FramelessHelper/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h new file mode 100644 index 0000000..074c40e --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h @@ -0,0 +1,154 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 + +#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE + +#include "framelesshelperquick_global.h" +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#include "quickchromepalette.h" +#include "quickstandardsystembutton_p.h" +#include +#include + +QT_BEGIN_NAMESPACE +class QQuickRow; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class QuickImageItem; + +class FRAMELESSHELPER_QUICK_API QuickStandardTitleBar : public QQuickRectangle +{ + Q_OBJECT +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(StandardTitleBar) +#endif // QML_NAMED_ELEMENT + Q_DISABLE_COPY_MOVE(QuickStandardTitleBar) + Q_PROPERTY(Qt::Alignment titleLabelAlignment READ titleLabelAlignment WRITE setTitleLabelAlignment NOTIFY titleLabelAlignmentChanged FINAL) + Q_PROPERTY(QQuickLabel* titleLabel READ titleLabel CONSTANT FINAL) +#ifndef Q_OS_MACOS + Q_PROPERTY(QuickStandardSystemButton* minimizeButton READ minimizeButton CONSTANT FINAL) + Q_PROPERTY(QuickStandardSystemButton* maximizeButton READ maximizeButton CONSTANT FINAL) + Q_PROPERTY(QuickStandardSystemButton* closeButton READ closeButton CONSTANT FINAL) +#endif // Q_OS_MACOS + Q_PROPERTY(bool extended READ isExtended WRITE setExtended NOTIFY extendedChanged FINAL) + Q_PROPERTY(bool hideWhenClose READ isHideWhenClose WRITE setHideWhenClose NOTIFY hideWhenCloseChanged FINAL) + Q_PROPERTY(QuickChromePalette* chromePalette READ chromePalette CONSTANT FINAL) + Q_PROPERTY(QSizeF windowIconSize READ windowIconSize WRITE setWindowIconSize NOTIFY windowIconSizeChanged FINAL) + Q_PROPERTY(bool windowIconVisible READ windowIconVisible WRITE setWindowIconVisible NOTIFY windowIconVisibleChanged FINAL) + Q_PROPERTY(QVariant windowIcon READ windowIcon WRITE setWindowIcon NOTIFY windowIconChanged FINAL) + +public: + explicit QuickStandardTitleBar(QQuickItem *parent = nullptr); + ~QuickStandardTitleBar() override; + + Q_NODISCARD Qt::Alignment titleLabelAlignment() const; + void setTitleLabelAlignment(const Qt::Alignment value); + + Q_NODISCARD QQuickLabel *titleLabel() const; +#ifndef Q_OS_MACOS + Q_NODISCARD QuickStandardSystemButton *minimizeButton() const; + Q_NODISCARD QuickStandardSystemButton *maximizeButton() const; + Q_NODISCARD QuickStandardSystemButton *closeButton() const; +#endif // Q_OS_MACOS + + Q_NODISCARD bool isExtended() const; + void setExtended(const bool value); + + Q_NODISCARD bool isHideWhenClose() const; + void setHideWhenClose(const bool value); + + Q_NODISCARD QuickChromePalette *chromePalette() const; + + Q_NODISCARD QSizeF windowIconSize() const; + void setWindowIconSize(const QSizeF &value); + + Q_NODISCARD bool windowIconVisible() const; + void setWindowIconVisible(const bool value); + + Q_NODISCARD QVariant windowIcon() const; + void setWindowIcon(const QVariant &value); + +protected: + void itemChange(const ItemChange change, const ItemChangeData &value) override; + Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; + void classBegin() override; + void componentComplete() override; + +private Q_SLOTS: + void updateMaximizeButton(); + void updateTitleLabelText(); + void updateTitleBarColor(); + void updateChromeButtonColor(); + void clickMinimizeButton(); + void clickMaximizeButton(); + void clickCloseButton(); + void retranslateUi(); + void updateWindowIcon(); + +Q_SIGNALS: + void titleLabelAlignmentChanged(); + void extendedChanged(); + void hideWhenCloseChanged(); + void windowIconSizeChanged(); + void windowIconVisibleChanged(); + void windowIconChanged(); + +private: + void initialize(); + void updateAll(); + Q_NODISCARD bool mouseEventHandler(QMouseEvent *event); + Q_NODISCARD QRect windowIconRect() const; + Q_NODISCARD bool isInTitleBarIconArea(const QPoint &pos) const; + Q_NODISCARD bool windowIconVisible_real() const; + +private: + Qt::Alignment m_labelAlignment = {}; + QuickImageItem *m_windowIcon = nullptr; + QQuickLabel *m_windowTitleLabel = nullptr; +#ifndef Q_OS_MACOS + QQuickRow *m_systemButtonsRow = nullptr; + QuickStandardSystemButton *m_minimizeButton = nullptr; + QuickStandardSystemButton *m_maximizeButton = nullptr; + QuickStandardSystemButton *m_closeButton = nullptr; +#endif // Q_OS_MACOS + QMetaObject::Connection m_windowStateChangeConnection = {}; + QMetaObject::Connection m_windowActiveChangeConnection = {}; + QMetaObject::Connection m_windowTitleChangeConnection = {}; + bool m_extended = false; + bool m_hideWhenClose = false; + QuickChromePalette *m_chromePalette = nullptr; + bool m_closeTriggered = false; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickStandardTitleBar)) +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickStandardTitleBar)) +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + +#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE diff --git a/FramelessHelper/include/FramelessHelper/Quick/private/quickwindowborder_p.h b/FramelessHelper/include/FramelessHelper/Quick/private/quickwindowborder_p.h new file mode 100644 index 0000000..f8d3375 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/private/quickwindowborder_p.h @@ -0,0 +1,65 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperquick_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class QuickWindowBorder; +class WindowBorderPainter; + +class FRAMELESSHELPER_QUICK_API QuickWindowBorderPrivate : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QuickWindowBorderPrivate) + Q_DECLARE_PUBLIC(QuickWindowBorder) + +public: + explicit QuickWindowBorderPrivate(QuickWindowBorder *q); + ~QuickWindowBorderPrivate() override; + + Q_NODISCARD static QuickWindowBorderPrivate *get(QuickWindowBorder *q); + Q_NODISCARD static const QuickWindowBorderPrivate *get(const QuickWindowBorder *q); + + void paint(QPainter *painter) const; + +public Q_SLOTS: + void update(); + +private: + void initialize(); + void rebindWindow(); + +private: + QuickWindowBorder *q_ptr = nullptr; + WindowBorderPainter *m_borderPainter = nullptr; + QMetaObject::Connection m_activeChangeConnection = {}; + QMetaObject::Connection m_visibilityChangeConnection = {}; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickWindowBorderPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Quick/quickchromepalette.h b/FramelessHelper/include/FramelessHelper/Quick/quickchromepalette.h new file mode 100644 index 0000000..38c74bb --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/quickchromepalette.h @@ -0,0 +1,56 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperquick_global.h" +#include +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQuickChromePalette) + +class FRAMELESSHELPER_QUICK_API QuickChromePalette : public ChromePalette, public QQmlParserStatus +{ + Q_OBJECT +#ifdef QML_ANONYMOUS + QML_ANONYMOUS +#endif + Q_DISABLE_COPY_MOVE(QuickChromePalette) + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QuickChromePalette(QObject *parent = nullptr); + ~QuickChromePalette() override; + +protected: + void classBegin() override; + void componentComplete() override; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickChromePalette)) +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickChromePalette)) diff --git a/FramelessHelper/include/FramelessHelper/Quick/quickimageitem.h b/FramelessHelper/include/FramelessHelper/Quick/quickimageitem.h new file mode 100644 index 0000000..3dafbd1 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/quickimageitem.h @@ -0,0 +1,70 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperquick_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQuickImageItem) + +class QuickImageItemPrivate; + +class FRAMELESSHELPER_QUICK_API QuickImageItem : public QQuickPaintedItem +{ + Q_OBJECT +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(ImageItem) +#endif + Q_DISABLE_COPY_MOVE(QuickImageItem) + Q_DECLARE_PRIVATE(QuickImageItem) + + Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged FINAL) + +public: + explicit QuickImageItem(QQuickItem *parent = nullptr); + ~QuickImageItem() override; + + void paint(QPainter *painter) override; + + Q_NODISCARD QVariant source() const; + void setSource(const QVariant &value); + +protected: + void classBegin() override; + void componentComplete() override; + +Q_SIGNALS: + void sourceChanged(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickImageItem)) +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickImageItem)) diff --git a/FramelessHelper/include/FramelessHelper/Quick/quickmicamaterial.h b/FramelessHelper/include/FramelessHelper/Quick/quickmicamaterial.h new file mode 100644 index 0000000..106bfea --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/quickmicamaterial.h @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperquick_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQuickMicaMaterial) + +class QuickMicaMaterialPrivate; + +class FRAMELESSHELPER_QUICK_API QuickMicaMaterial : public QQuickItem +{ + Q_OBJECT +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(MicaMaterial) +#endif + Q_DISABLE_COPY_MOVE(QuickMicaMaterial) + Q_DECLARE_PRIVATE(QuickMicaMaterial) + +public: + explicit QuickMicaMaterial(QQuickItem *parent = nullptr); + ~QuickMicaMaterial() override; + +protected: + void itemChange(const ItemChange change, const ItemChangeData &value) override; + [[nodiscard]] QSGNode *updatePaintNode(QSGNode *old, UpdatePaintNodeData *data) override; + void classBegin() override; + void componentComplete() override; + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickMicaMaterial)) +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickMicaMaterial)) diff --git a/FramelessHelper/include/FramelessHelper/Quick/quickwindowborder.h b/FramelessHelper/include/FramelessHelper/Quick/quickwindowborder.h new file mode 100644 index 0000000..db4335f --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Quick/quickwindowborder.h @@ -0,0 +1,96 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperquick_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQuickWindowBorder) + +class QuickWindowBorderPrivate; + +class FRAMELESSHELPER_QUICK_API QuickWindowBorder : public QQuickPaintedItem +{ + Q_OBJECT +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(WindowBorder) +#endif + Q_DISABLE_COPY_MOVE(QuickWindowBorder) + Q_DECLARE_PRIVATE(QuickWindowBorder) + + Q_PROPERTY(qreal thickness READ thickness WRITE setThickness NOTIFY thicknessChanged FINAL) + Q_PROPERTY(QuickGlobal::WindowEdges edges READ edges WRITE setEdges NOTIFY edgesChanged FINAL) + Q_PROPERTY(QColor activeColor READ activeColor WRITE setActiveColor NOTIFY activeColorChanged FINAL) + Q_PROPERTY(QColor inactiveColor READ inactiveColor WRITE setInactiveColor NOTIFY inactiveColorChanged FINAL) + + Q_PROPERTY(qreal nativeThickness READ nativeThickness NOTIFY nativeBorderChanged FINAL) + Q_PROPERTY(QuickGlobal::WindowEdges nativeEdges READ nativeEdges NOTIFY nativeBorderChanged FINAL) + Q_PROPERTY(QColor nativeActiveColor READ nativeActiveColor NOTIFY nativeBorderChanged FINAL) + Q_PROPERTY(QColor nativeInactiveColor READ nativeInactiveColor NOTIFY nativeBorderChanged FINAL) + +public: + explicit QuickWindowBorder(QQuickItem *parent = nullptr); + ~QuickWindowBorder() override; + + void paint(QPainter *painter) override; + + Q_NODISCARD qreal thickness() const; + Q_NODISCARD QuickGlobal::WindowEdges edges() const; + Q_NODISCARD QColor activeColor() const; + Q_NODISCARD QColor inactiveColor() const; + + Q_NODISCARD qreal nativeThickness() const; + Q_NODISCARD QuickGlobal::WindowEdges nativeEdges() const; + Q_NODISCARD QColor nativeActiveColor() const; + Q_NODISCARD QColor nativeInactiveColor() const; + +public Q_SLOTS: + void setThickness(const qreal value); + void setEdges(const QuickGlobal::WindowEdges value); + void setActiveColor(const QColor &value); + void setInactiveColor(const QColor &value); + +protected: + void itemChange(const ItemChange change, const ItemChangeData &value) override; + void classBegin() override; + void componentComplete() override; + +Q_SIGNALS: + void thicknessChanged(); + void edgesChanged(); + void activeColorChanged(); + void inactiveColorChanged(); + void nativeBorderChanged(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickWindowBorder)) +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickWindowBorder)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/FramelessDialog b/FramelessHelper/include/FramelessHelper/Widgets/FramelessDialog new file mode 100644 index 0000000..1291fea --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/FramelessDialog @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Widgets/FramelessMainWindow b/FramelessHelper/include/FramelessHelper/Widgets/FramelessMainWindow new file mode 100644 index 0000000..d915e06 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/FramelessMainWindow @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Widgets/FramelessWidget b/FramelessHelper/include/FramelessHelper/Widgets/FramelessWidget new file mode 100644 index 0000000..81513ae --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/FramelessWidget @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Widgets/FramelessWidgetsHelper b/FramelessHelper/include/FramelessHelper/Widgets/FramelessWidgetsHelper new file mode 100644 index 0000000..51467e8 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/FramelessWidgetsHelper @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Widgets/Global b/FramelessHelper/include/FramelessHelper/Widgets/Global new file mode 100644 index 0000000..c5f0177 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/Global @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Widgets/StandardSystemButton b/FramelessHelper/include/FramelessHelper/Widgets/StandardSystemButton new file mode 100644 index 0000000..69ec38d --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/StandardSystemButton @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Widgets/StandardTitleBar b/FramelessHelper/include/FramelessHelper/Widgets/StandardTitleBar new file mode 100644 index 0000000..4b8ff1e --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/StandardTitleBar @@ -0,0 +1 @@ +#include diff --git a/FramelessHelper/include/FramelessHelper/Widgets/framelessdialog.h b/FramelessHelper/include/FramelessHelper/Widgets/framelessdialog.h new file mode 100644 index 0000000..04a18c3 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/framelessdialog.h @@ -0,0 +1,52 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcFramelessDialog) + +class FramelessDialogPrivate; + +class FRAMELESSHELPER_WIDGETS_API FramelessDialog : public QDialog +{ + Q_OBJECT + Q_DECLARE_PRIVATE(FramelessDialog) + Q_DISABLE_COPY_MOVE(FramelessDialog) + +public: + explicit FramelessDialog(QWidget *parent = nullptr); + ~FramelessDialog() override; + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessDialog)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/framelesshelperwidgets_global.h b/FramelessHelper/include/FramelessHelper/Widgets/framelesshelperwidgets_global.h new file mode 100644 index 0000000..2b0dddb --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/framelesshelperwidgets_global.h @@ -0,0 +1,51 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 + +#ifndef FRAMELESSHELPER_WIDGETS_API +# ifdef FRAMELESSHELPER_WIDGETS_STATIC +# define FRAMELESSHELPER_WIDGETS_API +# else +# ifdef FRAMELESSHELPER_WIDGETS_LIBRARY +# define FRAMELESSHELPER_WIDGETS_API Q_DECL_EXPORT +# else +# define FRAMELESSHELPER_WIDGETS_API Q_DECL_IMPORT +# endif +# endif +#endif + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcWidgetsGlobal) + +namespace FramelessHelper::Widgets +{ +FRAMELESSHELPER_WIDGETS_API void initialize(); +FRAMELESSHELPER_WIDGETS_API void uninitialize(); +} // namespace FramelessHelper::Widgets + +FRAMELESSHELPER_END_NAMESPACE diff --git a/FramelessHelper/include/FramelessHelper/Widgets/framelessmainwindow.h b/FramelessHelper/include/FramelessHelper/Widgets/framelessmainwindow.h new file mode 100644 index 0000000..b27a3b9 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/framelessmainwindow.h @@ -0,0 +1,67 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcFramelessMainWindow) + +class FramelessMainWindowPrivate; + +class FRAMELESSHELPER_WIDGETS_API FramelessMainWindow : public QMainWindow +{ + Q_OBJECT + Q_DECLARE_PRIVATE(FramelessMainWindow) + Q_DISABLE_COPY_MOVE(FramelessMainWindow) + Q_PROPERTY(bool hidden READ isHidden NOTIFY hiddenChanged FINAL) + Q_PROPERTY(bool normal READ isNormal NOTIFY normalChanged FINAL) + Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL) + +public: + explicit FramelessMainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {}); + ~FramelessMainWindow() override; + + Q_NODISCARD bool isNormal() const; + Q_NODISCARD bool isZoomed() const; + +public Q_SLOTS: + void toggleMaximized(); + void toggleFullScreen(); + +Q_SIGNALS: + void hiddenChanged(); + void normalChanged(); + void zoomedChanged(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessMainWindow)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/framelesswidget.h b/FramelessHelper/include/FramelessHelper/Widgets/framelesswidget.h new file mode 100644 index 0000000..c299e3d --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/framelesswidget.h @@ -0,0 +1,67 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcFramelessWidget) + +class FramelessWidgetPrivate; + +class FRAMELESSHELPER_WIDGETS_API FramelessWidget : public QWidget +{ + Q_OBJECT + Q_DECLARE_PRIVATE(FramelessWidget) + Q_DISABLE_COPY_MOVE(FramelessWidget) + Q_PROPERTY(bool hidden READ isHidden NOTIFY hiddenChanged FINAL) + Q_PROPERTY(bool normal READ isNormal NOTIFY normalChanged FINAL) + Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL) + +public: + explicit FramelessWidget(QWidget *parent = nullptr); + ~FramelessWidget() override; + + Q_NODISCARD bool isNormal() const; + Q_NODISCARD bool isZoomed() const; + +public Q_SLOTS: + void toggleMaximized(); + void toggleFullScreen(); + +Q_SIGNALS: + void hiddenChanged(); + void normalChanged(); + void zoomedChanged(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/framelesswidgetshelper.h b/FramelessHelper/include/FramelessHelper/Widgets/framelesswidgetshelper.h new file mode 100644 index 0000000..d5b1ddc --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/framelesswidgetshelper.h @@ -0,0 +1,96 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcFramelessWidgetsHelper) + +class FramelessWidgetsHelperPrivate; +class MicaMaterial; +class WindowBorderPainter; + +class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelper : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(FramelessWidgetsHelper) + Q_DISABLE_COPY_MOVE(FramelessWidgetsHelper) + Q_PROPERTY(QWidget* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL) + Q_PROPERTY(bool windowFixedSize READ isWindowFixedSize WRITE setWindowFixedSize NOTIFY windowFixedSizeChanged FINAL) + Q_PROPERTY(bool blurBehindWindowEnabled READ isBlurBehindWindowEnabled WRITE setBlurBehindWindowEnabled NOTIFY blurBehindWindowEnabledChanged FINAL) + Q_PROPERTY(QWidget* window READ window NOTIFY windowChanged FINAL) + Q_PROPERTY(bool extendsContentIntoTitleBar READ isContentExtendedIntoTitleBar WRITE extendsContentIntoTitleBar NOTIFY extendsContentIntoTitleBarChanged FINAL) + +public: + explicit FramelessWidgetsHelper(QObject *parent = nullptr); + ~FramelessWidgetsHelper() override; + + Q_NODISCARD static FramelessWidgetsHelper *get(QObject *object); + + Q_NODISCARD QWidget *titleBarWidget() const; + Q_NODISCARD bool isWindowFixedSize() const; + Q_NODISCARD bool isBlurBehindWindowEnabled() const; + Q_NODISCARD QWidget *window() const; + Q_NODISCARD bool isContentExtendedIntoTitleBar() const; + + Q_NODISCARD MicaMaterial *micaMaterial() const; + Q_NODISCARD WindowBorderPainter *windowBorder() const; + +public Q_SLOTS: + void extendsContentIntoTitleBar(const bool value = true); + + void setTitleBarWidget(QWidget *widget); + void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType); + void setHitTestVisible(QWidget *widget, const bool visible = true); + void setHitTestVisible(const QRect &rect, const bool visible = true); + void setHitTestVisible(QObject *object, const bool visible = true); + + void showSystemMenu(const QPoint &pos); + void windowStartSystemMove2(const QPoint &pos); + void windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos); + + void moveWindowToDesktopCenter(); + void bringWindowToFront(); + void setWindowFixedSize(const bool value); + void setBlurBehindWindowEnabled(const bool value); + +Q_SIGNALS: + void extendsContentIntoTitleBarChanged(); + void titleBarWidgetChanged(); + void windowFixedSizeChanged(); + void blurBehindWindowEnabledChanged(); + void windowChanged(); + void ready(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidgetsHelper)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/private/framelessdialog_p.h b/FramelessHelper/include/FramelessHelper/Widgets/private/framelessdialog_p.h new file mode 100644 index 0000000..bb9ec1d --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/private/framelessdialog_p.h @@ -0,0 +1,59 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FramelessDialog; +class WidgetsSharedHelper; + +class FRAMELESSHELPER_WIDGETS_API FramelessDialogPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(FramelessDialog) + Q_DISABLE_COPY_MOVE(FramelessDialogPrivate) + +public: + explicit FramelessDialogPrivate(FramelessDialog *q); + ~FramelessDialogPrivate() override; + + Q_NODISCARD static FramelessDialogPrivate *get(FramelessDialog *pub); + Q_NODISCARD static const FramelessDialogPrivate *get(const FramelessDialog *pub); + + Q_NODISCARD WidgetsSharedHelper *widgetsSharedHelper() const; + +private: + void initialize(); + +private: + FramelessDialog *q_ptr = nullptr; + WidgetsSharedHelper *m_sharedHelper = nullptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessDialogPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/private/framelessmainwindow_p.h b/FramelessHelper/include/FramelessHelper/Widgets/private/framelessmainwindow_p.h new file mode 100644 index 0000000..95a45b0 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/private/framelessmainwindow_p.h @@ -0,0 +1,66 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FramelessMainWindow; +class WidgetsSharedHelper; + +class FRAMELESSHELPER_WIDGETS_API FramelessMainWindowPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(FramelessMainWindow) + Q_DISABLE_COPY_MOVE(FramelessMainWindowPrivate) + +public: + explicit FramelessMainWindowPrivate(FramelessMainWindow *q); + ~FramelessMainWindowPrivate() override; + + Q_NODISCARD static FramelessMainWindowPrivate *get(FramelessMainWindow *pub); + Q_NODISCARD static const FramelessMainWindowPrivate *get(const FramelessMainWindow *pub); + + Q_NODISCARD bool isNormal() const; + Q_NODISCARD bool isZoomed() const; + + void toggleMaximized(); + void toggleFullScreen(); + + Q_NODISCARD WidgetsSharedHelper *widgetsSharedHelper() const; + +private: + void initialize(); + +private: + FramelessMainWindow *q_ptr = nullptr; + Qt::WindowState m_savedWindowState = Qt::WindowNoState; + WidgetsSharedHelper *m_sharedHelper = nullptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessMainWindowPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/private/framelesswidget_p.h b/FramelessHelper/include/FramelessHelper/Widgets/private/framelesswidget_p.h new file mode 100644 index 0000000..a0babef --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/private/framelesswidget_p.h @@ -0,0 +1,66 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FramelessWidget; +class WidgetsSharedHelper; + +class FRAMELESSHELPER_WIDGETS_API FramelessWidgetPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(FramelessWidget) + Q_DISABLE_COPY_MOVE(FramelessWidgetPrivate) + +public: + explicit FramelessWidgetPrivate(FramelessWidget *q); + ~FramelessWidgetPrivate() override; + + Q_NODISCARD static FramelessWidgetPrivate *get(FramelessWidget *pub); + Q_NODISCARD static const FramelessWidgetPrivate *get(const FramelessWidget *pub); + + Q_NODISCARD bool isNormal() const; + Q_NODISCARD bool isZoomed() const; + + void toggleMaximized(); + void toggleFullScreen(); + + Q_NODISCARD WidgetsSharedHelper *widgetsSharedHelper() const; + +private: + void initialize(); + +private: + FramelessWidget *q_ptr = nullptr; + Qt::WindowState m_savedWindowState = Qt::WindowNoState; + WidgetsSharedHelper *m_sharedHelper = nullptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidgetPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h b/FramelessHelper/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h new file mode 100644 index 0000000..2170784 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h @@ -0,0 +1,109 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FramelessWidgetsHelper; +struct WidgetsHelperData; +class WidgetsSharedHelper; +class MicaMaterial; +class WindowBorderPainter; + +class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelperPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(FramelessWidgetsHelper) + Q_DISABLE_COPY_MOVE(FramelessWidgetsHelperPrivate) + +public: + explicit FramelessWidgetsHelperPrivate(FramelessWidgetsHelper *q); + ~FramelessWidgetsHelperPrivate() override; + + Q_NODISCARD static FramelessWidgetsHelperPrivate *get(FramelessWidgetsHelper *pub); + Q_NODISCARD static const FramelessWidgetsHelperPrivate *get(const FramelessWidgetsHelper *pub); + + Q_NODISCARD bool isContentExtendedIntoTitleBar() const; + void extendsContentIntoTitleBar(const bool value); + + Q_NODISCARD QWidget *getTitleBarWidget() const; + void setTitleBarWidget(QWidget *widget); + + void attach(); + void detach(); + void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType); + void setHitTestVisible(QWidget *widget, const bool visible = true); + void setHitTestVisible(const QRect &rect, const bool visible = true); + void setHitTestVisible(QObject *object, const bool visible = true); + void showSystemMenu(const QPoint &pos); + void windowStartSystemMove2(const QPoint &pos); + void windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos); + + void moveWindowToDesktopCenter(); + void bringWindowToFront(); + + Q_NODISCARD bool isWindowFixedSize() const; + void setWindowFixedSize(const bool value); + + void emitSignalForAllInstances(const QByteArray &signal); + + Q_NODISCARD bool isBlurBehindWindowEnabled() const; + void setBlurBehindWindowEnabled(const bool enable, const QColor &color); + + void setProperty(const QByteArray &name, const QVariant &value); + Q_NODISCARD QVariant getProperty(const QByteArray &name, const QVariant &defaultValue = {}); + + Q_NODISCARD QWidget *window() const; + + Q_NODISCARD MicaMaterial *getMicaMaterialIfAny() const; + Q_NODISCARD WindowBorderPainter *getWindowBorderIfAny() const; + + Q_NODISCARD static WidgetsSharedHelper *findOrCreateSharedHelper(QWidget *window); + Q_NODISCARD static FramelessWidgetsHelper *findOrCreateFramelessHelper(QObject *object); + +private: + Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const; + Q_NODISCARD bool isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const; + Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const; + Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const; + void setSystemButtonState(const Global::SystemButtonType button, const Global::ButtonState state); + Q_NODISCARD QWidget *findTopLevelWindow() const; + Q_NODISCARD WidgetsHelperData getWindowData() const; + Q_NODISCARD WidgetsHelperData *getWindowDataMutable() const; + +private: + FramelessWidgetsHelper *q_ptr = nullptr; + QColor m_savedWindowBackgroundColor = {}; + bool m_blurBehindWindowEnabled = false; + QPointer m_window = nullptr; + bool m_destroying = false; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidgetsHelperPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/private/standardsystembutton_p.h b/FramelessHelper/include/FramelessHelper/Widgets/private/standardsystembutton_p.h new file mode 100644 index 0000000..c1d09c7 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/private/standardsystembutton_p.h @@ -0,0 +1,103 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" + +QT_BEGIN_NAMESPACE +class QEnterEvent; +class QPaintEvent; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class StandardSystemButton; + +class FRAMELESSHELPER_WIDGETS_API StandardSystemButtonPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(StandardSystemButton) + Q_DISABLE_COPY_MOVE(StandardSystemButtonPrivate) + +public: + explicit StandardSystemButtonPrivate(StandardSystemButton *q); + ~StandardSystemButtonPrivate() override; + + Q_NODISCARD static StandardSystemButtonPrivate *get(StandardSystemButton *pub); + Q_NODISCARD static const StandardSystemButtonPrivate *get(const StandardSystemButton *pub); + + Q_NODISCARD QString getGlyph() const; + void setGlyph(const QString &value); + + Q_NODISCARD Global::SystemButtonType getButtonType() const; + void setButtonType(const Global::SystemButtonType type); + + Q_NODISCARD QSize getRecommendedButtonSize() const; + + Q_NODISCARD bool isHovered() const; + Q_NODISCARD bool isPressed() const; + Q_NODISCARD QColor getHoverColor() const; + Q_NODISCARD QColor getPressColor() const; + Q_NODISCARD QColor getNormalColor() const; + Q_NODISCARD QColor getActiveForegroundColor() const; + Q_NODISCARD QColor getInactiveForegroundColor() const; + Q_NODISCARD bool isActive() const; + Q_NODISCARD int iconSize2() const; + + void setHovered(const bool value); + void setPressed(const bool value); + void setHoverColor(const QColor &value); + void setPressColor(const QColor &value); + void setNormalColor(const QColor &value); + void setActiveForegroundColor(const QColor &value); + void setInactiveForegroundColor(const QColor &value); + void setActive(const bool value); + void setIconSize2(const int value); + + void enterEventHandler(QT_ENTER_EVENT_TYPE *event); + void leaveEventHandler(QEvent *event); + void paintEventHandler(QPaintEvent *event); + +private: + void initialize(); + +private: + StandardSystemButton *q_ptr = nullptr; + Global::SystemButtonType m_buttonType = Global::SystemButtonType::Unknown; + QString m_glyph = {}; + QColor m_hoverColor = {}; + QColor m_pressColor = {}; + QColor m_normalColor = {}; + QColor m_activeForegroundColor = {}; + QColor m_inactiveForegroundColor = {}; + bool m_hovered = false; + bool m_pressed = false; + bool m_active = false; + std::optional m_iconSize2 = std::nullopt; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(StandardSystemButtonPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/private/standardtitlebar_p.h b/FramelessHelper/include/FramelessHelper/Widgets/private/standardtitlebar_p.h new file mode 100644 index 0000000..f9dce4c --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/private/standardtitlebar_p.h @@ -0,0 +1,128 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" +#include + +QT_BEGIN_NAMESPACE +class QPaintEvent; +class QMouseEvent; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class StandardTitleBar; +class StandardSystemButton; +class ChromePalette; + +class FRAMELESSHELPER_WIDGETS_API StandardTitleBarPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(StandardTitleBar) + Q_DISABLE_COPY_MOVE(StandardTitleBarPrivate) + +public: + struct FontMetrics + { + int width = 0; + int height = 0; + int ascent = 0; + }; + + explicit StandardTitleBarPrivate(StandardTitleBar *q); + ~StandardTitleBarPrivate() override; + + Q_NODISCARD static StandardTitleBarPrivate *get(StandardTitleBar *pub); + Q_NODISCARD static const StandardTitleBarPrivate *get(const StandardTitleBar *pub); + + Q_NODISCARD Qt::Alignment titleLabelAlignment() const; + void setTitleLabelAlignment(const Qt::Alignment value); + + Q_NODISCARD bool isExtended() const; + void setExtended(const bool value); + + Q_NODISCARD bool isHideWhenClose() const; + void setHideWhenClose(const bool value); + + Q_NODISCARD ChromePalette *chromePalette() const; + + void paintTitleBar(QPaintEvent *event); + + Q_NODISCARD bool titleLabelVisible() const; + void setTitleLabelVisible(const bool value); + + Q_NODISCARD QSize windowIconSize() const; + void setWindowIconSize(const QSize &value); + + Q_NODISCARD bool windowIconVisible() const; + void setWindowIconVisible(const bool value); + + Q_NODISCARD QFont titleFont() const; + void setTitleFont(const QFont &value); + + Q_NODISCARD bool mouseEventHandler(QMouseEvent *event); + + Q_NODISCARD QRect windowIconRect() const; + Q_NODISCARD bool windowIconVisible_real() const; + Q_NODISCARD bool isInTitleBarIconArea(const QPoint &pos) const; + + Q_NODISCARD QFont defaultFont() const; + Q_NODISCARD FontMetrics titleLabelSize() const; + +public Q_SLOTS: + void updateMaximizeButton(); + void updateTitleBarColor(); + void updateChromeButtonColor(); + void retranslateUi(); + +protected: + Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; + +private: + void initialize(); + +private: + StandardTitleBar *q_ptr = nullptr; +#ifndef Q_OS_MACOS + StandardSystemButton *m_minimizeButton = nullptr; + StandardSystemButton *m_maximizeButton = nullptr; + StandardSystemButton *m_closeButton = nullptr; +#endif // Q_OS_MACOS + QPointer m_window = nullptr; + bool m_extended = false; + Qt::Alignment m_labelAlignment = {}; + bool m_hideWhenClose = false; + ChromePalette *m_chromePalette = nullptr; + bool m_titleLabelVisible = true; + std::optional m_windowIconSize = std::nullopt; + bool m_windowIconVisible = false; + std::optional m_titleFont = std::nullopt; + bool m_closeTriggered = false; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBarPrivate)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/private/widgetssharedhelper_p.h b/FramelessHelper/include/FramelessHelper/Widgets/private/widgetssharedhelper_p.h new file mode 100644 index 0000000..d5c2b9a --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/private/widgetssharedhelper_p.h @@ -0,0 +1,93 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" +#include + +QT_BEGIN_NAMESPACE +class QPaintEvent; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class MicaMaterial; +class WindowBorderPainter; + +class FRAMELESSHELPER_WIDGETS_API WidgetsSharedHelper : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(WidgetsSharedHelper) + Q_PROPERTY(bool micaEnabled READ isMicaEnabled WRITE setMicaEnabled NOTIFY micaEnabledChanged FINAL) + +public: + explicit WidgetsSharedHelper(QObject *parent = nullptr); + ~WidgetsSharedHelper() override; + + void setup(QWidget *widget); + + Q_NODISCARD bool isMicaEnabled() const; + void setMicaEnabled(const bool value); + + Q_NODISCARD MicaMaterial *rawMicaMaterial() const; + Q_NODISCARD WindowBorderPainter *rawWindowBorder() const; + +protected: + Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; + +private Q_SLOTS: + void updateContentsMargins(); + void handleScreenChanged(QScreen *screen); + +private: + void changeEventHandler(QEvent *event); + void paintEventHandler(QPaintEvent *event); + +Q_SIGNALS: + void micaEnabledChanged(); + +private: +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + // Due to a Qt bug, we can't initialize the QPointer objects with nullptr. + // The bug was fixed in Qt 5.15. + QPointer m_targetWidget; + QPointer m_screen; +#else + QPointer m_targetWidget = nullptr; + QPointer m_screen = nullptr; +#endif + bool m_micaEnabled = false; + MicaMaterial *m_micaMaterial = nullptr; + QMetaObject::Connection m_micaRedrawConnection = {}; + qreal m_screenDpr = 0.0; + QMetaObject::Connection m_screenDpiChangeConnection = {}; + WindowBorderPainter *m_borderPainter = nullptr; + QMetaObject::Connection m_borderRepaintConnection = {}; + QMetaObject::Connection m_screenChangeConnection = {}; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(WidgetsSharedHelper)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/standardsystembutton.h b/FramelessHelper/include/FramelessHelper/Widgets/standardsystembutton.h new file mode 100644 index 0000000..8347885 --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/standardsystembutton.h @@ -0,0 +1,108 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcStandardSystemButton) + +class StandardSystemButtonPrivate; + +class FRAMELESSHELPER_WIDGETS_API StandardSystemButton : public QAbstractButton +{ + Q_OBJECT + Q_DECLARE_PRIVATE(StandardSystemButton) + Q_DISABLE_COPY_MOVE(StandardSystemButton) + Q_PROPERTY(Global::SystemButtonType buttonType READ buttonType WRITE setButtonType NOTIFY buttonTypeChanged FINAL) + Q_PROPERTY(QString glyph READ glyph WRITE setGlyph NOTIFY glyphChanged FINAL) + Q_PROPERTY(bool hovered READ isHovered WRITE setHovered NOTIFY hoveredChanged FINAL) + Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL) + Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setHoverColor NOTIFY hoverColorChanged FINAL) + Q_PROPERTY(QColor pressColor READ pressColor WRITE setPressColor NOTIFY pressColorChanged FINAL) + Q_PROPERTY(QColor normalColor READ normalColor WRITE setNormalColor NOTIFY normalColorChanged FINAL) + Q_PROPERTY(QColor activeForegroundColor READ activeForegroundColor WRITE setActiveForegroundColor NOTIFY activeForegroundColorChanged FINAL) + Q_PROPERTY(QColor inactiveForegroundColor READ inactiveForegroundColor WRITE setInactiveForegroundColor NOTIFY inactiveForegroundColorChanged FINAL) + Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged FINAL) + Q_PROPERTY(int iconSize2 READ iconSize2 WRITE setIconSize2 NOTIFY iconSize2Changed FINAL) + +public: + explicit StandardSystemButton(QWidget *parent = nullptr); + explicit StandardSystemButton(const Global::SystemButtonType type, QWidget *parent = nullptr); + ~StandardSystemButton() override; + + Q_NODISCARD QSize sizeHint() const override; + Q_NODISCARD Global::SystemButtonType buttonType(); + Q_NODISCARD QString glyph() const; + Q_NODISCARD bool isHovered() const; + Q_NODISCARD bool isPressed() const; + Q_NODISCARD QColor hoverColor() const; + Q_NODISCARD QColor pressColor() const; + Q_NODISCARD QColor normalColor() const; + Q_NODISCARD QColor activeForegroundColor() const; + Q_NODISCARD QColor inactiveForegroundColor() const; + Q_NODISCARD bool isActive() const; + Q_NODISCARD int iconSize2() const; + +public Q_SLOTS: + void setButtonType(const Global::SystemButtonType value); + void setGlyph(const QString &glyph); + void setHovered(const bool value); + void setPressed(const bool value); + void setHoverColor(const QColor &value); + void setPressColor(const QColor &value); + void setNormalColor(const QColor &value); + void setActiveForegroundColor(const QColor &value); + void setInactiveForegroundColor(const QColor &value); + void setActive(const bool value); + void setIconSize2(const int value); + +protected: + void enterEvent(QT_ENTER_EVENT_TYPE *event) override; + void leaveEvent(QEvent *event) override; + void paintEvent(QPaintEvent *event) override; + +Q_SIGNALS: + void buttonTypeChanged(); + void glyphChanged(); + void hoveredChanged(); + void pressedChanged(); + void hoverColorChanged(); + void pressColorChanged(); + void normalColorChanged(); + void activeForegroundColorChanged(); + void inactiveForegroundColorChanged(); + void activeChanged(); + void iconSize2Changed(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(StandardSystemButton)) diff --git a/FramelessHelper/include/FramelessHelper/Widgets/standardtitlebar.h b/FramelessHelper/include/FramelessHelper/Widgets/standardtitlebar.h new file mode 100644 index 0000000..e60287b --- /dev/null +++ b/FramelessHelper/include/FramelessHelper/Widgets/standardtitlebar.h @@ -0,0 +1,110 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelperwidgets_global.h" +#include "standardsystembutton.h" +#include +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcStandardTitleBar) + +class StandardTitleBarPrivate; + +class FRAMELESSHELPER_WIDGETS_API StandardTitleBar : public QWidget +{ + Q_OBJECT + Q_DECLARE_PRIVATE(StandardTitleBar) + Q_DISABLE_COPY_MOVE(StandardTitleBar) + Q_PROPERTY(Qt::Alignment titleLabelAlignment READ titleLabelAlignment WRITE setTitleLabelAlignment NOTIFY titleLabelAlignmentChanged FINAL) +#ifndef Q_OS_MACOS + Q_PROPERTY(StandardSystemButton* minimizeButton READ minimizeButton CONSTANT FINAL) + Q_PROPERTY(StandardSystemButton* maximizeButton READ maximizeButton CONSTANT FINAL) + Q_PROPERTY(StandardSystemButton* closeButton READ closeButton CONSTANT FINAL) +#endif // Q_OS_MACOS + Q_PROPERTY(bool extended READ isExtended WRITE setExtended NOTIFY extendedChanged FINAL) + Q_PROPERTY(bool hideWhenClose READ isHideWhenClose WRITE setHideWhenClose NOTIFY hideWhenCloseChanged FINAL) + Q_PROPERTY(ChromePalette* chromePalette READ chromePalette CONSTANT FINAL) + Q_PROPERTY(bool titleLabelVisible READ titleLabelVisible WRITE setTitleLabelVisible NOTIFY titleLabelVisibleChanged FINAL) + Q_PROPERTY(QSize windowIconSize READ windowIconSize WRITE setWindowIconSize NOTIFY windowIconSizeChanged FINAL) + Q_PROPERTY(bool windowIconVisible READ windowIconVisible WRITE setWindowIconVisible NOTIFY windowIconVisibleChanged FINAL) + Q_PROPERTY(QFont titleFont READ titleFont WRITE setTitleFont NOTIFY titleFontChanged FINAL) + +public: + explicit StandardTitleBar(QWidget *parent = nullptr); + ~StandardTitleBar() override; + + Q_NODISCARD Qt::Alignment titleLabelAlignment() const; + void setTitleLabelAlignment(const Qt::Alignment value); + +#ifndef Q_OS_MACOS + Q_NODISCARD StandardSystemButton *minimizeButton() const; + Q_NODISCARD StandardSystemButton *maximizeButton() const; + Q_NODISCARD StandardSystemButton *closeButton() const; +#endif // Q_OS_MACOS + + Q_NODISCARD bool isExtended() const; + void setExtended(const bool value); + + Q_NODISCARD bool isHideWhenClose() const; + void setHideWhenClose(const bool value); + + Q_NODISCARD ChromePalette *chromePalette() const; + + Q_NODISCARD bool titleLabelVisible() const; + void setTitleLabelVisible(const bool value); + + Q_NODISCARD QSize windowIconSize() const; + void setWindowIconSize(const QSize &value); + + Q_NODISCARD bool windowIconVisible() const; + void setWindowIconVisible(const bool value); + + Q_NODISCARD QFont titleFont() const; + void setTitleFont(const QFont &value); + +protected: + void paintEvent(QPaintEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + +Q_SIGNALS: + void extendedChanged(); + void titleLabelAlignmentChanged(); + void hideWhenCloseChanged(); + void titleLabelVisibleChanged(); + void windowIconSizeChanged(); + void windowIconVisibleChanged(); + void titleFontChanged(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar)) diff --git a/QGoodWindow/.qmake.stash b/FramelessHelper/qmake/.qmake.stash similarity index 74% rename from QGoodWindow/.qmake.stash rename to FramelessHelper/qmake/.qmake.stash index 8525508..6978fc8 100644 --- a/QGoodWindow/.qmake.stash +++ b/FramelessHelper/qmake/.qmake.stash @@ -1,22 +1,23 @@ QMAKE_CXX.QT_COMPILER_STDCXX = 199711L -QMAKE_CXX.QMAKE_MSC_VER = 1932 -QMAKE_CXX.QMAKE_MSC_FULL_VER = 193231329 +QMAKE_CXX.QMAKE_MSC_VER = 1933 +QMAKE_CXX.QMAKE_MSC_FULL_VER = 193331630 QMAKE_CXX.COMPILER_MACROS = \ QT_COMPILER_STDCXX \ QMAKE_MSC_VER \ QMAKE_MSC_FULL_VER QMAKE_CXX.INCDIRS = \ - "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.32.31326\\ATLMFC\\include" \ - "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.32.31326\\include" \ - "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um" \ + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.33.31629\\include" \ + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.33.31629\\ATLMFC\\include" \ + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\VS\\include" \ "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.19041.0\\ucrt" \ - "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.19041.0\\\\shared" \ "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.19041.0\\\\um" \ + "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.19041.0\\\\shared" \ "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.19041.0\\\\winrt" \ - "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.19041.0\\\\cppwinrt" + "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.19041.0\\\\cppwinrt" \ + "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um" QMAKE_CXX.LIBDIRS = \ - "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.32.31326\\ATLMFC\\lib\\x64" \ - "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.32.31326\\lib\\x64" \ + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.33.31629\\ATLMFC\\lib\\x64" \ + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.33.31629\\lib\\x64" \ "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\lib\\um\\x64" \ "C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.19041.0\\ucrt\\x64" \ "C:\\Program Files (x86)\\Windows Kits\\10\\\\lib\\10.0.19041.0\\\\um\\x64" diff --git a/FramelessHelper/qmake/FramelessHelperCore.pro b/FramelessHelper/qmake/FramelessHelperCore.pro new file mode 100644 index 0000000..c91662e --- /dev/null +++ b/FramelessHelper/qmake/FramelessHelperCore.pro @@ -0,0 +1,80 @@ +QT += core core-private gui gui-private + +CORE_PUB_INC_DIR = $$PWD/../include/FramelessHelper/Core +CORE_PRIV_INC_DIR = $$CORE_PUB_INC_DIR/private +CORE_EXTRA_INC_DIR = $$PWD/inc/core +CORE_SRC_DIR = $$PWD/../src/core + +DEFINES += \ + FRAMELESSHELPER_CORE_STATIC + +INCLUDEPATH += \ + $$CORE_PUB_INC_DIR \ + $$CORE_PRIV_INC_DIR \ + $$CORE_EXTRA_INC_DIR + +DEPENDPATH += \ + $$CORE_PUB_INC_DIR \ + $$CORE_PRIV_INC_DIR \ + $$CORE_EXTRA_INC_DIR + +HEADERS += \ + $$CORE_EXTRA_INC_DIR/framelesshelper.version \ + $$CORE_PUB_INC_DIR/chromepalette.h \ + $$CORE_PUB_INC_DIR/framelesshelper_qt.h \ + $$CORE_PUB_INC_DIR/framelesshelpercore_global.h \ + $$CORE_PUB_INC_DIR/framelessmanager.h \ + $$CORE_PUB_INC_DIR/micamaterial.h \ + $$CORE_PUB_INC_DIR/utils.h \ + $$CORE_PUB_INC_DIR/windowborderpainter.h \ + $$CORE_PRIV_INC_DIR/chromepalette_p.h \ + $$CORE_PRIV_INC_DIR/framelessconfig_p.h \ + $$CORE_PRIV_INC_DIR/framelessmanager_p.h \ + $$CORE_PRIV_INC_DIR/micamaterial_p.h \ + $$CORE_PRIV_INC_DIR/sysapiloader_p.h \ + $$CORE_PRIV_INC_DIR/windowborderpainter_p.h + +SOURCES += \ + $$CORE_SRC_DIR/chromepalette.cpp \ + $$CORE_SRC_DIR/framelessconfig.cpp \ + $$CORE_SRC_DIR/framelesshelper_qt.cpp \ + $$CORE_SRC_DIR/framelessmanager.cpp \ + $$CORE_SRC_DIR/framelesshelpercore_global.cpp \ + $$CORE_SRC_DIR/micamaterial.cpp \ + $$CORE_SRC_DIR/sysapiloader.cpp \ + $$CORE_SRC_DIR/utils.cpp \ + $$CORE_SRC_DIR/windowborderpainter.cpp + +RESOURCES += \ + $$CORE_SRC_DIR/framelesshelpercore.qrc + +win32 { + HEADERS += \ + $$CORE_PUB_INC_DIR/framelesshelper_win.h \ + $$CORE_PUB_INC_DIR/framelesshelper_windows.h \ + $$CORE_PRIV_INC_DIR/registrykey_p.h \ + $$CORE_PRIV_INC_DIR/winverhelper_p.h + SOURCES += \ + $$CORE_SRC_DIR/framelesshelper_win.cpp \ + $$CORE_SRC_DIR/utils_win.cpp \ + $$CORE_SRC_DIR/registrykey.cpp \ + $$CORE_SRC_DIR/winverhelper.cpp \ + $$CORE_SRC_DIR/platformsupport_win.cpp + LIBS += -luser32 -lgdi32 -lshell32 +} + +unix:!macx { + CONFIG += link_pkgconfig + PKGCONFIG += gtk+-3.0 + DEFINES += GDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_6 + HEADERS += \ + $$CORE_PUB_INC_DIR/framelesshelper_linux.h + SOURCES += \ + $$CORE_SRC_DIR/utils_linux.cpp \ + $$CORE_SRC_DIR/platformsupport_linux.cpp +} + +macx { + SOURCES += $$CORE_SRC_DIR/utils_mac.mm + #LIBS += "-framework AppKit" "-framework Cocoa" "-framework Foundation" # TODO +} diff --git a/FramelessHelper/qmake/FramelessHelperCore.vcxproj b/FramelessHelper/qmake/FramelessHelperCore.vcxproj new file mode 100644 index 0000000..699fe08 --- /dev/null +++ b/FramelessHelper/qmake/FramelessHelperCore.vcxproj @@ -0,0 +1,272 @@ +锘 + + + + Release + x64 + + + Debug + x64 + + + + {F1BC586E-F1ED-320C-899E-76CCB5660AE1} + FramelessHelperCore + QtVS_v304 + 10.0.19041.0 + 10.0.19041.0 + $(MSBuildProjectDirectory)\QtMsBuild + + + + v142 + release\ + false + NotSet + Application + release\ + FramelessHelperCore + + + v143 + debug\ + false + NotSet + StaticLibrary + debug\ + FramelessHelperCore + + + + + + + + + + + + + + + + + + debug\ + debug\ + FramelessHelperCore + true + + + release\ + release\ + FramelessHelperCore + true + false + + + 5.15.2_msvc2019_64 + core;core-private;gui;gui-private + + + 5.15.2_msvc2019_64 + core;core-private;gui;gui-private + + + + + + + GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;..\include\FramelessHelper\Core;..\include\FramelessHelper\Core\private;inc\core;release;E:\VulkanSDK\1.3.216.0\include;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + release\ + false + None + 4577;4467;%(DisableSpecificWarnings) + Sync + release\ + MaxSpeed + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;FRAMELESSHELPER_CORE_STATIC;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + false + + + MultiThreadedDLL + true + true + Level3 + true + + + user32.lib;gdi32.lib;shell32.lib;shell32.lib;%(AdditionalDependencies) + C:\openssl\lib;C:\Utils\my_sql\mysql-5.7.25-winx64\lib;C:\Utils\postgresql\pgsql\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + false + true + false + true + $(OutDir)\FramelessHelperCore.exe + true + Windows + true + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;FRAMELESSHELPER_CORE_STATIC;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + + + msvc + D:/??2022/ArchitectureColoredPainting/FramelessHelper/qmake/$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + framelesshelpercore + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + + + GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;..\include\FramelessHelper\Core;..\include\FramelessHelper\Core\private;inc\core;debug;E:\VulkanSDK\1.3.216.0\include;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + debug\ + false + ProgramDatabase + 4577;4467;%(DisableSpecificWarnings) + Sync + debug\ + Disabled + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;FRAMELESSHELPER_CORE_STATIC;QT_NO_CAST_TO_ASCII;QT_NO_CAST_FROM_ASCII;QT_NO_CAST_FROM_BYTEARRAY;QT_NO_URL_CAST_FROM_STRING;QT_NO_NARROWING_CONVERSIONS_IN_CONNECT;QT_NO_FOREACH;QT_USE_QSTRINGBUILDER;QT_USE_FAST_OPERATOR_PLUS;QT_DEPRECATED_WARNINGS;QT_DEPRECATED_WARNINGS_SINCE=0x070000;QT_WARN_DEPRECATED_UP_TO=0x070000;QT_DISABLE_DEPRECATED_BEFORE=0x070000;QT_DISABLE_DEPRECATED_UP_TO=0x070000;QT_NO_KEYWORDS;%(PreprocessorDefinitions) + false + MultiThreadedDebugDLL + true + true + Level3 + true + stdcpp17 + + + user32.lib;gdi32.lib;shell32.lib;shell32.lib;%(AdditionalDependencies) + C:\openssl\lib;C:\Utils\my_sql\mysql-5.7.25-winx64\lib;C:\Utils\postgresql\pgsql\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + true + true + $(OutDir)\FramelessHelperCore.exe + true + Windows + true + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;FRAMELESSHELPER_CORE_STATIC;_DEBUG;%(PreprocessorDefinitions) + + + framelesshelpercore + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + msvc + D:/??2022/ArchitectureColoredPainting/FramelessHelper/qmake/$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + true + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h + Generate moc_predefs.h + debug\moc_predefs.h;%(Outputs) + + + Document + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >release\moc_predefs.h + Generate moc_predefs.h + release\moc_predefs.h;%(Outputs) + true + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FramelessHelper/qmake/FramelessHelperCore.vcxproj.filters b/FramelessHelper/qmake/FramelessHelperCore.vcxproj.filters new file mode 100644 index 0000000..6a1d510 --- /dev/null +++ b/FramelessHelper/qmake/FramelessHelperCore.vcxproj.filters @@ -0,0 +1,186 @@ +锘 + + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + + + + + + + + + + + + + + + + + + Generated Files + + + Generated Files + + + + + + + + + + + + + + + Resource Files + + + Resource Files + + + Resource Files + + + \ No newline at end of file diff --git a/FramelessHelper/qmake/FramelessHelperWidgets.pro b/FramelessHelper/qmake/FramelessHelperWidgets.pro new file mode 100644 index 0000000..0f1e999 --- /dev/null +++ b/FramelessHelper/qmake/FramelessHelperWidgets.pro @@ -0,0 +1,42 @@ +QT += widgets widgets-private + +WIDGETS_PUB_INC_DIR = $$PWD/../include/FramelessHelper/Widgets +WIDGETS_PRIV_INC_DIR = $$WIDGETS_PUB_INC_DIR/private +WIDGETS_SRC_DIR = $$PWD/../src/widgets + +DEFINES += \ + FRAMELESSHELPER_WIDGETS_STATIC + +INCLUDEPATH += \ + $$WIDGETS_PUB_INC_DIR \ + $$WIDGETS_PRIV_INC_DIR + +DEPENDPATH += \ + $$WIDGETS_PUB_INC_DIR \ + $$WIDGETS_PRIV_INC_DIR + +HEADERS += \ + $$WIDGETS_PUB_INC_DIR/framelesshelperwidgets_global.h \ + $$WIDGETS_PUB_INC_DIR/framelesswidget.h \ + $$WIDGETS_PUB_INC_DIR/framelessmainwindow.h \ + $$WIDGETS_PUB_INC_DIR/standardsystembutton.h \ + $$WIDGETS_PUB_INC_DIR/framelesswidgetshelper.h \ + $$WIDGETS_PUB_INC_DIR/standardtitlebar.h \ + $$WIDGETS_PUB_INC_DIR/framelessdialog.h \ + $$WIDGETS_PRIV_INC_DIR/framelesswidgetshelper_p.h \ + $$WIDGETS_PRIV_INC_DIR/standardsystembutton_p.h \ + $$WIDGETS_PRIV_INC_DIR/standardtitlebar_p.h \ + $$WIDGETS_PRIV_INC_DIR/framelesswidget_p.h \ + $$WIDGETS_PRIV_INC_DIR/framelessmainwindow_p.h \ + $$WIDGETS_PRIV_INC_DIR/widgetssharedhelper_p.h \ + $$WIDGETS_PRIV_INC_DIR/framelessdialog_p.h + +SOURCES += \ + $$WIDGETS_SRC_DIR/framelessmainwindow.cpp \ + $$WIDGETS_SRC_DIR/framelesswidgetshelper.cpp \ + $$WIDGETS_SRC_DIR/framelesswidget.cpp \ + $$WIDGETS_SRC_DIR/standardsystembutton.cpp \ + $$WIDGETS_SRC_DIR/standardtitlebar.cpp \ + $$WIDGETS_SRC_DIR/widgetssharedhelper.cpp \ + $$WIDGETS_SRC_DIR/framelesshelperwidgets_global.cpp \ + $$WIDGETS_SRC_DIR/framelessdialog.cpp diff --git a/QGoodWindow/QGoodWindow.vcxproj b/FramelessHelper/qmake/FramelessHelperWidgets.vcxproj similarity index 70% rename from QGoodWindow/QGoodWindow.vcxproj rename to FramelessHelper/qmake/FramelessHelperWidgets.vcxproj index 757ea42..1d7795e 100644 --- a/QGoodWindow/QGoodWindow.vcxproj +++ b/FramelessHelper/qmake/FramelessHelperWidgets.vcxproj @@ -11,22 +11,22 @@ - {B982E745-C0B1-46B3-A27B-743AF105F2D0} - QGoodWindow + {61651EC8-A30E-3935-BD0C-A01AE3EE0AD9} + FramelessHelperWidgets QtVS_v304 - 10.0 + 10.0.19041.0 10.0.19041.0 $(MSBuildProjectDirectory)\QtMsBuild - v143 + v142 release\ false NotSet - StaticLibrary + Application release\ - QGoodWindow + FramelessHelperWidgets v143 @@ -35,7 +35,7 @@ NotSet StaticLibrary debug\ - QGoodWindow + FramelessHelperWidgets @@ -55,30 +55,30 @@ debug\ debug\ - QGoodWindow + FramelessHelperWidgets true release\ release\ - QGoodWindow + FramelessHelperWidgets true false 5.15.2_msvc2019_64 - core;gui;widgets;winextras + core;core-private;gui;gui-private;widgets;widgets-private 5.15.2_msvc2019_64 - core;gui;widgets;winextras + core;core-private;gui;gui-private;widgets;widgets-private - GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;release;/include;%(AdditionalIncludeDirectories) + GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;..\include\FramelessHelper\Widgets;..\include\FramelessHelper\Widgets\private;release;E:\VulkanSDK\1.3.216.0\include;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false @@ -87,7 +87,7 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;QGOODWINDOW;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;FRAMELESSHELPER_WIDGETS_STATIC;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false @@ -98,7 +98,7 @@ true - User32.lib;Gdi32.lib;shell32.lib;%(AdditionalDependencies) + shell32.lib;%(AdditionalDependencies) C:\openssl\lib;C:\Utils\my_sql\mysql-5.7.25-winx64\lib;C:\Utils\postgresql\pgsql\lib;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true @@ -106,7 +106,7 @@ true false true - $(OutDir)\QGoodWindow.exe + $(OutDir)\FramelessHelperWidgets.exe true Windows true @@ -117,11 +117,11 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;QGOODWINDOW;NDEBUG;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_WINEXTRAS_LIB;QT_GUI_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;FRAMELESSHELPER_WIDGETS_STATIC;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) msvc - D:/??2022/ArchitectureColoredPainting/QGoodWindow/$(Configuration)/moc_predefs.h + ./$(Configuration)/moc_predefs.h Moc'ing %(Identity)... output $(Configuration) @@ -130,7 +130,7 @@ - GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;debug;/include;%(AdditionalIncludeDirectories) + ..\include\FramelessHelper\Widgets;..\include\FramelessHelper\Widgets\private;..\include\FramelessHelper\Core\..\..;..\include\FramelessHelper\Core;..\include\FramelessHelper\Core\private;inc\core;%(AdditionalIncludeDirectories) -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -139,22 +139,23 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;QGOODWINDOW;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;FRAMELESSHELPER_WIDGETS_STATIC;QT_NO_CAST_TO_ASCII;QT_NO_CAST_FROM_ASCII;QT_NO_CAST_FROM_BYTEARRAY;QT_NO_URL_CAST_FROM_STRING;QT_NO_NARROWING_CONVERSIONS_IN_CONNECT;QT_NO_FOREACH;QT_USE_QSTRINGBUILDER;QT_USE_FAST_OPERATOR_PLUS;QT_DEPRECATED_WARNINGS;QT_DEPRECATED_WARNINGS_SINCE=0x070000;QT_WARN_DEPRECATED_UP_TO=0x070000;QT_DISABLE_DEPRECATED_BEFORE=0x070000;QT_DISABLE_DEPRECATED_UP_TO=0x070000;QT_NO_KEYWORDS;%(PreprocessorDefinitions) false MultiThreadedDebugDLL true true Level3 true + stdcpp17 - User32.lib;Gdi32.lib;shell32.lib;%(AdditionalDependencies) + shell32.lib;%(AdditionalDependencies) C:\openssl\lib;C:\Utils\my_sql\mysql-5.7.25-winx64\lib;C:\Utils\postgresql\pgsql\lib;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true true true - $(OutDir)\QGoodWindow.exe + $(OutDir)\FramelessHelperWidgets.exe true Windows true @@ -165,11 +166,11 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;QGOODWINDOW;QT_WIDGETS_LIB;QT_WINEXTRAS_LIB;QT_GUI_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;FRAMELESSHELPER_WIDGETS_STATIC;_DEBUG;%(PreprocessorDefinitions) msvc - D:/??2022/ArchitectureColoredPainting/QGoodWindow/$(Configuration)/moc_predefs.h + ./$(Configuration)/moc_predefs.h Moc'ing %(Identity)... output $(Configuration) @@ -177,14 +178,42 @@ - - + + + + + + + + - - + - + + + + + + + + + + + + + + + + + + + + + + + + @@ -205,6 +234,11 @@ true + + + {f1bc586e-f1ed-320c-899e-76ccb5660ae1} + + diff --git a/FramelessHelper/qmake/FramelessHelperWidgets.vcxproj.filters b/FramelessHelper/qmake/FramelessHelperWidgets.vcxproj.filters new file mode 100644 index 0000000..428a464 --- /dev/null +++ b/FramelessHelper/qmake/FramelessHelperWidgets.vcxproj.filters @@ -0,0 +1,133 @@ +锘 + + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + + + + + + + + + + + + + + + + Generated Files + + + Generated Files + + + + + + + + + + + + + \ No newline at end of file diff --git a/FramelessHelper/qmake/core.pri b/FramelessHelper/qmake/core.pri new file mode 100644 index 0000000..c91662e --- /dev/null +++ b/FramelessHelper/qmake/core.pri @@ -0,0 +1,80 @@ +QT += core core-private gui gui-private + +CORE_PUB_INC_DIR = $$PWD/../include/FramelessHelper/Core +CORE_PRIV_INC_DIR = $$CORE_PUB_INC_DIR/private +CORE_EXTRA_INC_DIR = $$PWD/inc/core +CORE_SRC_DIR = $$PWD/../src/core + +DEFINES += \ + FRAMELESSHELPER_CORE_STATIC + +INCLUDEPATH += \ + $$CORE_PUB_INC_DIR \ + $$CORE_PRIV_INC_DIR \ + $$CORE_EXTRA_INC_DIR + +DEPENDPATH += \ + $$CORE_PUB_INC_DIR \ + $$CORE_PRIV_INC_DIR \ + $$CORE_EXTRA_INC_DIR + +HEADERS += \ + $$CORE_EXTRA_INC_DIR/framelesshelper.version \ + $$CORE_PUB_INC_DIR/chromepalette.h \ + $$CORE_PUB_INC_DIR/framelesshelper_qt.h \ + $$CORE_PUB_INC_DIR/framelesshelpercore_global.h \ + $$CORE_PUB_INC_DIR/framelessmanager.h \ + $$CORE_PUB_INC_DIR/micamaterial.h \ + $$CORE_PUB_INC_DIR/utils.h \ + $$CORE_PUB_INC_DIR/windowborderpainter.h \ + $$CORE_PRIV_INC_DIR/chromepalette_p.h \ + $$CORE_PRIV_INC_DIR/framelessconfig_p.h \ + $$CORE_PRIV_INC_DIR/framelessmanager_p.h \ + $$CORE_PRIV_INC_DIR/micamaterial_p.h \ + $$CORE_PRIV_INC_DIR/sysapiloader_p.h \ + $$CORE_PRIV_INC_DIR/windowborderpainter_p.h + +SOURCES += \ + $$CORE_SRC_DIR/chromepalette.cpp \ + $$CORE_SRC_DIR/framelessconfig.cpp \ + $$CORE_SRC_DIR/framelesshelper_qt.cpp \ + $$CORE_SRC_DIR/framelessmanager.cpp \ + $$CORE_SRC_DIR/framelesshelpercore_global.cpp \ + $$CORE_SRC_DIR/micamaterial.cpp \ + $$CORE_SRC_DIR/sysapiloader.cpp \ + $$CORE_SRC_DIR/utils.cpp \ + $$CORE_SRC_DIR/windowborderpainter.cpp + +RESOURCES += \ + $$CORE_SRC_DIR/framelesshelpercore.qrc + +win32 { + HEADERS += \ + $$CORE_PUB_INC_DIR/framelesshelper_win.h \ + $$CORE_PUB_INC_DIR/framelesshelper_windows.h \ + $$CORE_PRIV_INC_DIR/registrykey_p.h \ + $$CORE_PRIV_INC_DIR/winverhelper_p.h + SOURCES += \ + $$CORE_SRC_DIR/framelesshelper_win.cpp \ + $$CORE_SRC_DIR/utils_win.cpp \ + $$CORE_SRC_DIR/registrykey.cpp \ + $$CORE_SRC_DIR/winverhelper.cpp \ + $$CORE_SRC_DIR/platformsupport_win.cpp + LIBS += -luser32 -lgdi32 -lshell32 +} + +unix:!macx { + CONFIG += link_pkgconfig + PKGCONFIG += gtk+-3.0 + DEFINES += GDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_6 + HEADERS += \ + $$CORE_PUB_INC_DIR/framelesshelper_linux.h + SOURCES += \ + $$CORE_SRC_DIR/utils_linux.cpp \ + $$CORE_SRC_DIR/platformsupport_linux.cpp +} + +macx { + SOURCES += $$CORE_SRC_DIR/utils_mac.mm + #LIBS += "-framework AppKit" "-framework Cocoa" "-framework Foundation" # TODO +} diff --git a/FramelessHelper/qmake/core.vcxproj b/FramelessHelper/qmake/core.vcxproj new file mode 100644 index 0000000..f8e45a9 --- /dev/null +++ b/FramelessHelper/qmake/core.vcxproj @@ -0,0 +1,274 @@ +锘 + + + + Release + x64 + + + Debug + x64 + + + + {E5BE9C30-42FE-3F03-9A8E-F9C7795F516F} + + + QtVS_v304 + 10.0.19041.0 + 10.0.19041.0 + $(MSBuildProjectDirectory)\QtMsBuild + FramelessHelperCore + + + + v142 + release\ + false + NotSet + Application + release\ + core + + + v143 + debug\ + false + NotSet + StaticLibrary + debug\ + core + + + + + + + + + + + + + + + + + + debug\ + debug\ + core + true + + + release\ + release\ + core + true + false + + + 5.15.2_msvc2019_64 + core;core-private;gui;gui-private + + + 5.15.2_msvc2019_64 + core;core-private;gui;gui-private + + + + + + + GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;..\include\FramelessHelper\Core;..\include\FramelessHelper\Core\private;inc\core;release;E:\VulkanSDK\1.3.216.0\include;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + release\ + false + None + 4577;4467;%(DisableSpecificWarnings) + Sync + release\ + MaxSpeed + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;FRAMELESSHELPER_CORE_STATIC;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + false + + + MultiThreadedDLL + true + true + Level3 + true + + + user32.lib;gdi32.lib;shell32.lib;shell32.lib;%(AdditionalDependencies) + C:\openssl\lib;C:\Utils\my_sql\mysql-5.7.25-winx64\lib;C:\Utils\postgresql\pgsql\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + false + true + false + true + $(OutDir)\core.exe + true + Windows + true + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;FRAMELESSHELPER_CORE_STATIC;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + + + msvc + D:/??2022/ArchitectureColoredPainting/FramelessHelper/qmake/$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + default + framelesshelpercore + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + + + GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;..\include\FramelessHelper\Core;..\include\FramelessHelper\Core\private;inc\core;debug;E:\VulkanSDK\1.3.216.0\include;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + debug\ + false + ProgramDatabase + 4577;4467;%(DisableSpecificWarnings) + Sync + debug\ + Disabled + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;FRAMELESSHELPER_CORE_STATIC;%(PreprocessorDefinitions) + false + MultiThreadedDebugDLL + true + true + Level3 + true + stdcpp17 + + + user32.lib;gdi32.lib;shell32.lib;shell32.lib;%(AdditionalDependencies) + C:\openssl\lib;C:\Utils\my_sql\mysql-5.7.25-winx64\lib;C:\Utils\postgresql\pgsql\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + true + true + $(OutDir)\core.exe + true + Windows + true + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;FRAMELESSHELPER_CORE_STATIC;_DEBUG;%(PreprocessorDefinitions) + + + framelesshelpercore + default + Rcc'ing %(Identity)... + $(Configuration) + qrc_%(Filename).cpp + + + msvc + D:/??2022/ArchitectureColoredPainting/FramelessHelper/qmake/$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(Configuration) + moc_%(Filename).cpp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + true + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h + Generate moc_predefs.h + debug\moc_predefs.h;%(Outputs) + + + Document + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >release\moc_predefs.h + Generate moc_predefs.h + release\moc_predefs.h;%(Outputs) + true + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FramelessHelper/qmake/core.vcxproj.filters b/FramelessHelper/qmake/core.vcxproj.filters new file mode 100644 index 0000000..6a1d510 --- /dev/null +++ b/FramelessHelper/qmake/core.vcxproj.filters @@ -0,0 +1,186 @@ +锘 + + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + + + + + + + + + + + + + + + + + + Generated Files + + + Generated Files + + + + + + + + + + + + + + + Resource Files + + + Resource Files + + + Resource Files + + + \ No newline at end of file diff --git a/FramelessHelper/qmake/inc/core/framelesshelper.version b/FramelessHelper/qmake/inc/core/framelesshelper.version new file mode 100644 index 0000000..14cc5bb --- /dev/null +++ b/FramelessHelper/qmake/inc/core/framelesshelper.version @@ -0,0 +1,44 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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. + */ + +// Generated automatically by QMake. +// WARNING! DO NOT EDIT THIS FILE MANUALLY! +// ALL CHANGES WILL BE LOST AFTER RE-CONFIGURING! +// Also please do not include this file directly, +// it's designed to be included by FramelessHelper's own headers. + +#ifndef _FRAMELESSHELPER_VERSION_DEFINED_ +#define _FRAMELESSHELPER_VERSION_DEFINED_ + +#pragma once + +[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_MAJOR = 2; +[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_MINOR = 3; +[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_PATCH = 5; +[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_TWEAK = 0; +[[maybe_unused]] inline constexpr const char FRAMELESSHELPER_VERSION_STR[] = "2.3.5.0\0"; +[[maybe_unused]] inline constexpr const char FRAMELESSHELPER_COMMIT_STR[] = "UNKNOWN\0"; +[[maybe_unused]] inline constexpr const char FRAMELESSHELPER_COMPILE_DATETIME_STR[] = "UNKNOWN\0"; + +#endif // _FRAMELESSHELPER_VERSION_DEFINED_ diff --git a/FramelessHelper/qmake/quick.pri b/FramelessHelper/qmake/quick.pri new file mode 100644 index 0000000..df46bbe --- /dev/null +++ b/FramelessHelper/qmake/quick.pri @@ -0,0 +1,53 @@ +QT += \ + quick quick-private \ + quicktemplates2 quicktemplates2-private \ + quickcontrols2 quickcontrols2-private + +QUICK_PUB_INC_DIR = $$PWD/../include/FramelessHelper/Quick +QUICK_PRIV_INC_DIR = $$QUICK_PUB_INC_DIR/private +QUICK_SRC_DIR = $$PWD/../src/quick + +DEFINES += \ + FRAMELESSHELPER_QUICK_STATIC + +INCLUDEPATH += \ + $$QUICK_PUB_INC_DIR \ + $$QUICK_PRIV_INC_DIR + +DEPENDPATH += \ + $$QUICK_PUB_INC_DIR \ + $$QUICK_PRIV_INC_DIR + +HEADERS += \ + $$QUICK_PUB_INC_DIR/framelesshelperquick_global.h \ + $$QUICK_PUB_INC_DIR/framelessquickmodule.h \ + $$QUICK_PUB_INC_DIR/framelessquickhelper.h \ + $$QUICK_PUB_INC_DIR/framelessquickutils.h \ + $$QUICK_PUB_INC_DIR/quickchromepalette.h \ + $$QUICK_PUB_INC_DIR/quickmicamaterial.h \ + $$QUICK_PUB_INC_DIR/quickimageitem.h \ + $$QUICK_PUB_INC_DIR/quickwindowborder.h \ + $$QUICK_PRIV_INC_DIR/quickstandardsystembutton_p.h \ + $$QUICK_PRIV_INC_DIR/quickstandardtitlebar_p.h \ + $$QUICK_PRIV_INC_DIR/framelessquickhelper_p.h \ + $$QUICK_PRIV_INC_DIR/framelessquickwindow_p.h \ + $$QUICK_PRIV_INC_DIR/framelessquickwindow_p_p.h \ + $$QUICK_PRIV_INC_DIR/framelessquickapplicationwindow_p.h \ + $$QUICK_PRIV_INC_DIR/framelessquickapplicationwindow_p_p.h \ + $$QUICK_PRIV_INC_DIR/quickmicamaterial_p.h \ + $$QUICK_PRIV_INC_DIR/quickimageitem_p.h \ + $$QUICK_PRIV_INC_DIR/quickwindowborder_p.h + +SOURCES += \ + $$QUICK_SRC_DIR/quickstandardsystembutton.cpp \ + $$QUICK_SRC_DIR/quickstandardtitlebar.cpp \ + $$QUICK_SRC_DIR/framelessquickutils.cpp \ + $$QUICK_SRC_DIR/framelessquickmodule.cpp \ + $$QUICK_SRC_DIR/framelessquickwindow.cpp \ + $$QUICK_SRC_DIR/framelessquickapplicationwindow.cpp \ + $$QUICK_SRC_DIR/framelessquickhelper.cpp \ + $$QUICK_SRC_DIR/quickchromepalette.cpp \ + $$QUICK_SRC_DIR/framelesshelperquick_global.cpp \ + $$QUICK_SRC_DIR/quickmicamaterial.cpp \ + $$QUICK_SRC_DIR/quickimageitem.cpp \ + $$QUICK_SRC_DIR/quickwindowborder.cpp diff --git a/FramelessHelper/qmake/widgets.pri b/FramelessHelper/qmake/widgets.pri new file mode 100644 index 0000000..0f1e999 --- /dev/null +++ b/FramelessHelper/qmake/widgets.pri @@ -0,0 +1,42 @@ +QT += widgets widgets-private + +WIDGETS_PUB_INC_DIR = $$PWD/../include/FramelessHelper/Widgets +WIDGETS_PRIV_INC_DIR = $$WIDGETS_PUB_INC_DIR/private +WIDGETS_SRC_DIR = $$PWD/../src/widgets + +DEFINES += \ + FRAMELESSHELPER_WIDGETS_STATIC + +INCLUDEPATH += \ + $$WIDGETS_PUB_INC_DIR \ + $$WIDGETS_PRIV_INC_DIR + +DEPENDPATH += \ + $$WIDGETS_PUB_INC_DIR \ + $$WIDGETS_PRIV_INC_DIR + +HEADERS += \ + $$WIDGETS_PUB_INC_DIR/framelesshelperwidgets_global.h \ + $$WIDGETS_PUB_INC_DIR/framelesswidget.h \ + $$WIDGETS_PUB_INC_DIR/framelessmainwindow.h \ + $$WIDGETS_PUB_INC_DIR/standardsystembutton.h \ + $$WIDGETS_PUB_INC_DIR/framelesswidgetshelper.h \ + $$WIDGETS_PUB_INC_DIR/standardtitlebar.h \ + $$WIDGETS_PUB_INC_DIR/framelessdialog.h \ + $$WIDGETS_PRIV_INC_DIR/framelesswidgetshelper_p.h \ + $$WIDGETS_PRIV_INC_DIR/standardsystembutton_p.h \ + $$WIDGETS_PRIV_INC_DIR/standardtitlebar_p.h \ + $$WIDGETS_PRIV_INC_DIR/framelesswidget_p.h \ + $$WIDGETS_PRIV_INC_DIR/framelessmainwindow_p.h \ + $$WIDGETS_PRIV_INC_DIR/widgetssharedhelper_p.h \ + $$WIDGETS_PRIV_INC_DIR/framelessdialog_p.h + +SOURCES += \ + $$WIDGETS_SRC_DIR/framelessmainwindow.cpp \ + $$WIDGETS_SRC_DIR/framelesswidgetshelper.cpp \ + $$WIDGETS_SRC_DIR/framelesswidget.cpp \ + $$WIDGETS_SRC_DIR/standardsystembutton.cpp \ + $$WIDGETS_SRC_DIR/standardtitlebar.cpp \ + $$WIDGETS_SRC_DIR/widgetssharedhelper.cpp \ + $$WIDGETS_SRC_DIR/framelesshelperwidgets_global.cpp \ + $$WIDGETS_SRC_DIR/framelessdialog.cpp diff --git a/FramelessHelper/src/CMakeLists.txt b/FramelessHelper/src/CMakeLists.txt new file mode 100644 index 0000000..0cb2920 --- /dev/null +++ b/FramelessHelper/src/CMakeLists.txt @@ -0,0 +1,35 @@ +#[[ + MIT License + + Copyright (C) 2021-2023 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. +]] + +if(TARGET Qt${QT_VERSION_MAJOR}::Core AND TARGET Qt${QT_VERSION_MAJOR}::Gui) + add_subdirectory(core) +endif() + +if(FRAMELESSHELPER_BUILD_WIDGETS AND TARGET Qt${QT_VERSION_MAJOR}::Widgets) + add_subdirectory(widgets) +endif() + +if(FRAMELESSHELPER_BUILD_QUICK AND TARGET Qt${QT_VERSION_MAJOR}::Quick) + add_subdirectory(quick) +endif() diff --git a/FramelessHelper/src/core/CMakeLists.txt b/FramelessHelper/src/core/CMakeLists.txt new file mode 100644 index 0000000..6ea8dc0 --- /dev/null +++ b/FramelessHelper/src/core/CMakeLists.txt @@ -0,0 +1,252 @@ +#[[ + MIT License + + Copyright (C) 2021-2023 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. +]] + +if(UNIX AND NOT APPLE) + if(FRAMELESSHELPER_NO_PRIVATE) + # Qt X11Extras is only available in Qt5. + if(QT_VERSION_MAJOR LESS 6) + find_package(Qt5 QUIET COMPONENTS X11Extras) + endif() + endif() + find_package(X11 QUIET COMPONENTS xcb) + if(TARGET X11::xcb) + message("Found system XCB. The XCB wrapper will be disabled.") + else() + message("System XCB not found. The XCB wrapper will be used instead.") + endif() + find_package(PkgConfig QUIET) + if(PkgConfig_FOUND) + pkg_check_modules(GTK3 QUIET IMPORTED_TARGET gtk+-3.0) + endif() + if(TARGET PkgConfig::GTK3) + message("Found system GTK. The GTK wrapper will be disabled.") + else() + message("System GTK not found. The GTK wrapper will be used instead.") + endif() +endif() + +set(SUB_MOD_NAME Core) +set(SUB_PROJ_NAME ${PROJECT_NAME}${SUB_MOD_NAME}) +set(SUB_PROJ_PATH ${PROJECT_NAME}/${SUB_MOD_NAME}) + +set(INCLUDE_PREFIX ../../include/${SUB_PROJ_PATH}) + +configure_file(framelesshelper.version.in + ${CMAKE_CURRENT_BINARY_DIR}/framelesshelper.version @ONLY) + +set(PUBLIC_HEADERS + ${CMAKE_CURRENT_BINARY_DIR}/framelesshelper.version + ${INCLUDE_PREFIX}/framelesshelpercore_global.h + ${INCLUDE_PREFIX}/framelesshelper_qt.h + ${INCLUDE_PREFIX}/framelessmanager.h + ${INCLUDE_PREFIX}/utils.h + ${INCLUDE_PREFIX}/chromepalette.h + ${INCLUDE_PREFIX}/micamaterial.h + ${INCLUDE_PREFIX}/windowborderpainter.h +) + +set(PUBLIC_HEADERS_ALIAS + ${INCLUDE_PREFIX}/Global + ${INCLUDE_PREFIX}/FramelessHelper_Qt + ${INCLUDE_PREFIX}/FramelessManager + ${INCLUDE_PREFIX}/Utils + ${INCLUDE_PREFIX}/ChromePalette + ${INCLUDE_PREFIX}/MicaMaterial + ${INCLUDE_PREFIX}/WindowBorderPainter +) + +set(PRIVATE_HEADERS + ${INCLUDE_PREFIX}/private/framelessmanager_p.h + ${INCLUDE_PREFIX}/private/framelessconfig_p.h + ${INCLUDE_PREFIX}/private/sysapiloader_p.h + ${INCLUDE_PREFIX}/private/chromepalette_p.h + ${INCLUDE_PREFIX}/private/micamaterial_p.h + ${INCLUDE_PREFIX}/private/windowborderpainter_p.h +) + +set(SOURCES + utils.cpp + framelesshelper_qt.cpp + framelessmanager.cpp + framelessconfig.cpp + sysapiloader.cpp + chromepalette.cpp + framelesshelpercore_global.cpp + micamaterial.cpp + windowborderpainter.cpp +) + +if(WIN32) + list(APPEND PUBLIC_HEADERS + ${INCLUDE_PREFIX}/framelesshelper_windows.h + ${INCLUDE_PREFIX}/framelesshelper_win.h + ) + list(APPEND PUBLIC_HEADERS_ALIAS + ${INCLUDE_PREFIX}/FramelessHelper_Windows + ${INCLUDE_PREFIX}/FramelessHelper_Win + ) + list(APPEND PRIVATE_HEADERS + ${INCLUDE_PREFIX}/private/registrykey_p.h + ${INCLUDE_PREFIX}/private/winverhelper_p.h + ) + list(APPEND SOURCES + registrykey.cpp + utils_win.cpp + framelesshelper_win.cpp + winverhelper.cpp + platformsupport_win.cpp + ) +elseif(APPLE) + list(APPEND SOURCES utils_mac.mm) +elseif(UNIX) + list(APPEND PUBLIC_HEADERS + ${INCLUDE_PREFIX}/framelesshelper_linux.h + ) + list(APPEND PUBLIC_HEADERS_ALIAS + ${INCLUDE_PREFIX}/FramelessHelper_Linux + ) + list(APPEND SOURCES + utils_linux.cpp + platformsupport_linux.cpp + ) +endif() + +if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC) + enable_language(RC) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/framelesshelper.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/framelesshelpercore.rc @ONLY) + list(APPEND SOURCES + ${CMAKE_CURRENT_BINARY_DIR}/framelesshelpercore.rc + ) +endif() + +set(ALL_SOURCES ${PUBLIC_HEADERS} ${PRIVATE_HEADERS} ${SOURCES}) + +if(FRAMELESSHELPER_BUILD_STATIC) + add_library(${SUB_PROJ_NAME} STATIC ${ALL_SOURCES}) +else() + add_library(${SUB_PROJ_NAME} SHARED ${ALL_SOURCES}) +endif() +add_library(${PROJECT_NAME}::${SUB_PROJ_NAME} ALIAS ${SUB_PROJ_NAME}) +add_library(${PROJECT_NAME}::${SUB_MOD_NAME} ALIAS ${SUB_PROJ_NAME}) + +if(NOT FRAMELESSHELPER_NO_BUNDLE_RESOURCE) + if(QT_VERSION VERSION_GREATER_EQUAL "6.2") + qt_add_resources(${SUB_PROJ_NAME} framelesshelpercore + PREFIX + "/org.wangwenx190.${PROJECT_NAME}" + FILES + "resources/fonts/iconfont.ttf" + "resources/images/noise.png" + ) + else() + target_sources(${SUB_PROJ_NAME} PRIVATE + framelesshelpercore.qrc + ) + endif() +endif() + +if(FRAMELESSHELPER_BUILD_STATIC) + target_compile_definitions(${SUB_PROJ_NAME} PUBLIC + FRAMELESSHELPER_CORE_STATIC + ) +endif() + +if(FRAMELESSHELPER_NO_DEBUG_OUTPUT) + target_compile_definitions(${SUB_PROJ_NAME} PRIVATE + FRAMELESSHELPER_CORE_NO_DEBUG_OUTPUT + ) +endif() + +if(FRAMELESSHELPER_NO_BUNDLE_RESOURCE) + target_compile_definitions(${SUB_PROJ_NAME} PRIVATE + FRAMELESSHELPER_CORE_NO_BUNDLE_RESOURCE + ) +endif() + +if(FRAMELESSHELPER_NO_PRIVATE) + target_compile_definitions(${SUB_PROJ_NAME} PRIVATE + FRAMELESSHELPER_CORE_NO_PRIVATE + ) +endif() + +target_compile_definitions(${SUB_PROJ_NAME} PRIVATE + FRAMELESSHELPER_CORE_LIBRARY +) + +if(APPLE) + target_link_libraries(${SUB_PROJ_NAME} PRIVATE + "-framework Foundation" + "-framework Cocoa" + "-framework AppKit" + ) +elseif(UNIX) + if(TARGET X11::xcb) + target_link_libraries(${SUB_PROJ_NAME} PRIVATE + X11::xcb + ) + endif() + if(TARGET PkgConfig::GTK3) + target_link_libraries(${SUB_PROJ_NAME} PRIVATE + PkgConfig::GTK3 + ) + target_compile_definitions(${SUB_PROJ_NAME} PRIVATE + GDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_6 + ) + endif() +endif() + +if(FRAMELESSHELPER_NO_PRIVATE) + target_link_libraries(${SUB_PROJ_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + ) + # Qt X11Extras was first introduced in 5.1 and got removed in 6.0 + # But it was again brought back as a private feature of QtGui in 6.2 + if(TARGET Qt5::X11Extras) + target_link_libraries(${SUB_PROJ_NAME} PRIVATE + Qt5::X11Extras + ) + endif() +else() + target_link_libraries(${SUB_PROJ_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::CorePrivate + Qt${QT_VERSION_MAJOR}::GuiPrivate + ) +endif() + +target_include_directories(${SUB_PROJ_NAME} PUBLIC + "$" + "$" + "$" + "$" + "$" + "$" + "$" +) + +include(cmakehelper.cmake) +setup_compile_params(${SUB_PROJ_NAME}) +setup_package_export(${SUB_PROJ_NAME} ${SUB_PROJ_PATH} "${PUBLIC_HEADERS}" "${PUBLIC_HEADERS_ALIAS}" "${PRIVATE_HEADERS}") diff --git a/FramelessHelper/src/core/VC-LTL.cmake b/FramelessHelper/src/core/VC-LTL.cmake new file mode 100644 index 0000000..2bab2f2 --- /dev/null +++ b/FramelessHelper/src/core/VC-LTL.cmake @@ -0,0 +1,94 @@ +锘# https://github.com/Chuyu-Team/VC-LTL5 + +# +# VC-LTL鑷姩鍖栧姞杞介厤缃紝寤鸿浣犲皢姝ゆ枃浠跺崟鐙鍒跺埌浣犵殑宸ョ▼鍐嶄娇鐢紝璇ユ枃浠惰兘鑷姩璇嗗埆褰撳墠鐜鏄惁瀛樺湪VC-LTL锛屽苟涓旇嚜鍔ㄥ簲鐢ㄣ +# +# 浣跨敤鏂规硶锛 +# 1. 鍦ㄢ淐MakeLists.txt鈥 娣诲姞 鈥渋nclude("VC-LTL helper for cmake.cmake")鈥濄 +# +# VC-LTL榛樿鎼滅储椤哄簭 +# 1. 鈥淰C-LTL helper for cmake.cmake鈥濇墍鍦ㄦ牴鐩綍锛屽嵆 ${CMAKE_CURRENT_LIST_DIR} +# 2. 褰撳墠CMake鏍圭洰褰曪紝鍗 ${CMAKE_CURRENT_SOURCE_DIR}/VC-LTL +# 3. 褰撳墠椤圭洰鏍圭洰褰曪紝鍗 ${PROJECT_SOURCE_DIR}/VC-LTL +# 4. 褰撳墠CMake鐖剁洰褰曪紝鍗 ${CMAKE_CURRENT_SOURCE_DIR}/../VC-LTL +# 5. 褰撳墠椤圭洰鏍圭洰褰曪紝鍗 ${PROJECT_SOURCE_DIR}/../VC-LTL +# 6. 娉ㄥ唽琛℉KEY_CURRENT_USER\Code\VC-LTL@Root +# +# 鎶奦C-LTL鏀惧湪鍏朵腑涓涓綅缃嵆鍙紝VC-LTL灏辫兘琚嚜鍔ㄥ紩鐢ㄣ +# +# 濡傛灉浣犲榛樿鎼滅储椤哄簭涓嶆弧锛屼綘鍙互淇敼姝ゆ枃浠躲備綘涔熷彲浠ョ洿鎺ユ寚瀹${VC_LTL_Root}瀹忔洿鍔犱换鎬х殑鍘诲姞杞絍C-LTL銆 +# + +if(NOT MSVC OR DEFINED __VC_LTL_CMAKE_INCLUDE_GUARD) + return() +endif() +set(__VC_LTL_CMAKE_INCLUDE_GUARD 1) + +#####################################################################VC-LTL璁剧疆##################################################################### + +#鎺у埗TargetPlatform鐗堟湰锛岀洰鍓嶅彲鐢ㄧ増鏈负5.1.2600.0 6.0.6000.0锛堥粯璁わ級 6.2.9200.0 10.0.10240.0 10.0.19041.0 +if(NOT DEFINED WindowsTargetPlatformMinVersion) + set(WindowsTargetPlatformMinVersion "10.0.19041.0" CACHE STRING "" FORCE) +endif() + +#鍚敤骞插噣鐨勫鍏ヨ〃锛屾秷闄 ucrt apiset(濡傦細api-ms-win-crt-time-l1-1-0.dll)锛屾弧瓒冲己杩棁鎮h呫 +if(NOT DEFINED CleanImport) + set(CleanImport "true" CACHE STRING "" FORCE) +endif() + +#################################################################################################################################################### + +if(NOT VC_LTL_Root) + if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/_msvcrt.h) + set(VC_LTL_Root ${CMAKE_CURRENT_LIST_DIR}) + endif() +endif() + +if(NOT VC_LTL_Root) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/VC-LTL/_msvcrt.h) + set(VC_LTL_Root ${CMAKE_CURRENT_SOURCE_DIR}/VC-LTL) + endif() +endif() + +if(NOT VC_LTL_Root) + if(EXISTS ${PROJECT_SOURCE_DIR}/VC-LTL/_msvcrt.h) + set(VC_LTL_Root ${PROJECT_SOURCE_DIR}/VC-LTL) + endif() +endif() + +if(NOT VC_LTL_Root) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../VC-LTL/_msvcrt.h) + set(VC_LTL_Root ${CMAKE_CURRENT_SOURCE_DIR}/../VC-LTL) + endif() +endif() + +if(NOT VC_LTL_Root) + if(EXISTS ${PROJECT_SOURCE_DIR}/../VC-LTL/_msvcrt.h) + set(VC_LTL_Root ${PROJECT_SOURCE_DIR}/../VC-LTL) + endif() +endif() + +if(NOT VC_LTL_Root) + EXECUTE_PROCESS(COMMAND reg query "HKEY_CURRENT_USER\\Code\\VC-LTL" -v "Root" + OUTPUT_VARIABLE FOUND_FILE + ERROR_VARIABLE ERROR_INFO + ) + + string(REGEX MATCH "[a-zA-Z]:\\\\.+\\\\" + FOUND_LTL + ${FOUND_FILE}) + if (NOT ${FOUND_LTL} STREQUAL "") + set(VC_LTL_Root ${FOUND_LTL}) + endif() + + if(NOT DEFINED VC_LTL_Root) + string(REGEX MATCH "\\\\\\\\.+\\\\" FOUND_LTL ${FOUND_FILE}) + if (NOT ${FOUND_LTL} STREQUAL "") + set(VC_LTL_Root ${FOUND_LTL}) + endif() + endif() +endif() + +if(VC_LTL_Root) + include("${VC_LTL_Root}\\config\\config.cmake") +endif() diff --git a/FramelessHelper/src/core/chromepalette.cpp b/FramelessHelper/src/core/chromepalette.cpp new file mode 100644 index 0000000..dffbe4d --- /dev/null +++ b/FramelessHelper/src/core/chromepalette.cpp @@ -0,0 +1,437 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "chromepalette.h" +#include "chromepalette_p.h" +#include "framelessmanager.h" +#include "utils.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcChromePalette, "wangwenx190.framelesshelper.core.chromepalette") + +#ifdef FRAMELESSHELPER_CORE_NO_DEBUG_OUTPUT +# define INFO QT_NO_QDEBUG_MACRO() +# define DEBUG QT_NO_QDEBUG_MACRO() +# define WARNING QT_NO_QDEBUG_MACRO() +# define CRITICAL QT_NO_QDEBUG_MACRO() +#else +# define INFO qCInfo(lcChromePalette) +# define DEBUG qCDebug(lcChromePalette) +# define WARNING qCWarning(lcChromePalette) +# define CRITICAL qCCritical(lcChromePalette) +#endif + +using namespace Global; + +ChromePalettePrivate::ChromePalettePrivate(ChromePalette *q) : QObject(q) +{ + Q_ASSERT(q); + if (!q) { + return; + } + q_ptr = q; + connect(FramelessManager::instance(), + &FramelessManager::systemThemeChanged, this, &ChromePalettePrivate::refresh); + refresh(); +} + +ChromePalettePrivate::~ChromePalettePrivate() = default; + +ChromePalettePrivate *ChromePalettePrivate::get(ChromePalette *q) +{ + Q_ASSERT(q); + if (!q) { + return nullptr; + } + return q->d_func(); +} + +const ChromePalettePrivate *ChromePalettePrivate::get(const ChromePalette *q) +{ + Q_ASSERT(q); + if (!q) { + return nullptr; + } + return q->d_func(); +} + +void ChromePalettePrivate::refresh() +{ + const bool colorized = Utils::isTitleBarColorized(); + const bool dark = Utils::shouldAppsUseDarkMode(); + titleBarActiveBackgroundColor_sys = [colorized, dark]() -> QColor { + if (colorized) { +#ifdef Q_OS_WINDOWS + return Utils::getDwmAccentColor(); +#elif defined(Q_OS_LINUX) + return Utils::getWmThemeColor(); +#elif defined(Q_OS_MACOS) + return Utils::getControlsAccentColor(); +#else + return {}; +#endif + } else { + return (dark ? kDefaultBlackColor : kDefaultWhiteColor); + } + }(); + titleBarInactiveBackgroundColor_sys = (dark ? kDefaultSystemDarkColor : kDefaultWhiteColor); + titleBarActiveForegroundColor_sys = [this, dark, colorized]() -> QColor { + if (dark || colorized) { + // Calculate the most appropriate foreground color, based on the + // current background color. + const qreal grayF = ( + (0.299 * titleBarActiveBackgroundColor_sys.redF()) + + (0.587 * titleBarActiveBackgroundColor_sys.greenF()) + + (0.114 * titleBarActiveBackgroundColor_sys.blueF())); + if (grayF <= 0.5) { + return kDefaultWhiteColor; + } + } + return kDefaultBlackColor; + }(); + titleBarInactiveForegroundColor_sys = kDefaultDarkGrayColor; + chromeButtonNormalColor_sys = kDefaultTransparentColor; + chromeButtonHoverColor_sys = + Utils::calculateSystemButtonBackgroundColor(SystemButtonType::Minimize, ButtonState::Hovered); + chromeButtonPressColor_sys = + Utils::calculateSystemButtonBackgroundColor(SystemButtonType::Minimize, ButtonState::Pressed); + closeButtonNormalColor_sys = kDefaultTransparentColor; + closeButtonHoverColor_sys = + Utils::calculateSystemButtonBackgroundColor(SystemButtonType::Close, ButtonState::Hovered); + closeButtonPressColor_sys = + Utils::calculateSystemButtonBackgroundColor(SystemButtonType::Close, ButtonState::Pressed); + Q_Q(ChromePalette); + Q_EMIT q->titleBarActiveBackgroundColorChanged(); + Q_EMIT q->titleBarInactiveBackgroundColorChanged(); + Q_EMIT q->titleBarActiveForegroundColorChanged(); + Q_EMIT q->titleBarInactiveForegroundColorChanged(); + Q_EMIT q->chromeButtonNormalColorChanged(); + Q_EMIT q->chromeButtonHoverColorChanged(); + Q_EMIT q->chromeButtonPressColorChanged(); + Q_EMIT q->closeButtonNormalColorChanged(); + Q_EMIT q->closeButtonHoverColorChanged(); + Q_EMIT q->closeButtonPressColorChanged(); + Q_EMIT q->titleBarColorChanged(); + Q_EMIT q->chromeButtonColorChanged(); +} + +ChromePalette::ChromePalette(QObject *parent) : + QObject(parent), d_ptr(new ChromePalettePrivate(this)) +{ +} + +ChromePalette::~ChromePalette() = default; + +QColor ChromePalette::titleBarActiveBackgroundColor() const +{ + Q_D(const ChromePalette); + return d->titleBarActiveBackgroundColor.value_or(d->titleBarActiveBackgroundColor_sys); +} + +QColor ChromePalette::titleBarInactiveBackgroundColor() const +{ + Q_D(const ChromePalette); + return d->titleBarInactiveBackgroundColor.value_or(d->titleBarInactiveBackgroundColor_sys); +} + +QColor ChromePalette::titleBarActiveForegroundColor() const +{ + Q_D(const ChromePalette); + return d->titleBarActiveForegroundColor.value_or(d->titleBarActiveForegroundColor_sys); +} + +QColor ChromePalette::titleBarInactiveForegroundColor() const +{ + Q_D(const ChromePalette); + return d->titleBarInactiveForegroundColor.value_or(d->titleBarInactiveForegroundColor_sys); +} + +QColor ChromePalette::chromeButtonNormalColor() const +{ + Q_D(const ChromePalette); + return d->chromeButtonNormalColor.value_or(d->chromeButtonNormalColor_sys); +} + +QColor ChromePalette::chromeButtonHoverColor() const +{ + Q_D(const ChromePalette); + return d->chromeButtonHoverColor.value_or(d->chromeButtonHoverColor_sys); +} + +QColor ChromePalette::chromeButtonPressColor() const +{ + Q_D(const ChromePalette); + return d->chromeButtonPressColor.value_or(d->chromeButtonPressColor_sys); +} + +QColor ChromePalette::closeButtonNormalColor() const +{ + Q_D(const ChromePalette); + return d->closeButtonNormalColor.value_or(d->closeButtonNormalColor_sys); +} + +QColor ChromePalette::closeButtonHoverColor() const +{ + Q_D(const ChromePalette); + return d->closeButtonHoverColor.value_or(d->closeButtonHoverColor_sys); +} + +QColor ChromePalette::closeButtonPressColor() const +{ + Q_D(const ChromePalette); + return d->closeButtonPressColor.value_or(d->closeButtonPressColor_sys); +} + +void ChromePalette::setTitleBarActiveBackgroundColor(const QColor &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + Q_D(ChromePalette); + if (d->titleBarActiveBackgroundColor.value_or(QColor()) == value) { + return; + } + d->titleBarActiveBackgroundColor = value; + Q_EMIT titleBarActiveBackgroundColorChanged(); + Q_EMIT titleBarColorChanged(); +} + +void ChromePalette::resetTitleBarActiveBackgroundColor() +{ + Q_D(ChromePalette); + d->titleBarActiveBackgroundColor = std::nullopt; + Q_EMIT titleBarActiveBackgroundColorChanged(); + Q_EMIT titleBarColorChanged(); +} + +void ChromePalette::setTitleBarInactiveBackgroundColor(const QColor &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + Q_D(ChromePalette); + if (d->titleBarInactiveBackgroundColor.value_or(QColor()) == value) { + return; + } + d->titleBarInactiveBackgroundColor = value; + Q_EMIT titleBarInactiveBackgroundColorChanged(); + Q_EMIT titleBarColorChanged(); +} + +void ChromePalette::resetTitleBarInactiveBackgroundColor() +{ + Q_D(ChromePalette); + d->titleBarInactiveBackgroundColor = std::nullopt; + Q_EMIT titleBarInactiveBackgroundColorChanged(); + Q_EMIT titleBarColorChanged(); +} + +void ChromePalette::setTitleBarActiveForegroundColor(const QColor &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + Q_D(ChromePalette); + if (d->titleBarActiveForegroundColor.value_or(QColor()) == value) { + return; + } + d->titleBarActiveForegroundColor = value; + Q_EMIT titleBarActiveForegroundColorChanged(); + Q_EMIT titleBarColorChanged(); +} + +void ChromePalette::resetTitleBarActiveForegroundColor() +{ + Q_D(ChromePalette); + d->titleBarActiveForegroundColor = std::nullopt; + Q_EMIT titleBarActiveForegroundColorChanged(); + Q_EMIT titleBarColorChanged(); +} + +void ChromePalette::setTitleBarInactiveForegroundColor(const QColor &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + Q_D(ChromePalette); + if (d->titleBarInactiveForegroundColor.value_or(QColor()) == value) { + return; + } + d->titleBarInactiveForegroundColor = value; + Q_EMIT titleBarInactiveForegroundColorChanged(); + Q_EMIT titleBarColorChanged(); +} + +void ChromePalette::resetTitleBarInactiveForegroundColor() +{ + Q_D(ChromePalette); + d->titleBarInactiveForegroundColor = std::nullopt; + Q_EMIT titleBarInactiveForegroundColorChanged(); + Q_EMIT titleBarColorChanged(); +} + +void ChromePalette::setChromeButtonNormalColor(const QColor &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + Q_D(ChromePalette); + if (d->chromeButtonNormalColor.value_or(QColor()) == value) { + return; + } + d->chromeButtonNormalColor = value; + Q_EMIT chromeButtonNormalColorChanged(); + Q_EMIT chromeButtonColorChanged(); +} + +void ChromePalette::resetChromeButtonNormalColor() +{ + Q_D(ChromePalette); + d->chromeButtonNormalColor = std::nullopt; + Q_EMIT chromeButtonNormalColorChanged(); + Q_EMIT chromeButtonColorChanged(); +} + +void ChromePalette::setChromeButtonHoverColor(const QColor &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + Q_D(ChromePalette); + if (d->chromeButtonHoverColor.value_or(QColor()) == value) { + return; + } + d->chromeButtonHoverColor = value; + Q_EMIT chromeButtonHoverColorChanged(); + Q_EMIT chromeButtonColorChanged(); +} + +void ChromePalette::resetChromeButtonHoverColor() +{ + Q_D(ChromePalette); + d->chromeButtonHoverColor = std::nullopt; + Q_EMIT chromeButtonHoverColorChanged(); + Q_EMIT chromeButtonColorChanged(); +} + +void ChromePalette::setChromeButtonPressColor(const QColor &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + Q_D(ChromePalette); + if (d->chromeButtonPressColor.value_or(QColor()) == value) { + return; + } + d->chromeButtonPressColor = value; + Q_EMIT chromeButtonPressColorChanged(); + Q_EMIT chromeButtonColorChanged(); +} + +void ChromePalette::resetChromeButtonPressColor() +{ + Q_D(ChromePalette); + d->chromeButtonPressColor = std::nullopt; + Q_EMIT chromeButtonPressColorChanged(); + Q_EMIT chromeButtonColorChanged(); +} + +void ChromePalette::setCloseButtonNormalColor(const QColor &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + Q_D(ChromePalette); + if (d->closeButtonNormalColor.value_or(QColor()) == value) { + return; + } + d->closeButtonNormalColor = value; + Q_EMIT closeButtonNormalColorChanged(); + Q_EMIT chromeButtonColorChanged(); +} + +void ChromePalette::resetCloseButtonNormalColor() +{ + Q_D(ChromePalette); + d->closeButtonNormalColor = std::nullopt; + Q_EMIT closeButtonNormalColorChanged(); + Q_EMIT chromeButtonColorChanged(); +} + +void ChromePalette::setCloseButtonHoverColor(const QColor &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + Q_D(ChromePalette); + if (d->closeButtonHoverColor.value_or(QColor()) == value) { + return; + } + d->closeButtonHoverColor = value; + Q_EMIT closeButtonHoverColorChanged(); + Q_EMIT chromeButtonColorChanged(); +} + +void ChromePalette::resetCloseButtonHoverColor() +{ + Q_D(ChromePalette); + d->closeButtonHoverColor = std::nullopt; + Q_EMIT closeButtonHoverColorChanged(); + Q_EMIT chromeButtonColorChanged(); +} + +void ChromePalette::setCloseButtonPressColor(const QColor &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + Q_D(ChromePalette); + if (d->closeButtonPressColor.value_or(QColor()) == value) { + return; + } + d->closeButtonPressColor = value; + Q_EMIT closeButtonPressColorChanged(); + Q_EMIT chromeButtonColorChanged(); +} + +void ChromePalette::resetCloseButtonPressColor() +{ + Q_D(ChromePalette); + d->closeButtonPressColor = std::nullopt; + Q_EMIT closeButtonPressColorChanged(); + Q_EMIT chromeButtonColorChanged(); +} + +FRAMELESSHELPER_END_NAMESPACE diff --git a/FramelessHelper/src/core/chromepalette.h b/FramelessHelper/src/core/chromepalette.h new file mode 100644 index 0000000..68f6ff7 --- /dev/null +++ b/FramelessHelper/src/core/chromepalette.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Core/chromepalette.h" diff --git a/FramelessHelper/src/core/chromepalette_p.h b/FramelessHelper/src/core/chromepalette_p.h new file mode 100644 index 0000000..4202da2 --- /dev/null +++ b/FramelessHelper/src/core/chromepalette_p.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Core/private/chromepalette_p.h" diff --git a/FramelessHelper/src/core/cmakehelper.cmake b/FramelessHelper/src/core/cmakehelper.cmake new file mode 100644 index 0000000..f0bc76c --- /dev/null +++ b/FramelessHelper/src/core/cmakehelper.cmake @@ -0,0 +1,385 @@ +#[[ + MIT License + + Copyright (C) 2021-2023 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. +]] + +function(setup_compile_params arg_target) + target_compile_definitions(${arg_target} PRIVATE + QT_NO_CAST_TO_ASCII + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_FROM_BYTEARRAY + QT_NO_URL_CAST_FROM_STRING + QT_NO_NARROWING_CONVERSIONS_IN_CONNECT + QT_NO_FOREACH + #QT_TYPESAFE_FLAGS # QtQuick private headers prevent us from enabling this flag. + QT_USE_QSTRINGBUILDER + QT_USE_FAST_OPERATOR_PLUS + QT_DEPRECATED_WARNINGS # Have no effect since 6.0 + QT_DEPRECATED_WARNINGS_SINCE=0x070000 + QT_WARN_DEPRECATED_UP_TO=0x070000 # Since 6.5 + QT_DISABLE_DEPRECATED_BEFORE=0x070000 + QT_DISABLE_DEPRECATED_UP_TO=0x070000 # Since 6.5 + ) + if(NOT (DEFINED __DONT_DISABLE_QT_KEYWORDS AND __DONT_DISABLE_QT_KEYWORDS)) + target_compile_definitions(${arg_target} PRIVATE + QT_NO_KEYWORDS # Some QtQuick private headers still use the traditional Qt keywords. + ) + endif() + if(WIN32) # Needed by both MSVC and MinGW, otherwise some APIs we need will be invisible. + set(_WIN32_WINNT_WIN10 0x0A00) + set(NTDDI_WIN10_NI 0x0A00000C) + target_compile_definitions(${arg_target} PRIVATE + WINVER=${_WIN32_WINNT_WIN10} _WIN32_WINNT=${_WIN32_WINNT_WIN10} + _WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_NI} + ) + else() + target_compile_definitions(${arg_target} PRIVATE + QT_STRICT_ITERATORS # On Windows we need to re-compile Qt with this flag enabled, so only enable it on non-Windows platforms. + ) + endif() + if(MSVC) + target_compile_definitions(${arg_target} PRIVATE + _CRT_NON_CONFORMING_SWPRINTFS _CRT_SECURE_NO_WARNINGS + _CRT_SECURE_NO_DEPRECATE _CRT_NONSTDC_NO_WARNINGS + _CRT_NONSTDC_NO_DEPRECATE _SCL_SECURE_NO_WARNINGS + _SCL_SECURE_NO_DEPRECATE _ENABLE_EXTENDED_ALIGNED_STORAGE + _USE_MATH_DEFINES NOMINMAX UNICODE _UNICODE + WIN32_LEAN_AND_MEAN WINRT_LEAN_AND_MEAN + ) + target_compile_options(${arg_target} PRIVATE + /bigobj /utf-8 $<$>:/fp:fast /GT /Gw /Gy /guard:cf /Zc:inline> + ) + target_link_options(${arg_target} PRIVATE + $<$>:/OPT:REF /OPT:ICF /OPT:LBR /GUARD:CF> + /DYNAMICBASE /NXCOMPAT /LARGEADDRESSAWARE /WX + ) + set(__target_type "UNKNOWN") + get_target_property(__target_type ${arg_target} TYPE) + if(__target_type STREQUAL "EXECUTABLE") + target_compile_options(${arg_target} PRIVATE $<$>:/GA>) + target_link_options(${arg_target} PRIVATE /TSAWARE) + endif() + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + target_link_options(${arg_target} PRIVATE /SAFESEH) + endif() + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + target_link_options(${arg_target} PRIVATE /HIGHENTROPYVA) + endif() + if(MSVC_VERSION GREATER_EQUAL 1915) # Visual Studio 2017 version 15.8 + target_compile_options(${arg_target} PRIVATE $<$,$>:/JMC>) + endif() + if(MSVC_VERSION GREATER_EQUAL 1920) # Visual Studio 2019 version 16.0 + target_link_options(${arg_target} PRIVATE $<$>:/CETCOMPAT>) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + target_compile_options(${arg_target} PRIVATE /d2FH4) + endif() + endif() + if(MSVC_VERSION GREATER_EQUAL 1925) # Visual Studio 2019 version 16.5 + target_compile_options(${arg_target} PRIVATE $<$>:/QIntel-jcc-erratum>) # /Qspectre-load + #elseif(MSVC_VERSION GREATER_EQUAL 1912) # Visual Studio 2017 version 15.5 + # target_compile_options(${arg_target} PRIVATE /Qspectre) + endif() + #if((MSVC_VERSION GREATER_EQUAL 1927) AND (CMAKE_SIZEOF_VOID_P EQUAL 8)) # Visual Studio 2019 version 16.7 + # target_compile_options(${arg_target} PRIVATE $<$>:/guard:ehcont>) + # target_link_options(${arg_target} PRIVATE $<$>:/guard:ehcont>) + #endif() + if(MSVC_VERSION GREATER_EQUAL 1929) # Visual Studio 2019 version 16.10 + target_compile_options(${arg_target} PRIVATE /await:strict) + elseif(MSVC_VERSION GREATER_EQUAL 1900) # Visual Studio 2015 + target_compile_options(${arg_target} PRIVATE /await) + endif() + if(MSVC_VERSION GREATER_EQUAL 1930) # Visual Studio 2022 version 17.0 + target_compile_options(${arg_target} PRIVATE /options:strict) + endif() + if(NOT FRAMELESSHELPER_NO_PERMISSIVE_CHECKS) + target_compile_options(${arg_target} PRIVATE + /Zc:auto /Zc:forScope /Zc:implicitNoexcept /Zc:noexceptTypes /Zc:referenceBinding + /Zc:rvalueCast /Zc:sizedDealloc /Zc:strictStrings /Zc:throwingNew /Zc:trigraphs + /Zc:wchar_t + ) + if(MSVC_VERSION GREATER_EQUAL 1900) # Visual Studio 2015 + target_compile_options(${arg_target} PRIVATE /Zc:threadSafeInit) + endif() + if(MSVC_VERSION GREATER_EQUAL 1910) # Visual Studio 2017 version 15.0 + target_compile_options(${arg_target} PRIVATE /permissive- /Zc:ternary) + endif() + if(MSVC_VERSION GREATER_EQUAL 1912) # Visual Studio 2017 version 15.5 + target_compile_options(${arg_target} PRIVATE /Zc:alignedNew) + endif() + if(MSVC_VERSION GREATER_EQUAL 1913) # Visual Studio 2017 version 15.6 + target_compile_options(${arg_target} PRIVATE /Zc:externConstexpr) + endif() + if(MSVC_VERSION GREATER_EQUAL 1914) # Visual Studio 2017 version 15.7 + target_compile_options(${arg_target} PRIVATE /Zc:__cplusplus) + endif() + if(MSVC_VERSION GREATER_EQUAL 1921) # Visual Studio 2019 version 16.1 + target_compile_options(${arg_target} PRIVATE /Zc:char8_t) + endif() + if(MSVC_VERSION GREATER_EQUAL 1923) # Visual Studio 2019 version 16.3 + target_compile_options(${arg_target} PRIVATE /Zc:externC) + endif() + if(MSVC_VERSION GREATER_EQUAL 1924) # Visual Studio 2019 version 16.4 + target_compile_options(${arg_target} PRIVATE /Zc:hiddenFriend) + endif() + if(MSVC_VERSION GREATER_EQUAL 1925) # Visual Studio 2019 version 16.5 + target_compile_options(${arg_target} PRIVATE /Zc:preprocessor /Zc:tlsGuards) + endif() + if(MSVC_VERSION GREATER_EQUAL 1928) # Visual Studio 2019 version 16.8 & 16.9 + target_compile_options(${arg_target} PRIVATE /Zc:lambda /Zc:zeroSizeArrayNew) + endif() + if(MSVC_VERSION GREATER_EQUAL 1931) # Visual Studio 2022 version 17.1 + target_compile_options(${arg_target} PRIVATE /Zc:static_assert) + endif() + if(MSVC_VERSION GREATER_EQUAL 1932) # Visual Studio 2022 version 17.2 + target_compile_options(${arg_target} PRIVATE /Zc:__STDC__) + endif() + if(MSVC_VERSION GREATER_EQUAL 1934) # Visual Studio 2022 version 17.4 + target_compile_options(${arg_target} PRIVATE /Zc:enumTypes /Zc:gotoScope /Zc:nrvo) + endif() + if(MSVC_VERSION GREATER_EQUAL 1935) # Visual Studio 2022 version 17.5 + target_compile_options(${arg_target} PRIVATE /Zc:templateScope /Zc:checkGwOdr) + endif() + endif() + else() + target_compile_options(${arg_target} PRIVATE + -Wall -Wextra -Werror + $<$>:-ffunction-sections -fdata-sections> # -fcf-protection=full? -Wa,-mno-branches-within-32B-boundaries? + ) + if(APPLE) + target_link_options(${arg_target} PRIVATE + $<$>:-Wl,-dead_strip> + ) + else() + target_link_options(${arg_target} PRIVATE + $<$>:-Wl,--gc-sections> + ) + endif() + endif() +endfunction() + +function(setup_gui_app arg_target) + set_target_properties(${arg_target} PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER org.wangwenx190.${arg_target} + MACOSX_BUNDLE_BUNDLE_VERSION 1.0.0.0 + MACOSX_BUNDLE_SHORT_VERSION_STRING 1.0 + ) +endfunction() + +function(setup_package_export arg_target arg_path arg_public arg_alias arg_private) + if(FRAMELESSHELPER_NO_INSTALL) + return() + endif() + include(GNUInstallDirs) + set(__targets ${arg_target}) + if(TARGET ${arg_target}_resources_1) + list(APPEND __targets ${arg_target}_resources_1) # Ugly hack to workaround a CMake configure error. + endif() + install(TARGETS ${__targets} + EXPORT ${arg_target}Targets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${arg_path}" + ) + export(EXPORT ${arg_target}Targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${arg_target}Targets.cmake" + NAMESPACE ${PROJECT_NAME}:: + ) + install(FILES ${arg_public} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${arg_path}") + install(FILES ${arg_alias} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${arg_path}") + install(FILES ${arg_private} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${arg_path}/private") + install(EXPORT ${arg_target}Targets + FILE ${arg_target}Targets.cmake + NAMESPACE ${PROJECT_NAME}:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + ) +endfunction() + +function(deploy_qt_runtime arg_target) + if(NOT (DEFINED Qt5_FOUND OR DEFINED Qt6_FOUND)) + find_package(QT NAMES Qt6 Qt5 QUIET COMPONENTS Core) + endif() + if(NOT (Qt5_FOUND OR Qt6_FOUND)) + message("You need to install the QtCore module first to be able to deploy the Qt libraries.") + return() + endif() + find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) + if(NOT DEFINED QT_QMAKE_EXECUTABLE) # QT_QMAKE_EXECUTABLE is usually defined by QtCreator. + get_target_property(QT_QMAKE_EXECUTABLE Qt::qmake IMPORTED_LOCATION) + endif() + if(NOT EXISTS "${QT_QMAKE_EXECUTABLE}") + message("Cannot find the QMake executable.") + return() + endif() + get_filename_component(QT_BIN_DIRECTORY "${QT_QMAKE_EXECUTABLE}" DIRECTORY) + find_program(QT_DEPLOY_EXECUTABLE NAMES windeployqt macdeployqt HINTS "${QT_BIN_DIRECTORY}") + if(NOT EXISTS "${QT_DEPLOY_EXECUTABLE}") + message("Cannot find the deployqt tool.") + return() + endif() + set(__is_quick_app FALSE) + cmake_parse_arguments(DEPLOY_QT_RUNTIME_ARGS "" "QML_SOURCE_DIR;QML_IMPORT_DIR" "" ${ARGN}) + if(WIN32) + set(__old_deploy_params) + if(${QT_VERSION_MAJOR} LESS 6) + set(__old_deploy_params --no-webkit2 --no-angle) + endif() + set(__quick_deploy_params) + if(DEFINED DEPLOY_QT_RUNTIME_ARGS_QML_SOURCE_DIR) + set(__is_quick_app TRUE) + set(__quick_deploy_params + --dir "$/qml" + --qmldir "${DEPLOY_QT_RUNTIME_ARGS_QML_SOURCE_DIR}" + ) + endif() + if(DEFINED DEPLOY_QT_RUNTIME_ARGS_QML_IMPORT_DIR) + set(__is_quick_app TRUE) + set(__quick_deploy_params + ${__quick_deploy_params} + --qmlimport "${DEPLOY_QT_RUNTIME_ARGS_QML_IMPORT_DIR}" + ) + endif() + add_custom_command(TARGET ${arg_target} POST_BUILD COMMAND + "${QT_DEPLOY_EXECUTABLE}" + $<$:--debug> + $<$,$,$>:--release> + --libdir "$" + --plugindir "$/plugins" + --no-translations + --no-system-d3d-compiler + --no-virtualkeyboard + --no-compiler-runtime + --no-opengl-sw + --force + --verbose 0 + ${__quick_deploy_params} + ${__old_deploy_params} + "$" + ) + elseif(APPLE) + set(__quick_deploy_params) + if(DEFINED DEPLOY_QT_RUNTIME_ARGS_QML_SOURCE_DIR) + set(__is_quick_app TRUE) + set(__quick_deploy_params + -qmldir="${DEPLOY_QT_RUNTIME_ARGS_QML_SOURCE_DIR}" + -qmlimport="${PROJECT_BINARY_DIR}/qml" + ) + endif() + if(DEFINED DEPLOY_QT_RUNTIME_ARGS_QML_IMPORT_DIR) + set(__is_quick_app TRUE) + set(__quick_deploy_params + ${__quick_deploy_params} + -qmlimport="${DEPLOY_QT_RUNTIME_ARGS_QML_IMPORT_DIR}" + ) + endif() + add_custom_command(TARGET ${arg_target} POST_BUILD COMMAND + "${QT_DEPLOY_EXECUTABLE}" + "$" + -verbose=0 + ${__quick_deploy_params} + ) + elseif(UNIX) + # TODO + endif() + if(NOT FRAMELESSHELPER_NO_INSTALL) + include(GNUInstallDirs) + install(TARGETS ${arg_target} + BUNDLE DESTINATION . + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + ) + if(QT_VERSION VERSION_GREATER_EQUAL "6.3") + set(__deploy_script) + if(${__is_quick_app}) + qt_generate_deploy_qml_app_script( + TARGET ${arg_target} + FILENAME_VARIABLE __deploy_script + #MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM + ) + else() + qt_generate_deploy_app_script( + TARGET ${arg_target} + FILENAME_VARIABLE __deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR + ) + endif() + install(SCRIPT "${__deploy_script}") + endif() + endif() +endfunction() + +function(setup_translations arg_target) + # Qt5's CMake functions to create translations lack many features + # we need and what's worse, they also have a severe bug which will + # wipe out our .ts files' contents every time we call them, so we + # really can't use them until Qt6 (the functions have been completely + # re-written in Qt6 and according to my experiments they work reliably + # now finally). + if(NOT DEFINED Qt6LinguistTools_FOUND) + find_package(Qt6 QUIET COMPONENTS LinguistTools) + endif() + if(NOT Qt6LinguistTools_FOUND) + message("You need to install the Qt Linguist Tools first to be able to create translations.") + return() + endif() + cmake_parse_arguments(TRANSLATION_ARGS "" "TS_DIR;QM_DIR;INSTALL_DIR" "LOCALES" ${ARGN}) + if(NOT DEFINED TRANSLATION_ARGS_LOCALES) + message("You need to specify at least one locale.") + return() + endif() + set(__ts_dir translations) + if(DEFINED TRANSLATION_ARGS_TS_DIR) + set(__ts_dir "${TRANSLATION_ARGS_TS_DIR}") + endif() + set(__qm_dir "${PROJECT_BINARY_DIR}/translations") + if(DEFINED TRANSLATION_ARGS_QM_DIR) + set(__qm_dir "${TRANSLATION_ARGS_QM_DIR}") + endif() + set(__ts_files) + foreach(__locale ${TRANSLATION_ARGS_LOCALES}) + list(APPEND __ts_files "${__ts_dir}/${arg_target}_${__locale}.ts") + endforeach() + set_source_files_properties(${__ts_files} PROPERTIES + OUTPUT_LOCATION "${__qm_dir}" + ) + set(__qm_files) + qt_add_translations(${arg_target} + TS_FILES ${__ts_files} + QM_FILES_OUTPUT_VARIABLE __qm_files + LUPDATE_OPTIONS + -no-obsolete # Don't keep vanished translation contexts. + LRELEASE_OPTIONS + -compress # Compress the QM file if the file size can be decreased siginificantly. + -nounfinished # Don't include unfinished translations (to save file size). + -removeidentical # Don't include translations that are the same with their original texts (to save file size). + ) + if(NOT FRAMELESSHELPER_NO_INSTALL) + set(__inst_dir translations) + if(DEFINED TRANSLATION_ARGS_INSTALL_DIR) + set(__inst_dir "${TRANSLATION_ARGS_INSTALL_DIR}") + endif() + install(FILES ${__qm_files} DESTINATION "${__inst_dir}") + endif() +endfunction() diff --git a/FramelessHelper/src/core/framelessconfig.cpp b/FramelessHelper/src/core/framelessconfig.cpp new file mode 100644 index 0000000..dc57276 --- /dev/null +++ b/FramelessHelper/src/core/framelessconfig.cpp @@ -0,0 +1,176 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelessconfig_p.h" +#include +#include +#include +#include +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcFramelessConfig, "wangwenx190.framelesshelper.core.framelessconfig") + +#ifdef FRAMELESSHELPER_CORE_NO_DEBUG_OUTPUT +# define INFO QT_NO_QDEBUG_MACRO() +# define DEBUG QT_NO_QDEBUG_MACRO() +# define WARNING QT_NO_QDEBUG_MACRO() +# define CRITICAL QT_NO_QDEBUG_MACRO() +#else +# define INFO qCInfo(lcFramelessConfig) +# define DEBUG qCDebug(lcFramelessConfig) +# define WARNING qCWarning(lcFramelessConfig) +# define CRITICAL qCCritical(lcFramelessConfig) +#endif + +using namespace Global; + +FRAMELESSHELPER_STRING_CONSTANT2(ConfigFileName, ".framelesshelper.ini") + +static const struct +{ + const QByteArray env = {}; + const QByteArray cfg = {}; +} OptionsTable[] = { + {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_USE_CROSS_PLATFORM_QT_IMPLEMENTATION"), + FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/UseCrossPlatformQtImplementation")}, + {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_FORCE_HIDE_WINDOW_FRAME_BORDER"), + FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/ForceHideWindowFrameBorder")}, + {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_FORCE_SHOW_WINDOW_FRAME_BORDER"), + FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/ForceShowWindowFrameBorder")}, + {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_DISABLE_WINDOWS_SNAP_LAYOUT"), + FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/DisableWindowsSnapLayout")}, + {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_WINDOW_USE_ROUND_CORNERS"), + FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/WindowUseRoundCorners")}, + {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_CENTER_WINDOW_BEFORE_SHOW"), + FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/CenterWindowBeforeShow")}, + {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_ENABLE_BLUR_BEHIND_WINDOW"), + FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/EnableBlurBehindWindow")}, + {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_FORCE_NON_NATIVE_BACKGROUND_BLUR"), + FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/ForceNonNativeBackgroundBlur")}, + {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_DISABLE_LAZY_INITIALIZATION_FOR_MICA_MATERIAL"), + FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/DisableLazyInitializationForMicaMaterial")} +}; + +static constexpr const auto OptionCount = std::size(OptionsTable); + +struct ConfigData +{ + QMutex mutex; + bool loaded = false; + bool options[OptionCount] = {}; + bool disableEnvVar = false; + bool disableCfgFile = false; +}; + +Q_GLOBAL_STATIC(ConfigData, g_data) + +Q_GLOBAL_STATIC(FramelessConfig, g_config) + +static inline void warnInappropriateOptions() +{ + const FramelessConfig * const cfg = FramelessConfig::instance(); +#ifndef Q_OS_WINDOWS + if (cfg->isSet(Option::UseCrossPlatformQtImplementation)) { + WARNING << "Option::UseCrossPlatformQtImplementation is default on non-Windows platforms."; + } + if (cfg->isSet(Option::ForceHideWindowFrameBorder)) { + WARNING << "Option::ForceHideWindowFrameBorder is only available on Windows."; + } + if (cfg->isSet(Option::ForceShowWindowFrameBorder)) { + WARNING << "Option::ForceShowWindowFrameBorder is only available on Windows."; + } + if (cfg->isSet(Option::DisableWindowsSnapLayout)) { + WARNING << "Option::DisableWindowsSnapLayout is only available on Windows."; + } +#endif // Q_OS_WINDOWS + if (cfg->isSet(Option::WindowUseRoundCorners)) { + WARNING << "Option::WindowUseRoundCorners has not been implemented yet."; + } +} + +FramelessConfig::FramelessConfig(QObject *parent) : QObject(parent) +{ + reload(); +} + +FramelessConfig::~FramelessConfig() = default; + +FramelessConfig *FramelessConfig::instance() +{ + return g_config(); +} + +void FramelessConfig::reload(const bool force) +{ + const QMutexLocker locker(&g_data()->mutex); + if (g_data()->loaded && !force) { + return; + } + const auto configFile = []() -> std::unique_ptr { + if (!qApp) { + return nullptr; + } + const QDir appDir(QCoreApplication::applicationDirPath()); + return std::make_unique(appDir.filePath(kConfigFileName), QSettings::IniFormat); + }(); + for (int i = 0; i != OptionCount; ++i) { + const bool envVar = (!g_data()->disableEnvVar + && qEnvironmentVariableIsSet(OptionsTable[i].env.constData()) + && (qEnvironmentVariableIntValue(OptionsTable[i].env.constData()) > 0)); + const bool cfgFile = (!g_data()->disableCfgFile && configFile + && configFile->value(QUtf8String(OptionsTable[i].cfg), false).toBool()); + g_data()->options[i] = (envVar || cfgFile); + } + g_data()->loaded = true; + + QTimer::singleShot(0, this, [](){ warnInappropriateOptions(); }); +} + +void FramelessConfig::set(const Option option, const bool on) +{ + const QMutexLocker locker(&g_data()->mutex); + g_data()->options[static_cast(option)] = on; +} + +bool FramelessConfig::isSet(const Option option) const +{ + const QMutexLocker locker(&g_data()->mutex); + return g_data()->options[static_cast(option)]; +} + +void FramelessConfig::setLoadFromEnvironmentVariablesDisabled(const bool on) +{ + const QMutexLocker locker(&g_data()->mutex); + g_data()->disableEnvVar = on; +} + +void FramelessConfig::setLoadFromConfigurationFileDisabled(const bool on) +{ + const QMutexLocker locker(&g_data()->mutex); + g_data()->disableCfgFile = on; +} + +FRAMELESSHELPER_END_NAMESPACE diff --git a/FramelessHelper/src/core/framelessconfig_p.h b/FramelessHelper/src/core/framelessconfig_p.h new file mode 100644 index 0000000..cb7acd6 --- /dev/null +++ b/FramelessHelper/src/core/framelessconfig_p.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Core/private/framelessconfig_p.h" diff --git a/FramelessHelper/src/core/framelesshelper.rc.in b/FramelessHelper/src/core/framelesshelper.rc.in new file mode 100644 index 0000000..724c914 --- /dev/null +++ b/FramelessHelper/src/core/framelesshelper.rc.in @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 0,0,0,0 +PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@ +FILEFLAGSMASK 0x3fL +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "wangwenx190\0" + VALUE "FileDescription", "@PROJECT_NAME@ @SUB_MOD_NAME@ Module\0" + VALUE "FileVersion", "0.0.0.0\0" + VALUE "LegalCopyright", "MIT License\0" + #ifdef _DEBUG + VALUE "OriginalFilename", "@PROJECT_NAME@@SUB_MOD_NAME@d.dll\0" + #else + VALUE "OriginalFilename", "@PROJECT_NAME@@SUB_MOD_NAME@.dll\0" + #endif + VALUE "ProductName", "@PROJECT_NAME@\0" + VALUE "ProductVersion", "@PROJECT_VERSION@\0" + VALUE "Comments", "Built from commit @PROJECT_VERSION_COMMIT@ on @PROJECT_COMPILE_DATETIME@ (UTC).\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/FramelessHelper/src/core/framelesshelper.version.in b/FramelessHelper/src/core/framelesshelper.version.in new file mode 100644 index 0000000..4a391ce --- /dev/null +++ b/FramelessHelper/src/core/framelesshelper.version.in @@ -0,0 +1,44 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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. + */ + +// Generated automatically by CMake. +// WARNING! DO NOT EDIT THIS FILE MANUALLY! +// ALL CHANGES WILL BE LOST AFTER RE-CONFIGURING! +// Also please do not include this file directly, +// it's designed to be included by FramelessHelper's own headers. + +#ifndef _FRAMELESSHELPER_VERSION_DEFINED_ +#define _FRAMELESSHELPER_VERSION_DEFINED_ + +#pragma once + +[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_MAJOR = @PROJECT_VERSION_MAJOR@; +[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_MINOR = @PROJECT_VERSION_MINOR@; +[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_PATCH = @PROJECT_VERSION_PATCH@; +[[maybe_unused]] inline constexpr const int FRAMELESSHELPER_VERSION_TWEAK = @PROJECT_VERSION_TWEAK@; +[[maybe_unused]] inline constexpr const char FRAMELESSHELPER_VERSION_STR[] = "@PROJECT_VERSION@\0"; +[[maybe_unused]] inline constexpr const char FRAMELESSHELPER_COMMIT_STR[] = "@PROJECT_VERSION_COMMIT@\0"; +[[maybe_unused]] inline constexpr const char FRAMELESSHELPER_COMPILE_DATETIME_STR[] = "@PROJECT_COMPILE_DATETIME@\0"; + +#endif // _FRAMELESSHELPER_VERSION_DEFINED_ diff --git a/FramelessHelper/src/core/framelesshelper_linux.h b/FramelessHelper/src/core/framelesshelper_linux.h new file mode 100644 index 0000000..7bd7db9 --- /dev/null +++ b/FramelessHelper/src/core/framelesshelper_linux.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Core/framelesshelper_linux.h" diff --git a/FramelessHelper/src/core/framelesshelper_qt.cpp b/FramelessHelper/src/core/framelesshelper_qt.cpp new file mode 100644 index 0000000..babadd5 --- /dev/null +++ b/FramelessHelper/src/core/framelesshelper_qt.cpp @@ -0,0 +1,260 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelper_qt.h" +#include +#include +#include +#include "framelessmanager.h" +#include "framelessmanager_p.h" +#include "framelessconfig_p.h" +#include "utils.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcFramelessHelperQt, "wangwenx190.framelesshelper.core.impl.qt") + +#ifdef FRAMELESSHELPER_CORE_NO_DEBUG_OUTPUT +# define INFO QT_NO_QDEBUG_MACRO() +# define DEBUG QT_NO_QDEBUG_MACRO() +# define WARNING QT_NO_QDEBUG_MACRO() +# define CRITICAL QT_NO_QDEBUG_MACRO() +#else +# define INFO qCInfo(lcFramelessHelperQt) +# define DEBUG qCDebug(lcFramelessHelperQt) +# define WARNING qCWarning(lcFramelessHelperQt) +# define CRITICAL qCCritical(lcFramelessHelperQt) +#endif + +using namespace Global; + +struct QtHelperData +{ + SystemParameters params = {}; + FramelessHelperQt *eventFilter = nullptr; + bool cursorShapeChanged = false; + bool leftButtonPressed = false; +}; + +struct QtHelper +{ + QMutex mutex; + QHash data = {}; +}; + +Q_GLOBAL_STATIC(QtHelper, g_qtHelper) + +FramelessHelperQt::FramelessHelperQt(QObject *parent) : QObject(parent) {} + +FramelessHelperQt::~FramelessHelperQt() = default; + +void FramelessHelperQt::addWindow(const SystemParameters ¶ms) +{ + Q_ASSERT(params.isValid()); + if (!params.isValid()) { + return; + } + const WId windowId = params.getWindowId(); + g_qtHelper()->mutex.lock(); + if (g_qtHelper()->data.contains(windowId)) { + g_qtHelper()->mutex.unlock(); + return; + } + QtHelperData data = {}; + data.params = params; + QWindow *window = params.getWindowHandle(); + // Give it a parent so that it can be deleted even if we forget to do so. + data.eventFilter = new FramelessHelperQt(window); + g_qtHelper()->data.insert(windowId, data); + g_qtHelper()->mutex.unlock(); + const auto shouldApplyFramelessFlag = []() -> bool { +#ifdef Q_OS_MACOS + return false; +#elif defined(Q_OS_LINUX) + return !Utils::isCustomDecorationSupported(); +#else // Windows + return true; +#endif // Q_OS_MACOS + }(); +#if (defined(Q_OS_MACOS) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))) + window->setProperty("_q_mac_wantsLayer", 1); +#endif // (defined(Q_OS_MACOS) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))) + if (shouldApplyFramelessFlag) { + params.setWindowFlags(params.getWindowFlags() | Qt::FramelessWindowHint); + } else { +#ifdef Q_OS_LINUX + Q_UNUSED(Utils::tryHideSystemTitleBar(windowId, true)); +#elif defined(Q_OS_MACOS) + Utils::setSystemTitleBarVisible(windowId, false); +#endif // Q_OS_LINUX + } + window->installEventFilter(data.eventFilter); + FramelessHelper::Core::setApplicationOSThemeAware(); +} + +void FramelessHelperQt::removeWindow(const WId windowId) +{ + Q_ASSERT(windowId); + if (!windowId) { + return; + } + const QMutexLocker locker(&g_qtHelper()->mutex); + if (!g_qtHelper()->data.contains(windowId)) { + return; + } + if (const auto eventFilter = g_qtHelper()->data.value(windowId).eventFilter) { + if (QWindow * const window = Utils::findWindow(windowId)) { + window->removeEventFilter(eventFilter); + } + delete eventFilter; + } + g_qtHelper()->data.remove(windowId); +#ifdef Q_OS_MACOS + Utils::removeWindowProxy(windowId); +#endif +} + +bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) +{ + Q_ASSERT(object); + Q_ASSERT(event); + if (!object || !event) { + return false; + } +#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 0)) + // First detect whether we got a theme change event or not, if so, + // inform the user the system theme has changed. + if (Utils::isThemeChangeEvent(event)) { + // Sometimes the FramelessManager instance may be destroyed already. + if (FramelessManager * const manager = FramelessManager::instance()) { + if (FramelessManagerPrivate * const managerPriv = FramelessManagerPrivate::get(manager)) { + managerPriv->notifySystemThemeHasChangedOrNot(); + } + } + return QObject::eventFilter(object, event); + } +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 5, 0)) + // We are only interested in events that are dispatched to top level windows. + if (!object->isWindowType()) { + return QObject::eventFilter(object, event); + } + const QEvent::Type type = event->type(); + // We are only interested in some specific mouse events. + if ((type != QEvent::MouseButtonPress) && (type != QEvent::MouseButtonRelease) + && (type != QEvent::MouseButtonDblClick) && (type != QEvent::MouseMove)) { + return QObject::eventFilter(object, event); + } + const auto window = qobject_cast(object); + const WId windowId = window->winId(); + g_qtHelper()->mutex.lock(); + if (!g_qtHelper()->data.contains(windowId)) { + g_qtHelper()->mutex.unlock(); + return QObject::eventFilter(object, event); + } + const QtHelperData data = g_qtHelper()->data.value(windowId); + g_qtHelper()->mutex.unlock(); + const auto mouseEvent = static_cast(event); + const Qt::MouseButton button = mouseEvent->button(); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + const QPoint scenePos = mouseEvent->scenePosition().toPoint(); + const QPoint globalPos = mouseEvent->globalPosition().toPoint(); +#else + const QPoint scenePos = mouseEvent->windowPos().toPoint(); + const QPoint globalPos = mouseEvent->screenPos().toPoint(); +#endif + const bool windowFixedSize = data.params.isWindowFixedSize(); + const bool ignoreThisEvent = data.params.shouldIgnoreMouseEvents(scenePos); + const bool insideTitleBar = data.params.isInsideTitleBarDraggableArea(scenePos); + const bool dontOverrideCursor = data.params.getProperty(kDontOverrideCursorVar, false).toBool(); + const bool dontToggleMaximize = data.params.getProperty(kDontToggleMaximizeVar, false).toBool(); + switch (type) { + case QEvent::MouseButtonPress: { + if (button == Qt::LeftButton) { + g_qtHelper()->mutex.lock(); + g_qtHelper()->data[windowId].leftButtonPressed = true; + g_qtHelper()->mutex.unlock(); + if (!windowFixedSize) { + const Qt::Edges edges = Utils::calculateWindowEdges(window, scenePos); + if (edges != Qt::Edges{}) { + Utils::startSystemResize(window, edges, globalPos); + event->accept(); + return true; + } + } + } + } break; + case QEvent::MouseButtonRelease: { + if (button == Qt::LeftButton) { + const QMutexLocker locker(&g_qtHelper()->mutex); + g_qtHelper()->data[windowId].leftButtonPressed = false; + } + if (button == Qt::RightButton) { + if (!ignoreThisEvent && insideTitleBar) { + data.params.showSystemMenu(scenePos); + event->accept(); + return true; + } + } + } break; + case QEvent::MouseButtonDblClick: { + if (!dontToggleMaximize && (button == Qt::LeftButton) && !windowFixedSize && !ignoreThisEvent && insideTitleBar) { + Qt::WindowState newWindowState = Qt::WindowNoState; + if (data.params.getWindowState() != Qt::WindowMaximized) { + newWindowState = Qt::WindowMaximized; + } + data.params.setWindowState(newWindowState); + event->accept(); + return true; + } + } break; + case QEvent::MouseMove: { + if (!dontOverrideCursor && !windowFixedSize) { + const Qt::CursorShape cs = Utils::calculateCursorShape(window, scenePos); + if (cs == Qt::ArrowCursor) { + if (data.cursorShapeChanged) { + data.params.unsetCursor(); + const QMutexLocker locker(&g_qtHelper()->mutex); + g_qtHelper()->data[windowId].cursorShapeChanged = false; + } + } else { + data.params.setCursor(cs); + const QMutexLocker locker(&g_qtHelper()->mutex); + g_qtHelper()->data[windowId].cursorShapeChanged = true; + } + } + if (data.leftButtonPressed) { + if (!ignoreThisEvent && insideTitleBar) { + Utils::startSystemMove(window, globalPos); + event->accept(); + return true; + } + } + } break; + default: + break; + } + return QObject::eventFilter(object, event); +} + +FRAMELESSHELPER_END_NAMESPACE diff --git a/FramelessHelper/src/core/framelesshelper_qt.h b/FramelessHelper/src/core/framelesshelper_qt.h new file mode 100644 index 0000000..8f36da7 --- /dev/null +++ b/FramelessHelper/src/core/framelesshelper_qt.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Core/framelesshelper_qt.h" diff --git a/FramelessHelper/src/core/framelesshelper_win.cpp b/FramelessHelper/src/core/framelesshelper_win.cpp new file mode 100644 index 0000000..5539f78 --- /dev/null +++ b/FramelessHelper/src/core/framelesshelper_win.cpp @@ -0,0 +1,1327 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelper_win.h" +#include +#include +#include +#include +#include +#include +#include "framelessmanager.h" +#include "framelessmanager_p.h" +#include "framelessconfig_p.h" +#include "utils.h" +#include "winverhelper_p.h" +#include "framelesshelper_windows.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcFramelessHelperWin, "wangwenx190.framelesshelper.core.impl.win") + +#ifdef FRAMELESSHELPER_CORE_NO_DEBUG_OUTPUT +# define INFO QT_NO_QDEBUG_MACRO() +# define DEBUG QT_NO_QDEBUG_MACRO() +# define WARNING QT_NO_QDEBUG_MACRO() +# define CRITICAL QT_NO_QDEBUG_MACRO() +#else +# define INFO qCInfo(lcFramelessHelperWin) +# define DEBUG qCDebug(lcFramelessHelperWin) +# define WARNING qCWarning(lcFramelessHelperWin) +# define CRITICAL qCCritical(lcFramelessHelperWin) +#endif + +using namespace Global; + +[[maybe_unused]] static constexpr const wchar_t kFallbackTitleBarWindowClassName[] = + L"org.wangwenx190.FramelessHelper.FallbackTitleBarWindow"; +FRAMELESSHELPER_BYTEARRAY_CONSTANT2(Win32MessageTypeName, "windows_generic_MSG") +FRAMELESSHELPER_STRING_CONSTANT(MonitorFromWindow) +FRAMELESSHELPER_STRING_CONSTANT(GetMonitorInfoW) +FRAMELESSHELPER_STRING_CONSTANT(ScreenToClient) +FRAMELESSHELPER_STRING_CONSTANT(ClientToScreen) +FRAMELESSHELPER_STRING_CONSTANT(GetClientRect) +#ifdef Q_PROCESSOR_X86_64 + FRAMELESSHELPER_STRING_CONSTANT(GetWindowLongPtrW) + FRAMELESSHELPER_STRING_CONSTANT(SetWindowLongPtrW) +#else // Q_PROCESSOR_X86_64 + // WinUser.h defines G/SetClassLongPtr as G/SetClassLong due to the + // "Ptr" suffixed APIs are not available on 32-bit platforms, so we + // have to add the following workaround. Undefine the macros and then + // redefine them is also an option but the following solution is more simple. + FRAMELESSHELPER_STRING_CONSTANT2(GetWindowLongPtrW, "GetWindowLongW") + FRAMELESSHELPER_STRING_CONSTANT2(SetWindowLongPtrW, "SetWindowLongW") +#endif // Q_PROCESSOR_X86_64 +FRAMELESSHELPER_STRING_CONSTANT(RegisterClassExW) +FRAMELESSHELPER_STRING_CONSTANT(GetModuleHandleW) +FRAMELESSHELPER_STRING_CONSTANT(CreateWindowExW) +FRAMELESSHELPER_STRING_CONSTANT(SetLayeredWindowAttributes) +FRAMELESSHELPER_STRING_CONSTANT(SetWindowPos) +FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) +FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) +FRAMELESSHELPER_STRING_CONSTANT(UnregisterClassW) +FRAMELESSHELPER_STRING_CONSTANT(DestroyWindow) +[[maybe_unused]] static constexpr const char kFallbackTitleBarErrorMessage[] = + "FramelessHelper is unable to create the fallback title bar window, and thus the snap layout feature will be disabled" + " unconditionally. You can ignore this error and continue running your application, nothing else will be affected, " + "no need to worry. But if you really need the snap layout feature, please add a manifest file to your application and " + "explicitly declare Windows 11 compatibility in it. If you just want to hide this error message, please use the " + "FramelessConfig class to officially disable the snap layout feature for Windows 11."; +[[maybe_unused]] static constexpr const char kD3DWorkaroundEnvVar[] = "FRAMELESSHELPER_USE_D3D_WORKAROUND"; + +struct Win32HelperData +{ + SystemParameters params = {}; + bool trackingMouse = false; + WId fallbackTitleBarWindowId = 0; + Dpi dpi = {}; +}; + +struct Win32Helper +{ + QMutex mutex; + std::unique_ptr nativeEventFilter = nullptr; + QHash data = {}; + QHash fallbackTitleBarToParentWindowMapping = {}; +}; + +Q_GLOBAL_STATIC(Win32Helper, g_win32Helper) + +[[nodiscard]] static inline QString hwnd2str(const WId windowId) +{ + // NULL handle is allowed here. + return FRAMELESSHELPER_STRING_LITERAL("0x") + + QString::number(windowId, 16).toUpper(); +} + +[[nodiscard]] static inline QString hwnd2str(const HWND hwnd) +{ + // NULL handle is allowed here. + return hwnd2str(reinterpret_cast(hwnd)); +} + +[[nodiscard]] static inline LRESULT CALLBACK FallbackTitleBarWindowProc + (const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) +{ + Q_ASSERT(hWnd); + if (!hWnd) { + return 0; + } + // WM_QUIT won't be posted to the WindowProc function. + if (uMsg == WM_CLOSE) { + if (DestroyWindow(hWnd) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kDestroyWindow); + } + return 0; + } + if (uMsg == WM_DESTROY) { + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + const auto windowId = reinterpret_cast(hWnd); + g_win32Helper()->mutex.lock(); + if (!g_win32Helper()->fallbackTitleBarToParentWindowMapping.contains(windowId)) { + g_win32Helper()->mutex.unlock(); + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + const WId parentWindowId = g_win32Helper()->fallbackTitleBarToParentWindowMapping.value(windowId); + if (!g_win32Helper()->data.contains(parentWindowId)) { + g_win32Helper()->mutex.unlock(); + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + const Win32HelperData data = g_win32Helper()->data.value(parentWindowId); + g_win32Helper()->mutex.unlock(); + const auto parentWindowHandle = reinterpret_cast(parentWindowId); + // All mouse events: client area mouse events + non-client area mouse events. + // Hit-testing event should not be considered as a mouse event. + const bool isMouseEvent = (((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST)) || + ((uMsg >= WM_NCMOUSEMOVE) && (uMsg <= WM_NCXBUTTONDBLCLK))); + const auto releaseButtons = [&data](const std::optional exclude) -> void { + static constexpr const auto defaultButtonState = ButtonState::Unspecified; + const SystemButtonType button = exclude.value_or(SystemButtonType::Unknown); + if (button != SystemButtonType::WindowIcon) { + data.params.setSystemButtonState(SystemButtonType::WindowIcon, defaultButtonState); + } + if (button != SystemButtonType::Help) { + data.params.setSystemButtonState(SystemButtonType::Help, defaultButtonState); + } + if (button != SystemButtonType::Minimize) { + data.params.setSystemButtonState(SystemButtonType::Minimize, defaultButtonState); + } + if (button != SystemButtonType::Maximize) { + data.params.setSystemButtonState(SystemButtonType::Maximize, defaultButtonState); + } + if (button != SystemButtonType::Restore) { + data.params.setSystemButtonState(SystemButtonType::Restore, defaultButtonState); + } + if (button != SystemButtonType::Close) { + data.params.setSystemButtonState(SystemButtonType::Close, defaultButtonState); + } + }; + const auto hoverButton = [&releaseButtons, &data](const SystemButtonType button) -> void { + releaseButtons(button); + data.params.setSystemButtonState(button, ButtonState::Hovered); + }; + const auto pressButton = [&releaseButtons, &data](const SystemButtonType button) -> void { + releaseButtons(button); + data.params.setSystemButtonState(button, ButtonState::Pressed); + }; + const auto clickButton = [&releaseButtons, &data](const SystemButtonType button) -> void { + releaseButtons(button); + data.params.setSystemButtonState(button, ButtonState::Clicked); + }; + switch (uMsg) { + case WM_NCHITTEST: { + // Try to determine what part of the window is being hovered here. This + // is absolutely critical to making sure the snap layout works! + const POINT nativeGlobalPos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + POINT nativeLocalPos = nativeGlobalPos; + if (ScreenToClient(hWnd, &nativeLocalPos) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kScreenToClient); + break; + } + const QPoint qtScenePos = Utils::fromNativePixels(data.params.getWindowHandle(), + QPoint(nativeLocalPos.x, nativeLocalPos.y)); + SystemButtonType buttonType = SystemButtonType::Unknown; + if (data.params.isInsideSystemButtons(qtScenePos, &buttonType)) { + switch (buttonType) { + case SystemButtonType::Unknown: + Q_UNREACHABLE_RETURN(HTNOWHERE); + case SystemButtonType::WindowIcon: + return HTSYSMENU; + case SystemButtonType::Help: + return HTHELP; + case SystemButtonType::Minimize: + return HTREDUCE; + case SystemButtonType::Maximize: + case SystemButtonType::Restore: + return HTZOOM; + case SystemButtonType::Close: + return HTCLOSE; + } + } + // Returns "HTTRANSPARENT" to let the mouse event pass through this invisible + // window to the parent window beneath it, otherwise all the controls under it + // can't be hovered. + return HTTRANSPARENT; + } + case WM_NCMOUSEMOVE: { + // When we get this message, it's because the mouse moved when it was + // over somewhere we said was the non-client area. + // + // We'll use this to communicate state to the title bar control, so that + // it can update its visuals. + // - If we're over a button, hover it. + // - If we're over _anything else_, stop hovering the buttons. + switch (wParam) { + case HTTOP: + case HTCAPTION: { + releaseButtons(std::nullopt); + // Pass caption-related nonclient messages to the parent window. + // Make sure to do this for the HTTOP, which is the top resize + // border, so we can resize the window on the top. + return SendMessageW(parentWindowHandle, uMsg, wParam, lParam); + } + case HTSYSMENU: + hoverButton(SystemButtonType::WindowIcon); + break; + case HTHELP: + hoverButton(SystemButtonType::Help); + break; + case HTREDUCE: + hoverButton(SystemButtonType::Minimize); + break; + case HTZOOM: + hoverButton(SystemButtonType::Maximize); + break; + case HTCLOSE: + hoverButton(SystemButtonType::Close); + break; + default: + releaseButtons(std::nullopt); + break; + } + // If we haven't previously asked for mouse tracking, request mouse + // tracking. We need to do this so we can get the WM_NCMOUSELEAVE + // message when the mouse leave the title bar. Otherwise, we won't always + // get that message (especially if the user moves the mouse _real + // fast_). + if (!data.trackingMouse && ((wParam == HTSYSMENU) || (wParam == HTHELP) + || (wParam == HTREDUCE) || (wParam == HTZOOM) || (wParam == HTCLOSE))) { + TRACKMOUSEEVENT tme; + SecureZeroMemory(&tme, sizeof(tme)); + tme.cbSize = sizeof(tme); + // TME_NONCLIENT is absolutely critical here. In my experimentation, + // we'd get WM_MOUSELEAVE messages after just a HOVER_DEFAULT + // timeout even though we're not requesting TME_HOVER, which kinda + // ruined the whole point of this. + tme.dwFlags = (TME_LEAVE | TME_NONCLIENT); + tme.hwndTrack = hWnd; + tme.dwHoverTime = HOVER_DEFAULT; // We don't _really_ care about this. + if (TrackMouseEvent(&tme) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kTrackMouseEvent); + break; + } + const QMutexLocker locker(&g_win32Helper()->mutex); + g_win32Helper()->data[parentWindowId].trackingMouse = true; + } + } break; + case WM_NCMOUSELEAVE: + case WM_MOUSELEAVE: { + // When the mouse leaves the drag rect, make sure to dismiss any hover. + releaseButtons(std::nullopt); + const QMutexLocker locker(&g_win32Helper()->mutex); + g_win32Helper()->data[parentWindowId].trackingMouse = false; + } break; + // NB: *Shouldn't be forwarding these* when they're not over the caption + // because they can inadvertently take action using the system's default + // metrics instead of our own. + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONDBLCLK: { + // Manual handling for mouse clicks in the fallback title bar. If it's in a + // caption button, then tell the title bar to "press" the button, which + // should change its visual state. + // + // If it's not in a caption button, then just forward the message along + // to the root HWND. Make sure to do this for the HTTOP, which is the + // top resize border. + switch (wParam) { + case HTTOP: + case HTCAPTION: + // Pass caption-related nonclient messages to the parent window. + return SendMessageW(parentWindowHandle, uMsg, wParam, lParam); + // The buttons won't work as you'd expect; we need to handle those + // ourselves. + case HTSYSMENU: + pressButton(SystemButtonType::WindowIcon); + break; + case HTHELP: + pressButton(SystemButtonType::Help); + break; + case HTREDUCE: + pressButton(SystemButtonType::Minimize); + break; + case HTZOOM: + pressButton(SystemButtonType::Maximize); + break; + case HTCLOSE: + pressButton(SystemButtonType::Close); + break; + default: + break; + } + return 0; + } + case WM_NCLBUTTONUP: { + // Manual handling for mouse RELEASES in the fallback title bar. If it's in a + // caption button, then manually handle what we'd expect for that button. + // + // If it's not in a caption button, then just forward the message along + // to the root HWND. + switch (wParam) { + case HTTOP: + case HTCAPTION: + // Pass caption-related nonclient messages to the parent window. + return SendMessageW(parentWindowHandle, uMsg, wParam, lParam); + // The buttons won't work as you'd expect; we need to handle those ourselves. + case HTSYSMENU: + clickButton(SystemButtonType::WindowIcon); + break; + case HTHELP: + clickButton(SystemButtonType::Help); + break; + case HTREDUCE: + clickButton(SystemButtonType::Minimize); + break; + case HTZOOM: + clickButton(SystemButtonType::Maximize); + break; + case HTCLOSE: + clickButton(SystemButtonType::Close); + break; + default: + break; + } + return 0; + } + // Make sure to pass along right-clicks in this region to our parent window + // - we don't need to handle these. + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONDBLCLK: + case WM_NCRBUTTONUP: + return SendMessageW(parentWindowHandle, uMsg, wParam, lParam); + default: + break; + } + // Forward all the mouse events we don't handle here to the parent window, + // this is a necessary step to make sure the child widgets/quick items can still + // receive mouse events from our homemade title bar. + if (isMouseEvent) { + SendMessageW(parentWindowHandle, uMsg, wParam, lParam); + return 0; // There's nothing to do in this invisible window, so ignore it. + } + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +[[nodiscard]] static inline bool resizeFallbackTitleBarWindow + (const WId parentWindowId, const WId fallbackTitleBarWindowId, const bool hide) +{ + Q_ASSERT(parentWindowId); + Q_ASSERT(fallbackTitleBarWindowId); + if (!parentWindowId || !fallbackTitleBarWindowId) { + return false; + } + const auto parentWindowHandle = reinterpret_cast(parentWindowId); + RECT parentWindowClientRect = {}; + if (GetClientRect(parentWindowHandle, &parentWindowClientRect) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kGetClientRect); + return false; + } + const int titleBarHeight = Utils::getTitleBarHeight(parentWindowId, true); + const auto fallbackTitleBarWindowHandle = reinterpret_cast(fallbackTitleBarWindowId); + const UINT flags = (SWP_NOACTIVATE | (hide ? SWP_HIDEWINDOW : SWP_SHOWWINDOW)); + // As you can see from the code, we only use the fallback title bar window to activate the + // snap layout feature introduced in Windows 11. So you may wonder, why not just + // limit it to the area of the three system buttons, instead of covering the + // whole title bar area? Well, I've tried that solution already and unfortunately + // it doesn't work. And according to my experiment, it won't work either even if we + // only reduce the window width for some pixels. So we have to make it expand to the + // full width of the parent window to let it occupy the whole top area, and this time + // it finally works. Since our current solution works well, I have no interest in digging + // into all the magic behind it. + if (SetWindowPos(fallbackTitleBarWindowHandle, HWND_TOP, 0, 0, + parentWindowClientRect.right, titleBarHeight, flags) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kSetWindowPos); + return false; + } + return true; +} + +[[nodiscard]] static inline bool createFallbackTitleBarWindow(const WId parentWindowId, const bool hide) +{ + Q_ASSERT(parentWindowId); + if (!parentWindowId) { + return false; + } + if (!WindowsVersionHelper::isWin10OrGreater()) { + WARNING << "The fallback title bar window is only supported on Windows 10 and onwards."; + return false; + } + const auto parentWindowHandle = reinterpret_cast(parentWindowId); + const HINSTANCE instance = GetModuleHandleW(nullptr); + Q_ASSERT(instance); + if (!instance) { + WARNING << Utils::getSystemErrorMessage(kGetModuleHandleW); + return false; + } + static const auto fallbackTitleBarWindowClass = [instance]() -> bool { + WNDCLASSEXW wcex = {}; + // First try to find out if we have registered the window class already. + if (GetClassInfoExW(instance, kFallbackTitleBarWindowClassName, &wcex) != FALSE) { + // Register the same window class for multiple times will fail. + return true; + } + SecureZeroMemory(&wcex, sizeof(wcex)); + wcex.cbSize = sizeof(wcex); + // The "CS_DBLCLKS" style is necessary, don't remove it! + wcex.style = CS_DBLCLKS; + wcex.lpszClassName = kFallbackTitleBarWindowClassName; + wcex.hbrBackground = static_cast(GetStockObject(BLACK_BRUSH)); + wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW); + wcex.lpfnWndProc = FallbackTitleBarWindowProc; + wcex.hInstance = instance; + if (RegisterClassExW(&wcex) != INVALID_ATOM) { + FramelessHelper::Core::registerUninitializeHook([](){ + const HINSTANCE instance = GetModuleHandleW(nullptr); + if (!instance) { + //WARNING << Utils::getSystemErrorMessage(kGetModuleHandleW); + return; + } + if (UnregisterClassW(kFallbackTitleBarWindowClassName, instance) == FALSE) { + //WARNING << Utils::getSystemErrorMessage(kUnregisterClassW); + } + }); + return true; + } + WARNING << Utils::getSystemErrorMessage(kRegisterClassExW); + return false; + }(); + Q_ASSERT(fallbackTitleBarWindowClass); + if (!fallbackTitleBarWindowClass) { + WARNING << "Failed to register the window class for the fallback title bar window."; + return false; + } + const HWND fallbackTitleBarWindowHandle = CreateWindowExW((WS_EX_LAYERED | WS_EX_NOREDIRECTIONBITMAP), + kFallbackTitleBarWindowClassName, nullptr, WS_CHILD, 0, 0, 0, 0, + parentWindowHandle, nullptr, instance, nullptr); + // Some users reported that when using MinGW, the following assert won't trigger any + // message box and will just crash, that's absolutely not what we would want to see. + // And many users think they encountered FramelessHelper bugs when they get the assert + // error, so let's just remove this assert anyway. It is meant to give the user some + // hint about how to use the snap layout feature, but now it seems things are going + // to a wrong direction instead. + //Q_ASSERT_X(fallbackTitleBarWindowHandle, __FUNCTION__, kFallbackTitleBarErrorMessage); + if (!fallbackTitleBarWindowHandle) { + WARNING << Utils::getSystemErrorMessage(kCreateWindowExW); + WARNING << kFallbackTitleBarErrorMessage; + return false; + } + // Layered windows won't become visible unless we call the SetLayeredWindowAttributes() + // or UpdateLayeredWindow() function at least once. + if (SetLayeredWindowAttributes(fallbackTitleBarWindowHandle, 0, 255, LWA_ALPHA) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kSetLayeredWindowAttributes); + return false; + } + const auto fallbackTitleBarWindowId = reinterpret_cast(fallbackTitleBarWindowHandle); + if (!resizeFallbackTitleBarWindow(parentWindowId, fallbackTitleBarWindowId, hide)) { + WARNING << "Failed to re-position the fallback title bar window."; + return false; + } + const QMutexLocker locker(&g_win32Helper()->mutex); + g_win32Helper()->data[parentWindowId].fallbackTitleBarWindowId = fallbackTitleBarWindowId; + g_win32Helper()->fallbackTitleBarToParentWindowMapping.insert(fallbackTitleBarWindowId, parentWindowId); + return true; +} + +FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {} + +FramelessHelperWin::~FramelessHelperWin() = default; + +void FramelessHelperWin::addWindow(const SystemParameters ¶ms) +{ + Q_ASSERT(params.isValid()); + if (!params.isValid()) { + return; + } + const WId windowId = params.getWindowId(); + g_win32Helper()->mutex.lock(); + if (g_win32Helper()->data.contains(windowId)) { + g_win32Helper()->mutex.unlock(); + return; + } + Win32HelperData data = {}; + data.params = params; + data.dpi = {Utils::getWindowDpi(windowId, true), Utils::getWindowDpi(windowId, false)}; + g_win32Helper()->data.insert(windowId, data); + if (!g_win32Helper()->nativeEventFilter) { + g_win32Helper()->nativeEventFilter = std::make_unique(); + qApp->installNativeEventFilter(g_win32Helper()->nativeEventFilter.get()); + } + g_win32Helper()->mutex.unlock(); + DEBUG.noquote() << "The DPI of window" << hwnd2str(windowId) << "is" << data.dpi; +#if 0 + params.setWindowFlags(params.getWindowFlags() | Qt::FramelessWindowHint); + // We need some delay here, otherwise the window styles will be overwritten by + // QPA itself. But don't use QThread::sleep(), it doesn't help in our case. + QTimer::singleShot(0, qApp, [windowId](){ + Utils::maybeFixupQtInternals(windowId); + }); +#else + // Qt maintains a frame margin internally, we need to update it accordingly + // otherwise we'll get lots of warning messages when we change the window + // geometry, it will also affect the final window geometry because QPA will + // always take it into account when setting window size and position. + Utils::updateInternalWindowFrameMargins(params.getWindowHandle(), true); +#endif + // Tell DWM our preferred frame margin. + Utils::updateWindowFrameMargins(windowId, false); + // Tell DWM we don't use the window icon/caption/sysmenu, don't draw them. + Utils::hideOriginalTitleBarElements(windowId); + // Without this hack, the child windows can't get DPI change messages from + // Windows, which means only the top level windows can be scaled to the correct + // size, we of course don't want such thing from happening. + Utils::fixupChildWindowsDpiMessage(windowId); + if (WindowsVersionHelper::isWin10RS1OrGreater()) { + // Tell DWM we may need dark theme non-client area (title bar & frame border). + FramelessHelper::Core::setApplicationOSThemeAware(); + if (WindowsVersionHelper::isWin10RS5OrGreater()) { + const bool dark = Utils::shouldAppsUseDarkMode(); + const auto isWidget = [¶ms]() -> bool { + const auto widget = params.getWidgetHandle(); + return (widget && widget->isWidgetType()); + }(); + if (!isWidget) { + // Tell UXTheme we may need dark theme controls. + // Causes some QtWidgets paint incorrectly, so only apply to Qt Quick applications. + Utils::updateGlobalWin32ControlsTheme(windowId, dark); + } + Utils::refreshWin32ThemeResources(windowId, dark); + if (WindowsVersionHelper::isWin11OrGreater()) { + // The fallback title bar window is only used to activate the Snap Layout feature + // introduced in Windows 11, so it's not necessary to create it on systems below Win11. + if (!FramelessConfig::instance()->isSet(Option::DisableWindowsSnapLayout)) { + if (!createFallbackTitleBarWindow(windowId, params.isWindowFixedSize())) { + WARNING << "Failed to create the fallback title bar window."; + } + } + } + } + } +} + +void FramelessHelperWin::removeWindow(const WId windowId) +{ + Q_ASSERT(windowId); + if (!windowId) { + return; + } + g_win32Helper()->mutex.lock(); + if (!g_win32Helper()->data.contains(windowId)) { + g_win32Helper()->mutex.unlock(); + return; + } + g_win32Helper()->data.remove(windowId); + if (g_win32Helper()->data.isEmpty()) { + if (g_win32Helper()->nativeEventFilter) { + qApp->removeNativeEventFilter(g_win32Helper()->nativeEventFilter.get()); + g_win32Helper()->nativeEventFilter.reset(); + } + } + HWND hwnd = nullptr; + auto it = g_win32Helper()->fallbackTitleBarToParentWindowMapping.constBegin(); + while (it != g_win32Helper()->fallbackTitleBarToParentWindowMapping.constEnd()) { + if (it.value() == windowId) { + const WId key = it.key(); + hwnd = reinterpret_cast(key); + g_win32Helper()->fallbackTitleBarToParentWindowMapping.remove(key); + break; + } + ++it; + } + g_win32Helper()->mutex.unlock(); + if (DestroyWindow(reinterpret_cast(hwnd)) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kDestroyWindow); + } +} + +bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) +{ + if ((eventType != kWin32MessageTypeName) || !message || !result) { + return false; + } + // QPA by default stores the global mouse position in the pt field, + // but let's not reply on such Qt-specific extensions. +#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1)) + // Work-around a bug caused by typo which only exists in Qt 5.11.1 + const auto msg = *static_cast(message); +#else + const auto msg = static_cast(message); +#endif + const HWND hWnd = msg->hwnd; + if (!hWnd) { + // Why sometimes the window handle is null? Is it designed to be like this? + // Anyway, we should skip the entire processing in this case. + return false; + } + // Let's be extra safe. + if (IsWindow(hWnd) == FALSE) { + return false; + } + const UINT uMsg = msg->message; + // WM_QUIT won't be posted to the WindowProc function. + if ((uMsg == WM_CLOSE) || (uMsg == WM_DESTROY)) { + return false; + } + const auto windowId = reinterpret_cast(hWnd); + g_win32Helper()->mutex.lock(); + if (!g_win32Helper()->data.contains(windowId)) { + g_win32Helper()->mutex.unlock(); + return false; + } + const Win32HelperData data = g_win32Helper()->data.value(windowId); + g_win32Helper()->mutex.unlock(); + const bool frameBorderVisible = Utils::isWindowFrameBorderVisible(); + const WPARAM wParam = msg->wParam; + const LPARAM lParam = msg->lParam; + switch (uMsg) { +#if (QT_VERSION < QT_VERSION_CHECK(5, 9, 0)) // Qt has done this for us since 5.9.0 + case WM_NCCREATE: { + // Enable automatic DPI scaling for the non-client area of the window, + // such as the caption bar, the scrollbars, and the menu bar. We need + // to do this explicitly and manually here (only inside WM_NCCREATE). + // If we are using the PMv2 DPI awareness mode, the non-client area + // of the window will be scaled by the OS automatically, so there will + // be no need to do this in that case. + Utils::enableNonClientAreaDpiScalingForWindow(windowId); + } break; +#endif + case WM_NCCALCSIZE: { + // Windows鏄牴鎹繖涓秷鎭殑杩斿洖鍊兼潵璁剧疆绐楀彛鐨勫鎴峰尯锛堢獥鍙d腑鐪熸鏄剧ず鐨勫唴瀹癸級 + // 鍜岄潪瀹㈡埛鍖猴紙鏍囬鏍忋佺獥鍙h竟妗嗐佽彍鍗曟爮鍜岀姸鎬佹爮绛塛indows绯荤粺鑷鎻愪緵鐨勯儴鍒 + // 锛屼笉杩囧浜嶲t鏉ヨ锛岄櫎浜嗘爣棰樻爮鍜岀獥鍙h竟妗嗭紝闈炲鎴峰尯鍩烘湰涔熼兘鏄嚜缁樼殑锛夌殑鑼 + // 鍥寸殑锛宭Param閲屽瓨鏀剧殑灏辨槸鏂板鎴峰尯鐨勫嚑浣曞尯鍩燂紝榛樿鏄暣涓獥鍙g殑澶у皬锛屾甯 + // 鐨勭▼搴忛渶瑕佷慨鏀硅繖涓弬鏁帮紝鍛婄煡绯荤粺绐楀彛鐨勫鎴峰尯鍜岄潪瀹㈡埛鍖虹殑鑼冨洿锛堜竴鑸潵璇村彲 + // 浠ュ畬鍏ㄤ氦缁橶indows锛岃鍏惰嚜琛屽鐞嗭紝浣跨敤榛樿鐨勫鎴峰尯鍜岄潪瀹㈡埛鍖猴級锛屽洜姝ゅ鏋 + // 鎴戜滑涓嶄慨鏀筶Param锛屽氨鍙互浣垮鎴峰尯鍏呮弧鏁翠釜绐楀彛锛屼粠鑰屽幓鎺夋爣棰樻爮鍜岀獥鍙h竟妗 + // 锛堝洜涓鸿繖浜涗笢瑗块兘琚鎴峰尯缁欑洊浣忎簡銆備絾杈规闃村奖涔熶細鍥犳鑰屼涪澶憋紝涓嶈繃鎴戜滑浼氫娇 + // 鐢ㄥ叾浠栨柟寮忓皢鍏跺甫鍥烇紝璇峰弬鑰冨叾浠栨秷鎭殑澶勭悊锛屾澶勪笉杩囧鎻愬強锛夈備絾鏈変釜鎯呭喌瑕 + // 鐗瑰埆娉ㄦ剰锛岄偅灏辨槸绐楀彛鏈澶у寲鍚庯紝绐楀彛鐨勫疄闄呭昂瀵镐細姣斿睆骞曠殑灏哄澶т竴鐐癸紝浠庤屼娇 + // 鐢ㄦ埛鐪嬩笉鍒扮獥鍙g殑杈圭晫锛岃繖鏍风敤鎴峰氨涓嶈兘鍦ㄧ獥鍙f渶澶у寲鍚庤皟鏁寸獥鍙g殑澶у皬浜嗭紙铏界劧 + // 杩欎釜鍋氭硶鍚捣鏉ョ壒鍒鎬紝浣哤indows纭疄灏辨槸杩欐牱鍋氱殑锛夛紝鍥犳濡傛灉鎴戜滑瑕佽嚜琛 + // 澶勭悊绐楀彛鐨勯潪瀹㈡埛鍖猴紝灏辫鍦ㄧ獥鍙f渶澶у寲鍚庯紝灏嗙獥鍙h竟妗嗙殑瀹藉害鍜岄珮搴︼紙涓鑸槸鐩 + // 绛夌殑锛変粠瀹㈡埛鍖鸿鍓帀锛屽惁鍒欐垜浠獥鍙f墍鏄剧ず鐨勫唴瀹瑰氨浼氳秴鍑哄睆骞曡竟鐣岋紝鏄剧ず涓嶅叏銆 + // 濡傛灉鐢ㄦ埛寮鍚簡浠诲姟鏍忚嚜鍔ㄩ殣钘忥紝鍦ㄧ獥鍙f渶澶у寲鍚庯紝杩樿鑰冭檻浠诲姟鏍忕殑浣嶇疆銆傚洜涓 + // 濡傛灉绐楀彛鏈澶у寲鍚庯紝鍏跺昂瀵稿拰灞忓箷灏哄鐩哥瓑锛堝洜涓轰换鍔℃爮闅愯棌浜嗭紝鎵浠ョ獥鍙f渶澶у寲 + // 鍚庡叾瀹炴槸鍏呮弧浜嗘暣涓睆骞曪紝鍙樼浉鐨勫叏灞忎簡锛夛紝Windows浼氳涓虹獥鍙e凡缁忚繘鍏ュ叏灞忕殑 + // 鐘舵侊紝浠庤屽鑷磋嚜鍔ㄩ殣钘忕殑浠诲姟鏍忔棤娉曞脊鍑恒傝閬垮厤杩欎釜鐘跺喌锛屽氨瑕佷娇绐楀彛鐨勫昂瀵 + // 灏忎簬灞忓箷灏哄銆傛垜涓嬮潰鐨勫仛娉曞弬鑰冧簡鐏嫄銆丆hromium鍜學indows Terminal + // 濡傛灉娌℃湁寮鍚换鍔℃爮鑷姩闅愯棌锛屾槸涓嶅瓨鍦ㄨ繖涓棶棰樼殑锛屾墍浠ヨ鍏堣繘琛屽垽鏂 + // 涓鑸儏鍐典笅锛*result璁剧疆涓0锛堢浉褰撲簬DefWindowProc鐨勮繑鍥炲间负0锛夊氨鍙互浜嗭紝 + // 鏍规嵁MSDN鐨勮娉曪紝杩斿洖0鎰忎负姝ゆ秷鎭凡缁忚绋嬪簭鑷澶勭悊浜嗭紝璁¦indows璺宠繃姝ゆ秷 + // 鎭紝鍚﹀垯Windows浼氭坊鍔犲姝ゆ秷鎭殑榛樿澶勭悊锛屽浜庡綋鍓嶈繖涓秷鎭岃█锛屽氨鎰忓懗鐫 + // 鏍囬鏍忓拰绐楀彛杈规鍙堜細鍥炴潵锛岃繖褰撶劧涓嶆槸鎴戜滑鎯宠鐨勭粨鏋溿傛牴鎹甅SDN锛屽綋wParam + // 涓篎ALSE鏃讹紝鍙兘杩斿洖0锛屼絾褰撳叾涓篢RUE鏃讹紝鍙互杩斿洖0锛屼篃鍙互杩斿洖涓涓猈VR_甯 + // 閲忋傛牴鎹瓹hromium鐨勬敞閲婏紝褰撳瓨鍦ㄩ潪瀹㈡埛鍖烘椂锛屽鏋滆繑鍥濿VR_REDRAW浼氬鑷村瓙 + // 绐楀彛/瀛愭帶浠跺嚭鐜板鎬殑bug锛堣嚜缁樻帶浠堕敊浣嶏級锛屽苟涓擫ucas鍦╓indows 10 + // 涓婃垚鍔熷鐜帮紝璇存槑杩欎釜bug鑷充粖閮芥病鏈夎В鍐炽傛垜鏌ラ槄浜嗗ぇ閲忚祫鏂欙紝鍙戠幇鍞竴鐨勮В鍐 + // 鏂规灏辨槸杩斿洖0銆備絾濡傛灉涓嶅瓨鍦ㄩ潪瀹㈡埛鍖猴紝涓攚Param涓篢RUE锛屾渶濂借繑鍥 + // WVR_REDRAW锛屽惁鍒欑獥鍙e湪璋冩暣澶у皬鍙兘浼氫骇鐢熶弗閲嶇殑闂儊鐜拌薄銆 + // 铏界劧瀵瑰ぇ澶氭暟娑堟伅鏉ヨ锛岃繑鍥0閮戒唬琛ㄨWindows蹇界暐姝ゆ秷鎭紝浣嗗疄闄呬笂涓嶅悓娑堟伅 + // 鑳芥帴鍙楃殑杩斿洖鍊兼槸涓嶄竴鏍风殑锛岃娉ㄦ剰鑷鏌ラ槄MSDN銆 + + // Sent when the size and position of a window's client area must be + // calculated. By processing this message, an application can + // control the content of the window's client area when the size or + // position of the window changes. If wParam is TRUE, lParam points + // to an NCCALCSIZE_PARAMS structure that contains information an + // application can use to calculate the new size and position of the + // client rectangle. If wParam is FALSE, lParam points to a RECT + // structure. On entry, the structure contains the proposed window + // rectangle for the window. On exit, the structure should contain + // the screen coordinates of the corresponding window client area. + // The client area is the window's content area, the non-client area + // is the area which is provided by the system, such as the title + // bar, the four window borders, the frame shadow, the menu bar, the + // status bar, the scroll bar, etc. But for Qt, it draws most of the + // window area (client + non-client) itself. We now know that the + // title bar and the window frame is in the non-client area and we + // can set the scope of the client area in this message, so we can + // remove the title bar and the window frame by let the non-client + // area be covered by the client area (because we can't really get + // rid of the non-client area, it will always be there, all we can + // do is to hide it) , which means we should let the client area's + // size the same with the whole window's size. So there is no room + // for the non-client area and then the user won't be able to see it + // again. But how to achieve this? Very easy, just leave lParam (the + // re-calculated client area) untouched. But of course you can + // modify lParam, then the non-client area will be seen and the + // window borders and the window frame will show up. However, things + // are quite different when you try to modify the top margin of the + // client area. DWM will always draw the whole title bar no matter + // what margin value you set for the top, unless you don't modify it + // and remove the whole top area (the title bar + the one pixel + // height window border). This can be confirmed in Windows + // Terminal's source code, you can also try yourself to verify + // it. So things will become quite complicated if you want to + // preserve the four window borders. + + // If `wParam` is `FALSE`, `lParam` points to a `RECT` that contains + // the proposed window rectangle for our window. During our + // processing of the `WM_NCCALCSIZE` message, we are expected to + // modify the `RECT` that `lParam` points to, so that its value upon + // our return is the new client area. We must return 0 if `wParam` + // is `FALSE`. + // If `wParam` is `TRUE`, `lParam` points to a `NCCALCSIZE_PARAMS` + // struct. This struct contains an array of 3 `RECT`s, the first of + // which has the exact same meaning as the `RECT` that is pointed to + // by `lParam` when `wParam` is `FALSE`. The remaining `RECT`s, in + // conjunction with our return value, can + // be used to specify portions of the source and destination window + // rectangles that are valid and should be preserved. We opt not to + // implement an elaborate client-area preservation technique, and + // simply return 0, which means "preserve the entire old client area + // and align it with the upper-left corner of our new client area". + const auto clientRect = ((static_cast(wParam) == FALSE) ? + reinterpret_cast(lParam) : &(reinterpret_cast(lParam))->rgrc[0]); + if (frameBorderVisible) { + // Store the original top margin before the default window procedure applies the default frame. + const LONG originalTop = clientRect->top; + // Apply the default frame because we don't want to remove the whole window frame, + // we still need the standard window frame (the resizable frame border and the frame + // shadow) for the left, bottom and right edges. + // If we return 0 here directly, the whole window frame will be removed (which means + // there will be no resizable frame border and the frame shadow will also disappear), + // and that's also how most applications customize their title bars on Windows. It's + // totally OK but since we want to preserve as much original frame as possible, we + // can't use that solution. + const LRESULT ret = DefWindowProcW(hWnd, WM_NCCALCSIZE, wParam, lParam); + if (ret != 0) { + *result = ret; + return true; + } + // Re-apply the original top from before the size of the default frame was applied, + // and the whole top frame (the title bar and the top border) is gone now. + // For the top frame, we only has 2 choices: (1) remove the top frame entirely, or + // (2) don't touch it at all. We can't preserve the top border by adjusting the top + // margin here. If we try to modify the top margin, the original title bar will + // always be painted by DWM regardless what margin we set, so here we can only remove + // the top frame entirely and use some special technique to bring the top border back. + clientRect->top = originalTop; + } + const bool max = IsMaximized(hWnd); + const bool full = Utils::isFullScreen(windowId); + // We don't need this correction when we're fullscreen. We will + // have the WS_POPUP size, so we don't have to worry about + // borders, and the default frame will be fine. + if (max && !full) { + // When a window is maximized, its size is actually a little bit more + // than the monitor's work area. The window is positioned and sized in + // such a way that the resize handles are outside of the monitor and + // then the window is clipped to the monitor so that the resize handle + // do not appear because you don't need them (because you can't resize + // a window when it's maximized unless you restore it). + const int frameSizeY = Utils::getResizeBorderThickness(windowId, false, true); + clientRect->top += frameSizeY; + if (!frameBorderVisible) { + clientRect->bottom -= frameSizeY; + const int frameSizeX = Utils::getResizeBorderThickness(windowId, true, true); + clientRect->left += frameSizeX; + clientRect->right -= frameSizeX; + } + } + // Attempt to detect if there's an autohide taskbar, and if + // there is, reduce our size a bit on the side with the taskbar, + // so the user can still mouse-over the taskbar to reveal it. + // Make sure to use MONITOR_DEFAULTTONEAREST, so that this will + // still find the right monitor even when we're restoring from + // minimized. + if (max || full) { + APPBARDATA abd; + SecureZeroMemory(&abd, sizeof(abd)); + abd.cbSize = sizeof(abd); + const UINT taskbarState = SHAppBarMessage(ABM_GETSTATE, &abd); + // First, check if we have an auto-hide taskbar at all: + if (taskbarState & ABS_AUTOHIDE) { + bool top = false, bottom = false, left = false, right = false; + // Due to ABM_GETAUTOHIDEBAREX was introduced in Windows 8.1, + // we have to use another way to judge this if we are running + // on Windows 7 or Windows 8. + if (WindowsVersionHelper::isWin8Point1OrGreater()) { + MONITORINFOEXW monitorInfo; + SecureZeroMemory(&monitorInfo, sizeof(monitorInfo)); + monitorInfo.cbSize = sizeof(monitorInfo); + const HMONITOR monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + if (!monitor) { + WARNING << Utils::getSystemErrorMessage(kMonitorFromWindow); + break; + } + if (GetMonitorInfoW(monitor, &monitorInfo) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kGetMonitorInfoW); + break; + } + // This helper can be used to determine if there's a + // auto-hide taskbar on the given edge of the monitor + // we're currently on. + const auto hasAutohideTaskbar = [&monitorInfo](const UINT edge) -> bool { + APPBARDATA _abd; + SecureZeroMemory(&_abd, sizeof(_abd)); + _abd.cbSize = sizeof(_abd); + _abd.uEdge = edge; + _abd.rc = monitorInfo.rcMonitor; + const auto hTaskbar = reinterpret_cast(SHAppBarMessage(ABM_GETAUTOHIDEBAREX, &_abd)); + return (hTaskbar != nullptr); + }; + top = hasAutohideTaskbar(ABE_TOP); + bottom = hasAutohideTaskbar(ABE_BOTTOM); + left = hasAutohideTaskbar(ABE_LEFT); + right = hasAutohideTaskbar(ABE_RIGHT); + } else { + int edge = -1; + APPBARDATA _abd; + SecureZeroMemory(&_abd, sizeof(_abd)); + _abd.cbSize = sizeof(_abd); + _abd.hWnd = FindWindowW(L"Shell_TrayWnd", nullptr); + if (_abd.hWnd) { + const HMONITOR windowMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + if (!windowMonitor) { + WARNING << Utils::getSystemErrorMessage(kMonitorFromWindow); + break; + } + const HMONITOR taskbarMonitor = MonitorFromWindow(_abd.hWnd, MONITOR_DEFAULTTOPRIMARY); + if (!taskbarMonitor) { + WARNING << Utils::getSystemErrorMessage(kMonitorFromWindow); + break; + } + if (taskbarMonitor == windowMonitor) { + SHAppBarMessage(ABM_GETTASKBARPOS, &_abd); + edge = _abd.uEdge; + } + } else { + WARNING << Utils::getSystemErrorMessage(kFindWindowW); + break; + } + top = (edge == ABE_TOP); + bottom = (edge == ABE_BOTTOM); + left = (edge == ABE_LEFT); + right = (edge == ABE_RIGHT); + } + // If there's a taskbar on any side of the monitor, reduce + // our size a little bit on that edge. + // Note to future code archeologists: + // This doesn't seem to work for fullscreen on the primary + // display. However, testing a bunch of other apps with + // fullscreen modes and an auto-hiding taskbar has + // shown that _none_ of them reveal the taskbar from + // fullscreen mode. This includes Edge, Firefox, Chrome, + // Sublime Text, PowerPoint - none seemed to support this. + // This does however work fine for maximized. + if (top) { + // Peculiarly, when we're fullscreen, + clientRect->top += kAutoHideTaskBarThickness; + } else if (bottom) { + clientRect->bottom -= kAutoHideTaskBarThickness; + } else if (left) { + clientRect->left += kAutoHideTaskBarThickness; + } else if (right) { + clientRect->right -= kAutoHideTaskBarThickness; + } + } + } + Utils::syncWmPaintWithDwm(); // This should be executed at the very last. + // By returning WVR_REDRAW we can make the window resizing look less broken. + // But we must return 0 if wParam is FALSE, according to Microsoft Docs. + // **IMPORTANT NOTE**: + // If you are drawing something manually through D3D in your window, don't + // try to return WVR_REDRAW here, otherwise Windows exhibits bugs where + // client pixels and child windows are mispositioned by the width/height + // of the upper-left non-client area. It's confirmed that this issue exists + // from Windows 7 to Windows 10. Not tested on Windows 11 yet. Don't know + // whether it exists on Windows XP to Windows Vista or not. + const bool needD3DWorkaround = (qEnvironmentVariableIntValue(kD3DWorkaroundEnvVar) != 0); + *result = (((static_cast(wParam) == FALSE) || needD3DWorkaround) ? 0 : WVR_REDRAW); + return true; + } + case WM_NCHITTEST: { + // 鍘熺敓Win32绐楀彛鍙湁椤惰竟鏄湪绐楀彛鍐呴儴resize鐨勶紝鍏朵綑涓夎竟閮芥槸鍦ㄧ獥鍙 + // 澶栭儴杩涜resize鐨勶紝鍏跺師鐞嗘槸锛學S_THICKFRAME杩欎釜绐楀彛鏍峰紡浼氬湪绐 + // 鍙g殑宸︺佸彸鍜屽簳杈规坊鍔犱笁涓忔槑鐨剅esize鍖哄煙锛岃繖涓変釜鍖哄煙鍦ㄦ甯哥姸鎬 + // 涓嬫槸瀹屽叏涓嶅彲瑙佺殑锛屽畠浠敱DWM璐熻矗缁樺埗鍜屾帶鍒躲傝繖浜涘尯鍩熺殑瀹藉害绛変簬 + // (SM_CXSIZEFRAME + SM_CXPADDEDBORDER)锛岄珮搴︾瓑浜 + // (SM_CYSIZEFRAME + SM_CXPADDEDBORDER)锛屽湪100%缂╂斁鏃讹紝鍧囩瓑 + // 浜8鍍忕礌銆傚畠浠睘浜庣獥鍙e尯鍩熺殑涓閮ㄥ垎锛屼絾涓嶅睘浜庡鎴峰尯锛岃屾槸灞炰簬闈炲 + // 鎴峰尯锛屽洜姝etWindowRect鑾峰彇鐨勫尯鍩熶腑鏄寘鍚繖涓変釜resize鍖哄煙鐨勶紝 + // 鑰孏etClientRect鑾峰彇鐨勫尯鍩熸槸涓嶅寘鍚畠浠殑銆傚綋鎶 + // DWMWA_EXTENDED_FRAME_BOUNDS浣滀负鍙傛暟璋冪敤 + // DwmGetWindowAttribute鏃讹紝涔熻兘鑾峰彇鍒颁竴涓獥鍙eぇ灏忥紝杩欎釜澶у皬浠 + // 浜庡墠闈袱鑰呬箣闂达紝鏆傛椂涓嶇煡閬撹繖涓暟鎹殑鎰忎箟鍙婂叾浣滅敤銆傛垜浠湪 + // WM_NCCALCSIZE娑堟伅鐨勫鐞嗕腑锛屽凡缁忔妸鏁翠釜绐楀彛閮借缃负瀹㈡埛鍖轰簡锛屼篃 + // 灏辨槸璇达紝鎴戜滑鐨勭獥鍙e凡缁忔病鏈夐潪瀹㈡埛鍖轰簡锛屽洜姝ら偅涓変釜閫忔槑鐨剅esize鍖 + // 鍩燂紝姝ゅ埢涔熷凡缁忔垚涓虹獥鍙e鎴峰尯鐨勪竴閮ㄥ垎浜嗭紝浠庤屽彉寰椾笉閫忔槑浜嗐傛墍浠 + // 鐜板湪鐨剅esize锛岀湅璧锋潵鍍忔槸鍦ㄧ獥鍙e唴閮╮esize锛屾槸鍥犱负鍘熸湰閫忔槑鐨勫湴鏂 + // 鐜板湪鍙樺緱涓嶉忔槑浜嗭紝瀹為檯涓婏紝鍗曠函浠庤寖鍥翠笂鏉ョ湅锛岀幇鍦ㄦ垜浠瑀esize鐨勫湴鏂癸紝 + // 灏辨槸鏅氱獥鍙g殑杈规澶栭儴锛岄偅涓変釜閫忔槑鍖哄煙鐨勮寖鍥淬 + // 鍥犳锛屽鏋滄垜浠妸杈规瀹屽叏鍘绘帀锛堝氨鏄垜浠鍦ㄥ仛鐨勪簨鎯咃級锛宺esize灏 + // 浼氱湅璧锋潵鏄湪鍐呴儴杩涜锛岃繖涓棶棰橀氳繃甯歌鏂规硶闈炲父闅句互瑙e喅銆傛垜娴嬭瘯杩 + // QQ鍜岄拤閽夌殑绐楀彛锛屽畠浠殑绐楀彛灏辨槸鍦ㄥ閮╮esize锛屼絾瀹為檯涓婂畠浠槸閫氳繃 + // 鎶婄獥鍙e疄闄呯殑鍐呭锛屽祵鍏ュ埌涓涓畬鍏ㄩ忔槑鐨勪絾灏哄瑕佸ぇ涓鍦堢殑绐楀彛涓疄鐜 + // 鐨勶紝铏界劧鐪嬭捣鏉ユ晥鏋滆繕涓嶉敊锛屼絾瀵逛簬姝ら」鐩岃█锛屼唬鐮佸拰绐楀彛缁撴瀯杩囦簬澶 + // 鏉傦紝鍥犳鎴戞病鏈夐噰鐢ㄦ鏂规銆傜劧鑰岋紝瀵逛簬鍏蜂綋鐨勮蒋浠堕」鐩岃█锛屽叾鍋氭硶涔 + // 涓嶅け涓轰竴涓紭绉鐨勮В鍐虫柟妗堬紝姣曠珶鍏跺湪澶у鏁版潯浠朵笅鐨勮〃鐜伴兘杩樺彲浠ャ + // + // 鍜1.x鐨勫仛娉曚笉鍚岋紝鐜板湪鐨2.x閫夋嫨浜嗕繚鐣欑獥鍙d笁杈癸紝鍘婚櫎鏁翠釜绐楀彛椤堕儴锛 + // 濂藉鏄繚鐣欎簡绯荤粺鐨勫師鐢熻竟妗嗭紝澶栬杈冨ソ锛屼笖涓庣郴缁熺粨鍚堢揣瀵嗭紝鑰屼笖resize + // 鐨勮〃鐜颁篃鏈夊緢澶ф敼鍠勶紝缂虹偣鏄渶瑕佽嚜琛岀粯鍒堕《閮ㄨ竟妗嗙嚎銆傚師鏈互涓哄彧鑳藉儚 + // Windows Terminal閭f牱鍦╓M_PAINT閲屾悶榛戦瓟娉曪紝浣嗗悗鏉ュ彂鐜帮紝鍏跺疄鍙 + // 瑕侀鑹茬浉杩戯紝鎴戜滑鑷缁樺埗涓鏍瑰疄绾夸篃鍑犱箮鑳戒互鍋囦贡鐪燂紝鑰屼笖杩欐牱涔熶笉浼 + // 鐮村潖Qt鑷繁鐨勭粯鍒剁郴缁燂紝鑳藉仛鍒颁笉渚濊禆榛戦瓟娉曞氨鑳藉疄鐜板儚Windows Terminal + // 閭f牱澶栬鍜屽姛鑳介兘姣旇緝瀹岀編鐨勮嚜瀹氫箟杈规銆 + + // A normal Win32 window can be resized outside of it. Here is the + // reason: the WS_THICKFRAME window style will cause a window has three + // transparent areas beside the window's left, right and bottom + // edge. Their width or height is eight pixels if the window is not + // scaled. In most cases, they are totally invisible. It's DWM's + // responsibility to draw and control them. They exist to let the + // user resize the window, visually outside of it. They are in the + // window area, but not the client area, so they are in the + // non-client area actually. But we have turned the whole window + // area into client area in WM_NCCALCSIZE, so the three transparent + // resize areas also become a part of the client area and thus they + // become visible. When we resize the window, it looks like we are + // resizing inside of it, however, that's because the transparent + // resize areas are visible now, we ARE resizing outside of the + // window actually. But I don't know how to make them become + // transparent again without breaking the frame shadow drawn by DWM. + // If you really want to solve it, you can try to embed your window + // into a larger transparent window and draw the frame shadow + // yourself. As what we have said in WM_NCCALCSIZE, you can only + // remove the top area of the window, this will let us be able to + // resize outside of the window and don't need much process in this + // message, it looks like a perfect plan, however, the top border is + // missing due to the whole top area is removed, and it's very hard + // to bring it back because we have to use a trick in WM_PAINT + // (learned from Windows Terminal), but no matter what we do in + // WM_PAINT, it will always break the backing store mechanism of Qt, + // so actually we can't do it. And it's very difficult to do such + // things in NativeEventFilters as well. What's worse, if we really + // do this, the four window borders will become white and they look + // horrible in dark mode. This solution only supports Windows 10 + // because the border width on Win10 is only one pixel, however it's + // eight pixels on Windows 7 so preserving the three window borders + // looks terrible on old systems. + // + // Unlike the 1.x code, we choose to preserve the three edges of the + // window in 2.x, and get rid of the whole top part of the window. + // There are quite some advantages such as the appearance looks much + // better and due to we have the original system window frame, our + // window can behave just like a normal Win32 window even if we now + // doesn't have a title bar at all. Most importantly, the flicker and + // jitter during window resizing is totally gone now. The disadvantage + // is we have to draw a top frame border ourself. Previously I thought + // we have to do the black magic in WM_PAINT just like what Windows + // Terminal does, however, later I found that if we choose a proper + // color, our homemade top border can almost have exactly the same + // appearance with the system's one. + + const POINT nativeGlobalPos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + POINT nativeLocalPos = nativeGlobalPos; + if (ScreenToClient(hWnd, &nativeLocalPos) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kScreenToClient); + break; + } + const QPoint qtScenePos = Utils::fromNativePixels(data.params.getWindowHandle(), + QPoint(nativeLocalPos.x, nativeLocalPos.y)); + const bool max = IsMaximized(hWnd); + const bool full = Utils::isFullScreen(windowId); + const int frameSizeY = Utils::getResizeBorderThickness(windowId, false, true); + const bool isTop = (nativeLocalPos.y < frameSizeY); + const bool buttonSwapped = (GetSystemMetrics(SM_SWAPBUTTON) != FALSE); + const bool leftButtonPressed = (buttonSwapped ? + (GetAsyncKeyState(VK_RBUTTON) < 0) : (GetAsyncKeyState(VK_LBUTTON) < 0)); + const bool isTitleBar = (data.params.isInsideTitleBarDraggableArea(qtScenePos) && leftButtonPressed); + const bool isFixedSize = data.params.isWindowFixedSize(); + const bool dontOverrideCursor = data.params.getProperty(kDontOverrideCursorVar, false).toBool(); + const bool dontToggleMaximize = data.params.getProperty(kDontToggleMaximizeVar, false).toBool(); + if (dontToggleMaximize) { + static bool once = false; + if (!once) { + once = true; + DEBUG << "To disable window maximization, you should remove the " + "WS_MAXIMIZEBOX style from the window instead. FramelessHelper " + "won't do that for you, so you'll have to do it manually yourself."; + } + } + if (frameBorderVisible) { + // This will handle the left, right and bottom parts of the frame + // because we didn't change them. + const LRESULT originalRet = DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam); + if (originalRet != HTCLIENT) { + *result = (dontOverrideCursor ? HTBORDER : originalRet); + return true; + } + if (full) { + *result = HTCLIENT; + return true; + } + if (max) { + *result = (isTitleBar ? HTCAPTION : HTCLIENT); + return true; + } + // At this point, we know that the cursor is inside the client area + // so it has to be either the little border at the top of our custom + // title bar or the drag bar. Apparently, it must be the drag bar or + // the little border at the top which the user can use to move or + // resize the window. + if (isTop && !isFixedSize) { + // Return HTCLIENT instead of HTBORDER here, because the mouse is + // inside our homemade title bar now, return HTCLIENT to let our + // title bar can still capture mouse events. + *result = (dontOverrideCursor ? HTCLIENT : HTTOP); + return true; + } + if (isTitleBar) { + *result = HTCAPTION; + return true; + } + *result = HTCLIENT; + return true; + } else { + if (full) { + *result = HTCLIENT; + return true; + } + if (max) { + *result = (isTitleBar ? HTCAPTION : HTCLIENT); + return true; + } + if (!isFixedSize) { + RECT clientRect = {0, 0, 0, 0}; + if (GetClientRect(hWnd, &clientRect) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kGetClientRect); + break; + } + const LONG width = clientRect.right; + const LONG height = clientRect.bottom; + const bool isBottom = (nativeLocalPos.y >= (height - frameSizeY)); + // Make the border a little wider to let the user easy to resize on corners. + const qreal scaleFactor = ((isTop || isBottom) ? 2.0 : 1.0); + const int frameSizeX = Utils::getResizeBorderThickness(windowId, true, true); + const int scaledFrameSizeX = std::round(qreal(frameSizeX) * scaleFactor); + const bool isLeft = (nativeLocalPos.x < scaledFrameSizeX); + const bool isRight = (nativeLocalPos.x >= (width - scaledFrameSizeX)); + if (dontOverrideCursor && (isTop || isBottom || isLeft || isRight)) { + // Return HTCLIENT instead of HTBORDER here, because the mouse is + // inside the window now, return HTCLIENT to let the controls + // inside our window can still capture mouse events. + *result = HTCLIENT; + return true; + } + if (isTop) { + if (isLeft) { + *result = HTTOPLEFT; + return true; + } + if (isRight) { + *result = HTTOPRIGHT; + return true; + } + *result = HTTOP; + return true; + } + if (isBottom) { + if (isLeft) { + *result = HTBOTTOMLEFT; + return true; + } + if (isRight) { + *result = HTBOTTOMRIGHT; + return true; + } + *result = HTBOTTOM; + return true; + } + if (isLeft) { + *result = HTLEFT; + return true; + } + if (isRight) { + *result = HTRIGHT; + return true; + } + } + if (isTitleBar) { + *result = HTCAPTION; + return true; + } + *result = HTCLIENT; + return true; + } + } +#if (QT_VERSION < QT_VERSION_CHECK(6, 2, 2)) // I contributed this to Qt since 6.2.2 + case WM_WINDOWPOSCHANGING: { + // Tell Windows to discard the entire contents of the client area, as re-using + // parts of the client area would lead to jitter during resize. + const auto windowPos = reinterpret_cast(lParam); + windowPos->flags |= SWP_NOCOPYBITS; + } break; +#endif +#if (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2)) + case WM_GETDPISCALEDSIZE: { + // QtBase commit 2cfca7fd1911cc82a22763152c04c65bc05bc19a introduced a bug + // which caused the custom margins is ignored during the handling of the + // WM_GETDPISCALEDSIZE message, it was shipped with Qt 6.2.1 ~ 6.4.2. + // We workaround it by overriding the wrong handling directly. + RECT clientRect = {}; + if (GetClientRect(hWnd, &clientRect) == FALSE) { + WARNING << Utils::getSystemErrorMessage(kGetClientRect); + *result = FALSE; // Use the default linear DPI scaling provided by Windows. + return true; // Jump over Qt's wrong handling logic. + } + const QSizeF oldSize = {qreal(clientRect.right - clientRect.left), qreal(clientRect.bottom - clientRect.top)}; + static constexpr const auto defaultDpi = qreal(USER_DEFAULT_SCREEN_DPI); + // We need to round the scale factor according to Qt's rounding policy. + const qreal oldDpr = Utils::roundScaleFactor(qreal(data.dpi.x) / defaultDpi); + const auto newDpi = UINT(wParam); + const qreal newDpr = Utils::roundScaleFactor(qreal(newDpi) / defaultDpi); + const QSizeF newSize = (oldSize / oldDpr * newDpr); + const auto suggestedSize = reinterpret_cast(lParam); + suggestedSize->cx = std::round(newSize.width()); + suggestedSize->cy = std::round(newSize.height()); + // If the window frame is visible, we need to expand the suggested size, currently + // it's pure client size, we need to add the frame size to it. Windows expects a + // full window size, including the window frame. + // If the window frame is not visible, the window size equals to the client size, + // the suggested size doesn't need further adjustments. + if (frameBorderVisible) { + const int frameSizeX = Utils::getResizeBorderThicknessForDpi(true, newDpi); + const int frameSizeY = Utils::getResizeBorderThicknessForDpi(false, newDpi); + suggestedSize->cx += (frameSizeX * 2); // The size of the two resize borders on the left and right edge. + suggestedSize->cy += frameSizeY; // Only add the bottom resize border. We don't have anything on the top edge. + // Both the top resize border and the title bar are in the client area. + } + *result = TRUE; // We have set our preferred window size, don't use the default linear DPI scaling. + return true; // Jump over Qt's wrong handling logic. + } +#endif // (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2)) + case WM_DPICHANGED: { + const Dpi dpi = {UINT(LOWORD(wParam)), UINT(HIWORD(wParam))}; + DEBUG.noquote() << "New DPI for window" << hwnd2str(hWnd) << "is" << dpi; + g_win32Helper()->mutex.lock(); + g_win32Helper()->data[windowId].dpi = dpi; + g_win32Helper()->mutex.unlock(); +#if (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2)) + // We need to wait until Qt has handled this message, otherwise everything + // we have done here will always be overwritten. + QTimer::singleShot(0, qApp, [data](){ // Copy the variables intentionally, otherwise they'll go out of scope when Qt finally use them. + // Sync the internal window frame margins with the latest DPI, otherwise + // we will get wrong window sizes after the DPI change. + Utils::updateInternalWindowFrameMargins(data.params.getWindowHandle(), true); + }); +#endif // (QT_VERSION <= QT_VERSION_CHECK(6, 4, 2)) + } break; + case WM_DWMCOMPOSITIONCHANGED: { + // Re-apply the custom window frame if recovered from the basic theme. + Utils::updateWindowFrameMargins(windowId, false); + } break; + default: + break; + } + if (!frameBorderVisible) { + switch (uMsg) { + case WM_NCUAHDRAWCAPTION: + case WM_NCUAHDRAWFRAME: { + // These undocumented messages are sent to draw themed window + // borders. Block them to prevent drawing borders over the client + // area. + *result = 0; + return true; + } + case WM_NCPAINT: { + // 杈规闃村奖澶勪簬闈炲鎴峰尯鐨勮寖鍥达紝鍥犳濡傛灉鐩存帴闃绘闈炲鎴峰尯鐨勭粯鍒讹紝浼氬鑷磋竟妗嗛槾褰变涪澶 + + if (!Utils::isDwmCompositionEnabled()) { + // Only block WM_NCPAINT when DWM composition is disabled. If + // it's blocked when DWM composition is enabled, the frame + // shadow won't be drawn. + *result = 0; + return true; + } else { + break; + } + } + case WM_NCACTIVATE: { + if (Utils::isDwmCompositionEnabled()) { + // DefWindowProc won't repaint the window border if lParam (normally a HRGN) + // is -1. See the following link's "lParam" section: + // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate + // Don't use "*result = 0" here, otherwise the window won't respond to the + // window activation state change. + *result = DefWindowProcW(hWnd, WM_NCACTIVATE, wParam, -1); + } else { + if (static_cast(wParam) == FALSE) { + *result = TRUE; + } else { + *result = FALSE; + } + } + return true; + } + case WM_SETICON: + case WM_SETTEXT: { + // Disable painting while these messages are handled to prevent them + // from drawing a window caption over the client area. + SetLastError(ERROR_SUCCESS); + const auto oldStyle = static_cast(GetWindowLongPtrW(hWnd, GWL_STYLE)); + if (oldStyle == 0) { + WARNING << Utils::getSystemErrorMessage(kGetWindowLongPtrW); + break; + } + // Prevent Windows from drawing the default title bar by temporarily + // toggling the WS_VISIBLE style. + const DWORD newStyle = (oldStyle & ~WS_VISIBLE); + SetLastError(ERROR_SUCCESS); + if (SetWindowLongPtrW(hWnd, GWL_STYLE, static_cast(newStyle)) == 0) { + WARNING << Utils::getSystemErrorMessage(kSetWindowLongPtrW); + break; + } + Utils::triggerFrameChange(windowId); + const LRESULT ret = DefWindowProcW(hWnd, uMsg, wParam, lParam); + SetLastError(ERROR_SUCCESS); + if (SetWindowLongPtrW(hWnd, GWL_STYLE, static_cast(oldStyle)) == 0) { + WARNING << Utils::getSystemErrorMessage(kSetWindowLongPtrW); + break; + } + Utils::triggerFrameChange(windowId); + *result = ret; + return true; + } + default: + break; + } + } + if (WindowsVersionHelper::isWin11OrGreater() && data.fallbackTitleBarWindowId) { + switch (uMsg) { + case WM_SIZE: // Sent to a window after its size has changed. + case WM_DISPLAYCHANGE: // Sent to a window when the display resolution has changed. + { + const bool isFixedSize = data.params.isWindowFixedSize(); + if (!resizeFallbackTitleBarWindow(windowId, data.fallbackTitleBarWindowId, isFixedSize)) { + WARNING << "Failed to re-position the fallback title bar window."; + } + } break; + default: + break; + } + } + const bool wallpaperChanged = ((uMsg == WM_SETTINGCHANGE) && (wParam == SPI_SETDESKWALLPAPER)); + bool systemThemeChanged = ((uMsg == WM_THEMECHANGED) || (uMsg == WM_SYSCOLORCHANGE) + || (uMsg == WM_DWMCOLORIZATIONCOLORCHANGED)); + if (WindowsVersionHelper::isWin10RS1OrGreater()) { + if (uMsg == WM_SETTINGCHANGE) { + if ((wParam == 0) && (lParam != 0) // lParam sometimes may be NULL. + && (std::wcscmp(reinterpret_cast(lParam), kThemeSettingChangeEventName) == 0)) { + systemThemeChanged = true; + if (WindowsVersionHelper::isWin10RS5OrGreater()) { + const bool dark = Utils::shouldAppsUseDarkMode(); + const auto isWidget = [&data]() -> bool { + const auto widget = data.params.getWidgetHandle(); + return (widget && widget->isWidgetType()); + }(); + if (!isWidget) { + // Causes some QtWidgets paint incorrectly, so only apply to Qt Quick applications. + Utils::updateGlobalWin32ControlsTheme(windowId, dark); + } + Utils::refreshWin32ThemeResources(windowId, dark); + } + } + } + } + if (systemThemeChanged || wallpaperChanged) { + // Sometimes the FramelessManager instance may be destroyed already. + if (FramelessManager * const manager = FramelessManager::instance()) { + if (FramelessManagerPrivate * const managerPriv = FramelessManagerPrivate::get(manager)) { +#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 0)) + if (systemThemeChanged) { + managerPriv->notifySystemThemeHasChangedOrNot(); + } +#endif // (QT_VERSION < QT_VERSION_CHECK(6, 5, 0)) + if (wallpaperChanged) { + managerPriv->notifyWallpaperHasChangedOrNot(); + } + } + } + } + return false; +} + +FRAMELESSHELPER_END_NAMESPACE diff --git a/FramelessHelper/src/core/framelesshelper_win.h b/FramelessHelper/src/core/framelesshelper_win.h new file mode 100644 index 0000000..419c1c4 --- /dev/null +++ b/FramelessHelper/src/core/framelesshelper_win.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Core/framelesshelper_win.h" diff --git a/FramelessHelper/src/core/framelesshelper_windows.h b/FramelessHelper/src/core/framelesshelper_windows.h new file mode 100644 index 0000000..6825491 --- /dev/null +++ b/FramelessHelper/src/core/framelesshelper_windows.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Core/framelesshelper_windows.h" diff --git a/FramelessHelper/src/core/framelesshelpercore.qrc b/FramelessHelper/src/core/framelesshelpercore.qrc new file mode 100644 index 0000000..0f0fa48 --- /dev/null +++ b/FramelessHelper/src/core/framelesshelpercore.qrc @@ -0,0 +1,6 @@ + + + resources/fonts/iconfont.ttf + resources/images/noise.png + + diff --git a/FramelessHelper/src/core/framelesshelpercore_global.cpp b/FramelessHelper/src/core/framelesshelpercore_global.cpp new file mode 100644 index 0000000..d52a6d7 --- /dev/null +++ b/FramelessHelper/src/core/framelesshelpercore_global.cpp @@ -0,0 +1,385 @@ +/* + * MIT License + * + * Copyright (C) 2021-2023 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 "framelesshelpercore_global.h" +#include "utils.h" +#include "framelessmanager.h" +#include "framelesshelper_qt.h" +#include "chromepalette.h" +#include "micamaterial.h" +#include "windowborderpainter.h" +#include "sysapiloader_p.h" +#include "framelessmanager_p.h" +#include "framelessconfig_p.h" +#include "chromepalette_p.h" +#include "micamaterial_p.h" +#include "windowborderpainter_p.h" +#ifdef Q_OS_WINDOWS +# include "framelesshelper_win.h" +# include "registrykey_p.h" +#endif +#ifdef Q_OS_LINUX +# include "framelesshelper_linux.h" +#endif +#include +#include +#include + +#ifndef COMPILER_STRING +# ifdef Q_CC_CLANG // Must be before GNU, because Clang claims to be GNU too. +# define COMPILER_STRING __VERSION__ // Already includes the compiler's name. +# elif defined(Q_CC_GHS) +# define COMPILER_STRING "GHS " QT_STRINGIFY(__GHS_VERSION_NUMBER) +# elif defined(Q_CC_GNU) +# define COMPILER_STRING "GCC " __VERSION__ +# elif defined(Q_CC_MSVC) +# if (_MSC_VER < 1910) +# define COMPILER_STRING "MSVC 2015" +# elif (_MSC_VER < 1917) +# define COMPILER_STRING "MSVC 2017" +# elif (_MSC_VER < 1930) +# define COMPILER_STRING "MSVC 2019" +# elif (_MSC_VER < 2000) +# define COMPILER_STRING "MSVC 2022" +# else +# define COMPILER_STRING "MSVC version " QT_STRINGIFY(_MSC_VER) +# endif +# else +# define COMPILER_STRING "UNKNOWN" +# endif +#endif + +#ifndef QT_NO_DEBUG_STREAM +QT_BEGIN_NAMESPACE +QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber &ver) +{ + const QDebugStateSaver saver(d); + d.nospace().noquote() << "VersionNumber(" + << ver.major << ", " + << ver.minor << ", " + << ver.patch << ", " + << ver.tweak << ')'; + return d; +} + +QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionInfo &ver) +{ + const QDebugStateSaver saver(d); + int major = 0, minor = 0, patch = 0, tweak = 0; + FRAMELESSHELPER_EXTRACT_VERSION(ver.version, major, minor, patch, tweak) + const auto ver_num = FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber{major, minor, patch, tweak}; + d.nospace().noquote() << "VersionInfo(" + << "version number: " << ver_num << ", " + << "version string: " << ver.version_str << ", " + << "commit hash: " << ver.commit << ", " + << "compiler: " << ver.compiler << ", " + << "debug build: " << ver.isDebug << ", " + << "static build: " << ver.isStatic << ')'; + return d; +} + +QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Dpi &dpi) +{ + const QDebugStateSaver saver(d); + const qreal scaleFactor = (qreal(dpi.x) / qreal(96)); + d.nospace().noquote() << "Dpi(" + << "x: " << dpi.x << ", " + << "y: " << dpi.y << ", " + << "scale factor: " << scaleFactor << ')'; + return d; +} +QT_END_NAMESPACE +#endif // QT_NO_DEBUG_STREAM + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcCoreGlobal, "wangwenx190.framelesshelper.core.global") + +#ifdef FRAMELESSHELPER_CORE_NO_DEBUG_OUTPUT +# define INFO QT_NO_QDEBUG_MACRO() +# define DEBUG QT_NO_QDEBUG_MACRO() +# define WARNING QT_NO_QDEBUG_MACRO() +# define CRITICAL QT_NO_QDEBUG_MACRO() +#else +# define INFO qCInfo(lcCoreGlobal) +# define DEBUG qCDebug(lcCoreGlobal) +# define WARNING qCWarning(lcCoreGlobal) +# define CRITICAL qCCritical(lcCoreGlobal) +#endif + +using namespace Global; + +#ifdef Q_OS_WINDOWS +static_assert(std::size(WindowsVersions) == (static_cast(WindowsVersion::Latest) + 1)); +#endif + +#ifdef Q_OS_LINUX +[[maybe_unused]] static constexpr const char QT_QPA_ENV_VAR[] = "QT_QPA_PLATFORM"; +FRAMELESSHELPER_BYTEARRAY_CONSTANT(xcb) +#endif + +#ifdef Q_OS_MACOS +[[maybe_unused]] static constexpr const char MAC_LAYER_ENV_VAR[] = "QT_MAC_WANTS_LAYER"; +#endif + +[[maybe_unused]] static constexpr const char kNoLogoEnvVar[] = "FRAMELESSHELPER_NO_LOGO"; + +struct CoreData +{ + QMutex mutex; + QList initHooks = {}; + QList uninitHooks = {}; +}; + +Q_GLOBAL_STATIC(CoreData, coreData) + +namespace FramelessHelper::Core +{ + +void initialize() +{ + static bool inited = false; + if (inited) { + return; + } + inited = true; + + outputLogo(); + +#ifdef Q_OS_LINUX + // Qt's Wayland experience is not good, so we force the XCB backend here. + // TODO: Remove this hack once Qt's Wayland implementation is good enough. + // We are setting the preferred QPA backend, so we have to set it early + // enough, that is, before the construction of any Q(Gui)Application + // instances. QCoreApplication won't instantiate the platform plugin. + qputenv(QT_QPA_ENV_VAR, kxcb); + gtk_init(nullptr, nullptr); +#endif + +#if (defined(Q_OS_MACOS) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))) + qputenv(MAC_LAYER_ENV_VAR, FRAMELESSHELPER_BYTEARRAY_LITERAL("1")); +#endif + +#ifdef Q_OS_WINDOWS + // This is equivalent to set the "dpiAware" and "dpiAwareness" field in + // your manifest file. It works through out Windows Vista to Windows 11. + // It's highly recommended to enable the highest DPI awareness mode + // (currently it's PerMonitor Version 2, or PMv2 for short) for any GUI + // applications, to allow your user interface scale to an appropriate + // size and still stay sharp, though you will have to do the calculation + // and resize by yourself. + Utils::tryToEnableHighestDpiAwarenessLevel(); + // This function need to be called before any dialogs are created, so + // to be safe we call it here. + // Without this hack, our native dialogs won't be able to respond to + // DPI change messages correctly, especially the non-client area. + Utils::fixupDialogsDpiScaling(); +#endif + + // We need this flag to enable nested frameless windows, however, + // this flag is known to be __NOT__ compatible with QGLWidget. + QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + // Enable high DPI scaling by default, but only for Qt5 applications, + // because this has become the default setting since Qt6 and it can't + // be changed from outside anymore (except for internal testing purposes). + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif + + qRegisterMetaType