From 41cbeef3fdbb7e6bddc2b2ef3a5d801ab076f45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AD=90=E6=A5=9A=5Czhuzi?= Date: Thu, 28 Mar 2024 19:18:56 +0800 Subject: [PATCH] update --- example/example_en_US.ts | 48 +- example/example_zh_CN.ts | 48 +- example/qml/window/MainWindow.qml | 20 +- src/FluFrameless.cpp | 459 ++++++++++++++++ src/FluFrameless.h | 58 +++ src/FluFramelessHelper.cpp | 493 ------------------ src/FluFramelessHelper.h | 79 --- src/FluentUI.cpp | 4 +- .../imports/FluentUI/Controls/FluAppBar.qml | 103 +--- .../imports/FluentUI/Controls/FluWindow.qml | 67 +-- src/Qt5/imports/FluentUI/plugins.qmltypes | 110 ++-- .../imports/FluentUI/Controls/FluAppBar.qml | 103 +--- .../imports/FluentUI/Controls/FluWindow.qml | 67 +-- 13 files changed, 723 insertions(+), 936 deletions(-) create mode 100644 src/FluFrameless.cpp create mode 100644 src/FluFrameless.h delete mode 100644 src/FluFramelessHelper.cpp delete mode 100644 src/FluFramelessHelper.h diff --git a/example/example_en_US.ts b/example/example_en_US.ts index e39ea575..203cc1f2 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -535,104 +535,104 @@ MainWindow - + Dark Mode + - Quit - + Are you sure you want to exit the program? - + Minimize - + Friendly Reminder - + FluentUI is hidden from the tray, click on the tray to activate the window again - - + + Cancel - + Open in Separate Window - + Click Time - + Search - + Finish - + Next - + Previous - + Here you can switch to night mode. - + Hide Easter eggs - + Try a few more clicks!! - + Upgrade Tips - + FluentUI is currently up to date - + -- The current app version - + Now go and download the new version? @@ -641,17 +641,17 @@ Updated content: - + OK - + The current version is already the latest - + The network is abnormal diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index 763c76b4..eec79e2f 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -535,104 +535,104 @@ MainWindow - + Dark Mode 夜间模式 + - Quit 退出 - + Are you sure you want to exit the program? 您确定要退出程序吗 - + Minimize 最小化 - + Friendly Reminder 友情提示 - + FluentUI is hidden from the tray, click on the tray to activate the window again FluentUI 在托盘中处于隐藏状态,单击托盘以再次激活窗口 - - + + Cancel 取消 - + Open in Separate Window 在独立窗口中打开 - + Click Time 点击次数 - + Search 搜索 - + Finish 完成 - + Next 下一步 - + Previous 上一步 - + Here you can switch to night mode. 在这里,您可以切换到夜间模式。 - + Hide Easter eggs 隐藏彩蛋 - + Try a few more clicks!! 再试几下!! - + Upgrade Tips 升级提示 - + FluentUI is currently up to date FluentUI 目前最新版本 - + -- The current app version -- 当前应用版本 - + Now go and download the new version? @@ -645,17 +645,17 @@ Updated content: - + OK 确定 - + The current version is already the latest 当前版本已经是最新版本 - + The network is abnormal 网络异常 diff --git a/example/qml/window/MainWindow.qml b/example/qml/window/MainWindow.qml index 90e6840a..10cb310f 100644 --- a/example/qml/window/MainWindow.qml +++ b/example/qml/window/MainWindow.qml @@ -35,16 +35,8 @@ FluWindow { } } - onFirstVisible: { - timer_tour_delay.restart() - } - - Timer{ - id:timer_tour_delay - interval: 200 - onTriggered: { - tour.open() - } + onLazyLoad: { + tour.open() } Component.onCompleted: { @@ -168,7 +160,7 @@ FluWindow { } } Component.onCompleted: { - appBar.setHitTestVisible(layout_back_buttons) + window.setHitTestVisible(layout_back_buttons) } } FluRemoteLoader{ @@ -226,9 +218,9 @@ FluWindow { ItemsOriginal.paneItemMenu = nav_item_right_menu ItemsFooter.navigationView = nav_view ItemsFooter.paneItemMenu = nav_item_right_menu - appBar.setHitTestVisible(nav_view.buttonMenu) - appBar.setHitTestVisible(nav_view.buttonBack) - appBar.setHitTestVisible(nav_view.imageLogo) + window.setHitTestVisible(nav_view.buttonMenu) + window.setHitTestVisible(nav_view.buttonBack) + window.setHitTestVisible(nav_view.imageLogo) setCurrentIndex(0) } } diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp new file mode 100644 index 00000000..50feb104 --- /dev/null +++ b/src/FluFrameless.cpp @@ -0,0 +1,459 @@ +#include "FluFrameless.h" + +#include +#include +#include + +#ifdef Q_OS_WIN +#pragma comment (lib,"user32.lib") +#pragma comment (lib,"dwmapi.lib") +#include +#include +#include +static inline QByteArray qtNativeEventType() +{ + static const auto result = "windows_generic_MSG"; + return result; +} +static inline bool isCompositionEnabled(){ + typedef HRESULT (WINAPI* DwmIsCompositionEnabledPtr)(BOOL *pfEnabled); + HMODULE module = ::LoadLibraryW(L"dwmapi.dll"); + if (module) + { + BOOL composition_enabled = false; + DwmIsCompositionEnabledPtr dwm_is_composition_enabled; + dwm_is_composition_enabled= reinterpret_cast(::GetProcAddress(module, "DwmIsCompositionEnabled")); + if (dwm_is_composition_enabled) + { + dwm_is_composition_enabled(&composition_enabled); + } + return composition_enabled; + } + return false; +} +#endif + +FluFrameless::FluFrameless(QQuickItem *parent) + : QQuickItem{parent} +{ + appbar(nullptr); + maximizeButton(nullptr); + minimizedButton(nullptr); + closeButton(nullptr); + topmost(false); + disabled(false); +} + +FluFrameless::~FluFrameless(){ + qApp->removeNativeEventFilter(this); +} + +void FluFrameless::componentComplete(){ + if(_disabled){ + return; + } + _current = window()->winId(); + window()->setFlags(( window()->flags()) | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint | Qt::FramelessWindowHint); + if(!_fixSize){ + window()->setFlag(Qt::WindowMaximizeButtonHint); + } + window()->installEventFilter(this); + qApp->installNativeEventFilter(this); + if(_appbar){ + _appbar->installEventFilter(this); + } + if(_maximizeButton){ + setHitTestVisible(_maximizeButton); + } + if(_minimizedButton){ + setHitTestVisible(_minimizedButton); + } + if(_closeButton){ + setHitTestVisible(_closeButton); + } +#ifdef Q_OS_WIN + HWND hwnd = reinterpret_cast(window()->winId()); + DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); + if(_fixSize){ + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME); + for (int i = 0; i < qApp->screens().count(); ++i) { + connect( qApp->screens().at(i),&QScreen::logicalDotsPerInchChanged,this,[=]{ + SetWindowPos(hwnd,nullptr,0,0,0,0,SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); + }); + } + }else{ + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME); + } + SetWindowPos(hwnd,nullptr,0,0,0,0,SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); + connect(window(),&QQuickWindow::screenChanged,this,[hwnd]{ + ::SetWindowPos(hwnd,0,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOOWNERZORDER); + ::RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); + }); +#endif + connect(this,&FluFrameless::topmostChanged,this,[this]{ + _setWindowTopmost(topmost()); + }); + _setWindowTopmost(topmost()); +} + +bool FluFrameless::nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result){ +#ifdef Q_OS_WIN + if ((eventType != qtNativeEventType()) || !message) { + return false; + } + const auto msg = static_cast(message); + const HWND hwnd = msg->hwnd; + if (!hwnd || !msg) { + return false; + } + const qint64 wid = reinterpret_cast(hwnd); + if(wid != _current){ + return false; + } + const UINT uMsg = msg->message; + const WPARAM wParam = msg->wParam; + const LPARAM lParam = msg->lParam; + static QPoint offsetXY; + if(uMsg == WM_WINDOWPOSCHANGING){ + WINDOWPOS* wp = reinterpret_cast(lParam); + if (wp != nullptr && (wp->flags & SWP_NOSIZE) == 0) + { + wp->flags |= SWP_NOCOPYBITS; + *result = ::DefWindowProcW(hwnd, uMsg, wParam, lParam); + return true; + } + return false; + }else if(uMsg == WM_NCCALCSIZE){ + const auto clientRect = ((wParam == FALSE) ? reinterpret_cast(lParam) : &(reinterpret_cast(lParam))->rgrc[0]); + const LONG originalTop = clientRect->top; + const LONG originalLeft = clientRect->left; + const LONG originalRight = clientRect->right; + const LONG originalBottom = clientRect->bottom; + const LRESULT hitTestResult = ::DefWindowProcW(hwnd, WM_NCCALCSIZE, wParam, lParam); + if ((hitTestResult != HTERROR) && (hitTestResult != HTNOWHERE)) { + *result = hitTestResult; + return true; + } + int offsetSize = 0; + bool isMaximum = ::IsZoomed(hwnd); + offsetXY = QPoint(abs(clientRect->left - originalLeft),abs(clientRect->top - originalTop)); + if(isMaximum || _isFullScreen()){ + offsetSize = 0; + }else{ + offsetSize = 1; + } + if(!isCompositionEnabled()){ + offsetSize = 0; + } + if (!isMaximum || QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) { + clientRect->top = originalTop + offsetSize; + clientRect->bottom = originalBottom - offsetSize; + clientRect->left = originalLeft + offsetSize; + clientRect->right = originalRight - offsetSize; + } + *result = WVR_REDRAW; + return true; + }else if(uMsg == WM_NCHITTEST){ + if(_hitMaximizeButton()){ + if (*result == HTNOWHERE) { + *result = HTZOOM; + } + return true; + } + *result = 0; + POINT nativeGlobalPos{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + POINT nativeLocalPos = nativeGlobalPos; + ::ScreenToClient(hwnd, &nativeLocalPos); + RECT clientRect{0, 0, 0, 0}; + ::GetClientRect(hwnd, &clientRect); + auto clientWidth = clientRect.right-clientRect.left; + auto clientHeight = clientRect.bottom-clientRect.top; + bool left = nativeLocalPos.x < _margins; + bool right = nativeLocalPos.x > clientWidth - _margins; + bool top = nativeLocalPos.y < _margins; + bool bottom = nativeLocalPos.y > clientHeight - _margins; + *result = 0; + if (!_fixSize && !_isFullScreen() && !_isMaximized()) { + if (left && top) { + *result = HTTOPLEFT; + } else if (left && bottom) { + *result = HTBOTTOMLEFT; + } else if (right && top) { + *result = HTTOPRIGHT; + } else if (right && bottom) { + *result = HTBOTTOMRIGHT; + } else if (left) { + *result = HTLEFT; + } else if (right) { + *result = HTRIGHT; + } else if (top) { + *result = HTTOP; + } else if (bottom) { + *result = HTBOTTOM; + } + } + if (0 != *result) { + return true; + } + auto aa = _hitAppBar(); + if(aa){ + *result = HTCAPTION; + return true; + } + *result = HTCLIENT; + return true; + }else if(uMsg == WM_NCLBUTTONDBLCLK || uMsg == WM_NCLBUTTONDOWN){ + if(_hitMaximizeButton()){ + QMouseEvent event = QMouseEvent(QEvent::MouseButtonPress, QPoint(), QPoint(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QGuiApplication::sendEvent(_maximizeButton,&event); + return true; + } + }else if(uMsg == WM_NCLBUTTONUP || uMsg == WM_NCRBUTTONUP){ + if(_hitMaximizeButton()){ + QMouseEvent event = QMouseEvent(QEvent::MouseButtonRelease, QPoint(), QPoint(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QGuiApplication::sendEvent(_maximizeButton,&event); + return true; + } + }else if(uMsg == WM_NCPAINT){ + *result = FALSE; + return true; + }else if(uMsg == WM_NCACTIVATE){ + *result = ::DefWindowProcW(hwnd, WM_NCACTIVATE, wParam, -1); + return true; + }else if(uMsg == WM_GETMINMAXINFO){ + MINMAXINFO* minmaxInfo = reinterpret_cast(lParam); +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) + minmaxInfo->ptMaxPosition.x = 0; + minmaxInfo->ptMaxPosition.y = 0; + minmaxInfo->ptMaxSize.x = 0; + minmaxInfo->ptMaxSize.y = 0; +#else + auto pixelRatio = window()->devicePixelRatio(); + auto geometry = window()->screen()->availableGeometry(); + RECT rect; + SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0); + minmaxInfo->ptMaxPosition.x = rect.left - offsetXY.x(); + minmaxInfo->ptMaxPosition.y = rect.top - offsetXY.x(); + minmaxInfo->ptMaxSize.x = geometry.width()*pixelRatio + offsetXY.x() * 2; + minmaxInfo->ptMaxSize.y = geometry.height()*pixelRatio + offsetXY.y() * 2; +#endif + return false; + }else if(uMsg == WM_NCRBUTTONDOWN){ + if (wParam == HTCAPTION) { + _showSystemMenu(QCursor::pos()); + } + }else if(uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN){ + const bool altPressed = ((wParam == VK_MENU) || (::GetKeyState(VK_MENU) < 0)); + const bool spacePressed = ((wParam == VK_SPACE) || (::GetKeyState(VK_SPACE) < 0)); + if (altPressed && spacePressed) { + auto pos = window()->position(); + _showSystemMenu(QPoint(pos.x(),pos.y()+_appbar->height())); + } + }else if(uMsg == WM_SYSCOMMAND){ + if(wParam == SC_MINIMIZE){ + if(window()->transientParent()){ + window()->transientParent()->showMinimized(); + }else{ + window()->showMinimized(); + } + return true; + } + return false; + } + return false; +#endif + return false; +} + +bool FluFrameless::_isMaximized(){ + return window()->visibility() == QWindow::Maximized; +} + +bool FluFrameless::_isFullScreen(){ + return window()->visibility() == QWindow::FullScreen; +} + +void FluFrameless::_showSystemMenu(QPoint point){ +#ifdef Q_OS_WIN + HWND hwnd = reinterpret_cast(window()->winId()); + DWORD style = ::GetWindowLongPtr(hwnd,GWL_STYLE); + ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_SYSMENU); + const HMENU hMenu = ::GetSystemMenu(hwnd, FALSE); + if(_isMaximized() || _isFullScreen()){ + ::EnableMenuItem(hMenu,SC_MOVE,MFS_DISABLED); + ::EnableMenuItem(hMenu,SC_RESTORE,MFS_ENABLED); + }else{ + ::EnableMenuItem(hMenu,SC_MOVE,MFS_ENABLED); + ::EnableMenuItem(hMenu,SC_RESTORE,MFS_DISABLED); + } + if(!_fixSize && !_isMaximized() && !_isFullScreen()){ + ::EnableMenuItem(hMenu,SC_SIZE,MFS_ENABLED); + ::EnableMenuItem(hMenu,SC_MAXIMIZE,MFS_ENABLED); + }else{ + ::EnableMenuItem(hMenu,SC_SIZE,MFS_DISABLED); + ::EnableMenuItem(hMenu,SC_MAXIMIZE,MFS_DISABLED); + } + const int result = ::TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), point.x()*window()->devicePixelRatio(), point.y()*window()->devicePixelRatio(), 0, hwnd, nullptr); + if (result != FALSE) { + ::PostMessageW(hwnd, WM_SYSCOMMAND, result, 0); + } + ::SetWindowLongPtr(hwnd, GWL_STYLE, style &~ WS_SYSMENU); +#endif +} + +bool FluFrameless::_containsCursorToItem(QQuickItem* item){ + if(!item || !item->isVisible()){ + return false; + } + auto point = QCursor::pos(); + auto rect = QRectF(item->mapToGlobal(QPoint(0,0)),item->size()); + if(point.x()>rect.x() && point.x()<(rect.x()+rect.width()) && point.y()>rect.y() && point.y()<(rect.y()+rect.height())){ + return true; + } + return false; +} + +bool FluFrameless::_hitAppBar(){ + foreach (auto item, _hitTestList) { + if(_containsCursorToItem(item)){ + return false; + } + } + if(_containsCursorToItem(_appbar)){ + return true; + } + return false; +} + +bool FluFrameless::_hitMaximizeButton(){ + if(_containsCursorToItem(_maximizeButton)){ + return true; + } + return false; +} + +void FluFrameless::_updateCursor(int edges){ + switch (edges) { + case 0: + window()->setCursor(Qt::ArrowCursor); + break; + case Qt::LeftEdge: + case Qt::RightEdge: + window()->setCursor(Qt::SizeHorCursor); + break; + case Qt::TopEdge: + case Qt::BottomEdge: + window()->setCursor(Qt::SizeVerCursor); + break; + case Qt::LeftEdge | Qt::TopEdge: + case Qt::RightEdge | Qt::BottomEdge: + window()->setCursor(Qt::SizeFDiagCursor); + break; + case Qt::RightEdge | Qt::TopEdge: + case Qt::LeftEdge | Qt::BottomEdge: + window()->setCursor(Qt::SizeBDiagCursor); + break; + } +} + +void FluFrameless::showFullScreen(){ + window()->showFullScreen(); +} + +void FluFrameless::showMaximized(){ +#ifdef Q_OS_WIN + HWND hwnd = reinterpret_cast(window()->winId()); + ::ShowWindow(hwnd,3); +#else + window()->showMaximized(); +#endif +} + +void FluFrameless::showMinimized(){ + window()->showMinimized(); +} + +void FluFrameless::showNormal(){ + window()->showNormal(); +} + +void FluFrameless::setHitTestVisible(QQuickItem* val){ + _hitTestList.append(val); +} + + +void FluFrameless::_setWindowTopmost(bool topmost){ +#ifdef Q_OS_WIN + HWND hwnd = reinterpret_cast(window()->winId()); + if(topmost){ + ::SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + }else{ + ::SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } +#else + window()->setFlag(Qt::WindowStaysOnTopHint,topmost); +#endif +} + +bool FluFrameless::eventFilter(QObject *obj, QEvent *ev){ +#ifndef Q_OS_WIN + switch (ev->type()) { + case QEvent::MouseButtonPress: + if(_edges!=0){ + QMouseEvent *event = static_cast(ev); + if(event->button() == Qt::LeftButton){ + _updateCursor(_edges); + window()->startSystemResize(Qt::Edges(_edges)); + } + }else{ + if(_hitAppBar()){ + window()->startSystemMove(); + } + } + break; + case QEvent::MouseButtonRelease: + _edges = 0; + break; + case QEvent::MouseMove: { + if(_isMaximized() || _isFullScreen()){ + break; + } + if(_fixSize){ + break; + } + QMouseEvent *event = static_cast(ev); + QPoint p = +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) + event->pos(); +#else + event->position().toPoint(); +#endif + if(p.x() >= _margins && p.x() <= (window()->width() - _margins) && p.y() >= _margins && p.y() <= (window()->height() - _margins)){ + if(_edges != 0){ + _edges = 0; + _updateCursor(_edges); + } + break; + } + _edges = 0; + if ( p.x() < _margins ) { + _edges |= Qt::LeftEdge; + } + if ( p.x() > (window()->width() - _margins) ) { + _edges |= Qt::RightEdge; + } + if ( p.y() < _margins ) { + _edges |= Qt::TopEdge; + } + if ( p.y() > (window()->height() - _margins) ) { + _edges |= Qt::BottomEdge; + } + _updateCursor(_edges); + break; + } + default: + break; + } +#endif + return QObject::eventFilter(obj, ev); +} diff --git a/src/FluFrameless.h b/src/FluFrameless.h new file mode 100644 index 00000000..e3b09477 --- /dev/null +++ b/src/FluFrameless.h @@ -0,0 +1,58 @@ +#ifndef FLUFRAMELESS_H +#define FLUFRAMELESS_H + +#include +#include +#include +#include "stdafx.h" + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +using QT_NATIVE_EVENT_RESULT_TYPE = qintptr; +using QT_ENTER_EVENT_TYPE = QEnterEvent; +#else +using QT_NATIVE_EVENT_RESULT_TYPE = long; +using QT_ENTER_EVENT_TYPE = QEvent; +#endif + + +class FluFrameless : public QQuickItem,QAbstractNativeEventFilter +{ + Q_OBJECT + Q_PROPERTY_AUTO(QQuickItem*,appbar) + Q_PROPERTY_AUTO(bool,topmost) + Q_PROPERTY_AUTO(QQuickItem*,maximizeButton) + Q_PROPERTY_AUTO(QQuickItem*,minimizedButton) + Q_PROPERTY_AUTO(QQuickItem*,closeButton) + Q_PROPERTY_AUTO(bool,disabled) + Q_PROPERTY_AUTO(bool,fixSize) + QML_NAMED_ELEMENT(FluFrameless) +public: + explicit FluFrameless(QQuickItem* parent = nullptr); + ~FluFrameless(); + void componentComplete() override; + bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override; + Q_INVOKABLE void showFullScreen(); + Q_INVOKABLE void showMaximized(); + Q_INVOKABLE void showMinimized(); + Q_INVOKABLE void showNormal(); + Q_INVOKABLE void setHitTestVisible(QQuickItem*); +protected: + bool eventFilter(QObject *obj, QEvent *event) override; +private: + bool _isFullScreen(); + bool _isMaximized(); + void _updateCursor(int edges); + void _setWindowTopmost(bool topmost); + void _showSystemMenu(QPoint point); + bool _containsCursorToItem(QQuickItem* item); + bool _hitAppBar(); + bool _hitMaximizeButton(); + qint64 _current; + +private: + int _edges = 0; + int _margins = 8; + QList> _hitTestList; +}; + +#endif // FLUFRAMELESS_H diff --git a/src/FluFramelessHelper.cpp b/src/FluFramelessHelper.cpp deleted file mode 100644 index f31a3e13..00000000 --- a/src/FluFramelessHelper.cpp +++ /dev/null @@ -1,493 +0,0 @@ -#include "FluFramelessHelper.h" - -#include -#include -#include -#include "FluTools.h" - -#ifdef Q_OS_WIN -#pragma comment (lib,"user32.lib") -#pragma comment (lib,"dwmapi.lib") -#include -#include -#include - -static inline QByteArray qtNativeEventType() -{ - static const auto result = "windows_generic_MSG"; - return result; -} - -static inline bool isCompositionEnabled(){ - typedef HRESULT (WINAPI* DwmIsCompositionEnabledPtr)(BOOL *pfEnabled); - HMODULE module = ::LoadLibraryW(L"dwmapi.dll"); - if (module) - { - BOOL composition_enabled = false; - DwmIsCompositionEnabledPtr dwm_is_composition_enabled; - dwm_is_composition_enabled= reinterpret_cast(::GetProcAddress(module, "DwmIsCompositionEnabled")); - if (dwm_is_composition_enabled) - { - dwm_is_composition_enabled(&composition_enabled); - } - return composition_enabled; - } - return false; -} - -#endif - -FramelessEventFilter::FramelessEventFilter(FluFramelessHelper* helper){ - _helper = helper; - _current = _helper->window_->winId(); -} - -bool FramelessEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result){ -#ifdef Q_OS_WIN - if ((eventType != qtNativeEventType()) || !message || _helper.isNull() || _helper->window_.isNull()) { - return false; - } - const auto msg = static_cast(message); - const HWND hwnd = msg->hwnd; - if (!hwnd || !msg) { - return false; - } - const qint64 wid = reinterpret_cast(hwnd); - if(wid != _current){ - return false; - } - const UINT uMsg = msg->message; - const WPARAM wParam = msg->wParam; - const LPARAM lParam = msg->lParam; - static QPoint offsetXY; - if(uMsg == WM_WINDOWPOSCHANGING){ - WINDOWPOS* wp = reinterpret_cast(lParam); - if (wp != nullptr && (wp->flags & SWP_NOSIZE) == 0) - { - wp->flags |= SWP_NOCOPYBITS; - *result = ::DefWindowProcW(hwnd, uMsg, wParam, lParam); - return true; - } - return false; - }else if(uMsg == WM_NCCALCSIZE){ - const auto clientRect = ((wParam == FALSE) ? reinterpret_cast(lParam) : &(reinterpret_cast(lParam))->rgrc[0]); - const LONG originalTop = clientRect->top; - const LONG originalLeft = clientRect->left; - const LONG originalRight = clientRect->right; - const LONG originalBottom = clientRect->bottom; - const LRESULT hitTestResult = ::DefWindowProcW(hwnd, WM_NCCALCSIZE, wParam, lParam); - if ((hitTestResult != HTERROR) && (hitTestResult != HTNOWHERE)) { - *result = hitTestResult; - return true; - } - int offsetSize = 0; - bool isMaximum = ::IsZoomed(hwnd); - offsetXY = QPoint(abs(clientRect->left - originalLeft),abs(clientRect->top - originalTop)); - if(isMaximum || _helper->fullScreen()){ - offsetSize = 0; - }else{ - offsetSize = 1; - } - if(!isCompositionEnabled()){ - offsetSize = 0; - } - if (!isMaximum || QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) { - clientRect->top = originalTop + offsetSize; - clientRect->bottom = originalBottom - offsetSize; - clientRect->left = originalLeft + offsetSize; - clientRect->right = originalRight - offsetSize; - } - *result = WVR_REDRAW; - return true; - }if(uMsg == WM_NCHITTEST){ - if(FluTools::getInstance()->isWindows11OrGreater() && _helper->hoverMaxBtn() && _helper->resizeable()){ - if (*result == HTNOWHERE) { - *result = HTZOOM; - } - return true; - } - *result = 0; - POINT nativeGlobalPos{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; - POINT nativeLocalPos = nativeGlobalPos; - ::ScreenToClient(hwnd, &nativeLocalPos); - RECT clientRect{0, 0, 0, 0}; - ::GetClientRect(hwnd, &clientRect); - auto clientWidth = clientRect.right-clientRect.left; - auto clientHeight = clientRect.bottom-clientRect.top; - int margins = _helper->getMargins(); - bool left = nativeLocalPos.x < margins; - bool right = nativeLocalPos.x > clientWidth - margins; - bool top = nativeLocalPos.y < margins; - bool bottom = nativeLocalPos.y > clientHeight - margins; - *result = 0; - if (_helper->resizeable() && !_helper->fullScreen() && !_helper->maximized()) { - if (left && top) { - *result = HTTOPLEFT; - } else if (left && bottom) { - *result = HTBOTTOMLEFT; - } else if (right && top) { - *result = HTTOPRIGHT; - } else if (right && bottom) { - *result = HTBOTTOMRIGHT; - } else if (left) { - *result = HTLEFT; - } else if (right) { - *result = HTRIGHT; - } else if (top) { - *result = HTTOP; - } else if (bottom) { - *result = HTBOTTOM; - } - } - if (0 != *result) { - return true; - } - QVariant appBar = _helper->getAppBar(); - if(!appBar.isNull()&& _helper->hoverAppBar()){ - *result = HTCAPTION; - return true; - } - *result = HTCLIENT; - return true; - }else if(uMsg == WM_NCLBUTTONDBLCLK || uMsg == WM_NCLBUTTONDOWN){ - if(_helper->hoverMaxBtn()){ - QMouseEvent event = QMouseEvent(QEvent::MouseButtonPress, QPoint(), QPoint(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); - QGuiApplication::sendEvent(_helper->maximizeButton(),&event); - return true; - } - }else if(uMsg == WM_NCLBUTTONUP || uMsg == WM_NCRBUTTONUP){ - if(_helper->hoverMaxBtn()){ - QMouseEvent event = QMouseEvent(QEvent::MouseButtonRelease, QPoint(), QPoint(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); - QGuiApplication::sendEvent(_helper->maximizeButton(),&event); - } - }else if(uMsg == WM_NCPAINT){ - *result = FALSE; - return true; - }else if(uMsg == WM_NCACTIVATE){ - *result = ::DefWindowProcW(hwnd, WM_NCACTIVATE, wParam, -1); - return true; - }else if(uMsg == WM_GETMINMAXINFO){ - MINMAXINFO* minmaxInfo = reinterpret_cast(lParam); -#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) - minmaxInfo->ptMaxPosition.x = 0; - minmaxInfo->ptMaxPosition.y = 0; - minmaxInfo->ptMaxSize.x = 0; - minmaxInfo->ptMaxSize.y = 0; -#else - auto pixelRatio = _helper->window_->devicePixelRatio(); - auto geometry = _helper->window_->screen()->availableGeometry(); - RECT rect; - SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0); - minmaxInfo->ptMaxPosition.x = rect.left - offsetXY.x(); - minmaxInfo->ptMaxPosition.y = rect.top - offsetXY.x(); - minmaxInfo->ptMaxSize.x = geometry.width()*pixelRatio + offsetXY.x() * 2; - minmaxInfo->ptMaxSize.y = geometry.height()*pixelRatio + offsetXY.y() * 2; -#endif - return false; - }else if(uMsg == WM_NCRBUTTONDOWN){ - if (wParam == HTCAPTION) { - _helper->showSystemMenu(QCursor::pos()); - } - }else if(uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN){ - const bool altPressed = ((wParam == VK_MENU) || (::GetKeyState(VK_MENU) < 0)); - const bool spacePressed = ((wParam == VK_SPACE) || (::GetKeyState(VK_SPACE) < 0)); - if (altPressed && spacePressed) { - auto pos = _helper->window_->position(); - _helper->showSystemMenu(QPoint(pos.x(),pos.y()+_helper->getAppBarHeight())); - } - }else if(uMsg == WM_SYSCOMMAND){ - if(wParam == SC_MINIMIZE){ - if(_helper->window_->transientParent()){ - _helper->window_->transientParent()->showMinimized(); - }else{ - _helper->window_->showMinimized(); - } - return true; - } - return false; - } - return false; -#endif - return false; -} - -FluFramelessHelper::FluFramelessHelper(QQuickItem *parent) - : QQuickItem{parent} -{ -} - -void FluFramelessHelper::classBegin(){ -} - -void FluFramelessHelper::_updateCursor(int edges){ - switch (edges) { - case 0: - window_->setCursor(Qt::ArrowCursor); - break; - case Qt::LeftEdge: - case Qt::RightEdge: - window_->setCursor(Qt::SizeHorCursor); - break; - case Qt::TopEdge: - case Qt::BottomEdge: - window_->setCursor(Qt::SizeVerCursor); - break; - case Qt::LeftEdge | Qt::TopEdge: - case Qt::RightEdge | Qt::BottomEdge: - window_->setCursor(Qt::SizeFDiagCursor); - break; - case Qt::RightEdge | Qt::TopEdge: - case Qt::LeftEdge | Qt::BottomEdge: - window_->setCursor(Qt::SizeBDiagCursor); - break; - } -} - -bool FluFramelessHelper::eventFilter(QObject *obj, QEvent *ev){ - if (!window_.isNull() && window_->flags()) { - switch (ev->type()) { - case QEvent::MouseButtonPress: - if(_edges!=0){ - QMouseEvent *event = static_cast(ev); - if(event->button() == Qt::LeftButton){ - _updateCursor(_edges); - window_->startSystemResize(Qt::Edges(_edges)); - } - } - break; - case QEvent::MouseButtonRelease: - _edges = 0; - break; - case QEvent::MouseMove: { - if(maximized() || fullScreen()){ - break; - } - if(!resizeable()){ - break; - } - QMouseEvent *event = static_cast(ev); - QPoint p = -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) - event->pos(); -#else - event->position().toPoint(); -#endif - if(p.x() >= _margins && p.x() <= (window_->width() - _margins) && p.y() >= _margins && p.y() <= (window_->height() - _margins)){ - if(_edges != 0){ - _edges = 0; - _updateCursor(_edges); - } - break; - } - _edges = 0; - if ( p.x() < _margins ) { - _edges |= Qt::LeftEdge; - } - if ( p.x() > (window_->width() - _margins) ) { - _edges |= Qt::RightEdge; - } - if ( p.y() < _margins ) { - _edges |= Qt::TopEdge; - } - if ( p.y() > (window_->height() - _margins) ) { - _edges |= Qt::BottomEdge; - } - _updateCursor(_edges); - break; - } - default: - break; - } - } - return QObject::eventFilter(obj, ev); -} - -void FluFramelessHelper::componentComplete(){ - this->window_ = window(); - if(!window_.isNull()){ - _stayTop = QQmlProperty(window_,"stayTop"); - _screen = QQmlProperty(window_,"screen"); - _fixSize = QQmlProperty(window_,"fixSize"); - _realHeight = QQmlProperty(window_,"_realHeight"); - _realWidth = QQmlProperty(window_,"_realWidth"); - _appBarHeight = QQmlProperty(window_,"_appBarHeight"); - _appBar = window_->property("appBar"); -#ifdef Q_OS_WIN - if(!_appBar.isNull()){ - _appBar.value()->setProperty("systemMoveEnable",false); - } - window_->setFlags((window_->flags()) | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint | Qt::FramelessWindowHint); -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) - if(FluTools::getInstance()->isSoftware()){ - window_->setFlag(Qt::FramelessWindowHint,false); - } -#endif - if(resizeable()){ - window_->setFlag(Qt::WindowMaximizeButtonHint); - } - _nativeEvent =new FramelessEventFilter(this); - qApp->installNativeEventFilter(_nativeEvent); - HWND hwnd = reinterpret_cast(window_->winId()); - DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); - if(resizeable()){ - ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME); - }else{ - ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME); - for (int i = 0; i < qApp->screens().count(); ++i) { - connect( qApp->screens().at(i),&QScreen::logicalDotsPerInchChanged,this,[=]{ - SetWindowPos(hwnd,nullptr,0,0,0,0,SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); - }); - } - } - SetWindowPos(hwnd,nullptr,0,0,0,0,SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); -#else - window_->setFlags((window_->flags() & (~Qt::WindowMinMaxButtonsHint) & (~Qt::Dialog)) | Qt::FramelessWindowHint | Qt::Window); - window_->installEventFilter(this); -#endif - int w = _realWidth.read().toInt(); - int h = _realHeight.read().toInt()+_appBarHeight.read().toInt(); - if(!resizeable()){ - window_->setMaximumSize(QSize(w,h)); - window_->setMinimumSize(QSize(w,h)); - } - window_->resize(QSize(w,h)); - _onStayTopChange(); - _stayTop.connectNotifySignal(this,SLOT(_onStayTopChange())); - _screen.connectNotifySignal(this,SLOT(_onScreenChanged())); - Q_EMIT loadCompleted(); - } -} - -void FluFramelessHelper::_onScreenChanged(){ -#ifdef Q_OS_WIN - HWND hwnd = reinterpret_cast(window_->winId()); - ::SetWindowPos(hwnd,0,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOOWNERZORDER); - ::RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); -#endif -} - -void FluFramelessHelper::showSystemMenu(QPoint point){ -#ifdef Q_OS_WIN - HWND hwnd = reinterpret_cast(window_->winId()); - DWORD style = ::GetWindowLongPtr(hwnd,GWL_STYLE); - ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_SYSMENU); - const HMENU hMenu = ::GetSystemMenu(hwnd, FALSE); - if(maximized() || fullScreen()){ - ::EnableMenuItem(hMenu,SC_MOVE,MFS_DISABLED); - ::EnableMenuItem(hMenu,SC_RESTORE,MFS_ENABLED); - }else{ - ::EnableMenuItem(hMenu,SC_MOVE,MFS_ENABLED); - ::EnableMenuItem(hMenu,SC_RESTORE,MFS_DISABLED); - } - if(resizeable() && !maximized() && !fullScreen()){ - ::EnableMenuItem(hMenu,SC_SIZE,MFS_ENABLED); - ::EnableMenuItem(hMenu,SC_MAXIMIZE,MFS_ENABLED); - }else{ - ::EnableMenuItem(hMenu,SC_SIZE,MFS_DISABLED); - ::EnableMenuItem(hMenu,SC_MAXIMIZE,MFS_DISABLED); - } - const int result = ::TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), point.x()*window_->devicePixelRatio(), point.y()*window_->devicePixelRatio(), 0, hwnd, nullptr); - if (result != FALSE) { - ::PostMessageW(hwnd, WM_SYSCOMMAND, result, 0); - } - ::SetWindowLongPtr(hwnd, GWL_STYLE, style &~ WS_SYSMENU); -#endif -} - -void FluFramelessHelper::showMaximized(){ -#ifdef Q_OS_WIN - HWND hwnd = reinterpret_cast(window_->winId()); - ::ShowWindow(hwnd,3); -#endif -} - -void FluFramelessHelper::_onStayTopChange(){ - bool isStayTop = _stayTop.read().toBool(); -#ifdef Q_OS_WIN - HWND hwnd = reinterpret_cast(window_->winId()); - if(isStayTop){ - ::SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - }else{ - ::SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - } -#else - window_->setFlag(Qt::WindowStaysOnTopHint,isStayTop); -#endif -} - -FluFramelessHelper::~FluFramelessHelper(){ - if (!window_.isNull()) { - window_->setFlags(Qt::Window); -#ifdef Q_OS_WIN - qApp->removeNativeEventFilter(_nativeEvent); - delete _nativeEvent; -#endif - window_->removeEventFilter(this); - } -} - -bool FluFramelessHelper::hoverMaxBtn(){ - if(_appBar.isNull()){ - return false; - } - QVariant var; - QMetaObject::invokeMethod(_appBar.value(), "_maximizeButtonHover",Q_RETURN_ARG(QVariant, var)); - if(var.isNull()){ - return false; - } - return var.toBool(); -} - -bool FluFramelessHelper::hoverAppBar(){ - if(_appBar.isNull()){ - return false; - } - QVariant var; - QMetaObject::invokeMethod(_appBar.value(), "_appBarHover",Q_RETURN_ARG(QVariant, var)); - if(var.isNull()){ - return false; - } - return var.toBool(); -} - -QVariant FluFramelessHelper::getAppBar(){ - return _appBar; -} - -int FluFramelessHelper::getAppBarHeight(){ - if(_appBar.isNull()){ - return 0; - } - QVariant var = _appBar.value()->property("height"); - if(var.isNull()){ - return 0; - } - return var.toInt(); -} - -QObject* FluFramelessHelper::maximizeButton(){ - if(_appBar.isNull()){ - return nullptr; - } - QVariant var = _appBar.value()->property("buttonMaximize"); - if(var.isNull()){ - return nullptr; - } - return var.value(); -} - -bool FluFramelessHelper::resizeable(){ - return !_fixSize.read().toBool(); -} - -bool FluFramelessHelper::maximized(){ - return window_->visibility() == QWindow::Maximized; -} - -bool FluFramelessHelper::fullScreen(){ - return window_->visibility() == QWindow::FullScreen; -} - -int FluFramelessHelper::getMargins(){ - return _margins; -} diff --git a/src/FluFramelessHelper.h b/src/FluFramelessHelper.h deleted file mode 100644 index c39602f0..00000000 --- a/src/FluFramelessHelper.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef FLUFRAMELESSHELPER_H -#define FLUFRAMELESSHELPER_H - -#include -#include -#include -#include -#include -#include - -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) -using QT_NATIVE_EVENT_RESULT_TYPE = qintptr; -using QT_ENTER_EVENT_TYPE = QEnterEvent; -#else -using QT_NATIVE_EVENT_RESULT_TYPE = long; -using QT_ENTER_EVENT_TYPE = QEvent; -#endif - -class FluFramelessHelper; - -/** - * @brief The FramelessEventFilter class - */ -class FramelessEventFilter : public QAbstractNativeEventFilter -{ -public: - FramelessEventFilter(FluFramelessHelper* helper); - bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override; -public: - QPointer _helper = nullptr; - qint64 _current = 0; -}; - -/** - * @brief The FluFramelessHelper class - */ -class FluFramelessHelper : public QQuickItem -{ - Q_OBJECT - QML_NAMED_ELEMENT(FluFramelessHelper) -public: - explicit FluFramelessHelper(QQuickItem *parent = nullptr); - ~FluFramelessHelper(); - void classBegin() override; - void componentComplete() override; - bool hoverMaxBtn(); - bool hoverAppBar(); - bool resizeable(); - int getMargins(); - bool maximized(); - bool fullScreen(); - int getAppBarHeight(); - QVariant getAppBar(); - QObject* maximizeButton(); - Q_INVOKABLE void showSystemMenu(QPoint point); - Q_INVOKABLE void showMaximized(); - Q_SIGNAL void loadCompleted(); -protected: - bool eventFilter(QObject *obj, QEvent *event) override; -private: - void _updateCursor(int edges); - Q_SLOT void _onStayTopChange(); - Q_SLOT void _onScreenChanged(); -public: - QPointer window_ = nullptr; -private: - FramelessEventFilter* _nativeEvent = nullptr; - QQmlProperty _stayTop; - QQmlProperty _screen; - QQmlProperty _fixSize; - QQmlProperty _realHeight; - QQmlProperty _realWidth; - QQmlProperty _appBarHeight; - QVariant _appBar; - int _edges = 0; - int _margins = 8; -}; - -#endif // FLUFRAMELESSHELPER_H diff --git a/src/FluentUI.cpp b/src/FluentUI.cpp index 6a8b4e02..65760117 100644 --- a/src/FluentUI.cpp +++ b/src/FluentUI.cpp @@ -11,9 +11,9 @@ #include "FluCaptcha.h" #include "FluTreeModel.h" #include "FluRectangle.h" -#include "FluFramelessHelper.h" #include "FluQrCodeItem.h" #include "FluTableSortProxyModel.h" +#include "FluFrameless.h" void FluentUI::registerTypes(QQmlEngine *engine){ initializeEngine(engine,uri); @@ -31,7 +31,7 @@ void FluentUI::registerTypes(const char *uri){ qmlRegisterType(uri,major,minor,"FluAccentColor"); qmlRegisterType(uri,major,minor,"FluTreeModel"); qmlRegisterType(uri,major,minor,"FluRectangle"); - qmlRegisterType(uri,major,minor,"FluFramelessHelper"); + qmlRegisterType(uri,major,minor,"FluFrameless"); qmlRegisterType(uri,major,minor,"FluTableSortProxyModel"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluAcrylic.qml"),uri,major,minor,"FluAcrylic"); diff --git a/src/Qt5/imports/FluentUI/Controls/FluAppBar.qml b/src/Qt5/imports/FluentUI/Controls/FluAppBar.qml index a6a074b5..006d4cc8 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluAppBar.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluAppBar.qml @@ -34,7 +34,13 @@ Rectangle{ property int iconSize: 20 property bool isMac: FluTools.isMacos() property color borerlessColor : FluTheme.primaryColor - property bool systemMoveEnable: true + property alias buttonStayTop: btn_stay_top + property alias buttonMinimize: btn_minimize + property alias buttonMaximize: btn_maximize + property alias buttonClose: btn_close + property alias buttonDark: btn_dark + property alias layoutMacosButtons: layout_macos_buttons + property alias layoutStandardbuttons: layout_standard_buttons property var maxClickListener : function(){ if(FluTools.isMacos()){ if (d.win.visibility === Window.FullScreen) @@ -71,16 +77,6 @@ Rectangle{ FluTheme.darkMode = FluThemeType.Dark } } - property var systemMenuListener: function(){ - if(d.win instanceof FluWindow){ - d.win.showSystemMenu() - } - } - property alias buttonStayTop: btn_stay_top - property alias buttonMinimize: btn_minimize - property alias buttonMaximize: btn_maximize - property alias buttonClose: btn_close - property alias buttonDark: btn_dark id:control color: Qt.rgba(0,0,0,0) height: visible ? 30 : 0 @@ -108,29 +104,6 @@ Rectangle{ return false } } - MouseArea{ - id:mouse_app_bar - anchors.fill: parent - onPositionChanged: - (mouse)=>{ - if(systemMoveEnable){ - d.win.startSystemMove() - } - } - onDoubleClicked: - (mouse)=>{ - if(systemMoveEnable && d.resizable && Qt.LeftButton){ - btn_maximize.clicked() - } - } - acceptedButtons: Qt.LeftButton|Qt.RightButton - onClicked: - (mouse)=>{ - if (systemMoveEnable && mouse.button === Qt.RightButton){ - control.systemMenuListener() - } - } - } Row{ anchors{ verticalCenter: parent.verticalCenter @@ -153,9 +126,8 @@ Rectangle{ anchors.verticalCenter: parent.verticalCenter } } - Component{ - id:com_mac_buttons + id:com_macos_buttons RowLayout{ FluImageButton{ Layout.preferredHeight: 12 @@ -186,24 +158,11 @@ Rectangle{ } } } - - FluLoader{ - anchors{ - verticalCenter: parent.verticalCenter - left: parent.left - leftMargin: 10 - } - sourceComponent: isMac ? com_mac_buttons : undefined - } - RowLayout{ - id:layout_row + id:layout_standard_buttons + height: parent.height anchors.right: parent.right - height: control.height spacing: 0 - Component.onCompleted: { - setHitTestVisible(layout_row) - } FluIconButton{ id:btn_dark Layout.preferredWidth: 40 @@ -273,12 +232,9 @@ Rectangle{ horizontalPadding: 0 iconSource : d.isRestore ? FluentIcons.ChromeRestore : FluentIcons.ChromeMaximize color: { - if(down){ + if(pressed){ return maximizePressColor } - if(FluTools.isWindows11OrGreater()){ - return d.hoverMaxBtn ? maximizeHoverColor : maximizeNormalColor - } return hovered ? maximizeHoverColor : maximizeNormalColor } Layout.alignment: Qt.AlignVCenter @@ -312,36 +268,13 @@ Rectangle{ onClicked: closeClickListener() } } - function _maximizeButtonHover(){ - var hover = false - if(btn_maximize.visible && FluTools.isWindows11OrGreater() && d.resizable){ - if(d.containsPointToItem(FluTools.cursorPos(),btn_maximize)){ - hover = true - }else{ - if(btn_maximize.down){ - btn_maximize.down = false - } - } + FluLoader{ + id:layout_macos_buttons + anchors{ + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: 10 } - d.hoverMaxBtn = hover - return hover; - } - function _appBarHover(){ - var cursorPos = FluTools.cursorPos() - for(var i =0 ;i< d.hitTestList.length; i++){ - var item = d.hitTestList[i] - if(item.visible){ - if(d.containsPointToItem(cursorPos,item)){ - return false - } - } - } - if(d.containsPointToItem(cursorPos,control)){ - return true - } - return false - } - function setHitTestVisible(id){ - d.hitTestList.push(id) + sourceComponent: isMac ? com_macos_buttons : undefined } } diff --git a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml index 4e85b4f8..9ce55a93 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml @@ -56,29 +56,20 @@ Window { event.accepted = false } } - signal showSystemMenu signal initArgument(var argument) - signal firstVisible() - property int _realHeight - property int _realWidth - property int _appBarHeight: appBar.height + signal lazyLoad() property var _windowRegister property string _route id:window color:"transparent" Component.onCompleted: { FluRouter.addWindow(window) - _realHeight = height - _realWidth = width useSystemAppBar = FluApp.useSystemAppBar if(useSystemAppBar && autoCenter){ moveWindowToDesktopCenter() } fixWindowSize() initArgument(argument) - if(!useSystemAppBar){ - loader_frameless_helper.sourceComponent = com_frameless_helper - } if(window.autoVisible){ if(window.autoMaximize){ window.showMaximized() @@ -87,33 +78,30 @@ Window { } } } - onShowSystemMenu: { - if(loader_frameless_helper.item){ - loader_frameless_helper.item.showSystemMenu() - } - } onVisibleChanged: { - if(visible && d.isFirstVisible){ - window.firstVisible() - d.isFirstVisible = false + if(visible && d.isLazyInit){ + window.lazyLoad() + d.isLazyInit = false } } QtObject{ id:d - property bool isFirstVisible: true + property bool isLazyInit: true } Connections{ target: window function onClosing(event){closeListener(event)} } - Component{ - id:com_frameless_helper - FluFramelessHelper{ - onLoadCompleted:{ - if(autoCenter){ - window.moveWindowToDesktopCenter() - } - } + FluFrameless{ + id: frameless + appbar: window.appBar + maximizeButton: appBar.buttonMaximize + fixSize: window.fixSize + topmost: window.stayTop + disabled: FluApp.useSystemAppBar + Component.onCompleted: { + frameless.setHitTestVisible(appBar.layoutMacosButtons) + frameless.setHitTestVisible(appBar.layoutStandardbuttons) } } Component{ @@ -198,9 +186,6 @@ Window { border.color: window.resizeBorderColor } } - FluLoader{ - id:loader_frameless_helper - } FluLoader{ anchors.fill: parent sourceComponent: background @@ -256,11 +241,6 @@ Window { return com_border } } - function showLoading(text = qsTr("Loading..."),cancel = true){ - loader_loading.loadingText = text - loader_loading.cancel = cancel - loader_loading.sourceComponent = com_loading - } function hideLoading(){ loader_loading.sourceComponent = undefined } @@ -298,12 +278,17 @@ Window { } } function showMaximized(){ - if(FluTools.isWin()){ - if(loader_frameless_helper.item){ - loader_frameless_helper.item.showMaximized() - } - }else{ - window.visibility = Window.Maximized + frameless.showMaximized() + } + function showLoading(text = "",cancel = true){ + if(text===""){ + text = qsTr("Loading...") } + loader_loading.loadingText = text + loader_loading.cancel = cancel + loader_loading.sourceComponent = com_loading + } + function setHitTestVisible(val){ + frameless.setHitTestVisible(val) } } diff --git a/src/Qt5/imports/FluentUI/plugins.qmltypes b/src/Qt5/imports/FluentUI/plugins.qmltypes index 7155b7b1..e3ad2583 100644 --- a/src/Qt5/imports/FluentUI/plugins.qmltypes +++ b/src/Qt5/imports/FluentUI/plugins.qmltypes @@ -65,17 +65,26 @@ Module { } } Component { - name: "FluFramelessHelper" + name: "FluFrameless" defaultProperty: "data" prototype: "QQuickItem" - exports: ["FluentUI/FluFramelessHelper 1.0"] + exports: ["FluentUI/FluFrameless 1.0"] exportMetaObjectRevisions: [0] - Signal { name: "loadCompleted" } - Method { - name: "showSystemMenu" - Parameter { name: "point"; type: "QPoint" } - } + Property { name: "appbar"; type: "QQuickItem"; isPointer: true } + Property { name: "topmost"; type: "bool" } + Property { name: "maximizeButton"; type: "QQuickItem"; isPointer: true } + Property { name: "minimizedButton"; type: "QQuickItem"; isPointer: true } + Property { name: "closeButton"; type: "QQuickItem"; isPointer: true } + Property { name: "disabled"; type: "bool" } + Property { name: "fixSize"; type: "bool" } + Method { name: "showFullScreen" } Method { name: "showMaximized" } + Method { name: "showMinimized" } + Method { name: "showNormal" } + Method { + name: "setHitTestVisible" + Parameter { type: "QQuickItem"; isPointer: true } + } } Component { name: "FluNavigationViewType" @@ -2180,49 +2189,52 @@ Module { Property { name: "iconSize"; type: "int" } Property { name: "isMac"; type: "bool" } Property { name: "borerlessColor"; type: "QColor" } - Property { name: "systemMoveEnable"; type: "bool" } Property { name: "maxClickListener"; type: "QVariant" } Property { name: "minClickListener"; type: "QVariant" } Property { name: "closeClickListener"; type: "QVariant" } Property { name: "stayTopClickListener"; type: "QVariant" } Property { name: "darkClickListener"; type: "QVariant" } - Property { name: "systemMenuListener"; type: "QVariant" } Property { name: "buttonStayTop" - type: "FluIconButton_QMLTYPE_18" + type: "FluIconButton_QMLTYPE_20" isReadonly: true isPointer: true } Property { name: "buttonMinimize" - type: "FluIconButton_QMLTYPE_18" + type: "FluIconButton_QMLTYPE_20" isReadonly: true isPointer: true } Property { name: "buttonMaximize" - type: "FluIconButton_QMLTYPE_18" + type: "FluIconButton_QMLTYPE_20" isReadonly: true isPointer: true } Property { name: "buttonClose" - type: "FluIconButton_QMLTYPE_18" + type: "FluIconButton_QMLTYPE_20" isReadonly: true isPointer: true } Property { name: "buttonDark" - type: "FluIconButton_QMLTYPE_18" + type: "FluIconButton_QMLTYPE_20" isReadonly: true isPointer: true } - Method { name: "_maximizeButtonHover"; type: "QVariant" } - Method { name: "_appBarHover"; type: "QVariant" } - Method { - name: "setHitTestVisible" - type: "QVariant" - Parameter { name: "id"; type: "QVariant" } + Property { + name: "layoutMacosButtons" + type: "FluLoader_QMLTYPE_13" + isReadonly: true + isPointer: true + } + Property { + name: "layoutStandardbuttons" + type: "QQuickRowLayout" + isReadonly: true + isPointer: true } } Component { @@ -2894,15 +2906,15 @@ Module { defaultProperty: "data" Property { name: "logo"; type: "QUrl" } Property { name: "title"; type: "string" } - Property { name: "items"; type: "FluObject_QMLTYPE_128"; isPointer: true } - Property { name: "footerItems"; type: "FluObject_QMLTYPE_128"; isPointer: true } + Property { name: "items"; type: "FluObject_QMLTYPE_168"; isPointer: true } + Property { name: "footerItems"; type: "FluObject_QMLTYPE_168"; isPointer: true } Property { name: "displayMode"; type: "int" } Property { name: "autoSuggestBox"; type: "QQmlComponent"; isPointer: true } Property { name: "actionItem"; type: "QQmlComponent"; isPointer: true } Property { name: "topPadding"; type: "int" } Property { name: "pageMode"; type: "int" } - Property { name: "navItemRightMenu"; type: "FluMenu_QMLTYPE_34"; isPointer: true } - Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_34"; isPointer: true } + Property { name: "navItemRightMenu"; type: "FluMenu_QMLTYPE_32"; isPointer: true } + Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_32"; isPointer: true } Property { name: "navCompactWidth"; type: "int" } Property { name: "navTopMargin"; type: "int" } Property { name: "cellHeight"; type: "int" } @@ -2910,13 +2922,13 @@ Module { Property { name: "hideNavAppBar"; type: "bool" } Property { name: "buttonMenu" - type: "FluIconButton_QMLTYPE_18" + type: "FluIconButton_QMLTYPE_20" isReadonly: true isPointer: true } Property { name: "buttonBack" - type: "FluIconButton_QMLTYPE_18" + type: "FluIconButton_QMLTYPE_20" isReadonly: true isPointer: true } @@ -3777,24 +3789,14 @@ Module { Property { name: "resizeBorderColor"; type: "QColor" } Property { name: "resizeBorderWidth"; type: "int" } Property { name: "closeListener"; type: "QVariant" } - Property { name: "_realHeight"; type: "int" } - Property { name: "_realWidth"; type: "int" } - Property { name: "_appBarHeight"; type: "int" } Property { name: "_windowRegister"; type: "QVariant" } Property { name: "_route"; type: "string" } Property { name: "contentData"; type: "QObject"; isList: true; isReadonly: true } - Signal { name: "showSystemMenu" } Signal { name: "initArgument" Parameter { name: "argument"; type: "QVariant" } } - Signal { name: "firstVisible" } - Method { - name: "showLoading" - type: "QVariant" - Parameter { name: "text"; type: "QVariant" } - Parameter { name: "cancel"; type: "QVariant" } - } + Signal { name: "lazyLoad" } Method { name: "hideLoading"; type: "QVariant" } Method { name: "showSuccess" @@ -3837,6 +3839,17 @@ Module { Parameter { name: "data"; type: "QVariant" } } Method { name: "showMaximized"; type: "QVariant" } + Method { + name: "showLoading" + type: "QVariant" + Parameter { name: "text"; type: "QVariant" } + Parameter { name: "cancel"; type: "QVariant" } + } + Method { + name: "setHitTestVisible" + type: "QVariant" + Parameter { name: "val"; type: "QVariant" } + } } Component { prototype: "QQuickWindowQmlImpl" @@ -3870,24 +3883,14 @@ Module { Property { name: "resizeBorderColor"; type: "QColor" } Property { name: "resizeBorderWidth"; type: "int" } Property { name: "closeListener"; type: "QVariant" } - Property { name: "_realHeight"; type: "int" } - Property { name: "_realWidth"; type: "int" } - Property { name: "_appBarHeight"; type: "int" } Property { name: "_windowRegister"; type: "QVariant" } Property { name: "_route"; type: "string" } Property { name: "contentData"; type: "QObject"; isList: true; isReadonly: true } - Signal { name: "showSystemMenu" } Signal { name: "initArgument" Parameter { name: "argument"; type: "QVariant" } } - Signal { name: "firstVisible" } - Method { - name: "showLoading" - type: "QVariant" - Parameter { name: "text"; type: "QVariant" } - Parameter { name: "cancel"; type: "QVariant" } - } + Signal { name: "lazyLoad" } Method { name: "hideLoading"; type: "QVariant" } Method { name: "showSuccess" @@ -3930,6 +3933,17 @@ Module { Parameter { name: "data"; type: "QVariant" } } Method { name: "showMaximized"; type: "QVariant" } + Method { + name: "showLoading" + type: "QVariant" + Parameter { name: "text"; type: "QVariant" } + Parameter { name: "cancel"; type: "QVariant" } + } + Method { + name: "setHitTestVisible" + type: "QVariant" + Parameter { name: "val"; type: "QVariant" } + } } Component { prototype: "QQuickItem" diff --git a/src/Qt6/imports/FluentUI/Controls/FluAppBar.qml b/src/Qt6/imports/FluentUI/Controls/FluAppBar.qml index 8a62854b..c75c8165 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluAppBar.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluAppBar.qml @@ -34,7 +34,13 @@ Rectangle{ property int iconSize: 20 property bool isMac: FluTools.isMacos() property color borerlessColor : FluTheme.primaryColor - property bool systemMoveEnable: true + property alias buttonStayTop: btn_stay_top + property alias buttonMinimize: btn_minimize + property alias buttonMaximize: btn_maximize + property alias buttonClose: btn_close + property alias buttonDark: btn_dark + property alias layoutMacosButtons: layout_macos_buttons + property alias layoutStandardbuttons: layout_standard_buttons property var maxClickListener : function(){ if(FluTools.isMacos()){ if (d.win.visibility === Window.FullScreen) @@ -71,16 +77,6 @@ Rectangle{ FluTheme.darkMode = FluThemeType.Dark } } - property var systemMenuListener: function(){ - if(d.win instanceof FluWindow){ - d.win.showSystemMenu() - } - } - property alias buttonStayTop: btn_stay_top - property alias buttonMinimize: btn_minimize - property alias buttonMaximize: btn_maximize - property alias buttonClose: btn_close - property alias buttonDark: btn_dark id:control color: Qt.rgba(0,0,0,0) height: visible ? 30 : 0 @@ -108,29 +104,6 @@ Rectangle{ return false } } - MouseArea{ - id:mouse_app_bar - anchors.fill: parent - onPositionChanged: - (mouse)=>{ - if(systemMoveEnable){ - d.win.startSystemMove() - } - } - onDoubleClicked: - (mouse)=>{ - if(systemMoveEnable && d.resizable && Qt.LeftButton){ - btn_maximize.clicked() - } - } - acceptedButtons: Qt.LeftButton|Qt.RightButton - onClicked: - (mouse)=>{ - if (systemMoveEnable && mouse.button === Qt.RightButton){ - control.systemMenuListener() - } - } - } Row{ anchors{ verticalCenter: parent.verticalCenter @@ -153,9 +126,8 @@ Rectangle{ anchors.verticalCenter: parent.verticalCenter } } - Component{ - id:com_mac_buttons + id:com_macos_buttons RowLayout{ FluImageButton{ Layout.preferredHeight: 12 @@ -186,24 +158,11 @@ Rectangle{ } } } - - FluLoader{ - anchors{ - verticalCenter: parent.verticalCenter - left: parent.left - leftMargin: 10 - } - sourceComponent: isMac ? com_mac_buttons : undefined - } - RowLayout{ - id:layout_row + id:layout_standard_buttons + height: parent.height anchors.right: parent.right - height: control.height spacing: 0 - Component.onCompleted: { - setHitTestVisible(layout_row) - } FluIconButton{ id:btn_dark Layout.preferredWidth: 40 @@ -273,12 +232,9 @@ Rectangle{ horizontalPadding: 0 iconSource : d.isRestore ? FluentIcons.ChromeRestore : FluentIcons.ChromeMaximize color: { - if(down){ + if(pressed){ return maximizePressColor } - if(FluTools.isWindows11OrGreater()){ - return d.hoverMaxBtn ? maximizeHoverColor : maximizeNormalColor - } return hovered ? maximizeHoverColor : maximizeNormalColor } Layout.alignment: Qt.AlignVCenter @@ -312,36 +268,13 @@ Rectangle{ onClicked: closeClickListener() } } - function _maximizeButtonHover(){ - var hover = false - if(btn_maximize.visible && FluTools.isWindows11OrGreater() && d.resizable){ - if(d.containsPointToItem(FluTools.cursorPos(),btn_maximize)){ - hover = true - }else{ - if(btn_maximize.down){ - btn_maximize.down = false - } - } + FluLoader{ + id:layout_macos_buttons + anchors{ + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: 10 } - d.hoverMaxBtn = hover - return hover; - } - function _appBarHover(){ - var cursorPos = FluTools.cursorPos() - for(var i =0 ;i< d.hitTestList.length; i++){ - var item = d.hitTestList[i] - if(item.visible){ - if(d.containsPointToItem(cursorPos,item)){ - return false - } - } - } - if(d.containsPointToItem(cursorPos,control)){ - return true - } - return false - } - function setHitTestVisible(id){ - d.hitTestList.push(id) + sourceComponent: isMac ? com_macos_buttons : undefined } } diff --git a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml index 65106e6e..487c4350 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml @@ -55,29 +55,20 @@ Window { event.accepted = false } } - signal showSystemMenu signal initArgument(var argument) - signal firstVisible() - property int _realHeight - property int _realWidth - property int _appBarHeight: appBar.height + signal lazyLoad() property var _windowRegister property string _route id:window color:"transparent" Component.onCompleted: { FluRouter.addWindow(window) - _realHeight = height - _realWidth = width useSystemAppBar = FluApp.useSystemAppBar if(useSystemAppBar && autoCenter){ moveWindowToDesktopCenter() } fixWindowSize() initArgument(argument) - if(!useSystemAppBar){ - loader_frameless_helper.sourceComponent = com_frameless_helper - } if(window.autoVisible){ if(window.autoMaximize){ window.showMaximized() @@ -86,33 +77,30 @@ Window { } } } - onShowSystemMenu: { - if(loader_frameless_helper.item){ - loader_frameless_helper.item.showSystemMenu() - } - } onVisibleChanged: { - if(visible && d.isFirstVisible){ - window.firstVisible() - d.isFirstVisible = false + if(visible && d.isLazyInit){ + window.lazyLoad() + d.isLazyInit = false } } QtObject{ id:d - property bool isFirstVisible: true + property bool isLazyInit: true } Connections{ target: window function onClosing(event){closeListener(event)} } - Component{ - id:com_frameless_helper - FluFramelessHelper{ - onLoadCompleted:{ - if(autoCenter){ - window.moveWindowToDesktopCenter() - } - } + FluFrameless{ + id: frameless + appbar: window.appBar + maximizeButton: appBar.buttonMaximize + fixSize: window.fixSize + topmost: window.stayTop + disabled: FluApp.useSystemAppBar + Component.onCompleted: { + frameless.setHitTestVisible(appBar.layoutMacosButtons) + frameless.setHitTestVisible(appBar.layoutStandardbuttons) } } Component{ @@ -197,9 +185,6 @@ Window { border.color: window.resizeBorderColor } } - FluLoader{ - id:loader_frameless_helper - } FluLoader{ anchors.fill: parent sourceComponent: background @@ -255,11 +240,6 @@ Window { return com_border } } - function showLoading(text = qsTr("Loading..."),cancel = true){ - loader_loading.loadingText = text - loader_loading.cancel = cancel - loader_loading.sourceComponent = com_loading - } function hideLoading(){ loader_loading.sourceComponent = undefined } @@ -297,12 +277,17 @@ Window { } } function showMaximized(){ - if(FluTools.isWin()){ - if(loader_frameless_helper.item){ - loader_frameless_helper.item.showMaximized() - } - }else{ - window.visibility = Window.Maximized + frameless.showMaximized() + } + function showLoading(text = "",cancel = true){ + if(text===""){ + text = qsTr("Loading...") } + loader_loading.loadingText = text + loader_loading.cancel = cancel + loader_loading.sourceComponent = com_loading + } + function setHitTestVisible(val){ + frameless.setHitTestVisible(val) } }