将QGoodWindow换成FramelessHelper

dev-VirtualTexture
wuyize 2023-01-22 11:04:22 +08:00
parent 53c6a4fbe5
commit 4f9720ba89
293 changed files with 31573 additions and 5650 deletions

View File

@ -4,11 +4,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00
VisualStudioVersion = 17.2.32519.379 VisualStudioVersion = 17.2.32519.379
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ArchitectureColoredPainting", "ArchitectureColoredPainting\ArchitectureColoredPainting.vcxproj", "{3FE96A33-2BB7-4686-A710-3EB8E3BBD709}" 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 EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QGoodWindow", "QGoodWindow\QGoodWindow.vcxproj", "{B982E745-C0B1-46B3-A27B-743AF105F2D0}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FramelessHelperCore", "FramelessHelper\qmake\FramelessHelperCore.vcxproj", "{F1BC586E-F1ED-320C-899E-76CCB5660AE1}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FramelessHelperWidgets", "FramelessHelper\qmake\FramelessHelperWidgets.vcxproj", "{61651EC8-A30E-3935-BD0C-A01AE3EE0AD9}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -20,10 +19,14 @@ Global
{3FE96A33-2BB7-4686-A710-3EB8E3BBD709}.Debug|x64.Build.0 = Debug|x64 {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.ActiveCfg = Release|x64
{3FE96A33-2BB7-4686-A710-3EB8E3BBD709}.Release|x64.Build.0 = Release|x64 {3FE96A33-2BB7-4686-A710-3EB8E3BBD709}.Release|x64.Build.0 = Release|x64
{B982E745-C0B1-46B3-A27B-743AF105F2D0}.Debug|x64.ActiveCfg = Debug|x64 {F1BC586E-F1ED-320C-899E-76CCB5660AE1}.Debug|x64.ActiveCfg = Debug|x64
{B982E745-C0B1-46B3-A27B-743AF105F2D0}.Debug|x64.Build.0 = Debug|x64 {F1BC586E-F1ED-320C-899E-76CCB5660AE1}.Debug|x64.Build.0 = Debug|x64
{B982E745-C0B1-46B3-A27B-743AF105F2D0}.Release|x64.ActiveCfg = Release|x64 {F1BC586E-F1ED-320C-899E-76CCB5660AE1}.Release|x64.ActiveCfg = Release|x64
{B982E745-C0B1-46B3-A27B-743AF105F2D0}.Release|x64.Build.0 = Release|x64 {F1BC586E-F1ED-320C-899E-76CCB5660AE1}.Release|x64.Build.0 = Release|x64
{61651EC8-A30E-3935-BD0C-A01AE3EE0AD9}.Debug|x64.ActiveCfg = Debug|x64
{61651EC8-A30E-3935-BD0C-A01AE3EE0AD9}.Debug|x64.Build.0 = Debug|x64
{61651EC8-A30E-3935-BD0C-A01AE3EE0AD9}.Release|x64.ActiveCfg = Release|x64
{61651EC8-A30E-3935-BD0C-A01AE3EE0AD9}.Release|x64.Build.0 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -68,7 +68,8 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <ClCompile>
<LanguageStandard>stdcpp17</LanguageStandard> <LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(SolutionDir)ArchitectureColoredPainting\src\Editor\RightBar;$(SolutionDir)ArchitectureColoredPainting\src\Editor\;$(SolutionDir)QGoodWindow;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(SolutionDir)ArchitectureColoredPainting\src\Editor\RightBar;$(SolutionDir)ArchitectureColoredPainting\src\Editor\;$(SolutionDir)QGoodWindow;$(SolutionDir)FramelessHelper\include;$(SolutionDir)FramelessHelper\qmake\inc\core;$(SolutionDir)FramelessHelper\include\FramelessHelper\Core;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>FRAMELESSHELPER_WIDGETS_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile> </ClCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration">
@ -206,8 +207,8 @@
<QtMoc Include="src\Renderer\RendererGLWidget.h" /> <QtMoc Include="src\Renderer\RendererGLWidget.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\QGoodWindow\QGoodWindow.vcxproj"> <ProjectReference Include="..\FramelessHelper\qmake\FramelessHelperWidgets.vcxproj">
<Project>{b982e745-c0b1-46b3-a27b-743af105f2d0}</Project> <Project>{61651ec8-a30e-3935-bd0c-a01ae3ee0ad9}</Project>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -38,7 +38,7 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">#windowFrame{ background-color:palette(Window);}</string> <string notr="true"/>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing"> <property name="spacing">
@ -56,108 +56,13 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>1</number> <number>1</number>
</property> </property>
<item>
<widget class="TitleWidget" name="windowTitlebar" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">#windowTitlebar{border: 0px none palette(base); border-top-left-radius:5px; border-top-right-radius:5px; background-color:palette(shadow); height:20px;}</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0,0,0">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="spacer">
<property name="minimumSize">
<size>
<width>4</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>4</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="IconWidget" name="icon" native="true">
<property name="minimumSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="styleSheet">
<string notr="true">#icon {background-color:palette(shadow);}</string>
</property>
</widget>
</item>
<item>
<widget class="TitleWidget" name="titleWidget" native="true"/>
</item>
<item>
<widget class="CaptionButton" name="minimizeButton" native="true"/>
</item>
<item>
<widget class="CaptionButton" name="restoreButton" native="true"/>
</item>
<item>
<widget class="CaptionButton" name="maximizeButton" native="true"/>
</item>
<item>
<widget class="CaptionButton" name="closeButton" native="true"/>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QWidget" name="windowContent" native="true"> <widget class="QWidget" name="windowContent" native="true">
<property name="autoFillBackground"> <property name="autoFillBackground">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">#windowContent{ <string notr="true"/>
border: 0px none palette(base);
border-radius:0px 0px 5px 5px;
}</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing"> <property name="spacing">
@ -184,25 +89,6 @@
</layout> </layout>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>CaptionButton</class>
<extends>QWidget</extends>
<header>CaptionButton.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>IconWidget</class>
<extends>QWidget</extends>
<header>IconWidget.h</header>
</customwidget>
<customwidget>
<class>TitleWidget</class>
<extends>QWidget</extends>
<header>TitleWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>

View File

@ -13,7 +13,10 @@
<property name="windowTitle"> <property name="windowTitle">
<string>RendererWidget</string> <string>RendererWidget</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1"> <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -27,21 +30,75 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<spacer name="verticalSpacer"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="orientation"> <property name="sizeConstraint">
<enum>Qt::Vertical</enum> <enum>QLayout::SetDefaultConstraint</enum>
</property> </property>
<property name="sizeType"> <item>
<enum>QSizePolicy::Fixed</enum> <widget class="QPushButton" name="openButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="minimumSize">
<size> <size>
<width>20</width> <width>0</width>
<height>40</height> <height>40</height>
</size> </size>
</property> </property>
<property name="text">
<string>打开</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer> </spacer>
</item> </item>
</layout>
</item>
<item>
<widget class="QMenuBar" name="menuBar">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<widget class="QMenu" name="menu">
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="title">
<string>打开</string>
</property>
</widget>
<addaction name="menu"/>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0"> <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
<property name="sizeConstraint"> <property name="sizeConstraint">

View File

@ -3,8 +3,14 @@
#include "qslider.h" #include "qslider.h"
#include <QPushButton> #include <QPushButton>
#include "NavigationBarWidget.h" #include "NavigationBarWidget.h"
#include <FramelessHelper/Core/utils.h>
#include <FramelessHelper/Widgets/standardtitlebar.h>
#include <FramelessHelper/Widgets/standardsystembutton.h>
#include <FramelessHelper/Widgets/framelesswidgetshelper.h>
#include <qDebug> #include <qDebug>
FRAMELESSHELPER_USE_NAMESPACE
inline void fontTheme() inline void fontTheme()
{ {
QFont defaultFont = qApp->font(); QFont defaultFont = qApp->font();
@ -118,367 +124,54 @@ CentralWidget::~CentralWidget()
} }
MainWindow::MainWindow(QWidget* parent) MainWindow::MainWindow(QWidget* parent, const Qt::WindowFlags flags)
: QGoodWindow(parent)
{ {
m_titleBar = new StandardTitleBar(this);
m_titleBar->setWindowIconVisible(true);
constexpr int kTitleBarHeight = 28;
m_titleBar->setFixedHeight(kTitleBarHeight);
m_titleBar->minimizeButton()->setFixedHeight(kTitleBarHeight);
m_titleBar->maximizeButton()->setFixedHeight(kTitleBarHeight);
m_titleBar->closeButton()->setFixedHeight(kTitleBarHeight);
m_titleBar->setTitleFont(QFont(QString("Segoe UI, Microsoft YaHei UI"), 10));
m_titleBar->chromePalette()->setTitleBarActiveBackgroundColor(QColor(0,0,0,0));
m_titleBar->chromePalette()->setTitleBarInactiveBackgroundColor(QColor(0, 0, 0, 0));
// 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_window = new FramelessWindow(this);
m_central_widget = new CentralWidget(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); m_window->ui->windowContent->layout()->addWidget(m_central_widget);
connect(this, &QGoodWindow::windowTitleChanged, this, [=](const QString& title) { setMenuWidget(m_titleBar);
m_window->ui->titleWidget->setText(title); setCentralWidget(m_window);
FramelessWidgetsHelper* helper = FramelessWidgetsHelper::get(this);
helper->setTitleBarWidget(m_titleBar);
#ifndef Q_OS_MACOS
helper->setSystemButton(m_titleBar->minimizeButton(), Global::SystemButtonType::Minimize);
helper->setSystemButton(m_titleBar->maximizeButton(), Global::SystemButtonType::Maximize);
helper->setSystemButton(m_titleBar->closeButton(), Global::SystemButtonType::Close);
#endif // Q_OS_MACOS
connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper]() {
helper->moveWindowToDesktopCenter();
}); });
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")); setWindowIcon(QIcon(":/images/icon.png"));
setWindowTitle("ArchitectureColoredPainting"); setWindowTitle("ArchitectureColoredPainting");
resize(m_central_widget->size()); resize(m_central_widget->size());
setCentralWidget(m_window); //setCentralWidget(m_window);
move(QGuiApplication::primaryScreen()->availableGeometry().center() - rect().center()); //move(QGuiApplication::primaryScreen()->availableGeometry().center() - rect().center());
} }
MainWindow::~MainWindow() 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<MSG*>(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) void MainWindow::closeEvent(QCloseEvent* event)
{ {

View File

@ -4,8 +4,12 @@
#include "ui_MainWindow.h" #include "ui_MainWindow.h"
#include "ui_FramelessWindow.h" #include "ui_FramelessWindow.h"
#define QGOODWINDOW #include <FramelessHelper/Widgets/framelessmainwindow.h>
#include <QGoodWindow>
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBar;
FRAMELESSHELPER_END_NAMESPACE
class FramelessWindow : public QWidget class FramelessWindow : public QWidget
{ {
@ -31,26 +35,21 @@ public:
}; };
class MainWindow : public QGoodWindow class MainWindow : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessMainWindow)
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY_MOVE(MainWindow)
public: public:
explicit MainWindow(QWidget* parent = nullptr); explicit MainWindow(QWidget * parent = nullptr, const Qt::WindowFlags flags = {});
~MainWindow(); ~MainWindow() override;
private: private:
//Functions FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar)* m_titleBar = nullptr;
void styleWindow(); Ui::MainWindowClass ui;
void captionButtonStateChanged(const QGoodWindow::CaptionButtonState& state);
bool event(QEvent* event);
bool nativeEvent(const QByteArray& eventType, void* message, long* result);
void closeEvent(QCloseEvent* event); void closeEvent(QCloseEvent* event);
//Variables
FramelessWindow* m_window; FramelessWindow* m_window;
CentralWidget* m_central_widget; CentralWidget* m_central_widget;
bool m_draw_borders;
bool m_dark;
}; };

View File

@ -83,6 +83,7 @@ QMatrix4x4 Light::getLightSpaceMatrix(const float nearPlane, const float farPlan
minZ = std::min(minZ, -trf.z()); minZ = std::min(minZ, -trf.z());
maxZ = std::max(maxZ, -trf.z()); maxZ = std::max(maxZ, -trf.z());
} }
if (model != nullptr)
for (const QVector3D& v : model->AABB) for (const QVector3D& v : model->AABB)
{ {
const QVector4D trf = lightView * QVector4D(v, 1); const QVector4D trf = lightView * QVector4D(v, 1);

View File

@ -18,7 +18,7 @@ namespace Renderer
std::vector<float> shadowCascadeLevels; std::vector<float> shadowCascadeLevels;
float blendRatio = 0.3; float blendRatio = 0.3;
std::vector<float> frustumSizes; std::vector<float> frustumSizes;
Model* model; Model* model = nullptr;
Light(Camera* camera); Light(Camera* camera);
std::vector<QVector4D> getFrustumCornersWorldSpace(const QMatrix4x4& projview); std::vector<QVector4D> getFrustumCornersWorldSpace(const QMatrix4x4& projview);
std::vector<QVector4D> getFrustumCornersWorldSpace(const QMatrix4x4& proj, const QMatrix4x4& view); std::vector<QVector4D> getFrustumCornersWorldSpace(const QMatrix4x4& proj, const QMatrix4x4& view);

View File

@ -1,4 +1,5 @@
#include "Mesh.h" #include "Mesh.h"
#include <qDebug>
using namespace Renderer; using namespace Renderer;
Mesh::Mesh(QOpenGLFunctions_4_5_Compatibility* glFunc, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* shadowProgram, aiMatrix4x4 model) Mesh::Mesh(QOpenGLFunctions_4_5_Compatibility* glFunc, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* shadowProgram, aiMatrix4x4 model)
: glFunc(glFunc) : glFunc(glFunc)
@ -35,6 +36,7 @@ void Mesh::draw()
QOpenGLVertexArrayObject::Binder bind(&VAO); QOpenGLVertexArrayObject::Binder bind(&VAO);
shaderProgram->setUniformValue("model", model); shaderProgram->setUniformValue("model", model);
EBO.bind();
glFunc->glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); glFunc->glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
shaderProgram->release(); shaderProgram->release();
@ -59,6 +61,7 @@ void Mesh::drawShadow()
QOpenGLVertexArrayObject::Binder bind(&VAO); QOpenGLVertexArrayObject::Binder bind(&VAO);
shadowProgram->setUniformValue("model", model); shadowProgram->setUniformValue("model", model);
EBO.bind();
glFunc->glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); glFunc->glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
shadowProgram->release(); shadowProgram->release();

View File

@ -35,36 +35,15 @@ Model::Model(QString path, QOpenGLContext* context, QOpenGLShaderProgram* shader
processNode(scene->mRootNode, scene); processNode(scene->mRootNode, scene);
} }
Model::Model(QString path, QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* paintingProgram, QOpenGLShaderProgram* shadowProgram, PaintingHelper* paintingHelper) Model::Model(QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* paintingProgram, QOpenGLShaderProgram* shadowProgram, PaintingHelper* paintingHelper)
: context(context) : context(context)
, glFunc(context->versionFunctions<QOpenGLFunctions_4_5_Compatibility>()) , glFunc(context->versionFunctions<QOpenGLFunctions_4_5_Compatibility>())
, shaderProgram(shaderProgram) , shaderProgram(shaderProgram)
, paintingProgram(paintingProgram) , paintingProgram(paintingProgram)
, shadowProgram(shadowProgram) , shadowProgram(shadowProgram)
, paintingHelper(paintingHelper) , paintingHelper(paintingHelper)
, directory(path)
{ {
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(directory.absolutePath().toUtf8(), aiProcess_Triangulate | aiProcess_FlipUVs);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
{
qDebug() << "ERROR::ASSIMP::" << importer.GetErrorString() << endl;
return;
}
qDebug() << directory.absolutePath() << "Loaded Successfully";
qDebug() << "NumMeshes: " << scene->mNumMeshes;
qDebug() << "NumMaterials: " << scene->mNumMaterials;
qDebug() << "NumTextures: " << scene->mNumTextures;
directory.cdUp();
processNode(scene->mRootNode, scene);
AABB.push_back(QVector3D(minX, minY, minZ));
AABB.push_back(QVector3D(minX, minY, maxZ));
AABB.push_back(QVector3D(minX, maxY, minZ));
AABB.push_back(QVector3D(minX, maxY, maxZ));
AABB.push_back(QVector3D(maxX, minY, minZ));
AABB.push_back(QVector3D(maxX, minY, maxZ));
AABB.push_back(QVector3D(maxX, maxY, minZ));
AABB.push_back(QVector3D(maxX, maxY, maxZ));
} }
Model::~Model() //Ïú»Ù¶ÔÏó Model::~Model() //Ïú»Ù¶ÔÏó
@ -103,6 +82,32 @@ Model* Model::createModel(QString path, QOpenGLContext* context, QOpenGLShaderPr
return new Model(path, context, shaderProgram); return new Model(path, context, shaderProgram);
} }
void Renderer::Model::loadModel(QString path)
{
directory = path;
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(directory.absolutePath().toUtf8(), aiProcess_Triangulate | aiProcess_FlipUVs);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
{
qDebug() << "ERROR::ASSIMP::" << importer.GetErrorString() << endl;
return;
}
qDebug() << directory.absolutePath() << "Loaded Successfully";
qDebug() << "NumMeshes: " << scene->mNumMeshes;
qDebug() << "NumMaterials: " << scene->mNumMaterials;
qDebug() << "NumTextures: " << scene->mNumTextures;
directory.cdUp();
processNode(scene->mRootNode, scene);
AABB.push_back(QVector3D(minX, minY, minZ));
AABB.push_back(QVector3D(minX, minY, maxZ));
AABB.push_back(QVector3D(minX, maxY, minZ));
AABB.push_back(QVector3D(minX, maxY, maxZ));
AABB.push_back(QVector3D(maxX, minY, minZ));
AABB.push_back(QVector3D(maxX, minY, maxZ));
AABB.push_back(QVector3D(maxX, maxY, minZ));
AABB.push_back(QVector3D(maxX, maxY, maxZ));
}
void Model::processNode(aiNode* node, const aiScene* scene, aiMatrix4x4 mat4) void Model::processNode(aiNode* node, const aiScene* scene, aiMatrix4x4 mat4)
{ {

View File

@ -16,7 +16,8 @@ namespace Renderer
void drawShadow(); void drawShadow();
void destroy(); void destroy();
static Model* createModel(QString path, QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram); static Model* createModel(QString path, QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram);
Model(QString path, QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* paintingProgram, QOpenGLShaderProgram* shadowProgram, PaintingHelper* paintingHelper); void loadModel(QString path);
Model(QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* paintingProgram, QOpenGLShaderProgram* shadowProgram, PaintingHelper* paintingHelper);
private: private:
Model(QString path, QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram); Model(QString path, QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram);

View File

@ -14,6 +14,7 @@ void PaintingMesh::draw()
shaderProgram->bind(); shaderProgram->bind();
QOpenGLVertexArrayObject::Binder bind(&VAO); QOpenGLVertexArrayObject::Binder bind(&VAO);
shaderProgram->setUniformValue("model", model); shaderProgram->setUniformValue("model", model);
EBO.bind();
glFunc->glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); glFunc->glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
shaderProgram->release(); shaderProgram->release();
@ -23,6 +24,7 @@ void PaintingMesh::drawShadow()
shadowProgram->bind(); shadowProgram->bind();
QOpenGLVertexArrayObject::Binder bind(&VAO); QOpenGLVertexArrayObject::Binder bind(&VAO);
shadowProgram->setUniformValue("model", model); shadowProgram->setUniformValue("model", model);
EBO.bind();
glFunc->glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); glFunc->glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
shadowProgram->release(); shadowProgram->release();
} }

View File

@ -63,6 +63,13 @@ void RendererGLWidget::stopTimer()
timerId = -1; timerId = -1;
} }
bool loadingModel = false;
void RendererGLWidget::setModel()
{
loadingModel = true;
}
void RendererGLWidget::setMainLightPitch(float pitch) void RendererGLWidget::setMainLightPitch(float pitch)
{ {
//qDebug() << "pitch" << pitch; //qDebug() << "pitch" << pitch;
@ -212,19 +219,8 @@ void RendererGLWidget::initializeGL()
finalProgramPtr->release(); finalProgramPtr->release();
paintingHelper = new PaintingHelper(QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_5_Compatibility>()); paintingHelper = new PaintingHelper(QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_5_Compatibility>());
model = new Model(context(), modelProgramPtr, paintingProgramPtr, shadowProgramPtr, paintingHelper);
model = new Model("Models/Sponza/Sponza.gltf", context(), modelProgramPtr, paintingProgramPtr, shadowProgramPtr, paintingHelper);
//model = new Model("E:\\3D Objects\\gallery_gltf\\gallery_gltf.gltf", context(), modelProgramPtr, paintingProgramPtr, shadowProgramPtr, paintingHelper);
light.model = model;
qDebug() << model->AABB;
paintingHelper->allocateBuffers();
paintingCompProgramPtr->bind();
paintingHelper->bindPaintingBuffers();
paintingCompProgramPtr->release();
quadVAO.create(); quadVAO.create();
@ -242,12 +238,11 @@ void RendererGLWidget::initializeGL()
quadVBO.allocate(vertex, sizeof(vertex)); quadVBO.allocate(vertex, sizeof(vertex));
quadVBO.bind(); quadVBO.bind();
QOpenGLFunctions_4_5_Compatibility* f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_5_Compatibility>(); glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat),
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat),
nullptr); nullptr);
f->glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat),
reinterpret_cast<void*>(3 * sizeof(GLfloat))); reinterpret_cast<void*>(3 * sizeof(GLfloat)));
quadVBO.release(); quadVBO.release();
@ -257,6 +252,19 @@ void RendererGLWidget::initializeGL()
void RendererGLWidget::paintGL() void RendererGLWidget::paintGL()
{ {
if (loadingModel)
{
model->loadModel("Models/Sponza/Sponza.gltf");
//model = new Model("E:\\3D Objects\\gallery_gltf\\gallery_gltf.gltf", context(), modelProgramPtr, paintingProgramPtr, shadowProgramPtr, paintingHelper);
light.model = model;
qDebug() << model->AABB;
paintingHelper->allocateBuffers();
paintingCompProgramPtr->bind();
paintingHelper->bindPaintingBuffers();
paintingCompProgramPtr->release();
loadingModel = false;
}
light.lightDirection.setX(cos(qDegreesToRadians(sunPitch)) * cos(qDegreesToRadians(sunYaw))); light.lightDirection.setX(cos(qDegreesToRadians(sunPitch)) * cos(qDegreesToRadians(sunYaw)));
light.lightDirection.setY(sin(qDegreesToRadians(sunPitch))); light.lightDirection.setY(sin(qDegreesToRadians(sunPitch)));
light.lightDirection.setZ(cos(qDegreesToRadians(sunPitch)) * sin(qDegreesToRadians(sunYaw))); light.lightDirection.setZ(cos(qDegreesToRadians(sunPitch)) * sin(qDegreesToRadians(sunYaw)));
@ -275,6 +283,7 @@ void RendererGLWidget::paintGL()
glViewport(0, 0, shadowMapResolution, shadowMapResolution); glViewport(0, 0, shadowMapResolution, shadowMapResolution);
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT);
//glCullFace(GL_FRONT); //glCullFace(GL_FRONT);
if (model != nullptr)
model->drawShadow(); model->drawShadow();
//glCullFace(GL_BACK); //glCullFace(GL_BACK);
@ -296,6 +305,7 @@ void RendererGLWidget::paintGL()
paintingProgramPtr->setUniformValue("projection", projection); paintingProgramPtr->setUniformValue("projection", projection);
paintingProgramPtr->setUniformValue("view", view); paintingProgramPtr->setUniformValue("view", view);
paintingProgramPtr->release(); paintingProgramPtr->release();
if (model != nullptr)
model->draw(); model->draw();

View File

@ -23,6 +23,7 @@ namespace Renderer
void startTimer(); void startTimer();
void stopTimer(); void stopTimer();
public slots: public slots:
void setModel();
void setMainLightPitch(float pitch); void setMainLightPitch(float pitch);
void setMainLightYaw(float yaw); void setMainLightYaw(float yaw);
protected: protected:
@ -63,7 +64,7 @@ namespace Renderer
GLuint lightSpaceMatricesUBO; GLuint lightSpaceMatricesUBO;
QOpenGLBuffer quadVBO; QOpenGLBuffer quadVBO;
QOpenGLVertexArrayObject quadVAO; QOpenGLVertexArrayObject quadVAO;
Model* model; Model* model = nullptr;
PaintingHelper* paintingHelper; PaintingHelper* paintingHelper;
}; };
} }

View File

@ -11,6 +11,8 @@ RendererWidget::RendererWidget(QWidget *parent)
ui.openGLWidget, &RendererGLWidget::setMainLightYaw); ui.openGLWidget, &RendererGLWidget::setMainLightYaw);
ui.horizontalSlider->setValue(105); ui.horizontalSlider->setValue(105);
ui.horizontalSlider_2->setValue(80); ui.horizontalSlider_2->setValue(80);
QObject::connect(ui.openButton, &QPushButton::clicked,
ui.openGLWidget, &RendererGLWidget::setModel);
} }
RendererWidget::~RendererWidget() RendererWidget::~RendererWidget()

View File

@ -2,6 +2,7 @@
#include "Renderer/Painting/CubicBezier.h" #include "Renderer/Painting/CubicBezier.h"
#include <QGuiApplication> #include <QGuiApplication>
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
#include <FramelessHelper/Core/private/framelessconfig_p.h>
using Renderer::CubicBezier; using Renderer::CubicBezier;
@ -10,11 +11,18 @@ extern "C"
_declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; _declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
} }
FRAMELESSHELPER_USE_NAMESPACE
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); //FramelessHelper::Widgets::initialize();
FramelessHelper::Core::initialize();
//QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
QApplication a(argc, argv); QApplication a(argc, argv);
FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
FramelessConfig::instance()->set(Global::Option::CenterWindowBeforeShow);
MainWindow w; MainWindow w;
w.show(); w.show();
return a.exec(); return a.exec();

View File

@ -0,0 +1 @@
ref: refs/heads/main

View File

@ -0,0 +1,16 @@
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
[remote "origin"]
url = https://github.com/wangwenx190/framelesshelper.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
[gui]
wmstate = normal
geometry = 1640x853+343+343 665 389

View File

@ -0,0 +1 @@
Unnamed repository; edit this file 'description' to name the repository.

View File

@ -0,0 +1,15 @@
#!/bin/sh
#
# An example hook script to check the commit log message taken by
# applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit. The hook is
# allowed to edit the commit message file.
#
# To enable this hook, rename this file to "applypatch-msg".
. git-sh-setup
commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
:

View File

@ -0,0 +1,24 @@
#!/bin/sh
#
# An example hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.
#
# To enable this hook, rename this file to "commit-msg".
# Uncomment the below to add a Signed-off-by line to the message.
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
# hook is more suited to it.
#
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
# This example catches duplicate Signed-off-by lines.
test "" = "$(grep '^Signed-off-by: ' "$1" |
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
echo >&2 Duplicate Signed-off-by lines.
exit 1
}

View File

@ -0,0 +1,173 @@
#!/usr/bin/perl
use strict;
use warnings;
use IPC::Open2;
# An example hook script to integrate Watchman
# (https://facebook.github.io/watchman/) with git to speed up detecting
# new and modified files.
#
# The hook is passed a version (currently 2) and last update token
# formatted as a string and outputs to stdout a new update token and
# all files that have been modified since the update token. Paths must
# be relative to the root of the working tree and separated by a single NUL.
#
# To enable this hook, rename this file to "query-watchman" and set
# 'git config core.fsmonitor .git/hooks/query-watchman'
#
my ($version, $last_update_token) = @ARGV;
# Uncomment for debugging
# print STDERR "$0 $version $last_update_token\n";
# Check the hook interface version
if ($version ne 2) {
die "Unsupported query-fsmonitor hook version '$version'.\n" .
"Falling back to scanning...\n";
}
my $git_work_tree = get_working_dir();
my $retry = 1;
my $json_pkg;
eval {
require JSON::XS;
$json_pkg = "JSON::XS";
1;
} or do {
require JSON::PP;
$json_pkg = "JSON::PP";
};
launch_watchman();
sub launch_watchman {
my $o = watchman_query();
if (is_work_tree_watched($o)) {
output_result($o->{clock}, @{$o->{files}});
}
}
sub output_result {
my ($clockid, @files) = @_;
# Uncomment for debugging watchman output
# open (my $fh, ">", ".git/watchman-output.out");
# binmode $fh, ":utf8";
# print $fh "$clockid\n@files\n";
# close $fh;
binmode STDOUT, ":utf8";
print $clockid;
print "\0";
local $, = "\0";
print @files;
}
sub watchman_clock {
my $response = qx/watchman clock "$git_work_tree"/;
die "Failed to get clock id on '$git_work_tree'.\n" .
"Falling back to scanning...\n" if $? != 0;
return $json_pkg->new->utf8->decode($response);
}
sub watchman_query {
my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
or die "open2() failed: $!\n" .
"Falling back to scanning...\n";
# In the query expression below we're asking for names of files that
# changed since $last_update_token but not from the .git folder.
#
# To accomplish this, we're using the "since" generator to use the
# recency index to select candidate nodes and "fields" to limit the
# output to file names only. Then we're using the "expression" term to
# further constrain the results.
if (substr($last_update_token, 0, 1) eq "c") {
$last_update_token = "\"$last_update_token\"";
}
my $query = <<" END";
["query", "$git_work_tree", {
"since": $last_update_token,
"fields": ["name"],
"expression": ["not", ["dirname", ".git"]]
}]
END
# Uncomment for debugging the watchman query
# open (my $fh, ">", ".git/watchman-query.json");
# print $fh $query;
# close $fh;
print CHLD_IN $query;
close CHLD_IN;
my $response = do {local $/; <CHLD_OUT>};
# Uncomment for debugging the watch response
# open ($fh, ">", ".git/watchman-response.json");
# print $fh $response;
# close $fh;
die "Watchman: command returned no output.\n" .
"Falling back to scanning...\n" if $response eq "";
die "Watchman: command returned invalid output: $response\n" .
"Falling back to scanning...\n" unless $response =~ /^\{/;
return $json_pkg->new->utf8->decode($response);
}
sub is_work_tree_watched {
my ($output) = @_;
my $error = $output->{error};
if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
$retry--;
my $response = qx/watchman watch "$git_work_tree"/;
die "Failed to make watchman watch '$git_work_tree'.\n" .
"Falling back to scanning...\n" if $? != 0;
$output = $json_pkg->new->utf8->decode($response);
$error = $output->{error};
die "Watchman: $error.\n" .
"Falling back to scanning...\n" if $error;
# Uncomment for debugging watchman output
# open (my $fh, ">", ".git/watchman-output.out");
# close $fh;
# Watchman will always return all files on the first query so
# return the fast "everything is dirty" flag to git and do the
# Watchman query just to get it over with now so we won't pay
# the cost in git to look up each individual file.
my $o = watchman_clock();
$error = $output->{error};
die "Watchman: $error.\n" .
"Falling back to scanning...\n" if $error;
output_result($o->{clock}, ("/"));
$last_update_token = $o->{clock};
eval { launch_watchman() };
return 0;
}
die "Watchman: $error.\n" .
"Falling back to scanning...\n" if $error;
return 1;
}
sub get_working_dir {
my $working_dir;
if ($^O =~ 'msys' || $^O =~ 'cygwin') {
$working_dir = Win32::GetCwd();
$working_dir =~ tr/\\/\//;
} else {
require Cwd;
$working_dir = Cwd::cwd();
}
return $working_dir;
}

View File

@ -0,0 +1,8 @@
#!/bin/sh
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-update".
exec git update-server-info

View File

@ -0,0 +1,14 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed
# by applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-applypatch".
. git-sh-setup
precommit="$(git rev-parse --git-path hooks/pre-commit)"
test -x "$precommit" && exec "$precommit" ${1+"$@"}
:

View File

@ -0,0 +1,49 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=$(git hash-object -t tree /dev/null)
fi
# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --type=bool hooks.allownonascii)
# Redirect output to stderr.
exec 1>&2
# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
# Note that the use of brackets around a tr range is ok here, (it's
# even required, for portability to Solaris 10's /usr/bin/tr), since
# the square bracket bytes happen to fall in the designated range.
test $(git diff --cached --name-only --diff-filter=A -z $against |
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
cat <<\EOF
Error: Attempt to add a non-ASCII file name.
This can cause problems if you want to work with people on other platforms.
To be portable it is advisable to rename the file.
If you know what you are doing you can disable this check using:
git config hooks.allownonascii true
EOF
exit 1
fi
# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --

View File

@ -0,0 +1,13 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git merge" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message to
# stderr if it wants to stop the merge commit.
#
# To enable this hook, rename this file to "pre-merge-commit".
. git-sh-setup
test -x "$GIT_DIR/hooks/pre-commit" &&
exec "$GIT_DIR/hooks/pre-commit"
:

View File

@ -0,0 +1,53 @@
#!/bin/sh
# An example hook script to verify what is about to be pushed. Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed. If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
# <local ref> <local sha1> <remote ref> <remote sha1>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).
remote="$1"
url="$2"
z40=0000000000000000000000000000000000000000
while read local_ref local_sha remote_ref remote_sha
do
if [ "$local_sha" = $z40 ]
then
# Handle delete
:
else
if [ "$remote_sha" = $z40 ]
then
# New branch, examine all commits
range="$local_sha"
else
# Update to existing branch, examine new commits
range="$remote_sha..$local_sha"
fi
# Check for WIP commit
commit=`git rev-list -n 1 --grep '^WIP' "$range"`
if [ -n "$commit" ]
then
echo >&2 "Found WIP commit in $local_ref, not pushing"
exit 1
fi
fi
done
exit 0

View File

@ -0,0 +1,169 @@
#!/bin/sh
#
# Copyright (c) 2006, 2008 Junio C Hamano
#
# The "pre-rebase" hook is run just before "git rebase" starts doing
# its job, and can prevent the command from running by exiting with
# non-zero status.
#
# The hook is called with the following parameters:
#
# $1 -- the upstream the series was forked from.
# $2 -- the branch being rebased (or empty when rebasing the current branch).
#
# This sample shows how to prevent topic branches that are already
# merged to 'next' branch from getting rebased, because allowing it
# would result in rebasing already published history.
publish=next
basebranch="$1"
if test "$#" = 2
then
topic="refs/heads/$2"
else
topic=`git symbolic-ref HEAD` ||
exit 0 ;# we do not interrupt rebasing detached HEAD
fi
case "$topic" in
refs/heads/??/*)
;;
*)
exit 0 ;# we do not interrupt others.
;;
esac
# Now we are dealing with a topic branch being rebased
# on top of master. Is it OK to rebase it?
# Does the topic really exist?
git show-ref -q "$topic" || {
echo >&2 "No such branch $topic"
exit 1
}
# Is topic fully merged to master?
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
if test -z "$not_in_master"
then
echo >&2 "$topic is fully merged to master; better remove it."
exit 1 ;# we could allow it, but there is no point.
fi
# Is topic ever merged to next? If so you should not be rebasing it.
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
only_next_2=`git rev-list ^master ${publish} | sort`
if test "$only_next_1" = "$only_next_2"
then
not_in_topic=`git rev-list "^$topic" master`
if test -z "$not_in_topic"
then
echo >&2 "$topic is already up to date with master"
exit 1 ;# we could allow it, but there is no point.
else
exit 0
fi
else
not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
/usr/bin/perl -e '
my $topic = $ARGV[0];
my $msg = "* $topic has commits already merged to public branch:\n";
my (%not_in_next) = map {
/^([0-9a-f]+) /;
($1 => 1);
} split(/\n/, $ARGV[1]);
for my $elem (map {
/^([0-9a-f]+) (.*)$/;
[$1 => $2];
} split(/\n/, $ARGV[2])) {
if (!exists $not_in_next{$elem->[0]}) {
if ($msg) {
print STDERR $msg;
undef $msg;
}
print STDERR " $elem->[1]\n";
}
}
' "$topic" "$not_in_next" "$not_in_master"
exit 1
fi
<<\DOC_END
This sample hook safeguards topic branches that have been
published from being rewound.
The workflow assumed here is:
* Once a topic branch forks from "master", "master" is never
merged into it again (either directly or indirectly).
* Once a topic branch is fully cooked and merged into "master",
it is deleted. If you need to build on top of it to correct
earlier mistakes, a new topic branch is created by forking at
the tip of the "master". This is not strictly necessary, but
it makes it easier to keep your history simple.
* Whenever you need to test or publish your changes to topic
branches, merge them into "next" branch.
The script, being an example, hardcodes the publish branch name
to be "next", but it is trivial to make it configurable via
$GIT_DIR/config mechanism.
With this workflow, you would want to know:
(1) ... if a topic branch has ever been merged to "next". Young
topic branches can have stupid mistakes you would rather
clean up before publishing, and things that have not been
merged into other branches can be easily rebased without
affecting other people. But once it is published, you would
not want to rewind it.
(2) ... if a topic branch has been fully merged to "master".
Then you can delete it. More importantly, you should not
build on top of it -- other people may already want to
change things related to the topic as patches against your
"master", so if you need further changes, it is better to
fork the topic (perhaps with the same name) afresh from the
tip of "master".
Let's look at this example:
o---o---o---o---o---o---o---o---o---o "next"
/ / / /
/ a---a---b A / /
/ / / /
/ / c---c---c---c B /
/ / / \ /
/ / / b---b C \ /
/ / / / \ /
---o---o---o---o---o---o---o---o---o---o---o "master"
A, B and C are topic branches.
* A has one fix since it was merged up to "next".
* B has finished. It has been fully merged up to "master" and "next",
and is ready to be deleted.
* C has not merged to "next" at all.
We would want to allow C to be rebased, refuse A, and encourage
B to be deleted.
To compute (1):
git rev-list ^master ^topic next
git rev-list ^master next
if these match, topic has not merged in next at all.
To compute (2):
git rev-list master..topic
if this is empty, it is fully merged to "master".
DOC_END

View File

@ -0,0 +1,24 @@
#!/bin/sh
#
# An example hook script to make use of push options.
# The example simply echoes all push options that start with 'echoback='
# and rejects all pushes when the "reject" push option is used.
#
# To enable this hook, rename this file to "pre-receive".
if test -n "$GIT_PUSH_OPTION_COUNT"
then
i=0
while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
do
eval "value=\$GIT_PUSH_OPTION_$i"
case "$value" in
echoback=*)
echo "echo from the pre-receive-hook: ${value#*=}" >&2
;;
reject)
exit 1
esac
i=$((i + 1))
done
fi

View File

@ -0,0 +1,42 @@
#!/bin/sh
#
# An example hook script to prepare the commit log message.
# Called by "git commit" with the name of the file that has the
# commit message, followed by the description of the commit
# message's source. The hook's purpose is to edit the commit
# message file. If the hook fails with a non-zero status,
# the commit is aborted.
#
# To enable this hook, rename this file to "prepare-commit-msg".
# This hook includes three examples. The first one removes the
# "# Please enter the commit message..." help message.
#
# The second includes the output of "git diff --name-status -r"
# into the message, just before the "git status" output. It is
# commented because it doesn't cope with --amend or with squashed
# commits.
#
# The third example adds a Signed-off-by line to the message, that can
# still be edited. This is rarely a good idea.
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
SHA1=$3
/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"
# case "$COMMIT_SOURCE,$SHA1" in
# ,|template,)
# /usr/bin/perl -i.bak -pe '
# print "\n" . `git diff --cached --name-status -r`
# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
# *) ;;
# esac
# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
# if test -z "$COMMIT_SOURCE"
# then
# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
# fi

View File

@ -0,0 +1,128 @@
#!/bin/sh
#
# An example hook script to block unannotated tags from entering.
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
#
# To enable this hook, rename this file to "update".
#
# Config
# ------
# hooks.allowunannotated
# This boolean sets whether unannotated tags will be allowed into the
# repository. By default they won't be.
# hooks.allowdeletetag
# This boolean sets whether deleting tags will be allowed in the
# repository. By default they won't be.
# hooks.allowmodifytag
# This boolean sets whether a tag may be modified after creation. By default
# it won't be.
# hooks.allowdeletebranch
# This boolean sets whether deleting branches will be allowed in the
# repository. By default they won't be.
# hooks.denycreatebranch
# This boolean sets whether remotely creating branches will be denied
# in the repository. By default this is allowed.
#
# --- Command line
refname="$1"
oldrev="$2"
newrev="$3"
# --- Safety check
if [ -z "$GIT_DIR" ]; then
echo "Don't run this script from the command line." >&2
echo " (if you want, you could supply GIT_DIR then run" >&2
echo " $0 <ref> <oldrev> <newrev>)" >&2
exit 1
fi
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
echo "usage: $0 <ref> <oldrev> <newrev>" >&2
exit 1
fi
# --- Config
allowunannotated=$(git config --type=bool hooks.allowunannotated)
allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch)
denycreatebranch=$(git config --type=bool hooks.denycreatebranch)
allowdeletetag=$(git config --type=bool hooks.allowdeletetag)
allowmodifytag=$(git config --type=bool hooks.allowmodifytag)
# check for no description
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
case "$projectdesc" in
"Unnamed repository"* | "")
echo "*** Project description file hasn't been set" >&2
exit 1
;;
esac
# --- Check types
# if $newrev is 0000...0000, it's a commit to delete a ref.
zero="0000000000000000000000000000000000000000"
if [ "$newrev" = "$zero" ]; then
newrev_type=delete
else
newrev_type=$(git cat-file -t $newrev)
fi
case "$refname","$newrev_type" in
refs/tags/*,commit)
# un-annotated tag
short_refname=${refname##refs/tags/}
if [ "$allowunannotated" != "true" ]; then
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
exit 1
fi
;;
refs/tags/*,delete)
# delete tag
if [ "$allowdeletetag" != "true" ]; then
echo "*** Deleting a tag is not allowed in this repository" >&2
exit 1
fi
;;
refs/tags/*,tag)
# annotated tag
if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
then
echo "*** Tag '$refname' already exists." >&2
echo "*** Modifying a tag is not allowed in this repository." >&2
exit 1
fi
;;
refs/heads/*,commit)
# branch
if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
echo "*** Creating a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/heads/*,delete)
# delete branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/remotes/*,commit)
# tracking branch
;;
refs/remotes/*,delete)
# delete tracking branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
exit 1
fi
;;
*)
# Anything else (is there anything else?)
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
exit 1
;;
esac
# --- Finished
exit 0

BIN
FramelessHelper/.git_/index Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~

View File

@ -0,0 +1,32 @@
# pack-refs with: peeled fully-peeled sorted
a7aea71b474138986ec72605f91359d78adac234 refs/remotes/origin/1.x
2f629a9e5be1d377b34912816969cd081f8b7709 refs/remotes/origin/2.0
1a3d1c82a1aaef372d30a7188809b887e22e3514 refs/remotes/origin/2.1
d13d74783f91d3d3c9c3fe7b245b7deae4bba33a refs/remotes/origin/2.1.1
45be647baf276932ebc37dab8cdefe14483c192a refs/remotes/origin/2.1.2
ff1605ee7b46361bf9dc167597ff39d384b07afd refs/remotes/origin/2.1.3
06653433d7a772e50d163df6dc4290e38a2a31dd refs/remotes/origin/2.1.4
5754f2ec20751016d9bcb475285dde70eb0f54ad refs/remotes/origin/2.1.5
a04fd53a38edd94e6d24a86d68976636d7b55359 refs/remotes/origin/2.1.6
a89f19af96a478d2775964da31791a92cb210dfc refs/remotes/origin/2.1.7
f4d20e5f062c4dc12756202a295460310be4bdfd refs/remotes/origin/altairwei/2.0
a23438b02bbefd24f9277496475e89ae8af9f575 refs/remotes/origin/main
a0a9b8d108cf13795f9e8aa17a16cbcdbc50c250 refs/tags/2.0.0
3c0209c979d5b6453e5ad79e9c41f6af0224480a refs/tags/2.0.1
3d7576e06255c92de54465f7eadc631292d350a8 refs/tags/2.0.2
fd2b3f5e84658f45e4319c4055a69b0e7a94ceda refs/tags/2.0.3
1a3d1c82a1aaef372d30a7188809b887e22e3514 refs/tags/2.1.0
d13d74783f91d3d3c9c3fe7b245b7deae4bba33a refs/tags/2.1.1
45be647baf276932ebc37dab8cdefe14483c192a refs/tags/2.1.2
ff1605ee7b46361bf9dc167597ff39d384b07afd refs/tags/2.1.3
06653433d7a772e50d163df6dc4290e38a2a31dd refs/tags/2.1.4
5754f2ec20751016d9bcb475285dde70eb0f54ad refs/tags/2.1.5
a04fd53a38edd94e6d24a86d68976636d7b55359 refs/tags/2.1.6
a89f19af96a478d2775964da31791a92cb210dfc refs/tags/2.1.7
7a35b09a74b1836f92e256d2241583f283fa45e5 refs/tags/2.2.0
f052a087dec97925661bdfe8f8bc5f02e68488cc refs/tags/2.2.1
78638a3e2350955392498679cf0257b58b391aed refs/tags/2.3.0
3842f4e353447e724ca049cf74e4dea01fce4ac4 refs/tags/2.3.1
ddb2dcc7f70d44b767d065418eb582ac9ec56048 refs/tags/2.3.2
524f65cc9bc1382333cd7acf0d31ff88cf6c30fd refs/tags/2.3.3
9c08c69a4df401c318fccf801797c0c02b9abd68 refs/tags/2.3.4

View File

@ -0,0 +1 @@
a23438b02bbefd24f9277496475e89ae8af9f575

View File

@ -0,0 +1 @@
ref: refs/remotes/origin/main

View File

@ -0,0 +1,70 @@
name: "CI: Build Test"
on:
push:
branches:
- main
paths-ignore:
- "**.md"
- "**.png"
- "**.jpg"
- "**.jpeg"
- ".gitignore"
workflow_dispatch:
jobs:
build:
name: Build
strategy:
matrix:
qt-version: [5.15.2, 6.4.1]
library-type: [shared, static]
platform: [windows-latest, ubuntu-latest, macos-latest]
include:
- platform: windows-latest
CC: cl
CXX: cl
- platform: ubuntu-latest
CC: gcc
CXX: g++
- platform: macos-latest
CC: clang
CXX: clang++
- library-type: shared
lib_type_flag: -DFRAMELESSHELPER_BUILD_STATIC=OFF
- library-type: static
lib_type_flag: -DFRAMELESSHELPER_BUILD_STATIC=ON
runs-on: ${{ matrix.platform }}
steps:
- name: Check out repository
uses: actions/checkout@v3
- name: Install Qt SDK
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt-version }}
cache: true
- name: Set up Ninja
uses: seanmiddleditch/gha-setup-ninja@v3
with:
version: 1.11.1 # Current latest version.
- name: Set up MSVC environment
if: ${{ matrix.platform == 'windows-latest' }}
uses: ilammy/msvc-dev-cmd@v1
- name: Install Linux dependencies
if: ${{ matrix.platform == 'ubuntu-latest' }}
run: |
sudo apt install -y libgl1-mesa-dev libxcb1-dev libgtk-3-dev
- name: Build library with CMake
run: |
mkdir ci-test-build
cd ci-test-build
cmake -DCMAKE_C_COMPILER=${{ matrix.CC }} -DCMAKE_CXX_COMPILER=${{ matrix.CXX }} -DCMAKE_INSTALL_PREFIX=../ci-test-install -DCMAKE_BUILD_TYPE=Release -DFRAMELESSHELPER_BUILD_EXAMPLES=ON ${{ matrix.lib_type_flag }} -GNinja ..
cmake --build . --target all --config Release --parallel
cmake --install . --config Release --strip

84
FramelessHelper/.gitignore vendored Normal file
View File

@ -0,0 +1,84 @@
# C++ objects and libs
*.slo
*.lo
*.o
*.a
*.la
*.lai
*.so
*.so.*
*.dll
*.dylib
# Qt-es
object_script.*.Release
object_script.*.Debug
*_plugin_import.cpp
/.qmake.cache
/.qmake.stash
*.pro.user
*.pro.user.*
*.qbs.user
*.qbs.user.*
*.moc
moc_*.cpp
moc_*.h
qrc_*.cpp
ui_*.h
*.qmlc
*.jsc
Makefile*
*build-*
*.qm
*.prl
# Qt unit tests
target_wrapper.*
# QtCreator
*.autosave
# QtCreator Qml
*.qmlproject.user
*.qmlproject.user.*
# QtCreator CMake
CMakeLists.txt.user*
# QtCreator 4.8< compilation database
compile_commands.json
# QtCreator local machine specific files for imported projects
*creator.user*
# My
[Bb]in/
[Bb]in64/
[Bb]uild*/
*.7z
*.zip
*.rar
*.tar
*.gz
*.xz
*.exe
*.lib
*.pdb
*.ilk
*.exp
*.obj
build.user.bat
build.user.sh
user.conf
[Dd]oc/
[Dd]ocs/
Thumbs.db
*.rc
*.bin
*.run
.qmake.conf
*.res
.DS_Store
.vscode/
.vs/
.cmake.conf

View File

@ -0,0 +1,226 @@
#[[
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
cmake_minimum_required(VERSION 3.20)
project(FramelessHelper
VERSION 2.3.5.0
DESCRIPTION "Cross-platform window customization framework for Qt Widgets and Qt Quick."
HOMEPAGE_URL "https://github.com/wangwenx190/framelesshelper/"
LANGUAGES CXX
)
option(FRAMELESSHELPER_BUILD_STATIC "Build FramelessHelper as a static library." OFF)
option(FRAMELESSHELPER_BUILD_WIDGETS "Build FramelessHelper's Widgets module." ON)
option(FRAMELESSHELPER_BUILD_QUICK "Build FramelessHelper's Quick module." ON)
option(FRAMELESSHELPER_BUILD_EXAMPLES "Build FramelessHelper demo applications." OFF)
option(FRAMELESSHELPER_EXAMPLES_DEPLOYQT "Deploy the Qt framework after building the demo projects." OFF)
option(FRAMELESSHELPER_NO_DEBUG_OUTPUT "Suppress the debug messages from FramelessHelper." OFF)
option(FRAMELESSHELPER_NO_BUNDLE_RESOURCE "Do not bundle any resources within FramelessHelper." OFF)
option(FRAMELESSHELPER_NO_PRIVATE "Do not use any private functionalities from Qt." OFF)
option(FRAMELESSHELPER_ENABLE_VCLTL "MSVC only: link to the system MSVCRT/UCRT and get rid of API sets." OFF)
option(FRAMELESSHELPER_NO_PERMISSIVE_CHECKS "MSVC only: disable the additional permissive checks." OFF)
option(FRAMELESSHELPER_NO_INSTALL "Don't install any files." OFF)
if(FRAMELESSHELPER_NO_BUNDLE_RESOURCE)
message(WARNING "Nothing will be embeded into FramelessHelper, the chrome buttons will have no icon.")
endif()
if(FRAMELESSHELPER_ENABLE_VCLTL AND NOT MSVC)
message(WARNING "VC-LTL is only available for the MSVC toolchain.")
endif()
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
if(NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE)
# MinGW has many bugs when LTO is enabled, and they are all very
# hard to workaround, so just don't enable LTO at all for MinGW.
if(NOT (MINGW OR FRAMELESSHELPER_BUILD_STATIC))
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
endif()
endif()
if(NOT DEFINED CMAKE_DEBUG_POSTFIX)
if(WIN32)
set(CMAKE_DEBUG_POSTFIX d)
else()
set(CMAKE_DEBUG_POSTFIX _debug)
endif()
endif()
if(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
endif()
if(NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
endif()
if(NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
endif()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(PROJECT_VERSION_COMMIT "UNKNOWN")
# Get a git hash value. We do not want to use git command here
# because we don't want to make git a build-time dependency.
if(EXISTS "${PROJECT_SOURCE_DIR}/.git/HEAD")
file(READ "${PROJECT_SOURCE_DIR}/.git/HEAD" PROJECT_VERSION_COMMIT)
string(STRIP "${PROJECT_VERSION_COMMIT}" PROJECT_VERSION_COMMIT)
if(PROJECT_VERSION_COMMIT MATCHES "^ref: (.*)")
set(HEAD "${CMAKE_MATCH_1}")
if(EXISTS "${PROJECT_SOURCE_DIR}/.git/${HEAD}")
file(READ "${PROJECT_SOURCE_DIR}/.git/${HEAD}" PROJECT_VERSION_COMMIT)
string(STRIP "${PROJECT_VERSION_COMMIT}" PROJECT_VERSION_COMMIT)
else()
file(READ "${PROJECT_SOURCE_DIR}/.git/packed-refs" PACKED_REFS)
string(REGEX REPLACE ".*\n([0-9a-f]+) ${HEAD}\n.*" "\\1" PROJECT_VERSION_COMMIT "\n${PACKED_REFS}")
endif()
endif()
endif()
set(PROJECT_COMPILE_DATETIME "UNKNOWN")
string(TIMESTAMP PROJECT_COMPILE_DATETIME UTC)
if(MSVC)
string(REGEX REPLACE "[-|/]GR-? " " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
string(REGEX REPLACE "[-|/]EHs-?c-? " " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
string(REGEX REPLACE "[-|/]W[0|1|2|3|4|all] " " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
string(APPEND CMAKE_CXX_FLAGS " /GR /EHsc /W4 ")
set(CMAKE_RC_FLAGS "/c65001 /DWIN32 /nologo")
if(MSVC_VERSION GREATER_EQUAL 1920) # Visual Studio 2019 version 16.0
string(REGEX REPLACE "[-|/]Ob[0|1|2|3] " " " CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
string(APPEND CMAKE_CXX_FLAGS_RELEASE " /Ob3 ")
endif()
if(FRAMELESSHELPER_ENABLE_VCLTL)
include(src/core/VC-LTL.cmake)
if("x${SupportLTL}" STREQUAL "xtrue")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>" CACHE STRING "" FORCE)
endif()
endif()
endif()
if(NOT DEFINED QML_IMPORT_PATH)
set(QML_IMPORT_PATH)
endif()
list(APPEND QML_IMPORT_PATH "${PROJECT_BINARY_DIR}/qml")
list(REMOVE_DUPLICATES QML_IMPORT_PATH)
set(QML_IMPORT_PATH ${QML_IMPORT_PATH} CACHE STRING "Qt Creator extra QML import paths" FORCE)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Gui)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui)
find_package(QT NAMES Qt6 Qt5 QUIET COMPONENTS Widgets Quick)
find_package(Qt${QT_VERSION_MAJOR} QUIET COMPONENTS Widgets Quick)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
NO_CHECK_REQUIRED_COMPONENTS_MACRO
)
if(NOT FRAMELESSHELPER_NO_INSTALL)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)
endif()
if(TARGET Qt${QT_VERSION_MAJOR}::Core AND TARGET Qt${QT_VERSION_MAJOR}::Gui)
add_subdirectory(src)
endif()
if(FRAMELESSHELPER_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
message("#######################################")
message("CMake version: ${CMAKE_VERSION}")
message("Host system: ${CMAKE_HOST_SYSTEM}")
message("Host processor: ${CMAKE_HOST_SYSTEM_PROCESSOR}")
#message("C compiler: ${CMAKE_C_COMPILER_ID} (${CMAKE_C_COMPILER})") # Currently we are not using any C compilers.
#message("C compiler version: ${CMAKE_C_COMPILER_VERSION}")
message("C++ compiler: ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER})")
message("C++ compiler version: ${CMAKE_CXX_COMPILER_VERSION}")
message("Linker: ${CMAKE_LINKER}")
message("Make program: ${CMAKE_MAKE_PROGRAM}")
message("Build type: ${CMAKE_BUILD_TYPE}")
message("Install prefix: ${CMAKE_INSTALL_PREFIX}")
message("#######################################")
set(__qt_inst_dir)
if(DEFINED Qt6_DIR)
set(__qt_inst_dir "${Qt6_DIR}")
else()
set(__qt_inst_dir "${Qt5_DIR}")
endif()
# /whatever/Qt/6.4.0/gcc_64/lib/cmake/Qt6
set(__qt_inst_dir ${__qt_inst_dir}/../../..)
cmake_path(NORMAL_PATH __qt_inst_dir)
message("Qt install dir: ${__qt_inst_dir}")
message("Qt version: ${QT_VERSION}")
get_target_property(__qt_type Qt${QT_VERSION_MAJOR}::Core TYPE)
if(__qt_type STREQUAL "STATIC_LIBRARY")
set(__qt_type static)
else()
set(__qt_type shared)
endif()
message("Qt library type: ${__qt_type}")
message("#######################################")
message("FramelessHelper version: ${PROJECT_VERSION}")
message("FramelessHelper commit hash: ${PROJECT_VERSION_COMMIT}")
message("FramelessHelper configure date and time: ${PROJECT_COMPILE_DATETIME} (UTC)")
message("Build the static version of FramelessHelper: ${FRAMELESSHELPER_BUILD_STATIC}")
message("Build the FramelessHelper::Widgets module: ${FRAMELESSHELPER_BUILD_WIDGETS}")
message("Build the FramelessHelper::Quick module: ${FRAMELESSHELPER_BUILD_QUICK}")
message("Build the FramelessHelper demo applications: ${FRAMELESSHELPER_BUILD_EXAMPLES}")
message("Deploy Qt libraries after compilation: ${FRAMELESSHELPER_EXAMPLES_DEPLOYQT}")
message("Suppress debug messages from FramelessHelper: ${FRAMELESSHELPER_NO_DEBUG_OUTPUT}")
message("Do not bundle any resources within FramelessHelper: ${FRAMELESSHELPER_NO_BUNDLE_RESOURCE}")
message("Do not use any private functionalities from Qt: ${FRAMELESSHELPER_NO_PRIVATE}")
message("#######################################")

View File

@ -0,0 +1,35 @@
#[[
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
@PACKAGE_INIT@
set(_@PROJECT_NAME@_supported_components Core Widgets Quick)
foreach(_comp ${@PROJECT_NAME@_FIND_COMPONENTS})
if(NOT _comp IN_LIST _@PROJECT_NAME@_supported_components)
set(@PROJECT_NAME@_FOUND FALSE)
set(@PROJECT_NAME@_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}")
endif()
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@${_comp}Targets.cmake")
endforeach()

View File

@ -1,7 +1,6 @@
/* MIT License
The MIT License (MIT)
Copyright © 2021 Antonio Dias Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@ -20,6 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/
#include "src/qgoodwindow.h"

393
FramelessHelper/README.md Normal file
View File

@ -0,0 +1,393 @@
# FramelessHelper 2.x
[![CI: Build Test](https://github.com/wangwenx190/framelesshelper/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/wangwenx190/framelesshelper/actions/workflows/ci.yml)
Cross-platform window customization framework for Qt Widgets and Qt Quick. Supports Windows, Linux and macOS.
## Join with Us :triangular_flag_on_post:
You can join our [Discord channel](https://discord.gg/grrM4Tmesy) to communicate with us. You can share your findings, thoughts and ideas on improving / implementing FramelessHelper functionalities on more platforms and apps!
## HELP WANTED!
1. The current CMake package code is not complete and still has some small issues. Need some experienced CMake developers to help me improve it!
2. The current Linux/X11 implementation is not stable enough and still has some bugs. Need some experienced Linux developers to help me improve it!
3. The current macOS implementation is not stable enough and still has some bugs. Need some experienced macOS developers to help me improve it!
## Roadmap
- Common: Add cross-platform customizable system menu for both Qt Widgets and Qt Quick. Also supports both light and dark theme.
- Examples: Add QtWebEngine based demo projects for both Qt Widgets and Qt Quick. The whole user interface will be written in HTML instead of C++/QML.
- Examples: Add demo projects that emulate the classic appearance of UWP applications. They will have a backward button on the left side of the title bar and a search box in the middle of the title bar. And maybe a side bar on the left side to switch between different pages.
- Examples: Add demo projects that the main window is not resizable.
- Examples: Add demo projects that have transparent background and doesn't have rectangular window frame.
- Feature requests are welcome!
## Highlights v2.4
- Widgets: Nested frameless windows are supported now!
- Linux: There have been many improvements to the Linux/X11 implementation! Most of them won't be directly visible to the user, but the code quality has been greatly improved.
- Routine bug fixes and internal refactorings.
## Highlights v2.3
- Widgets & Quick: When you clicks the window icon, the system menu will now popup, and when you double clicks it, the window will be closed. This emulates the traditional window behavior on Windows.
- Widgets & Quick: The StandardSystemButton's icon size can't be changed due to I forgot to implement such funtionality, it's implemented now.
- Widgets: Added `FramelessDialog` class. You can use it in case a `QDialog` is preferred over a general `QWidget`.
- Widgets: The StandardSystemButton's size can't be changed due to a FramelessHelper bug, it's fixed now.
- Widgets: Added public interface to change the title label font (family, point size, style, etc...).
- Widgets: The window borders are now drawn by the newly introduced `WindowBorderPainter` class, and it's also exposed publicly, so you'll be able to change how we draw the window border easily.
- Quick: Added `WindowBorder` element. It's a cross-platform window border decorator, and can work without the `FramelessHelper` element.
- Quick: Added `FramelessApplicationWindow` element. It's a simple wrapper of the standard `ApplicationWindow` element, just removes the title bar and adds the window border.
- Windows: Added support for dark theme system menu. The system menu triggered by right-clicking on the title bar will now use the same theme with the current system theme, and will switch between light and dark theme automatically.
- macOS: Added support for old macOS versions and old Qt versions, in theory.
- Common: Internal code improvements & bug fixes.
## Highlights v2.2
- Common: Added blur behind window feature for Windows (7\~11), Linux and macOS. On Windows 11 and macOS, FramelessHelper will make use of the blur effect provided by the OS to get the best appearance and performance, while on Windows 7\~10 and Linux, FramelessHelper will use a homemade blur effect to provide as much consistent experience as possible.
- Common: Added window icon support. It's now possible to set the window icon's image, size and visibility for the standard title bar control.
- Windows: If you are using Qt 6.4+, your Qt Widgets applications will now automatically switch to light/dark theme if the OS theme changes. It requires you are using the default palette provided by Qt. Qt Quick applications will not be affected.
- Linux: FramelessHelper is now theme-aware. If you change your OS theme, FramelessHelper will now emit the theme change signal and refresh it's internal palette.
- Build system: Implemented CMake package support. It's now possible to use `find_package` to find FramelessHelper.
- Build system: Implemented limited QMake support. FramelessHelper doesn't provide complete QMake project, to decrease the maintainance burden, but you can use the .pri files to directly embed FramelessHelper into your own application.
- Examples: Enabled blur behind window and round window corner by default.
- Common: Migrated to categorized logging output. You can now enable or disable some specific debug messages using QLoggingCategory.
- Common: Internal code improvements & bug fixes.
## Highlights v2.1
- Windows: Added support for the snap layout feature introduced in Windows 11.
- Widgets: Redesigned the public interface, the use of FramelessHelper is now more elegant.
- Quick: Redesigned the public interface, the use of FramelessHelper is now more elegant.
- Common: Redesigned the standard title bar interface, it's now possible to customize it from outside. Previously there's no standard title bar in the widgets module, it's now added and exported.
- Doc: Add initial simple documentation to show how to use this library for both Qt Widgets and Qt Quick.
- Misc: Removed bundled Qt internal classes that are licensed under Commercial/GPL/LGPL. This library is now pure MIT licensed.
- Misc: Bug fixes and internal refactorings.
## Highlights v2.0
- Windows: Gained the ability to only remove the title bar but preserve the window frame at the same time.
- Windows: The flicker and jitter during window resizing is completely gone.
- Windows: The system menu will be opened if you right-click on your custom title bar.
- Windows: Replaced Qt's original system menu with FramelessHelper's homemade one, which looks a lot better than the original one.
- Linux: Removed the limitation of the Qt version. The minimum supported version is lowered to Qt 5.6 now (previously was 5.15).
- macOS: Removed the limitation of the Qt version. The minimum supported version is lowered to Qt 5.6 now (previously was 5.15).
- macOS: The frameless window now supports native resizing.
- Common: Almost completely rewritten of the whole library, it's now a lot more easier to setup your own custom title bar than before.
- Common: Added many more helper functions to allow creating your own custom window easier.
- Misc: Reorganized the project structure to be more like a modern library, it's now a lot more friendly to the library users.
- Misc: Many bugs from the 1.x times are fixed (they were not fixable in 1.x due to technical reasons).
## Screenshots
### Windows
![Light](./doc/win_light.png)
![Dark](./doc/win_dark.png)
### Linux
![Light](./doc/linux_light.png)
![Dark](./doc/linux_dark.png)
### macOS
![Light](./doc/mac_light.png)
![Dark](./doc/mac_dark.png)
## Use Cases
### QVogenClient
![QVogenClient](./doc/QVogenClient.png)
Vogen editor using **QSynthesis** framework. Repository URL: <https://gitee.com/functioner/qvogenclient>.
## Requiredments
- Compiler: a modern compiler which supports C++17 at least. Tested on MSVC 2022 (Windows), GCC 11 (Linux) and Clang 13 (macOS).
- Qt version: using the latest stable version of Qt is highly recommended, the minimum supported version is Qt 5.6. However, if you are using some old Qt versions (such as older than 5.12), some features may not be available. Only Qt 5.15 and Qt 6 are actively tested.
- Qt modules: QtCore and QtGui for the core module; QtWidgets for the widgets module; QtQuick, QtQuickControls2 and QtQuickTemplates2 for the quick module.
- CMake & ninja: the newer, the better. Other build systems are not tested.
## Supported Platforms
- Windows: Windows 7, Windows 8, Windows 8.1, Windows 10, Windows 11 (only actively tested on Windows 10 & 11)
- Linux: any modern Linux distros should work, but only tested on Ubuntu 20.04 and Ubuntu 22.04
- macOS: only tested on macOS 12.3 due to lack of Apple devices
There are some additional restrictions for each platform, please refer to the _Platform notes_ section below.
## Build
```bash
git clone https://github.com/wangwenx190/framelesshelper.git
mkdir A_TEMP_DIR
cd A_TEMP_DIR
cmake -DCMAKE_PREFIX_PATH=<YOUR_QT_SDK_DIR_PATH> -DCMAKE_INSTALL_PREFIX=<WHERE_YOU_WANT_TO_INSTALL> -DCMAKE_BUILD_TYPE=Release -GNinja <PATH_TO_THE_REPOSITORY>
cmake --build . --config Release --target all --parallel
cmake --install . --config Release --strip
```
Once the compilation and installation is done, you will be able to use the `find_package(FramelessHelper REQUIRED COMPONENTS Core Widgets Quick)` command to find and link to the FramelessHelper library. But before doing that, please make sure CMake knows where to find FramelessHelper, by passing the `CMAKE_PREFIX_PATH` variable to it. For example: `-DCMAKE_PREFIX_PATH=C:/my-cmake-packages;C:/my-toolchain;etc...`. Build FramelessHelper as a sub-directory of your CMake project is of course also supported. The supported FramelessHelper target names are `FramelessHelper::FramelessHelperCore`, `FramelessHelper::FramelessHelperWidgets` and `FramelessHelper::FramelessHelperQuick`.
## Use
### Qt Widgets
To customize the window frame of a QWidget, you need to instantiate a `FramelessWidgetsHelper` object and then attach it to the widget's top level widget, and then `FramelessWidgetsHelper` will do all the rest work for you: the window frame will be removed automatically once it has been attached to the top level widget successfully. In theory you can instantiate multiple `FramelessWidgetsHelper` objects for a same widget, in this case there will be only one object that keeps functional, all other objects will become a wrapper of that one. But to make sure everything goes smoothly and normally, you should not do that in any case. The simplest way to instantiate a `FramelessWidgetsHelper`
object is to call the static method `FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)`. It will return the handle of the previously instantiated object if any, or it will instantiate a new object if it can't find one. It's safe to call this method multiple times for a same widget, it won't instantiate any new objects if there is one already. It also does not matter when and where you call that function as long as the top level widget is the same. The internally created objects will always be parented to the top level widget. Once you get the handle of the `FramelessWidgetsHelper` object, you can call `void FramelessWidgetsHelper::setContentExtendedIntoTitleBar(true)` to let it hide the default title bar provided by the operating system. In order to make sure `FramelessWidgetsHelper` can find the correct top level widget, you should call the `FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)` function on a widget which has a complete parent-chain whose root parent is the top level widget. To make the frameless window draggable, you should provide a homemade title bar widget yourself, the title bar widget doesn't need to be in rectangular shape, it also doesn't need to be placed on the first row of the window. Call `void FramelessWidgetsHelper::setTitleBarWidget(QWidget *)` to let `FramelessHelper` know what's your title bar widget. By default, all the widgets in the title bar area won't be responsible to any mouse and keyboard events due to they have been intercepted by FramelessHelper. To make them recover the responsible state, you should make them visible to hit test. Call `void FramelessWidgetsHelper::setHitTestVisible(QWidget* )` to do that. You can of course call it on a widget that is not inside the title bar at all, it won't have any effect though. Due to Qt's own limitations, you need to make sure your widget has a complete parent-chain whose root parent is the top level widget. Do not ever try to delete the `FramelessWidgetsHelper` object, it may still be monitoring and controlling your widget, and Qt will delete it for you automatically. No need to worry about memory leaks.
There are also two classes called `FramelessWidget` and `FramelessMainWindow`, they are only simple wrappers of `FramelessWidgetsHelper`, which just saves the call of the `void FramelessWidgetsHelper::setContentExtendedIntoTitleBar(true)` function for you. You can absolutely use plain `QWidget` instead.
#### Code Snippet
First of all, call `void FramelessHelper::Widgets::initialize()` in your `main` function in a very early stage:
```cpp
int main(int, char **)
{
FramelessHelper::Widgets::initialize();
// ...
}
```
Then hide the standard title bar provided by the OS:
```cpp
MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
// You should do this early enough.
FramelessWidgetsHelper::get(this)->setContentExtendedIntoTitleBar(true);
// ...
}
```
Then let `FramelessHelper` know what should be the title bar:
```cpp
void MyWidget::myFunction()
{
// ...
FramelessWidgetsHelper::get(this)->setTitleBarWidget(m_myTitleBarWidget);
// ...
}
```
Then make some widgets inside your title bar visible to hit test:
```cpp
void MyWidget::myFunction2()
{
// ...
FramelessWidgetsHelper::get(this)->setHitTestVisible(m_someSearchBox);
FramelessWidgetsHelper::get(this)->setHitTestVisible(m_someButton);
FramelessWidgetsHelper::get(this)->setHitTestVisible(m_someMenuItem);
// ...
}
```
**IMPORTANT NOTE for Qt Widgets applications**: Some functionalities may only be available when `FramelessHelper` has finished the window customization process, such as changing window geometry/flags/state. In this case you can connect to the public `void ready()` signal of `FramelessHelper` to get the accurate time point and do your rest initialization process afterwards.
### Qt Quick
#### Code Snippet
First of all, you should call `void FramelessHelper::Quick::initialize()` in your `main` function in a very early stage:
```cpp
int main(int, char **)
{
FramelessHelper::Quick::initialize();
// ...
}
```
Then you need to register the custom types provided by `FramelessHelper` by calling `void FramelessHelper::Quick::registerTypes(QQmlEngine *)`, before the QML engine loads any QML documents:
```cpp
int main(int, char **)
{
// ...
QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes(&engine);
// ...
}
```
Now you can write your QML documents. You should import `FramelessHelper` from the URI `org.wangwenx190.FramelessHelper`. You should specify a version number right after it if you are using Qt5:
```qml
import org.wangwenx190.FramelessHelper 1.0 // You can use "auto" or omit the version number in Qt6.
```
And then you can use the attached properties from the QML type `FramelessHelper`:
```qml
Window {
Item {
id: myTitleBar
Item { id: someControl1 }
Item { id: someControl2 }
Item { id: someControl3 }
Component.onCompleted: {
// Don't access FramelessHelper too early, otherwise it may not be able to find the root window!
FramelessHelper.titleBarItem = myTitleBar;
FramelessHelper.setHitTestVisible(someControl1);
FramelessHelper.setHitTestVisible(someControl2);
FramelessHelper.setHitTestVisible(someControl3);
}
}
}
```
It's the same with the `FramelessWidgetsHelper` interface, the QML type `FramelessHelper` will be instantiated only once for each `Window`, no matter when and where you use attached properties from it. However, due to the special design of the `FramelessHelper` type, you can also use it just like a normal QML type:
```qml
Window {
Item {
id: myTitleBar
Item { id: someControl1 }
Item { id: someControl2 }
Item { id: someControl3 }
Component.onCompleted: {
framelessHelper.setHitTestVisible(someControl1);
framelessHelper.setHitTestVisible(someControl2);
framelessHelper.setHitTestVisible(someControl3);
}
}
FramelessHelper {
id: framelessHelper
titleBarItem: myTitleBar
}
}
```
In theory it's possible to instantiate multiple `FramelessHelper` objects for a same `Window`, in this case only one of them will keep functional, all other objects will become a wrapper of it, but doing so is not recommended and may cause unexpected behavior or bugs, so please avoid trying to do that in any case.
If you find any of `FramelessHelper` functions have no effect after calling, the most possible reason is by the time you call the function/change the property of `FramelessHelper`, the root window has not finished its initialization process and thus `FramelessHelper` can't get the handle of it, so any action from the user will be ignored until the root window finished initialization.
There's also a QML type called `FramelessWindow`, it's only a simple wrapper of `FramelessHelper`, you can absolutely use plain `Window` instead.
**IMPORTANT NOTE for Qt Quick applications**: Some functionalities may only be available when `FramelessHelper` has finished the window customization process, such as changing window geometry/flags/state. In this case you can connect to the public `void ready()` signal of `FramelessHelper` to get the accurate time point and do your rest initialization process afterwards:
```qml
Window {
FramelessHelper.onReady: {
// do something here ...
}
}
```
```qml
Window {
FramelessHelper {
onReady: {
// do something here ...
}
}
}
```
**IMPORTANT NOTE for all applications**: Once you called `QWidget::close()` or `Q(Quick)Window::close()`, Qt will release all the resources of the corresponding widget/window, and thus FramelessHelper's custom event handler will also be removed from them at the same time. However, this will make the title bar become unresponsible if you re-open the widget/window. The current workaround for this issue is to hide the widget/window instead of closing it, if you are going to show it again later. But if you have no plan to show the widget/window again after it has been closed, you don't need to do anything to workaround this issue.
### More
Please refer to the demo projects to see more detailed usages: [examples](./examples/)
### Title Bar Design Guidance
- Microsoft: <https://docs.microsoft.com/en-us/windows/apps/design/basics/titlebar-design>
- KDE: <https://develop.kde.org/hig/>
- GNOME: <https://developer.gnome.org/hig/patterns/containers/header-bars.html>
- Apple: <https://developer.apple.com/design/human-interface-guidelines/macos/windows-and-views/window-anatomy/>
## Platform Notes
### Windows
- If DWM composition is disabled in some very rare cases (only possible on Windows 7), the top-left corner and top-right corner will appear in round shape. The round corners can be restored to square if you re-enable DWM composition.
- There's an OpenGL driver bug which will cause some frameless windows have a strange black bar right on top of your homemade title bar, and it also makes the controls in your windows shifted to the bottom-right corner for some pixels. It's a bug of your graphics card driver, specifically, your OpenGL driver, not FramelessHelper. There are some solutions provided by our users but some of them may not work in all conditions, you can pick one from them:
Solution | Principle
-------- | ---------
Upgrade the graphics driver | Try to use a newer driver which may ship with the fix
Change the system theme to "Basic" (in contrary to "Windows Aero") | Let Windows use pure software rendering
If there are multiple graphics cards, use another one instead | Try to use a different driver which may don't have such bug at all
Upgrade the system to at least Windows 11 | Windows 11 redesigned the windowing system so the bug can no longer be triggered
Remove the `WS_THICKFRAME` and `WS_OVERLAPPED` styles from the window, and maybe also add the `WS_POPUP` style at the same time, and don't do anything inside the `WM_NCCALCSIZE` block (just return `false` directly or remove/comment out the whole block) | Try to mirror Qt's `FramelessWindowHint`'s behavior
Use `Qt::FramelessWindowHint` instead of doing the `WM_NCCALCSIZE` trick | Qt's rendering code path is totally different between these two solutions
Force Qt to use the ANGLE backend instead of the Desktop OpenGL | ANGLE will translate OpenGL directives into D3D ones
Force Qt to use pure software rendering instead of rendering through OpenGL | Qt is not using OpenGL at all
Force Qt to use the Mesa 3D libraries instead of normal OpenGL | Try to use a different OpenGL implementation
Use Direct3D/Vulkan/Metal instead of OpenGL | Just don't use the buggy OpenGL
If you are lucky enough, one of them may fix the issue for you. If not, you may try to use multiple solutions together. **But I can't guarantee the issue can 100% be fixed.**
- Due to there are many sub-versions of Windows 10, it's highly recommended to use the latest version of Windows 10, at least **no older than Windows 10 1809**. If you try to use this framework on some very old Windows 10 versions such as 1507 or 1607, there may be some compatibility issues. Using this framework on Windows 7 is also supported but not recommended. To get the most stable behavior and the best appearance, you should use it on the latest version of Windows 10 or Windows 11.
- To make the snap layout work as expected, there are some additional rules for your homemade system buttons to follow:
- **Add a manifest file to your application. In the manifest file, you need to claim your application supports Windows 11 explicitly. This step is VERY VERY IMPORTANT. Without this step, the snap layout feature can't be enabled.**
- Make sure there are two public invokable functions (slot functions are always invokable): `void setHovered(bool)` and `void setPressed(bool)`. These two functions will be invoked by FramelessHelper when the button is being hovered or pressed. You should change the button's visual state inside these functions. If you need to show tooltips, you'll have to do it manually in these functions.
- Make sure there's a public signal: `void clicked()`. When the button is being clicked, that signal will be triggered by FramelessHelper. You should connect your event handler to that signal.
- For Qt Quick applications, for the C++ side, you need to inherit your button from the `QQuickAbstractButton` class, for the QML side, you need to inherit your button from the `Button` type (from the `QtQuick.Controls.Basic` module). They have all the invokable functions and signals we need, so no more extra work is needed.
- Don't forget to call `setSystemButton()` for each button to let FramelessHelper know which is the minimize/maximize/close button.
- System buttons will not be able to receive any actual mouse and keyboard events so there's no need to handle these events inside these buttons. That's also why we need to set the button's visual state manually.
- I know this is making everything complicated but unfortunately we can't avoid this mess if we need to support the snap layout feature. Snap layout is really only designed for the original standard window frame, so if we want to forcely support it without a standard window frame, many black magic will be needed.
### Linux
- FramelessHelper will force your application to use the _XCB_ platform plugin when running on Wayland.
- The resize area is inside of the window.
### macOS
- The frameless windows will appear in square corners instead of round corners (Qt Widgets applications only).
- The resize area is inside of the window.
- Some users reported that the window is not resizable on some old macOS versions.
## FAQs
### `When running on Win10, it seems the top border is missing? But the demo applications still have it?`
`FramelessHelper` hides the system title bar by removing the whole top part of the window frame, including the top border. There's no way to only remove the system title bar but still preserve the top border at the same time, even Microsoft themself can't do that either. The exact reason is unknown to non-Microsoft developers, and I have no interest in digging into all the magic behind it. So you'll have to draw one manually yourself to pretend the top border is still there. You can retrieve it's height and color through official DWM APIs. Please refer to the documentation of [`DwmGetWindowAttribute()`](https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmgetwindowattribute) and [`DwmGetColorizationColor()`](https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmgetcolorizationcolor). The demo applications still have the top border because their main windows all inherit from `FramelessWidget` or `FramelessMainWindow`, which will draw the top border for you internally. As for Qt Quick, the QML type `FramelessWindow` will also draw the top border.
### `When running on Wayland, dragging the title bar causes crash?`
You need to force Qt to use the **XCB** QPA when running on Wayland. Try setting the environment variable `QT_QPA_PLATFORM` (case sensitive) to `xcb` (case sensitive) before instantiating any `Q(Gui)Application` instances. Or just call `void FramelessHelper::Widgets/Quick::initialize()` in your `main` function, this function will take care of it for you.
### `I can see the black background during window resizing?`
First of all, it's a Qt issue, not caused by FramelessHelper. And it should not be possible for Qt Widgets applications. It's a common issue for Qt Quick applications. Most of the time it's caused by D3D11/Vulkan/Metal because they are not good at dealing with texture resizing operations. If you really want to fix this issue, you can try to change Qt's RHI backend to **OpenGL** (be careful of the bug of your graphics card driver) or **Software** (if you don't care about performance). And please keep in mind that this issue is not fixable from outside of Qt.
### `Can I preserve the window frame border even on Win7? How does Google Chrome/Microsoft Edge's installer achieve that?`
Short answer: it's impossible. Full explaination: of course we can use the same technique we use on Win10 to remove the whole top part of the window and preserve the other three frame borders at the same time, but on Win10 we can bring the top border back, either by doing some black magic in the `WM_PAINT` handler or draw a thin frame border manually ourself, however, it's impossible to do this on Win7. I've tried it on Win7 already and sadly the result is the `WM_PAINT` trick won't work on Win7, and we also can't draw a frame border which looks very similar to the original one (a semi-transparent rectangle, blended with system's accent color and the visual content behind the window, also with some blur effect applied). But it seems Google Chrome/Microsoft Edge's installer have achieved what we wanted to do, how? Well, their installer is open source and I've read it's code already. They achieve that by overlapping two windows, one normal window on the bottom, another border-less window on the top to cover the bottom window's title bar. They draw their homemade title bar on the border-less window and use it to emulate the standard title bar's behavior. The original title bar provided by the system is still there, but it can't be seen by anyone just because it's covered by another window. I admit it's a good solution in such cases but for our library it's not appropriate because the code complexity will blow up.
## License
```text
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

View File

@ -0,0 +1,34 @@
#[[
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
if(FRAMELESSHELPER_BUILD_WIDGETS AND TARGET Qt${QT_VERSION_MAJOR}::Widgets)
add_subdirectory(widget)
add_subdirectory(mainwindow)
#add_subdirectory(openglwidget) # Currently not functional.
add_subdirectory(dialog)
endif()
if(FRAMELESSHELPER_BUILD_QUICK AND TARGET Qt${QT_VERSION_MAJOR}::Quick AND (${QT_VERSION_MAJOR} GREATER_EQUAL 6) AND (NOT FRAMELESSHELPER_NO_PRIVATE))
add_subdirectory(quick)
endif()

View File

@ -0,0 +1,55 @@
#[[
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
set(SOURCES
../shared/log.h
../shared/log.cpp
../shared/settings.h
../shared/settings.cpp
dialog.h
dialog.cpp
main.cpp
)
if(WIN32)
enable_language(RC)
list(APPEND SOURCES
../shared/example.rc
../shared/example.manifest
)
endif()
add_executable(Dialog ${SOURCES})
target_link_libraries(Dialog PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
FramelessHelper::Widgets
)
include(../../src/core/cmakehelper.cmake)
setup_gui_app(Dialog)
setup_compile_params(Dialog)
if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT)
deploy_qt_runtime(Dialog)
endif()

View File

@ -0,0 +1,151 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "dialog.h"
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qlineedit.h>
#include <QtWidgets/qcheckbox.h>
#include <QtWidgets/qdialogbuttonbox.h>
#include <QtWidgets/qpushbutton.h>
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qfileiconprovider.h>
#include <QtWidgets/qmessagebox.h>
#include <FramelessHelper/Widgets/standardtitlebar.h>
#include <FramelessHelper/Widgets/framelesswidgetshelper.h>
#include <FramelessHelper/Widgets/standardsystembutton.h>
#include <FramelessHelper/Widgets/private/framelesswidgetshelper_p.h>
#include "../shared/settings.h"
extern template void Settings::set<QRect>(const QString &, const QString &, const QRect &);
extern template void Settings::set<qreal>(const QString &, const QString &, const qreal &);
extern template QRect Settings::get<QRect>(const QString &, const QString &);
extern template qreal Settings::get<qreal>(const QString &, const QString &);
FRAMELESSHELPER_USE_NAMESPACE
using namespace Global;
FRAMELESSHELPER_STRING_CONSTANT(Geometry)
FRAMELESSHELPER_STRING_CONSTANT(DevicePixelRatio)
Dialog::Dialog(QWidget *parent) : FramelessDialog(parent)
{
setupUi();
}
Dialog::~Dialog() = default;
void Dialog::closeEvent(QCloseEvent *event)
{
if (!parent()) {
Settings::set({}, kGeometry, geometry());
Settings::set({}, kDevicePixelRatio, devicePixelRatioF());
}
FramelessDialog::closeEvent(event);
}
void Dialog::setupUi()
{
setWindowTitle(tr("Qt Dialog demo"));
setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer));
titleBar = new StandardTitleBar(this);
titleBar->setWindowIconVisible(true);
#ifndef Q_OS_MACOS
titleBar->maximizeButton()->hide();
#endif // Q_OS_MACOS
label = new QLabel(tr("Find &what:"));
lineEdit = new QLineEdit;
label->setBuddy(lineEdit);
caseCheckBox = new QCheckBox(tr("Match &case"));
fromStartCheckBox = new QCheckBox(tr("Search from &start"));
fromStartCheckBox->setChecked(true);
findButton = new QPushButton(tr("&Find"));
findButton->setDefault(true);
connect(findButton, &QPushButton::clicked, this, [this](){
const QString text = lineEdit->text();
if (text.isEmpty()) {
QMessageBox::warning(this, tr("Warning"), tr("You didn't enter anything in the search box."));
} else {
QMessageBox::information(this, tr("Result"), tr("You wanted to find: \"%1\".").arg(text));
}
});
moreButton = new QPushButton(tr("&More"));
moreButton->setCheckable(true);
moreButton->setAutoDefault(false);
extension = new QWidget;
wholeWordsCheckBox = new QCheckBox(tr("&Whole words"));
backwardCheckBox = new QCheckBox(tr("Search &backward"));
searchSelectionCheckBox = new QCheckBox(tr("Search se&lection"));
buttonBox = new QDialogButtonBox(Qt::Vertical);
buttonBox->addButton(findButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(moreButton, QDialogButtonBox::ActionRole);
connect(moreButton, &QPushButton::toggled, extension, &QWidget::setVisible);
QVBoxLayout *extensionLayout = new QVBoxLayout;
extensionLayout->setContentsMargins(0, 0, 0, 0);
extensionLayout->addWidget(wholeWordsCheckBox);
extensionLayout->addWidget(backwardCheckBox);
extensionLayout->addWidget(searchSelectionCheckBox);
extension->setLayout(extensionLayout);
QHBoxLayout *topLeftLayout = new QHBoxLayout;
topLeftLayout->addWidget(label);
topLeftLayout->addWidget(lineEdit);
QVBoxLayout *leftLayout = new QVBoxLayout;
leftLayout->addLayout(topLeftLayout);
leftLayout->addWidget(caseCheckBox);
leftLayout->addWidget(fromStartCheckBox);
QGridLayout *controlsLayout = new QGridLayout;
controlsLayout->setContentsMargins(11, 11, 11, 11);
controlsLayout->addLayout(leftLayout, 0, 0);
controlsLayout->addWidget(buttonBox, 0, 1);
controlsLayout->addWidget(extension, 1, 0, 1, 2);
controlsLayout->setRowStretch(2, 1);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
mainLayout->addWidget(titleBar);
mainLayout->addLayout(controlsLayout);
setLayout(mainLayout);
extension->hide();
FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this);
helper->setTitleBarWidget(titleBar);
#ifndef Q_OS_MACOS
helper->setSystemButton(titleBar->minimizeButton(), SystemButtonType::Minimize);
helper->setSystemButton(titleBar->maximizeButton(), SystemButtonType::Maximize);
helper->setSystemButton(titleBar->closeButton(), SystemButtonType::Close);
#endif // Q_OS_MACOS
// Special hack to disable the overriding of the mouse cursor, it's totally different
// with making the window un-resizable: we still want the window be able to resize
// programatically, but we also want the user not able to resize the window manually.
// So apparently we can't use QWidget::setFixedWidth/Height/Size() here.
FramelessWidgetsHelperPrivate::get(helper)->setProperty(kDontOverrideCursorVar, true);
connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){
const auto savedGeometry = Settings::get<QRect>({}, kGeometry);
if (savedGeometry.isValid() && !parent()) {
const auto savedDpr = Settings::get<qreal>({}, kDevicePixelRatio);
// Qt doesn't support dpr < 1.
const qreal oldDpr = std::max(savedDpr, qreal(1));
const qreal scale = (devicePixelRatioF() / oldDpr);
setGeometry({savedGeometry.topLeft() * scale, savedGeometry.size() * scale});
} else {
helper->moveWindowToDesktopCenter();
}
});
}

View File

@ -0,0 +1,49 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#pragma once
#include <FramelessHelper/Widgets/framelessdialog.h>
QT_BEGIN_NAMESPACE
class QCheckBox;
class QDialogButtonBox;
class QGroupBox;
class QLabel;
class QLineEdit;
class QPushButton;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBar;
FRAMELESSHELPER_END_NAMESPACE
class Dialog : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessDialog)
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(Dialog)
public:
explicit Dialog(QWidget *parent = nullptr);
~Dialog() override;
protected:
void closeEvent(QCloseEvent *event) override;
private:
void setupUi();
private:
FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar) *titleBar = nullptr;
QLabel *label = nullptr;
QLineEdit *lineEdit = nullptr;
QCheckBox *caseCheckBox = nullptr;
QCheckBox *fromStartCheckBox = nullptr;
QCheckBox *wholeWordsCheckBox = nullptr;
QCheckBox *searchSelectionCheckBox = nullptr;
QCheckBox *backwardCheckBox = nullptr;
QDialogButtonBox *buttonBox = nullptr;
QPushButton *findButton = nullptr;
QPushButton *moreButton = nullptr;
QWidget *extension = nullptr;
};

View File

@ -0,0 +1,63 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <QtWidgets/qapplication.h>
#include <FramelessHelper/Core/private/framelessconfig_p.h>
#include <clocale>
#include "dialog.h"
#include "../shared/log.h"
FRAMELESSHELPER_USE_NAMESPACE
int main(int argc, char *argv[])
{
std::setlocale(LC_ALL, "en_US.UTF-8");
Log::setup(FRAMELESSHELPER_STRING_LITERAL("dialog"));
// Not necessary, but better call this function, before the construction
// of any Q(Core|Gui)Application instances.
FramelessHelper::Widgets::initialize();
#if 0
if (!qEnvironmentVariableIsSet("QT_WIDGETS_RHI")) {
qputenv("QT_WIDGETS_RHI", FRAMELESSHELPER_BYTEARRAY_LITERAL("1"));
}
#endif
const auto application = std::make_unique<QApplication>(argc, argv);
// Must be called after QGuiApplication has been constructed, we are using
// some private functions from QPA which won't be available until there's
// a QGuiApplication instance.
FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
const auto dialog = std::make_unique<Dialog>();
dialog->show();
return QCoreApplication::exec();
}

View File

@ -0,0 +1,60 @@
#[[
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
set(SOURCES
../shared/log.h
../shared/log.cpp
../shared/settings.h
../shared/settings.cpp
../dialog/dialog.h
../dialog/dialog.cpp
../widget/widget.h
../widget/widget.cpp
mainwindow.ui
mainwindow.h
mainwindow.cpp
main.cpp
)
if(WIN32)
enable_language(RC)
list(APPEND SOURCES
../shared/example.rc
../shared/example.manifest
)
endif()
add_executable(MainWindow ${SOURCES})
target_link_libraries(MainWindow PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
FramelessHelper::Widgets
)
include(../../src/core/cmakehelper.cmake)
setup_gui_app(MainWindow)
setup_compile_params(MainWindow)
if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT)
deploy_qt_runtime(MainWindow)
endif()

View File

@ -0,0 +1,63 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <QtWidgets/qapplication.h>
#include <FramelessHelper/Core/private/framelessconfig_p.h>
#include <clocale>
#include "mainwindow.h"
#include "../shared/log.h"
FRAMELESSHELPER_USE_NAMESPACE
int main(int argc, char *argv[])
{
std::setlocale(LC_ALL, "en_US.UTF-8");
Log::setup(FRAMELESSHELPER_STRING_LITERAL("mainwindow"));
// Not necessary, but better call this function, before the construction
// of any Q(Core|Gui)Application instances.
FramelessHelper::Widgets::initialize();
#if 0
if (!qEnvironmentVariableIsSet("QT_WIDGETS_RHI")) {
qputenv("QT_WIDGETS_RHI", FRAMELESSHELPER_BYTEARRAY_LITERAL("1"));
}
#endif
const auto application = std::make_unique<QApplication>(argc, argv);
// Must be called after QGuiApplication has been constructed, we are using
// some private functions from QPA which won't be available until there's
// a QGuiApplication instance.
FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
const auto mainWindow = std::make_unique<MainWindow>();
mainWindow->show();
return QCoreApplication::exec();
}

View File

@ -0,0 +1,139 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qfileiconprovider.h>
#include <FramelessHelper/Core/utils.h>
#include <FramelessHelper/Widgets/standardtitlebar.h>
#include <FramelessHelper/Widgets/standardsystembutton.h>
#include <FramelessHelper/Widgets/framelesswidgetshelper.h>
#include "../shared/settings.h"
#include "../widget/widget.h"
#include "../dialog/dialog.h"
extern template void Settings::set<QRect>(const QString &, const QString &, const QRect &);
extern template void Settings::set<qreal>(const QString &, const QString &, const qreal &);
extern template void Settings::set<QByteArray>(const QString &, const QString &, const QByteArray &);
extern template QRect Settings::get<QRect>(const QString &, const QString &);
extern template qreal Settings::get<qreal>(const QString &, const QString &);
extern template QByteArray Settings::get<QByteArray>(const QString &, const QString &);
FRAMELESSHELPER_USE_NAMESPACE
using namespace Global;
FRAMELESSHELPER_STRING_CONSTANT(Geometry)
FRAMELESSHELPER_STRING_CONSTANT(State)
FRAMELESSHELPER_STRING_CONSTANT(DevicePixelRatio)
MainWindow::MainWindow(QWidget *parent, const Qt::WindowFlags flags) : FramelessMainWindow(parent, flags)
{
initialize();
}
MainWindow::~MainWindow() = default;
void MainWindow::closeEvent(QCloseEvent *event)
{
if (!parent()) {
Settings::set({}, kGeometry, geometry());
Settings::set({}, kState, saveState());
Settings::set({}, kDevicePixelRatio, devicePixelRatioF());
}
FramelessMainWindow::closeEvent(event);
}
void MainWindow::initialize()
{
m_titleBar = new StandardTitleBar(this);
m_titleBar->setTitleLabelAlignment(Qt::AlignCenter);
m_mainWindow = new Ui::MainWindow;
m_mainWindow->setupUi(this);
QMenuBar * const mb = menuBar();
mb->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
mb->setStyleSheet(FRAMELESSHELPER_STRING_LITERAL(R"(
QMenuBar {
background-color: transparent;
}
QMenuBar::item {
background: transparent;
}
QMenuBar::item:selected {
background: #a8a8a8;
}
QMenuBar::item:pressed {
background: #888888;
}
)"));
const auto titleBarLayout = static_cast<QHBoxLayout *>(m_titleBar->layout());
titleBarLayout->insertWidget(0, mb);
// setMenuWidget(): make the menu widget become the first row of the window.
setMenuWidget(m_titleBar);
FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this);
helper->setTitleBarWidget(m_titleBar);
#ifndef Q_OS_MACOS
helper->setSystemButton(m_titleBar->minimizeButton(), SystemButtonType::Minimize);
helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize);
helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close);
#endif // Q_OS_MACOS
helper->setHitTestVisible(mb); // IMPORTANT!
connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){
const auto savedGeometry = Settings::get<QRect>({}, kGeometry);
if (savedGeometry.isValid() && !parent()) {
const auto savedDpr = Settings::get<qreal>({}, kDevicePixelRatio);
// Qt doesn't support dpr < 1.
const qreal oldDpr = std::max(savedDpr, qreal(1));
const qreal scale = (devicePixelRatioF() / oldDpr);
setGeometry({savedGeometry.topLeft() * scale, savedGeometry.size() * scale});
} else {
helper->moveWindowToDesktopCenter();
}
const QByteArray savedState = Settings::get<QByteArray>({}, kState);
if (!savedState.isEmpty() && !parent()) {
restoreState(savedState);
}
});
setWindowTitle(tr("FramelessHelper demo application - Qt MainWindow"));
setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer));
connect(m_mainWindow->pushButton, &QPushButton::clicked, this, [this]{
const auto dialog = new Dialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->exec();
});
connect(m_mainWindow->pushButton_2, &QPushButton::clicked, this, [this]{
const auto widget = new Widget(this);
widget->setAttribute(Qt::WA_DeleteOnClose);
widget->show();
});
}

View File

@ -0,0 +1,56 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <FramelessHelper/Widgets/framelessmainwindow.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBar;
FRAMELESSHELPER_END_NAMESPACE
namespace Ui
{
class MainWindow;
}
class MainWindow : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessMainWindow)
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(MainWindow)
public:
explicit MainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {});
~MainWindow() override;
protected:
void closeEvent(QCloseEvent *event) override;
private:
void initialize();
private:
FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar) *m_titleBar = nullptr;
Ui::MainWindow *m_mainWindow = nullptr;
};

View File

@ -0,0 +1,296 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Hello, World!</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="tableWidget">
<row>
<property name="text">
<string>Nouvelle ligne</string>
</property>
</row>
<row>
<property name="text">
<string>Nouvelle ligne</string>
</property>
</row>
<row>
<property name="text">
<string>Nouvelle ligne</string>
</property>
</row>
<row>
<property name="text">
<string>Nouvelle ligne</string>
</property>
</row>
<row>
<property name="text">
<string>Nouvelle ligne</string>
</property>
</row>
<row>
<property name="text">
<string>Nouvelle ligne</string>
</property>
</row>
<column>
<property name="text">
<string>Nouvelle colonne</string>
</property>
</column>
<column>
<property name="text">
<string>Nouvelle colonne</string>
</property>
</column>
<column>
<property name="text">
<string>Nouvelle colonne</string>
</property>
</column>
<column>
<property name="text">
<string>Nouvelle colonne</string>
</property>
</column>
<column>
<property name="text">
<string>Nouvelle colonne</string>
</property>
</column>
<column>
<property name="text">
<string>Nouvelle colonne</string>
</property>
</column>
<column>
<property name="text">
<string>Nouvelle colonne</string>
</property>
</column>
<column>
<property name="text">
<string>Nouvelle colonne</string>
</property>
</column>
<column>
<property name="text">
<string>Nouvelle colonne</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>518</width>
<height>25</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<widget class="QMenu" name="menuItem_1">
<property name="title">
<string>Menu 1</string>
</property>
<addaction name="actionItem_1"/>
<addaction name="actionItem_2"/>
</widget>
<widget class="QMenu" name="menuItem_2">
<property name="title">
<string>Menu 2</string>
</property>
</widget>
<widget class="QMenu" name="menuMenu_3">
<property name="title">
<string>Menu 3</string>
</property>
</widget>
<addaction name="menuItem_1"/>
<addaction name="menuItem_2"/>
<addaction name="menuMenu_3"/>
</widget>
<widget class="QDockWidget" name="dockWidget">
<property name="windowTitle">
<string>Dock 1</string>
</property>
<attribute name="dockWidgetArea">
<number>1</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;a href=&quot;https://www.google.com&quot;&gt;https://www.google.com&lt;/a&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_2">
<property name="windowTitle">
<string>Dock 2</string>
</property>
<attribute name="dockWidgetArea">
<number>1</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents_2">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="radioButton">
<property name="text">
<string>RadioButton</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_2">
<property name="text">
<string>RadioButton</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listWidget"/>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>89</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionAction_1"/>
<addaction name="actionAction_2"/>
</widget>
<widget class="QToolBar" name="toolBar_2">
<property name="windowTitle">
<string>toolBar_2</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QToolBar" name="toolBar_3">
<property name="windowTitle">
<string>toolBar_3</string>
</property>
<attribute name="toolBarArea">
<enum>BottomToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionItem_1"/>
<addaction name="actionAction_2"/>
</widget>
<action name="actionAction_1">
<property name="text">
<string>Action 1</string>
</property>
</action>
<action name="actionAction_2">
<property name="text">
<string>Action 2</string>
</property>
</action>
<action name="actionItem_1">
<property name="text">
<string>Item 1</string>
</property>
</action>
<action name="actionItem_2">
<property name="text">
<string>Item 2</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,75 @@
#[[
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS OpenGL)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS OpenGL)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS OpenGLWidgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS OpenGLWidgets)
endif()
set(SOURCES
../shared/log.h
../shared/log.cpp
../shared/settings.h
../shared/settings.cpp
images.qrc
logo.h
logo.cpp
glwidget.h
glwidget.cpp
mainwindow.h
mainwindow.cpp
main.cpp
)
if(WIN32)
enable_language(RC)
list(APPEND SOURCES
../shared/example.rc
../shared/example.manifest
)
endif()
add_executable(OpenGLWidget ${SOURCES})
target_link_libraries(OpenGLWidget PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::OpenGL
FramelessHelper::Widgets
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
target_link_libraries(OpenGLWidget PRIVATE
Qt${QT_VERSION_MAJOR}::OpenGLWidgets
)
endif()
include(../../src/core/cmakehelper.cmake)
setup_gui_app(OpenGLWidget)
setup_compile_params(OpenGLWidget)
if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT)
deploy_qt_runtime(OpenGLWidget)
endif()

View File

@ -0,0 +1,276 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "glwidget.h"
#include <QImage>
#include <QOpenGLTexture>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLContext>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLExtraFunctions>
#include <QPropertyAnimation>
#include <QSequentialAnimationGroup>
#include <QTimer>
GLWidget::GLWidget(QWidget *parent, Qt::WindowFlags f) : QOpenGLWidget(parent, f)
{
m_world.setToIdentity();
m_world.translate(0, 0, -1);
m_world.rotate(180, 1, 0, 0);
QSequentialAnimationGroup *animGroup = new QSequentialAnimationGroup(this);
animGroup->setLoopCount(-1);
QPropertyAnimation *zAnim0 = new QPropertyAnimation(this, QByteArrayLiteral("z"));
zAnim0->setStartValue(1.5f);
zAnim0->setEndValue(10.0f);
zAnim0->setDuration(2000);
animGroup->addAnimation(zAnim0);
QPropertyAnimation *zAnim1 = new QPropertyAnimation(this, QByteArrayLiteral("z"));
zAnim1->setStartValue(10.0f);
zAnim1->setEndValue(50.0f);
zAnim1->setDuration(4000);
zAnim1->setEasingCurve(QEasingCurve::OutElastic);
animGroup->addAnimation(zAnim1);
QPropertyAnimation *zAnim2 = new QPropertyAnimation(this, QByteArrayLiteral("z"));
zAnim2->setStartValue(50.0f);
zAnim2->setEndValue(1.5f);
zAnim2->setDuration(2000);
animGroup->addAnimation(zAnim2);
animGroup->start();
QPropertyAnimation* rAnim = new QPropertyAnimation(this, QByteArrayLiteral("r"));
rAnim->setStartValue(0.0f);
rAnim->setEndValue(360.0f);
rAnim->setDuration(2000);
rAnim->setLoopCount(-1);
rAnim->start();
QTimer::singleShot(4000, this, &GLWidget::startSecondStage);
}
GLWidget::~GLWidget()
{
makeCurrent();
delete m_texture;
delete m_program;
delete m_vbo;
delete m_vao;
}
void GLWidget::startSecondStage()
{
QPropertyAnimation* r2Anim = new QPropertyAnimation(this, QByteArrayLiteral("r2"));
r2Anim->setStartValue(0.0f);
r2Anim->setEndValue(360.0f);
r2Anim->setDuration(20000);
r2Anim->setLoopCount(-1);
r2Anim->start();
}
void GLWidget::setZ(float v)
{
m_eye.setZ(v);
m_uniformsDirty = true;
update();
Q_EMIT zChanged();
}
void GLWidget::setR(float v)
{
m_r = v;
m_uniformsDirty = true;
update();
Q_EMIT rChanged();
}
void GLWidget::setR2(float v)
{
m_r2 = v;
m_uniformsDirty = true;
update();
Q_EMIT r2Changed();
}
static constexpr const char vertexShaderSource[] =
"layout(location = 0) in vec4 vertex;\n"
"layout(location = 1) in vec3 normal;\n"
"out vec3 vert;\n"
"out vec3 vertNormal;\n"
"out vec3 color;\n"
"uniform mat4 projMatrix;\n"
"uniform mat4 camMatrix;\n"
"uniform mat4 worldMatrix;\n"
"uniform mat4 myMatrix;\n"
"uniform sampler2D sampler;\n"
"void main() {\n"
" ivec2 pos = ivec2(gl_InstanceID % 32, gl_InstanceID / 32);\n"
" vec2 t = vec2(float(-16 + pos.x) * 0.8, float(-18 + pos.y) * 0.6);\n"
" float val = 2.0 * length(texelFetch(sampler, pos, 0).rgb);\n"
" mat4 wm = myMatrix * mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, val, 1) * worldMatrix;\n"
" color = texelFetch(sampler, pos, 0).rgb * vec3(0.4, 1.0, 0.0);\n"
" vert = vec3(wm * vertex);\n"
" vertNormal = mat3(transpose(inverse(wm))) * normal;\n"
" gl_Position = projMatrix * camMatrix * wm * vertex;\n"
"}\n";
static constexpr const char fragmentShaderSource[] =
"in highp vec3 vert;\n"
"in highp vec3 vertNormal;\n"
"in highp vec3 color;\n"
"out highp vec4 fragColor;\n"
"uniform highp vec3 lightPos;\n"
"void main() {\n"
" highp vec3 L = normalize(lightPos - vert);\n"
" highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n"
" highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n"
" fragColor = vec4(col, 1.0);\n"
"}\n";
QByteArray versionedShaderCode(const char *src)
{
QByteArray versionedSrc;
if (QOpenGLContext::currentContext()->isOpenGLES())
versionedSrc.append(QByteArrayLiteral("#version 300 es\n"));
else
versionedSrc.append(QByteArrayLiteral("#version 330\n"));
versionedSrc.append(src);
return versionedSrc;
}
void GLWidget::initializeGL()
{
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
QImage img(QStringLiteral(":/images/qtlogo.png"));
Q_ASSERT(!img.isNull());
delete m_texture;
m_texture = new QOpenGLTexture(img.scaled(32, 36).mirrored());
delete m_program;
m_program = new QOpenGLShaderProgram;
// Prepend the correct version directive to the sources. The rest is the
// same, thanks to the common GLSL syntax.
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, versionedShaderCode(vertexShaderSource));
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, versionedShaderCode(fragmentShaderSource));
m_program->link();
m_projMatrixLoc = m_program->uniformLocation("projMatrix");
m_camMatrixLoc = m_program->uniformLocation("camMatrix");
m_worldMatrixLoc = m_program->uniformLocation("worldMatrix");
m_myMatrixLoc = m_program->uniformLocation("myMatrix");
m_lightPosLoc = m_program->uniformLocation("lightPos");
// Create a VAO. Not strictly required for ES 3, but it is for plain OpenGL.
delete m_vao;
m_vao = new QOpenGLVertexArrayObject;
if (m_vao->create())
m_vao->bind();
m_program->bind();
delete m_vbo;
m_vbo = new QOpenGLBuffer;
m_vbo->create();
m_vbo->bind();
m_vbo->allocate(m_logo.constData(), m_logo.count() * sizeof(GLfloat));
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),
nullptr);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),
reinterpret_cast<void *>(3 * sizeof(GLfloat)));
m_vbo->release();
f->glEnable(GL_DEPTH_TEST);
f->glEnable(GL_CULL_FACE);
}
void GLWidget::resizeGL(int w, int h)
{
m_proj.setToIdentity();
m_proj.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
m_uniformsDirty = true;
}
void GLWidget::paintGL()
{
// Now use QOpenGLExtraFunctions instead of QOpenGLFunctions as we want to
// do more than what GL(ES) 2.0 offers.
QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
static constexpr const auto rgb = 240.0f / 255.0f;
f->glClearColor(rgb, rgb, rgb, 1);
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_program->bind();
m_texture->bind();
if (m_uniformsDirty) {
m_uniformsDirty = false;
QMatrix4x4 camera;
camera.lookAt(m_eye, m_eye + m_target, QVector3D(0, 1, 0));
m_program->setUniformValue(m_projMatrixLoc, m_proj);
m_program->setUniformValue(m_camMatrixLoc, camera);
QMatrix4x4 wm = m_world;
wm.rotate(m_r, 1, 1, 0);
m_program->setUniformValue(m_worldMatrixLoc, wm);
QMatrix4x4 mm;
mm.setToIdentity();
mm.rotate(-m_r2, 1, 0, 0);
m_program->setUniformValue(m_myMatrixLoc, mm);
m_program->setUniformValue(m_lightPosLoc, QVector3D(0, 0, 70));
}
// Now call a function introduced in OpenGL 3.1 / OpenGL ES 3.0. We
// requested a 3.3 or ES 3.0 context, so we know this will work.
f->glDrawArraysInstanced(GL_TRIANGLES, 0, m_logo.vertexCount(), 32 * 36);
}

View File

@ -0,0 +1,117 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#pragma once
#include <QOpenGLWidget>
#include <QMatrix4x4>
#include <QVector3D>
#include "logo.h"
QT_BEGIN_NAMESPACE
class QOpenGLTexture;
class QOpenGLShaderProgram;
class QOpenGLBuffer;
class QOpenGLVertexArrayObject;
QT_END_NAMESPACE
class GLWidget : public QOpenGLWidget
{
Q_OBJECT
Q_PROPERTY(float z READ z WRITE setZ NOTIFY zChanged FINAL)
Q_PROPERTY(float r READ r WRITE setR NOTIFY rChanged FINAL)
Q_PROPERTY(float r2 READ r2 WRITE setR2 NOTIFY r2Changed FINAL)
public:
explicit GLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = {});
~GLWidget() override;
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
float z() const { return m_eye.z(); }
void setZ(float v);
float r() const { return m_r; }
void setR(float v);
float r2() const { return m_r2; }
void setR2(float v);
private Q_SLOTS:
void startSecondStage();
Q_SIGNALS:
void zChanged();
void rChanged();
void r2Changed();
private:
QOpenGLTexture *m_texture = nullptr;
QOpenGLShaderProgram *m_program = nullptr;
QOpenGLBuffer *m_vbo = nullptr;
QOpenGLVertexArrayObject *m_vao = nullptr;
Logo m_logo;
int m_projMatrixLoc = 0;
int m_camMatrixLoc = 0;
int m_worldMatrixLoc = 0;
int m_myMatrixLoc = 0;
int m_lightPosLoc = 0;
QMatrix4x4 m_proj;
QMatrix4x4 m_world;
QVector3D m_eye;
QVector3D m_target = {0, 0, -1};
bool m_uniformsDirty = true;
float m_r = 0;
float m_r2 = 0;
};

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>images/qtlogo.png</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,150 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "logo.h"
#include <qmath.h>
Logo::Logo()
{
m_data.resize(2500 * 6);
const GLfloat x1 = +0.06f;
const GLfloat y1 = -0.14f;
const GLfloat x2 = +0.14f;
const GLfloat y2 = -0.06f;
const GLfloat x3 = +0.08f;
const GLfloat y3 = +0.00f;
const GLfloat x4 = +0.30f;
const GLfloat y4 = +0.22f;
quad(x1, y1, x2, y2, y2, x2, y1, x1);
quad(x3, y3, x4, y4, y4, x4, y3, x3);
extrude(x1, y1, x2, y2);
extrude(x2, y2, y2, x2);
extrude(y2, x2, y1, x1);
extrude(y1, x1, x1, y1);
extrude(x3, y3, x4, y4);
extrude(x4, y4, y4, x4);
extrude(y4, x4, y3, x3);
const int NumSectors = 100;
for (int i = 0; i < NumSectors; ++i) {
GLfloat angle = (i * 2 * M_PI) / NumSectors;
GLfloat angleSin = qSin(angle);
GLfloat angleCos = qCos(angle);
const GLfloat x5 = 0.30f * angleSin;
const GLfloat y5 = 0.30f * angleCos;
const GLfloat x6 = 0.20f * angleSin;
const GLfloat y6 = 0.20f * angleCos;
angle = ((i + 1) * 2 * M_PI) / NumSectors;
angleSin = qSin(angle);
angleCos = qCos(angle);
const GLfloat x7 = 0.20f * angleSin;
const GLfloat y7 = 0.20f * angleCos;
const GLfloat x8 = 0.30f * angleSin;
const GLfloat y8 = 0.30f * angleCos;
quad(x5, y5, x6, y6, x7, y7, x8, y8);
extrude(x6, y6, x7, y7);
extrude(x8, y8, x5, y5);
}
}
void Logo::add(const QVector3D &v, const QVector3D &n)
{
GLfloat *p = m_data.data() + m_count;
*p++ = v.x();
*p++ = v.y();
*p++ = v.z();
*p++ = n.x();
*p++ = n.y();
*p++ = n.z();
m_count += 6;
}
void Logo::quad(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3, GLfloat x4, GLfloat y4)
{
QVector3D n = QVector3D::normal(QVector3D(x4 - x1, y4 - y1, 0.0f), QVector3D(x2 - x1, y2 - y1, 0.0f));
add(QVector3D(x1, y1, -0.05f), n);
add(QVector3D(x4, y4, -0.05f), n);
add(QVector3D(x2, y2, -0.05f), n);
add(QVector3D(x3, y3, -0.05f), n);
add(QVector3D(x2, y2, -0.05f), n);
add(QVector3D(x4, y4, -0.05f), n);
n = QVector3D::normal(QVector3D(x1 - x4, y1 - y4, 0.0f), QVector3D(x2 - x4, y2 - y4, 0.0f));
add(QVector3D(x4, y4, 0.05f), n);
add(QVector3D(x1, y1, 0.05f), n);
add(QVector3D(x2, y2, 0.05f), n);
add(QVector3D(x2, y2, 0.05f), n);
add(QVector3D(x3, y3, 0.05f), n);
add(QVector3D(x4, y4, 0.05f), n);
}
void Logo::extrude(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
{
QVector3D n = QVector3D::normal(QVector3D(0.0f, 0.0f, -0.1f), QVector3D(x2 - x1, y2 - y1, 0.0f));
add(QVector3D(x1, y1, +0.05f), n);
add(QVector3D(x1, y1, -0.05f), n);
add(QVector3D(x2, y2, +0.05f), n);
add(QVector3D(x2, y2, -0.05f), n);
add(QVector3D(x2, y2, +0.05f), n);
add(QVector3D(x1, y1, -0.05f), n);
}

View File

@ -0,0 +1,72 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#pragma once
#include <qopengl.h>
#include <QVector>
#include <QVector3D>
class Logo
{
public:
Logo();
const GLfloat *constData() const { return m_data.constData(); }
int count() const { return m_count; }
int vertexCount() const { return m_count / 6; }
private:
void quad(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3, GLfloat x4, GLfloat y4);
void extrude(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);
void add(const QVector3D &v, const QVector3D &n);
QVector<GLfloat> m_data;
int m_count = 0;
};

View File

@ -0,0 +1,109 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QDebug>
#include <QApplication>
#include <QSurfaceFormat>
#include <QOpenGLContext>
#include <FramelessHelper/Core/private/framelessconfig_p.h>
#include <clocale>
#include "mainwindow.h"
#include "../shared/log.h"
// This example demonstrates easy, cross-platform usage of OpenGL ES 3.0 functions via
// QOpenGLExtraFunctions in an application that works identically on desktop platforms
// with OpenGL 3.3 and mobile/embedded devices with OpenGL ES 3.0.
// The code is always the same, with the exception of two places: (1) the OpenGL context
// creation has to have a sufficiently high version number for the features that are in
// use, and (2) the shader code's version directive is different.
FRAMELESSHELPER_USE_NAMESPACE
int main(int argc, char *argv[])
{
std::setlocale(LC_ALL, "en_US.UTF-8");
Log::setup(FRAMELESSHELPER_STRING_LITERAL("openglwidget"));
// Not necessary, but better call this function, before the construction
// of any Q(Core|Gui)Application instances.
FramelessHelper::Widgets::initialize();
const auto application = std::make_unique<QApplication>(argc, argv);
// Must be called after QGuiApplication has been constructed, we are using
// some private functions from QPA which won't be available until there's
// a QGuiApplication instance.
FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
QSurfaceFormat fmt = {};
fmt.setDepthBufferSize(24);
// Request OpenGL 3.3 core or OpenGL ES 3.0.
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
qDebug() << "Requesting OpenGL 3.3 core context ...";
fmt.setVersion(3, 3);
fmt.setProfile(QSurfaceFormat::CoreProfile);
} else {
qDebug() << "Requesting OpenGL ES 3.0 context ...";
fmt.setVersion(3, 0);
}
QSurfaceFormat::setDefaultFormat(fmt);
const auto mainWindow = std::make_unique<MainWindow>();
mainWindow->show();
return QCoreApplication::exec();
}

View File

@ -0,0 +1,97 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "mainwindow.h"
#include "glwidget.h"
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qfileiconprovider.h>
#include <FramelessHelper/Widgets/framelesswidgetshelper.h>
#include <FramelessHelper/Widgets/standardtitlebar.h>
#include <FramelessHelper/Widgets/standardsystembutton.h>
#include "../shared/settings.h"
extern template void Settings::set<QRect>(const QString &, const QString &, const QRect &);
extern template void Settings::set<qreal>(const QString &, const QString &, const qreal &);
extern template QRect Settings::get<QRect>(const QString &, const QString &);
extern template qreal Settings::get<qreal>(const QString &, const QString &);
FRAMELESSHELPER_USE_NAMESPACE
using namespace Global;
FRAMELESSHELPER_STRING_CONSTANT(Geometry)
FRAMELESSHELPER_STRING_CONSTANT(DevicePixelRatio)
MainWindow::MainWindow(QWidget *parent) : FramelessWidget(parent)
{
initialize();
}
MainWindow::~MainWindow() = default;
void MainWindow::closeEvent(QCloseEvent *event)
{
if (!parent()) {
Settings::set({}, kGeometry, geometry());
Settings::set({}, kDevicePixelRatio, devicePixelRatioF());
}
FramelessWidget::closeEvent(event);
}
void MainWindow::initialize()
{
setWindowTitle(tr("FramelessHelper demo application - QOpenGLWidget"));
setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer));
resize(800, 600);
m_titleBar = new StandardTitleBar(this);
m_titleBar->setWindowIconVisible(true);
m_glWidget = new GLWidget(this);
const auto mainLayout = new QVBoxLayout(this);
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addWidget(m_titleBar);
mainLayout->addWidget(m_glWidget);
setLayout(mainLayout);
FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this);
helper->setTitleBarWidget(m_titleBar);
#ifndef Q_OS_MACOS
helper->setSystemButton(m_titleBar->minimizeButton(), SystemButtonType::Minimize);
helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize);
helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close);
#endif // Q_OS_MACOS
connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){
const auto savedGeometry = Settings::get<QRect>({}, kGeometry);
if (savedGeometry.isValid() && !parent()) {
const auto savedDpr = Settings::get<qreal>({}, kDevicePixelRatio);
// Qt doesn't support dpr < 1.
const qreal oldDpr = std::max(savedDpr, qreal(1));
const qreal scale = (devicePixelRatioF() / oldDpr);
setGeometry({savedGeometry.topLeft() * scale, savedGeometry.size() * scale});
} else {
helper->moveWindowToDesktopCenter();
}
});
}

View File

@ -0,0 +1,53 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <FramelessHelper/Widgets/framelesswidget.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBar;
FRAMELESSHELPER_END_NAMESPACE
class GLWidget;
class MainWindow : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget)
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(MainWindow)
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
protected:
void closeEvent(QCloseEvent *event) override;
private:
void initialize();
private:
FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar) *m_titleBar = nullptr;
GLWidget *m_glWidget = nullptr;
};

View File

@ -0,0 +1,100 @@
#[[
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Quick)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Quick)
set(SOURCES
../shared/log.h
../shared/log.cpp
../shared/settings.h
../shared/settings.cpp
main.cpp
quicksettings.h
quicksettings.cpp
)
if(${QT_VERSION} VERSION_LESS 6.2)
list(APPEND SOURCES resources.qrc)
endif()
if(WIN32)
enable_language(RC)
list(APPEND SOURCES
../shared/example.rc
../shared/example.manifest
)
endif()
add_executable(Quick ${SOURCES})
if(${QT_VERSION} VERSION_GREATER_EQUAL 6.2)
set(QML_SOURCES
"qml/Window.qml"
"qml/ApplicationWindow.qml"
"qml/HomePage.qml"
)
set_source_files_properties(${QML_SOURCES}
PROPERTIES QT_DISCARD_FILE_CONTENTS TRUE
)
qt_add_qml_module(Quick
URI Demo
VERSION 1.0
IMPORT_PATH "${PROJECT_BINARY_DIR}/qml"
OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/qml/Demo"
RESOURCE_PREFIX "/"
NO_RESOURCE_TARGET_PATH
IMPORTS
QtQuick/auto
DEPENDENCIES
org.wangwenx190.FramelessHelper/auto
QML_FILES ${QML_SOURCES}
#ENABLE_TYPE_COMPILER
)
qt_add_resources(Quick resources
PREFIX
"/"
FILES
"images/microsoft.svg"
)
endif()
target_link_libraries(Quick PRIVATE
Qt${QT_VERSION_MAJOR}::QuickPrivate
FramelessHelper::Quick
)
target_compile_definitions(Quick PRIVATE
$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>
)
include(../../src/core/cmakehelper.cmake)
setup_gui_app(Quick)
setup_compile_params(Quick)
if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT)
deploy_qt_runtime(Quick
QML_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
#QML_IMPORT_DIR "${PROJECT_BINARY_DIR}/qml" # Some applications failed to start.
)
endif()

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1661848097607" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3344" width="512" height="512" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M0 0h486.592v486.592H0z" fill="#F25022" p-id="3345"/>
<path d="M537.408 0H1024v486.592H537.408z" fill="#7FBA00" p-id="3346"/>
<path d="M0 537.408h486.592V1024H0z" fill="#00A4EF" p-id="3347"/>
<path d="M537.408 537.408H1024V1024H537.408z" fill="#FFB900" p-id="3348"/>
</svg>

After

Width:  |  Height:  |  Size: 636 B

View File

@ -0,0 +1,163 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef QMLTC_ENABLED
# define QMLTC_ENABLED 0
#endif
#include <QtGui/qguiapplication.h>
#include <QtQml/qqmlapplicationengine.h>
#include <QtQml/qqmlcontext.h>
#include <QtQuick/qquickwindow.h>
#include <FramelessHelper/Quick/framelessquickmodule.h>
#include <FramelessHelper/Core/private/framelessconfig_p.h>
#include <clocale>
#include "quicksettings.h"
#if QMLTC_ENABLED
# include <homepage.h>
#endif
#include "../shared/log.h"
FRAMELESSHELPER_USE_NAMESPACE
static constexpr const bool IS_MACOS_HOST =
#ifdef Q_OS_MACOS
true
#else // !Q_OS_MACOS
false
#endif // Q_OS_MACOS
;
int main(int argc, char *argv[])
{
std::setlocale(LC_ALL, "en_US.UTF-8");
Log::setup(FRAMELESSHELPER_STRING_LITERAL("quick"));
// Not necessary, but better call this function, before the construction
// of any Q(Core|Gui)Application instances.
FramelessHelper::Quick::initialize();
const auto application = std::make_unique<QGuiApplication>(argc, argv);
// Must be called after QGuiApplication has been constructed, we are using
// some private functions from QPA which won't be available until there's
// a QGuiApplication instance.
FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
// Enable QtRHI debug output if not explicitly requested by the user.
if (!qEnvironmentVariableIsSet("QSG_INFO")) {
qputenv("QSG_INFO", FRAMELESSHELPER_BYTEARRAY_LITERAL("1"));
}
// Allow testing other RHI backends through environment variable.
if (!qEnvironmentVariableIsSet("QSG_RHI_BACKEND")) {
// This line is not relevant to FramelessHelper, we change
// the default RHI backend to "software" just because it's
// stable enough and have exactly the same behavior on all
// supported platforms. Other backends may behave differently
// on different platforms and graphics cards, so I think they
// are not suitable for our demonstration.
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
#elif (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
#endif
}
if (!qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_STYLE")) {
// This line is not relevant to FramelessHelper, we change the default
// Qt Quick Controls theme to "Basic" (Qt6) or "Default" (Qt5) just
// because other themes will make our homemade system buttons look
// not good. This line has nothing to do with FramelessHelper itself.
qputenv("QT_QUICK_CONTROLS_STYLE", []() -> QByteArray {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
return FRAMELESSHELPER_BYTEARRAY_LITERAL("Basic");
#else
return FRAMELESSHELPER_BYTEARRAY_LITERAL("Default");
#endif
}());
}
// Enable some helpful debugging messages.
if (!qEnvironmentVariableIsSet("QML_IMPORT_TRACE")) {
qputenv("QML_IMPORT_TRACE", FRAMELESSHELPER_BYTEARRAY_LITERAL("1"));
}
const auto engine = std::make_unique<QQmlApplicationEngine>();
engine->rootContext()->setContextProperty(
FRAMELESSHELPER_STRING_LITERAL("$isMacOSHost"), QVariant(IS_MACOS_HOST));
#if (((QT_VERSION < QT_VERSION_CHECK(6, 2, 0)) || defined(QUICK_USE_QMAKE)) && !QMLTC_ENABLED)
// Don't forget to register our own custom QML types!
FramelessHelper::Quick::registerTypes(engine.get());
qmlRegisterSingletonType<QuickSettings>("Demo", 1, 0, "Settings",
[](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * {
Q_UNUSED(engine);
Q_UNUSED(scriptEngine);
return new QuickSettings;
});
#endif
#if !QMLTC_ENABLED
const QUrl mainUrl(FRAMELESSHELPER_STRING_LITERAL("qrc:///qml/HomePage.qml"));
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QObject::connect(engine.get(), &QQmlApplicationEngine::objectCreationFailed, qApp,
[](const QUrl &url){
qCritical() << "The QML engine failed to create component:" << url;
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
#elif !QMLTC_ENABLED
const QMetaObject::Connection connection = QObject::connect(
engine.get(), &QQmlApplicationEngine::objectCreated, &application,
[&mainUrl, &connection](QObject *object, const QUrl &url) {
if (url != mainUrl) {
return;
}
if (object) {
QObject::disconnect(connection);
} else {
QCoreApplication::exit(-1);
}
}, Qt::QueuedConnection);
#endif
#if !QMLTC_ENABLED
engine->load(mainUrl);
#endif
#if QMLTC_ENABLED
const auto homePage = std::make_unique<HomePage>(engine.get());
homePage->show();
#endif
return QCoreApplication::exec();
}

View File

@ -0,0 +1,111 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick
import QtQuick.Controls.Basic
import org.wangwenx190.FramelessHelper
import Demo
FramelessApplicationWindow {
id: window
objectName: "applicationWindow"
visible: false // Hide the window before we sets up it's correct size and position.
width: 800
height: 600
title: qsTr("FramelessHelper demo application - Qt Quick")
color: {
if (FramelessHelper.blurBehindWindowEnabled) {
return "transparent";
}
if (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) {
return FramelessUtils.defaultSystemDarkColor;
}
return FramelessUtils.defaultSystemLightColor;
}
Component.onDestruction: Settings.saveGeometry(window)
FramelessHelper.onReady: {
// Let FramelessHelper know what's our homemade title bar, otherwise
// our window won't be draggable.
FramelessHelper.titleBarItem = titleBar;
if (!$isMacOSHost) {
// Make our own items visible to the hit test and on Windows, enable
// the snap layout feature (available since Windows 11).
FramelessHelper.setSystemButton(titleBar.minimizeButton, FramelessHelperConstants.Minimize);
FramelessHelper.setSystemButton(titleBar.maximizeButton, FramelessHelperConstants.Maximize);
FramelessHelper.setSystemButton(titleBar.closeButton, FramelessHelperConstants.Close);
}
if (!Settings.restoreGeometry(window)) {
FramelessHelper.moveWindowToDesktopCenter();
}
// Finally, show the window after everything is setted.
window.visible = true;
}
Shortcut {
sequences: [ StandardKey.Cancel, StandardKey.Close, StandardKey.Quit ]
onActivated: {
if (window.visibility === Window.FullScreen) {
window.toggleFullScreen();
} else {
window.close();
}
}
}
Shortcut {
sequences: [ StandardKey.FullScreen, "ALT+RETURN" ]
onActivated: window.toggleFullScreen()
}
Timer {
interval: 500
running: true
repeat: true
onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss")
}
Label {
id: timeLabel
anchors.centerIn: parent
font {
pointSize: 70
bold: true
}
color: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) ? Qt.color("white") : Qt.color("black")
}
StandardTitleBar {
id: titleBar
anchors {
top: parent.top
topMargin: window.visibility === Window.Windowed ? 1 : 0
left: parent.left
right: parent.right
}
windowIcon: "qrc:///images/microsoft.svg"
windowIconVisible: true
}
}

View File

@ -0,0 +1,35 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQml
QtObject {
property Window window: Window{}
property ApplicationWindow applicationWindow: ApplicationWindow{}
Component.onCompleted: {
window.show();
applicationWindow.show();
}
}

View File

@ -0,0 +1,111 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import QtQuick
import QtQuick.Controls.Basic
import org.wangwenx190.FramelessHelper
import Demo
FramelessWindow {
id: window
objectName: "window"
visible: false // Hide the window before we sets up it's correct size and position.
width: 800
height: 600
title: qsTr("FramelessHelper demo application - Qt Quick")
color: {
if (FramelessHelper.blurBehindWindowEnabled) {
return "transparent";
}
if (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) {
return FramelessUtils.defaultSystemDarkColor;
}
return FramelessUtils.defaultSystemLightColor;
}
Component.onDestruction: Settings.saveGeometry(window)
FramelessHelper.onReady: {
// Let FramelessHelper know what's our homemade title bar, otherwise
// our window won't be draggable.
FramelessHelper.titleBarItem = titleBar;
if (!$isMacOSHost) {
// Make our own items visible to the hit test and on Windows, enable
// the snap layout feature (available since Windows 11).
FramelessHelper.setSystemButton(titleBar.minimizeButton, FramelessHelperConstants.Minimize);
FramelessHelper.setSystemButton(titleBar.maximizeButton, FramelessHelperConstants.Maximize);
FramelessHelper.setSystemButton(titleBar.closeButton, FramelessHelperConstants.Close);
}
if (!Settings.restoreGeometry(window)) {
FramelessHelper.moveWindowToDesktopCenter();
}
// Finally, show the window after everything is setted.
window.visible = true;
}
Shortcut {
sequences: [ StandardKey.Cancel, StandardKey.Close, StandardKey.Quit ]
onActivated: {
if (window.visibility === Window.FullScreen) {
window.toggleFullScreen();
} else {
window.close();
}
}
}
Shortcut {
sequences: [ StandardKey.FullScreen, "ALT+RETURN" ]
onActivated: window.toggleFullScreen()
}
Timer {
interval: 500
running: true
repeat: true
onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss")
}
Label {
id: timeLabel
anchors.centerIn: parent
font {
pointSize: 70
bold: true
}
color: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) ? Qt.color("white") : Qt.color("black")
}
StandardTitleBar {
id: titleBar
anchors {
top: parent.top
topMargin: window.visibility === Window.Windowed ? 1 : 0
left: parent.left
right: parent.right
}
windowIcon: "qrc:///images/microsoft.svg"
windowIconVisible: true
}
}

View File

@ -0,0 +1,18 @@
TEMPLATE = app
TARGET = Quick
QT += qml quick quickcontrols2
win32: CONFIG -= embed_manifest_exe
DEFINES += QUICK_USE_QMAKE
HEADERS += \
../shared/log.h \
../shared/settings.h \
quicksettings.h
SOURCES += \
../shared/log.cpp \
../shared/settings.cpp \
quicksettings.cpp \
main.cpp
RESOURCES += resources.qrc
win32: RC_FILE = ../shared/example.rc
include(../../qmake/core.pri)
include(../../qmake/quick.pri)

View File

@ -0,0 +1,71 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "quicksettings.h"
#include "../shared/settings.h"
FRAMELESSHELPER_STRING_CONSTANT(Geometry)
FRAMELESSHELPER_STRING_CONSTANT(DevicePixelRatio)
extern template void Settings::set<QRect>(const QString &, const QString &, const QRect &);
extern template void Settings::set<qreal>(const QString &, const QString &, const qreal &);
extern template QRect Settings::get<QRect>(const QString &, const QString &);
extern template qreal Settings::get<qreal>(const QString &, const QString &);
QuickSettings::QuickSettings(QObject *parent) : QObject(parent)
{
}
QuickSettings::~QuickSettings() = default;
void QuickSettings::saveGeometry(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
const QString objName = window->objectName();
Settings::set(objName, kGeometry, window->geometry());
Settings::set(objName, kDevicePixelRatio, window->devicePixelRatio());
}
bool QuickSettings::restoreGeometry(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return false;
}
const QString objName = window->objectName();
const auto savedGeometry = Settings::get<QRect>(objName, kGeometry);
if (!savedGeometry.isValid()) {
return false;
}
const auto savedDpr = Settings::get<qreal>(objName, kDevicePixelRatio);
// Qt doesn't support dpr < 1.
const qreal oldDpr = std::max(savedDpr, qreal(1));
const qreal scale = (window->devicePixelRatio() / oldDpr);
window->setGeometry({savedGeometry.topLeft() * scale, savedGeometry.size() * scale});
return true;
}

View File

@ -0,0 +1,48 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <QtGui/qwindow.h>
#include <FramelessHelper/Quick/framelesshelperquick_global.h>
class QuickSettings : public QObject
{
Q_OBJECT
#ifdef QML_NAMED_ELEMENT
QML_NAMED_ELEMENT(Settings)
#endif
#ifdef QML_SINGLETON
QML_SINGLETON
#endif
Q_DISABLE_COPY_MOVE(QuickSettings)
public:
explicit QuickSettings(QObject *parent = nullptr);
~QuickSettings() override;
public Q_SLOTS:
void saveGeometry(QWindow *window);
Q_NODISCARD bool restoreGeometry(QWindow *window);
};

View File

@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/">
<file>images/microsoft.svg</file>
<file>qml/Window.qml</file>
<file>qml/ApplicationWindow.qml</file>
<file>qml/HomePage.qml</file>
</qresource>
</RCC>

View File

@ -0,0 +1,28 @@
:: MIT License
::
:: Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
::
:: Permission is hereby granted, free of charge, to any person obtaining a copy
:: of this software and associated documentation files (the "Software"), to deal
:: in the Software without restriction, including without limitation the rights
:: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
:: copies of the Software, and to permit persons to whom the Software is
:: furnished to do so, subject to the following conditions:
::
:: The above copyright notice and this permission notice shall be included in
:: all copies or substantial portions of the Software.
::
:: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
:: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
:: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
:: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
:: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
:: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
:: SOFTWARE.
@echo off
setlocal
set QSG_RHI_BACKEND=d3d11
"%~dp0Quick.exe"
endlocal
exit /b 0

View File

@ -0,0 +1,28 @@
:: MIT License
::
:: Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
::
:: Permission is hereby granted, free of charge, to any person obtaining a copy
:: of this software and associated documentation files (the "Software"), to deal
:: in the Software without restriction, including without limitation the rights
:: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
:: copies of the Software, and to permit persons to whom the Software is
:: furnished to do so, subject to the following conditions:
::
:: The above copyright notice and this permission notice shall be included in
:: all copies or substantial portions of the Software.
::
:: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
:: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
:: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
:: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
:: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
:: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
:: SOFTWARE.
@echo off
setlocal
set QSG_RHI_BACKEND=opengl
"%~dp0Quick.exe"
endlocal
exit /b 0

View File

@ -0,0 +1,28 @@
:: MIT License
::
:: Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
::
:: Permission is hereby granted, free of charge, to any person obtaining a copy
:: of this software and associated documentation files (the "Software"), to deal
:: in the Software without restriction, including without limitation the rights
:: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
:: copies of the Software, and to permit persons to whom the Software is
:: furnished to do so, subject to the following conditions:
::
:: The above copyright notice and this permission notice shall be included in
:: all copies or substantial portions of the Software.
::
:: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
:: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
:: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
:: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
:: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
:: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
:: SOFTWARE.
@echo off
setlocal
set QSG_RHI_BACKEND=vulkan
"%~dp0Quick.exe"
endlocal
exit /b 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="org.wangwenx190.framelesshelper.demo" version="1.0.0.0"/>
<description>FramelessHelper Demo</description>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 Version 1809 (October 2018 Update) -->
<maxversiontested Id="10.0.17763.0"/>
<!-- Windows 10 Version 1903 (May 2019 Update) -->
<maxversiontested Id="10.0.18362.0"/>
<!-- Windows 10 Version 1909 (November 2019 Update) -->
<maxversiontested Id="10.0.18363.0"/>
<!-- Windows 10 Version 2004 (May 2020 Update) -->
<maxversiontested Id="10.0.19041.0"/>
<!-- Windows 10 Version 20H2 (October 2020 Update) -->
<maxversiontested Id="10.0.19042.0"/>
<!-- Windows 10 Version 21H1 (May 2021 Update) -->
<maxversiontested Id="10.0.19043.0"/>
<!-- Windows 10 Version 21H2 (November 2021 Update) -->
<maxversiontested Id="10.0.19044.0"/>
<!-- Windows 10 Version 22H2 (October 2022 Update) -->
<maxversiontested Id="10.0.19045.0"/>
<!-- Windows 11 Version 21H2 -->
<maxversiontested Id="10.0.22000.0"/>
<!-- Windows 11 Version 22H2 (October 2022 Update) -->
<maxversiontested Id="10.0.22621.0"/>
<!-- Windows Vista and Windows Server 2008 -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7 and Windows Server 2008 R2 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8 and Windows Server 2012 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 8.1 and Windows Server 2012 R2 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
<printerDriverIsolation xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">True</printerDriverIsolation>
<disableWindowFiltering xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">True</disableWindowFiltering>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">True</longPathAware>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
<heapType xmlns="http://schemas.microsoft.com/SMI/2020/WindowsSettings">SegmentHeap</heapType>
</windowsSettings>
</application>
</assembly>

View File

@ -0,0 +1,94 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "log.h"
#include <QtCore/qdebug.h>
#include <QtCore/qfile.h>
#include <QtCore/qtextstream.h>
#include <iostream>
#include <framelesshelpercore_global.h>
#ifndef QT_ENDL
# if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
# define QT_ENDL Qt::endl
# else
# define QT_ENDL endl
# endif
#endif
static QString g_app = {};
static bool g_logError = false;
static std::unique_ptr<QFile> g_logFile = nullptr;
static std::unique_ptr<QTextStream> g_logStream = nullptr;
static inline void myMessageHandler(const QtMsgType type, const QMessageLogContext &context, const QString &message)
{
if (message.isEmpty()) {
return;
}
const QString finalMessage = qFormatLogMessage(type, context, message).trimmed();
if ((type == QtInfoMsg) || (type == QtDebugMsg)) {
std::cout << qUtf8Printable(finalMessage) << std::endl;
} else {
std::cerr << qUtf8Printable(finalMessage) << std::endl;
}
if (g_logError) {
return;
}
if (!g_logFile) {
g_logFile = std::make_unique<QFile>();
g_logFile->setFileName(FRAMELESSHELPER_STRING_LITERAL("debug-%1.log").arg(g_app));
if (!g_logFile->open(QFile::WriteOnly | QFile::Text | QFile::Append)) {
std::cerr << "Can't open file to write: " << qUtf8Printable(g_logFile->errorString()) << std::endl;
g_logFile.reset();
g_logError = true;
return;
}
}
if (!g_logStream) {
g_logStream = std::make_unique<QTextStream>();
g_logStream->setDevice(g_logFile.get());
}
(*g_logStream) << finalMessage << QT_ENDL;
}
void Log::setup(const QString &app)
{
Q_ASSERT(!app.isEmpty());
if (app.isEmpty()) {
return;
}
static bool once = false;
if (once) {
return;
}
once = true;
g_app = app;
qSetMessagePattern(FRAMELESSHELPER_STRING_LITERAL(
"[%{time yyyy/MM/dd hh:mm:ss.zzz}] <%{if-info}INFO%{endif}%{if-debug}DEBUG"
"%{endif}%{if-warning}WARNING%{endif}%{if-critical}CRITICAL%{endif}%{if-fatal}"
"FATAL%{endif}> %{if-category}%{category}: %{endif}%{message}"));
qInstallMessageHandler(myMessageHandler);
}

View File

@ -0,0 +1,32 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <QtCore/qstring.h>
namespace Log
{
void setup(const QString &app);
} // namespace Log

View File

@ -0,0 +1,83 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "settings.h"
#include <QtCore/qsettings.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qrect.h>
#include <framelesshelpercore_global.h>
static std::unique_ptr<QSettings> g_settings = nullptr;
[[nodiscard]] static inline QSettings *appConfigFile()
{
const QFileInfo fileInfo(QCoreApplication::applicationFilePath());
const QString iniFileName = fileInfo.completeBaseName() + FRAMELESSHELPER_STRING_LITERAL(".ini");
const QString iniFilePath = fileInfo.canonicalPath() + u'/' + iniFileName;
return new QSettings(iniFilePath, QSettings::IniFormat);
}
[[nodiscard]] static inline QString appKey(const QString &id, const QString &key)
{
Q_ASSERT(!key.isEmpty());
if (key.isEmpty()) {
return {};
}
return (id.isEmpty() ? key : (id + u'/' + key));
}
template<typename T>
void Settings::set(const QString &id, const QString &key, const T &data)
{
Q_ASSERT(!key.isEmpty());
if (key.isEmpty()) {
return;
}
if (!g_settings) {
g_settings.reset(appConfigFile());
}
g_settings->setValue(appKey(id, key), data);
}
template<typename T>
T Settings::get(const QString &id, const QString &key)
{
Q_ASSERT(!key.isEmpty());
if (key.isEmpty()) {
return T{};
}
if (!g_settings) {
g_settings.reset(appConfigFile());
}
return qvariant_cast<T>(g_settings->value(appKey(id, key)));
}
template void Settings::set<QRect>(const QString &, const QString &, const QRect &);
template void Settings::set<qreal>(const QString &, const QString &, const qreal &);
template void Settings::set<QByteArray>(const QString &, const QString &, const QByteArray &);
template QRect Settings::get<QRect>(const QString &, const QString &);
template qreal Settings::get<qreal>(const QString &, const QString &);
template QByteArray Settings::get<QByteArray>(const QString &, const QString &);

View File

@ -0,0 +1,36 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <QtCore/qstring.h>
namespace Settings
{
template<typename T>
void set(const QString &id, const QString &key, const T &data);
template<typename T>
[[nodiscard]] T get(const QString &id, const QString &key);
} // namespace Settings

View File

@ -0,0 +1,55 @@
#[[
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
set(SOURCES
../shared/log.h
../shared/log.cpp
../shared/settings.h
../shared/settings.cpp
widget.h
widget.cpp
main.cpp
)
if(WIN32)
enable_language(RC)
list(APPEND SOURCES
../shared/example.rc
../shared/example.manifest
)
endif()
add_executable(Widget ${SOURCES})
target_link_libraries(Widget PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
FramelessHelper::Widgets
)
include(../../src/core/cmakehelper.cmake)
setup_gui_app(Widget)
setup_compile_params(Widget)
if(FRAMELESSHELPER_EXAMPLES_DEPLOYQT)
deploy_qt_runtime(Widget)
endif()

View File

@ -0,0 +1,68 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <QtWidgets/qapplication.h>
#include <FramelessHelper/Core/private/framelessconfig_p.h>
#include <clocale>
#include "widget.h"
#include "../shared/log.h"
FRAMELESSHELPER_USE_NAMESPACE
int main(int argc, char *argv[])
{
std::setlocale(LC_ALL, "en_US.UTF-8");
Log::setup(FRAMELESSHELPER_STRING_LITERAL("widget"));
// Not necessary, but better call this function, before the construction
// of any Q(Core|Gui)Application instances.
FramelessHelper::Widgets::initialize();
#if 0
if (!qEnvironmentVariableIsSet("QT_WIDGETS_RHI")) {
qputenv("QT_WIDGETS_RHI", FRAMELESSHELPER_BYTEARRAY_LITERAL("1"));
}
#endif
const auto application = std::make_unique<QApplication>(argc, argv);
// Must be called after QGuiApplication has been constructed, we are using
// some private functions from QPA which won't be available until there's
// a QGuiApplication instance.
FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
const auto window1 = std::make_unique<Widget>();
window1->setObjectName(FRAMELESSHELPER_STRING_LITERAL("window1"));
window1->show();
const auto window2 = std::make_unique<Widget>();
window2->setObjectName(FRAMELESSHELPER_STRING_LITERAL("window2"));
window2->show();
return QCoreApplication::exec();
}

View File

@ -0,0 +1,164 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "widget.h"
#include <QtCore/qdatetime.h>
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
# include <QtGui/qshortcut.h>
#else
# include <QtWidgets/qshortcut.h>
#endif
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qfileiconprovider.h>
#include <FramelessHelper/Core/framelessmanager.h>
#include <FramelessHelper/Core/utils.h>
#include <FramelessHelper/Widgets/framelesswidgetshelper.h>
#include <FramelessHelper/Widgets/standardtitlebar.h>
#include <FramelessHelper/Widgets/standardsystembutton.h>
#include "../shared/settings.h"
extern template void Settings::set<QRect>(const QString &, const QString &, const QRect &);
extern template void Settings::set<qreal>(const QString &, const QString &, const qreal &);
extern template QRect Settings::get<QRect>(const QString &, const QString &);
extern template qreal Settings::get<qreal>(const QString &, const QString &);
FRAMELESSHELPER_USE_NAMESPACE
using namespace Global;
FRAMELESSHELPER_STRING_CONSTANT(Geometry)
FRAMELESSHELPER_STRING_CONSTANT(DevicePixelRatio)
Widget::Widget(QWidget *parent) : FramelessWidget(parent)
{
initialize();
startTimer(500);
connect(FramelessManager::instance(), &FramelessManager::systemThemeChanged, this, &Widget::updateStyleSheet);
}
Widget::~Widget() = default;
void Widget::timerEvent(QTimerEvent *event)
{
FramelessWidget::timerEvent(event);
if (m_clockLabel) {
m_clockLabel->setText(QTime::currentTime().toString(FRAMELESSHELPER_STRING_LITERAL("hh:mm:ss")));
}
}
void Widget::closeEvent(QCloseEvent *event)
{
if (!parent()) {
const QString objName = objectName();
Settings::set(objName, kGeometry, geometry());
Settings::set(objName, kDevicePixelRatio, devicePixelRatioF());
}
FramelessWidget::closeEvent(event);
}
void Widget::initialize()
{
setWindowTitle(tr("FramelessHelper demo application - Qt Widgets"));
setWindowIcon(QFileIconProvider().icon(QFileIconProvider::Computer));
resize(800, 600);
m_titleBar = new StandardTitleBar(this);
m_titleBar->setWindowIconVisible(true);
m_clockLabel = new QLabel(this);
m_clockLabel->setFrameShape(QFrame::NoFrame);
QFont clockFont = font();
clockFont.setBold(true);
clockFont.setPointSize(70);
m_clockLabel->setFont(clockFont);
const auto contentLayout = new QHBoxLayout;
contentLayout->setContentsMargins(0, 0, 0, 0);
contentLayout->setSpacing(0);
contentLayout->addStretch();
contentLayout->addWidget(m_clockLabel);
contentLayout->addStretch();
const auto mainLayout = new QVBoxLayout(this);
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addWidget(m_titleBar);
mainLayout->addLayout(contentLayout);
setLayout(mainLayout);
updateStyleSheet();
m_cancelShortcut = new QShortcut(this);
m_cancelShortcut->setKey(FRAMELESSHELPER_STRING_LITERAL("ESC"));
connect(m_cancelShortcut, &QShortcut::activated, this, [this](){
if (isFullScreen()) {
Q_EMIT m_fullScreenShortcut->activated();
} else {
close();
}
});
m_fullScreenShortcut = new QShortcut(this);
m_fullScreenShortcut->setKey(FRAMELESSHELPER_STRING_LITERAL("ALT+RETURN"));
connect(m_fullScreenShortcut, &QShortcut::activated, this, [this](){
if (isFullScreen()) {
setWindowState(windowState() & ~Qt::WindowFullScreen);
} else {
showFullScreen();
}
});
FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this);
helper->setTitleBarWidget(m_titleBar);
#ifndef Q_OS_MACOS
helper->setSystemButton(m_titleBar->minimizeButton(), SystemButtonType::Minimize);
helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize);
helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close);
#endif // Q_OS_MACOS
connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){
const QString objName = objectName();
const auto savedGeometry = Settings::get<QRect>(objName, kGeometry);
if (savedGeometry.isValid() && !parent()) {
const auto savedDpr = Settings::get<qreal>(objName, kDevicePixelRatio);
// Qt doesn't support dpr < 1.
const qreal oldDpr = std::max(savedDpr, qreal(1));
const qreal scale = (devicePixelRatioF() / oldDpr);
setGeometry({savedGeometry.topLeft() * scale, savedGeometry.size() * scale});
} else {
helper->moveWindowToDesktopCenter();
}
});
}
void Widget::updateStyleSheet()
{
const bool dark = Utils::shouldAppsUseDarkMode();
const QColor clockLabelTextColor = (dark ? kDefaultWhiteColor : kDefaultBlackColor);
m_clockLabel->setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("background-color: transparent; color: %1;")
.arg(clockLabelTextColor.name()));
if (FramelessWidgetsHelper::get(this)->isBlurBehindWindowEnabled()) {
setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("background-color: transparent;"));
} else {
const QColor windowBackgroundColor = (dark ? kDefaultSystemDarkColor : kDefaultSystemLightColor);
setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("background-color: %1;").arg(windowBackgroundColor.name()));
}
update();
}

View File

@ -0,0 +1,62 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <FramelessHelper/Widgets/framelesswidget.h>
QT_BEGIN_NAMESPACE
class QLabel;
class QShortcut;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBar;
FRAMELESSHELPER_END_NAMESPACE
class Widget : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget)
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(Widget)
public:
explicit Widget(QWidget *parent = nullptr);
~Widget() override;
protected:
void timerEvent(QTimerEvent *event) override;
void closeEvent(QCloseEvent *event) override;
private:
void initialize();
private Q_SLOTS:
void updateStyleSheet();
private:
QLabel *m_clockLabel = nullptr;
FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar) *m_titleBar = nullptr;
QShortcut *m_fullScreenShortcut = nullptr;
QShortcut *m_cancelShortcut = nullptr;
};

View File

@ -0,0 +1,17 @@
TEMPLATE = app
TARGET = Widget
QT += widgets
win32: CONFIG -= embed_manifest_exe
DEFINES += WIDGET_USE_QMAKE
HEADERS += \
../shared/log.h \
../shared/settings.h \
widget.h
SOURCES += \
../shared/log.cpp \
../shared/settings.cpp \
widget.cpp \
main.cpp
win32: RC_FILE = ../shared/example.rc
include(../../qmake/core.pri)
include(../../qmake/widgets.pri)

View File

@ -0,0 +1 @@
#include <chromepalette.h>

View File

@ -0,0 +1 @@
#include <framelesshelper_linux.h>

View File

@ -0,0 +1 @@
#include <framelesshelper_qt.h>

View File

@ -0,0 +1 @@
#include <framelesshelper_win.h>

View File

@ -0,0 +1 @@
#include <framelesshelper_windows.h>

View File

@ -0,0 +1 @@
#include <framelessmanager.h>

View File

@ -0,0 +1 @@
#include <framelesshelpercore_global.h>

View File

@ -0,0 +1 @@
#include <micamaterial.h>

View File

@ -0,0 +1 @@
#include <utils.h>

View File

@ -0,0 +1 @@
#include <windowborderpainter.h>

View File

@ -0,0 +1,129 @@
/*
* MIT License
*
* Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelpercore_global.h"
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcChromePalette)
class ChromePalettePrivate;
class FRAMELESSHELPER_CORE_API ChromePalette : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(ChromePalette)
Q_DISABLE_COPY_MOVE(ChromePalette)
Q_PROPERTY(QColor titleBarActiveBackgroundColor READ titleBarActiveBackgroundColor
WRITE setTitleBarActiveBackgroundColor RESET resetTitleBarActiveBackgroundColor
NOTIFY titleBarActiveBackgroundColorChanged FINAL)
Q_PROPERTY(QColor titleBarInactiveBackgroundColor READ titleBarInactiveBackgroundColor
WRITE setTitleBarInactiveBackgroundColor RESET resetTitleBarInactiveBackgroundColor
NOTIFY titleBarInactiveBackgroundColorChanged FINAL)
Q_PROPERTY(QColor titleBarActiveForegroundColor READ titleBarActiveForegroundColor
WRITE setTitleBarActiveForegroundColor RESET resetTitleBarActiveForegroundColor
NOTIFY titleBarActiveForegroundColorChanged FINAL)
Q_PROPERTY(QColor titleBarInactiveForegroundColor READ titleBarInactiveForegroundColor
WRITE setTitleBarInactiveForegroundColor RESET resetTitleBarInactiveForegroundColor
NOTIFY titleBarInactiveForegroundColorChanged FINAL)
Q_PROPERTY(QColor chromeButtonNormalColor READ chromeButtonNormalColor
WRITE setChromeButtonNormalColor RESET resetChromeButtonNormalColor
NOTIFY chromeButtonNormalColorChanged FINAL)
Q_PROPERTY(QColor chromeButtonHoverColor READ chromeButtonHoverColor
WRITE setChromeButtonHoverColor RESET resetChromeButtonHoverColor
NOTIFY chromeButtonHoverColorChanged FINAL)
Q_PROPERTY(QColor chromeButtonPressColor READ chromeButtonPressColor
WRITE setChromeButtonPressColor RESET resetChromeButtonPressColor
NOTIFY chromeButtonPressColorChanged FINAL)
Q_PROPERTY(QColor closeButtonNormalColor READ closeButtonNormalColor
WRITE setCloseButtonNormalColor RESET resetCloseButtonNormalColor
NOTIFY closeButtonNormalColorChanged FINAL)
Q_PROPERTY(QColor closeButtonHoverColor READ closeButtonHoverColor
WRITE setCloseButtonHoverColor RESET resetCloseButtonHoverColor
NOTIFY closeButtonHoverColorChanged FINAL)
Q_PROPERTY(QColor closeButtonPressColor READ closeButtonPressColor
WRITE setCloseButtonPressColor RESET resetCloseButtonPressColor
NOTIFY closeButtonPressColorChanged FINAL)
public:
explicit ChromePalette(QObject *parent = nullptr);
~ChromePalette() override;
Q_NODISCARD QColor titleBarActiveBackgroundColor() const;
Q_NODISCARD QColor titleBarInactiveBackgroundColor() const;
Q_NODISCARD QColor titleBarActiveForegroundColor() const;
Q_NODISCARD QColor titleBarInactiveForegroundColor() const;
Q_NODISCARD QColor chromeButtonNormalColor() const;
Q_NODISCARD QColor chromeButtonHoverColor() const;
Q_NODISCARD QColor chromeButtonPressColor() const;
Q_NODISCARD QColor closeButtonNormalColor() const;
Q_NODISCARD QColor closeButtonHoverColor() const;
Q_NODISCARD QColor closeButtonPressColor() const;
public Q_SLOTS:
void setTitleBarActiveBackgroundColor(const QColor &value);
void resetTitleBarActiveBackgroundColor();
void setTitleBarInactiveBackgroundColor(const QColor &value);
void resetTitleBarInactiveBackgroundColor();
void setTitleBarActiveForegroundColor(const QColor &value);
void resetTitleBarActiveForegroundColor();
void setTitleBarInactiveForegroundColor(const QColor &value);
void resetTitleBarInactiveForegroundColor();
void setChromeButtonNormalColor(const QColor &value);
void resetChromeButtonNormalColor();
void setChromeButtonHoverColor(const QColor &value);
void resetChromeButtonHoverColor();
void setChromeButtonPressColor(const QColor &value);
void resetChromeButtonPressColor();
void setCloseButtonNormalColor(const QColor &value);
void resetCloseButtonNormalColor();
void setCloseButtonHoverColor(const QColor &value);
void resetCloseButtonHoverColor();
void setCloseButtonPressColor(const QColor &value);
void resetCloseButtonPressColor();
Q_SIGNALS:
void titleBarActiveBackgroundColorChanged();
void titleBarInactiveBackgroundColorChanged();
void titleBarActiveForegroundColorChanged();
void titleBarInactiveForegroundColorChanged();
void chromeButtonNormalColorChanged();
void chromeButtonHoverColorChanged();
void chromeButtonPressColorChanged();
void closeButtonNormalColorChanged();
void closeButtonHoverColorChanged();
void closeButtonPressColorChanged();
void titleBarColorChanged();
void chromeButtonColorChanged();
private:
QScopedPointer<ChromePalettePrivate> d_ptr;
};
FRAMELESSHELPER_END_NAMESPACE
Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(ChromePalette))

Some files were not shown because too many files have changed in this diff Show More