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