diff --git a/ArchitectureColoredPainting.sln b/ArchitectureColoredPainting.sln
index 1f4fb23..c034bd5 100644
--- a/ArchitectureColoredPainting.sln
+++ b/ArchitectureColoredPainting.sln
@@ -4,6 +4,11 @@ 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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +20,10 @@ 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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj
index 0c3888c..0799117 100644
--- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj
+++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj
@@ -32,7 +32,7 @@
5.15.2_msvc2019_64
- core;gui;widgets
+ core;gui;widgets;winextras
debug
@@ -61,11 +61,13 @@
stdcpp17
+ $(SolutionDir)QGoodWindow;%(AdditionalIncludeDirectories);$(Qt_INCLUDEPATH_)
stdcpp17
+ $(SolutionDir)QGoodWindow;%(AdditionalIncludeDirectories);
@@ -95,9 +97,12 @@
+
+
+
@@ -113,12 +118,17 @@
+
+
+
+
+
@@ -137,6 +147,10 @@
+
+
+
+
@@ -156,6 +170,11 @@
+
+
+ {b982e745-c0b1-46b3-a27b-743af105f2d0}
+
+
diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters
index b013fdb..a03f502 100644
--- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters
+++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters
@@ -9,10 +9,6 @@
{93995380-89BD-4b04-88EB-625FBE52EBFB}
h;hh;hpp;hxx;hm;inl;inc;xsd
-
- {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
- qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
-
{99349809-55BA-4b9d-BF79-8FDBB0286EB3}
ui
@@ -21,9 +17,6 @@
{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}
ts
-
- {60515177-3da7-420f-8f5b-27c16bb2b77b}
-
{bd91d673-6674-478d-a43d-54ac6c09d77f}
@@ -36,14 +29,23 @@
{f52671e3-3263-45e6-8d6c-976d19dc7e24}
+
+ {7a710fdd-46c9-44ff-aae7-7809e9c34d23}
+
+
+ {60515177-3da7-420f-8f5b-27c16bb2b77b}
+
-
- Resource Files
-
Form Files
+
+ Form Files
+
+
+ Form Files
+
@@ -100,6 +102,18 @@
Source Files\Renderer
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
@@ -114,6 +128,18 @@
Header Files\Renderer
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
@@ -164,6 +190,12 @@
Resource Files\Shaders
+
+ Resource Files
+
+
+ Resource Files
+
@@ -217,4 +249,9 @@
Source Files
+
+
+ Resource Files
+
+
\ No newline at end of file
diff --git a/ArchitectureColoredPainting/EditorWidget.ui b/ArchitectureColoredPainting/EditorWidget.ui
index 4622380..68e9a6c 100644
--- a/ArchitectureColoredPainting/EditorWidget.ui
+++ b/ArchitectureColoredPainting/EditorWidget.ui
@@ -31,6 +31,9 @@
纹理编辑
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
diff --git a/ArchitectureColoredPainting/FramelessWindow.ui b/ArchitectureColoredPainting/FramelessWindow.ui
new file mode 100644
index 0000000..9660810
--- /dev/null
+++ b/ArchitectureColoredPainting/FramelessWindow.ui
@@ -0,0 +1,208 @@
+
+
+ FramelessWindow
+
+
+
+ 0
+ 0
+ 560
+ 544
+
+
+
+
+
+
+ false
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ false
+
+
+ #windowFrame{ background-color:palette(Window);}
+
+
+
+ 0
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 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;
+}
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+ CaptionButton
+ QWidget
+
+ 1
+
+
+ IconWidget
+ QWidget
+
+
+
+ TitleWidget
+ QWidget
+
+ 1
+
+
+
+
+
diff --git a/ArchitectureColoredPainting/MainWindow.qrc b/ArchitectureColoredPainting/MainWindow.qrc
index 150f170..8a9a1ea 100644
--- a/ArchitectureColoredPainting/MainWindow.qrc
+++ b/ArchitectureColoredPainting/MainWindow.qrc
@@ -16,5 +16,15 @@
Shaders/model_shadow.geom
Shaders/shadow_mapping.comp
Shaders/ssgi.comp
+ images/icon.png
+ images/icon_window_close.png
+ images/icon_window_maximize.png
+ images/icon_window_minimize.png
+ images/icon_window_restore.png
+ darkstyle.qss
+ lightstyle.qss
+
+
+ qt.conf
diff --git a/ArchitectureColoredPainting/MainWindow.ui b/ArchitectureColoredPainting/MainWindow.ui
index 89d242e..2922f49 100644
--- a/ArchitectureColoredPainting/MainWindow.ui
+++ b/ArchitectureColoredPainting/MainWindow.ui
@@ -13,6 +13,9 @@
MainWindow
+
+ font: 10pt "Segoe UI, Microsoft YaHei UI";
+
@@ -21,6 +24,9 @@
+
+ 0
+
0
@@ -34,7 +40,10 @@
0
-
-
+
+
+ 0
+
QLayout::SetDefaultConstraint
@@ -42,10 +51,29 @@
- Microsoft YaHei UI
- 13
+ Segoe UI, Microsoft YaHei UI
+ 10
+ 50
+ false
+ false
+
+ QTabBar::tab {
+height: 0px;
+margin-top:0px;
+}
+QTabWidget::tab-bar
+{
+ height: 0px;
+ top:0px;
+}
+QTabWidget::pane {
+ border: 0px;
+ background-color: rgba(0, 0, 0, 0);
+}
+
+
QTabWidget::North
@@ -53,7 +81,13 @@
QTabWidget::Rounded
- 1
+ 0
+
+
+ Qt::ElideNone
+
+
+ false
diff --git a/ArchitectureColoredPainting/NavigationBarWidget.ui b/ArchitectureColoredPainting/NavigationBarWidget.ui
new file mode 100644
index 0000000..32061cf
--- /dev/null
+++ b/ArchitectureColoredPainting/NavigationBarWidget.ui
@@ -0,0 +1,100 @@
+
+
+ NavigationBarWidgetClass
+
+
+
+ 0
+ 0
+ 612
+ 41
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 40
+
+
+
+
+ 0
+ 0
+
+
+
+ NavigationBarWidget
+
+
+ QRadioButton::indicator {
+ width: 0px;
+ border: 0px;
+ background-color: rgba(0, 0, 0, 0);
+}
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+ 纹理编辑
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 场景渲染
+
+
+ true
+
+
+
+
+
+
+
+
+
diff --git a/ArchitectureColoredPainting/RendererWidget.ui b/ArchitectureColoredPainting/RendererWidget.ui
index d6f9fbf..190a6ca 100644
--- a/ArchitectureColoredPainting/RendererWidget.ui
+++ b/ArchitectureColoredPainting/RendererWidget.ui
@@ -13,7 +13,7 @@
RendererWidget
-
+
0
@@ -26,6 +26,22 @@
0
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 40
+
+
+
+
-
diff --git a/ArchitectureColoredPainting/darkstyle.qss b/ArchitectureColoredPainting/darkstyle.qss
new file mode 100644
index 0000000..e4cbe88
--- /dev/null
+++ b/ArchitectureColoredPainting/darkstyle.qss
@@ -0,0 +1,297 @@
+QToolTip{
+ color:palette(text);
+ background-color:palette(base);
+ border:1px solid palette(highlight);
+ border-radius:0px;
+}
+QStatusBar{
+ background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+ color:palette(mid);
+}
+QMenuBar{
+ background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+ border-bottom:2px solid rgba(25,25,25,75);
+}
+QMenuBar::item{
+ spacing:2px;
+ padding:3px 4px;
+ background:transparent;
+}
+QMenuBar::item:selected{
+ background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(106,106,106,255),stop:1 rgba(106,106,106,75));
+ border-left:1px solid rgba(106,106,106,127);
+ border-right:1px solid rgba(106,106,106,127);
+}
+QMenuBar::item:pressed{
+ background-color:palette(highlight);
+ color:rgba(255,255,255,255);
+ border-left:1px solid rgba(25,25,25,127);
+ border-right:1px solid rgba(25,25,25,127);
+}
+QMenu{
+ background-color:palette(window);
+ border:1px solid palette(shadow);
+}
+QMenu::item{
+ padding:3px 15px 3px 15px;
+ border:1px solid transparent;
+}
+QMenu::item:disabled{
+ background-color:rgba(35,35,35,127);
+ color:palette(disabled);
+}
+QMenu::item:selected{
+ border-color:rgba(147,191,236,127);
+ background:palette(highlight);
+ color:rgba(255,255,255,255);
+}
+QMenu::icon:checked{
+ background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+ border:1px solid palette(highlight);
+ border-radius:2px;
+}
+QMenu::separator{
+ height:1px;
+ background:palette(alternate-base);
+ margin-left:5px;
+ margin-right:5px;
+}
+QMenu::indicator{
+ width:15px;
+ height:15px;
+}
+QMenu::indicator:non-exclusive:checked{
+ padding-left:2px;
+}
+QMenu::indicator:non-exclusive:unchecked{
+ padding-left:2px;
+}
+QMenu::indicator:exclusive:checked{
+ padding-left:2px;
+}
+QMenu::indicator:exclusive:unchecked{
+ padding-left:2px;
+}
+QToolBar::top{
+ background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+ border-bottom:3px solid qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+}
+QToolBar::bottom{
+ background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+ border-top:3px solid qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+}
+QToolBar::left{
+ background-color:qlineargradient(x1:0,y1:0,x2:1,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+ border-right:3px solid qlineargradient(x1:0,y1:0,x2:1,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+}
+QToolBar::right{
+ background-color:qlineargradient(x1:1,y1:0,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+ border-left:3px solid qlineargradient(x1:1,y1:0,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+}
+QMainWindow::separator{
+ width:6px;
+ height:5px;
+ padding:2px;
+}
+QSplitter::handle:horizontal{
+ width:10px;
+}
+QSplitter::handle:vertical{
+ height:10px;
+}
+QMainWindow::separator:hover,QSplitter::handle:hover{
+ background:palette(highlight);
+}
+QDockWidget::title{
+ padding:4px;
+ background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+ border:1px solid rgba(25,25,25,75);
+ border-bottom:2px solid rgba(25,25,25,75);
+}
+QDockWidget{
+ titlebar-close-icon:url(:/darkstyle/icon_close.png);
+ titlebar-normal-icon:url(:/darkstyle/icon_restore.png);
+}
+QDockWidget::close-button,QDockWidget::float-button{
+ subcontrol-position:top right;
+ subcontrol-origin:margin;
+ position:absolute;
+ top:3px;
+ bottom:0px;
+ width:20px;
+ height:20px;
+}
+QDockWidget::close-button{
+ right:3px;
+}
+QDockWidget::float-button{
+ right:25px;
+}
+QGroupBox{
+ background-color:rgba(66,66,66,50%);
+ margin-top:27px;
+ border:1px solid rgba(25,25,25,127);
+ border-radius:4px;
+}
+QGroupBox::title{
+ subcontrol-origin:margin;
+ subcontrol-position:left top;
+ padding:4px 6px;
+ margin-left:3px;
+ background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+ border:1px solid rgba(25,25,25,75);
+ border-bottom:2px solid rgb(127,127,127);
+ border-top-left-radius:4px;
+ border-top-right-radius:4px;
+}
+QTabWidget::pane{
+ background-color:rgba(66,66,66,50%);
+ border-top:1px solid rgba(25,25,25,50%);
+}
+QTabWidget::tab-bar{
+ left:3px;
+ top:1px;
+}
+QTabBar{
+ background-color:transparent;
+ qproperty-drawBase:0;
+ border-bottom:1px solid rgba(25,25,25,50%);
+}
+QTabBar::tab{
+ padding:4px 6px;
+ background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+ border:1px solid rgba(25,25,25,75);
+ border-top-left-radius:4px;
+ border-top-right-radius:4px;
+}
+QTabBar::tab:selected,QTabBar::tab:hover{
+ background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(53,53,53,127),stop:1 rgba(66,66,66,50%));
+ border-bottom-color:rgba(66,66,66,75%);
+}
+QTabBar::tab:selected{
+ border-bottom:2px solid palette(highlight);
+}
+QTabBar::tab::selected:disabled{
+ border-bottom:2px solid rgb(127,127,127);
+}
+QTabBar::tab:!selected{
+ margin-top:2px;
+}
+QTabBar::close-button {
+ border:1px solid transparent;
+ border-radius:2px;
+}
+QTabBar::close-button:hover {
+ background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(106,106,106,255),stop:1 rgba(106,106,106,75));
+ border:1px solid palette(base);
+}
+QTabBar::close-button:pressed {
+ background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+ border:1px solid palette(base);
+}
+QCheckBox::indicator{
+ width:18px;
+ height:18px;
+}
+QRadioButton::indicator{
+ width:18px;
+ height:18px;
+}
+QTreeView, QTableView{
+ alternate-background-color:palette(window);
+ background:palette(base);
+}
+QTreeView QHeaderView::section, QTableView QHeaderView::section{
+ background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
+ border-style:none;
+ border-bottom:1px solid palette(dark);
+ padding-left:5px;
+ padding-right:5px;
+}
+QTreeView::item:selected:disabled, QTableView::item:selected:disabled{
+ background:rgb(80,80,80);
+}
+QTreeView::branch{
+ background-color:palette(base);
+}
+QTreeView::branch:has-children:!has-siblings:closed,
+QTreeView::branch:closed:has-children:has-siblings{
+ border-image:none;
+}
+QTreeView::branch:open:has-children:!has-siblings,
+QTreeView::branch:open:has-children:has-siblings{
+ border-image:none;
+}
+QScrollBar:vertical{
+ background:palette(base);
+ border-top-right-radius:2px;
+ border-bottom-right-radius:2px;
+ width:16px;
+ margin:0px;
+}
+QScrollBar::handle:vertical{
+ background-color:palette(alternate-base);
+ border-radius:2px;
+ min-height:20px;
+ margin:2px 4px 2px 4px;
+}
+QScrollBar::handle:vertical:hover{
+ background-color:palette(highlight);
+}
+QScrollBar::add-line:vertical{
+ background:none;
+ height:0px;
+ subcontrol-position:right;
+ subcontrol-origin:margin;
+}
+QScrollBar::sub-line:vertical{
+ background:none;
+ height:0px;
+ subcontrol-position:left;
+ subcontrol-origin:margin;
+}
+QScrollBar:horizontal{
+ background:palette(base);
+ height:16px;
+ margin:0px;
+}
+QScrollBar::handle:horizontal{
+ background-color:palette(alternate-base);
+ border-radius:2px;
+ min-width:20px;
+ margin:4px 2px 4px 2px;
+}
+QScrollBar::handle:horizontal:hover{
+ background-color:palette(highlight);
+}
+QScrollBar::add-line:horizontal{
+ background:none;
+ width:0px;
+ subcontrol-position:bottom;
+ subcontrol-origin:margin;
+}
+QScrollBar::sub-line:horizontal{
+ background:none;
+ width:0px;
+ subcontrol-position:top;
+ subcontrol-origin:margin;
+}
+QSlider::handle:horizontal{
+ border-radius:4px;
+ border:1px solid rgba(25,25,25,255);
+ background-color:palette(alternate-base);
+ min-height:20px;
+ margin:0 -4px;
+}
+QSlider::handle:horizontal:hover{
+ background:palette(highlight);
+}
+QSlider::add-page:horizontal{
+ background:palette(base);
+}
+QSlider::sub-page:horizontal{
+ background:palette(highlight);
+}
+QSlider::sub-page:horizontal:disabled{
+ background:rgb(80,80,80);
+}
diff --git a/ArchitectureColoredPainting/images/icon.png b/ArchitectureColoredPainting/images/icon.png
new file mode 100644
index 0000000..4bac081
Binary files /dev/null and b/ArchitectureColoredPainting/images/icon.png differ
diff --git a/ArchitectureColoredPainting/images/icon_window_close.png b/ArchitectureColoredPainting/images/icon_window_close.png
new file mode 100644
index 0000000..ece7c28
Binary files /dev/null and b/ArchitectureColoredPainting/images/icon_window_close.png differ
diff --git a/ArchitectureColoredPainting/images/icon_window_maximize.png b/ArchitectureColoredPainting/images/icon_window_maximize.png
new file mode 100644
index 0000000..53ae289
Binary files /dev/null and b/ArchitectureColoredPainting/images/icon_window_maximize.png differ
diff --git a/ArchitectureColoredPainting/images/icon_window_minimize.png b/ArchitectureColoredPainting/images/icon_window_minimize.png
new file mode 100644
index 0000000..29bceed
Binary files /dev/null and b/ArchitectureColoredPainting/images/icon_window_minimize.png differ
diff --git a/ArchitectureColoredPainting/images/icon_window_restore.png b/ArchitectureColoredPainting/images/icon_window_restore.png
new file mode 100644
index 0000000..be29650
Binary files /dev/null and b/ArchitectureColoredPainting/images/icon_window_restore.png differ
diff --git a/ArchitectureColoredPainting/lightstyle.qss b/ArchitectureColoredPainting/lightstyle.qss
new file mode 100644
index 0000000..e69de29
diff --git a/ArchitectureColoredPainting/msvc_make.bat b/ArchitectureColoredPainting/msvc_make.bat
new file mode 100644
index 0000000..416198b
--- /dev/null
+++ b/ArchitectureColoredPainting/msvc_make.bat
@@ -0,0 +1,2 @@
+chcp 65001
+"E:\Qt\Tools\QtCreator\bin\jom\jom.exe" %*
\ No newline at end of file
diff --git a/ArchitectureColoredPainting/qt.conf b/ArchitectureColoredPainting/qt.conf
new file mode 100644
index 0000000..367a59e
--- /dev/null
+++ b/ArchitectureColoredPainting/qt.conf
@@ -0,0 +1,2 @@
+[Platforms]
+WindowsArguments = fontengine=freetype
diff --git a/ArchitectureColoredPainting/src/CaptionButton.cpp b/ArchitectureColoredPainting/src/CaptionButton.cpp
new file mode 100644
index 0000000..ec4a2ea
--- /dev/null
+++ b/ArchitectureColoredPainting/src/CaptionButton.cpp
@@ -0,0 +1,261 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2018-2022 Antonio Dias
+
+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 "captionbutton.h"
+
+CaptionButton::CaptionButton(QWidget *parent) : QWidget(parent)
+{
+ m_is_active = false;
+ m_is_under_mouse = false;
+ m_is_pressed = false;
+ m_icon_dark = false;
+
+ setAttribute(Qt::WA_Hover);
+}
+
+CaptionButton::~CaptionButton()
+{
+
+}
+
+QPixmap CaptionButton::drawIcon(const QPixmap &icon, bool active, bool force_light)
+{
+ QImage tmp = icon.toImage();
+
+ if (!active)
+ {
+ for (int i = 0; i < tmp.height(); i++)
+ {
+ for (int j = 0; j < tmp.width(); j++)
+ {
+ QColor pixel = QColor::fromRgba(tmp.pixel(j, i));
+
+ pixel.setRedF(pixel.redF() * 0.5f);
+ pixel.setGreenF(pixel.greenF() * 0.5f);
+ pixel.setBlueF(pixel.blueF() * 0.5f);
+
+ tmp.setPixel(j, i, pixel.rgba());
+ }
+ }
+ }
+
+ if (m_icon_dark && !force_light)
+ tmp.invertPixels();
+
+ return QPixmap::fromImage(tmp);
+}
+
+void CaptionButton::init(IconType type)
+{
+ m_type = type;
+
+ setColors();
+ drawIcons();
+}
+
+void CaptionButton::drawIcons()
+{
+ switch (m_type)
+ {
+ case IconType::Minimize:
+ {
+ QPixmap icon = QPixmap(":/images/icon_window_minimize.png");
+
+ m_active_icon = drawIcon(icon, true);
+ m_inactive_icon = drawIcon(icon, false);
+
+ break;
+ }
+ case IconType::Restore:
+ {
+ QPixmap icon = QPixmap(":/images/icon_window_restore.png");
+
+ m_active_icon = drawIcon(icon, true);
+ m_inactive_icon = drawIcon(icon, false);
+
+ break;
+ }
+ case IconType::Maximize:
+ {
+ QPixmap icon = QPixmap(":/images/icon_window_maximize.png");
+
+ m_active_icon = drawIcon(icon, true);
+ m_inactive_icon = drawIcon(icon, false);
+
+ break;
+ }
+ case IconType::Close:
+ {
+ QPixmap icon = QPixmap(":/images/icon_window_close.png");
+
+ m_active_icon = drawIcon(icon, true);
+ m_inactive_icon = drawIcon(icon, false);
+ m_close_icon_hover = drawIcon(icon, true, true);
+
+ break;
+ }
+ }
+}
+
+void CaptionButton::setColors()
+{
+ if (m_icon_dark)
+ {
+ if (m_type == IconType::Close)
+ {
+ m_normal = QColor("transparent");
+ m_hover = QColor("#F00000");
+ m_pressed = QColor("#F1707A");
+ }
+ else
+ {
+ m_normal = QColor("transparent");
+ m_hover = QColor("#E5E5E5");
+ m_pressed = QColor("#CACACB");
+ }
+ }
+ else
+ {
+ if (m_type == IconType::Close)
+ {
+ m_normal = QColor("transparent");
+ m_hover = QColor("#F00000");
+ m_pressed = QColor("#F1707A");
+ }
+ else
+ {
+ m_normal = QColor("transparent");
+ m_hover = QColor("#505050");
+ m_pressed = QColor("#3F3F3F");
+ }
+ }
+
+ repaint();
+}
+
+void CaptionButton::setIconMode(bool icon_dark)
+{
+ m_icon_dark = icon_dark;
+
+ drawIcons();
+ setColors();
+
+ repaint();
+}
+
+void CaptionButton::setActive(bool is_active)
+{
+ m_is_active = is_active;
+
+ repaint();
+}
+
+void CaptionButton::setState(int state)
+{
+ switch (state)
+ {
+ case QEvent::HoverEnter:
+ {
+ m_is_under_mouse = true;
+
+ repaint();
+
+ break;
+ }
+ case QEvent::HoverLeave:
+ {
+ m_is_under_mouse = false;
+
+ repaint();
+
+ break;
+ }
+ case QEvent::MouseButtonPress:
+ {
+ m_is_pressed = true;
+
+ m_is_under_mouse = true;
+
+ repaint();
+
+ break;
+ }
+ case QEvent::MouseButtonRelease:
+ {
+ m_is_pressed = false;
+
+ m_is_under_mouse = false;
+
+ repaint();
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void CaptionButton::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+
+ QPixmap current_icon = m_active_icon;
+ QColor current_color = m_normal;
+
+ //Change icon if needed
+ if (m_is_under_mouse)
+ {
+ if (m_type == IconType::Close)
+ current_icon = m_close_icon_hover;
+ }
+ else
+ {
+ if (!m_is_active)
+ current_icon = m_inactive_icon;
+ }
+
+ //Change background color if needed
+ if (m_is_pressed)
+ {
+ if (m_is_under_mouse)
+ current_color = m_pressed;
+ }
+ else
+ {
+ if (m_is_under_mouse)
+ current_color = m_hover;
+ }
+
+ QPainter painter(this);
+ painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
+ painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
+
+ painter.fillRect(rect(), current_color);
+
+ QRect target_rect;
+ target_rect = current_icon.rect();
+ target_rect.setSize(QSize(16, 16));
+ target_rect = QRect(rect().center() - target_rect.center(), target_rect.size());
+ painter.drawPixmap(target_rect, current_icon);
+}
diff --git a/ArchitectureColoredPainting/src/CaptionButton.h b/ArchitectureColoredPainting/src/CaptionButton.h
new file mode 100644
index 0000000..e2a3f2b
--- /dev/null
+++ b/ArchitectureColoredPainting/src/CaptionButton.h
@@ -0,0 +1,81 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2018-2022 Antonio Dias
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef CAPTIONBUTTON_H
+#define CAPTIONBUTTON_H
+
+#include
+#include
+#include
+
+class CaptionButton : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit CaptionButton(QWidget *parent = nullptr);
+ ~CaptionButton();
+
+ enum class IconType
+ {
+ Minimize,
+ Restore,
+ Maximize,
+ Close
+ };
+
+ void init(IconType type);
+
+signals:
+ void clicked();
+
+public slots:
+ void setIconMode(bool icon_dark);
+ void setActive(bool is_active);
+ void setState(int state);
+
+private:
+ //Functions
+ QPixmap drawIcon(const QPixmap &icon, bool active, bool force_light = false);
+ void setColors();
+ void drawIcons();
+ void paintEvent(QPaintEvent *event);
+
+ //Variables
+ QPixmap m_inactive_icon;
+ QPixmap m_active_icon;
+
+ QPixmap m_close_icon_hover;
+
+ QColor m_normal;
+ QColor m_hover;
+ QColor m_pressed;
+
+ IconType m_type;
+ bool m_is_active;
+ bool m_is_under_mouse;
+ bool m_is_pressed;
+ bool m_icon_dark;
+};
+
+#endif // CAPTIONBUTTON_H
diff --git a/ArchitectureColoredPainting/src/IconWidget.cpp b/ArchitectureColoredPainting/src/IconWidget.cpp
new file mode 100644
index 0000000..65412b2
--- /dev/null
+++ b/ArchitectureColoredPainting/src/IconWidget.cpp
@@ -0,0 +1,75 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2018-2022 Antonio Dias
+
+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 "iconwidget.h"
+
+#define ICONWIDTH 16
+#define ICONHEIGHT 16
+
+IconWidget::IconWidget(QWidget *parent) : QWidget(parent)
+{
+ m_active = true;
+}
+
+void IconWidget::setPixmap(const QPixmap &pixmap)
+{
+ m_pixmap = pixmap;
+
+ QImage tmp = m_pixmap.toImage();
+
+ for (int i = 0; i < m_pixmap.width(); i++)
+ {
+ for (int j = 0; j < m_pixmap.height(); j++)
+ {
+ int gray = qGray(tmp.pixel(i, j));
+
+ int alpha = qAlpha(tmp.pixel(i, j));
+
+ tmp.setPixel(i, j, qRgba(gray, gray, gray, alpha));
+ }
+ }
+
+ m_grayed_pixmap = QPixmap::fromImage(tmp);
+
+ repaint();
+}
+
+void IconWidget::setActive(bool active)
+{
+ m_active = active;
+ repaint();
+}
+
+void IconWidget::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+
+ QPainter painter(this);
+ painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
+
+ painter.drawPixmap((width() - ICONWIDTH)/ 2,
+ (height() - ICONHEIGHT) / 2,
+ ICONWIDTH, ICONHEIGHT,
+ m_active ? m_pixmap : m_grayed_pixmap);
+}
diff --git a/ArchitectureColoredPainting/src/IconWidget.h b/ArchitectureColoredPainting/src/IconWidget.h
new file mode 100644
index 0000000..94f489d
--- /dev/null
+++ b/ArchitectureColoredPainting/src/IconWidget.h
@@ -0,0 +1,52 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2018-2022 Antonio Dias
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef ICONWIDGET_H
+#define ICONWIDGET_H
+
+#include
+#include
+#include
+
+class IconWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit IconWidget(QWidget *parent = nullptr);
+
+public slots:
+ void setPixmap(const QPixmap &pixmap);
+ void setActive(bool active);
+
+private:
+ //Functions
+ void paintEvent(QPaintEvent *event);
+
+ //Variables
+ QPixmap m_pixmap;
+ QPixmap m_grayed_pixmap;
+ bool m_active;
+};
+
+#endif // ICONWIDGET_H
diff --git a/ArchitectureColoredPainting/src/MainWindow.cpp b/ArchitectureColoredPainting/src/MainWindow.cpp
index 6ac0d73..d5c1af8 100644
--- a/ArchitectureColoredPainting/src/MainWindow.cpp
+++ b/ArchitectureColoredPainting/src/MainWindow.cpp
@@ -1,12 +1,491 @@
#include "MainWindow.h"
#include "Renderer/RendererGLWidget.h"
#include "qslider.h"
+#include
+#include "NavigationBarWidget.h"
+#include
-MainWindow::MainWindow(QWidget *parent)
- : QMainWindow(parent)
+inline void fontTheme()
{
- ui.setupUi(this);
+ QFont defaultFont = qApp->font();
+ defaultFont.setPointSize(defaultFont.pointSize() + 2);
+ qApp->setFont(defaultFont);
+}
+
+inline void setThemeStyleSheet(bool dark)
+{
+ QFile file(dark ? ":/darkstyle.qss" : ":/lightstyle.qss");
+
+ if (!file.open(QFile::ReadOnly))
+ return;
+
+ const QString style_sheet = QLatin1String(file.readAll());
+
+ file.close();
+
+ qApp->setStyleSheet(style_sheet);
+}
+
+inline void darkTheme()
+{
+ 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));
+
+ qApp->setPalette(darkPalette);
+
+ setThemeStyleSheet(true /*dark*/);
+}
+
+
+inline void lightTheme()
+{
+ 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));
+
+ qApp->setPalette(lightPalette);
+
+ setThemeStyleSheet(false /*dark*/);
+}
+
+FramelessWindow::FramelessWindow(QWidget* parent) : QWidget(parent), ui(new Ui::FramelessWindow)
+{
+ ui->setupUi(this);
+}
+
+FramelessWindow::~FramelessWindow()
+{
+ delete ui;
+}
+
+CentralWidget::CentralWidget(QWidget* parent) : QMainWindow(parent)
+{
+ ui.setupUi(this);
+ NavigationBarWidget* navigationBarWidget = new NavigationBarWidget();
+
+ QHBoxLayout* tabBarLayout = new QHBoxLayout(ui.tabWidget);
+ tabBarLayout->setSpacing(0);
+ tabBarLayout->setMargin(0);
+ tabBarLayout->addWidget(navigationBarWidget, 0, Qt::AlignTop | Qt::AlignHCenter);
+
+ QObject::connect(navigationBarWidget->buttonGroup, &QButtonGroup::idClicked,
+ ui.tabWidget, &QTabWidget::setCurrentIndex);
+ QObject::connect(ui.tabWidget, &QTabWidget::currentChanged,
+ ui.rendererWidget, &RendererWidget::currentTabChanged);
+}
+
+CentralWidget::~CentralWidget()
+{
+}
+
+
+MainWindow::MainWindow(QWidget* parent)
+ : QGoodWindow(parent)
+{
+
+ // if the system will draw borders or if this application must do that
+ m_draw_borders = !QGoodWindow::shouldBordersBeDrawnBySystem();
+
+ m_dark = QGoodWindow::isSystemThemeDark();
+
+ // create frameless window
+ m_window = new FramelessWindow(this);
+
+ m_central_widget = new CentralWidget(m_window);
+
+ // add the mainwindow to our custom frameless window
+ m_window->ui->windowContent->layout()->addWidget(m_central_widget);
+
+ 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");
+
+
+ resize(m_central_widget->size());
+ setCentralWidget(m_window);
+
+ 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?");
+
+ if (result != QMessageBox::Yes)
+ event->ignore();*/
+}
+
+
diff --git a/ArchitectureColoredPainting/src/MainWindow.h b/ArchitectureColoredPainting/src/MainWindow.h
index 337ecec..1fdb6f2 100644
--- a/ArchitectureColoredPainting/src/MainWindow.h
+++ b/ArchitectureColoredPainting/src/MainWindow.h
@@ -2,15 +2,55 @@
#include
#include "ui_MainWindow.h"
+#include "ui_FramelessWindow.h"
-class MainWindow : public QMainWindow
+#define QGOODWINDOW
+#include
+
+class FramelessWindow : public QWidget
{
Q_OBJECT
-
public:
- MainWindow(QWidget *parent = nullptr);
+ explicit FramelessWindow(QWidget* parent = nullptr);
+
+ ~FramelessWindow();
+
+ Ui::FramelessWindow* ui;
+};
+
+
+class CentralWidget : public QMainWindow
+{
+ Q_OBJECT
+public:
+ explicit CentralWidget(QWidget* parent = nullptr);
+
+ ~CentralWidget();
+
+ Ui::MainWindowClass ui;
+};
+
+
+class MainWindow : public QGoodWindow
+{
+ Q_OBJECT
+public:
+ explicit MainWindow(QWidget* parent = nullptr);
~MainWindow();
private:
- Ui::MainWindowClass ui;
+ //Functions
+ void styleWindow();
+ void captionButtonStateChanged(const QGoodWindow::CaptionButtonState& state);
+ bool event(QEvent* event);
+ bool nativeEvent(const QByteArray& eventType, void* message, long* result);
+
+ void closeEvent(QCloseEvent* event);
+
+ //Variables
+ FramelessWindow* m_window;
+
+ CentralWidget* m_central_widget;
+ bool m_draw_borders;
+ bool m_dark;
};
diff --git a/ArchitectureColoredPainting/src/NavigationBarWidget.cpp b/ArchitectureColoredPainting/src/NavigationBarWidget.cpp
new file mode 100644
index 0000000..e4cdacc
--- /dev/null
+++ b/ArchitectureColoredPainting/src/NavigationBarWidget.cpp
@@ -0,0 +1,14 @@
+#include "NavigationBarWidget.h"
+
+NavigationBarWidget::NavigationBarWidget(QWidget *parent)
+ : QWidget(parent)
+{
+ ui.setupUi(this);
+ buttonGroup = new QButtonGroup(this);
+ buttonGroup->addButton(ui.radioButton0, 0);
+ buttonGroup->addButton(ui.radioButton1, 1);
+ ui.radioButton0->setChecked(true);
+;}
+
+NavigationBarWidget::~NavigationBarWidget()
+{}
diff --git a/ArchitectureColoredPainting/src/NavigationBarWidget.h b/ArchitectureColoredPainting/src/NavigationBarWidget.h
new file mode 100644
index 0000000..07d48e7
--- /dev/null
+++ b/ArchitectureColoredPainting/src/NavigationBarWidget.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include
+#include
+#include "ui_NavigationBarWidget.h"
+
+class NavigationBarWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ NavigationBarWidget(QWidget *parent = nullptr);
+ ~NavigationBarWidget();
+ QButtonGroup* buttonGroup;
+private:
+ Ui::NavigationBarWidgetClass ui;
+
+};
diff --git a/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.cpp b/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.cpp
index de162b5..0a821b6 100644
--- a/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.cpp
+++ b/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.cpp
@@ -17,8 +17,7 @@ RendererGLWidget::RendererGLWidget(QWidget* parent)
, camera(QVector3D(0.0f, 100.0f, 0.0f))
, light(&camera)
{
- //startTimer(1000 / QGuiApplication::primaryScreen()->refreshRate());
- startTimer(1);
+ //startTimer();
lastFrame = std::clock();
setFocusPolicy(Qt::StrongFocus);
QSurfaceFormat format;
@@ -51,6 +50,19 @@ RendererGLWidget::~RendererGLWidget()
}
}
+void RendererGLWidget::startTimer()
+{
+ //startTimer(1000 / QGuiApplication::primaryScreen()->refreshRate());
+ if (timerId == -1)
+ timerId = QObject::startTimer(1);
+}
+
+void RendererGLWidget::stopTimer()
+{
+ killTimer(timerId);
+ timerId = -1;
+}
+
void RendererGLWidget::setMainLightPitch(float pitch)
{
//qDebug() << "pitch" << pitch;
@@ -393,7 +405,7 @@ void RendererGLWidget::paintGL()
void RendererGLWidget::resizeGL(int width, int height)
{
- frameWidth = ceil( devicePixelRatioF() * width);
+ frameWidth = ceil(devicePixelRatioF() * width);
frameHeight = ceil(devicePixelRatioF() * height);
qDebug() << frameWidth << "x" << frameHeight;
camera.Ratio = (float)frameWidth / (float)frameHeight;
@@ -557,6 +569,7 @@ void RendererGLWidget::timerEvent(QTimerEvent* event)
float yoffset = center.y() - cursor().pos().y();
camera.ProcessMouseMovement(xoffset, yoffset);
cursor().setPos(center);
+ //qDebug() << center;
}
if (pressedKeys.contains(Qt::Key_W)) {
diff --git a/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.h b/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.h
index dd8c5e7..5d4635f 100644
--- a/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.h
+++ b/ArchitectureColoredPainting/src/Renderer/RendererGLWidget.h
@@ -19,6 +19,8 @@ public:
RendererGLWidget(QWidget* parent = nullptr);
~RendererGLWidget();
+ void startTimer();
+ void stopTimer();
public slots:
void setMainLightPitch(float pitch);
void setMainLightYaw(float yaw);
@@ -34,6 +36,7 @@ protected:
void focusOutEvent(QFocusEvent* event) override;
private:
+ int timerId = -1;
int frameWidth, frameHeight;
int depthWidth, depthHeight;
QSet pressedKeys;
diff --git a/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp b/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp
index 32fcad2..8eeb965 100644
--- a/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp
+++ b/ArchitectureColoredPainting/src/Renderer/RendererWidget.cpp
@@ -13,3 +13,15 @@ RendererWidget::RendererWidget(QWidget *parent)
RendererWidget::~RendererWidget()
{}
+
+void RendererWidget::currentTabChanged(int index)
+{
+ if (index == 1)
+ {
+ ui.openGLWidget->startTimer();
+ }
+ else
+ {
+ ui.openGLWidget->stopTimer();
+ }
+}
\ No newline at end of file
diff --git a/ArchitectureColoredPainting/src/Renderer/RendererWidget.h b/ArchitectureColoredPainting/src/Renderer/RendererWidget.h
index b09b0e7..d1c518c 100644
--- a/ArchitectureColoredPainting/src/Renderer/RendererWidget.h
+++ b/ArchitectureColoredPainting/src/Renderer/RendererWidget.h
@@ -8,9 +8,11 @@ class RendererWidget : public QWidget
Q_OBJECT
public:
+
RendererWidget(QWidget *parent = nullptr);
~RendererWidget();
-
+public slots:
+ void currentTabChanged(int index);
private:
Ui::RendererWidgetClass ui;
};
diff --git a/ArchitectureColoredPainting/src/TitleWidget.cpp b/ArchitectureColoredPainting/src/TitleWidget.cpp
new file mode 100644
index 0000000..cd4d5b6
--- /dev/null
+++ b/ArchitectureColoredPainting/src/TitleWidget.cpp
@@ -0,0 +1,80 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2018-2022 Antonio Dias
+
+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 "titlewidget.h"
+
+TitleWidget::TitleWidget(QWidget *parent) : QWidget(parent)
+{
+ m_active = false;
+}
+
+void TitleWidget::setText(const QString &text)
+{
+ m_title = text;
+ repaint();
+}
+
+void TitleWidget::setActive(bool active)
+{
+ m_active = active;
+ repaint();
+}
+
+void TitleWidget::setTitleColor(const QColor &active_color, const QColor &inactive_color)
+{
+ m_active_color = active_color;
+ m_inactive_color = inactive_color;
+
+ repaint();
+}
+
+void TitleWidget::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+
+ QPainter painter(this);
+ painter.setRenderHints(QPainter::Antialiasing);
+
+ QFont font;
+ font.setPointSize(10);
+#ifdef Q_OS_WIN
+ font.setFamily("Segoe UI, Microsoft YaHei UI");
+#else
+ font.setFamily(qApp->font().family());
+#endif
+
+ painter.setFont(font);
+
+ QPen pen;
+ pen.setColor(m_active ? m_active_color : m_inactive_color);
+
+ painter.setPen(pen);
+
+ QFontMetrics metrics(painter.font());
+ QSize title_size = metrics.size(0, m_title);
+
+ QString title = metrics.elidedText(m_title, Qt::ElideRight, width());
+
+ painter.drawText(0, (height() - title_size.height()) / 2, title_size.width(), title_size.height(), 0, title);
+}
diff --git a/ArchitectureColoredPainting/src/TitleWidget.h b/ArchitectureColoredPainting/src/TitleWidget.h
new file mode 100644
index 0000000..ea297b7
--- /dev/null
+++ b/ArchitectureColoredPainting/src/TitleWidget.h
@@ -0,0 +1,54 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2018-2022 Antonio Dias
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef TITLEWIDGET_H
+#define TITLEWIDGET_H
+
+#include
+#include
+#include
+
+class TitleWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit TitleWidget(QWidget *parent = nullptr);
+
+public slots:
+ void setText(const QString &text);
+ void setActive(bool active);
+ void setTitleColor(const QColor &active_color, const QColor &inactive_color);
+
+private:
+ //Functions
+ void paintEvent(QPaintEvent *event);
+
+ //Variables
+ QString m_title;
+ bool m_active;
+ QColor m_active_color;
+ QColor m_inactive_color;
+};
+
+#endif // TITLEWIDGET_H
diff --git a/QGoodWindow/.qmake.stash b/QGoodWindow/.qmake.stash
new file mode 100644
index 0000000..8525508
--- /dev/null
+++ b/QGoodWindow/.qmake.stash
@@ -0,0 +1,22 @@
+QMAKE_CXX.QT_COMPILER_STDCXX = 199711L
+QMAKE_CXX.QMAKE_MSC_VER = 1932
+QMAKE_CXX.QMAKE_MSC_FULL_VER = 193231329
+QMAKE_CXX.COMPILER_MACROS = \
+ QT_COMPILER_STDCXX \
+ QMAKE_MSC_VER \
+ QMAKE_MSC_FULL_VER
+QMAKE_CXX.INCDIRS = \
+ "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.32.31326\\ATLMFC\\include" \
+ "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.32.31326\\include" \
+ "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um" \
+ "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.19041.0\\ucrt" \
+ "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.19041.0\\\\shared" \
+ "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.19041.0\\\\um" \
+ "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.19041.0\\\\winrt" \
+ "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.19041.0\\\\cppwinrt"
+QMAKE_CXX.LIBDIRS = \
+ "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.32.31326\\ATLMFC\\lib\\x64" \
+ "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.32.31326\\lib\\x64" \
+ "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\lib\\um\\x64" \
+ "C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.19041.0\\ucrt\\x64" \
+ "C:\\Program Files (x86)\\Windows Kits\\10\\\\lib\\10.0.19041.0\\\\um\\x64"
diff --git a/QGoodWindow/QGoodWindow b/QGoodWindow/QGoodWindow
new file mode 100644
index 0000000..6bc1130
--- /dev/null
+++ b/QGoodWindow/QGoodWindow
@@ -0,0 +1,25 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2021 Antonio Dias
+
+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 "src/qgoodwindow.h"
diff --git a/QGoodWindow/QGoodWindow.pro b/QGoodWindow/QGoodWindow.pro
new file mode 100644
index 0000000..0bb2163
--- /dev/null
+++ b/QGoodWindow/QGoodWindow.pro
@@ -0,0 +1,92 @@
+#The MIT License (MIT)
+
+#Copyright © 2021-2022 Antonio Dias
+
+#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.
+
+QT += core gui widgets
+
+CONFIG += c++11
+
+SOURCES += \
+ $$PWD/src/qgoodwindow.cpp
+
+HEADERS += \
+ $$PWD/src/qgoodwindow.h
+
+INCLUDEPATH += $$PWD
+
+win32 {
+equals(QT_MAJOR_VERSION, 5){
+QT += winextras
+}
+
+SOURCES += \
+ $$PWD/src/shadow.cpp
+
+HEADERS += \
+ $$PWD/src/common.h \
+ $$PWD/src/shadow.h
+
+LIBS += -lUser32 -lGdi32
+
+DEFINES += QGOODWINDOW
+CONFIG += qgoodwindow
+}
+
+unix:!mac:!android {
+equals(QT_MAJOR_VERSION, 5){
+QT += testlib x11extras
+}
+
+equals(QT_MAJOR_VERSION, 6){
+QT += gui-private
+}
+
+SOURCES += \
+ $$PWD/src/shadow.cpp
+
+HEADERS += \
+ $$PWD/src/shadow.h
+
+LIBS += -lX11
+
+CONFIG += link_pkgconfig
+PKGCONFIG += gtk+-2.0
+
+DEFINES += QGOODWINDOW
+CONFIG += qgoodwindow
+}
+
+mac {
+OBJECTIVE_SOURCES += \
+ $$PWD/src/macosnative.mm
+
+SOURCES += \
+ $$PWD/src/notification.cpp
+
+HEADERS += \
+ $$PWD/src/macosnative.h \
+ $$PWD/src/notification.h
+
+LIBS += -framework Foundation -framework Cocoa -framework AppKit
+
+DEFINES += QGOODWINDOW
+CONFIG += qgoodwindow
+}
diff --git a/QGoodWindow/QGoodWindow.vcxproj b/QGoodWindow/QGoodWindow.vcxproj
new file mode 100644
index 0000000..2b9b86f
--- /dev/null
+++ b/QGoodWindow/QGoodWindow.vcxproj
@@ -0,0 +1,213 @@
+
+
+
+
+ Release
+ x64
+
+
+ Debug
+ x64
+
+
+
+ {B982E745-C0B1-46B3-A27B-743AF105F2D0}
+ QGoodWindow
+ QtVS_v304
+ 10.0.19041.0
+ 10.0.19041.0
+ $(MSBuildProjectDirectory)\QtMsBuild
+
+
+
+ v143
+ release\
+ false
+ NotSet
+ StaticLibrary
+ release\
+ QGoodWindow
+
+
+ v143
+ debug\
+ false
+ NotSet
+ StaticLibrary
+ debug\
+ QGoodWindow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ debug\
+ debug\
+ QGoodWindow
+ true
+
+
+ release\
+ release\
+ QGoodWindow
+ true
+ false
+
+
+ 5.15.2_msvc2019_64
+ core;gui;widgets;winextras
+
+
+ 5.15.2_msvc2019_64
+ core;gui;widgets;winextras
+
+
+
+
+
+
+ GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;release;/include;%(AdditionalIncludeDirectories)
+ -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)
+ release\
+ false
+ None
+ 4577;4467;%(DisableSpecificWarnings)
+ Sync
+ release\
+ MaxSpeed
+ _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;QGOODWINDOW;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)
+ false
+
+
+ MultiThreadedDLL
+ true
+ true
+ Level3
+ true
+
+
+ User32.lib;Gdi32.lib;shell32.lib;%(AdditionalDependencies)
+ C:\openssl\lib;C:\Utils\my_sql\mysql-5.7.25-winx64\lib;C:\Utils\postgresql\pgsql\lib;%(AdditionalLibraryDirectories)
+ "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)
+ true
+ false
+ true
+ false
+ true
+ $(OutDir)\QGoodWindow.exe
+ true
+ Windows
+ true
+
+
+ Unsigned
+ None
+ 0
+
+
+ _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;QGOODWINDOW;NDEBUG;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_WINEXTRAS_LIB;QT_GUI_LIB;QT_CORE_LIB;%(PreprocessorDefinitions)
+
+
+ msvc
+ D:/??2022/ArchitectureColoredPainting/QGoodWindow/$(Configuration)/moc_predefs.h
+ Moc'ing %(Identity)...
+ output
+ $(Configuration)
+ moc_%(Filename).cpp
+
+
+
+
+ GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;debug;/include;%(AdditionalIncludeDirectories)
+ -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)
+ debug\
+ false
+ ProgramDatabase
+ 4577;4467;%(DisableSpecificWarnings)
+ Sync
+ debug\
+ Disabled
+ _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;QGOODWINDOW;%(PreprocessorDefinitions)
+ false
+ MultiThreadedDebugDLL
+ true
+ true
+ Level3
+ true
+
+
+ User32.lib;Gdi32.lib;shell32.lib;%(AdditionalDependencies)
+ C:\openssl\lib;C:\Utils\my_sql\mysql-5.7.25-winx64\lib;C:\Utils\postgresql\pgsql\lib;%(AdditionalLibraryDirectories)
+ "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)
+ true
+ true
+ true
+ $(OutDir)\QGoodWindow.exe
+ true
+ Windows
+ true
+
+
+ Unsigned
+ None
+ 0
+
+
+ _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;QGOODWINDOW;QT_WIDGETS_LIB;QT_WINEXTRAS_LIB;QT_GUI_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions)
+
+
+ msvc
+ D:/??2022/ArchitectureColoredPainting/QGoodWindow/$(Configuration)/moc_predefs.h
+ Moc'ing %(Identity)...
+ output
+ $(Configuration)
+ moc_%(Filename).cpp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Document
+ true
+ $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs)
+ cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h
+ Generate moc_predefs.h
+ debug\moc_predefs.h;%(Outputs)
+
+
+ Document
+ $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs)
+ cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >release\moc_predefs.h
+ Generate moc_predefs.h
+ release\moc_predefs.h;%(Outputs)
+ true
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QGoodWindow/QGoodWindow.vcxproj.filters b/QGoodWindow/QGoodWindow.vcxproj.filters
new file mode 100644
index 0000000..4b17046
--- /dev/null
+++ b/QGoodWindow/QGoodWindow.vcxproj.filters
@@ -0,0 +1,60 @@
+
+
+
+
+ {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}
+ cpp;c;cxx;moc;h;def;odl;idl;res;
+
+
+ {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}
+ cpp;c;cxx;moc;h;def;odl;idl;res;
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hpp;hxx;hm;inl;inc;xsd
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hpp;hxx;hm;inl;inc;xsd
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Generated Files
+
+
+ Generated Files
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QGoodWindow/QGoodWindow.write.1u.tlog b/QGoodWindow/QGoodWindow.write.1u.tlog
new file mode 100644
index 0000000..6e0efc4
Binary files /dev/null and b/QGoodWindow/QGoodWindow.write.1u.tlog differ
diff --git a/QGoodWindow/moc.read.1u.tlog b/QGoodWindow/moc.read.1u.tlog
new file mode 100644
index 0000000..5cb46b7
Binary files /dev/null and b/QGoodWindow/moc.read.1u.tlog differ
diff --git a/QGoodWindow/moc.write.1u.tlog b/QGoodWindow/moc.write.1u.tlog
new file mode 100644
index 0000000..b407de4
Binary files /dev/null and b/QGoodWindow/moc.write.1u.tlog differ
diff --git a/QGoodWindow/src/common.h b/QGoodWindow/src/common.h
new file mode 100644
index 0000000..f65cd3e
--- /dev/null
+++ b/QGoodWindow/src/common.h
@@ -0,0 +1,79 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2018-2022 Antonio Dias
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#ifdef _WIN32
+
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+
+#define _WIN32_WINNT _WIN32_WINNT_VISTA
+
+#endif
+
+#include
+#include
+#include
+
+#ifdef Q_OS_LINUX
+
+#define BORDERWIDTH 10 //PIXELS
+
+#define MOVERESIZE_MOVE 8 //X11 Fixed Value
+
+#endif
+
+#if defined Q_OS_LINUX || defined Q_OS_MAC
+//The positive values are mandatory on Linux and arbitrary on macOS,
+//using the same for convenience.
+//The negative value are arbitrary on both platforms.
+
+#define NO_WHERE -1
+#define HTMINBUTTON -2
+#define HTMAXBUTTON -3
+#define HTCLOSE -4
+#define TOP_LEFT 0
+#define TOP 1
+#define TOP_RIGHT 2
+#define LEFT 7
+#define RIGHT 3
+#define BOTTOM_LEFT 6
+#define BOTTOM 5
+#define BOTTOM_RIGHT 4
+#define TITLE_BAR 8
+
+#endif
+
+#ifdef Q_OS_WIN
+
+#include
+
+#define BORDERWIDTH (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER))
+
+#endif
+
+#endif // COMMON_H
diff --git a/QGoodWindow/src/macosnative.h b/QGoodWindow/src/macosnative.h
new file mode 100644
index 0000000..59d1615
--- /dev/null
+++ b/QGoodWindow/src/macosnative.h
@@ -0,0 +1,50 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2021-2022 Antonio Dias
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef MACOSNATIVE_H
+#define MACOSNATIVE_H
+
+#include "notification.h"
+
+extern Notification notification;
+
+//\cond HIDDEN_SYMBOLS
+namespace macOSNative
+{
+ void registerThemeChangeNotification();
+ void registerNotification(const char *notification_name, long wid);
+ void unregisterNotification();
+
+ inline void handleNotification(const char *notification_name, long wid)
+ {
+ notification.notification(notification_name, wid);
+ }
+
+ void setStyle(long winid, bool fullscreen);
+
+ const char *themeName();
+}
+//\endcond
+
+#endif // MACOSNATIVE_H
diff --git a/QGoodWindow/src/macosnative.mm b/QGoodWindow/src/macosnative.mm
new file mode 100644
index 0000000..7d056b4
--- /dev/null
+++ b/QGoodWindow/src/macosnative.mm
@@ -0,0 +1,148 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2021-2022 Antonio Dias
+
+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 "macosnative.h"
+#include
+
+void macOSNative::setStyle(long winid, bool fullscreen)
+{
+ NSView *nativeView = reinterpret_cast(winid);
+ NSWindow *nativeWindow = [nativeView window];
+
+ if (!fullscreen)
+ {
+ [nativeWindow setStyleMask:NSWindowStyleMaskResizable |
+ NSWindowStyleMaskMiniaturizable |
+ NSWindowStyleMaskClosable |
+ NSWindowStyleMaskTitled |
+ NSWindowStyleMaskFullSizeContentView];
+ [nativeWindow setMovableByWindowBackground:NO];
+ [nativeWindow setMovable:NO];
+ [nativeWindow setTitlebarAppearsTransparent:YES];
+ [nativeWindow setShowsToolbarButton:NO];
+ [nativeWindow setTitleVisibility:NSWindowTitleHidden];
+ [nativeWindow standardWindowButton:NSWindowMiniaturizeButton].hidden = YES;
+ [nativeWindow standardWindowButton:NSWindowCloseButton].hidden = YES;
+ [nativeWindow standardWindowButton:NSWindowZoomButton].hidden = YES;
+ [nativeWindow makeKeyWindow];
+ }
+ else
+ {
+ [nativeWindow setStyleMask:0];
+ }
+}
+
+//\cond HIDDEN_SYMBOLS
+@interface Handler : NSObject
+{
+}
+@end
+//\endcond
+
+Handler *m_handler;
+
+void macOSNative::registerNotification(const char *notification_name, long wid)
+{
+ NSView *nativeView = reinterpret_cast(wid);
+ NSWindow *nativeWindow = [nativeView window];
+
+ [[NSNotificationCenter defaultCenter]
+ addObserver:m_handler
+ selector:@selector(NotificationHandler:)
+ name:[NSString stringWithUTF8String:notification_name]
+ object:nativeWindow];
+}
+
+void macOSNative::unregisterNotification()
+{
+ [[NSNotificationCenter defaultCenter]
+ removeObserver:m_handler];
+
+ [m_handler release];
+}
+
+//\cond HIDDEN_SYMBOLS
+@implementation Handler
++ (void)load
+{
+ m_handler = static_cast(self);
+}
+
++(void)NotificationHandler:(NSNotification*)notification
+{
+ const NSString *str = [notification name];
+
+ const NSWindow *nativeWindow = [notification object];
+
+ const NSView *nativeView = [nativeWindow contentView];
+
+ const char *cstr = [str cStringUsingEncoding:NSUTF8StringEncoding];
+
+ macOSNative::handleNotification(cstr, long(nativeView));
+}
+@end
+//\endcond
+
+//\cond HIDDEN_SYMBOLS
+@interface ThemeChangeHandler : NSObject
+{
+}
+@end
+//\endcond
+
+ThemeChangeHandler *m_theme_change_handler;
+
+//\cond HIDDEN_SYMBOLS
+@implementation ThemeChangeHandler
++ (void)load
+{
+ m_theme_change_handler = static_cast(self);
+}
+
++(void)ThemeChangeNotification:(NSNotification*)notification
+{
+ const NSString *str = [notification name];
+
+ const char *cstr = [str cStringUsingEncoding:NSUTF8StringEncoding];
+
+ macOSNative::handleNotification(cstr, 0);
+}
+@end
+//\endcond
+
+void macOSNative::registerThemeChangeNotification()
+{
+ [[NSDistributedNotificationCenter defaultCenter]
+ addObserver:m_theme_change_handler
+ selector:@selector(ThemeChangeNotification:)
+ name:@"AppleInterfaceThemeChangedNotification"
+ object:nil];
+}
+
+const char *macOSNative::themeName()
+{
+ NSString *str = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
+ const char *cstr = [str cStringUsingEncoding:NSUTF8StringEncoding];
+ return cstr;
+}
diff --git a/QGoodWindow/src/notification.cpp b/QGoodWindow/src/notification.cpp
new file mode 100644
index 0000000..082d5d5
--- /dev/null
+++ b/QGoodWindow/src/notification.cpp
@@ -0,0 +1,67 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2021-2022 Antonio Dias
+
+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 "notification.h"
+#include "qgoodwindow.h"
+#include "macosnative.h"
+
+Notification::Notification()
+{
+
+}
+
+void Notification::addWindow(void *ptr)
+{
+ m_ptr_list.append(ptr);
+}
+
+void Notification::removeWindow(void *ptr)
+{
+ m_ptr_list.removeAll(ptr);
+}
+
+void Notification::registerNotification(const QByteArray &name, WId wid)
+{
+ macOSNative::registerNotification(name.constData(), long(wid));
+}
+
+void Notification::notification(const char *notification_name, long wid)
+{
+ const QByteArray notification = QByteArray(notification_name);
+
+ for (void *ptr : m_ptr_list)
+ {
+ QGoodWindow *gw = static_cast(ptr);
+
+ if (wid == 0)
+ {
+ gw->notificationReceiver(notification);
+ }
+ else if (gw->winId() == WId(wid))
+ {
+ gw->notificationReceiver(notification);
+ break;
+ }
+ }
+}
diff --git a/QGoodWindow/src/notification.h b/QGoodWindow/src/notification.h
new file mode 100644
index 0000000..acf2b2c
--- /dev/null
+++ b/QGoodWindow/src/notification.h
@@ -0,0 +1,47 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2021-2022 Antonio Dias
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef NOTIFICATION_H
+#define NOTIFICATION_H
+
+#include
+#include
+
+//\cond HIDDEN_SYMBOLS
+class Notification
+{
+public:
+ Notification();
+
+ void addWindow(void *ptr);
+ void removeWindow(void *ptr);
+ void notification(const char *notification_name, long wid);
+ void registerNotification(const QByteArray &name, WId wid);
+
+private:
+ QList m_ptr_list;
+};
+//\endcond
+
+#endif // NOTIFICATION_H
diff --git a/QGoodWindow/src/qgoodwindow.cpp b/QGoodWindow/src/qgoodwindow.cpp
new file mode 100644
index 0000000..2f3c44e
--- /dev/null
+++ b/QGoodWindow/src/qgoodwindow.cpp
@@ -0,0 +1,3652 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2018-2022 Antonio Dias
+
+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 defined QGOODWINDOW && defined __linux__
+#include
+#endif
+
+#include "common.h"
+#include "qgoodwindow.h"
+#include "shadow.h"
+
+#ifndef QGOODWINDOW
+#ifdef Q_OS_WIN
+#undef Q_OS_WIN
+#endif
+#ifdef Q_OS_LINUX
+#undef Q_OS_LINUX
+#endif
+#ifdef Q_OS_MAC
+#undef Q_OS_MAC
+#endif
+#endif
+
+#ifdef Q_OS_WIN
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#include
+#endif
+#include
+#include
+#include
+
+namespace QGoodWindowUtils
+{
+class ParentWindow : public QWidget
+{
+public:
+ explicit ParentWindow(QWidget *parent) : QWidget(parent, Qt::Window)
+ {
+
+ }
+
+private:
+ bool event(QEvent *event)
+ {
+ switch (event->type())
+ {
+ case QEvent::ChildRemoved:
+ {
+ delete this;
+ return true;
+ }
+ default:
+ break;
+ }
+
+ return QWidget::event(event);
+ }
+};
+
+class NativeEventFilter : public QAbstractNativeEventFilter
+{
+public:
+ NativeEventFilter(QGoodWindow *gw)
+ {
+ m_gw = gw;
+ m_gw_hwnd = HWND(m_gw->winId());
+ }
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override
+#else
+ bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override
+#endif
+ {
+ Q_UNUSED(eventType)
+
+ MSG *msg = static_cast(message);
+
+ if (!IsWindowVisible(msg->hwnd))
+ return false;
+
+ if (!IsChild(m_gw_hwnd, msg->hwnd))
+ return false;
+
+ switch (msg->message)
+ {
+ case WM_NCHITTEST:
+ {
+ QPoint pos = QPoint(GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam));
+
+ HRESULT lresult = m_gw->ncHitTest(pos.x(), pos.y());
+
+ if (lresult == HTNOWHERE)
+ break;
+
+ //If region not contains the mouse, pass the
+ //WM_NCHITTEST event to main window.
+ *result = HTTRANSPARENT;
+ return true;
+ }
+ case WM_KEYDOWN:
+ {
+ if (GetKeyState(VK_SHIFT) & 0x8000)
+ {
+ switch (msg->wParam)
+ {
+ case VK_TAB:
+ //Prevent that SHIFT+TAB crashes the application.
+ return true;
+ default:
+ break;
+ }
+ }
+
+ break;
+ }
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ {
+ //Pass to main window...
+
+ if ((GetKeyState(VK_SHIFT) & 0x8000) && msg->wParam == VK_F10)
+ {
+ // ...when SHIFT+F10 is pressed.
+ SendMessageW(m_gw_hwnd, msg->message, msg->wParam, msg->lParam);
+ return true;
+ }
+
+ if ((GetKeyState(VK_MENU) & 0x8000) && msg->wParam == VK_SPACE)
+ {
+ // ...when ALT+SPACE is pressed.
+ SendMessageW(m_gw_hwnd, msg->message, msg->wParam, msg->lParam);
+ return true;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+private:
+ QPointer m_gw;
+ HWND m_gw_hwnd;
+};
+
+inline bool isWin11OrGreater()
+{
+ bool is_win_11_or_greater = false;
+
+ typedef NTSTATUS(WINAPI *tRtlGetVersion)(LPOSVERSIONINFOEXW);
+ tRtlGetVersion pRtlGetVersion = tRtlGetVersion(QLibrary::resolve("ntdll", "RtlGetVersion"));
+
+ if (pRtlGetVersion)
+ {
+ OSVERSIONINFOEXW os_info;
+ memset(&os_info, 0, sizeof(OSVERSIONINFOEXW));
+ os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+ NTSTATUS status = pRtlGetVersion(&os_info);
+ if (status == 0)
+ is_win_11_or_greater = (os_info.dwMajorVersion >= 10 && os_info.dwBuildNumber >= 22000);
+ }
+
+ return is_win_11_or_greater;
+}
+}
+#endif
+
+#ifdef Q_OS_LINUX
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#include
+#include
+#else
+#include
+#endif
+#include
+#include
+#include
+
+#define SHADOW_WINDOW_COUNT 8
+
+namespace QGoodWindowUtils
+{
+QList m_gw_list;
+
+GtkSettings *m_settings = nullptr;
+
+void themeChangeNotification()
+{
+ for (QGoodWindow *gw : m_gw_list)
+ {
+ if (gw->m_theme_change_timer)
+ gw->m_theme_change_timer->start();
+ }
+}
+
+void registerThemeChangeNotification()
+{
+ if (!m_settings)
+ {
+ m_settings = gtk_settings_get_default();
+ g_signal_connect(m_settings, "notify::gtk-theme-name", themeChangeNotification, nullptr);
+ }
+}
+}
+#endif
+
+#ifdef Q_OS_MAC
+
+#include "macosnative.h"
+
+Notification notification;
+
+namespace QGoodWindowUtils
+{
+bool m_theme_change_registered = false;
+}
+#endif
+
+QGoodWindow::QGoodWindow(QWidget *parent, const QColor &clear_color) : QMainWindow(parent)
+{
+#ifdef QGOODWINDOW
+ setParent(nullptr);
+
+ m_dark = isSystemThemeDark();
+
+ m_caption_buttons_handled = false;
+
+ m_is_caption_button_pressed = false;
+ m_last_caption_button_hovered = -1;
+ m_caption_button_pressed = -1;
+
+ m_theme_change_timer = new QTimer(this);
+ connect(m_theme_change_timer, &QTimer::timeout, this, [=]{
+ bool dark = isSystemThemeDark();
+
+ if (m_dark != dark)
+ {
+ m_dark = dark;
+ emit systemThemeChanged();
+ }
+ });
+ m_theme_change_timer->setSingleShot(true);
+ m_theme_change_timer->setInterval(0);
+
+ m_hover_timer = new QTimer(this);
+ m_hover_timer->setSingleShot(true);
+ m_hover_timer->setInterval(300);
+#endif
+#ifdef Q_OS_WIN
+ m_clear_color = clear_color;
+#else
+ Q_UNUSED(clear_color)
+#endif
+#if defined Q_OS_WIN || defined Q_OS_LINUX
+ m_fixed_size = false;
+#endif
+#if defined Q_OS_LINUX || defined Q_OS_MAC
+ m_last_move_button = -1;
+#endif
+#ifdef Q_OS_MAC
+ m_mouse_button_pressed = false;
+ m_on_animate_event = false;
+#endif
+#ifdef Q_OS_WIN
+ m_closed = false;
+
+ m_is_full_screen = false;
+
+ m_active_state = false;
+
+ m_state = Qt::WindowNoState;
+
+ m_native_event = nullptr;
+
+ HINSTANCE hInstance = GetModuleHandleW(nullptr);
+
+ WNDCLASSEXW wcx;
+ memset(&wcx, 0, sizeof(WNDCLASSEXW));
+ wcx.cbSize = sizeof(WNDCLASSEXW);
+ wcx.style = CS_HREDRAW | CS_VREDRAW;
+ wcx.hInstance = hInstance;
+ wcx.lpfnWndProc = WNDPROC(WndProc);
+ wcx.cbClsExtra = 0;
+ wcx.cbWndExtra = 0;
+ wcx.hCursor = LoadCursorW(nullptr, IDC_ARROW);
+ wcx.hbrBackground = HBRUSH(CreateSolidBrush(RGB(m_clear_color.red(),
+ m_clear_color.green(),
+ m_clear_color.blue())));
+ wcx.lpszClassName = L"QGoodWindowClass";
+
+ RegisterClassExW(&wcx);
+
+ m_hwnd = CreateWindowW(wcx.lpszClassName, nullptr,
+ WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);
+
+ if (!m_hwnd)
+ {
+ QMessageBox::critical(nullptr, "Error", "Error creating window");
+ return;
+ }
+
+ SetWindowLongPtrW(m_hwnd, GWLP_USERDATA, reinterpret_cast(this));
+
+ connect(qApp, &QApplication::aboutToQuit, this, [=]{
+ //Fix QApplication::exit() crash.
+ SetWindowLongPtrW(m_hwnd, GWLP_USERDATA, reinterpret_cast(nullptr));
+ });
+
+ m_win_use_native_borders = QGoodWindowUtils::isWin11OrGreater();
+
+ m_window_handle = QWindow::fromWinId(WId(m_hwnd));
+
+ initGW();
+
+ m_helper_widget = new QWidget();
+
+ QScreen *screen = windowHandle()->screen();
+ //qreal pixel_ratio = screen->logicalDotsPerInch() / qreal(96);
+ qreal pixel_ratio = 1;
+ if (!m_win_use_native_borders)
+ m_shadow = new Shadow(pixel_ratio, m_hwnd);
+
+ m_main_window = static_cast(this);
+ m_main_window->installEventFilter(this);
+
+ if (!m_native_event)
+ {
+ m_native_event = new QGoodWindowUtils::NativeEventFilter(this);
+ qApp->installNativeEventFilter(m_native_event);
+ }
+
+ setMargins(30, 0, 0, 0);
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ if (!QtWin::isCompositionEnabled())
+ {
+ disableCaption();
+ frameChanged();
+ }
+#endif
+
+#endif
+#ifdef Q_OS_LINUX
+ m_resize_move = false;
+ m_resize_move_started = false;
+
+ setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
+
+ for (int i = 0; i < SHADOW_WINDOW_COUNT; i++)
+ {
+ Shadow *shadow = new Shadow(this);
+ m_shadow_list.append(shadow);
+ }
+
+ installEventFilter(this);
+ setMouseTracking(true);
+
+ createWinId();
+
+ QGoodWindowUtils::registerThemeChangeNotification();
+
+ QGoodWindowUtils::m_gw_list.append(this);
+
+ QScreen *screen = windowHandle()->screen();
+ m_pixel_ratio = screen->logicalDotsPerInch() / qreal(96);
+
+ setMargins(30, 0, 0, 0);
+#endif
+#ifdef Q_OS_MAC
+ installEventFilter(this);
+ setMouseTracking(true);
+
+ createWinId();
+
+ notification.addWindow(this);
+
+ notification.registerNotification("NSWindowWillEnterFullScreenNotification", winId());
+ notification.registerNotification("NSWindowDidExitFullScreenNotification", winId());
+
+ if (!QGoodWindowUtils::m_theme_change_registered)
+ {
+ QGoodWindowUtils::m_theme_change_registered = true;
+ macOSNative::registerThemeChangeNotification();
+ }
+
+ setMargins(30, 0, 0, 0);
+#endif
+}
+
+QGoodWindow::~QGoodWindow()
+{
+#ifdef Q_OS_WIN
+ if (m_native_event)
+ {
+ qApp->removeNativeEventFilter(m_native_event);
+ delete m_native_event;
+ m_native_event = nullptr;
+ }
+#endif
+#ifdef Q_OS_LINUX
+ QGoodWindowUtils::m_gw_list.removeAll(this);
+#endif
+#ifdef Q_OS_MAC
+ notification.removeWindow(this);
+#endif
+#if defined Q_OS_LINUX || defined Q_OS_MAC
+ removeEventFilter(this);
+#endif
+}
+
+WId QGoodWindow::winId() const
+{
+#ifdef Q_OS_WIN
+ return WId(m_hwnd);
+#else
+ return QMainWindow::winId();
+#endif
+}
+
+/*** QGOODWINDOW FUNCTIONS BEGIN ***/
+
+bool QGoodWindow::isSystemThemeDark()
+{
+ bool dark = false;
+#ifdef Q_OS_WIN
+ typedef LONG(WINAPI *tRegGetValueW)(HKEY,LPCWSTR,LPCWSTR,DWORD,LPDWORD,PVOID,LPDWORD);
+ tRegGetValueW pRegGetValueW = tRegGetValueW(QLibrary::resolve("advapi32", "RegGetValueW"));
+
+ if (pRegGetValueW)
+ {
+ DWORD value;
+ DWORD size = sizeof(value);
+ if (pRegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
+ L"AppsUseLightTheme", RRF_RT_DWORD, nullptr, &value, &size) == ERROR_SUCCESS)
+ dark = (value == 0);
+ }
+#endif
+#ifdef Q_OS_LINUX
+ GtkSettings *settings = gtk_settings_get_default();
+ gchar *theme_name;
+ g_object_get(settings, "gtk-theme-name", &theme_name, nullptr);
+ dark = QString(theme_name).endsWith("Dark", Qt::CaseInsensitive);
+#endif
+#ifdef Q_OS_MAC
+ dark = QString(macOSNative::themeName()).endsWith("Dark", Qt::CaseInsensitive);
+#endif
+ return dark;
+}
+
+bool QGoodWindow::shouldBordersBeDrawnBySystem()
+{
+ bool drawn_by_system = true;
+#ifdef Q_OS_WIN
+ drawn_by_system = QGoodWindowUtils::isWin11OrGreater();
+#endif
+#ifdef Q_OS_LINUX
+ drawn_by_system = false;
+#endif
+ return drawn_by_system;
+}
+
+int QGoodWindow::titleBarHeight() const
+{
+#ifdef QGOODWINDOW
+ return m_title_bar_height;
+#else
+ return 0;
+#endif
+}
+
+int QGoodWindow::iconWidth() const
+{
+#ifdef QGOODWINDOW
+ return m_icon_width;
+#else
+ return 0;
+#endif
+}
+
+int QGoodWindow::leftMargin() const
+{
+#ifdef QGOODWINDOW
+ return m_left_margin;
+#else
+ return 0;
+#endif
+}
+
+int QGoodWindow::rightMargin() const
+{
+#ifdef QGOODWINDOW
+ return m_right_margin;
+#else
+ return 0;
+#endif
+}
+
+void QGoodWindow::setMargins(int title_bar_height, int icon_width, int left, int right)
+{
+#ifdef QGOODWINDOW
+ if (title_bar_height < 0)
+ title_bar_height = 0;
+
+ m_title_bar_height = title_bar_height;
+
+ if (icon_width < 0)
+ icon_width = 0;
+
+ m_icon_width = icon_width;
+
+ if (left < 0)
+ left = 0;
+
+ m_left_margin = left;
+
+ if (right < 0)
+ right = 0;
+
+ m_right_margin = right;
+
+ m_left_mask = QRegion(0, 0, m_left_margin, m_title_bar_height);
+ m_right_mask = QRegion(0, 0, m_right_margin, m_title_bar_height);
+
+ m_caption_buttons_handled = false;
+
+ m_min_mask = QRegion();
+ m_max_mask = QRegion();
+ m_cls_mask = QRegion();
+
+#else
+ Q_UNUSED(title_bar_height)
+ Q_UNUSED(icon_width)
+ Q_UNUSED(left)
+ Q_UNUSED(right)
+#endif
+}
+
+void QGoodWindow::setLeftMask(const QRegion &mask)
+{
+#ifdef QGOODWINDOW
+ m_left_mask = mask;
+#else
+ Q_UNUSED(mask)
+#endif
+}
+
+void QGoodWindow::setRightMask(const QRegion &mask)
+{
+#ifdef QGOODWINDOW
+ m_right_mask = mask;
+#else
+ Q_UNUSED(mask)
+#endif
+}
+
+void QGoodWindow::setCaptionButtonsHandled(bool handled, const Qt::Corner &corner)
+{
+#ifdef QGOODWINDOW
+ if (handled)
+ {
+ switch (corner)
+ {
+ case Qt::TopLeftCorner:
+ case Qt::TopRightCorner:
+ {
+ m_caption_buttons_corner = corner;
+ break;
+ }
+ default:
+ {
+ m_caption_buttons_corner = Qt::TopRightCorner;
+ break;
+ }
+ }
+
+ m_caption_buttons_handled = true;
+ }
+ else
+ {
+ m_caption_buttons_handled = false;
+
+ m_min_mask = QRegion();
+ m_max_mask = QRegion();
+ m_cls_mask = QRegion();
+ }
+#else
+ Q_UNUSED(handled)
+ Q_UNUSED(corner)
+#endif
+}
+
+void QGoodWindow::setMinimizeMask(const QRegion &mask)
+{
+#ifdef QGOODWINDOW
+ if (m_caption_buttons_handled)
+ m_min_mask = mask;
+#else
+ Q_UNUSED(mask)
+#endif
+}
+
+void QGoodWindow::setMaximizeMask(const QRegion &mask)
+{
+#ifdef QGOODWINDOW
+ if (m_caption_buttons_handled)
+ m_max_mask = mask;
+#else
+ Q_UNUSED(mask)
+#endif
+}
+
+void QGoodWindow::setCloseMask(const QRegion &mask)
+{
+#ifdef QGOODWINDOW
+ if (m_caption_buttons_handled)
+ m_cls_mask = mask;
+#else
+ Q_UNUSED(mask)
+#endif
+}
+
+QSize QGoodWindow::leftMaskSize() const
+{
+#ifdef QGOODWINDOW
+ return QSize(m_left_margin, m_title_bar_height);
+#else
+ return QSize();
+#endif
+}
+
+QSize QGoodWindow::rightMaskSize() const
+{
+#ifdef QGOODWINDOW
+ return QSize(m_right_margin, m_title_bar_height);
+#else
+ return QSize();
+#endif
+}
+
+QRect QGoodWindow::leftCaptionButtonsRect() const
+{
+#ifdef QGOODWINDOW
+ return QRect(0, 0, m_left_margin, m_title_bar_height);
+#else
+ return QRect();
+#endif
+}
+
+QRect QGoodWindow::rightCaptionButtonsRect() const
+{
+#ifdef QGOODWINDOW
+ return QRect(0, 0, m_right_margin, m_title_bar_height);
+#else
+ return QRect();
+#endif
+}
+
+/*** QGOODWINDOW FUNCTIONS END ***/
+
+void QGoodWindow::setFixedSize(int w, int h)
+{
+#if defined Q_OS_WIN || defined Q_OS_LINUX
+ m_fixed_size = true;
+#endif
+#ifdef Q_OS_WIN
+ SetWindowLongW(m_hwnd, GWL_STYLE, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX);
+ resize(w, h);
+#else
+ QMainWindow::setFixedSize(w, h);
+#endif
+}
+
+void QGoodWindow::setFixedSize(const QSize &size)
+{
+#if defined Q_OS_WIN || defined Q_OS_LINUX
+ setFixedSize(size.width(), size.height());
+#else
+ QMainWindow::setFixedSize(size);
+#endif
+}
+
+QRect QGoodWindow::frameGeometry() const
+{
+#ifdef Q_OS_WIN
+ RECT window_rect;
+ GetWindowRect(m_hwnd, &window_rect);
+
+ QRect rect = QRect(window_rect.left/devicePixelRatioF(), window_rect.top/devicePixelRatioF(),
+ (window_rect.right - window_rect.left)/devicePixelRatioF(),
+ (window_rect.bottom - window_rect.top)/devicePixelRatioF());
+
+ if (isMaximized())
+ {
+ const int border_width = BORDERWIDTH;
+ rect.adjust(border_width, border_width, -border_width, -border_width);
+ }
+
+ return rect;
+#else
+ return QMainWindow::frameGeometry();
+#endif
+}
+
+QRect QGoodWindow::geometry() const
+{
+#ifdef Q_OS_WIN
+ RECT client_rect;
+ GetClientRect(m_hwnd, &client_rect);
+
+ QPoint window_pos = pos();
+
+ QRect rect = QRect((window_pos.x() + client_rect.left)/devicePixelRatioF(),
+ (window_pos.y() + client_rect.top)/devicePixelRatioF(),
+ (client_rect.right - client_rect.left)/devicePixelRatioF(),
+ (client_rect.bottom - client_rect.top)/devicePixelRatioF());
+
+ return rect;
+#else
+ return QMainWindow::geometry();
+#endif
+}
+
+QRect QGoodWindow::rect() const
+{
+#ifdef Q_OS_WIN
+ return QRect(0, 0, width(), height());
+#else
+ return QMainWindow::rect();
+#endif
+}
+
+QPoint QGoodWindow::pos() const
+{
+#ifdef Q_OS_WIN
+ return frameGeometry().topLeft();
+#else
+ return QMainWindow::pos();
+#endif
+}
+
+QSize QGoodWindow::size() const
+{
+#ifdef Q_OS_WIN
+ return QSize(width(), height());
+#else
+ return QMainWindow::size();
+#endif
+}
+
+int QGoodWindow::x() const
+{
+#ifdef Q_OS_WIN
+ return pos().x();
+#else
+ return QMainWindow::x();
+#endif
+}
+
+int QGoodWindow::y() const
+{
+#ifdef Q_OS_WIN
+ return pos().y();
+#else
+ return QMainWindow::y();
+#endif
+}
+
+int QGoodWindow::width() const
+{
+#ifdef Q_OS_WIN
+ return geometry().width();
+#else
+ return QMainWindow::width();
+#endif
+}
+
+int QGoodWindow::height() const
+{
+#ifdef Q_OS_WIN
+ return geometry().height();
+#else
+ return QMainWindow::height();
+#endif
+}
+
+void QGoodWindow::move(int x, int y)
+{
+#ifdef Q_OS_WIN
+ SetWindowPos(m_hwnd, nullptr, x*devicePixelRatioF(), y*devicePixelRatioF(), 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
+#else
+ QMainWindow::move(x, y);
+#endif
+}
+
+void QGoodWindow::move(const QPoint &pos)
+{
+#ifdef Q_OS_WIN
+ move(pos.x(), pos.y());
+#else
+ QMainWindow::move(pos);
+#endif
+}
+
+void QGoodWindow::resize(int width, int height)
+{
+#ifdef Q_OS_WIN
+ if (m_win_use_native_borders)
+ {
+ const int border_width = BORDERWIDTH;
+ width += border_width * 2;
+ height += border_width;
+ }
+
+ SetWindowPos(m_hwnd, nullptr, 0, 0, width*devicePixelRatioF(), height*devicePixelRatioF(), SWP_NOMOVE | SWP_NOACTIVATE);
+#else
+ QMainWindow::resize(width, height);
+#endif
+}
+
+void QGoodWindow::resize(const QSize &size)
+{
+#ifdef Q_OS_WIN
+ resize(size.width(), size.height());
+#else
+ QMainWindow::resize(size);
+#endif
+}
+
+void QGoodWindow::setGeometry(int x, int y, int w, int h)
+{
+#ifdef Q_OS_WIN
+ move(x, y);
+ resize(w, h);
+#else
+ QMainWindow::setGeometry(x, y, w, h);
+#endif
+}
+
+void QGoodWindow::setGeometry(const QRect &rect)
+{
+#ifdef Q_OS_WIN
+ setGeometry(rect.x(), rect.y(), rect.width(), rect.height());
+#else
+ QMainWindow::setGeometry(rect);
+#endif
+}
+
+void QGoodWindow::activateWindow()
+{
+#ifdef Q_OS_WIN
+ SetForegroundWindow(m_hwnd);
+#else
+ QMainWindow::activateWindow();
+#endif
+}
+
+void QGoodWindow::show()
+{
+#ifdef Q_OS_WIN
+ if (m_is_full_screen)
+ showNormal();
+ ShowWindow(m_hwnd, SW_SHOW);
+#else
+ QMainWindow::show();
+#endif
+}
+
+void QGoodWindow::showNormal()
+{
+#ifdef Q_OS_WIN
+ ShowWindow(m_hwnd, SW_SHOWNORMAL);
+
+ if (m_is_full_screen)
+ {
+ m_is_full_screen = false;
+ SetWindowLongW(m_hwnd, GWL_STYLE, GetWindowLongW(m_hwnd, GWL_STYLE) | WS_OVERLAPPEDWINDOW);
+ setGeometry(m_rect_origin);
+ m_rect_origin = QRect();
+ sizeMoveWindow();
+ }
+#else
+ QMainWindow::showNormal();
+#endif
+}
+
+void QGoodWindow::showMaximized()
+{
+#if defined Q_OS_WIN || defined Q_OS_LINUX
+ if (m_fixed_size)
+ return;
+#endif
+#ifdef Q_OS_WIN
+ if (m_is_full_screen)
+ showNormal();
+
+ ShowWindow(m_hwnd, SW_SHOWMAXIMIZED);
+#else
+ QMainWindow::showMaximized();
+#endif
+}
+
+void QGoodWindow::showMinimized()
+{
+#ifdef Q_OS_WIN
+ if (m_is_full_screen)
+ showNormal();
+
+ ShowWindow(m_hwnd, SW_SHOWMINIMIZED);
+#else
+ QMainWindow::showMinimized();
+#endif
+}
+
+void QGoodWindow::showFullScreen()
+{
+#if defined Q_OS_WIN || defined Q_OS_LINUX
+ if (m_fixed_size)
+ return;
+#endif
+#ifdef Q_OS_WIN
+ if (!m_is_full_screen)
+ {
+ ShowWindow(m_hwnd, SW_SHOWNORMAL);
+
+ m_rect_origin = geometry();
+
+ SetWindowLongW(m_hwnd, GWL_STYLE, GetWindowLongW(m_hwnd, GWL_STYLE) & ~WS_OVERLAPPEDWINDOW);
+
+ QTimer::singleShot(0, this, [=]{
+ m_is_full_screen = true;
+ sizeMoveWindow();
+ });
+ }
+#else
+ QMainWindow::showFullScreen();
+#endif
+}
+
+void QGoodWindow::hide()
+{
+#ifdef Q_OS_WIN
+ if (m_is_full_screen)
+ showNormal();
+
+ ShowWindow(m_hwnd, SW_HIDE);
+#else
+ QMainWindow::hide();
+#endif
+}
+
+void QGoodWindow::close()
+{
+#ifdef Q_OS_WIN
+ PostMessageW(m_hwnd, WM_CLOSE, 0, 0);
+#else
+ QMainWindow::close();
+#endif
+}
+
+bool QGoodWindow::isVisible() const
+{
+#ifdef Q_OS_WIN
+ return IsWindowVisible(m_hwnd);
+#else
+ return QMainWindow::isVisible();
+#endif
+}
+
+bool QGoodWindow::isEnabled() const
+{
+#ifdef Q_OS_WIN
+ return IsWindowEnabled(m_hwnd);
+#else
+ return QMainWindow::isEnabled();
+#endif
+}
+
+bool QGoodWindow::isActiveWindow() const
+{
+#ifdef Q_OS_WIN
+ return (GetForegroundWindow() == m_hwnd);
+#else
+ return QMainWindow::isActiveWindow();
+#endif
+}
+
+bool QGoodWindow::isMaximized() const
+{
+#ifdef Q_OS_WIN
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(m_hwnd, &wp);
+ return (wp.showCmd == SW_SHOWMAXIMIZED);
+#else
+ return QMainWindow::isMaximized();
+#endif
+}
+
+bool QGoodWindow::isMinimized() const
+{
+#ifdef Q_OS_WIN
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(m_hwnd, &wp);
+ return (wp.showCmd == SW_SHOWMINIMIZED);
+#else
+ return QMainWindow::isMinimized();
+#endif
+}
+
+bool QGoodWindow::isFullScreen() const
+{
+#ifdef Q_OS_WIN
+ return m_is_full_screen;
+#else
+ return QMainWindow::isFullScreen();
+#endif
+}
+
+Qt::WindowStates QGoodWindow::windowState() const
+{
+#ifdef Q_OS_WIN
+ return m_state;
+#else
+ return QMainWindow::windowState();
+#endif
+}
+
+void QGoodWindow::setWindowState(Qt::WindowStates state)
+{
+#ifdef Q_OS_WIN
+ if (state.testFlag(Qt::WindowFullScreen))
+ showFullScreen();
+ else if (state.testFlag(Qt::WindowMaximized))
+ showMaximized();
+ else if (state.testFlag(Qt::WindowMinimized))
+ showMinimized();
+ else if (state.testFlag(Qt::WindowNoState))
+ showNormal();
+
+ if (state.testFlag(Qt::WindowActive))
+ activateWindow();
+#else
+ QMainWindow::setWindowState(state);
+#endif
+}
+
+QWindow *QGoodWindow::windowHandle() const
+{
+#ifdef Q_OS_WIN
+ return m_window_handle;
+#else
+ return QMainWindow::windowHandle();
+#endif
+}
+
+qreal QGoodWindow::windowOpacity() const
+{
+ return windowHandle()->opacity();
+}
+
+void QGoodWindow::setWindowOpacity(qreal level)
+{
+ windowHandle()->setOpacity(level);
+}
+
+QString QGoodWindow::windowTitle() const
+{
+ return QMainWindow::windowTitle();
+}
+
+void QGoodWindow::setWindowTitle(const QString &title)
+{
+#ifdef Q_OS_WIN
+ SetWindowTextW(m_hwnd, reinterpret_cast(title.utf16()));
+
+ QMainWindow::setWindowTitle(title);
+#else
+ QMainWindow::setWindowTitle(title);
+#endif
+}
+
+QIcon QGoodWindow::windowIcon() const
+{
+ return QMainWindow::windowIcon();
+}
+
+void QGoodWindow::setWindowIcon(const QIcon &icon)
+{
+#ifdef Q_OS_WIN
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ HICON hicon_big = QtWin::toHICON(icon.pixmap(GetSystemMetrics(SM_CXICON),
+ GetSystemMetrics(SM_CYICON)));
+ HICON hicon_small = QtWin::toHICON(icon.pixmap(GetSystemMetrics(SM_CXSMICON),
+ GetSystemMetrics(SM_CYSMICON)));
+#else
+ HICON hicon_big = icon.pixmap(GetSystemMetrics(SM_CXICON),
+ GetSystemMetrics(SM_CYICON)).toImage().toHICON();
+ HICON hicon_small = icon.pixmap(GetSystemMetrics(SM_CXSMICON),
+ GetSystemMetrics(SM_CYSMICON)).toImage().toHICON();
+#endif
+
+ SendMessageW(m_hwnd, WM_SETICON, ICON_BIG, LPARAM(hicon_big));
+ SendMessageW(m_hwnd, WM_SETICON, ICON_SMALL, LPARAM(hicon_small));
+
+ QMainWindow::setWindowIcon(icon);
+#else
+ QMainWindow::setWindowIcon(icon);
+#endif
+}
+
+bool QGoodWindow::event(QEvent *event)
+{
+#if defined Q_OS_LINUX || defined Q_OS_MAC
+ switch (event->type())
+ {
+ case QEvent::Leave:
+ {
+ buttonLeave(m_last_caption_button_hovered);
+ m_last_move_button = -1;
+
+ break;
+ }
+ case QEvent::Hide:
+ case QEvent::HoverLeave:
+ {
+ if (QApplication::overrideCursor())
+ QApplication::restoreOverrideCursor();
+
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+#ifdef Q_OS_WIN
+ switch (event->type())
+ {
+ case QEvent::ChildPolished:
+ {
+ QChildEvent *child_event = static_cast(event);
+
+ QWidget *widget = qobject_cast(child_event->child());
+
+ if (!widget->isWindow())
+ break;
+
+ if (!widget->isModal())
+ break;
+
+ widget->adjustSize();
+
+ widget->setParent(bestParentForModalWindow(), widget->windowFlags());
+
+ break;
+ }
+ case QEvent::WindowBlocked:
+ {
+ EnableWindow(m_hwnd, FALSE);
+ break;
+ }
+ case QEvent::WindowUnblocked:
+ {
+ EnableWindow(m_hwnd, TRUE);
+ break;
+ }
+ case QEvent::WindowStateChange:
+ {
+ bool window_no_state = windowState().testFlag(Qt::WindowNoState);
+
+ for (QSizeGrip *size_grip : findChildren())
+ {
+ if (!size_grip->window()->windowFlags().testFlag(Qt::SubWindow))
+ size_grip->setVisible(window_no_state);
+ }
+
+ break;
+ }
+ case QEvent::Resize:
+ {
+ if (isVisible() && size() != m_main_window->size())
+ resize(m_main_window->size());
+
+ break;
+ }
+ case QEvent::Close:
+ {
+ if (m_closed)
+ {
+ hide();
+ return true;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+#ifdef Q_OS_LINUX
+ switch (event->type())
+ {
+ case QEvent::WindowActivate:
+ {
+ QTimer::singleShot(0, this, &QGoodWindow::sizeMoveBorders);
+
+ break;
+ }
+ case QEvent::Hide:
+ case QEvent::WindowDeactivate:
+ {
+ for (Shadow *shadow : m_shadow_list)
+ shadow->hide();
+
+ break;
+ }
+ case QEvent::WindowStateChange:
+ {
+ if (!windowState().testFlag(Qt::WindowNoState))
+ {
+ for (Shadow *shadow : m_shadow_list)
+ shadow->hide();
+ }
+
+ break;
+ }
+ case QEvent::Show:
+ case QEvent::Resize:
+ {
+ setMask(rect());
+
+ QTimer::singleShot(0, this, &QGoodWindow::sizeMoveBorders);
+
+ break;
+ }
+ case QEvent::Move:
+ {
+ QTimer::singleShot(0, this, &QGoodWindow::sizeMoveBorders);
+
+ break;
+ }
+ case QEvent::WindowBlocked:
+ {
+ setEnabled(false);
+
+ m_last_fixed_size_value = m_fixed_size;
+
+ setFixedSize(size());
+
+ if (QWidget *w = qApp->activeModalWidget())
+ w->setEnabled(true);
+
+ break;
+ }
+ case QEvent::WindowUnblocked:
+ {
+ setEnabled(true);
+
+ if (!m_last_fixed_size_value)
+ {
+ setMinimumSize(0, 0);
+ setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+
+ m_fixed_size = false;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+#ifdef Q_OS_MAC
+ switch (event->type())
+ {
+ case QEvent::Show:
+ {
+ if (isFullScreen())
+ break;
+
+ macOSNative::setStyle(long(winId()), false);
+
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+ return QMainWindow::event(event);
+}
+
+bool QGoodWindow::eventFilter(QObject *watched, QEvent *event)
+{
+#ifdef Q_OS_WIN
+ if (watched == m_main_window)
+ {
+ switch (event->type())
+ {
+ case QEvent::WindowActivate:
+ {
+ if (!m_main_window->isActiveWindow() || m_active_state)
+ return true;
+
+ m_active_state = true;
+
+ break;
+ }
+ case QEvent::WindowDeactivate:
+ {
+ if (m_main_window->isActiveWindow() || !m_active_state)
+ return true;
+
+ m_active_state = false;
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+#endif
+#if defined Q_OS_LINUX || defined Q_OS_MAC
+ if (!isEnabled())
+ return QMainWindow::eventFilter(watched, event);
+
+ QPoint cursor_pos = QCursor::pos();
+ long button = ncHitTest(cursor_pos.x(), cursor_pos.y());
+
+ switch (event->type())
+ {
+#ifdef Q_OS_MAC
+ case QEvent::ChildAdded:
+ case QEvent::ChildRemoved:
+ {
+ if (qApp->activeModalWidget())
+ break;
+
+ if (isFullScreen())
+ break;
+
+ if (m_on_animate_event)
+ break;
+
+ macOSNative::setStyle(long(winId()), false);
+
+ break;
+ }
+#endif
+ case QEvent::ChildPolished:
+ {
+ QChildEvent *child_event = static_cast(event);
+
+ QWidget *widget = qobject_cast(child_event->child());
+
+ if (!widget)
+ break;
+
+ widget->setMouseTracking(true);
+ widget->installEventFilter(this);
+
+ for (QWidget *w : widget->findChildren())
+ {
+ w->setMouseTracking(true);
+ w->installEventFilter(this);
+ }
+
+ break;
+ }
+ case QEvent::MouseButtonPress:
+ {
+ QMouseEvent *mouse_event = static_cast(event);
+
+ if (mouse_event->button() != Qt::LeftButton)
+ break;
+
+ switch (button)
+ {
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ {
+ buttonPress(button);
+ break;
+ }
+ default:
+ break;
+ }
+
+ break;
+ }
+ case QEvent::MouseMove:
+ {
+ if (m_last_move_button != button)
+ {
+ m_last_move_button = button;
+
+ buttonLeave(m_last_caption_button_hovered);
+
+ switch (button)
+ {
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ {
+ if (!m_is_caption_button_pressed || (button == m_caption_button_pressed))
+ buttonEnter(button);
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ break;
+ }
+ case QEvent::MouseButtonRelease:
+ {
+ QMouseEvent *mouse_event = static_cast(event);
+
+ if (mouse_event->button() != Qt::LeftButton)
+ break;
+
+ m_last_move_button = -1;
+
+ buttonRelease(m_caption_button_pressed, (button == m_caption_button_pressed));
+
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+#ifdef Q_OS_LINUX
+ if (m_is_caption_button_pressed)
+ return QMainWindow::eventFilter(watched, event);
+
+ switch (event->type())
+ {
+ case QEvent::MouseButtonDblClick:
+ {
+ QMouseEvent *mouse_event = static_cast(event);
+
+ if (mouse_event->button() == Qt::LeftButton)
+ {
+ if (m_margin == TITLE_BAR)
+ {
+ if (!isMaximized())
+ showMaximized();
+ else
+ showNormal();
+ }
+ }
+
+ switch (m_margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ return true;
+ default:
+ break;
+ }
+
+ break;
+ }
+ case QEvent::MouseButtonPress:
+ {
+ setCursorForCurrentPos();
+
+ QMouseEvent *mouse_event = static_cast(event);
+
+ if (!m_resize_move && mouse_event->button() == Qt::LeftButton)
+ {
+ if (m_margin != NO_WHERE)
+ m_resize_move = true;
+ }
+
+ switch (m_margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ return true;
+ default:
+ break;
+ }
+
+ break;
+ }
+ case QEvent::MouseMove:
+ {
+ setCursorForCurrentPos();
+
+ QMouseEvent *mouse_event = static_cast(event);
+
+ if (m_resize_move && mouse_event->buttons() == Qt::LeftButton)
+ sizeMove();
+
+ switch (m_margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ return true;
+ default:
+ break;
+ }
+
+ break;
+ }
+ case QEvent::MouseButtonRelease:
+ {
+ setCursorForCurrentPos();
+
+ QMouseEvent *mouse_event = static_cast(event);
+
+ if (m_resize_move && mouse_event->button() == Qt::LeftButton)
+ {
+ if (m_margin != NO_WHERE)
+ m_resize_move = false;
+ }
+
+ switch (m_margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ return true;
+ default:
+ break;
+ }
+
+ break;
+ }
+ case QEvent::Wheel:
+ {
+ switch (m_margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ return true;
+ default:
+ break;
+ }
+
+ break;
+ }
+ case QEvent::ContextMenu:
+ {
+ switch (m_margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ return true;
+ default:
+ break;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+#ifdef Q_OS_MAC
+ if (m_is_caption_button_pressed)
+ return QMainWindow::eventFilter(watched, event);
+
+ int margin = ncHitTest(cursor_pos.x(), cursor_pos.y());
+
+ switch (event->type())
+ {
+ case QEvent::MouseButtonDblClick:
+ {
+ if (margin == TITLE_BAR)
+ {
+ QMouseEvent *mouse_event = static_cast(event);
+
+ if (!isFullScreen())
+ {
+ if (mouse_event->button() == Qt::LeftButton)
+ {
+ if (margin == TITLE_BAR)
+ {
+ if (!isMaximized())
+ showMaximized();
+ else
+ showNormal();
+ }
+ }
+ }
+ }
+
+ switch (margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ return true;
+ default:
+ break;
+ }
+
+ break;
+ }
+ case QEvent::MouseButtonPress:
+ {
+ if (margin == TITLE_BAR)
+ {
+ QMouseEvent *mouse_event = static_cast(event);
+
+ if (!isFullScreen())
+ {
+ if (mouse_event->button() == Qt::LeftButton)
+ {
+ m_pos = cursor_pos - pos();
+ }
+ }
+ }
+
+ m_mouse_button_pressed = true;
+
+ switch (margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ return true;
+ default:
+ break;
+ }
+
+ break;
+ }
+ case QEvent::MouseMove:
+ {
+ if (!m_pos.isNull())
+ {
+ QMouseEvent *mouse_event = static_cast(event);
+
+ if (!isFullScreen())
+ {
+ if (mouse_event->buttons() == Qt::LeftButton)
+ {
+ move(cursor_pos - m_pos);
+ }
+ }
+ }
+
+ if (!m_mouse_button_pressed)
+ {
+ QWidget *widget = QApplication::widgetAt(QCursor::pos());
+
+ if (widget)
+ {
+ switch (margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ {
+ if (QApplication::overrideCursor() &&
+ QApplication::overrideCursor()->shape() == Qt::ArrowCursor)
+ break;
+
+ QApplication::setOverrideCursor(Qt::ArrowCursor);
+
+ break;
+ }
+ case NO_WHERE:
+ {
+ if (!QApplication::overrideCursor())
+ break;
+
+ if (QApplication::overrideCursor()->shape() != Qt::ArrowCursor)
+ break;
+
+ QApplication::restoreOverrideCursor();
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ switch (margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ return true;
+ default:
+ break;
+ }
+
+ break;
+ }
+ case QEvent::MouseButtonRelease:
+ {
+ if (!m_pos.isNull())
+ {
+ QMouseEvent *mouse_event = static_cast(event);
+
+ if (!isFullScreen())
+ {
+ if (mouse_event->button() == Qt::LeftButton)
+ m_pos = QPoint();
+ }
+ }
+
+ m_mouse_button_pressed = false;
+
+ switch (margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ return true;
+ default:
+ break;
+ }
+
+ break;
+ }
+ case QEvent::Wheel:
+ {
+ switch (margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ return true;
+ default:
+ break;
+ }
+
+ break;
+ }
+ case QEvent::ContextMenu:
+ {
+ switch (margin)
+ {
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ return true;
+ default:
+ break;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+ return QMainWindow::eventFilter(watched, event);
+}
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+bool QGoodWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
+#else
+bool QGoodWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
+#endif
+{
+#ifdef Q_OS_LINUX
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ if (eventType == "xcb_generic_event_t")
+ {
+ xcb_generic_event_t *event = static_cast(message);
+
+ if (event->response_type == XCB_GE_GENERIC)
+ {
+ if (m_resize_move_started)
+ {
+ m_resize_move_started = false;
+
+ //Fix mouse problems after resize or move.
+ QTest::mouseClick(windowHandle(), Qt::NoButton, Qt::NoModifier);
+ }
+ else
+ {
+ //Fix no mouse event after moving mouse from resize borders.
+ QEvent event(QEvent::MouseMove);
+ QApplication::sendEvent(this, &event);
+ }
+ }
+ }
+#endif
+#endif
+ return QMainWindow::nativeEvent(eventType, message, result);
+}
+
+#ifdef Q_OS_WIN
+void QGoodWindow::initGW()
+{
+ setProperty("_q_embedded_native_parent_handle", WId(m_hwnd));
+ setWindowFlags(Qt::FramelessWindowHint);
+
+ QEvent event(QEvent::EmbeddingControl);
+ QApplication::sendEvent(this, &event);
+}
+
+void QGoodWindow::destroyGW()
+{
+ QMainWindow::close();
+ QMainWindow::destroy();
+}
+
+LRESULT QGoodWindow::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ QGoodWindow *gw = reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
+
+ if (!gw)
+ return DefWindowProcW(hwnd, message, wParam, lParam);
+
+ switch (message)
+ {
+ case WM_ACTIVATE:
+ {
+ switch (wParam)
+ {
+ case WA_ACTIVE:
+ case WA_CLICKACTIVE:
+ {
+ QTimer::singleShot(0, gw, [=]{
+ gw->setWidgetFocus();
+ gw->handleActivation();
+ });
+
+ break;
+ }
+ case WA_INACTIVE:
+ {
+ if (gw->focusWidget())
+ {
+ //If app going to be inactive,
+ //save current focused widget
+ //to restore focus later.
+
+ gw->m_focus_widget = gw->focusWidget();
+ }
+
+ QTimer::singleShot(0, gw, [=]{
+ gw->handleDeactivation();
+ });
+
+ if (!IsWindowEnabled(hwnd))
+ {
+ if (gw->m_shadow)
+ gw->m_shadow->hide();
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ break;
+ }
+ case WM_SETFOCUS:
+ {
+ //Pass focus to last focused widget.
+ gw->setWidgetFocus();
+ gw->m_main_window->activateWindow();
+
+ break;
+ }
+ case WM_SIZE:
+ {
+ gw->sizeMoveWindow();
+
+ break;
+ }
+ case WM_MOVE:
+ {
+ gw->sizeMoveWindow();
+
+ QMoveEvent event(QPoint(gw->x(), gw->y()), QPoint());
+ QApplication::sendEvent(gw, &event);
+
+ break;
+ }
+ case WM_GETMINMAXINFO:
+ {
+ MINMAXINFO *mmi = reinterpret_cast(lParam);
+
+ QSize minimum = gw->minimumSize();
+
+ QSize sizeHint = gw->minimumSizeHint();
+
+ const int border_width = BORDERWIDTH;
+
+ mmi->ptMinTrackSize.x = qMax(minimum.width(), sizeHint.width()) +
+ (gw->m_win_use_native_borders ? border_width * 2 : 0);
+ mmi->ptMinTrackSize.y = qMax(minimum.height(), sizeHint.height()) +
+ (gw->m_win_use_native_borders ? border_width : 0);
+
+ QSize maximum = gw->maximumSize();
+
+ mmi->ptMaxTrackSize.x = maximum.width() + (gw->m_win_use_native_borders ? border_width * 2 : 0);
+ mmi->ptMaxTrackSize.y = maximum.height() + (gw->m_win_use_native_borders ? border_width : 0);
+
+ break;
+ }
+ case WM_CLOSE:
+ {
+ //Send Qt QCloseEvent to the window,
+ //which allows to accept or reject the window close.
+ QCloseEvent event;
+ QApplication::sendEvent(gw, &event);
+
+ if (!event.isAccepted())
+ return 0;
+
+ gw->m_closed = true;
+
+ //Do the needed cleanup.
+ gw->destroyGW();
+
+ if (gw->m_shadow)
+ delete gw->m_shadow;
+ if (gw->m_helper_widget)
+ delete gw->m_helper_widget;
+
+ SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast(nullptr));
+
+ if (gw->testAttribute(Qt::WA_DeleteOnClose))
+ delete gw;
+
+ return DefWindowProcW(hwnd, message, wParam, lParam);
+ }
+ case WM_DESTROY:
+ {
+ return DefWindowProcW(hwnd, message, wParam, lParam);
+ }
+ case WM_NCHITTEST:
+ {
+ QPoint pos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+
+ LRESULT lresult = LRESULT(gw->ncHitTest(pos.x(), pos.y()));
+
+ if (gw->m_is_full_screen)
+ {
+ //If on full screen, the whole window can be clicked.
+
+ lresult = HTNOWHERE;
+
+ return lresult;
+ }
+
+ if (gw->m_fixed_size)
+ {
+ //If have fixed size, then only HTCAPTION hit test is valid,
+ //which means that only the title bar click and move is valid.
+
+ if (lresult != HTNOWHERE && lresult != HTCAPTION)
+ lresult = HTNOWHERE;
+
+ return lresult;
+ }
+
+ return lresult;
+ }
+ case WM_NCMOUSELEAVE:
+ {
+ if (gw->m_is_caption_button_pressed)
+ break;
+
+ gw->buttonLeave(gw->m_last_caption_button_hovered);
+
+ break;
+ }
+ case WM_NCMOUSEMOVE:
+ {
+ QPoint pos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+
+ long button = gw->ncHitTest(pos.x(), pos.y());
+
+ bool valid_caption_button = gw->winButtonHover(button);
+
+ if (valid_caption_button)
+ {
+ if (gw->m_is_caption_button_pressed)
+ {
+ if (GetCapture() != hwnd)
+ {
+ SetCapture(hwnd);
+ }
+ }
+ }
+ else
+ {
+ gw->buttonLeave(gw->m_last_caption_button_hovered);
+ }
+
+ break;
+ }
+ case WM_MOUSEMOVE:
+ {
+ QPoint pos = gw->mapToGlobal(QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
+
+ long button = gw->ncHitTest(pos.x(), pos.y());
+
+ bool valid_caption_button = gw->winButtonHover(button);
+
+ if (!valid_caption_button)
+ {
+ if (!gw->m_is_caption_button_pressed && GetCapture() == hwnd)
+ {
+ ReleaseCapture();
+ }
+
+ gw->buttonLeave(gw->m_last_caption_button_hovered);
+ }
+
+ break;
+ }
+ case WM_NCLBUTTONDOWN:
+ {
+ QPoint pos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+
+ long button = gw->ncHitTest(pos.x(), pos.y());
+
+ bool valid_caption_button = gw->buttonPress(button);
+
+ if (valid_caption_button)
+ return 0;
+
+ break;
+ }
+ case WM_NCLBUTTONUP:
+ {
+ QPoint pos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+
+ long button = gw->ncHitTest(pos.x(), pos.y());
+
+ bool valid_caption_button = gw->buttonRelease(gw->m_caption_button_pressed,
+ (button == gw->m_caption_button_pressed));
+
+ if (valid_caption_button)
+ return 0;
+
+ break;
+ }
+ case WM_LBUTTONDOWN:
+ {
+ QPoint pos = gw->mapToGlobal(QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
+
+ long button = gw->ncHitTest(pos.x(), pos.y());
+
+ gw->buttonPress(button);
+
+ break;
+ }
+ case WM_LBUTTONUP:
+ {
+ QPoint pos = gw->mapToGlobal(QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
+
+ long button = gw->ncHitTest(pos.x(), pos.y());
+
+ if (button != gw->m_caption_button_pressed)
+ gw->buttonEnter(button);
+
+ gw->buttonRelease(gw->m_caption_button_pressed, (button == gw->m_caption_button_pressed));
+
+ break;
+ }
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ {
+ if ((GetKeyState(VK_SHIFT) & 0x8000) && wParam == VK_F10)
+ {
+ //When SHIFT+F10 is pressed.
+ gw->showContextMenu();
+ return 0;
+ }
+
+ if ((GetKeyState(VK_MENU) & 0x8000) && wParam == VK_SPACE)
+ {
+ //When ALT+SPACE is pressed.
+ gw->showContextMenu();
+ return 0;
+ }
+
+ break;
+ }
+ case WM_NCRBUTTONUP:
+ {
+ //Show context menu on right click on title bar.
+
+ int x = GET_X_LPARAM(lParam);
+ int y = GET_Y_LPARAM(lParam);
+
+ LRESULT lRet = gw->ncHitTest(x, y);
+
+ if (lRet == HTCAPTION || lRet == HTSYSMENU)
+ gw->showContextMenu(x, y);
+
+ break;
+ }
+ case WM_NCCALCSIZE:
+ {
+ if (gw->isFullScreen())
+ break;
+
+ if (gw->m_win_use_native_borders && !gw->isMaximized())
+ {
+ const int border_width = BORDERWIDTH;
+
+ RECT *rect = reinterpret_cast(lParam);
+ rect->left += border_width;
+ rect->bottom -= border_width;
+ rect->right -= border_width;
+ }
+
+ if (gw->isMaximized())
+ {
+ //Compensate window client area when maximized,
+ //by removing BORDERWIDTH value for all edges.
+
+ const int border_width = BORDERWIDTH;
+
+ InflateRect(reinterpret_cast(lParam), -border_width, -border_width);
+ }
+
+ //Make the whole window as client area.
+ return 0;
+ }
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ case WM_NCPAINT:
+ case WM_NCACTIVATE:
+ {
+ //Prevent undesired painting on window when DWM is not enabled.
+
+ if (!QtWin::isCompositionEnabled())
+ return TRUE;
+
+ break;
+ }
+#endif
+ case WM_WINDOWPOSCHANGING:
+ {
+ Qt::WindowState state;
+
+ if (gw->isMinimized())
+ state = Qt::WindowMinimized;
+ else if (gw->isMaximized())
+ state = Qt::WindowMaximized;
+ else if (gw->isFullScreen())
+ state = Qt::WindowFullScreen;
+ else
+ state = Qt::WindowNoState;
+
+ if (state != gw->m_state)
+ {
+ //If window state changed.
+
+ gw->m_state = state;
+
+ QWindowStateChangeEvent event(state);
+ QApplication::sendEvent(gw, &event);
+
+ if (gw->m_shadow)
+ {
+ if (state != Qt::WindowNoState)
+ {
+ //Hide shadow if not on "normal" state.
+ gw->m_shadow->hide();
+ }
+ else if (gw->isVisible())
+ {
+ //Show shadow if switching to "normal" state, with delay.
+ gw->m_shadow->showLater();
+ }
+ }
+
+ if (state == Qt::WindowMinimized)
+ {
+ if (gw->focusWidget())
+ {
+ //If app going to be minimized,
+ //save current focused widget
+ //to restore focus later.
+
+ gw->m_focus_widget = gw->focusWidget();
+ }
+ }
+ }
+
+ WINDOWPOS *pwp = reinterpret_cast(lParam);
+
+ if (pwp->flags & SWP_SHOWWINDOW)
+ {
+ QShowEvent event;
+ QApplication::sendEvent(gw, &event);
+
+ if (gw->m_fixed_size)
+ {
+ SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) & ~WS_MAXIMIZEBOX);
+ }
+
+ {
+ //Restore brush with clear_color.
+
+ HBRUSH brush = HBRUSH(CreateSolidBrush(RGB(gw->m_clear_color.red(),
+ gw->m_clear_color.green(),
+ gw->m_clear_color.blue())));
+ HBRUSH oldbrush = HBRUSH(SetWindowLongPtrW(hwnd, GCLP_HBRBACKGROUND,
+ reinterpret_cast(brush)));
+
+ DeleteObject(oldbrush);
+ InvalidateRect(hwnd, nullptr, TRUE);
+ }
+
+ if (gw->m_shadow)
+ {
+ if (state == Qt::WindowNoState)
+ {
+ //Show shadow if on "normal" state with delay.
+ gw->m_shadow->showLater();
+ }
+ }
+
+ SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_FRAMECHANGED);
+
+ QTimer::singleShot(0, gw->m_main_window, [=]{
+ if (gw->m_main_window->isVisible())
+ return;
+
+ //Fix bug that make the window frozen
+ //when initializing window.
+ QWidget *central_widget = gw->centralWidget();
+ if (central_widget) central_widget->hide();
+ gw->m_main_window->show();
+ gw->m_main_window->resize(gw->size());
+ if (central_widget) central_widget->show();
+ });
+ }
+ else if (pwp->flags & SWP_HIDEWINDOW)
+ {
+ if (gw->focusWidget())
+ {
+ //If app have a valid focused widget,
+ //save them to restore focus later.
+
+ gw->m_focus_widget = gw->focusWidget();
+ }
+
+ {
+ //Set NULL brush.
+
+ HBRUSH brush = HBRUSH(GetStockObject(NULL_BRUSH));
+ HBRUSH oldbrush = HBRUSH(SetWindowLongPtrW(hwnd, GCLP_HBRBACKGROUND,
+ reinterpret_cast(brush)));
+
+ DeleteObject(oldbrush);
+ InvalidateRect(hwnd, nullptr, TRUE);
+ }
+
+ QHideEvent event;
+ QApplication::sendEvent(gw, &event);
+
+ if (gw->m_shadow)
+ gw->m_shadow->hide();
+ }
+ else if (pwp->flags == (SWP_NOSIZE + SWP_NOMOVE))
+ {
+ //Activate window to fix no activation
+ //problem when QGoodWindow isn't shown initially in
+ //active state.
+
+ gw->m_main_window->activateWindow();
+
+ if (gw->m_shadow)
+ gw->m_shadow->showLater();
+ }
+
+ break;
+ }
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ case WM_WINDOWPOSCHANGED:
+ {
+ if (!QtWin::isCompositionEnabled() && gw->isMaximized())
+ {
+ //Hack for prevent window goes to full screen when it's being maximized,
+ //enable WS_CAPTION and schedule for disable it,
+ //not mantaining WS_CAPTION all the time to prevent undesired painting on window
+ //when title or icon of the window is changed when DWM is not enabled.
+
+ gw->enableCaption();
+ QTimer::singleShot(0, gw, &QGoodWindow::disableCaption);
+ }
+
+ break;
+ }
+#endif
+ case WM_SETTEXT:
+ {
+ emit gw->windowTitleChanged(gw->windowTitle());
+ break;
+ }
+ case WM_SETICON:
+ {
+ emit gw->windowIconChanged(gw->windowIcon());
+ break;
+ }
+ case WM_DISPLAYCHANGE:
+ {
+ if (gw->isFullScreen())
+ gw->showNormal();
+
+ break;
+ }
+ case WM_SETTINGCHANGE:
+ {
+ if (gw->m_theme_change_timer)
+ {
+ if (QString::fromWCharArray(LPCWSTR(lParam)) == "ImmersiveColorSet")
+ {
+ gw->m_theme_change_timer->start();
+ }
+ }
+
+ break;
+ }
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ case WM_THEMECHANGED:
+ case WM_DWMCOMPOSITIONCHANGED:
+ {
+ //Send the window change event to m_helper_widget,
+ //this hack corrects the background color when switching between
+ //Windows composition modes or system themes.
+ SendMessageW(HWND(gw->m_helper_widget->winId()), message, 0, 0);
+
+ if (QtWin::isCompositionEnabled())
+ {
+ QTimer::singleShot(500, gw, &QGoodWindow::enableCaption);
+ QTimer::singleShot(750, gw, &QGoodWindow::frameChanged);
+
+ QTimer::singleShot(1000, gw, [=]{
+ SetWindowRgn(hwnd, nullptr, TRUE);
+ });
+ }
+ else
+ {
+ QTimer::singleShot(500, gw, &QGoodWindow::disableCaption);
+ QTimer::singleShot(750, gw, &QGoodWindow::frameChanged);
+ }
+
+ QTimer::singleShot(500, gw, &QGoodWindow::sizeMoveWindow);
+
+ QTimer::singleShot(500, gw, [=]{
+ gw->repaint();
+ });
+
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+
+ MSG msg;
+ msg.hwnd = hwnd;
+ msg.message = message;
+ msg.lParam = lParam;
+ msg.wParam = wParam;
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ long result = 0;
+#else
+ qintptr result = 0;
+#endif
+
+ bool return_value = gw->nativeEvent(QByteArray(), &msg, &result);
+
+ if (return_value)
+ return result;
+
+ return DefWindowProcW(hwnd, message, wParam, lParam);
+}
+
+void QGoodWindow::handleActivation()
+{
+ if (m_shadow)
+ {
+ if (m_state == Qt::WindowNoState)
+ {
+ //If in "normal" state, make shadow visible.
+ m_shadow->setActive(true);
+ m_shadow->show();
+ }
+ }
+}
+
+void QGoodWindow::handleDeactivation()
+{
+ if (m_shadow)
+ m_shadow->setActive(false);
+
+ if (!isEnabled())
+ buttonLeave(m_last_caption_button_hovered);
+}
+
+void QGoodWindow::setWidgetFocus()
+{
+ if (!m_focus_widget)
+ {
+ bool have_focusable_widget = false;
+
+ for (QWidget *next_focus : findChildren())
+ {
+ if (next_focus->focusPolicy() != Qt::NoFocus)
+ {
+ //Set focus to first focusable widget.
+ have_focusable_widget = true;
+ m_focus_widget = next_focus;
+ break;
+ }
+ }
+
+ if (!have_focusable_widget)
+ {
+ //If not have focusable widget
+ //set focus to m_main_window.
+ m_focus_widget = m_main_window;
+ }
+ }
+
+ if (m_focus_widget)
+ {
+ //If have a valid m_focus_widget,
+ //set the focus to this widget
+ m_focus_widget->setFocus();
+ }
+}
+
+void QGoodWindow::enableCaption()
+{
+ SetWindowLongW(m_hwnd, GWL_STYLE, GetWindowLongW(m_hwnd, GWL_STYLE) | WS_CAPTION);
+}
+
+void QGoodWindow::disableCaption()
+{
+ SetWindowLongW(m_hwnd, GWL_STYLE, GetWindowLongW(m_hwnd, GWL_STYLE) & ~WS_CAPTION);
+}
+
+void QGoodWindow::frameChanged()
+{
+ SetWindowPos(m_hwnd, nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_FRAMECHANGED);
+}
+
+void QGoodWindow::sizeMoveWindow()
+{
+ for (QScreen *screen : QApplication::screens())
+ {
+ if (screen->geometry().contains(frameGeometry().center()))
+ {
+ if (windowHandle()->screen() != screen)
+ windowHandle()->setScreen(screen);
+ }
+ }
+
+ if (isFullScreen())
+ {
+ QScreen *screen = windowHandle()->screen();
+ setGeometry(screen->geometry());
+ }
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ if (!QtWin::isCompositionEnabled())
+ {
+ QRect window_rect = rect();
+
+ const int border_width = BORDERWIDTH;
+
+ if (isMaximized())
+ window_rect.moveTopLeft(QPoint(border_width, border_width));
+
+ SetWindowRgn(m_hwnd, QtWin::toHRGN(window_rect), TRUE);
+ }
+#endif
+
+ m_main_window->setGeometry(rect());
+
+ if (m_shadow)
+ {
+ if (m_state == Qt::WindowNoState)
+ {
+ const int shadow_width = m_shadow->shadowWidth();
+
+ m_shadow->move(x() - shadow_width, y() - shadow_width);
+ m_shadow->resize(width() + shadow_width * 2, height() + shadow_width * 2);
+
+ m_shadow->setActive(isActiveWindow());
+ }
+ }
+}
+
+LRESULT QGoodWindow::ncHitTest(int x, int y)
+{
+ QPoint gpos(x,y);
+ QWindow * handle = windowHandle();
+ if(handle && handle->screen())
+ {
+ QScreen * screen = handle->screen();
+ QPoint offset = screen->geometry().topLeft();
+ gpos = (gpos - offset) / screen->devicePixelRatio() + offset;
+ }
+
+ x=gpos.x();
+ y=gpos.y();
+ for (QSizeGrip *size_grip : findChildren())
+ {
+ QPoint pos = QPoint(x, y);
+
+ if (size_grip->isEnabled() &&
+ !size_grip->window()->windowFlags().testFlag(Qt::SubWindow))
+ {
+ if (size_grip->rect().contains(size_grip->mapFromGlobal(pos)))
+ return HTBOTTOMRIGHT;
+ }
+ }
+
+ if (!m_win_use_native_borders)
+ {
+ const int border_width = (m_state == Qt::WindowNoState ? 1 : 0); //in pixels.
+ int title_height = titleBarHeight(); //in pixels.
+
+ //Get the point coordinates for the hit test.
+ QPoint mouse_pos = QPoint(x, y);
+
+ //Get the window rectangle.
+ QRect window_rect = frameGeometry();
+
+ //Determine if the hit test is for resizing. Default middle (1,1).
+ int row = 1;
+ int col = 1;
+ bool on_resize_border = false;
+
+ //Determine if the point is at the top or bottom of the window.
+ if (mouse_pos.y() < window_rect.top() + title_height)
+ {
+ on_resize_border = (mouse_pos.y() < (window_rect.top() + border_width));
+ row = 0;
+ }
+ else if (mouse_pos.y() >= window_rect.bottom() - border_width)
+ {
+ row = 2;
+ }
+
+ //Determine if the point is at the left or right of the window.
+ if (mouse_pos.x() < window_rect.left() + border_width)
+ {
+ col = 0; //left side
+ }
+ else if (mouse_pos.x() > window_rect.right())
+ {
+ col = 2; //right side
+ }
+ else if (row == 0 && !on_resize_border)
+ {
+ const QRegion left_mask = m_left_mask.translated(iconWidth(), 0);
+ const QRegion right_mask = m_right_mask.translated(width() - rightMargin(), 0);
+
+ QPoint mouse_pos_map = mapFromGlobal(mouse_pos);
+
+ if (mouse_pos.x() > window_rect.right() - rightMargin())
+ {
+ if (m_caption_buttons_handled && m_caption_buttons_corner == Qt::TopRightCorner)
+ {
+ QPoint pos = mouse_pos_map;
+ pos.setX(pos.x() - (window_rect.right() - window_rect.left() - rightMargin()));
+
+ if (m_cls_mask.contains(pos))
+ return HTCLOSE; //title bar close button
+ else if (m_max_mask.contains(pos))
+ return HTMAXBUTTON; //title bar maximize button
+ else if (m_min_mask.contains(pos))
+ return HTMINBUTTON; //title bar minimize button
+ }
+
+ if (right_mask.contains(mouse_pos_map))
+ {
+ return HTNOWHERE; //title bar buttons right
+ }
+ }
+ else if (mouse_pos.x() > window_rect.left() + iconWidth() &&
+ mouse_pos.x() < window_rect.left() + iconWidth() + leftMargin())
+ {
+ if (m_caption_buttons_handled && m_caption_buttons_corner == Qt::TopLeftCorner)
+ {
+ QPoint pos = mouse_pos_map;
+ pos.setX(pos.x() - iconWidth());
+
+ if (m_cls_mask.contains(pos))
+ return HTCLOSE; //title bar close button
+ else if (m_max_mask.contains(pos))
+ return HTMAXBUTTON; //title bar maximize button
+ else if (m_min_mask.contains(pos))
+ return HTMINBUTTON; //title bar minimize button
+ }
+
+ if (left_mask.contains(mouse_pos_map))
+ {
+ return HTNOWHERE; //custom title bar buttons left
+ }
+ }
+ else if (mouse_pos.x() < window_rect.left() + iconWidth())
+ {
+ return HTSYSMENU; //title bar icon
+ }
+ }
+
+ //Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
+ LRESULT hitTests[3][3] =
+ {
+ {HTTOPLEFT, on_resize_border ? HTTOP : HTCAPTION, HTTOPRIGHT},
+ {HTLEFT, HTNOWHERE, HTRIGHT},
+ {HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT},
+ };
+
+ return hitTests[row][col];
+ }
+ else
+ {
+ const int border_width = (m_state == Qt::WindowNoState ? BORDERWIDTH : 0); //in pixels.
+ int title_height = titleBarHeight(); //in pixels.
+
+ //Get the point coordinates for the hit test.
+ QPoint mouse_pos = QPoint(x, y);
+
+ //Get the window rectangle.
+ QRect window_rect = geometry();
+
+ //Determine if the hit test is for resizing. Default middle (1,1).
+ int row = 1;
+ int col = 1;
+ bool on_resize_border = false;
+
+ //Determine if the point is at the top or bottom of the window.
+ if (mouse_pos.y() < window_rect.top() + title_height)
+ {
+ on_resize_border = (mouse_pos.y() < (window_rect.top() + border_width) &&
+ mouse_pos.x() > (window_rect.left() + border_width) &&
+ mouse_pos.x() < (window_rect.right() + border_width * 2));
+ row = 0;
+ }
+ else if (mouse_pos.y() >= window_rect.bottom())
+ {
+ row = 2;
+ }
+
+ //Determine if the point is at the left or right of the window.
+ if (mouse_pos.x() < window_rect.left() + border_width)
+ {
+ col = 0; //left side
+ }
+ else if (mouse_pos.x() > window_rect.right() + border_width)
+ {
+ col = 2; //right side
+ }
+ else if (row == 0 && !on_resize_border)
+ {
+ const QRegion left_mask = m_left_mask.translated(iconWidth(), 0);
+ const QRegion right_mask = m_right_mask.translated(width() - rightMargin(), 0);
+
+ QPoint mouse_pos_map = mapFromGlobal(mouse_pos);
+
+ if (mouse_pos.x() > window_rect.right() - rightMargin() + border_width)
+ {
+ if (m_caption_buttons_handled && m_caption_buttons_corner == Qt::TopRightCorner)
+ {
+ QPoint pos = mouse_pos_map;
+ pos.setX(pos.x() - (window_rect.right() - window_rect.left() - rightMargin()));
+
+ if (m_cls_mask.contains(pos))
+ return HTCLOSE; //title bar close button
+ else if (m_max_mask.contains(pos))
+ return HTMAXBUTTON; //title bar maximize button
+ else if (m_min_mask.contains(pos))
+ return HTMINBUTTON; //title bar minimize button
+ }
+
+ if (right_mask.contains(mouse_pos_map))
+ {
+ return HTNOWHERE; //title bar buttons right
+ }
+ }
+ else if (mouse_pos.x() > window_rect.left() + iconWidth() &&
+ mouse_pos.x() < window_rect.left() + iconWidth() + leftMargin())
+ {
+ if (m_caption_buttons_handled && m_caption_buttons_corner == Qt::TopLeftCorner)
+ {
+ QPoint pos = mouse_pos_map;
+ pos.setX(pos.x() - iconWidth());
+
+ if (m_cls_mask.contains(pos))
+ return HTCLOSE; //title bar close button
+ else if (m_max_mask.contains(pos))
+ return HTMAXBUTTON; //title bar maximize button
+ else if (m_min_mask.contains(pos))
+ return HTMINBUTTON; //title bar minimize button
+ }
+
+ if (left_mask.contains(mouse_pos_map))
+ {
+ return HTNOWHERE; //custom title bar buttons left
+ }
+ }
+ else if (mouse_pos.x() < window_rect.left() + iconWidth())
+ {
+ return HTSYSMENU; //title bar icon
+ }
+ }
+
+ //Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
+ LRESULT hitTests[3][3] =
+ {
+ {HTTOPLEFT, on_resize_border ? HTTOP : HTCAPTION, HTTOPRIGHT},
+ {HTLEFT, HTNOWHERE, HTRIGHT},
+ {HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT},
+ };
+
+ return hitTests[row][col];
+ }
+}
+
+void QGoodWindow::showContextMenu(int x, int y)
+{
+ HMENU menu = GetSystemMenu(m_hwnd, FALSE);
+
+ if (!menu)
+ return;
+
+ MENUITEMINFOW mi;
+ memset(&mi, 0, sizeof(MENUITEMINFOW));
+ mi.cbSize = sizeof(MENUITEMINFOW);
+ mi.fMask = MIIM_STATE;
+
+ mi.fState = MF_ENABLED;
+
+ SetMenuItemInfoW(menu, SC_RESTORE, FALSE, &mi);
+ SetMenuItemInfoW(menu, SC_SIZE, FALSE, &mi);
+ SetMenuItemInfoW(menu, SC_MOVE, FALSE, &mi);
+ SetMenuItemInfoW(menu, SC_MAXIMIZE, FALSE, &mi);
+ SetMenuItemInfoW(menu, SC_MINIMIZE, FALSE, &mi);
+
+ mi.fState = MF_GRAYED;
+
+ WINDOWPLACEMENT wp;
+ GetWindowPlacement(m_hwnd, &wp);
+
+ switch (wp.showCmd)
+ {
+ case SW_SHOWMAXIMIZED:
+ {
+ SetMenuItemInfoW(menu, SC_SIZE, FALSE, &mi);
+ SetMenuItemInfoW(menu, SC_MOVE, FALSE, &mi);
+ SetMenuItemInfoW(menu, SC_MAXIMIZE, FALSE, &mi);
+ SetMenuDefaultItem(menu, SC_CLOSE, FALSE);
+ break;
+ }
+ case SW_SHOWMINIMIZED:
+ {
+ SetMenuItemInfoW(menu, SC_MINIMIZE, FALSE, &mi);
+ SetMenuDefaultItem(menu, SC_RESTORE, FALSE);
+ break;
+ }
+ case SW_SHOWNORMAL:
+ {
+ SetMenuItemInfoW(menu, SC_RESTORE, FALSE, &mi);
+ SetMenuDefaultItem(menu, SC_CLOSE, FALSE);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (!(GetWindowLongW(m_hwnd, GWL_STYLE) & WS_MAXIMIZEBOX))
+ {
+ SetMenuItemInfoW(menu, SC_MAXIMIZE, FALSE, &mi);
+ SetMenuItemInfoW(menu, SC_RESTORE, FALSE, &mi);
+ }
+
+ if (!(GetWindowLongW(m_hwnd, GWL_STYLE) & WS_MINIMIZEBOX))
+ {
+ SetMenuItemInfoW(menu, SC_MINIMIZE, FALSE, &mi);
+ }
+
+ if (!(GetWindowLongW(m_hwnd, GWL_STYLE) & WS_SIZEBOX))
+ {
+ SetMenuItemInfoW(menu, SC_SIZE, FALSE, &mi);
+ }
+
+ int cmd = int(TrackPopupMenu(menu, TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD,
+ x, y, 0, m_hwnd, nullptr));
+
+ if (cmd)
+ PostMessageW(m_hwnd, WM_SYSCOMMAND, WPARAM(cmd), 0);
+}
+
+void QGoodWindow::showContextMenu()
+{
+ const int border_width = BORDERWIDTH;
+
+ int x_pos = x() + (!isMaximized() ? border_width : 0);
+ int y_pos = y() + titleBarHeight() - (isMaximized() ? border_width : 0);
+
+ showContextMenu(x_pos, y_pos);
+}
+
+QWidget *QGoodWindow::bestParentForModalWindow()
+{
+ QGoodWindowUtils::ParentWindow *parent = new QGoodWindowUtils::ParentWindow(this);
+ moveCenterWindow(parent);
+
+ return parent;
+}
+
+void QGoodWindow::moveCenterWindow(QWidget *widget)
+{
+ const int title_bar_height = (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION) +
+ GetSystemMetrics(SM_CXPADDEDBORDER));
+ const int border_width = (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER));
+
+ QScreen *screen = windowHandle()->screen();
+ QRect rect;
+
+ if (!isMinimized() && isVisible())
+ rect = frameGeometry();
+ else
+ rect = screen->availableGeometry();
+
+ QRect screen_rect = screen->availableGeometry();
+
+ QRect dialog_rect = widget->geometry();
+
+ dialog_rect.moveCenter(rect.center());
+
+ dialog_rect.moveLeft(qMax(dialog_rect.left(), screen_rect.left() + border_width));
+ dialog_rect.moveTop(qMax(dialog_rect.top() + titleBarHeight() / 2, screen_rect.top() + title_bar_height));
+ dialog_rect.moveRight(qMin(dialog_rect.right(), screen_rect.right() - border_width));
+ dialog_rect.moveBottom(qMin(dialog_rect.bottom(), screen_rect.bottom() - border_width));
+
+ widget->setGeometry(dialog_rect);
+
+ if (widget->windowIcon().isNull())
+ widget->setWindowIcon(windowIcon());
+
+ if (isMinimized())
+ showNormal();
+}
+
+bool QGoodWindow::winButtonHover(long button)
+{
+ if (button == -1)
+ return false;
+
+ switch (button)
+ {
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ {
+ if (!m_is_caption_button_pressed)
+ {
+ if (button != m_last_caption_button_hovered)
+ buttonLeave(m_last_caption_button_hovered);
+
+ if (isEnabled())
+ buttonEnter(button);
+ }
+ else
+ {
+ if (button != m_last_caption_button_hovered)
+ buttonLeave(m_last_caption_button_hovered);
+
+ if (!m_is_caption_button_pressed || (button == m_caption_button_pressed))
+ buttonEnter(button);
+ }
+
+ return true;
+ }
+ default:
+ {
+ buttonLeave(m_last_caption_button_hovered);
+ return false;
+ }
+ }
+}
+#endif
+#ifdef Q_OS_LINUX
+void QGoodWindow::setCursorForCurrentPos()
+{
+ const QPoint cursor_pos = QCursor::pos();
+ const int margin = ncHitTest(cursor_pos.x(), cursor_pos.y());
+
+ m_cursor_pos = cursor_pos;
+ m_margin = margin;
+
+ QWidget *widget = QApplication::widgetAt(cursor_pos);
+
+ if (!widget)
+ return;
+
+ Display *dpy = QX11Info::display();
+
+ switch (margin)
+ {
+ case TOP_LEFT:
+ {
+ Cursor cursor = XCreateFontCursor(dpy, XC_top_left_corner);
+
+ XDefineCursor(dpy, Window(widget->winId()), cursor);
+
+ XFlush(dpy);
+
+ XFreeCursor(dpy, cursor);
+
+ break;
+ }
+ case TOP:
+ {
+ Cursor cursor;
+
+ if (!m_fixed_size)
+ cursor = XCreateFontCursor(dpy, XC_top_side);
+ else
+ cursor = XCreateFontCursor(dpy, XC_arrow);
+
+ XDefineCursor(dpy, Window(widget->winId()), cursor);
+
+ XFlush(dpy);
+
+ XFreeCursor(dpy, cursor);
+
+ break;
+ }
+ case TOP_RIGHT:
+ {
+ Cursor cursor;
+
+ if (!m_fixed_size)
+ cursor = XCreateFontCursor(dpy, XC_top_right_corner);
+ else
+ cursor = XCreateFontCursor(dpy, XC_arrow);
+
+ XDefineCursor(dpy, Window(widget->winId()), cursor);
+
+ XFlush(dpy);
+
+ XFreeCursor(dpy, cursor);
+
+ break;
+ }
+ case LEFT:
+ {
+ Cursor cursor;
+
+ if (!m_fixed_size)
+ cursor = XCreateFontCursor(dpy, XC_left_side);
+ else
+ cursor = XCreateFontCursor(dpy, XC_arrow);
+
+ XDefineCursor(dpy, Window(widget->winId()), cursor);
+
+ XFlush(dpy);
+
+ XFreeCursor(dpy, cursor);
+
+ break;
+ }
+ case RIGHT:
+ {
+ Cursor cursor;
+
+ if (!m_fixed_size)
+ cursor = XCreateFontCursor(dpy, XC_right_side);
+ else
+ cursor = XCreateFontCursor(dpy, XC_arrow);
+
+ XDefineCursor(dpy, Window(widget->winId()), cursor);
+
+ XFlush(dpy);
+
+ XFreeCursor(dpy, cursor);
+
+ break;
+ }
+ case BOTTOM_LEFT:
+ {
+ Cursor cursor;
+
+ if (!m_fixed_size)
+ cursor = XCreateFontCursor(dpy, XC_bottom_left_corner);
+ else
+ cursor = XCreateFontCursor(dpy, XC_arrow);
+
+ XDefineCursor(dpy, Window(widget->winId()), cursor);
+
+ XFlush(dpy);
+
+ XFreeCursor(dpy, cursor);
+
+ break;
+ }
+ case BOTTOM:
+ {
+ Cursor cursor;
+
+ if (!m_fixed_size)
+ cursor = XCreateFontCursor(dpy, XC_bottom_side);
+ else
+ cursor = XCreateFontCursor(dpy, XC_arrow);
+
+ XDefineCursor(dpy, Window(widget->winId()), cursor);
+
+ XFlush(dpy);
+
+ XFreeCursor(dpy, cursor);
+
+ break;
+ }
+ case BOTTOM_RIGHT:
+ {
+ Cursor cursor;
+
+ if (!m_fixed_size)
+ cursor = XCreateFontCursor(dpy, XC_bottom_right_corner);
+ else
+ cursor = XCreateFontCursor(dpy, XC_arrow);
+
+ XDefineCursor(dpy, Window(widget->winId()), cursor);
+
+ XFlush(dpy);
+
+ XFreeCursor(dpy, cursor);
+
+ break;
+ }
+ case TITLE_BAR:
+ case HTMINBUTTON:
+ case HTMAXBUTTON:
+ case HTCLOSE:
+ {
+ if (QApplication::overrideCursor() &&
+ QApplication::overrideCursor()->shape() == Qt::ArrowCursor)
+ break;
+
+ QApplication::setOverrideCursor(Qt::ArrowCursor);
+
+ break;
+ }
+ case NO_WHERE:
+ {
+ if (!QApplication::overrideCursor())
+ break;
+
+ if (QApplication::overrideCursor()->shape() != Qt::ArrowCursor)
+ break;
+
+ QApplication::restoreOverrideCursor();
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void QGoodWindow::startSystemMoveResize()
+{
+ const int margin = m_margin;
+
+ if (margin == NO_WHERE)
+ return;
+
+ if (m_fixed_size && margin != TITLE_BAR)
+ return;
+
+ QPoint cursor_pos = m_cursor_pos;
+
+ XClientMessageEvent xmsg;
+ memset(&xmsg, 0, sizeof(XClientMessageEvent));
+
+ xmsg.type = ClientMessage;
+ xmsg.window = Window(winId());
+ xmsg.message_type = XInternAtom(QX11Info::display(), "_NET_WM_MOVERESIZE", False);
+ xmsg.format = 32;
+ xmsg.data.l[0] = long(cursor_pos.x());
+ xmsg.data.l[1] = long(cursor_pos.y());
+
+ if (margin == TITLE_BAR)
+ xmsg.data.l[2] = MOVERESIZE_MOVE;
+ else
+ xmsg.data.l[2] = long(margin);
+
+ xmsg.data.l[3] = 0;
+ xmsg.data.l[4] = 0;
+
+ XSendEvent(QX11Info::display(), QX11Info::appRootWindow(), False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ reinterpret_cast(&xmsg));
+
+ XUngrabPointer(QX11Info::display(), QX11Info::appTime());
+ XFlush(QX11Info::display());
+
+ QTimer::singleShot(500, this, [=]{
+ m_resize_move_started = true;
+ });
+}
+
+void QGoodWindow::sizeMove()
+{
+ if (!m_resize_move)
+ return;
+
+ m_resize_move = false;
+
+ const int margin = m_margin;
+
+ if (margin == TITLE_BAR)
+ {
+ QTimer::singleShot(0, this, [=]{
+ startSystemMoveResize();
+ });
+ }
+ else if (m_shadow_list.size() == SHADOW_WINDOW_COUNT)
+ {
+ startSystemMoveResize();
+ }
+}
+
+void QGoodWindow::sizeMoveBorders()
+{
+ if (m_shadow_list.size() != SHADOW_WINDOW_COUNT)
+ return;
+
+ if (!windowState().testFlag(Qt::WindowNoState))
+ return;
+
+ const int border_width = qCeil(BORDERWIDTH * m_pixel_ratio);
+
+ QRect geom = geometry();
+ geom.adjust(-border_width, -border_width, border_width, border_width);
+
+ QRegion available_geom;
+
+ for (const QScreen *screen : QApplication::screens())
+ {
+ available_geom += screen->availableGeometry();
+ }
+
+ { //TOP LEFT
+ Shadow *shadow = m_shadow_list.at(0);
+
+ QRect geom1 = geom;
+ geom1.moveTo(geom1.left(), geom1.top());
+ geom1.setWidth(border_width);
+ geom1.setHeight(border_width);
+
+ if (available_geom.intersects(geom1))
+ {
+ shadow->setGeometry(geom1);
+ shadow->setGeometry(available_geom.intersected(shadow->frameGeometry()).boundingRect());
+ shadow->show();
+ }
+ else
+ {
+ shadow->hide();
+ }
+ }
+
+ { //TOP RIGHT
+ Shadow *shadow = m_shadow_list.at(1);
+
+ QRect geom1 = geom;
+ geom1.moveTo(geom1.right() - border_width + 1, geom1.top());
+ geom1.setWidth(border_width);
+ geom1.setHeight(border_width);
+
+ if (available_geom.intersects(geom1))
+ {
+ shadow->setGeometry(geom1);
+ shadow->setGeometry(available_geom.intersected(shadow->frameGeometry()).boundingRect());
+ shadow->show();
+ }
+ else
+ {
+ shadow->hide();
+ }
+ }
+
+ { //BOTTOM LEFT
+ Shadow *shadow = m_shadow_list.at(2);
+
+ QRect geom1 = geom;
+ geom1.moveTo(geom1.left(), geom1.bottom() - border_width + 1);
+ geom1.setWidth(border_width);
+ geom1.setHeight(border_width);
+
+ if (available_geom.intersects(geom1))
+ {
+ shadow->setGeometry(geom1);
+ shadow->setGeometry(available_geom.intersected(shadow->frameGeometry()).boundingRect());
+ shadow->show();
+ }
+ else
+ {
+ shadow->hide();
+ }
+ }
+
+ { //BOTTOM RIGHT
+ Shadow *shadow = m_shadow_list.at(3);
+
+ QRect geom1 = geom;
+ geom1.moveTo(geom1.right() - border_width + 1, geom1.bottom() - border_width + 1);
+ geom1.setWidth(border_width);
+ geom1.setHeight(border_width);
+
+ if (available_geom.intersects(geom1))
+ {
+ shadow->setGeometry(geom1);
+ shadow->setGeometry(available_geom.intersected(shadow->frameGeometry()).boundingRect());
+ shadow->show();
+ }
+ else
+ {
+ shadow->hide();
+ }
+ }
+
+ { //TOP
+ Shadow *shadow = m_shadow_list.at(4);
+
+ QRect geom1 = geom;
+ geom1.moveTo(geom1.left() + border_width, geom1.top());
+ geom1.setWidth(geom1.width() - border_width * 2);
+ geom1.setHeight(border_width);
+
+ if (available_geom.intersects(geom1))
+ {
+ shadow->setGeometry(geom1);
+ shadow->setGeometry(available_geom.intersected(shadow->frameGeometry()).boundingRect());
+ shadow->show();
+ }
+ else
+ {
+ shadow->hide();
+ }
+ }
+
+ { //LEFT
+ Shadow *shadow = m_shadow_list.at(5);
+
+ QRect geom1 = geom;
+ geom1.moveTo(geom1.left(), geom1.top() + border_width);
+ geom1.setWidth(border_width);
+ geom1.setHeight(geom1.height() - border_width * 2);
+
+ if (available_geom.intersects(geom1))
+ {
+ shadow->setGeometry(geom1);
+ shadow->setGeometry(available_geom.intersected(shadow->frameGeometry()).boundingRect());
+ shadow->show();
+ }
+ else
+ {
+ shadow->hide();
+ }
+ }
+
+ { //RIGHT
+ Shadow *shadow = m_shadow_list.at(6);
+
+ QRect geom1 = geom;
+ geom1.moveTo(geom1.right() - border_width + 1, geom1.top() + border_width);
+ geom1.setWidth(border_width);
+ geom1.setHeight(geom1.height() - border_width * 2);
+
+ if (available_geom.intersects(geom1))
+ {
+ shadow->setGeometry(geom1);
+ shadow->setGeometry(available_geom.intersected(shadow->frameGeometry()).boundingRect());
+ shadow->show();
+ }
+ else
+ {
+ shadow->hide();
+ }
+ }
+
+ { //BOTTOM
+ Shadow *shadow = m_shadow_list.at(7);
+
+ QRect geom1 = geom;
+ geom1.moveTo(geom1.left() + border_width, geom1.bottom() - border_width + 1);
+ geom1.setWidth(geom1.width() - border_width * 2);
+ geom1.setHeight(border_width);
+
+ if (available_geom.intersects(geom1))
+ {
+ shadow->setGeometry(geom1);
+ shadow->setGeometry(available_geom.intersected(shadow->frameGeometry()).boundingRect());
+ shadow->show();
+ }
+ else
+ {
+ shadow->hide();
+ }
+ }
+}
+#endif
+#ifdef Q_OS_MAC
+void QGoodWindow::notificationReceiver(const QByteArray ¬ification)
+{
+ if (notification == "NSWindowWillEnterFullScreenNotification")
+ {
+ m_on_animate_event = true;
+ macOSNative::setStyle(long(winId()), true);
+ }
+ else if (notification == "NSWindowDidExitFullScreenNotification")
+ {
+ macOSNative::setStyle(long(winId()), false);
+ m_on_animate_event = false;
+ }
+ else if (notification == "AppleInterfaceThemeChangedNotification")
+ {
+ if (m_theme_change_timer)
+ m_theme_change_timer->start();
+ }
+}
+#endif
+#if defined Q_OS_LINUX || defined Q_OS_MAC
+int QGoodWindow::ncHitTest(int x, int y)
+{
+ const int title_height = titleBarHeight(); //in pixels.
+
+ //Get the point coordinates for the hit test.
+ QPoint mouse_pos = QPoint(x, y);
+
+ //Get the window rectangle.
+ QRect window_rect = frameGeometry();
+
+ //Determine if the hit test is for resizing. Default middle (1,1).
+ int row = 1;
+ int col = 1;
+ bool on_resize_border = false;
+
+ //Determine if the point is at the top or bottom of the window.
+ if (mouse_pos.y() < window_rect.top() + title_height)
+ {
+ on_resize_border = (mouse_pos.y() < window_rect.top());
+ row = 0;
+ }
+ else if (mouse_pos.y() > window_rect.bottom())
+ {
+ row = 2;
+ }
+
+ //Determine if the point is at the left or right of the window.
+ if (mouse_pos.x() < window_rect.left())
+ {
+ col = 0; //left side
+ }
+ else if (mouse_pos.x() > window_rect.right())
+ {
+ col = 2; //right side
+ }
+ else if (row == 0 && !on_resize_border)
+ {
+ const QRegion left_mask = m_left_mask.translated(iconWidth(), 0);
+ const QRegion right_mask = m_right_mask.translated(width() - rightMargin(), 0);
+
+ const QPoint mouse_pos_map = mapFromGlobal(mouse_pos);
+
+ if (mouse_pos.x() > window_rect.right() - rightMargin())
+ {
+ if (m_caption_buttons_handled && m_caption_buttons_corner == Qt::TopRightCorner)
+ {
+ QPoint pos = mouse_pos_map;
+ pos.setX(pos.x() - (window_rect.right() - window_rect.left() - rightMargin()));
+
+ if (m_cls_mask.contains(pos))
+ return HTCLOSE; //title bar close button
+ else if (m_max_mask.contains(pos))
+ return HTMAXBUTTON; //title bar maximize button
+ else if (m_min_mask.contains(pos))
+ return HTMINBUTTON; //title bar minimize button
+ }
+
+ if (right_mask.contains(mouse_pos_map))
+ return NO_WHERE; //custom title bar buttons right
+ }
+ else if (mouse_pos.x() > window_rect.left() + iconWidth() &&
+ mouse_pos.x() < window_rect.left() + iconWidth() + leftMargin())
+ {
+ if (m_caption_buttons_handled && m_caption_buttons_corner == Qt::TopLeftCorner)
+ {
+ QPoint pos = mouse_pos_map;
+ pos.setX(pos.x() - iconWidth());
+
+ if (m_cls_mask.contains(pos))
+ return HTCLOSE; //title bar close button
+ else if (m_max_mask.contains(pos))
+ return HTMAXBUTTON; //title bar maximize button
+ else if (m_min_mask.contains(pos))
+ return HTMINBUTTON; //title bar minimize button
+ }
+
+ if (left_mask.contains(mouse_pos_map))
+ return NO_WHERE; //custom title bar buttons left
+ }
+ }
+
+ //Hit test (TOP_LEFT, ... BOTTOM_RIGHT)
+ int hitTests[3][3] =
+ {
+ {TOP_LEFT, on_resize_border ? TOP : TITLE_BAR, TOP_RIGHT},
+ {LEFT, NO_WHERE, RIGHT},
+ {BOTTOM_LEFT, BOTTOM, BOTTOM_RIGHT},
+ };
+
+ return hitTests[row][col];
+}
+#endif
+#ifdef QGOODWINDOW
+void QGoodWindow::buttonEnter(long button)
+{
+ if (button == -1)
+ return;
+
+ m_last_caption_button_hovered = button;
+
+ switch (button)
+ {
+ case HTMINBUTTON:
+ {
+ emit captionButtonStateChanged(CaptionButtonState::MinimizeHoverEnter);
+
+ break;
+ }
+ case HTMAXBUTTON:
+ {
+ emit captionButtonStateChanged(CaptionButtonState::MaximizeHoverEnter);
+
+ break;
+ }
+ case HTCLOSE:
+ {
+ emit captionButtonStateChanged(CaptionButtonState::CloseHoverEnter);
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void QGoodWindow::buttonLeave(long button)
+{
+ if (button == -1)
+ return;
+
+ if (button != m_last_caption_button_hovered)
+ return;
+
+ m_last_caption_button_hovered = -1;
+
+ switch (button)
+ {
+ case HTMINBUTTON:
+ {
+ emit captionButtonStateChanged(CaptionButtonState::MinimizeHoverLeave);
+
+ break;
+ }
+ case HTMAXBUTTON:
+ {
+ emit captionButtonStateChanged(CaptionButtonState::MaximizeHoverLeave);
+
+ break;
+ }
+ case HTCLOSE:
+ {
+ emit captionButtonStateChanged(CaptionButtonState::CloseHoverLeave);
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+bool QGoodWindow::buttonPress(long button)
+{
+ if (button == -1)
+ return false;
+
+ switch (button)
+ {
+ case HTMINBUTTON:
+ {
+ m_is_caption_button_pressed = true;
+ m_caption_button_pressed = HTMINBUTTON;
+
+ emit captionButtonStateChanged(CaptionButtonState::MinimizePress);
+
+ activateWindow();
+
+ return true;
+ }
+ case HTMAXBUTTON:
+ {
+ m_is_caption_button_pressed = true;
+ m_caption_button_pressed = HTMAXBUTTON;
+
+ emit captionButtonStateChanged(CaptionButtonState::MaximizePress);
+
+ activateWindow();
+
+ return true;
+ }
+ case HTCLOSE:
+ {
+ m_is_caption_button_pressed = true;
+ m_caption_button_pressed = HTCLOSE;
+
+ emit captionButtonStateChanged(CaptionButtonState::ClosePress);
+
+ activateWindow();
+
+ return true;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool QGoodWindow::buttonRelease(long button, bool valid_click)
+{
+ if (button == -1)
+ return false;
+
+ if (!m_is_caption_button_pressed)
+ return false;
+
+ if (m_hover_timer->isActive())
+ return false;
+
+ m_is_caption_button_pressed = false;
+ m_caption_button_pressed = -1;
+
+ switch (button)
+ {
+ case HTMINBUTTON:
+ {
+ emit captionButtonStateChanged(CaptionButtonState::MinimizeRelease);
+
+ if (valid_click)
+ {
+ emit captionButtonStateChanged(CaptionButtonState::MinimizeClicked);
+ m_hover_timer->start();
+ }
+
+ return true;
+ }
+ case HTMAXBUTTON:
+ {
+ emit captionButtonStateChanged(CaptionButtonState::MaximizeRelease);
+
+ if (valid_click)
+ {
+ emit captionButtonStateChanged(CaptionButtonState::MaximizeClicked);
+ m_hover_timer->start();
+ }
+
+ return true;
+ }
+ case HTCLOSE:
+ {
+ emit captionButtonStateChanged(CaptionButtonState::CloseRelease);
+
+ if (valid_click)
+ {
+ emit captionButtonStateChanged(CaptionButtonState::CloseClicked);
+ m_hover_timer->start();
+ }
+
+ return true;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+#endif
diff --git a/QGoodWindow/src/qgoodwindow.h b/QGoodWindow/src/qgoodwindow.h
new file mode 100644
index 0000000..bc3b3e7
--- /dev/null
+++ b/QGoodWindow/src/qgoodwindow.h
@@ -0,0 +1,446 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2018-2022 Antonio Dias
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef QGOODWINDOW_H
+#define QGOODWINDOW_H
+
+#include
+#include
+#include
+
+#ifdef QGOODWINDOW
+
+#ifdef Q_OS_WIN
+namespace QGoodWindowUtils
+{
+class NativeEventFilter;
+}
+#endif
+
+#if defined Q_OS_WIN || defined Q_OS_LINUX
+class Shadow;
+#endif
+
+#endif
+
+/** **QGoodWindow** class contains the public API's to control the behavior of the customized window.
+ *
+ * On Windows, **QGoodWindow** class inherits from `QMainWindow` which is used as a native widget that
+ * creates, fill and control the native window of **QGoodWindow**.
+ *
+ * On Linux and macOS the **QGoodWindow** behaves like a frameless `QMainWindow`.
+ */
+class QGoodWindow : public QMainWindow
+{
+ Q_OBJECT
+public:
+ /** Constructor of *QGoodWindow*.
+ *
+ * On Windows creates the native window, turns the `QMainWindow` as a native widget,
+ * creates the shadow, initialize default values and calls the `QMainWindow`
+ * parent constructor.
+ *
+ * On Linux creates the frameless `QMainWindow`, use the shadow only to create resize borders,
+ * and the real shadow is draw by current Linux window manager.
+ *
+ * On macOS creates a `QMainWindow` with full access to the title bar,
+ * and hide native minimize, zoom and close buttons.
+ */
+ explicit QGoodWindow(QWidget *parent = nullptr,
+ const QColor &clear_color =
+ QColor(!isSystemThemeDark() ? Qt::white : Qt::black));
+
+ /** Destructor of *QGoodWindow*. */
+ ~QGoodWindow();
+
+ /** Enum that contains caption buttons states when it's states are handled by *QGoodWindow*.*/
+ enum class CaptionButtonState
+ {
+ /** Minimize button hover enter. */
+ MinimizeHoverEnter,
+
+ /** Minimize button hover leave. */
+ MinimizeHoverLeave,
+
+ /** Minimize button press. */
+ MinimizePress,
+
+ /** Minimize button release. */
+ MinimizeRelease,
+
+ /** Minimize button clicked. */
+ MinimizeClicked,
+
+ /** Maximize or restore button hover enter. */
+ MaximizeHoverEnter,
+
+ /** Maximize or restore button hover leave. */
+ MaximizeHoverLeave,
+
+ /** Maximize or restore button press. */
+ MaximizePress,
+
+ /** Maximize or restore button release. */
+ MaximizeRelease,
+
+ /** Maximize or restore button clicked. */
+ MaximizeClicked,
+
+ /** Close button hover enter. */
+ CloseHoverEnter,
+
+ /** Close button hover leave. */
+ CloseHoverLeave,
+
+ /** Close button press. */
+ ClosePress,
+
+ /** Close button release. */
+ CloseRelease,
+
+ /** Close button clicked. */
+ CloseClicked
+ };
+
+ //Functions
+ /** Returns the window id of the *QGoodWindow*. */
+ WId winId() const;
+
+ //Variables
+ /** Reserved. */
+ QPointer m_theme_change_timer;
+
+signals:
+ /** On handled caption buttons, this SIGNAL report the state of these buttons. */
+ void captionButtonStateChanged(const QGoodWindow::CaptionButtonState &state);
+
+ /** Notify that the system has changed between light and dark mode. */
+ void systemThemeChanged();
+
+public slots:
+ /*** QGOODWINDOW FUNCTIONS BEGIN ***/
+
+ /** Returns if the current system theme is dark or not. */
+ static bool isSystemThemeDark();
+
+ /** Returns if there is a one pixel margin around window for resizing or not, i.e. if system draw margins. */
+ static bool shouldBordersBeDrawnBySystem();
+
+ /** On Windows, Linux and macOS, returns the actual title bar height, on other OSes returns 0. */
+ int titleBarHeight() const;
+
+ /** On Windows, Linux and macOS, return the actual icon width, on other OSes returns 0. */
+ int iconWidth() const;
+
+ /** On Windows, Linux and macOS, returns the left margin of the customized title bar, on other OSes returns 0. */
+ int leftMargin() const;
+
+ /** On Windows, Linux and macOS, returns the right margin of the customized title bar, on other OSes returns 0. */
+ int rightMargin() const;
+
+ /** Set the tile bar height, icon width, left and right margins of the customized title bar. */
+ void setMargins(int title_bar_height, int icon_width, int left, int right);
+
+ /** Set the mask for the left margin of the customized title bar. */
+ void setLeftMask(const QRegion &mask);
+
+ /** Set the mask for the right margin of the customized title bar. */
+ void setRightMask(const QRegion &mask);
+
+ /** Set if the caption buttons should be handled by *QGoodWindow* and on which \e corner, valid only top left and top right corners. */
+ void setCaptionButtonsHandled(bool handled, const Qt::Corner &corner = Qt::TopRightCorner);
+
+ /** Set the location and shape of handled minimize button, relative to handled corner. */
+ void setMinimizeMask(const QRegion &mask);
+
+ /** Set the location and shape of handled maximize button, relative to handled corner. */
+ void setMaximizeMask(const QRegion &mask);
+
+ /** Set the location and shape of handled close button, relative to handled corner. */
+ void setCloseMask(const QRegion &mask);
+
+ /** Get the size that should be the size of the mask on the left margin of the customized title bar. */
+ QSize leftMaskSize() const;
+
+ /** Get the size that should be the size of the mask on the right margin of the customized title bar. */
+ QSize rightMaskSize() const;
+
+ /** If caption buttons are handled on left corner, their buttons masks should be in the bounds of this rect. */
+ QRect leftCaptionButtonsRect() const;
+
+ /** If caption buttons are handled on right corner, their buttons masks should be in the bounds of this rect. */
+ QRect rightCaptionButtonsRect() const;
+
+ /*** QGOODWINDOW FUNCTIONS END ***/
+
+ /** Set fixed size for *QGoodWindow* to width \e w and height \e h. */
+ void setFixedSize(int w, int h);
+
+ /** Set fixed size for *QGoodWindow* to \e size. */
+ void setFixedSize(const QSize &size);
+
+ /** Returns the geometry for *QGoodWindow* including extended frame and excluding shadow. */
+ QRect frameGeometry() const;
+
+ /** Returns the client area geometry. */
+ QRect geometry() const;
+
+ /** Returns the client area size, position is always `QPoint(0, 0)`. */
+ QRect rect() const;
+
+ /** Position of the window on screen. */
+ QPoint pos() const;
+
+ /** Size of the window on screen. */
+ QSize size() const;
+
+ /** X position of the window on screen. */
+ int x() const;
+
+ /** Y position of the window on screen. */
+ int y() const;
+
+ /** Width of the window. */
+ int width() const;
+
+ /** Height of the window. */
+ int height() const;
+
+ /** Move the window to \e x - \e y coordinates. */
+ void move(int x, int y);
+
+ /** Move the window to \e pos. */
+ void move(const QPoint &pos);
+
+ /** Resize the window to \e width - \e height size. */
+ void resize(int width, int height);
+
+ /** Resize the window to \e size. */
+ void resize(const QSize &size);
+
+ /** Set geometry to pos \e x - \e y, width \e w and height \e h. */
+ void setGeometry(int x, int y, int w, int h);
+
+ /** Set geometry to \e rect. */
+ void setGeometry(const QRect &rect);
+
+ /** Activates the *QGoodWindow*. */
+ void activateWindow();
+
+ /** Shows the *QGoodWindow*. */
+ void show();
+
+ /** Shows or restore the *QGoodWindow*. */
+ void showNormal();
+
+ /** Shows or maximize the *QGoodWindow*. */
+ void showMaximized();
+
+ /** Minimize the *QGoodWindow*. */
+ void showMinimized();
+
+ /** Turns the *QGoodWindow* into full screen mode. Including the title bar. */
+ void showFullScreen();
+
+ /** Hide the *QGoodWindow*. */
+ void hide();
+
+ /** Close the *QGoodWindow*. */
+ void close();
+
+ /** Returns if the *QGoodWindow* is visible or not. */
+ bool isVisible() const;
+
+ /** Returns if the *QGoodWindow* is enabled or not. */
+ bool isEnabled() const;
+
+ /** Returns if the *QGoodWindow* is the foreground window or not. */
+ bool isActiveWindow() const;
+
+ /** Returns if the *QGoodWindow* is maximized or not. */
+ bool isMaximized() const;
+
+ /** Returns if the *QGoodWindow* is minimized or not. */
+ bool isMinimized() const;
+
+ /** Returns if the *QGoodWindow* is in full screen mode or not. */
+ bool isFullScreen() const;
+
+ /** Returns the *QGoodWindow* state. */
+ Qt::WindowStates windowState() const;
+
+ /** Sets the state of the *QGoodWindow* to \e state. */
+ void setWindowState(Qt::WindowStates state);
+
+ /** Returns the window handle of the *QGoodWindow*. */
+ QWindow *windowHandle() const;
+
+ /** Returns the opacity of the *QGoodWindow*. */
+ qreal windowOpacity() const;
+
+ /** Sets the opacity of the *QGoodWindow* to \e level. Where 0.0 is fully transparent and 1.0 fully opaque. */
+ void setWindowOpacity(qreal level);
+
+ /** Returns the title of the *QGoodWindow*. */
+ QString windowTitle() const;
+
+ /** Sets the title of the *QGoodWindow* to \e title. */
+ void setWindowTitle(const QString &title);
+
+ /** Returns the icon of the *QGoodWindow*. */
+ QIcon windowIcon() const;
+
+ /** Sets the icon of the *QGoodWindow* to \e icon. */
+ void setWindowIcon(const QIcon &icon);
+
+protected:
+ //Functions
+ bool event(QEvent *event);
+ bool eventFilter(QObject *watched, QEvent *event);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ bool nativeEvent(const QByteArray &eventType, void *message, long *result);
+#else
+ bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result);
+#endif
+
+private:
+#ifdef QGOODWINDOW
+#ifdef Q_OS_WIN
+ //Functions
+ void initGW();
+ void destroyGW();
+ static LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+ void handleActivation();
+ void handleDeactivation();
+ void setWidgetFocus();
+ void enableCaption();
+ void disableCaption();
+ void frameChanged();
+ void sizeMoveWindow();
+ LRESULT ncHitTest(int x, int y);
+ void showContextMenu(int x, int y);
+ void showContextMenu();
+ QWidget *bestParentForModalWindow();
+ void moveCenterWindow(QWidget *widget);
+ bool winButtonHover(long button);
+
+ //Variables
+ HWND m_hwnd;
+ bool m_win_use_native_borders;
+ QPointer m_main_window;
+ QPointer m_shadow;
+ QPointer m_helper_widget;
+ QGoodWindowUtils::NativeEventFilter *m_native_event;
+ QWindow *m_window_handle;
+
+ QPointer m_focus_widget;
+
+ bool m_closed;
+
+ bool m_is_full_screen;
+ QRect m_rect_origin;
+
+ bool m_active_state;
+
+ Qt::WindowState m_state;
+
+ QColor m_clear_color;
+
+ friend class QGoodWindowUtils::NativeEventFilter;
+#endif
+#ifdef Q_OS_LINUX
+ //Functions
+ void setCursorForCurrentPos();
+ void startSystemMoveResize();
+ void sizeMove();
+ void sizeMoveBorders();
+
+ //Variables
+ QList> m_shadow_list;
+
+ int m_margin;
+ QPoint m_cursor_pos;
+ bool m_resize_move;
+ bool m_resize_move_started;
+ qreal m_pixel_ratio;
+
+ friend class Shadow;
+#endif
+#ifdef Q_OS_MAC
+ //Functions
+ void notificationReceiver(const QByteArray ¬ification);
+
+ //Variables
+ QPoint m_pos;
+ bool m_mouse_button_pressed;
+ bool m_on_animate_event;
+
+ friend class Notification;
+#endif
+#if defined Q_OS_LINUX || defined Q_OS_MAC
+ //Functions
+ int ncHitTest(int x, int y);
+
+ //Variables
+ int m_last_move_button;
+#endif
+#if defined Q_OS_WIN || defined Q_OS_LINUX
+ bool m_fixed_size;
+#endif
+#ifdef Q_OS_LINUX
+ bool m_last_fixed_size_value;
+#endif
+ //Functions
+ void buttonEnter(long button);
+ void buttonLeave(long button);
+ bool buttonPress(long button);
+ bool buttonRelease(long button, bool valid_click);
+
+ //Variables
+ QPointer m_hover_timer;
+
+ QRegion m_left_mask;
+ QRegion m_right_mask;
+
+ QRegion m_min_mask;
+ QRegion m_max_mask;
+ QRegion m_cls_mask;
+
+ bool m_dark;
+
+ bool m_caption_buttons_handled;
+ Qt::Corner m_caption_buttons_corner;
+
+ int m_title_bar_height;
+ int m_icon_width;
+ int m_left_margin;
+ int m_right_margin;
+
+ bool m_is_caption_button_pressed;
+ long m_last_caption_button_hovered;
+ long m_caption_button_pressed;
+#endif
+};
+
+#endif // QGOODWINDOW_H
diff --git a/QGoodWindow/src/shadow.cpp b/QGoodWindow/src/shadow.cpp
new file mode 100644
index 0000000..39026f0
--- /dev/null
+++ b/QGoodWindow/src/shadow.cpp
@@ -0,0 +1,258 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2018-2022 Antonio Dias
+
+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 "shadow.h"
+
+#ifdef Q_OS_LINUX
+#include "qgoodwindow.h"
+#endif
+
+#ifdef Q_OS_WIN
+#define SHADOWWIDTH qCeil(10 * m_pixel_ratio)
+#define COLOR1 QColor(0, 0, 0, 75)
+#define COLOR2 QColor(0, 0, 0, 30)
+#define COLOR3 QColor(0, 0, 0, 1)
+#endif
+
+#ifdef Q_OS_WIN
+Shadow::Shadow(qreal pixel_ratio, HWND hwnd) : QWidget()
+{
+ m_pixel_ratio = pixel_ratio;
+
+ m_hwnd = hwnd;
+ m_active = true;
+
+ setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::Tool);
+
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAttribute(Qt::WA_TranslucentBackground);
+
+ setAttribute(Qt::WA_TransparentForMouseEvents);
+
+ m_timer = new QTimer(this);
+ connect(m_timer, &QTimer::timeout, this, &Shadow::show);
+ m_timer->setInterval(500/*Time to wait before showing shadow when showLater() is callled.*/);
+ m_timer->setSingleShot(true);
+}
+#endif
+#ifdef Q_OS_LINUX
+Shadow::Shadow(QWidget *parent) : QWidget(parent)
+{
+ m_parent = qobject_cast(parent);
+
+ setWindowFlags(Qt::Window |
+ Qt::FramelessWindowHint |
+ Qt::WindowDoesNotAcceptFocus |
+ Qt::BypassWindowManagerHint);
+
+ setAttribute(Qt::WA_NoSystemBackground);
+ setAttribute(Qt::WA_TranslucentBackground);
+}
+#endif
+
+int Shadow::shadowWidth()
+{
+#ifdef Q_OS_WIN
+ return SHADOWWIDTH;
+#else
+ return 0;
+#endif
+}
+
+void Shadow::showLater()
+{
+#ifdef Q_OS_WIN
+ m_timer->stop();
+ m_timer->start();
+#endif
+}
+
+void Shadow::show()
+{
+#ifdef Q_OS_WIN
+ if (m_timer->isActive())
+ return;
+
+ if (!IsWindowEnabled(m_hwnd))
+ return;
+
+ QWidget::show();
+ QWidget::raise();
+
+ SetWindowPos(m_hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
+#else
+ QWidget::show();
+#endif
+}
+
+void Shadow::hide()
+{
+#ifdef Q_OS_WIN
+ m_timer->stop();
+
+ if (!isVisible())
+ return;
+
+ QWidget::hide();
+#else
+ QWidget::hide();
+#endif
+}
+
+void Shadow::setActive(bool active)
+{
+#ifdef Q_OS_WIN
+ m_active = active;
+ repaint();
+#else
+ Q_UNUSED(active)
+#endif
+}
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+bool Shadow::nativeEvent(const QByteArray &eventType, void *message, long *result)
+#else
+bool Shadow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
+#endif
+{
+#ifdef Q_OS_WIN
+ MSG* msg = static_cast(message);
+
+ switch (msg->message)
+ {
+ case WM_ACTIVATE:
+ {
+ switch (msg->wParam)
+ {
+ case WA_ACTIVE:
+ case WA_CLICKACTIVE:
+ {
+ //When shadow got focus, transfer it to main window.
+ SetForegroundWindow(m_hwnd);
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ break;
+ }
+ case WM_MOUSEACTIVATE:
+ {
+ //When shadow got focus, transfer it to main window.
+ SetForegroundWindow(m_hwnd);
+
+ //Prevent main window from "focus flickering".
+ *result = MA_NOACTIVATE;
+
+ return true;
+ }
+ case WM_NCMOUSEMOVE:
+ case WM_NCLBUTTONDOWN:
+ case WM_NCLBUTTONUP:
+ case WM_NCLBUTTONDBLCLK:
+ case WM_NCHITTEST:
+ {
+ //Transfer the above messages to main window,
+ //this way the resize and snap effects happens also
+ //when interacting with the shadow, and acts like a secondary border.
+ *result = long(SendMessageW(m_hwnd, msg->message, msg->wParam, msg->lParam));
+ return true;
+ }
+ default:
+ break;
+ }
+#endif
+
+ return QWidget::nativeEvent(eventType, message, result);
+}
+
+void Shadow::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+
+#ifdef Q_OS_WIN
+ //Draw shadow
+
+ const int shadow_width = SHADOWWIDTH;
+
+ QPainter painter(this);
+
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+
+ if (!m_active)
+ {
+ painter.fillRect(rect(), QColor(0, 0, 0, 1));
+
+ QRect rect1 = rect().adjusted(shadow_width, shadow_width, -shadow_width, -shadow_width);
+
+ painter.fillRect(rect1, Qt::transparent);
+
+ return;
+ }
+
+ QPixmap radial_gradient = QPixmap(shadow_width * 2, shadow_width * 2);
+
+ {
+ //Draw a radial gradient then split it in 4 parts and draw it to corners and edges
+
+ radial_gradient.fill(QColor(0, 0, 0, 1));
+
+ QPainter painter(&radial_gradient);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+
+ QRadialGradient gradient(shadow_width, shadow_width, shadow_width);
+ gradient.setColorAt(0.0, COLOR1);
+ gradient.setColorAt(0.2, COLOR2);
+ gradient.setColorAt(0.5, COLOR3);
+
+ QPen pen(Qt::transparent, 0);
+ painter.setPen(pen);
+ painter.setBrush(gradient);
+ painter.drawEllipse(0, 0, shadow_width * 2, shadow_width * 2);
+ }
+
+ QRect rect1 = rect().adjusted(shadow_width, shadow_width, -shadow_width, -shadow_width);
+
+ painter.drawPixmap(0, 0, shadow_width, shadow_width, radial_gradient, 0, 0, shadow_width, shadow_width); //Top-left corner
+ painter.drawPixmap(rect().width() - shadow_width, 0, radial_gradient, shadow_width, 0, shadow_width, shadow_width); //Top-right corner
+ painter.drawPixmap(0, rect().height() - shadow_width, radial_gradient, 0, shadow_width, shadow_width, shadow_width); //Bottom-left corner
+ painter.drawPixmap(rect().width() - shadow_width, rect().height() - shadow_width, radial_gradient, shadow_width, shadow_width, shadow_width, shadow_width); //Bottom-right corner
+
+ painter.drawPixmap(shadow_width, 0, rect1.width(), shadow_width, radial_gradient, shadow_width, 0, 1, shadow_width); //Top
+ painter.drawPixmap(0, shadow_width, shadow_width, rect1.height(), radial_gradient, 0, shadow_width, shadow_width, 1); //Left
+ painter.drawPixmap(rect1.width() + shadow_width, shadow_width, shadow_width, rect1.height(), radial_gradient, shadow_width, shadow_width, shadow_width, 1); //Right
+ painter.drawPixmap(shadow_width, rect1.height() + shadow_width, rect1.width(), shadow_width, radial_gradient, shadow_width, shadow_width, 1, SHADOWWIDTH); //Bottom
+#endif
+#ifdef Q_OS_LINUX
+ if (!m_parent)
+ return;
+
+ QPainter painter(this);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.fillRect(rect(), Qt::transparent);
+#endif
+}
diff --git a/QGoodWindow/src/shadow.h b/QGoodWindow/src/shadow.h
new file mode 100644
index 0000000..cc40b4d
--- /dev/null
+++ b/QGoodWindow/src/shadow.h
@@ -0,0 +1,77 @@
+/*
+The MIT License (MIT)
+
+Copyright © 2018-2022 Antonio Dias
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef SHADOW_H
+#define SHADOW_H
+
+#include
+#include
+#include
+
+#ifdef Q_OS_LINUX
+class QGoodWindow;
+#endif
+
+//\cond HIDDEN_SYMBOLS
+class Shadow : public QWidget
+{
+ Q_OBJECT
+public:
+#ifdef Q_OS_WIN
+ explicit Shadow(qreal pixel_ratio, HWND hwnd);
+#endif
+#ifdef Q_OS_LINUX
+ explicit Shadow(QWidget *parent);
+#endif
+
+public slots:
+ void showLater();
+ void show();
+ void hide();
+ void setActive(bool active);
+ int shadowWidth();
+
+private:
+ //Functions
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ bool nativeEvent(const QByteArray &eventType, void *message, long *result);
+#else
+ bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result);
+#endif
+ void paintEvent(QPaintEvent *event);
+
+ //Variables
+#ifdef Q_OS_WIN
+ HWND m_hwnd;
+ QTimer *m_timer;
+ bool m_active;
+ qreal m_pixel_ratio;
+#endif
+#ifdef Q_OS_LINUX
+ QPointer m_parent;
+#endif
+};
+//\endcond
+
+#endif // SHADOW_H