diff --git a/.gitignore b/.gitignore index aa3808c..16a004e 100644 --- a/.gitignore +++ b/.gitignore @@ -70,7 +70,7 @@ CMakeLists.txt.user* # Binaries # -------- -*.dll +#*.dll *.exe # Directories with generated files diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt index 0a976a0..94a5b9d 100644 --- a/src/3rdparty/CMakeLists.txt +++ b/src/3rdparty/CMakeLists.txt @@ -5,17 +5,27 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") PRIVATE Qt::Core Qt::Widgets - ${CMAKE_CURRENT_LIST_DIR}/QNotify/lib/QNotifyd.lib) + ${CMAKE_CURRENT_LIST_DIR}/QNotify/lib/QNotifyd.lib + ${CMAKE_CURRENT_LIST_DIR}/qwindowkit/lib/QWKCored.lib + ${CMAKE_CURRENT_LIST_DIR}/qwindowkit/lib/QWKWidgetsd.lib + ${CMAKE_CURRENT_LIST_DIR}/qwindowkit/lib/WidgetFramed.lib + ) + elseif(CMAKE_BUILD_TYPE STREQUAL "Release") # Release 模式链接发布库 target_link_libraries(VideoClient PRIVATE Qt::Core Qt::Widgets - ${CMAKE_CURRENT_LIST_DIR}/QNotify/lib/QNotify.lib) + ${CMAKE_CURRENT_LIST_DIR}/QNotify/lib/QNotify.lib + ${CMAKE_CURRENT_LIST_DIR}/qwindowkit/lib/QWKCore.lib + ${CMAKE_CURRENT_LIST_DIR}/qwindowkit/lib/QWKWidgets.lib + ${CMAKE_CURRENT_LIST_DIR}/qwindowkit/lib/WidgetFrame.lib + ) endif() target_include_directories(VideoClient PUBLIC ${CMAKE_CURRENT_LIST_DIR}/QNotify/include -) \ No newline at end of file + ${CMAKE_CURRENT_LIST_DIR}/qwindowkit/include +) diff --git a/src/3rdparty/QNotify/bin/QNotify.dll b/src/3rdparty/QNotify/bin/QNotify.dll new file mode 100644 index 0000000..6f6f296 Binary files /dev/null and b/src/3rdparty/QNotify/bin/QNotify.dll differ diff --git a/src/3rdparty/QNotify/bin/QNotifyd.dll b/src/3rdparty/QNotify/bin/QNotifyd.dll new file mode 100644 index 0000000..bd7e9b8 Binary files /dev/null and b/src/3rdparty/QNotify/bin/QNotifyd.dll differ diff --git a/src/3rdparty/ffmpeg/bin/avcodec-61.dll b/src/3rdparty/ffmpeg/bin/avcodec-61.dll new file mode 100644 index 0000000..96ce212 Binary files /dev/null and b/src/3rdparty/ffmpeg/bin/avcodec-61.dll differ diff --git a/src/3rdparty/ffmpeg/bin/avdevice-61.dll b/src/3rdparty/ffmpeg/bin/avdevice-61.dll new file mode 100644 index 0000000..a86e951 Binary files /dev/null and b/src/3rdparty/ffmpeg/bin/avdevice-61.dll differ diff --git a/src/3rdparty/ffmpeg/bin/avfilter-10.dll b/src/3rdparty/ffmpeg/bin/avfilter-10.dll new file mode 100644 index 0000000..27c2a55 Binary files /dev/null and b/src/3rdparty/ffmpeg/bin/avfilter-10.dll differ diff --git a/src/3rdparty/ffmpeg/bin/avformat-61.dll b/src/3rdparty/ffmpeg/bin/avformat-61.dll new file mode 100644 index 0000000..b7d8830 Binary files /dev/null and b/src/3rdparty/ffmpeg/bin/avformat-61.dll differ diff --git a/src/3rdparty/ffmpeg/bin/avutil-59.dll b/src/3rdparty/ffmpeg/bin/avutil-59.dll new file mode 100644 index 0000000..82d25a8 Binary files /dev/null and b/src/3rdparty/ffmpeg/bin/avutil-59.dll differ diff --git a/src/3rdparty/ffmpeg/bin/postproc-58.dll b/src/3rdparty/ffmpeg/bin/postproc-58.dll new file mode 100644 index 0000000..0195807 Binary files /dev/null and b/src/3rdparty/ffmpeg/bin/postproc-58.dll differ diff --git a/src/3rdparty/ffmpeg/bin/swresample-5.dll b/src/3rdparty/ffmpeg/bin/swresample-5.dll new file mode 100644 index 0000000..a42d042 Binary files /dev/null and b/src/3rdparty/ffmpeg/bin/swresample-5.dll differ diff --git a/src/3rdparty/ffmpeg/bin/swscale-8.dll b/src/3rdparty/ffmpeg/bin/swscale-8.dll new file mode 100644 index 0000000..65a5d8d Binary files /dev/null and b/src/3rdparty/ffmpeg/bin/swscale-8.dll differ diff --git a/src/3rdparty/qwindowkit/include/QWKCore/contexts/abstractwindowcontext_p.h b/src/3rdparty/qwindowkit/include/QWKCore/contexts/abstractwindowcontext_p.h new file mode 100644 index 0000000..e978cb4 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/contexts/abstractwindowcontext_p.h @@ -0,0 +1,177 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ABSTRACTWINDOWCONTEXT_P_H +#define ABSTRACTWINDOWCONTEXT_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace QWK { + + class QWK_CORE_EXPORT AbstractWindowContext : public QObject, + public NativeEventDispatcher, + public SharedEventDispatcher { + Q_OBJECT + public: + AbstractWindowContext(); + ~AbstractWindowContext() override; + + public: + void setup(QObject *host, WindowItemDelegate *delegate); + + inline QObject *host() const; + inline QWindow *window() const; + inline WId windowId() const; + inline WindowItemDelegate *delegate() const; + + inline bool isHitTestVisible(const QObject *obj) const; + bool setHitTestVisible(QObject *obj, bool visible); + + inline QObject *systemButton(WindowAgentBase::SystemButton button) const; + bool setSystemButton(WindowAgentBase::SystemButton button, QObject *obj); + + inline QObject *titleBar() const; + bool setTitleBar(QObject *obj); + +#ifdef Q_OS_MAC + inline ScreenRectCallback systemButtonAreaCallback() const; + void setSystemButtonAreaCallback(const ScreenRectCallback &callback); +#endif + + bool isInSystemButtons(const QPoint &pos, WindowAgentBase::SystemButton *button) const; + bool isInTitleBarDraggableArea(const QPoint &pos) const; + + inline bool isHostWidthFixed() const { + return m_windowHandle + ? ((m_windowHandle->flags() & Qt::MSWindowsFixedSizeDialogHint) || + m_windowHandle->minimumWidth() == m_windowHandle->maximumWidth()) + : false; + } + inline bool isHostHeightFixed() const { + return m_windowHandle + ? ((m_windowHandle->flags() & Qt::MSWindowsFixedSizeDialogHint) || + m_windowHandle->minimumHeight() == m_windowHandle->maximumHeight()) + : false; + } + inline bool isHostSizeFixed() const { + return m_windowHandle ? ((m_windowHandle->flags() & Qt::MSWindowsFixedSizeDialogHint) || + m_windowHandle->minimumSize() == m_windowHandle->maximumSize()) + : false; + } + + virtual QString key() const; + + enum WindowContextHook { + CentralizeHook = 1, + RaiseWindowHook, + ShowSystemMenuHook, + DefaultColorsHook, + DrawWindows10BorderHook_Emulated, // Only works on Windows 10, emulated workaround + DrawWindows10BorderHook_Native, // Only works on Windows 10, native workaround + SystemButtonAreaChangedHook, // Only works on Mac + }; + virtual void virtual_hook(int id, void *data); + + void showSystemMenu(const QPoint &pos); + void notifyWinIdChange(); + + virtual QVariant windowAttribute(const QString &key) const; + virtual bool setWindowAttribute(const QString &key, const QVariant &attribute); + + protected: + bool eventFilter(QObject *obj, QEvent *event) override; + + protected: + virtual void winIdChanged(WId winId, WId oldWinId) = 0; + virtual bool windowAttributeChanged(const QString &key, const QVariant &attribute, + const QVariant &oldAttribute); + + protected: + QObject *m_host{}; + std::unique_ptr m_delegate; + QPointer m_windowHandle; + WId m_windowId{}; + + QSet m_hitTestVisibleItems; +#ifdef Q_OS_MAC + ScreenRectCallback m_systemButtonAreaCallback; +#endif + + QObject *m_titleBar{}; + std::array m_systemButtons{}; + + std::list> m_windowAttributesOrder; + QHash m_windowAttributes; + + std::unique_ptr m_winIdChangeEventFilter; + + void removeSystemButtonsAndHitTestItems(); + + private: + void _q_titleBarDistroyed(QObject *obj); + void _q_hitTestVisibleItemDestroyed(QObject *obj); + void _q_systemButtonDestroyed(QObject *obj); + }; + + inline QObject *AbstractWindowContext::host() const { + return m_host; + } + + inline QWindow *AbstractWindowContext::window() const { + return m_windowHandle; + } + + inline WId AbstractWindowContext::windowId() const { + return m_windowId; + } + + inline WindowItemDelegate *AbstractWindowContext::delegate() const { + return m_delegate.get(); + } + + inline bool AbstractWindowContext::isHitTestVisible(const QObject *obj) const { + return m_hitTestVisibleItems.contains(obj); + } + + inline QObject * + AbstractWindowContext::systemButton(WindowAgentBase::SystemButton button) const { + return m_systemButtons[button]; + } + + inline QObject *AbstractWindowContext::titleBar() const { + return m_titleBar; + } + +#ifdef Q_OS_MAC + inline ScreenRectCallback AbstractWindowContext::systemButtonAreaCallback() const { + return m_systemButtonAreaCallback; + } +#endif + +} + +#endif // ABSTRACTWINDOWCONTEXT_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/contexts/cocoawindowcontext.mm b/src/3rdparty/qwindowkit/include/QWKCore/contexts/cocoawindowcontext.mm new file mode 100644 index 0000000..c26152f --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/contexts/cocoawindowcontext.mm @@ -0,0 +1,748 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#include "cocoawindowcontext_p.h" + +#include +#include + +#include + +#include + +#include "qwkglobal_p.h" +#include "systemwindow_p.h" + +// https://forgetsou.github.io/2020/11/06/macos%E5%BC%80%E5%8F%91-%E5%85%B3%E9%97%AD-%E6%9C%80%E5%B0%8F%E5%8C%96-%E5%85%A8%E5%B1%8F%E5%B1%85%E4%B8%AD%E5%A4%84%E7%90%86(%E4%BB%BFMac%20QQ)/ +// https://nyrra33.com/2019/03/26/changing-titlebars-height/ + +namespace QWK { + + struct NSWindowProxy; + + using ProxyList = QHash; + Q_GLOBAL_STATIC(ProxyList, g_proxyList); + + using ProxyList2 = QHash; + Q_GLOBAL_STATIC(ProxyList2, g_proxyIndexes); + +} + +struct QWK_NSWindowDelegate { +public: + enum NSEventType { + WillEnterFullScreen, + DidEnterFullScreen, + WillExitFullScreen, + DidExitFullScreen, + DidResize, + }; + + virtual ~QWK_NSWindowDelegate() = default; + virtual void windowEvent(NSEventType eventType) = 0; +}; + +// +// Objective C++ Begin +// + +@interface QWK_NSWindowObserver : NSObject { +} +@end + +@implementation QWK_NSWindowObserver + +- (id)init { + self = [super init]; + if (self) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowWillEnterFullScreen:) + name:NSWindowWillEnterFullScreenNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowDidEnterFullScreen:) + name:NSWindowDidEnterFullScreenNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowWillExitFullScreen:) + name:NSWindowWillExitFullScreenNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowDidExitFullScreen:) + name:NSWindowDidExitFullScreenNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowDidResize:) + name:NSWindowDidResizeNotification + object:nil]; + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +- (void)windowWillEnterFullScreen:(NSNotification *)notification { + auto nswindow = reinterpret_cast(notification.object); + if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { + reinterpret_cast(proxy)->windowEvent( + QWK_NSWindowDelegate::WillEnterFullScreen); + } +} + +- (void)windowDidEnterFullScreen:(NSNotification *)notification { + auto nswindow = reinterpret_cast(notification.object); + if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { + reinterpret_cast(proxy)->windowEvent( + QWK_NSWindowDelegate::DidEnterFullScreen); + } +} + +- (void)windowWillExitFullScreen:(NSNotification *)notification { + auto nswindow = reinterpret_cast(notification.object); + if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { + reinterpret_cast(proxy)->windowEvent( + QWK_NSWindowDelegate::WillExitFullScreen); + } +} + +- (void)windowDidExitFullScreen:(NSNotification *)notification { + auto nswindow = reinterpret_cast(notification.object); + if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { + reinterpret_cast(proxy)->windowEvent( + QWK_NSWindowDelegate::DidExitFullScreen); + } +} + +- (void)windowDidResize:(NSNotification *)notification { + auto nswindow = reinterpret_cast(notification.object); + if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { + reinterpret_cast(proxy)->windowEvent( + QWK_NSWindowDelegate::DidResize); + } +} + +@end + +// +// Objective C++ End +// + +namespace QWK { + + struct NSWindowProxy : public QWK_NSWindowDelegate { + enum class BlurMode { + Dark, + Light, + None, + }; + + NSWindowProxy(NSWindow *macWindow) { + nswindow = macWindow; + g_proxyIndexes->insert(nswindow, this); + } + + ~NSWindowProxy() override { + g_proxyIndexes->remove(nswindow); + } + + // Delegate + void windowEvent(NSEventType eventType) override { + switch (eventType) { + case WillExitFullScreen: { + if (!screenRectCallback || !systemButtonVisible) + return; + + // The system buttons will stuck at their default positions when the + // exit-fullscreen animation is running, we need to hide them until the + // animation finishes + for (const auto &button : systemButtons()) { + button.hidden = true; + } + break; + } + + case DidExitFullScreen: { + if (!screenRectCallback || !systemButtonVisible) + return; + + for (const auto &button : systemButtons()) { + button.hidden = false; + } + updateSystemButtonRect(); + break; + } + + case DidResize: { + if (!screenRectCallback || !systemButtonVisible) { + return; + } + updateSystemButtonRect(); + break; + } + + default: + break; + } + } + + // System buttons visibility + void setSystemButtonVisible(bool visible) { + systemButtonVisible = visible; + for (const auto &button : systemButtons()) { + button.hidden = !visible; + } + + if (!screenRectCallback || !visible) { + return; + } + updateSystemButtonRect(); + } + + // System buttons area + void setScreenRectCallback(const ScreenRectCallback &callback) { + screenRectCallback = callback; + + if (!callback || !systemButtonVisible) { + return; + } + updateSystemButtonRect(); + } + + void updateSystemButtonRect() { + const auto &buttons = systemButtons(); + const auto &leftButton = buttons[0]; + const auto &midButton = buttons[1]; + const auto &rightButton = buttons[2]; + + auto titlebar = rightButton.superview; + int titlebarHeight = titlebar.frame.size.height; + + auto spacing = midButton.frame.origin.x - leftButton.frame.origin.x; + auto width = midButton.frame.size.width; + auto height = midButton.frame.size.height; + + auto viewSize = + nswindow.contentView ? nswindow.contentView.frame.size : nswindow.frame.size; + QPoint center = screenRectCallback(QSize(viewSize.width, titlebarHeight)).center(); + + // The origin of the NSWindow coordinate system is in the lower left corner, we + // do the necessary transformations + center.ry() = titlebarHeight - center.y(); + + // Mid button + NSPoint centerOrigin = { + center.x() - width / 2, + center.y() - height / 2, + }; + [midButton setFrameOrigin:centerOrigin]; + + // Left button + NSPoint leftOrigin = { + centerOrigin.x - spacing, + centerOrigin.y, + }; + [leftButton setFrameOrigin:leftOrigin]; + + // Right button + NSPoint rightOrigin = { + centerOrigin.x + spacing, + centerOrigin.y, + }; + [rightButton setFrameOrigin:rightOrigin]; + } + + inline std::array systemButtons() { + NSButton *closeBtn = [nswindow standardWindowButton:NSWindowCloseButton]; + NSButton *minimizeBtn = [nswindow standardWindowButton:NSWindowMiniaturizeButton]; + NSButton *zoomBtn = [nswindow standardWindowButton:NSWindowZoomButton]; + return {closeBtn, minimizeBtn, zoomBtn}; + } + + inline int titleBarHeight() const { + NSButton *closeBtn = [nswindow standardWindowButton:NSWindowCloseButton]; + return closeBtn.superview.frame.size.height; + } + + // Blur effect + bool setBlurEffect(BlurMode mode) { + static Class visualEffectViewClass = NSClassFromString(@"NSVisualEffectView"); + if (!visualEffectViewClass) + return false; + + NSVisualEffectView *effectView = nil; + NSView *const view = [nswindow contentView]; + for (NSView *subview in [[view superview] subviews]) { + if ([subview isKindOfClass:visualEffectViewClass]) { + effectView = reinterpret_cast(subview); + } + } + if (effectView == nil) { + return false; + } + + static const auto originalMaterial = effectView.material; + static const auto originalBlendingMode = effectView.blendingMode; + static const auto originalState = effectView.state; + + if (mode == BlurMode::None) { + effectView.material = originalMaterial; + effectView.blendingMode = originalBlendingMode; + effectView.state = originalState; + effectView.appearance = nil; + } else { + effectView.material = NSVisualEffectMaterialUnderWindowBackground; + effectView.blendingMode = NSVisualEffectBlendingModeBehindWindow; + effectView.state = NSVisualEffectStateFollowsWindowActiveState; + + if (mode == BlurMode::Dark) { + effectView.appearance = + [NSAppearance appearanceNamed:@"NSAppearanceNameVibrantDark"]; + } else { + effectView.appearance = + [NSAppearance appearanceNamed:@"NSAppearanceNameVibrantLight"]; + } + } + return true; + } + + // System title bar + void setSystemTitleBarVisible(const bool visible) { + NSView *nsview = [nswindow contentView]; + if (!nsview) { + return; + } + + nsview.wantsLayer = YES; + nswindow.styleMask |= NSWindowStyleMaskResizable; + if (visible) { + nswindow.styleMask &= ~NSWindowStyleMaskFullSizeContentView; + } else { + nswindow.styleMask |= NSWindowStyleMaskFullSizeContentView; + } + nswindow.titlebarAppearsTransparent = (visible ? NO : YES); + nswindow.titleVisibility = (visible ? NSWindowTitleVisible : NSWindowTitleHidden); + nswindow.hasShadow = YES; + // nswindow.showsToolbarButton = NO; + nswindow.movableByWindowBackground = NO; + nswindow.movable = NO; // This line causes the window in the wrong position when + // become fullscreen. + [nswindow standardWindowButton:NSWindowCloseButton].hidden = NO; + [nswindow standardWindowButton:NSWindowMiniaturizeButton].hidden = NO; + [nswindow standardWindowButton:NSWindowZoomButton].hidden = NO; + } + + static void replaceImplementations() { + Method method = class_getInstanceMethod(windowClass, @selector(setStyleMask:)); + oldSetStyleMask = reinterpret_cast( + method_setImplementation(method, reinterpret_cast(setStyleMask))); + + method = + class_getInstanceMethod(windowClass, @selector(setTitlebarAppearsTransparent:)); + oldSetTitlebarAppearsTransparent = + reinterpret_cast(method_setImplementation( + method, reinterpret_cast(setTitlebarAppearsTransparent))); + +#if 0 + method = class_getInstanceMethod(windowClass, @selector(canBecomeKeyWindow)); + oldCanBecomeKeyWindow = reinterpret_cast(method_setImplementation(method, reinterpret_cast(canBecomeKeyWindow))); + + method = class_getInstanceMethod(windowClass, @selector(canBecomeMainWindow)); + oldCanBecomeMainWindow = reinterpret_cast(method_setImplementation(method, reinterpret_cast(canBecomeMainWindow))); +#endif + + method = class_getInstanceMethod(windowClass, @selector(sendEvent:)); + oldSendEvent = reinterpret_cast( + method_setImplementation(method, reinterpret_cast(sendEvent))); + + // Alloc + windowObserver = [[QWK_NSWindowObserver alloc] init]; + } + + static void restoreImplementations() { + Method method = class_getInstanceMethod(windowClass, @selector(setStyleMask:)); + method_setImplementation(method, reinterpret_cast(oldSetStyleMask)); + oldSetStyleMask = nil; + + method = + class_getInstanceMethod(windowClass, @selector(setTitlebarAppearsTransparent:)); + method_setImplementation(method, + reinterpret_cast(oldSetTitlebarAppearsTransparent)); + oldSetTitlebarAppearsTransparent = nil; + +#if 0 + method = class_getInstanceMethod(windowClass, @selector(canBecomeKeyWindow)); + method_setImplementation(method, reinterpret_cast(oldCanBecomeKeyWindow)); + oldCanBecomeKeyWindow = nil; + + method = class_getInstanceMethod(windowClass, @selector(canBecomeMainWindow)); + method_setImplementation(method, reinterpret_cast(oldCanBecomeMainWindow)); + oldCanBecomeMainWindow = nil; +#endif + + method = class_getInstanceMethod(windowClass, @selector(sendEvent:)); + method_setImplementation(method, reinterpret_cast(oldSendEvent)); + oldSendEvent = nil; + + // Delete + [windowObserver release]; + windowObserver = nil; + } + + static inline const Class windowClass = [NSWindow class]; + + protected: + static BOOL canBecomeKeyWindow(id obj, SEL sel) { + if (g_proxyIndexes->contains(reinterpret_cast(obj))) { + return YES; + } + + if (oldCanBecomeKeyWindow) { + return oldCanBecomeKeyWindow(obj, sel); + } + + return YES; + } + + static BOOL canBecomeMainWindow(id obj, SEL sel) { + if (g_proxyIndexes->contains(reinterpret_cast(obj))) { + return YES; + } + + if (oldCanBecomeMainWindow) { + return oldCanBecomeMainWindow(obj, sel); + } + + return YES; + } + + static void setStyleMask(id obj, SEL sel, NSWindowStyleMask styleMask) { + if (g_proxyIndexes->contains(reinterpret_cast(obj))) { + styleMask |= NSWindowStyleMaskFullSizeContentView; + } + + if (oldSetStyleMask) { + oldSetStyleMask(obj, sel, styleMask); + } + } + + static void setTitlebarAppearsTransparent(id obj, SEL sel, BOOL transparent) { + if (g_proxyIndexes->contains(reinterpret_cast(obj))) { + transparent = YES; + } + + if (oldSetTitlebarAppearsTransparent) { + oldSetTitlebarAppearsTransparent(obj, sel, transparent); + } + } + + static void sendEvent(id obj, SEL sel, NSEvent *event) { + if (oldSendEvent) { + oldSendEvent(obj, sel, event); + } + +#if 0 + const auto nswindow = reinterpret_cast(obj); + const auto it = instances.find(nswindow); + if (it == instances.end()) { + return; + } + + NSWindowProxy *proxy = it.value(); + if (event.type == NSEventTypeLeftMouseDown) { + proxy->lastMouseDownEvent = event; + QCoreApplication::processEvents(); + proxy->lastMouseDownEvent = nil; + } +#endif + } + + private: + Q_DISABLE_COPY(NSWindowProxy) + + NSWindow *nswindow = nil; + + bool systemButtonVisible = true; + ScreenRectCallback screenRectCallback; + + static inline QWK_NSWindowObserver *windowObserver = nil; + + // NSEvent *lastMouseDownEvent = nil; + + using setStyleMaskPtr = void (*)(id, SEL, NSWindowStyleMask); + static inline setStyleMaskPtr oldSetStyleMask = nil; + + using setTitlebarAppearsTransparentPtr = void (*)(id, SEL, BOOL); + static inline setTitlebarAppearsTransparentPtr oldSetTitlebarAppearsTransparent = nil; + + using canBecomeKeyWindowPtr = BOOL (*)(id, SEL); + static inline canBecomeKeyWindowPtr oldCanBecomeKeyWindow = nil; + + using canBecomeMainWindowPtr = BOOL (*)(id, SEL); + static inline canBecomeMainWindowPtr oldCanBecomeMainWindow = nil; + + using sendEventPtr = void (*)(id, SEL, NSEvent *); + static inline sendEventPtr oldSendEvent = nil; + }; + + static inline NSWindow *mac_getNSWindow(const WId windowId) { + const auto nsview = reinterpret_cast(windowId); + return [nsview window]; + } + + static inline NSWindowProxy *ensureWindowProxy(const WId windowId) { + if (g_proxyList->isEmpty()) { + NSWindowProxy::replaceImplementations(); + } + + auto it = g_proxyList->find(windowId); + if (it == g_proxyList->end()) { + NSWindow *nswindow = mac_getNSWindow(windowId); + const auto proxy = new NSWindowProxy(nswindow); + it = g_proxyList->insert(windowId, proxy); + } + return it.value(); + } + + static inline void releaseWindowProxy(const WId windowId) { + if (auto proxy = g_proxyList->take(windowId)) { + // TODO: Determine if the window is valid + + // The window has been destroyed + // proxy->setSystemTitleBarVisible(true); + delete proxy; + } else { + return; + } + + if (g_proxyList->isEmpty()) { + NSWindowProxy::restoreImplementations(); + } + } + + class CocoaWindowEventFilter : public SharedEventFilter { + public: + explicit CocoaWindowEventFilter(AbstractWindowContext *context); + ~CocoaWindowEventFilter() override; + + enum WindowStatus { + Idle, + WaitingRelease, + PreparingMove, + Moving, + }; + + protected: + bool sharedEventFilter(QObject *object, QEvent *event) override; + + private: + AbstractWindowContext *m_context; + bool m_cursorShapeChanged; + WindowStatus m_windowStatus; + }; + + CocoaWindowEventFilter::CocoaWindowEventFilter(AbstractWindowContext *context) + : m_context(context), m_cursorShapeChanged(false), m_windowStatus(Idle) { + m_context->installSharedEventFilter(this); + } + + CocoaWindowEventFilter::~CocoaWindowEventFilter() = default; + + bool CocoaWindowEventFilter::sharedEventFilter(QObject *obj, QEvent *event) { + Q_UNUSED(obj) + + auto type = event->type(); + if (type < QEvent::MouseButtonPress || type > QEvent::MouseMove) { + return false; + } + auto host = m_context->host(); + auto window = m_context->window(); + auto delegate = m_context->delegate(); + auto me = static_cast(event); + + QPoint scenePos = getMouseEventScenePos(me); + QPoint globalPos = getMouseEventGlobalPos(me); + + bool inTitleBar = m_context->isInTitleBarDraggableArea(scenePos); + switch (type) { + case QEvent::MouseButtonPress: { + switch (me->button()) { + case Qt::LeftButton: { + if (inTitleBar) { + // If we call startSystemMove() now but release the mouse without actual + // movement, there will be no MouseReleaseEvent, so we defer it when the + // mouse is actually moving for the first time + m_windowStatus = PreparingMove; + event->accept(); + return true; + } + break; + } + case Qt::RightButton: { + m_context->showSystemMenu(globalPos); + break; + } + default: + break; + } + m_windowStatus = WaitingRelease; + break; + } + + case QEvent::MouseButtonRelease: { + switch (m_windowStatus) { + case PreparingMove: + case Moving: { + m_windowStatus = Idle; + event->accept(); + return true; + } + case WaitingRelease: { + m_windowStatus = Idle; + break; + } + default: { + if (inTitleBar) { + event->accept(); + return true; + } + break; + } + } + break; + } + + case QEvent::MouseMove: { + switch (m_windowStatus) { + case Moving: { + return true; + } + case PreparingMove: { + m_windowStatus = Moving; + startSystemMove(window); + event->accept(); + return true; + } + default: + break; + } + break; + } + + case QEvent::MouseButtonDblClick: { + if (me->button() == Qt::LeftButton && inTitleBar && !m_context->isHostSizeFixed()) { + Qt::WindowFlags windowFlags = delegate->getWindowFlags(host); + Qt::WindowStates windowState = delegate->getWindowState(host); + if ((windowFlags & Qt::WindowMaximizeButtonHint) && + !(windowState & Qt::WindowFullScreen)) { + if (windowState & Qt::WindowMaximized) { + delegate->setWindowState(host, windowState & ~Qt::WindowMaximized); + } else { + delegate->setWindowState(host, windowState | Qt::WindowMaximized); + } + event->accept(); + return true; + } + } + break; + } + + default: + break; + } + return false; + } + + CocoaWindowContext::CocoaWindowContext() : AbstractWindowContext() { + cocoaWindowEventFilter = std::make_unique(this); + } + + CocoaWindowContext::~CocoaWindowContext() { + releaseWindowProxy(m_windowId); + } + + QString CocoaWindowContext::key() const { + return QStringLiteral("cocoa"); + } + + void CocoaWindowContext::virtual_hook(int id, void *data) { + switch (id) { + case SystemButtonAreaChangedHook: { + ensureWindowProxy(m_windowId)->setScreenRectCallback(m_systemButtonAreaCallback); + return; + } + + default: + break; + } + AbstractWindowContext::virtual_hook(id, data); + } + + QVariant CocoaWindowContext::windowAttribute(const QString &key) const { + if (key == QStringLiteral("title-bar-height")) { + if (!m_windowId) + return {}; + return ensureWindowProxy(m_windowId)->titleBarHeight(); + } + return AbstractWindowContext::windowAttribute(key); + } + + void CocoaWindowContext::winIdChanged(WId winId, WId oldWinId) { + // If the original window id is valid, remove all resources related + if (oldWinId) { + releaseWindowProxy(oldWinId); + } + + if (!winId) { + return; + } + + // Allocate new resources + ensureWindowProxy(winId)->setSystemTitleBarVisible(false); + } + + bool CocoaWindowContext::windowAttributeChanged(const QString &key, const QVariant &attribute, + const QVariant &oldAttribute) { + Q_UNUSED(oldAttribute) + + Q_ASSERT(m_windowId); + + if (key == QStringLiteral("no-system-buttons")) { + if (attribute.type() != QVariant::Bool) + return false; + ensureWindowProxy(m_windowId)->setSystemButtonVisible(!attribute.toBool()); + return true; + } + + if (key == QStringLiteral("blur-effect")) { + auto mode = NSWindowProxy::BlurMode::None; + if (attribute.type() == QVariant::Bool) { + if (attribute.toBool()) { + NSString *osxMode = + [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; + mode = [osxMode isEqualToString:@"Dark"] ? NSWindowProxy::BlurMode::Dark + : NSWindowProxy::BlurMode::Light; + } + } else if (attribute.type() == QVariant::String) { + auto value = attribute.toString(); + if (value == QStringLiteral("dark")) { + mode = NSWindowProxy::BlurMode::Dark; + } else if (value == QStringLiteral("light")) { + mode = NSWindowProxy::BlurMode::Light; + } else if (value == QStringLiteral("none")) { + // ... + } else { + return false; + } + } else { + return false; + } + return ensureWindowProxy(m_windowId)->setBlurEffect(mode); + } + return false; + } + +} diff --git a/src/3rdparty/qwindowkit/include/QWKCore/contexts/cocoawindowcontext_p.h b/src/3rdparty/qwindowkit/include/QWKCore/contexts/cocoawindowcontext_p.h new file mode 100644 index 0000000..e35c686 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/contexts/cocoawindowcontext_p.h @@ -0,0 +1,43 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef COCOAWINDOWCONTEXT_P_H +#define COCOAWINDOWCONTEXT_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include + +namespace QWK { + + class CocoaWindowContext : public AbstractWindowContext { + Q_OBJECT + public: + CocoaWindowContext(); + ~CocoaWindowContext() override; + + QString key() const override; + void virtual_hook(int id, void *data) override; + + QVariant windowAttribute(const QString &key) const override; + + protected: + void winIdChanged(WId winId, WId oldWinId) override; + bool windowAttributeChanged(const QString &key, const QVariant &attribute, + const QVariant &oldAttribute) override; + + protected: + std::unique_ptr cocoaWindowEventFilter; + }; + +} + +#endif // COCOAWINDOWCONTEXT_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/contexts/qtwindowcontext_p.h b/src/3rdparty/qwindowkit/include/QWKCore/contexts/qtwindowcontext_p.h new file mode 100644 index 0000000..1625ea1 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/contexts/qtwindowcontext_p.h @@ -0,0 +1,39 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef QTWINDOWCONTEXT_P_H +#define QTWINDOWCONTEXT_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include + +namespace QWK { + + class QtWindowContext : public AbstractWindowContext { + Q_OBJECT + public: + QtWindowContext(); + ~QtWindowContext() override; + + QString key() const override; + void virtual_hook(int id, void *data) override; + + protected: + void winIdChanged(WId winId, WId oldWinId) override; + + protected: + std::unique_ptr qtWindowEventFilter; + }; + +} + +#endif // QTWINDOWCONTEXT_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/contexts/win32windowcontext_p.h b/src/3rdparty/qwindowkit/include/QWKCore/contexts/win32windowcontext_p.h new file mode 100644 index 0000000..2ea1cf4 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/contexts/win32windowcontext_p.h @@ -0,0 +1,83 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WIN32WINDOWCONTEXT_P_H +#define WIN32WINDOWCONTEXT_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include +#include + +namespace QWK { + + class Win32WindowContext : public AbstractWindowContext { + Q_OBJECT + public: + Win32WindowContext(); + ~Win32WindowContext() override; + + enum WindowPart { + Outside, + ClientArea, + ChromeButton, + ResizeBorder, + FixedBorder, + TitleBar, + }; + Q_ENUM(WindowPart) + + QString key() const override; + void virtual_hook(int id, void *data) override; + + QVariant windowAttribute(const QString &key) const override; + + protected: + void winIdChanged(WId winId, WId oldWinId) override; + bool windowAttributeChanged(const QString &key, const QVariant &attribute, + const QVariant &oldAttribute) override; + + public: + bool windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); + + bool systemMenuHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, + LRESULT *result); + + // In order to perfectly apply Windows 11 Snap Layout into the Qt window, we need to + // intercept and emulate most of the mouse events, so that the processing logic + // is quite complex. Simultaneously, in order to make the handling code of other + // Windows messages clearer, we have separated them into this function. + bool snapLayoutHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, + LRESULT *result); + + bool customWindowHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, + LRESULT *result); + + bool nonClientCalcSizeHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, + LRESULT *result); + + protected: + // The last hit test result, helpful to handle WM_MOUSEMOVE and WM_NCMOUSELEAVE. + WindowPart lastHitTestResult = WindowPart::Outside; + int lastHitTestResultRaw = HTNOWHERE; + + // Whether the last mouse leave message is blocked, mainly for handling the unexpected + // WM_MOUSELEAVE. + bool mouseLeaveBlocked = false; + + // For emulating traditional icon button behavior + uint64_t iconButtonClickTime = 0; + int iconButtonClickLevel = 0; + }; + +} + +#endif // WIN32WINDOWCONTEXT_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/kernel/nativeeventfilter_p.h b/src/3rdparty/qwindowkit/include/QWKCore/kernel/nativeeventfilter_p.h new file mode 100644 index 0000000..d7d3e3f --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/kernel/nativeeventfilter_p.h @@ -0,0 +1,70 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef NATIVEEVENTFILTER_P_H +#define NATIVEEVENTFILTER_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include + +namespace QWK { + + class NativeEventFilter; + + class QWK_CORE_EXPORT NativeEventDispatcher { + public: + NativeEventDispatcher(); + virtual ~NativeEventDispatcher(); + + public: + virtual bool nativeDispatch(const QByteArray &eventType, void *message, + QT_NATIVE_EVENT_RESULT_TYPE *result); + + public: + void installNativeEventFilter(NativeEventFilter *filter); + void removeNativeEventFilter(NativeEventFilter *filter); + + protected: + QList m_nativeEventFilters; + + friend class NativeEventFilter; + + Q_DISABLE_COPY(NativeEventDispatcher) + }; + + class QWK_CORE_EXPORT NativeEventFilter { + public: + NativeEventFilter(); + virtual ~NativeEventFilter(); + + public: + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, + QT_NATIVE_EVENT_RESULT_TYPE *result) = 0; + + protected: + NativeEventDispatcher *m_nativeDispatcher; + + friend class NativeEventDispatcher; + + Q_DISABLE_COPY(NativeEventFilter) + }; + + // Automatically install to QCoreApplication at construction + class QWK_CORE_EXPORT AppNativeEventFilter : public NativeEventFilter { + public: + AppNativeEventFilter(); + ~AppNativeEventFilter() override; + }; + +} + +#endif // NATIVEEVENTFILTER_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/kernel/sharedeventfilter_p.h b/src/3rdparty/qwindowkit/include/QWKCore/kernel/sharedeventfilter_p.h new file mode 100644 index 0000000..5c1935b --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/kernel/sharedeventfilter_p.h @@ -0,0 +1,61 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SHAREDEVENTFILTER_P_H +#define SHAREDEVENTFILTER_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include + +namespace QWK { + + class SharedEventFilter; + + class QWK_CORE_EXPORT SharedEventDispatcher { + public: + SharedEventDispatcher(); + virtual ~SharedEventDispatcher(); + + public: + virtual bool sharedDispatch(QObject *obj, QEvent *event); + + public: + void installSharedEventFilter(SharedEventFilter *filter); + void removeSharedEventFilter(SharedEventFilter *filter); + + protected: + QList m_sharedEventFilters; + + friend class SharedEventFilter; + + Q_DISABLE_COPY(SharedEventDispatcher) + }; + + class QWK_CORE_EXPORT SharedEventFilter { + public: + SharedEventFilter(); + virtual ~SharedEventFilter(); + + public: + virtual bool sharedEventFilter(QObject *obj, QEvent *event) = 0; + + protected: + SharedEventDispatcher *m_sharedDispatcher; + + friend class SharedEventDispatcher; + + Q_DISABLE_COPY(SharedEventFilter) + }; + +} + +#endif // SHAREDEVENTFILTER_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/kernel/winidchangeeventfilter_p.h b/src/3rdparty/qwindowkit/include/QWKCore/kernel/winidchangeeventfilter_p.h new file mode 100644 index 0000000..fc1b732 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/kernel/winidchangeeventfilter_p.h @@ -0,0 +1,53 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINIDCHANGEEVENTFILTER_P_H +#define WINIDCHANGEEVENTFILTER_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include + +#include + +namespace QWK { + + class AbstractWindowContext; + + class WinIdChangeEventFilter : public QObject { + public: + WinIdChangeEventFilter(QObject *host, AbstractWindowContext *context) + : host(host), context(context) { + } + + virtual WId winId() const = 0; + + protected: + QObject *host; + AbstractWindowContext *context; + }; + + class QWK_CORE_EXPORT WindowWinIdChangeEventFilter : public WinIdChangeEventFilter { + public: + WindowWinIdChangeEventFilter(QWindow *host, AbstractWindowContext *context); + + WId winId() const override; + + protected: + QWindow *win; + bool isAboutToBeDestroyed; + + bool eventFilter(QObject *obj, QEvent *event) override; + }; + +} + +#endif // WINIDCHANGEEVENTFILTER_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/abstractwindowcontext_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/abstractwindowcontext_p.h new file mode 100644 index 0000000..e6faf43 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/abstractwindowcontext_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/contexts/abstractwindowcontext_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/cocoawindowcontext_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/cocoawindowcontext_p.h new file mode 100644 index 0000000..b3d71eb --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/cocoawindowcontext_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/contexts/cocoawindowcontext_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/nativeeventfilter_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/nativeeventfilter_p.h new file mode 100644 index 0000000..96e881e --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/nativeeventfilter_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/kernel/nativeeventfilter_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/qtwindowcontext_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/qtwindowcontext_p.h new file mode 100644 index 0000000..ecbc9d6 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/qtwindowcontext_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/contexts/qtwindowcontext_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/qwkglobal_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/qwkglobal_p.h new file mode 100644 index 0000000..93de4fd --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/qwkglobal_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/qwkglobal_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/qwkwindowsextra_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/qwkwindowsextra_p.h new file mode 100644 index 0000000..b46571e --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/qwkwindowsextra_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/shared/qwkwindowsextra_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/sharedeventfilter_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/sharedeventfilter_p.h new file mode 100644 index 0000000..7778ae7 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/sharedeventfilter_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/kernel/sharedeventfilter_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/styleagent_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/styleagent_p.h new file mode 100644 index 0000000..d3b8d03 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/styleagent_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/style/styleagent_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/systemwindow_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/systemwindow_p.h new file mode 100644 index 0000000..c89b276 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/systemwindow_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/shared/systemwindow_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/win32windowcontext_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/win32windowcontext_p.h new file mode 100644 index 0000000..7f93889 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/win32windowcontext_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/contexts/win32windowcontext_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/windowagentbase_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/windowagentbase_p.h new file mode 100644 index 0000000..bc95439 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/windowagentbase_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/windowagentbase_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/windowitemdelegate_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/windowitemdelegate_p.h new file mode 100644 index 0000000..4ae6929 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/windowitemdelegate_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/windowitemdelegate_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/windows10borderhandler_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/windows10borderhandler_p.h new file mode 100644 index 0000000..20e85d4 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/windows10borderhandler_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/shared/windows10borderhandler_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/private/winidchangeeventfilter_p.h b/src/3rdparty/qwindowkit/include/QWKCore/private/winidchangeeventfilter_p.h new file mode 100644 index 0000000..319c759 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/private/winidchangeeventfilter_p.h @@ -0,0 +1 @@ +#include "../../../../src/core/kernel/winidchangeeventfilter_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/qwindowkit_linux.h b/src/3rdparty/qwindowkit/include/QWKCore/qwindowkit_linux.h new file mode 100644 index 0000000..17e0106 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/qwindowkit_linux.h @@ -0,0 +1,9 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef QWINDOWKIT_LINUX_H +#define QWINDOWKIT_LINUX_H + + +#endif // QWINDOWKIT_LINUX_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/qwindowkit_windows.h b/src/3rdparty/qwindowkit/include/QWKCore/qwindowkit_windows.h new file mode 100644 index 0000000..7e9b8a7 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/qwindowkit_windows.h @@ -0,0 +1,257 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef QWINDOWKIT_WINDOWS_H +#define QWINDOWKIT_WINDOWS_H + +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) +# include +#endif + +#include + +#ifndef GET_X_LPARAM +# define GET_X_LPARAM(lp) (static_cast(static_cast(LOWORD(lp)))) +#endif + +#ifndef GET_Y_LPARAM +# define GET_Y_LPARAM(lp) (static_cast(static_cast(HIWORD(lp)))) +#endif + +#ifndef RECT_WIDTH +# define RECT_WIDTH(rect) ((rect).right - (rect).left) +#endif + +#ifndef RECT_HEIGHT +# define RECT_HEIGHT(rect) ((rect).bottom - (rect).top) +#endif + +#ifndef USER_DEFAULT_SCREEN_DPI +# define USER_DEFAULT_SCREEN_DPI (96) +#endif + +// Maybe undocumented Windows messages +// https://github.com/tinysec/public/blob/master/win32k/MessageTable.md +// https://ulib.sourceforge.io/doxy/a00239.html +#ifndef WM_UAHDESTROYWINDOW +# define WM_UAHDESTROYWINDOW (0x0090) +#endif + +#ifndef WM_UNREGISTER_WINDOW_SERVICES +# define WM_UNREGISTER_WINDOW_SERVICES (0x0272) +#endif + +#ifndef WM_NCUAHDRAWCAPTION +# define WM_NCUAHDRAWCAPTION (0x00AE) +#endif + +#ifndef WM_NCUAHDRAWFRAME +# define WM_NCUAHDRAWFRAME (0x00AF) +#endif + +namespace QWK { + + namespace Private { + + QWK_CORE_EXPORT RTL_OSVERSIONINFOW GetRealOSVersion(); + + inline bool IsWindows1122H2OrGreater_Real() { + RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); + return (rovi.dwMajorVersion > 10) || + (rovi.dwMajorVersion == 10 && + (rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 22621)); + } + + inline bool IsWindows11OrGreater_Real() { + RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); + return (rovi.dwMajorVersion > 10) || + (rovi.dwMajorVersion == 10 && + (rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 22000)); + } + + inline bool IsWindows1020H1OrGreater_Real() { + RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); + return (rovi.dwMajorVersion > 10) || + (rovi.dwMajorVersion == 10 && + (rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 19041)); + } + + inline bool IsWindows102004OrGreater_Real() { + return IsWindows1020H1OrGreater_Real(); + } + + inline bool IsWindows101903OrGreater_Real() { + RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); + return (rovi.dwMajorVersion > 10) || + (rovi.dwMajorVersion == 10 && + (rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 18362)); + } + + inline bool IsWindows1019H1OrGreater_Real() { + return IsWindows101903OrGreater_Real(); + } + + inline bool IsWindows101809OrGreater_Real() { + RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); + return (rovi.dwMajorVersion > 10) || + (rovi.dwMajorVersion == 10 && + (rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 17763)); + } + + inline bool IsWindows10RS5OrGreater_Real() { + return IsWindows101809OrGreater_Real(); + } + + inline bool IsWindows10OrGreater_Real() { + RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); + return rovi.dwMajorVersion >= 10; + } + + inline bool IsWindows8Point1OrGreater_Real() { + RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); + return (rovi.dwMajorVersion > 6) || + (rovi.dwMajorVersion == 6 && rovi.dwMinorVersion >= 3); + } + + inline bool IsWindows8OrGreater_Real() { + RTL_OSVERSIONINFOW rovi = GetRealOSVersion(); + return (rovi.dwMajorVersion > 6) || + (rovi.dwMajorVersion == 6 && rovi.dwMinorVersion >= 2); + } + + inline bool IsWindows10Only_Real() { + return IsWindows10OrGreater_Real() && !IsWindows11OrGreater_Real(); + } + + } + + // + // Registry Helpers + // + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + class QWK_CORE_EXPORT WindowsRegistryKey { + public: + WindowsRegistryKey(HKEY parentHandle, QStringView subKey, REGSAM permissions = KEY_READ, + REGSAM access = 0); + + ~WindowsRegistryKey(); + + bool isValid() const; + + void close(); + QString stringValue(QStringView subKey) const; + std::pair dwordValue(QStringView subKey) const; + + private: + HKEY m_key; + + Q_DISABLE_COPY(WindowsRegistryKey) + }; + + inline bool WindowsRegistryKey::isValid() const { + return m_key != nullptr; + } +#elif QT_VERSION < QT_VERSION_CHECK(6, 8, 1) + using WindowsRegistryKey = QWinRegistryKey; +#else + class WindowsRegistryKey : public QWinRegistryKey { + public: + WindowsRegistryKey(HKEY parentHandle, QStringView subKey, REGSAM permissions = KEY_READ, + REGSAM access = 0) + : QWinRegistryKey(parentHandle, subKey, permissions, access) { + } + + inline std::pair dwordValue(QStringView subKey) const; + }; + + inline std::pair WindowsRegistryKey::dwordValue(QStringView subKey) const { + const auto val = value(subKey); + if (!val) { + return {0, false}; + } + return {val.value(), true}; + } +#endif + + // + // Version Helpers + // + + inline bool isWin8OrGreater() { + static const bool result = Private::IsWindows8OrGreater_Real(); + return result; + } + + inline bool isWin8Point1OrGreater() { + static const bool result = Private::IsWindows8Point1OrGreater_Real(); + return result; + } + + inline bool isWin10OrGreater() { + static const bool result = Private::IsWindows10OrGreater_Real(); + return result; + } + + inline bool isWin101809OrGreater() { + static const bool result = Private::IsWindows101809OrGreater_Real(); + return result; + } + + inline bool isWin10RS5OrGreater() { + return isWin101809OrGreater(); + } + + inline bool isWin101903OrGreater() { + static const bool result = Private::IsWindows101903OrGreater_Real(); + return result; + } + + inline bool isWin1019H1OrGreater() { + return isWin101903OrGreater(); + } + + inline bool isWin1020H1OrGreater() { + static const bool result = Private::IsWindows1020H1OrGreater_Real(); + return result; + } + + inline bool isWin102004OrGreater() { + return isWin1020H1OrGreater(); + } + + inline bool isWin11OrGreater() { + static const bool result = Private::IsWindows11OrGreater_Real(); + return result; + } + + inline bool isWin1122H2OrGreater() { + static const bool result = Private::IsWindows1122H2OrGreater_Real(); + return result; + } + + inline bool isWin10Only() { + static const bool result = Private::IsWindows10Only_Real(); + return result; + }; + + // + // Native Event Helpers + // + + inline bool isImmersiveColorSetChange(WPARAM wParam, LPARAM lParam) { + return !wParam && lParam && + std::wcscmp(reinterpret_cast(lParam), L"ImmersiveColorSet") == 0; + } + +} + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +Q_DECLARE_METATYPE(QMargins) +#endif + +#endif // QWINDOWKIT_WINDOWS_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/qwkglobal.h b/src/3rdparty/qwindowkit/include/QWKCore/qwkglobal.h new file mode 100644 index 0000000..ded26b5 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/qwkglobal.h @@ -0,0 +1,43 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef QWKGLOBAL_H +#define QWKGLOBAL_H + +#include + +#include +#include + +#ifndef QWK_CORE_EXPORT +# ifdef QWK_CORE_STATIC +# define QWK_CORE_EXPORT +# else +# ifdef QWK_CORE_LIBRARY +# define QWK_CORE_EXPORT Q_DECL_EXPORT +# else +# define QWK_CORE_EXPORT Q_DECL_IMPORT +# endif +# endif +#endif + +#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 + +#ifndef QWINDOWKIT_CONFIG +# define QWINDOWKIT_CONFIG(feature) ((1 / QWINDOWKIT_##feature) == 1) +#endif + +namespace QWK { + + using ScreenRectCallback = std::function; + +} + +#endif // QWKGLOBAL_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/qwkglobal_p.h b/src/3rdparty/qwindowkit/include/QWKCore/qwkglobal_p.h new file mode 100644 index 0000000..57b499f --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/qwkglobal_p.h @@ -0,0 +1,71 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef QWKGLOBAL_P_H +#define QWKGLOBAL_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include +#include +#include + +#include + +QWK_CORE_EXPORT Q_DECLARE_LOGGING_CATEGORY(qWindowKitLog) + +#define QWK_INFO qCInfo(qWindowKitLog) +#define QWK_DEBUG qCDebug(qWindowKitLog) +#define QWK_WARNING qCWarning(qWindowKitLog) +#define QWK_CRITICAL qCCritical(qWindowKitLog) +#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) +# define QWK_FATAL qCFatal(qWindowKitLog) +#endif + +#define MAKE_RGB_COLOR(r, g, b) ((quint32) (((r) &0xFF) << 16) | (((g) &0xFF) << 8) | ((b) &0xFF)) + +#define MAKE_RGBA_COLOR(r, g, b, a) \ + ((quint32) (((a) &0xFF) << 24) | (((r) &0xFF) << 16) | (((g) &0xFF) << 8) | ((b) &0xFF)) + +#if defined(Q_CC_MSVC) +# define QWK_NOINLINE __declspec(noinline) +# define QWK_INLINE __forceinline +# define QWK_USED +#else +# define QWK_NOINLINE __attribute__((noinline)) +# define QWK_INLINE __attribute__((always_inline)) +# define QWK_USED __attribute__((used)) +#endif + +namespace QWK { + + inline QPoint getMouseEventScenePos(const QMouseEvent *event) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + return event->scenePosition().toPoint(); +#else + return event->windowPos().toPoint(); +#endif + } + + inline QPoint getMouseEventGlobalPos(const QMouseEvent *event) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + return event->globalPosition().toPoint(); +#else + return event->screenPos().toPoint(); +#endif + } + + // Be careful when apply this function to a widget + QWK_CORE_EXPORT bool forwardObjectEventFilters(QObject *currentFilter, QObject *receiver, + QEvent *event); +} + +#endif // QWKGLOBAL_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/shared/qwkwindowsextra_p.h b/src/3rdparty/qwindowkit/include/QWKCore/shared/qwkwindowsextra_p.h new file mode 100644 index 0000000..8fdc86e --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/shared/qwkwindowsextra_p.h @@ -0,0 +1,481 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef QWKWINDOWSEXTRA_P_H +#define QWKWINDOWSEXTRA_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +// Don't include this header in any header files. + +namespace QWK { + + enum _DWMWINDOWATTRIBUTE { + // [set] BOOL, Allows the use of host backdrop brushes for the window. + _DWMWA_USE_HOSTBACKDROPBRUSH = 17, + + // Undocumented, the same with DWMWA_USE_IMMERSIVE_DARK_MODE, but available on systems + // before Win10 20H1. + _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19, + + // [set] BOOL, Allows a window to either use the accent color, or dark, according to the + // user Color Mode preferences. + _DWMWA_USE_IMMERSIVE_DARK_MODE = 20, + + // [set] WINDOW_CORNER_PREFERENCE, Controls the policy that rounds top-level window corners + _DWMWA_WINDOW_CORNER_PREFERENCE = 33, + + // [get] UINT, width of the visible border around a thick frame window + _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37, + + // [get, set] SYSTEMBACKDROP_TYPE, Controls the system-drawn backdrop material of a window, + // including behind the non-client area. + _DWMWA_SYSTEMBACKDROP_TYPE = 38, + + // Undocumented, use this value to enable Mica material on Win11 21H2. You should use + // DWMWA_SYSTEMBACKDROP_TYPE instead on Win11 22H2 and newer. + _DWMWA_MICA_EFFECT = 1029 + }; + + // Types used with DWMWA_SYSTEMBACKDROP_TYPE + enum _DWM_SYSTEMBACKDROP_TYPE { + _DWMSBT_AUTO, // [Default] Let DWM automatically decide the system-drawn backdrop for this + // window. + _DWMSBT_NONE, // [Disable] Do not draw any system backdrop. + _DWMSBT_MAINWINDOW, // [Mica] Draw the backdrop material effect corresponding to a + // long-lived window. + _DWMSBT_TRANSIENTWINDOW, // [Acrylic] Draw the backdrop material effect corresponding to a + // transient window. + _DWMSBT_TABBEDWINDOW, // [Mica Alt] Draw the backdrop material effect corresponding to a + // window with a tabbed title bar. + }; + + enum WINDOWCOMPOSITIONATTRIB { + WCA_UNDEFINED = 0, + WCA_NCRENDERING_ENABLED = 1, + WCA_NCRENDERING_POLICY = 2, + WCA_TRANSITIONS_FORCEDISABLED = 3, + WCA_ALLOW_NCPAINT = 4, + WCA_CAPTION_BUTTON_BOUNDS = 5, + WCA_NONCLIENT_RTL_LAYOUT = 6, + WCA_FORCE_ICONIC_REPRESENTATION = 7, + WCA_EXTENDED_FRAME_BOUNDS = 8, + WCA_HAS_ICONIC_BITMAP = 9, + WCA_THEME_ATTRIBUTES = 10, + WCA_NCRENDERING_EXILED = 11, + WCA_NCADORNMENTINFO = 12, + WCA_EXCLUDED_FROM_LIVEPREVIEW = 13, + WCA_VIDEO_OVERLAY_ACTIVE = 14, + WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15, + WCA_DISALLOW_PEEK = 16, + WCA_CLOAK = 17, + WCA_CLOAKED = 18, + WCA_ACCENT_POLICY = 19, + WCA_FREEZE_REPRESENTATION = 20, + WCA_EVER_UNCLOAKED = 21, + WCA_VISUAL_OWNER = 22, + WCA_HOLOGRAPHIC = 23, + WCA_EXCLUDED_FROM_DDA = 24, + WCA_PASSIVEUPDATEMODE = 25, + WCA_USEDARKMODECOLORS = 26, + WCA_CORNER_STYLE = 27, + WCA_PART_COLOR = 28, + WCA_DISABLE_MOVESIZE_FEEDBACK = 29, + WCA_LAST = 30 + }; + + enum ACCENT_STATE { + ACCENT_DISABLED = 0, + ACCENT_ENABLE_GRADIENT = 1, + ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, + ACCENT_ENABLE_BLURBEHIND = 3, // Traditional DWM blur + ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803 + ACCENT_ENABLE_HOST_BACKDROP = 5, // RS5 1809 + ACCENT_INVALID_STATE = 6 // Using this value will remove the window background + }; + + enum ACCENT_FLAG { + ACCENT_NONE = 0, + ACCENT_ENABLE_ACRYLIC = 1, + ACCENT_ENABLE_ACRYLIC_WITH_LUMINOSITY = 482 + }; + + struct ACCENT_POLICY { + DWORD dwAccentState; + DWORD dwAccentFlags; + DWORD dwGradientColor; // #AABBGGRR + DWORD dwAnimationId; + }; + using PACCENT_POLICY = ACCENT_POLICY *; + + struct WINDOWCOMPOSITIONATTRIBDATA { + WINDOWCOMPOSITIONATTRIB Attrib; + PVOID pvData; + SIZE_T cbData; + }; + using PWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA *; + + enum PREFERRED_APP_MODE { + PAM_DEFAULT = 0, // Default behavior on systems before Win10 1809. It indicates the + // application doesn't support dark mode at all. + PAM_AUTO = + 1, // Available since Win10 1809, let system decide whether to enable dark mode or not. + PAM_DARK = 2, // Available since Win10 1903, force dark mode regardless of the system theme. + PAM_LIGHT = + 3, // Available since Win10 1903, force light mode regardless of the system theme. + PAM_MAX = 4 + }; + + using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA); + + // Win10 1809 (10.0.17763) + using RefreshImmersiveColorPolicyStatePtr = VOID(WINAPI *)(VOID); // Ordinal 104 + using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL); // Ordinal 133 + using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL); // Ordinal 135 + using FlushMenuThemesPtr = VOID(WINAPI *)(VOID); // Ordinal 136 + // Win10 1903 (10.0.18362) + using SetPreferredAppModePtr = PREFERRED_APP_MODE(WINAPI *)(PREFERRED_APP_MODE); // Ordinal 135 + + namespace { + + struct DynamicApis { + static const DynamicApis &instance() { + static const DynamicApis inst; + return inst; + } + +#define DYNAMIC_API_DECLARE(NAME) decltype(&::NAME) p##NAME = nullptr + + DYNAMIC_API_DECLARE(DwmFlush); + DYNAMIC_API_DECLARE(DwmIsCompositionEnabled); + DYNAMIC_API_DECLARE(DwmGetWindowAttribute); + DYNAMIC_API_DECLARE(DwmSetWindowAttribute); + DYNAMIC_API_DECLARE(DwmExtendFrameIntoClientArea); + DYNAMIC_API_DECLARE(DwmEnableBlurBehindWindow); + DYNAMIC_API_DECLARE(GetDpiForWindow); + DYNAMIC_API_DECLARE(GetSystemMetricsForDpi); + DYNAMIC_API_DECLARE(AdjustWindowRectExForDpi); + DYNAMIC_API_DECLARE(GetDpiForMonitor); + +#undef DYNAMIC_API_DECLARE + + SetWindowCompositionAttributePtr pSetWindowCompositionAttribute = nullptr; + RefreshImmersiveColorPolicyStatePtr pRefreshImmersiveColorPolicyState = nullptr; + AllowDarkModeForWindowPtr pAllowDarkModeForWindow = nullptr; + AllowDarkModeForAppPtr pAllowDarkModeForApp = nullptr; + FlushMenuThemesPtr pFlushMenuThemes = nullptr; + SetPreferredAppModePtr pSetPreferredAppMode = nullptr; + + private: + DynamicApis() { +#define DYNAMIC_API_RESOLVE(DLL, NAME) \ + p##NAME = reinterpret_cast(DLL.resolve(#NAME)) + + QSystemLibrary user32(QStringLiteral("user32")); + DYNAMIC_API_RESOLVE(user32, GetDpiForWindow); + DYNAMIC_API_RESOLVE(user32, GetSystemMetricsForDpi); + DYNAMIC_API_RESOLVE(user32, SetWindowCompositionAttribute); + DYNAMIC_API_RESOLVE(user32, AdjustWindowRectExForDpi); + + QSystemLibrary shcore(QStringLiteral("shcore")); + DYNAMIC_API_RESOLVE(shcore, GetDpiForMonitor); + + QSystemLibrary dwmapi(QStringLiteral("dwmapi")); + DYNAMIC_API_RESOLVE(dwmapi, DwmFlush); + DYNAMIC_API_RESOLVE(dwmapi, DwmIsCompositionEnabled); + DYNAMIC_API_RESOLVE(dwmapi, DwmGetWindowAttribute); + DYNAMIC_API_RESOLVE(dwmapi, DwmSetWindowAttribute); + DYNAMIC_API_RESOLVE(dwmapi, DwmExtendFrameIntoClientArea); + DYNAMIC_API_RESOLVE(dwmapi, DwmEnableBlurBehindWindow); + +#undef DYNAMIC_API_RESOLVE + +#define UNDOC_API_RESOLVE(DLL, NAME, ORDINAL) \ + p##NAME = reinterpret_cast(DLL.resolve(MAKEINTRESOURCEA(ORDINAL))) + + QSystemLibrary uxtheme(QStringLiteral("uxtheme")); + UNDOC_API_RESOLVE(uxtheme, RefreshImmersiveColorPolicyState, 104); + UNDOC_API_RESOLVE(uxtheme, AllowDarkModeForWindow, 133); + UNDOC_API_RESOLVE(uxtheme, AllowDarkModeForApp, 135); + UNDOC_API_RESOLVE(uxtheme, FlushMenuThemes, 136); + UNDOC_API_RESOLVE(uxtheme, SetPreferredAppMode, 135); + +#undef UNDOC_API_RESOLVE + } + + ~DynamicApis() = default; + + Q_DISABLE_COPY(DynamicApis) + }; + + } + + inline constexpr bool operator==(const POINT &lhs, const POINT &rhs) noexcept { + return ((lhs.x == rhs.x) && (lhs.y == rhs.y)); + } + + inline constexpr bool operator!=(const POINT &lhs, const POINT &rhs) noexcept { + return !operator==(lhs, rhs); + } + + inline constexpr bool operator==(const SIZE &lhs, const SIZE &rhs) noexcept { + return ((lhs.cx == rhs.cx) && (lhs.cy == rhs.cy)); + } + + inline constexpr bool operator!=(const SIZE &lhs, const SIZE &rhs) noexcept { + return !operator==(lhs, rhs); + } + + inline constexpr bool operator>(const SIZE &lhs, const SIZE &rhs) noexcept { + return ((lhs.cx * lhs.cy) > (rhs.cx * rhs.cy)); + } + + inline constexpr bool operator>=(const SIZE &lhs, const SIZE &rhs) noexcept { + return (operator>(lhs, rhs) || operator==(lhs, rhs)); + } + + inline constexpr bool operator<(const SIZE &lhs, const SIZE &rhs) noexcept { + return (operator!=(lhs, rhs) && !operator>(lhs, rhs)); + } + + inline constexpr bool operator<=(const SIZE &lhs, const SIZE &rhs) noexcept { + return (operator<(lhs, rhs) || operator==(lhs, rhs)); + } + + inline constexpr bool operator==(const RECT &lhs, const RECT &rhs) noexcept { + return ((lhs.left == rhs.left) && (lhs.top == rhs.top) && (lhs.right == rhs.right) && + (lhs.bottom == rhs.bottom)); + } + + inline constexpr bool operator!=(const RECT &lhs, const RECT &rhs) noexcept { + return !operator==(lhs, rhs); + } + + inline constexpr QPoint point2qpoint(const POINT &point) { + return QPoint{int(point.x), int(point.y)}; + } + + inline constexpr POINT qpoint2point(const QPoint &point) { + return POINT{LONG(point.x()), LONG(point.y())}; + } + + inline constexpr QSize size2qsize(const SIZE &size) { + return QSize{int(size.cx), int(size.cy)}; + } + + inline constexpr SIZE qsize2size(const QSize &size) { + return SIZE{LONG(size.width()), LONG(size.height())}; + } + + inline constexpr QRect rect2qrect(const RECT &rect) { + return QRect{ + QPoint{int(rect.left), int(rect.top) }, + QSize{int(RECT_WIDTH(rect)), int(RECT_HEIGHT(rect))} + }; + } + + inline constexpr RECT qrect2rect(const QRect &qrect) { + return RECT{LONG(qrect.left()), LONG(qrect.top()), LONG(qrect.right()), + LONG(qrect.bottom())}; + } + + inline constexpr QMargins margins2qmargins(const MARGINS &margins) { + return {margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, + margins.cyBottomHeight}; + } + + inline constexpr MARGINS qmargins2margins(const QMargins &qmargins) { + return {qmargins.left(), qmargins.right(), qmargins.top(), qmargins.bottom()}; + } + + inline /*constexpr*/ QString hwnd2str(const WId windowId) { + // NULL handle is allowed here. + return QLatin1String("0x") + + QString::number(windowId, 16).toUpper().rightJustified(8, u'0'); + } + + inline /*constexpr*/ QString hwnd2str(HWND hwnd) { + // NULL handle is allowed here. + return hwnd2str(reinterpret_cast(hwnd)); + } + + inline bool isDwmCompositionEnabled() { + if (isWin8OrGreater()) { + return true; + } + const DynamicApis &apis = DynamicApis::instance(); + if (!apis.pDwmIsCompositionEnabled) { + return false; + } + BOOL enabled = FALSE; + return SUCCEEDED(apis.pDwmIsCompositionEnabled(&enabled)) && enabled; + } + + inline bool isWindowFrameBorderColorized() { + WindowsRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)"); + if (!registry.isValid()) { + return false; + } + auto value = registry.dwordValue(L"ColorPrevalence"); + if (!value.second) { + return false; + } + return value.first; + } + + inline bool isHighContrastModeEnabled() { + HIGHCONTRASTW hc{}; + hc.cbSize = sizeof(hc); + ::SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, FALSE); + return (hc.dwFlags & HCF_HIGHCONTRASTON); + } + + inline bool isDarkThemeActive() { + if (!isWin101809OrGreater()) { + return false; + } +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + return QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark; +#else + WindowsRegistryKey registry( + HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)"); + if (!registry.isValid()) { + return false; + } + auto value = registry.dwordValue(L"AppsUseLightTheme"); + if (!value.second) { + return false; + } + return !value.first; +#endif + } + + inline bool isDarkWindowFrameEnabled(HWND hwnd) { + if (!isWin101809OrGreater()) { + return false; + } + BOOL enabled = FALSE; + const DynamicApis &apis = DynamicApis::instance(); + const auto attr = isWin1020H1OrGreater() ? _DWMWA_USE_IMMERSIVE_DARK_MODE + : _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1; + return SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd, attr, &enabled, sizeof(enabled))) && + enabled; + } + + inline QColor getAccentColor() { +#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) + return QGuiApplication::palette().color(QPalette::Accent); +#else + WindowsRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)"); + if (!registry.isValid()) { + return {}; + } + auto value = registry.dwordValue(L"AccentColor"); + if (!value.second) { + return {}; + } + // The retrieved value is in the #AABBGGRR format, we need to + // convert it to the #AARRGGBB format which Qt expects. + QColor color = QColor::fromRgba(value.first); + if (!color.isValid()) { + return {}; + } + return QColor::fromRgb(color.blue(), color.green(), color.red(), color.alpha()); +#endif + } + + inline quint32 getDpiForWindow(HWND hwnd) { + const DynamicApis &apis = DynamicApis::instance(); + if (apis.pGetDpiForWindow) { // Win10 + return apis.pGetDpiForWindow(hwnd); + } else if (apis.pGetDpiForMonitor) { // Win8.1 + HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + UINT dpiX{0}; + UINT dpiY{0}; + apis.pGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + return dpiX; + } else { // Win2K + HDC hdc = ::GetDC(nullptr); + const int dpiX = ::GetDeviceCaps(hdc, LOGPIXELSX); + // const int dpiY = ::GetDeviceCaps(hdc, LOGPIXELSY); + ::ReleaseDC(nullptr, hdc); + return quint32(dpiX); + } + } + + inline quint32 getSystemMetricsForDpi(int index, quint32 dpi) { + const DynamicApis &apis = DynamicApis::instance(); + if (apis.pGetSystemMetricsForDpi) { + return apis.pGetSystemMetricsForDpi(index, dpi); + } + const int result = ::GetSystemMetrics(index); + // GetSystemMetrics() always give you scaled value. + if (dpi != USER_DEFAULT_SCREEN_DPI) { + return result; + } + const qreal dpr = qreal(dpi) / qreal(USER_DEFAULT_SCREEN_DPI); + // ### Not sure how Windows itself rounds non-integer value. + return qFloor(qreal(result) / dpr); + } + + inline quint32 getWindowFrameBorderThickness(HWND hwnd) { + const DynamicApis &apis = DynamicApis::instance(); + if (isWin11OrGreater()) { + UINT result = 0; + if (SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, + &result, sizeof(result)))) { + return result; + } + } + if (isWin10OrGreater()) { + const quint32 dpi = getDpiForWindow(hwnd); + // When DPI is 96, it should be 1px. + return getSystemMetricsForDpi(SM_CXBORDER, dpi); + } + // There's no such thing (a visible frame border line) before Win10. + return 0; + } + + inline quint32 getResizeBorderThickness(HWND hwnd) { + const quint32 dpi = getDpiForWindow(hwnd); + // When DPI is 96, SM_CXSIZEFRAME is 4px, SM_CXPADDEDBORDER is also 4px, + // so the result should be 8px. This result won't be affected by OS version, + // it's 8px in Win7, and so in Win11. + return getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + + getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); + } + + inline quint32 getTitleBarHeight(HWND hwnd) { + const quint32 dpi = getDpiForWindow(hwnd); + // When DPI is 96, SM_CYCAPTION is 23px, so the result should be 31px. + // However, according to latest MS design manual, the title bar height + // should be 32px, maybe there's some rounding issue. + return getSystemMetricsForDpi(SM_CYCAPTION, dpi) + + getSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) + + getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); + } + +} + +#endif // QWKWINDOWSEXTRA_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/shared/systemwindow_p.h b/src/3rdparty/qwindowkit/include/QWKCore/shared/systemwindow_p.h new file mode 100644 index 0000000..d969aa9 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/shared/systemwindow_p.h @@ -0,0 +1,151 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SYSTEMWINDOW_P_H +#define SYSTEMWINDOW_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include +#include + +#include + +namespace QWK { + + class WindowMoveManipulator : public QObject { + public: + explicit WindowMoveManipulator(QWindow *targetWindow) + : QObject(targetWindow), target(targetWindow), initialMousePosition(QCursor::pos()), + initialWindowPosition(targetWindow->position()) { + target->installEventFilter(this); + } + + protected: + bool eventFilter(QObject *obj, QEvent *event) override { + switch (event->type()) { + case QEvent::MouseMove: { + auto mouseEvent = static_cast(event); + QPoint delta = getMouseEventGlobalPos(mouseEvent) - initialMousePosition; + target->setPosition(initialWindowPosition + delta); + return true; + } + + case QEvent::MouseButtonRelease: { + if (target->y() < 0) { + target->setPosition(target->x(), 0); + } + target->removeEventFilter(this); + deleteLater(); + } + + default: + break; + } + return false; + } + + private: + QWindow *target; + QPoint initialMousePosition; + QPoint initialWindowPosition; + }; + + class WindowResizeManipulator : public QObject { + public: + WindowResizeManipulator(QWindow *targetWindow, Qt::Edges edges) + : QObject(targetWindow), target(targetWindow), resizeEdges(edges), + initialMousePosition(QCursor::pos()), initialWindowRect(target->geometry()) { + target->installEventFilter(this); + } + + protected: + bool eventFilter(QObject *obj, QEvent *event) override { + switch (event->type()) { + case QEvent::MouseMove: { + auto mouseEvent = static_cast(event); + QPoint globalMousePos = getMouseEventGlobalPos(mouseEvent); + QRect windowRect = initialWindowRect; + + if (resizeEdges & Qt::LeftEdge) { + int delta = globalMousePos.x() - initialMousePosition.x(); + windowRect.setLeft(initialWindowRect.left() + delta); + } + if (resizeEdges & Qt::RightEdge) { + int delta = globalMousePos.x() - initialMousePosition.x(); + windowRect.setRight(initialWindowRect.right() + delta); + } + if (resizeEdges & Qt::TopEdge) { + int delta = globalMousePos.y() - initialMousePosition.y(); + windowRect.setTop(initialWindowRect.top() + delta); + } + if (resizeEdges & Qt::BottomEdge) { + int delta = globalMousePos.y() - initialMousePosition.y(); + windowRect.setBottom(initialWindowRect.bottom() + delta); + } + + target->setGeometry(windowRect); + return true; + } + + case QEvent::MouseButtonRelease: { + target->removeEventFilter(this); + deleteLater(); + } + + default: + break; + } + return false; + } + + private: + QWindow *target; + QPoint initialMousePosition; + QRect initialWindowRect; + Qt::Edges resizeEdges; + }; + + // QWindow::startSystemMove() and QWindow::startSystemResize() is first supported at Qt 5.15 + // QWindow::startSystemResize() returns false on macOS + // QWindow::startSystemMove() and QWindow::startSystemResize() returns false on Linux Unity DE + + // When the new API fails, we emulate the window actions using the classical API. + + inline void startSystemMove(QWindow *window) { +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + std::ignore = new WindowMoveManipulator(window); +#elif defined(Q_OS_LINUX) + if (window->startSystemMove()) { + return; + } + std::ignore = new WindowMoveManipulator(window); +#else + window->startSystemMove(); +#endif + } + + inline void startSystemResize(QWindow *window, Qt::Edges edges) { +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + std::ignore = new WindowResizeManipulator(window, edges); +#elif defined(Q_OS_MAC) || defined(Q_OS_LINUX) + if (window->startSystemResize(edges)) { + return; + } + std::ignore = new WindowResizeManipulator(window, edges); +#else + window->startSystemResize(edges); +#endif + } + +} + +#endif // SYSTEMWINDOW_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/shared/windows10borderhandler_p.h b/src/3rdparty/qwindowkit/include/QWKCore/shared/windows10borderhandler_p.h new file mode 100644 index 0000000..8099fe0 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/shared/windows10borderhandler_p.h @@ -0,0 +1,135 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINDOWS10BORDERHANDLER_P_H +#define WINDOWS10BORDERHANDLER_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include +#include + +#include +#include +#include + +namespace QWK { + + class Windows10BorderHandler : public NativeEventFilter, public SharedEventFilter { + public: + inline Windows10BorderHandler(AbstractWindowContext *ctx) : ctx(ctx) { + ctx->installNativeEventFilter(this); + ctx->installSharedEventFilter(this); + } + + inline void setupNecessaryAttributes() { + // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L940 + // Must extend top frame to client area + static QVariant defaultMargins = QVariant::fromValue(QMargins(0, 1, 0, 0)); + ctx->setWindowAttribute(QStringLiteral("extra-margins"), defaultMargins); + + // Enable dark mode by default, otherwise the system borders are white + ctx->setWindowAttribute(QStringLiteral("dark-mode"), true); + } + + inline bool isNormalWindow() const { + return !(ctx->window()->windowStates() & + (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen)); + } + + inline void drawBorder() { + ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook_Native, nullptr); + } + + inline int borderThickness() const { + return ctx->windowAttribute(QStringLiteral("border-thickness")).toInt(); + } + + inline void updateExtraMargins(bool windowActive) { + if (windowActive) { + // Restore margins when the window is active + static QVariant defaultMargins = QVariant::fromValue(QMargins(0, 1, 0, 0)); + ctx->setWindowAttribute(QStringLiteral("extra-margins"), defaultMargins); + return; + } + + // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L904 + // When the window is inactive, there is a transparency bug in the top + // border, and we need to extend the non-client area to the whole title + // bar. + QRect frame = ctx->windowAttribute(QStringLiteral("window-rect")).toRect(); + QMargins margins{0, -frame.top(), 0, 0}; + ctx->setWindowAttribute(QStringLiteral("extra-margins"), QVariant::fromValue(margins)); + } + + virtual void updateGeometry() = 0; + + virtual bool isWindowActive() const { + return ctx->window()->isActive(); + } + + protected: + bool nativeEventFilter(const QByteArray &eventType, void *message, + QT_NATIVE_EVENT_RESULT_TYPE *result) override { + Q_UNUSED(eventType) + + const auto msg = static_cast(message); + switch (msg->message) { + case WM_DPICHANGED: { + updateGeometry(); + updateExtraMargins(isWindowActive()); + break; + } + + case WM_ACTIVATE: { + updateExtraMargins(LOWORD(msg->wParam) != WA_INACTIVE); + break; + } + + case WM_THEMECHANGED: + case WM_SYSCOLORCHANGE: + case WM_DWMCOLORIZATIONCOLORCHANGED: { + // If we do not refresh this property, the native border will turn white + // permanently (like the dark mode is turned off) after the user changes + // the accent color in system personalization settings. + // So we need this ugly hack to re-apply dark mode to get rid of this + // strange Windows bug. + if (ctx->windowAttribute(QStringLiteral("dark-mode")).toBool()) { + ctx->setWindowAttribute(QStringLiteral("dark-mode"), true); + } + break; + } + + default: + break; + } + return false; + } + + bool sharedEventFilter(QObject *obj, QEvent *event) override { + Q_UNUSED(obj) + + if (event->type() == QEvent::WinIdChange) { + if (ctx->windowId()) { + setupNecessaryAttributes(); + updateGeometry(); + } + } + return false; + } + + protected: + AbstractWindowContext *ctx; + }; + +} + +#endif // WINDOWS10BORDERHANDLER_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKCore/style/styleagent.h b/src/3rdparty/qwindowkit/include/QWKCore/style/styleagent.h new file mode 100644 index 0000000..c573e8d --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/style/styleagent.h @@ -0,0 +1,48 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef STYLEAGENT_H +#define STYLEAGENT_H + +#include + +#include +#include + +#include + +namespace QWK { + + class StyleAgentPrivate; + + class QWK_CORE_EXPORT StyleAgent : public QObject { + Q_OBJECT + Q_DECLARE_PRIVATE(StyleAgent) + public: + explicit StyleAgent(QObject *parent = nullptr); + ~StyleAgent() override; + + enum SystemTheme { + Unknown, + Light, + Dark, + HighContrast, + }; + Q_ENUM(SystemTheme) + + public: + SystemTheme systemTheme() const; + + Q_SIGNALS: + void systemThemeChanged(); + + protected: + StyleAgent(StyleAgentPrivate &d, QObject *parent = nullptr); + + const std::unique_ptr d_ptr; + }; + +} + +#endif // STYLEAGENT_H \ No newline at end of file diff --git a/src/3rdparty/qwindowkit/include/QWKCore/style/styleagent_mac.mm b/src/3rdparty/qwindowkit/include/QWKCore/style/styleagent_mac.mm new file mode 100644 index 0000000..e4764ad --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/style/styleagent_mac.mm @@ -0,0 +1,98 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#include "styleagent_p.h" + +#include + +#include + +namespace QWK { + + static StyleAgent::SystemTheme getSystemTheme() { + NSString *osxMode = + [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; + bool isDark = [osxMode isEqualToString:@"Dark"]; + return isDark ? StyleAgent::Dark : StyleAgent::Light; + } + + static void notifyAllStyleAgents(); + +} + +// +// Objective C++ Begin +// + +@interface QWK_SystemThemeObserver : NSObject { +} +@end + +@implementation QWK_SystemThemeObserver + +- (id)init { + self = [super init]; + if (self) { + [[NSDistributedNotificationCenter defaultCenter] + addObserver:self + selector:@selector(interfaceModeChanged:) + name:@"AppleInterfaceThemeChangedNotification" + object:nil]; + } + return self; +} + +- (void)dealloc { + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +- (void)interfaceModeChanged:(NSNotification *)notification { + QWK::notifyAllStyleAgents(); +} + +@end + +// +// Objective C++ End +// + + +namespace QWK { + + using StyleAgentSet = QSet; + Q_GLOBAL_STATIC(StyleAgentSet, g_styleAgentSet) + + static QWK_SystemThemeObserver *g_systemThemeObserver = nil; + + void notifyAllStyleAgents() { + auto theme = getSystemTheme(); + for (auto &&ap : std::as_const(*g_styleAgentSet())) { + ap->notifyThemeChanged(theme); + } + } + + void StyleAgentPrivate::setupSystemThemeHook() { + systemTheme = getSystemTheme(); + + // Alloc + if (g_styleAgentSet->isEmpty()) { + g_systemThemeObserver = [[QWK_SystemThemeObserver alloc] init]; + } + + g_styleAgentSet->insert(this); + } + + void StyleAgentPrivate::removeSystemThemeHook() { + if (!g_styleAgentSet->remove(this)) + return; + + if (g_styleAgentSet->isEmpty()) { + // Delete + [g_systemThemeObserver release]; + g_systemThemeObserver = nil; + } + } + +} \ No newline at end of file diff --git a/src/3rdparty/qwindowkit/include/QWKCore/style/styleagent_p.h b/src/3rdparty/qwindowkit/include/QWKCore/style/styleagent_p.h new file mode 100644 index 0000000..0ee6500 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/style/styleagent_p.h @@ -0,0 +1,43 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef STYLEAGENTPRIVATE_H +#define STYLEAGENTPRIVATE_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include + +#include + +namespace QWK { + + class StyleAgentPrivate : public QObject { + Q_DECLARE_PUBLIC(StyleAgent) + public: + StyleAgentPrivate(); + ~StyleAgentPrivate() override; + + void init(); + + StyleAgent *q_ptr; + + StyleAgent::SystemTheme systemTheme = StyleAgent::Unknown; + + void setupSystemThemeHook(); + void removeSystemThemeHook(); + + void notifyThemeChanged(StyleAgent::SystemTheme theme); + }; + +} + +#endif // STYLEAGENTPRIVATE_H \ No newline at end of file diff --git a/src/3rdparty/qwindowkit/include/QWKCore/styleagent.h b/src/3rdparty/qwindowkit/include/QWKCore/styleagent.h new file mode 100644 index 0000000..9e3c2d8 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/styleagent.h @@ -0,0 +1 @@ +#include "../../../src/core/style/styleagent.h" diff --git a/src/3rdparty/qwindowkit/include/QWKCore/windowagentbase.h b/src/3rdparty/qwindowkit/include/QWKCore/windowagentbase.h new file mode 100644 index 0000000..5b25528 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/windowagentbase.h @@ -0,0 +1,50 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINDOWAGENTBASE_H +#define WINDOWAGENTBASE_H + +#include + +#include + +#include + +namespace QWK { + + class WindowAgentBasePrivate; + + class QWK_CORE_EXPORT WindowAgentBase : public QObject { + Q_OBJECT + Q_DECLARE_PRIVATE(WindowAgentBase) + public: + ~WindowAgentBase() override; + + enum SystemButton { + Unknown, + WindowIcon, + Help, + Minimize, + Maximize, + Close, + }; + Q_ENUM(SystemButton) + + QVariant windowAttribute(const QString &key) const; + Q_INVOKABLE bool setWindowAttribute(const QString &key, const QVariant &attribute); + + public Q_SLOTS: + void showSystemMenu(const QPoint &pos); // Only available on Windows now + void centralize(); + void raise(); + + protected: + explicit WindowAgentBase(WindowAgentBasePrivate &d, QObject *parent = nullptr); + + const std::unique_ptr d_ptr; + }; + +} + +#endif // WINDOWAGENTBASE_H \ No newline at end of file diff --git a/src/3rdparty/qwindowkit/include/QWKCore/windowagentbase_p.h b/src/3rdparty/qwindowkit/include/QWKCore/windowagentbase_p.h new file mode 100644 index 0000000..e7b67ec --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/windowagentbase_p.h @@ -0,0 +1,49 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINDOWAGENTBASEPRIVATE_H +#define WINDOWAGENTBASEPRIVATE_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include +#include + +namespace QWK { + + class QWK_CORE_EXPORT WindowAgentBasePrivate { + Q_DECLARE_PUBLIC(WindowAgentBase) + public: + WindowAgentBasePrivate(); + virtual ~WindowAgentBasePrivate(); + + void init(); + + WindowAgentBase *q_ptr; // no need to initialize + + virtual AbstractWindowContext *createContext() const; + + void setup(QObject *host, WindowItemDelegate *delegate); + + std::unique_ptr context; + + public: + using WindowContextFactoryMethod = AbstractWindowContext *(*) (); + + static WindowContextFactoryMethod windowContextFactoryMethod; + + private: + Q_DISABLE_COPY(WindowAgentBasePrivate) + }; + +} + +#endif // WINDOWAGENTBASEPRIVATE_H \ No newline at end of file diff --git a/src/3rdparty/qwindowkit/include/QWKCore/windowitemdelegate_p.h b/src/3rdparty/qwindowkit/include/QWKCore/windowitemdelegate_p.h new file mode 100644 index 0000000..47f2848 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKCore/windowitemdelegate_p.h @@ -0,0 +1,65 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINDOWITEMDELEGATE_P_H +#define WINDOWITEMDELEGATE_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include +#include +#include + +#include +#include + +namespace QWK { + + class QWK_CORE_EXPORT WindowItemDelegate { + public: + WindowItemDelegate(); + virtual ~WindowItemDelegate(); + + public: + // Item property query + virtual QWindow *window(const QObject *obj) const = 0; + virtual bool isEnabled(const QObject *obj) const = 0; + virtual bool isVisible(const QObject *obj) const = 0; + virtual QRect mapGeometryToScene(const QObject *obj) const = 0; + + // Host property query + virtual QWindow *hostWindow(const QObject *host) const = 0; + virtual bool isWindowActive(const QObject *host) const = 0; + virtual Qt::WindowStates getWindowState(const QObject *host) const = 0; + virtual Qt::WindowFlags getWindowFlags(const QObject *host) const = 0; + virtual QRect getGeometry(const QObject *host) const = 0; + + // Callbacks + virtual void resetQtGrabbedControl(QObject *host) const; + virtual void setWindowState(QObject *host, Qt::WindowStates state) const = 0; + virtual void setCursorShape(QObject *host, Qt::CursorShape shape) const = 0; + virtual void restoreCursorShape(QObject *host) const = 0; + virtual void setWindowFlags(QObject *host, Qt::WindowFlags flags) const = 0; + virtual void setWindowVisible(QObject *host, bool visible) const = 0; + virtual void setGeometry(QObject *host, const QRect &rect) = 0; + virtual void bringWindowToTop(QObject *host) const = 0; + + // Factories + virtual WinIdChangeEventFilter * + createWinIdEventFilter(QObject *host, AbstractWindowContext *context) const; + + private: + Q_DISABLE_COPY(WindowItemDelegate) + }; + +} + +#endif // WINDOWITEMDELEGATE_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKWidgets/private/widgetitemdelegate_p.h b/src/3rdparty/qwindowkit/include/QWKWidgets/private/widgetitemdelegate_p.h new file mode 100644 index 0000000..7c4b3e5 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKWidgets/private/widgetitemdelegate_p.h @@ -0,0 +1 @@ +#include "../../../../src/widgets/widgetitemdelegate_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKWidgets/private/widgetwindowagent_p.h b/src/3rdparty/qwindowkit/include/QWKWidgets/private/widgetwindowagent_p.h new file mode 100644 index 0000000..fa50572 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKWidgets/private/widgetwindowagent_p.h @@ -0,0 +1 @@ +#include "../../../../src/widgets/widgetwindowagent_p.h" diff --git a/src/3rdparty/qwindowkit/include/QWKWidgets/qwkwidgetsglobal.h b/src/3rdparty/qwindowkit/include/QWKWidgets/qwkwidgetsglobal.h new file mode 100644 index 0000000..5bca751 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKWidgets/qwkwidgetsglobal.h @@ -0,0 +1,22 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef QWKWIDGETSGLOBAL_H +#define QWKWIDGETSGLOBAL_H + +#include + +#ifndef QWK_WIDGETS_EXPORT +# ifdef QWK_WIDGETS_STATIC +# define QWK_WIDGETS_EXPORT +# else +# ifdef QWK_WIDGETS_LIBRARY +# define QWK_WIDGETS_EXPORT Q_DECL_EXPORT +# else +# define QWK_WIDGETS_EXPORT Q_DECL_IMPORT +# endif +# endif +#endif + +#endif // QWKWIDGETSGLOBAL_H diff --git a/src/3rdparty/qwindowkit/include/QWKWidgets/widgetitemdelegate_p.h b/src/3rdparty/qwindowkit/include/QWKWidgets/widgetitemdelegate_p.h new file mode 100644 index 0000000..fcd4d31 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKWidgets/widgetitemdelegate_p.h @@ -0,0 +1,57 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WIDGETITEMDELEGATE_P_H +#define WIDGETITEMDELEGATE_P_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include +#include + +#include +#include + +namespace QWK { + + class QWK_WIDGETS_EXPORT WidgetItemDelegate : public WindowItemDelegate { + public: + WidgetItemDelegate(); + ~WidgetItemDelegate() override; + + public: + QWindow *window(const QObject *obj) const override; + bool isEnabled(const QObject *obj) const override; + bool isVisible(const QObject *obj) const override; + QRect mapGeometryToScene(const QObject *obj) const override; + + QWindow *hostWindow(const QObject *host) const override; + bool isWindowActive(const QObject *host) const override; + Qt::WindowStates getWindowState(const QObject *host) const override; + Qt::WindowFlags getWindowFlags(const QObject *host) const override; + QRect getGeometry(const QObject *host) const override; + + void resetQtGrabbedControl(QObject *host) const override; + void setWindowState(QObject *host, Qt::WindowStates state) const override; + void setCursorShape(QObject *host, Qt::CursorShape shape) const override; + void restoreCursorShape(QObject *host) const override; + void setWindowFlags(QObject *host, Qt::WindowFlags flags) const override; + void setWindowVisible(QObject *host, bool visible) const override; + void setGeometry(QObject *host, const QRect &rect) override; + void bringWindowToTop(QObject *host) const override; + + WinIdChangeEventFilter * + createWinIdEventFilter(QObject *host, AbstractWindowContext *context) const override; + }; + +} + +#endif // WIDGETITEMDELEGATE_P_H diff --git a/src/3rdparty/qwindowkit/include/QWKWidgets/widgetwindowagent.h b/src/3rdparty/qwindowkit/include/QWKWidgets/widgetwindowagent.h new file mode 100644 index 0000000..4a0f9c8 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKWidgets/widgetwindowagent.h @@ -0,0 +1,55 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WIDGETWINDOWAGENT_H +#define WIDGETWINDOWAGENT_H + +#include + +#include +#include + +namespace QWK { + + class WidgetWindowAgentPrivate; + + class QWK_WIDGETS_EXPORT WidgetWindowAgent : public WindowAgentBase { + Q_OBJECT + Q_DECLARE_PRIVATE(WidgetWindowAgent) + public: + explicit WidgetWindowAgent(QObject *parent = nullptr); + ~WidgetWindowAgent() override; + + public: + bool setup(QWidget *w); + + QWidget *titleBar() const; + void setTitleBar(QWidget *w); + + QWidget *systemButton(SystemButton button) const; + void setSystemButton(SystemButton button, QWidget *w); + +#ifdef Q_OS_MAC + // The system button area APIs are experimental, very likely to change in the future. + QWidget *systemButtonArea() const; + void setSystemButtonArea(QWidget *widget); + + ScreenRectCallback systemButtonAreaCallback() const; + void setSystemButtonAreaCallback(const ScreenRectCallback &callback); +#endif + + bool isHitTestVisible(const QWidget *w) const; + void setHitTestVisible(QWidget *w, bool visible = true); + + Q_SIGNALS: + void titleBarChanged(QWidget *w); + void systemButtonChanged(SystemButton button, QWidget *w); + + protected: + WidgetWindowAgent(WidgetWindowAgentPrivate &d, QObject *parent = nullptr); + }; + +} + +#endif // WIDGETWINDOWAGENT_H \ No newline at end of file diff --git a/src/3rdparty/qwindowkit/include/QWKWidgets/widgetwindowagent_p.h b/src/3rdparty/qwindowkit/include/QWKWidgets/widgetwindowagent_p.h new file mode 100644 index 0000000..1382dd5 --- /dev/null +++ b/src/3rdparty/qwindowkit/include/QWKWidgets/widgetwindowagent_p.h @@ -0,0 +1,47 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WIDGETWINDOWAGENTPRIVATE_H +#define WIDGETWINDOWAGENTPRIVATE_H + +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + +#include +#include +#include + +namespace QWK { + + class WidgetWindowAgentPrivate : public WindowAgentBasePrivate { + Q_DECLARE_PUBLIC(WidgetWindowAgent) + public: + WidgetWindowAgentPrivate(); + ~WidgetWindowAgentPrivate(); + + void init(); + + // Host + QWidget *hostWidget{}; + +#ifdef Q_OS_MAC + QWidget *systemButtonAreaWidget{}; + std::unique_ptr systemButtonAreaWidgetEventFilter; +#endif + +#if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) + void setupWindows10BorderWorkaround(); + std::unique_ptr borderHandler; +#endif + }; + +} + +#endif // WIDGETWINDOWAGENTPRIVATE_H diff --git a/src/3rdparty/qwindowkit/include/widgetframe/windowbar.h b/src/3rdparty/qwindowkit/include/widgetframe/windowbar.h new file mode 100644 index 0000000..e9cf04c --- /dev/null +++ b/src/3rdparty/qwindowkit/include/widgetframe/windowbar.h @@ -0,0 +1,78 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINDOWBAR_H +#define WINDOWBAR_H + +#include +#include +#include +#include + +namespace QWK { + + class WindowBarPrivate; + + class WindowBar : public QFrame { + Q_OBJECT + Q_DECLARE_PRIVATE(WindowBar) + public: + explicit WindowBar(QWidget *parent = nullptr); + ~WindowBar(); + + public: + QMenuBar *menuBar() const; + QLabel *titleLabel() const; + QAbstractButton *iconButton() const; + QAbstractButton *pinButton() const; + QAbstractButton *minButton() const; + QAbstractButton *maxButton() const; + QAbstractButton *closeButton() const; + + void setMenuBar(QMenuBar *menuBar); + void setTitleLabel(QLabel *label); + void setIconButton(QAbstractButton *btn); + void setPinButton(QAbstractButton *btn); + void setMinButton(QAbstractButton *btn); + void setMaxButton(QAbstractButton *btn); + void setCloseButton(QAbstractButton *btn); + + QMenuBar *takeMenuBar(); + QLabel *takeTitleLabel(); + QAbstractButton *takeIconButton(); + QAbstractButton *takePinButton(); + QAbstractButton *takeMinButton(); + QAbstractButton *takeMaxButton(); + QAbstractButton *takeCloseButton(); + + QWidget *hostWidget() const; + void setHostWidget(QWidget *w); + + bool titleFollowWindow() const; + void setTitleFollowWindow(bool value); + + bool iconFollowWindow() const; + void setIconFollowWindow(bool value); + + Q_SIGNALS: + void pinRequested(bool pin = false); + void minimizeRequested(); + void maximizeRequested(bool max = false); + void closeRequested(); + + protected: + bool eventFilter(QObject *obj, QEvent *event) override; + + virtual void titleChanged(const QString &text); + virtual void iconChanged(const QIcon &icon); + + protected: + WindowBar(WindowBarPrivate &d, QWidget *parent = nullptr); + + QScopedPointer d_ptr; + }; + +} + +#endif // WINDOWBAR_H \ No newline at end of file diff --git a/src/3rdparty/qwindowkit/include/widgetframe/windowbar_p.h b/src/3rdparty/qwindowkit/include/widgetframe/windowbar_p.h new file mode 100644 index 0000000..35422ae --- /dev/null +++ b/src/3rdparty/qwindowkit/include/widgetframe/windowbar_p.h @@ -0,0 +1,58 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINDOWBARPRIVATE_H +#define WINDOWBARPRIVATE_H + +#include + +#include "windowbar.h" + +namespace QWK { + + class WindowBarPrivate { + Q_DECLARE_PUBLIC(WindowBar) + public: + WindowBarPrivate(); + virtual ~WindowBarPrivate(); + + void init(); + + WindowBar *q_ptr; + + QWidget *w; + bool autoTitle; + bool autoIcon; + + enum WindowBarItem { + IconButton, + MenuWidget, + TitleLabel, + PinButton, + MinimizeButton, + MaximizeButton, + CloseButton, + }; + + QHBoxLayout *layout; + + inline QWidget *widgetAt(int index) const { + return layout->itemAt(index)->widget(); + } + + void setWidgetAt(int index, QWidget *widget); + + QWidget *takeWidgetAt(int index); + + inline void insertDefaultSpace(int index) { + layout->insertSpacerItem(index, new QSpacerItem(0, 0)); + } + + private: + Q_DISABLE_COPY(WindowBarPrivate) + }; + +} + +#endif // WINDOWBARPRIVATE_H \ No newline at end of file diff --git a/src/3rdparty/qwindowkit/include/widgetframe/windowbutton.h b/src/3rdparty/qwindowkit/include/widgetframe/windowbutton.h new file mode 100644 index 0000000..402b50f --- /dev/null +++ b/src/3rdparty/qwindowkit/include/widgetframe/windowbutton.h @@ -0,0 +1,50 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINDOWBUTTON_H +#define WINDOWBUTTON_H + +#include + +namespace QWK { + + class WindowButtonPrivate; + + class WindowButton : public QPushButton { + Q_OBJECT + Q_DECLARE_PRIVATE(WindowButton) + Q_PROPERTY(QIcon iconNormal READ iconNormal WRITE setIconNormal FINAL) + Q_PROPERTY(QIcon iconChecked READ iconChecked WRITE setIconChecked FINAL) + Q_PROPERTY(QIcon iconDisabled READ iconDisabled WRITE setIconDisabled FINAL) + public: + explicit WindowButton(QWidget *parent = nullptr); + ~WindowButton(); + + public: + QIcon iconNormal() const; + void setIconNormal(const QIcon &icon); + + QIcon iconChecked() const; + void setIconChecked(const QIcon &icon); + + QIcon iconDisabled() const; + void setIconDisabled(const QIcon &icon); + + Q_SIGNALS: + void doubleClicked(); + + protected: + void checkStateSet() override; + + void mouseDoubleClickEvent(QMouseEvent *event) override; + + protected: + WindowButton(WindowButtonPrivate &d, QWidget *parent = nullptr); + + QScopedPointer d_ptr; + }; + +} + +#endif // WINDOWBUTTON_H \ No newline at end of file diff --git a/src/3rdparty/qwindowkit/include/widgetframe/windowbutton_p.h b/src/3rdparty/qwindowkit/include/widgetframe/windowbutton_p.h new file mode 100644 index 0000000..34aaaaf --- /dev/null +++ b/src/3rdparty/qwindowkit/include/widgetframe/windowbutton_p.h @@ -0,0 +1,31 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINDOWBUTTONPRIVATE_H +#define WINDOWBUTTONPRIVATE_H + +#include "windowbutton.h" + +namespace QWK { + + class WindowButtonPrivate { + Q_DECLARE_PUBLIC(WindowButton) + public: + WindowButtonPrivate(); + virtual ~WindowButtonPrivate(); + + void init(); + + WindowButton *q_ptr; + + QIcon iconNormal; + QIcon iconChecked; + QIcon iconDisabled; + + void reloadIcon(); + }; + +} + +#endif // WINDOWBUTTONPRIVATE_H \ No newline at end of file diff --git a/src/3rdparty/qwindowkit/lib/QWKCore.lib b/src/3rdparty/qwindowkit/lib/QWKCore.lib new file mode 100644 index 0000000..6fedce9 Binary files /dev/null and b/src/3rdparty/qwindowkit/lib/QWKCore.lib differ diff --git a/src/3rdparty/qwindowkit/lib/QWKCored.lib b/src/3rdparty/qwindowkit/lib/QWKCored.lib new file mode 100644 index 0000000..8de9d8c Binary files /dev/null and b/src/3rdparty/qwindowkit/lib/QWKCored.lib differ diff --git a/src/3rdparty/qwindowkit/lib/QWKWidgets.lib b/src/3rdparty/qwindowkit/lib/QWKWidgets.lib new file mode 100644 index 0000000..bcb515a Binary files /dev/null and b/src/3rdparty/qwindowkit/lib/QWKWidgets.lib differ diff --git a/src/3rdparty/qwindowkit/lib/QWKWidgetsd.lib b/src/3rdparty/qwindowkit/lib/QWKWidgetsd.lib new file mode 100644 index 0000000..e79be5d Binary files /dev/null and b/src/3rdparty/qwindowkit/lib/QWKWidgetsd.lib differ diff --git a/src/3rdparty/qwindowkit/lib/WidgetFrame.lib b/src/3rdparty/qwindowkit/lib/WidgetFrame.lib new file mode 100644 index 0000000..ee67ef3 Binary files /dev/null and b/src/3rdparty/qwindowkit/lib/WidgetFrame.lib differ diff --git a/src/3rdparty/qwindowkit/lib/WidgetFramed.lib b/src/3rdparty/qwindowkit/lib/WidgetFramed.lib new file mode 100644 index 0000000..1070dbe Binary files /dev/null and b/src/3rdparty/qwindowkit/lib/WidgetFramed.lib differ diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index cf7ea95..5cc39a4 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,14 +1,14 @@ #include "mainwindow.h" // #include -// #include +#include +#include +#include #include #include #include "ui_mainwindow.h" -// #include "widgetframe/windowbar.h" -// #include "widgetframe/windowbutton.h" #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include #else @@ -19,47 +19,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); - this->setWindowTitle("载荷视频播放软件"); - /* - auto windowAgent = new QWK::WidgetWindowAgent(this); - windowAgent->setup(this); - auto windowBar = new QWK::WindowBar(); - auto iconButton = new QWK::WindowButton(); - iconButton->setObjectName(QStringLiteral("icon-button")); - iconButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - - auto minButton = new QWK::WindowButton(); - minButton->setObjectName(QStringLiteral("min-button")); - minButton->setProperty("system-button", true); - minButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - - auto maxButton = new QWK::WindowButton(); - maxButton->setCheckable(true); - maxButton->setObjectName(QStringLiteral("max-button")); - maxButton->setProperty("system-button", true); - maxButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - - auto closeButton = new QWK::WindowButton(); - closeButton->setObjectName(QStringLiteral("close-button")); - closeButton->setProperty("system-button", true); - closeButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - - windowBar->setIconButton(iconButton); - windowBar->setMinButton(minButton); - windowBar->setMaxButton(maxButton); - windowBar->setCloseButton(closeButton); - windowAgent->setTitleBar(windowBar); - setMenuWidget(windowBar); - // windowAgent->setHitTestVisible(windowBar->menuBar(), true); - */ + // this->setWindowTitle("载荷视频播放软件"); + installWindowAgent(); + loadStyleSheet(Dark); // apply the qss QFile qssfile(":/Qss/qss.qss"); if (qssfile.open(QFile::ReadOnly)) { - // file.open(QFile::ReadOnly); QString style = QLatin1String(qssfile.readAll()); - qApp->setStyleSheet(style); - // qDebug()<message == WM_SYSCOMMAND) { + if (61587 == msg->wParam) { // 单击事件 + return true; + } + if (61539 == msg->wParam) { // 双击事件 + return true; + } + } +#endif + return QMainWindow::nativeEvent(eventType, message, result); +} + void MainWindow::initSignalConnection() { connect(ui->commandWidget, &CommandWidget::startConnectionSignal, ui->videoWidget, &VideoWidget::udpPlay, Qt::UniqueConnection); @@ -123,111 +106,18 @@ void MainWindow::showMessageSlots(QString message, int type) { } } -/* void MainWindow::installWindowAgent() { // 1. Setup window agent windowAgent = new QWK::WidgetWindowAgent(this); windowAgent->setup(this); - // 2. Construct your title bar - auto menuBar = [this]() { - auto menuBar = new QMenuBar(this); - - // Virtual menu - auto file = new QMenu(tr("File(&F)"), menuBar); - file->addAction(new QAction(tr("New(&N)"), menuBar)); - file->addAction(new QAction(tr("Open(&O)"), menuBar)); - file->addSeparator(); - - auto edit = new QMenu(tr("Edit(&E)"), menuBar); - edit->addAction(new QAction(tr("Undo(&U)"), menuBar)); - edit->addAction(new QAction(tr("Redo(&R)"), menuBar)); - - // Theme action - auto darkAction = new QAction(tr("Enable dark theme"), menuBar); - darkAction->setCheckable(true); - // connect(darkAction, &QAction::triggered, this, [this](bool checked) { - // loadStyleSheet(checked ? Dark : Light); // - // }); - // connect(this, &MainWindow::themeChanged, darkAction, - // [this, darkAction]() { - // darkAction->setChecked(currentTheme == Dark); // - // }); - - auto noneAction = new QAction(tr("None"), menuBar); - noneAction->setData(QStringLiteral("none")); - noneAction->setCheckable(true); - noneAction->setChecked(true); - - auto dwmBlurAction = new QAction(tr("Enable DWM blur"), menuBar); - dwmBlurAction->setData(QStringLiteral("dwm-blur")); - dwmBlurAction->setCheckable(true); - - auto acrylicAction = - new QAction(tr("Enable acrylic material"), menuBar); - acrylicAction->setData(QStringLiteral("acrylic-material")); - acrylicAction->setCheckable(true); - - auto micaAction = new QAction(tr("Enable mica"), menuBar); - micaAction->setData(QStringLiteral("mica")); - micaAction->setCheckable(true); - - auto micaAltAction = new QAction(tr("Enable mica alt"), menuBar); - micaAltAction->setData(QStringLiteral("mica-alt")); - micaAltAction->setCheckable(true); - - auto winStyleGroup = new QActionGroup(menuBar); - winStyleGroup->addAction(noneAction); - winStyleGroup->addAction(dwmBlurAction); - winStyleGroup->addAction(acrylicAction); - winStyleGroup->addAction(micaAction); - winStyleGroup->addAction(micaAltAction); - // connect(winStyleGroup, &QActionGroup::triggered, this, - // [this, winStyleGroup](QAction *action) { - // // Unset all custom style attributes first, otherwise the - // // style will not display correctly - // for (const QAction *_act : winStyleGroup->actions()) { - // const QString data = _act->data().toString(); - // if (data.isEmpty() || data == QStringLiteral("none")) - // { - // continue; - // } - // windowAgent->setWindowAttribute(data, false); - // } - // const QString data = action->data().toString(); - // if (data == QStringLiteral("none")) { - // setProperty("custom-style", false); - // } else if (!data.isEmpty()) { - // windowAgent->setWindowAttribute(data, true); - // setProperty("custom-style", true); - // } - // style()->polish(this); - // }); - - // Real menu - auto settings = new QMenu(tr("Settings(&S)"), menuBar); - settings->addAction(darkAction); - - settings->addSeparator(); - settings->addAction(noneAction); - settings->addAction(dwmBlurAction); - settings->addAction(acrylicAction); - settings->addAction(micaAction); - settings->addAction(micaAltAction); - - menuBar->addMenu(file); - menuBar->addMenu(edit); - menuBar->addMenu(settings); - return menuBar; - }(); - menuBar->setObjectName(QStringLiteral("win-menu-bar")); - auto titleLabel = new QLabel(); titleLabel->setAlignment(Qt::AlignCenter); titleLabel->setObjectName(QStringLiteral("win-title-label")); #ifndef Q_OS_MAC auto iconButton = new QWK::WindowButton(); + iconButton->setEnabled(false); iconButton->setObjectName(QStringLiteral("icon-button")); iconButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); @@ -262,7 +152,7 @@ void MainWindow::installWindowAgent() { windowBar->setMaxButton(maxButton); windowBar->setCloseButton(closeButton); #endif - windowBar->setMenuBar(menuBar); + // windowBar->setMenuBar(menuBar); windowBar->setTitleLabel(titleLabel); windowBar->setHostWidget(this); @@ -274,7 +164,6 @@ void MainWindow::installWindowAgent() { windowAgent->setSystemButton(QWK::WindowAgentBase::Maximize, maxButton); windowAgent->setSystemButton(QWK::WindowAgentBase::Close, closeButton); #endif - windowAgent->setHitTestVisible(menuBar, true); #ifdef Q_OS_MAC windowAgent->setSystemButtonAreaCallback([](const QSize &size) { @@ -316,4 +205,15 @@ void MainWindow::installWindowAgent() { connect(windowBar, &QWK::WindowBar::closeRequested, this, &QWidget::close); #endif } -*/ + +void MainWindow::loadStyleSheet(Theme theme) { + if (!styleSheet().isEmpty() && theme == currentTheme) return; + currentTheme = theme; + + if (QFile qss(theme == Dark ? QStringLiteral(":/Qss/dark-style.qss") + : QStringLiteral(":/Qss/light-style.qss")); + qss.open(QIODevice::ReadOnly | QIODevice::Text)) { + setStyleSheet(QString::fromUtf8(qss.readAll())); + Q_EMIT themeChanged(); + } +} diff --git a/src/mainwindow.h b/src/mainwindow.h index c26930d..a84eb6e 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -3,11 +3,18 @@ #include #include +#include #include "NotifyManager.h" #include "commandwidget.h" #include "global.h" +#ifdef Q_OS_WIN +#include "windows.h" +#include "windowsx.h" +#pragma comment(lib, "user32.lib") +#endif + namespace QWK { class WidgetWindowAgent; class StyleAgent; @@ -26,6 +33,17 @@ public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); + enum Theme { + Dark, + Light, + }; + Q_ENUM(Theme) +protected: + bool nativeEvent(const QByteArray &eventType, void *message, + qintptr *result); +signals: + void themeChanged(); + private: void initSignalConnection(); void initNotifyMessageConnection(); @@ -33,11 +51,13 @@ private: void showMessageSlots(QString message, int type); - // void installWindowAgent(); + void installWindowAgent(); + void loadStyleSheet(Theme theme); private: Ui::MainWindow *ui; - // QWK::WidgetWindowAgent *windowAgent; + QWK::WidgetWindowAgent *windowAgent; + Theme currentTheme{}; NotifyManager *m_notifyManager = nullptr; }; #endif // MAINWINDOW_H diff --git a/src/resources/Qss/dark-style.qss b/src/resources/Qss/dark-style.qss new file mode 100644 index 0000000..09f32ab --- /dev/null +++ b/src/resources/Qss/dark-style.qss @@ -0,0 +1,160 @@ +/* Window bar */ + +QWK--WindowBar[bar-active=true] { + /*background-color: #3C3C3C;*/ + background-color: transparent; +} + +QWK--WindowBar[bar-active=false] { + /*background-color: #505050;*/ + background-color: transparent; +} + + +/* Title label */ + +QWK--WindowBar>QLabel#win-title-label { + padding: 0; + border: none; + color: #ECECEC; + background-color: transparent; + min-height: 28px; +} + + +/* System buttons */ + +QWK--WindowBar>QAbstractButton[system-button=true] { + qproperty-iconSize: 12px 12px; + min-width: 50px; + border: none; + padding: 0; + background-color: transparent; +} + +QWK--WindowBar>QAbstractButton#min-button { + qproperty-iconNormal: url(":/window-bar/minimize.svg"); + qproperty-iconSize: 12px 12px; +} + +QWK--WindowBar>QAbstractButton#min-button:hover, +QWK--WindowBar>QAbstractButton#min-button:pressed { + background-color: rgba(255, 255, 255, 15%); +} + +QWK--WindowBar>QAbstractButton#max-button { + qproperty-iconNormal: url(":/window-bar/maximize.svg"); + qproperty-iconChecked: url(":/window-bar/restore.svg"); +} + +QWK--WindowBar>QAbstractButton#max-button:hover, +QWK--WindowBar>QAbstractButton#max-button:pressed { + background-color: rgba(255, 255, 255, 15%); +} + +QWK--WindowBar>QAbstractButton#close-button { + qproperty-iconNormal: url(":/window-bar/close.svg"); +} + +QWK--WindowBar>QAbstractButton#close-button:hover, +QWK--WindowBar>QAbstractButton#close-button:pressed { + background-color: #e81123; +} + + +/* Icon button */ + +QWK--WindowBar>QAbstractButton#icon-button { + qproperty-iconNormal: url(":/app/example.png"); + qproperty-iconSize: 18px 18px; + min-width: 40px; + border: none; + padding: 0; + background-color: transparent; +} + + +/* Menu Bar */ + +QMenuBar { + background-color: transparent; + border: none; +} + +QMenuBar>QToolButton#qt_menubar_ext_button { + qproperty-icon: url(":/window-bar/more-line.svg"); +} + +QMenuBar>QToolButton#qt_menubar_ext_button:hover, +QMenuBar>QToolButton#qt_menubar_ext_button:pressed { + background-color: rgba(255, 255, 255, 10%); +} + +QMenuBar::item { + color: #CCCCCC; + border: none; + padding: 8px 12px; +} + +QMenuBar::item:selected { + background-color: rgba(255, 255, 255, 10%); +} + + +/* Menu */ + +QMenu { + padding: 4px; + background: #303030; + border: 1px solid transparent; +} + +QMenu::indicator { + left: 6px; + width: 20px; + height: 20px; +} + +QMenu::icon { + left: 6px; +} + +QMenu::item { + background: transparent; + color: #CCCCCC; + padding: 6px 24px; +} + +QMenu::item:selected { + color: white; + background-color: #0060C0; +} + +QMenu::item:disabled { + color: #666666; + background-color: transparent; +} + +QMenu::separator { + height: 2px; + background-color: #5B5B5B; + margin: 6px 0; +} + + +/* Window */ + +MainWindow { + background-color: #1E1E1E; +} + +MainWindow[custom-style=true] { + background-color: transparent; +} + +QWidget#clock-widget { + font-size: 75px; + color: #FEFEFE; + font-weight: bold; + background-color: transparent; +} \ No newline at end of file diff --git a/src/resources/Qss/light-style.qss b/src/resources/Qss/light-style.qss new file mode 100644 index 0000000..9180001 --- /dev/null +++ b/src/resources/Qss/light-style.qss @@ -0,0 +1,158 @@ +/* Window bar */ + +QWK--WindowBar[bar-active=true] { + /*background-color: #195ABE;*/ + background-color: transparent; +} + +QWK--WindowBar[bar-active=false] { + /*background-color: #195ABE;*/ + background-color: transparent; +} + + +/* Title label */ + +QWK--WindowBar>QLabel#win-title-label { + padding: 0; + border: none; + color: #ECECEC; + background-color: transparent; + min-height: 28px; +} + + +/* System buttons */ + +QWK--WindowBar>QAbstractButton[system-button=true] { + qproperty-iconSize: 12px 12px; + min-width: 50px; + border: none; + padding: 0; + background-color: transparent; +} + +QWK--WindowBar>QAbstractButton#min-button { + qproperty-iconNormal: url(":/window-bar/minimize.svg"); + qproperty-iconSize: 12px 12px; +} + +QWK--WindowBar>QAbstractButton#min-button:hover, +QWK--WindowBar>QAbstractButton#min-button:pressed { + background-color: rgba(0, 0, 0, 15%); +} + +QWK--WindowBar>QAbstractButton#max-button { + qproperty-iconNormal: url(":/window-bar/maximize.svg"); + qproperty-iconChecked: url(":/window-bar/restore.svg"); +} + +QWK--WindowBar>QAbstractButton#max-button:hover, +QWK--WindowBar>QAbstractButton#max-button:pressed { + background-color: rgba(0, 0, 0, 15%); +} + +QWK--WindowBar>QAbstractButton#close-button { + qproperty-iconNormal: url(":/window-bar/close.svg"); +} + +QWK--WindowBar>QAbstractButton#close-button:hover, +QWK--WindowBar>QAbstractButton#close-button:pressed { + background-color: #e81123; +} + + +/* Icon button */ + +QWK--WindowBar>QAbstractButton#icon-button { + qproperty-iconNormal: url(":/app/example.png"); + qproperty-iconSize: 18px 18px; + min-width: 40px; + border: none; + padding: 0; + background-color: transparent; +} + + +/* Menu Bar */ + +QMenuBar { + background-color: transparent; + border: none; +} + +QMenuBar>QToolButton#qt_menubar_ext_button { + qproperty-icon: url(":/window-bar/more-line.svg"); +} + +QMenuBar>QToolButton#qt_menubar_ext_button:hover, +QMenuBar>QToolButton#qt_menubar_ext_button:pressed { + background-color: rgba(255, 255, 255, 10%); +} + +QMenuBar::item { + color: #EEEEEE; + border: none; + padding: 8px 12px; +} + +QMenuBar::item:selected { + background-color: rgba(255, 255, 255, 10%); +} + + +/* Menu */ + +QMenu { + padding: 4px; + background: white; + border: 1px solid #E0E0E0; +} + +QMenu::indicator { + left: 6px; + width: 20px; + height: 20px; +} + +QMenu::icon { + left: 6px; +} + +QMenu::item { + background: transparent; + color: #333333; + padding: 6px 24px; +} + +QMenu::item:selected { + background-color: rgba(0, 0, 0, 10%); +} + +QMenu::item:disabled { + color: #CCCCCC; +} + +QMenu::separator { + height: 2px; + background-color: #CCCCCC; + margin: 6px 0; +} + + +/* Window */ + +MainWindow { + background-color: #F3F3F3; +} + +MainWindow[custom-style=true] { + background-color: transparent; +} + +QWidget#clock-widget { + font-size: 75px; + color: #333333; + font-weight: bold; + background-color: transparent; +} \ No newline at end of file diff --git a/src/shared/resources/app/example.icns b/src/resources/app/example.icns similarity index 100% rename from src/shared/resources/app/example.icns rename to src/resources/app/example.icns diff --git a/src/shared/resources/app/example.ico b/src/resources/app/example.ico similarity index 100% rename from src/shared/resources/app/example.ico rename to src/resources/app/example.ico diff --git a/src/shared/resources/app/example.png b/src/resources/app/example.png similarity index 100% rename from src/shared/resources/app/example.png rename to src/resources/app/example.png diff --git a/src/resources/res.qrc b/src/resources/res.qrc index a8dec87..0130e41 100644 --- a/src/resources/res.qrc +++ b/src/resources/res.qrc @@ -9,5 +9,16 @@ images/settings.png images/up.png images/wifi4G.png + Qss/dark-style.qss + Qss/light-style.qss + app/example.icns + app/example.ico + app/example.png + window-bar/close.svg + window-bar/fullscreen.svg + window-bar/maximize.svg + window-bar/minimize.svg + window-bar/more-line.svg + window-bar/restore.svg diff --git a/src/shared/resources/window-bar/close.svg b/src/resources/window-bar/close.svg similarity index 100% rename from src/shared/resources/window-bar/close.svg rename to src/resources/window-bar/close.svg diff --git a/src/shared/resources/window-bar/fullscreen.svg b/src/resources/window-bar/fullscreen.svg similarity index 100% rename from src/shared/resources/window-bar/fullscreen.svg rename to src/resources/window-bar/fullscreen.svg diff --git a/src/shared/resources/window-bar/maximize.svg b/src/resources/window-bar/maximize.svg similarity index 100% rename from src/shared/resources/window-bar/maximize.svg rename to src/resources/window-bar/maximize.svg diff --git a/src/shared/resources/window-bar/minimize.svg b/src/resources/window-bar/minimize.svg similarity index 100% rename from src/shared/resources/window-bar/minimize.svg rename to src/resources/window-bar/minimize.svg diff --git a/src/shared/resources/window-bar/more-line.svg b/src/resources/window-bar/more-line.svg similarity index 100% rename from src/shared/resources/window-bar/more-line.svg rename to src/resources/window-bar/more-line.svg diff --git a/src/shared/resources/window-bar/restore.svg b/src/resources/window-bar/restore.svg similarity index 100% rename from src/shared/resources/window-bar/restore.svg rename to src/resources/window-bar/restore.svg diff --git a/src/shared/resources/shared.qrc b/src/shared/resources/shared.qrc deleted file mode 100644 index 5aec715..0000000 --- a/src/shared/resources/shared.qrc +++ /dev/null @@ -1,11 +0,0 @@ - - - window-bar/close.svg - window-bar/fullscreen.svg - window-bar/maximize.svg - window-bar/minimize.svg - window-bar/restore.svg - window-bar/more-line.svg - app/example.png - - diff --git a/src/shared/widgetframe/CMakeLists.txt b/src/shared/widgetframe/CMakeLists.txt index bf43085..4c0ca6b 100644 --- a/src/shared/widgetframe/CMakeLists.txt +++ b/src/shared/widgetframe/CMakeLists.txt @@ -19,10 +19,10 @@ PUBLIC windowbutton_p.h ) -qt6_add_resources(${PROJECT_NAME} "resources" - PREFIX "/" - FILES "../resources/shared.qrc" -) +# qt6_add_resources(${PROJECT_NAME} "resources" +# PREFIX "/" +# FILES "resources/shared.qrc" +# ) target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Core