diff --git a/examples/mainwindow/main.cpp b/examples/mainwindow/main.cpp index e2f6e880..410935ce 100644 --- a/examples/mainwindow/main.cpp +++ b/examples/mainwindow/main.cpp @@ -23,13 +23,10 @@ */ #include -#include #include "mainwindow.h" FRAMELESSHELPER_USE_NAMESPACE -using namespace Global; - int main(int argc, char *argv[]) { // Not necessary, but better call this function, before the construction @@ -38,8 +35,6 @@ int main(int argc, char *argv[]) QApplication application(argc, argv); - FramelessConfig::instance()->set(Option::CenterWindowBeforeShow); - MainWindow mainWindow; mainWindow.show(); diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp index 42ae850a..9b98b0b2 100644 --- a/examples/mainwindow/mainwindow.cpp +++ b/examples/mainwindow/mainwindow.cpp @@ -24,6 +24,10 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include +#include +#include +#include #include #include #include @@ -34,6 +38,18 @@ FRAMELESSHELPER_USE_NAMESPACE using namespace Global; +FRAMELESSHELPER_STRING_CONSTANT2(GeoKeyPath, "Window/Geometry") +FRAMELESSHELPER_STRING_CONSTANT2(StateKeyPath, "Window/State") + +[[nodiscard]] static inline QSettings *appConfigFile() +{ + const QFileInfo fileInfo(QCoreApplication::applicationFilePath()); + const QString iniFileName = fileInfo.completeBaseName() + FRAMELESSHELPER_STRING_LITERAL(".ini"); + const QString iniFilePath = fileInfo.canonicalPath() + QDir::separator() + iniFileName; + const auto settings = new QSettings(iniFilePath, QSettings::IniFormat); + return settings; +} + MainWindow::MainWindow(QWidget *parent, const Qt::WindowFlags flags) : FramelessMainWindow(parent, flags) { initialize(); @@ -41,6 +57,14 @@ MainWindow::MainWindow(QWidget *parent, const Qt::WindowFlags flags) : Frameless MainWindow::~MainWindow() = default; +void MainWindow::closeEvent(QCloseEvent *event) +{ + const QScopedPointer settings(appConfigFile()); + settings->setValue(kGeoKeyPath, saveGeometry()); + settings->setValue(kStateKeyPath, saveState()); + FramelessMainWindow::closeEvent(event); +} + void MainWindow::initialize() { m_titleBar.reset(new StandardTitleBar(this)); @@ -60,6 +84,19 @@ void MainWindow::initialize() helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize); helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close); helper->setHitTestVisible(mb); // IMPORTANT! + connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){ + const QScopedPointer settings(appConfigFile()); + const QByteArray geoData = settings->value(kGeoKeyPath).toByteArray(); + const QByteArray stateData = settings->value(kStateKeyPath).toByteArray(); + if (geoData.isEmpty()) { + helper->moveWindowToDesktopCenter(); + } else { + restoreGeometry(geoData); + } + if (!stateData.isEmpty()) { + restoreState(stateData); + } + }); setWindowTitle(tr("FramelessHelper demo application - Qt MainWindow")); } diff --git a/examples/mainwindow/mainwindow.h b/examples/mainwindow/mainwindow.h index 22e377ac..ed9643cf 100644 --- a/examples/mainwindow/mainwindow.h +++ b/examples/mainwindow/mainwindow.h @@ -44,6 +44,9 @@ class MainWindow : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessMainWindow) explicit MainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {}); ~MainWindow() override; +protected: + void closeEvent(QCloseEvent *event) override; + private: void initialize(); diff --git a/examples/openglwidget/main.cpp b/examples/openglwidget/main.cpp index 51739c61..1c8523bc 100644 --- a/examples/openglwidget/main.cpp +++ b/examples/openglwidget/main.cpp @@ -52,8 +52,6 @@ #include #include #include -#include -#include #include "mainwindow.h" // This example demonstrates easy, cross-platform usage of OpenGL ES 3.0 functions via @@ -66,8 +64,6 @@ FRAMELESSHELPER_USE_NAMESPACE -using namespace Global; - int main(int argc, char *argv[]) { // Not necessary, but better call this function, before the construction @@ -76,8 +72,6 @@ int main(int argc, char *argv[]) QApplication application(argc, argv); - FramelessConfig::instance()->set(Option::CenterWindowBeforeShow); - QSurfaceFormat fmt = {}; fmt.setDepthBufferSize(24); diff --git a/examples/openglwidget/mainwindow.cpp b/examples/openglwidget/mainwindow.cpp index 907621d3..831e5241 100644 --- a/examples/openglwidget/mainwindow.cpp +++ b/examples/openglwidget/mainwindow.cpp @@ -24,6 +24,10 @@ #include "mainwindow.h" #include "glwidget.h" +#include +#include +#include +#include #include #include #include @@ -33,6 +37,17 @@ FRAMELESSHELPER_USE_NAMESPACE using namespace Global; +FRAMELESSHELPER_STRING_CONSTANT2(IniKeyPath, "Window/Geometry") + +[[nodiscard]] static inline QSettings *appConfigFile() +{ + const QFileInfo fileInfo(QCoreApplication::applicationFilePath()); + const QString iniFileName = fileInfo.completeBaseName() + FRAMELESSHELPER_STRING_LITERAL(".ini"); + const QString iniFilePath = fileInfo.canonicalPath() + QDir::separator() + iniFileName; + const auto settings = new QSettings(iniFilePath, QSettings::IniFormat); + return settings; +} + MainWindow::MainWindow(QWidget *parent) : FramelessWidget(parent) { initialize(); @@ -40,6 +55,13 @@ MainWindow::MainWindow(QWidget *parent) : FramelessWidget(parent) MainWindow::~MainWindow() = default; +void MainWindow::closeEvent(QCloseEvent *event) +{ + const QScopedPointer settings(appConfigFile()); + settings->setValue(kIniKeyPath, saveGeometry()); + FramelessWidget::closeEvent(event); +} + void MainWindow::initialize() { resize(800, 600); @@ -58,4 +80,13 @@ void MainWindow::initialize() helper->setSystemButton(m_titleBar->minimizeButton(), SystemButtonType::Minimize); helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize); helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close); + connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){ + const QScopedPointer settings(appConfigFile()); + const QByteArray data = settings->value(kIniKeyPath).toByteArray(); + if (data.isEmpty()) { + helper->moveWindowToDesktopCenter(); + } else { + restoreGeometry(data); + } + }); } diff --git a/examples/openglwidget/mainwindow.h b/examples/openglwidget/mainwindow.h index cdbabe65..01644355 100644 --- a/examples/openglwidget/mainwindow.h +++ b/examples/openglwidget/mainwindow.h @@ -41,6 +41,9 @@ class MainWindow : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget) explicit MainWindow(QWidget *parent = nullptr); ~MainWindow() override; +protected: + void closeEvent(QCloseEvent *event) override; + private: void initialize(); diff --git a/examples/quick/CMakeLists.txt b/examples/quick/CMakeLists.txt index 538f26b7..01b2bff0 100644 --- a/examples/quick/CMakeLists.txt +++ b/examples/quick/CMakeLists.txt @@ -28,6 +28,8 @@ find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS QuickControls2) set(SOURCES qml.qrc main.cpp + settings.h + settings.cpp ) if(WIN32) diff --git a/examples/quick/MainWindow.qml b/examples/quick/MainWindow.qml index 77619483..1dc182e8 100644 --- a/examples/quick/MainWindow.qml +++ b/examples/quick/MainWindow.qml @@ -26,14 +26,33 @@ import QtQuick 2.0 import QtQuick.Window 2.0 import QtQuick.Controls 2.0 import org.wangwenx190.FramelessHelper 1.0 +import Demo 1.0 FramelessWindow { id: 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: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) ? FramelessUtils.defaultSystemDarkColor : FramelessUtils.defaultSystemLightColor + onClosing: Settings.saveGeometry(window) + + FramelessHelper.onReady: { + // Let FramelessHelper know what's our homemade title bar, otherwise + // our window won't be draggable. + FramelessHelper.titleBarItem = titleBar; + // Make our own items visible to the hit test and on Windows, enable + // the snap layouts 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; + } Timer { interval: 500 @@ -55,19 +74,9 @@ FramelessWindow { StandardTitleBar { id: titleBar anchors { - top: window.topBorderBottom + top: window.topBorderBottom // IMPORTANT! left: parent.left right: parent.right } - Component.onCompleted: { - // Make our homemade title bar draggable, and open the system menu - // when the user right clicks on the title bar area. - FramelessHelper.titleBarItem = titleBar; - // 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); - } } } diff --git a/examples/quick/main.cpp b/examples/quick/main.cpp index d86ae518..fddccfbe 100644 --- a/examples/quick/main.cpp +++ b/examples/quick/main.cpp @@ -27,12 +27,10 @@ #include #include #include -#include +#include "settings.h" FRAMELESSHELPER_USE_NAMESPACE -using namespace Global; - int main(int argc, char *argv[]) { // Not necessary, but better call this function, before the construction @@ -41,8 +39,6 @@ int main(int argc, char *argv[]) QGuiApplication application(argc, argv); - FramelessConfig::instance()->set(Option::CenterWindowBeforeShow); - // Allow testing other RHI backends through environment variable. if (!qEnvironmentVariableIsSet("QSG_RHI_BACKEND")) { // This line is not relevant to FramelessHelper, we change @@ -63,6 +59,13 @@ int main(int argc, char *argv[]) // Don't forget to register our own custom QML types! FramelessHelper::Quick::registerTypes(&engine); + qmlRegisterSingletonType("Demo", 1, 0, "Settings", + [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { + Q_UNUSED(engine); + Q_UNUSED(scriptEngine); + return new Settings; + }); + // 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 @@ -73,11 +76,11 @@ int main(int argc, char *argv[]) QQuickStyle::setStyle(FRAMELESSHELPER_STRING_LITERAL("Default")); #endif - const QUrl homepageUrl(FRAMELESSHELPER_STRING_LITERAL("qrc:///Demo/qml/MainWindow.qml")); + const QUrl mainUrl(FRAMELESSHELPER_STRING_LITERAL("qrc:///Demo/qml/MainWindow.qml")); const QMetaObject::Connection connection = QObject::connect( &engine, &QQmlApplicationEngine::objectCreated, &application, - [&homepageUrl, &connection](QObject *object, const QUrl &url) { - if (url != homepageUrl) { + [&mainUrl, &connection](QObject *object, const QUrl &url) { + if (url != mainUrl) { return; } if (object) { @@ -87,7 +90,7 @@ int main(int argc, char *argv[]) } }, Qt::QueuedConnection); - engine.load(homepageUrl); + engine.load(mainUrl); return QCoreApplication::exec(); } diff --git a/examples/quick/settings.cpp b/examples/quick/settings.cpp new file mode 100644 index 00000000..c0ec26b4 --- /dev/null +++ b/examples/quick/settings.cpp @@ -0,0 +1,77 @@ +/* + * MIT License + * + * Copyright (C) 2022 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "settings.h" +#include +#include +#include +#include +#include +#include + +FRAMELESSHELPER_STRING_CONSTANT2(IniKeyPath, "Window/Geometry") + +Settings::Settings(QObject *parent) : QObject(parent) +{ + const QFileInfo fileInfo(QCoreApplication::applicationFilePath()); + const QString iniFileName = fileInfo.completeBaseName() + FRAMELESSHELPER_STRING_LITERAL(".ini"); + const QString iniFilePath = fileInfo.canonicalPath() + QDir::separator() + iniFileName; + m_settings.reset(new QSettings(iniFilePath, QSettings::IniFormat)); +} + +Settings::~Settings() = default; + +void Settings::saveGeometry(QWindow *window) +{ + Q_ASSERT(window); + if (!window) { + return; + } + QByteArray data = {}; + QDataStream stream(&data, QDataStream::WriteOnly); + stream.setVersion(QDataStream::Qt_5_6); + stream << window->geometry(); + m_settings->setValue(kIniKeyPath, data); +} + +bool Settings::restoreGeometry(QWindow *window) +{ + Q_ASSERT(window); + if (!window) { + return false; + } + const QByteArray data = m_settings->value(kIniKeyPath).toByteArray(); + if (data.isEmpty()) { + return false; + } + QRect geometry = {}; + QDataStream stream(data); + stream.setVersion(QDataStream::Qt_5_6); + stream >> geometry; + if (!geometry.isValid()) { + return false; + } + window->setGeometry(geometry); + return true; +} diff --git a/examples/quick/settings.h b/examples/quick/settings.h new file mode 100644 index 00000000..67888bb8 --- /dev/null +++ b/examples/quick/settings.h @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (C) 2022 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include +#include + +QT_BEGIN_NAMESPACE +class QWindow; +class QSettings; +QT_END_NAMESPACE + +class Settings : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(Settings) + +public: + explicit Settings(QObject *parent = nullptr); + ~Settings() override; + +public Q_SLOTS: + void saveGeometry(QWindow *window); + Q_NODISCARD bool restoreGeometry(QWindow *window); + +private: + QScopedPointer m_settings; +}; diff --git a/examples/widget/main.cpp b/examples/widget/main.cpp index 57033da6..49c3d964 100644 --- a/examples/widget/main.cpp +++ b/examples/widget/main.cpp @@ -23,13 +23,10 @@ */ #include -#include #include "widget.h" FRAMELESSHELPER_USE_NAMESPACE -using namespace Global; - int main(int argc, char *argv[]) { // Not necessary, but better call this function, before the construction @@ -38,8 +35,6 @@ int main(int argc, char *argv[]) QApplication application(argc, argv); - FramelessConfig::instance()->set(Option::CenterWindowBeforeShow); - Widget widget; widget.show(); diff --git a/examples/widget/widget.cpp b/examples/widget/widget.cpp index f37953fa..0f0d6105 100644 --- a/examples/widget/widget.cpp +++ b/examples/widget/widget.cpp @@ -24,6 +24,10 @@ #include "widget.h" #include +#include +#include +#include +#include #include #include #include @@ -36,6 +40,17 @@ FRAMELESSHELPER_USE_NAMESPACE using namespace Global; +FRAMELESSHELPER_STRING_CONSTANT2(IniKeyPath, "Window/Geometry") + +[[nodiscard]] static inline QSettings *appConfigFile() +{ + const QFileInfo fileInfo(QCoreApplication::applicationFilePath()); + const QString iniFileName = fileInfo.completeBaseName() + FRAMELESSHELPER_STRING_LITERAL(".ini"); + const QString iniFilePath = fileInfo.canonicalPath() + QDir::separator() + iniFileName; + const auto settings = new QSettings(iniFilePath, QSettings::IniFormat); + return settings; +} + Widget::Widget(QWidget *parent) : FramelessWidget(parent) { initialize(); @@ -53,6 +68,13 @@ void Widget::timerEvent(QTimerEvent *event) } } +void Widget::closeEvent(QCloseEvent *event) +{ + const QScopedPointer settings(appConfigFile()); + settings->setValue(kIniKeyPath, saveGeometry()); + FramelessWidget::closeEvent(event); +} + void Widget::initialize() { setWindowTitle(tr("FramelessHelper demo application - Qt Widgets")); @@ -83,6 +105,15 @@ void Widget::initialize() helper->setSystemButton(m_titleBar->minimizeButton(), SystemButtonType::Minimize); helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize); helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close); + connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){ + const QScopedPointer settings(appConfigFile()); + const QByteArray data = settings->value(kIniKeyPath).toByteArray(); + if (data.isEmpty()) { + helper->moveWindowToDesktopCenter(); + } else { + restoreGeometry(data); + } + }); } void Widget::updateStyleSheet() diff --git a/examples/widget/widget.h b/examples/widget/widget.h index 4002a7eb..550771cd 100644 --- a/examples/widget/widget.h +++ b/examples/widget/widget.h @@ -45,6 +45,7 @@ class Widget : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget) protected: void timerEvent(QTimerEvent *event) override; + void closeEvent(QCloseEvent *event) override; private: void initialize(); diff --git a/include/FramelessHelper/Quick/framelessquickhelper.h b/include/FramelessHelper/Quick/framelessquickhelper.h index f0fcb599..161a9435 100644 --- a/include/FramelessHelper/Quick/framelessquickhelper.h +++ b/include/FramelessHelper/Quick/framelessquickhelper.h @@ -71,6 +71,7 @@ public Q_SLOTS: Q_SIGNALS: void titleBarItemChanged(); + void ready(); private: QScopedPointer d_ptr; diff --git a/include/FramelessHelper/Quick/private/framelessquickhelper_p.h b/include/FramelessHelper/Quick/private/framelessquickhelper_p.h index 83a2d2fe..b3880b3c 100644 --- a/include/FramelessHelper/Quick/private/framelessquickhelper_p.h +++ b/include/FramelessHelper/Quick/private/framelessquickhelper_p.h @@ -65,6 +65,8 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickHelperPrivate : public QObject Q_NODISCARD bool isWindowFixedSize() const; void setWindowFixedSize(const bool value); + void emitSignalForAllInstances(const char *signal); + private: Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const; Q_NODISCARD bool isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const; diff --git a/include/FramelessHelper/Widgets/framelesswidgetshelper.h b/include/FramelessHelper/Widgets/framelesswidgetshelper.h index 54380927..7d753ffd 100644 --- a/include/FramelessHelper/Widgets/framelesswidgetshelper.h +++ b/include/FramelessHelper/Widgets/framelesswidgetshelper.h @@ -64,6 +64,7 @@ public Q_SLOTS: Q_SIGNALS: void titleBarWidgetChanged(); + void ready(); private: QScopedPointer d_ptr; diff --git a/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h b/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h index b811f283..b7137d82 100644 --- a/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h +++ b/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h @@ -61,6 +61,8 @@ class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelperPrivate : public QObject Q_NODISCARD bool isWindowFixedSize() const; void setWindowFixedSize(const bool value); + void emitSignalForAllInstances(const char *signal); + private: Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const; Q_NODISCARD bool isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const; diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index 2178a561..a0872342 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -163,7 +163,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) switch (uMsg) { case WM_NCHITTEST: { // Try to determine what part of the window is being hovered here. This - // is absolutely critical to making sure the snap layout works! + // is absolutely critical to making sure the snap layouts works! const POINT nativeGlobalPos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; POINT nativeLocalPos = nativeGlobalPos; if (ScreenToClient(hWnd, &nativeLocalPos) == FALSE) { @@ -371,7 +371,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) const auto dragBarWindowHandle = reinterpret_cast(dragBarWindowId); const UINT flags = (SWP_NOACTIVATE | (hide ? SWP_HIDEWINDOW : SWP_SHOWWINDOW)); // As you can see from the code, we only use the drag bar window to activate the - // snap layout feature introduced in Windows 11. So you may wonder, why not just + // snap layouts feature introduced in Windows 11. So you may wonder, why not just // limit it to the rectangle of the three system buttons, instead of covering the // whole title bar area? Well, I've tried that solution already and unfortunately // it doesn't work. Since our current solution works well, I have no plan to dig @@ -536,7 +536,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me const LPARAM lParam = msg->lParam; switch (uMsg) { case WM_NCCALCSIZE: { - const bool isFixedSize = data.params.isWindowFixedSize(); // Windows是根据这个消息的返回值来设置窗口的客户区(窗口中真正显示的内容) // 和非客户区(标题栏、窗口边框、菜单栏和状态栏等Windows系统自行提供的部分 // ,不过对于Qt来说,除了标题栏和窗口边框,非客户区基本也都是自绘的)的范 @@ -870,8 +869,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me (GetAsyncKeyState(VK_RBUTTON) < 0) : (GetAsyncKeyState(VK_LBUTTON) < 0)); const bool isTitleBar = (data.params.isInsideTitleBarDraggableArea(qtScenePos) && leftButtonPressed); const bool isFixedSize = data.params.isWindowFixedSize(); - if( frameBorderVisible ) - { + if (frameBorderVisible) { // This will handle the left, right and bottom parts of the frame // because we didn't change them. const LRESULT originalRet = DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam); @@ -1066,8 +1064,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me case WM_DISPLAYCHANGE: // Sent to a window when the display resolution has changed. { const bool isFixedSize = data.params.isWindowFixedSize(); - if( !resizeDragBarWindow(windowId, data.dragBarWindowId, isFixedSize) ) - { + if (!resizeDragBarWindow(windowId, data.dragBarWindowId, isFixedSize)) { qWarning() << "Failed to re-position the drag bar window."; } } break; diff --git a/src/quick/framelessquickhelper.cpp b/src/quick/framelessquickhelper.cpp index c0b7ef4f..701ee480 100644 --- a/src/quick/framelessquickhelper.cpp +++ b/src/quick/framelessquickhelper.cpp @@ -148,8 +148,7 @@ void FramelessQuickHelperPrivate::setTitleBarItem(QQuickItem *value) return; } data->titleBarItem = value; - Q_Q(FramelessQuickHelper); - Q_EMIT q->titleBarItemChanged(); + emitSignalForAllInstances("titleBarItemChanged"); } void FramelessQuickHelperPrivate::attachToWindow() @@ -218,11 +217,11 @@ void FramelessQuickHelperPrivate::attachToWindow() // we reach here, and all the modifications from the Qt side will be lost // due to QPA will reset the position and size of the window during it's // initialization process. - QTimer::singleShot(0, this, [this, window](){ + QTimer::singleShot(0, this, [this](){ if (FramelessConfig::instance()->isSet(Option::CenterWindowBeforeShow)) { moveWindowToDesktopCenter(); } - window->setVisible(true); + emitSignalForAllInstances("ready"); }); } @@ -391,6 +390,27 @@ void FramelessQuickHelperPrivate::setWindowFixedSize(const bool value) #endif } +void FramelessQuickHelperPrivate::emitSignalForAllInstances(const char *signal) +{ + Q_ASSERT(signal); + if (!signal) { + return; + } + Q_Q(FramelessQuickHelper); + const QQuickWindow * const window = q->window(); + if (!window) { + return; + } + const auto rootObject = (window->contentItem() ? qobject_cast(window->contentItem()) : qobject_cast(window)); + const auto instances = rootObject->findChildren(); + if (instances.isEmpty()) { + return; + } + for (auto &&instance : qAsConst(instances)) { + QMetaObject::invokeMethod(instance, signal); + } +} + QRect FramelessQuickHelperPrivate::mapItemGeometryToScene(const QQuickItem * const item) const { Q_ASSERT(item); @@ -616,7 +636,7 @@ FramelessQuickHelper *FramelessQuickHelper::get(QObject *object) QObject *parent = nullptr; if (isItem(object)) { const auto item = qobject_cast(object); - parent = (item->window() ? item->window()->contentItem() : item); + parent = ((item->window() && item->window()->contentItem()) ? item->window()->contentItem() : item); } else { parent = object; } @@ -625,9 +645,8 @@ FramelessQuickHelper *FramelessQuickHelper::get(QObject *object) instance = new FramelessQuickHelper; if (isItem(parent)) { instance->setParentItem(qobject_cast(parent)); - } else { - instance->setParent(parent); } + instance->setParent(parent); // No need to do this here, we'll do it once the item has been assigned to a specific window. //instance->d_func()->attachToWindow(); } @@ -734,8 +753,16 @@ void FramelessQuickHelper::itemChange(const ItemChange change, const ItemChangeD { QQuickItem::itemChange(change, value); if ((change == ItemSceneChange) && value.window) { - if (parentItem() != value.window->contentItem()) { - setParentItem(value.window->contentItem()); + QQuickItem * const rootItem = value.window->contentItem(); + if (rootItem) { + if ((parentItem() != rootItem) || (parent() != rootItem)) { + setParentItem(rootItem); + setParent(rootItem); + } + } else { + if (parent() != value.window) { + setParent(value.window); + } } Q_D(FramelessQuickHelper); d->attachToWindow(); diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index 943260ad..6717c7f9 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -128,6 +128,25 @@ void FramelessWidgetsHelperPrivate::setWindowFixedSize(const bool value) #endif } +void FramelessWidgetsHelperPrivate::emitSignalForAllInstances(const char *signal) +{ + Q_ASSERT(signal); + if (!signal) { + return; + } + const QWidget * const window = getWindow(); + if (!window) { + return; + } + const auto instances = window->findChildren(); + if (instances.isEmpty()) { + return; + } + for (auto &&instance : qAsConst(instances)) { + QMetaObject::invokeMethod(instance, signal); + } +} + void FramelessWidgetsHelperPrivate::setTitleBarWidget(QWidget *widget) { Q_ASSERT(widget); @@ -143,8 +162,7 @@ void FramelessWidgetsHelperPrivate::setTitleBarWidget(QWidget *widget) return; } data->titleBarWidget = widget; - Q_Q(FramelessWidgetsHelper); - Q_EMIT q->titleBarWidgetChanged(); + emitSignalForAllInstances("titleBarWidgetChanged"); } QWidget *FramelessWidgetsHelperPrivate::getTitleBarWidget() const @@ -245,11 +263,11 @@ void FramelessWidgetsHelperPrivate::attachToWindow() // we reach here, and all the modifications from the Qt side will be lost // due to QPA will reset the position and size of the window during it's // initialization process. - QTimer::singleShot(0, this, [this, window](){ + QTimer::singleShot(0, this, [this](){ if (FramelessConfig::instance()->isSet(Option::CenterWindowBeforeShow)) { moveWindowToDesktopCenter(); } - window->setVisible(true); + emitSignalForAllInstances("ready"); }); }