Compare commits
8 Commits
530b18113c
...
cd83387b67
Author | SHA1 | Date |
---|---|---|
|
cd83387b67 | 3 weeks ago |
|
25dcb7e5c5 | 3 weeks ago |
|
ef0c77fd96 | 3 weeks ago |
|
ad1fca5565 | 3 weeks ago |
|
1d4c950828 | 3 weeks ago |
|
01c4ad2a20 | 3 weeks ago |
|
8e2d490d30 | 3 weeks ago |
|
1687e9950f | 4 weeks ago |
@ -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 <array>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <QtCore/QSet>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtGui/QRegion>
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
#include <QWKCore/windowagentbase.h>
|
||||
#include <QWKCore/private/nativeeventfilter_p.h>
|
||||
#include <QWKCore/private/sharedeventfilter_p.h>
|
||||
#include <QWKCore/private/windowitemdelegate_p.h>
|
||||
#include <QWKCore/private/winidchangeeventfilter_p.h>
|
||||
|
||||
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<WindowItemDelegate> m_delegate;
|
||||
QPointer<QWindow> m_windowHandle;
|
||||
WId m_windowId{};
|
||||
|
||||
QSet<const QObject *> m_hitTestVisibleItems;
|
||||
#ifdef Q_OS_MAC
|
||||
ScreenRectCallback m_systemButtonAreaCallback;
|
||||
#endif
|
||||
|
||||
QObject *m_titleBar{};
|
||||
std::array<QObject *, WindowAgentBase::Close + 1> m_systemButtons{};
|
||||
|
||||
std::list<std::pair<QString, QVariant>> m_windowAttributesOrder;
|
||||
QHash<QString, decltype(m_windowAttributesOrder)::iterator> m_windowAttributes;
|
||||
|
||||
std::unique_ptr<WinIdChangeEventFilter> 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
|
@ -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 <objc/runtime.h>
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
#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<WId, NSWindowProxy *>;
|
||||
Q_GLOBAL_STATIC(ProxyList, g_proxyList);
|
||||
|
||||
using ProxyList2 = QHash<NSWindow *, NSWindowProxy *>;
|
||||
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<NSWindow *>(notification.object);
|
||||
if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) {
|
||||
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
|
||||
QWK_NSWindowDelegate::WillEnterFullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidEnterFullScreen:(NSNotification *)notification {
|
||||
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
|
||||
if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) {
|
||||
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
|
||||
QWK_NSWindowDelegate::DidEnterFullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowWillExitFullScreen:(NSNotification *)notification {
|
||||
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
|
||||
if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) {
|
||||
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
|
||||
QWK_NSWindowDelegate::WillExitFullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidExitFullScreen:(NSNotification *)notification {
|
||||
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
|
||||
if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) {
|
||||
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
|
||||
QWK_NSWindowDelegate::DidExitFullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidResize:(NSNotification *)notification {
|
||||
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
|
||||
if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) {
|
||||
reinterpret_cast<QWK_NSWindowDelegate *>(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<NSButton *, 3> 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<NSVisualEffectView *>(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<setStyleMaskPtr>(
|
||||
method_setImplementation(method, reinterpret_cast<IMP>(setStyleMask)));
|
||||
|
||||
method =
|
||||
class_getInstanceMethod(windowClass, @selector(setTitlebarAppearsTransparent:));
|
||||
oldSetTitlebarAppearsTransparent =
|
||||
reinterpret_cast<setTitlebarAppearsTransparentPtr>(method_setImplementation(
|
||||
method, reinterpret_cast<IMP>(setTitlebarAppearsTransparent)));
|
||||
|
||||
#if 0
|
||||
method = class_getInstanceMethod(windowClass, @selector(canBecomeKeyWindow));
|
||||
oldCanBecomeKeyWindow = reinterpret_cast<canBecomeKeyWindowPtr>(method_setImplementation(method, reinterpret_cast<IMP>(canBecomeKeyWindow)));
|
||||
|
||||
method = class_getInstanceMethod(windowClass, @selector(canBecomeMainWindow));
|
||||
oldCanBecomeMainWindow = reinterpret_cast<canBecomeMainWindowPtr>(method_setImplementation(method, reinterpret_cast<IMP>(canBecomeMainWindow)));
|
||||
#endif
|
||||
|
||||
method = class_getInstanceMethod(windowClass, @selector(sendEvent:));
|
||||
oldSendEvent = reinterpret_cast<sendEventPtr>(
|
||||
method_setImplementation(method, reinterpret_cast<IMP>(sendEvent)));
|
||||
|
||||
// Alloc
|
||||
windowObserver = [[QWK_NSWindowObserver alloc] init];
|
||||
}
|
||||
|
||||
static void restoreImplementations() {
|
||||
Method method = class_getInstanceMethod(windowClass, @selector(setStyleMask:));
|
||||
method_setImplementation(method, reinterpret_cast<IMP>(oldSetStyleMask));
|
||||
oldSetStyleMask = nil;
|
||||
|
||||
method =
|
||||
class_getInstanceMethod(windowClass, @selector(setTitlebarAppearsTransparent:));
|
||||
method_setImplementation(method,
|
||||
reinterpret_cast<IMP>(oldSetTitlebarAppearsTransparent));
|
||||
oldSetTitlebarAppearsTransparent = nil;
|
||||
|
||||
#if 0
|
||||
method = class_getInstanceMethod(windowClass, @selector(canBecomeKeyWindow));
|
||||
method_setImplementation(method, reinterpret_cast<IMP>(oldCanBecomeKeyWindow));
|
||||
oldCanBecomeKeyWindow = nil;
|
||||
|
||||
method = class_getInstanceMethod(windowClass, @selector(canBecomeMainWindow));
|
||||
method_setImplementation(method, reinterpret_cast<IMP>(oldCanBecomeMainWindow));
|
||||
oldCanBecomeMainWindow = nil;
|
||||
#endif
|
||||
|
||||
method = class_getInstanceMethod(windowClass, @selector(sendEvent:));
|
||||
method_setImplementation(method, reinterpret_cast<IMP>(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<NSWindow *>(obj))) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (oldCanBecomeKeyWindow) {
|
||||
return oldCanBecomeKeyWindow(obj, sel);
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
static BOOL canBecomeMainWindow(id obj, SEL sel) {
|
||||
if (g_proxyIndexes->contains(reinterpret_cast<NSWindow *>(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<NSWindow *>(obj))) {
|
||||
styleMask |= NSWindowStyleMaskFullSizeContentView;
|
||||
}
|
||||
|
||||
if (oldSetStyleMask) {
|
||||
oldSetStyleMask(obj, sel, styleMask);
|
||||
}
|
||||
}
|
||||
|
||||
static void setTitlebarAppearsTransparent(id obj, SEL sel, BOOL transparent) {
|
||||
if (g_proxyIndexes->contains(reinterpret_cast<NSWindow *>(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<NSWindow *>(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<NSView *>(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<const QMouseEvent *>(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<CocoaWindowEventFilter>(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;
|
||||
}
|
||||
|
||||
}
|
@ -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 <QWKCore/private/abstractwindowcontext_p.h>
|
||||
|
||||
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<SharedEventFilter> cocoaWindowEventFilter;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // COCOAWINDOWCONTEXT_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 <QWKCore/private/abstractwindowcontext_p.h>
|
||||
|
||||
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<SharedEventFilter> qtWindowEventFilter;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // QTWINDOWCONTEXT_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 <QWKCore/qwindowkit_windows.h>
|
||||
#include <QWKCore/private/abstractwindowcontext_p.h>
|
||||
|
||||
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
|
@ -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 <QWKCore/qwkglobal.h>
|
||||
|
||||
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<NativeEventFilter *> 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
|
@ -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 <QWKCore/qwkglobal.h>
|
||||
|
||||
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<SharedEventFilter *> 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
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/contexts/abstractwindowcontext_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/contexts/cocoawindowcontext_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/kernel/nativeeventfilter_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/contexts/qtwindowcontext_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/qwkglobal_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/shared/qwkwindowsextra_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/kernel/sharedeventfilter_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/style/styleagent_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/shared/systemwindow_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/contexts/win32windowcontext_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/windowagentbase_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/windowitemdelegate_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/shared/windows10borderhandler_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/core/kernel/winidchangeeventfilter_p.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
|
@ -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 <QtCore/qt_windows.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
# include <QtCore/private/qwinregistry_p.h>
|
||||
#endif
|
||||
|
||||
#include <QWKCore/qwkglobal.h>
|
||||
|
||||
#ifndef GET_X_LPARAM
|
||||
# define GET_X_LPARAM(lp) (static_cast<int>(static_cast<short>(LOWORD(lp))))
|
||||
#endif
|
||||
|
||||
#ifndef GET_Y_LPARAM
|
||||
# define GET_Y_LPARAM(lp) (static_cast<int>(static_cast<short>(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<DWORD, bool> 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<DWORD, bool> dwordValue(QStringView subKey) const;
|
||||
};
|
||||
|
||||
inline std::pair<DWORD, bool> WindowsRegistryKey::dwordValue(QStringView subKey) const {
|
||||
const auto val = value<DWORD>(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<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
Q_DECLARE_METATYPE(QMargins)
|
||||
#endif
|
||||
|
||||
#endif // QWINDOWKIT_WINDOWS_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 <functional>
|
||||
|
||||
#include <QtCore/QEvent>
|
||||
#include <QtGui/QtEvents>
|
||||
|
||||
#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<QRect(const QSize &)>;
|
||||
|
||||
}
|
||||
|
||||
#endif // QWKGLOBAL_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 <QtCore/QObject>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtGui/QMouseEvent>
|
||||
|
||||
#include <QWKCore/qwkglobal.h>
|
||||
|
||||
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
|
@ -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 <shellscalingapi.h>
|
||||
#include <dwmapi.h>
|
||||
#include <timeapi.h>
|
||||
|
||||
#include <QWKCore/qwindowkit_windows.h>
|
||||
|
||||
#include <QtCore/QtMath>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QStyleHints>
|
||||
#include <QtGui/QPalette>
|
||||
|
||||
#include <QtCore/private/qsystemlibrary_p.h>
|
||||
|
||||
// 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<decltype(p##NAME)>(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<decltype(p##NAME)>(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<WId>(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
|
@ -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 <QtGui/QWindow>
|
||||
#include <QtGui/QMouseEvent>
|
||||
|
||||
#include <QWKCore/private/qwkglobal_p.h>
|
||||
|
||||
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<QMouseEvent *>(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<QMouseEvent *>(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
|
@ -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 <QtGui/QWindow>
|
||||
#include <QtGui/QMouseEvent>
|
||||
|
||||
#include <QWKCore/qwindowkit_windows.h>
|
||||
#include <QWKCore/private/qwkglobal_p.h>
|
||||
#include <QWKCore/private/abstractwindowcontext_p.h>
|
||||
|
||||
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<const MSG *>(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
|
@ -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 <memory>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
#include <QWKCore/qwkglobal.h>
|
||||
|
||||
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<StyleAgentPrivate> d_ptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // STYLEAGENT_H
|
@ -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 <Cocoa/Cocoa.h>
|
||||
|
||||
#include <QtCore/QVariant>
|
||||
|
||||
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<StyleAgentPrivate *>;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 <QtCore/QHash>
|
||||
|
||||
#include <QWKCore/styleagent.h>
|
||||
|
||||
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
|
@ -0,0 +1 @@
|
||||
#include "../../../src/core/style/styleagent.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 <memory>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include <QWKCore/qwkglobal.h>
|
||||
|
||||
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<WindowAgentBasePrivate> d_ptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WINDOWAGENTBASE_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 <QWKCore/windowagentbase.h>
|
||||
#include <QWKCore/private/abstractwindowcontext_p.h>
|
||||
|
||||
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<AbstractWindowContext> context;
|
||||
|
||||
public:
|
||||
using WindowContextFactoryMethod = AbstractWindowContext *(*) ();
|
||||
|
||||
static WindowContextFactoryMethod windowContextFactoryMethod;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(WindowAgentBasePrivate)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WINDOWAGENTBASEPRIVATE_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 <QtCore/QObject>
|
||||
#include <QtCore/QPoint>
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
#include <QWKCore/private/winidchangeeventfilter_p.h>
|
||||
#include <QWKCore/qwkglobal.h>
|
||||
|
||||
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
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/widgets/widgetitemdelegate_p.h"
|
@ -0,0 +1 @@
|
||||
#include "../../../../src/widgets/widgetwindowagent_p.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 <QtCore/QtGlobal>
|
||||
|
||||
#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
|
@ -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 <QtCore/QObject>
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
#include <QWKCore/private/windowitemdelegate_p.h>
|
||||
#include <QWKWidgets/qwkwidgetsglobal.h>
|
||||
|
||||
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
|
@ -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 <QtWidgets/QWidget>
|
||||
|
||||
#include <QWKCore/windowagentbase.h>
|
||||
#include <QWKWidgets/qwkwidgetsglobal.h>
|
||||
|
||||
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
|
@ -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 <QWKCore/qwkconfig.h>
|
||||
#include <QWKCore/private/windowagentbase_p.h>
|
||||
#include <QWKWidgets/widgetwindowagent.h>
|
||||
|
||||
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<QObject> systemButtonAreaWidgetEventFilter;
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS)
|
||||
void setupWindows10BorderWorkaround();
|
||||
std::unique_ptr<QObject> borderHandler;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WIDGETWINDOWAGENTPRIVATE_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 <QFrame>
|
||||
#include <QAbstractButton>
|
||||
#include <QMenuBar>
|
||||
#include <QLabel>
|
||||
|
||||
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<WindowBarPrivate> d_ptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WINDOWBAR_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 <QBoxLayout>
|
||||
|
||||
#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
|
@ -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 <QtWidgets/QPushButton>
|
||||
|
||||
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<WindowButtonPrivate> d_ptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WINDOWBUTTON_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
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 809 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 511 B |
After Width: | Height: | Size: 820 B |
Before Width: | Height: | Size: 444 B After Width: | Height: | Size: 444 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 328 B After Width: | Height: | Size: 328 B |
Before Width: | Height: | Size: 467 B After Width: | Height: | Size: 467 B |
Before Width: | Height: | Size: 419 B After Width: | Height: | Size: 419 B |
Before Width: | Height: | Size: 559 B After Width: | Height: | Size: 559 B |
@ -1,11 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>window-bar/close.svg</file>
|
||||
<file>window-bar/fullscreen.svg</file>
|
||||
<file>window-bar/maximize.svg</file>
|
||||
<file>window-bar/minimize.svg</file>
|
||||
<file>window-bar/restore.svg</file>
|
||||
<file>window-bar/more-line.svg</file>
|
||||
<file>app/example.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -0,0 +1,107 @@
|
||||
#include "streamaddrsettingsdlg.h"
|
||||
|
||||
#include "ui_streamaddrsettingsdlg.h"
|
||||
|
||||
StreamAddrSettingsDlg::StreamAddrSettingsDlg(QWidget *parent)
|
||||
: QDialog(parent), ui(new Ui::StreamAddrSettingsDlg) {
|
||||
ui->setupUi(this);
|
||||
this->setWindowTitle(QStringLiteral("视频流设置"));
|
||||
|
||||
// 飞机类型
|
||||
QStringList uavTypeList;
|
||||
uavTypeList.append("FP98");
|
||||
uavTypeList.append("FP985");
|
||||
uavTypeList.append("FP981C");
|
||||
uavTypeList.append("FP981CS");
|
||||
uavTypeList.append("FP981A");
|
||||
ui->uavTypeCombox->insertItems(0, uavTypeList);
|
||||
//
|
||||
ui->pushStreamIPCombox->insertItem(0, QStringLiteral("航天飞鹏服务器"));
|
||||
ui->pushStreamIPCombox->insertItem(1,
|
||||
QStringLiteral("航天飞鹏阿里云服务器"));
|
||||
ui->pushStreamIPCombox->insertItem(2, QStringLiteral("自定义"));
|
||||
}
|
||||
|
||||
StreamAddrSettingsDlg::~StreamAddrSettingsDlg() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void StreamAddrSettingsDlg::showEvent(QShowEvent *event) {
|
||||
initSavedParms();
|
||||
}
|
||||
|
||||
void StreamAddrSettingsDlg::on_pushStreamIPCombox_currentIndexChanged(
|
||||
int index) {
|
||||
if (2 == index) {
|
||||
ui->pushStreamIPCombox->setEditable(true);
|
||||
} else {
|
||||
ui->pushStreamIPCombox->setEditable(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存设置
|
||||
void StreamAddrSettingsDlg::on_saveBtn_clicked() {
|
||||
if (!g_networkSettingInfo) {
|
||||
this->close();
|
||||
return;
|
||||
}
|
||||
|
||||
g_networkSettingInfo->setValue("NetworkStreamSettings/uavType",
|
||||
ui->uavTypeCombox->currentIndex());
|
||||
g_networkSettingInfo->setValue("NetworkStreamSettings/uavID",
|
||||
ui->uavIDspinBox->value());
|
||||
g_networkSettingInfo->setValue("NetworkStreamSettings/podPullAddress",
|
||||
ui->podPullStreamIPEdit->text());
|
||||
g_networkSettingInfo->setValue("NetworkStreamSettings/pullAddress1",
|
||||
ui->pullStreamAddrEdit1->text());
|
||||
g_networkSettingInfo->setValue("NetworkStreamSettings/pullAddress2",
|
||||
ui->pullStreamAddrEdit2->text());
|
||||
g_networkSettingInfo->setValue("NetworkStreamSettings/pullAddress3",
|
||||
ui->pullStreamAddrEdit3->text());
|
||||
g_networkSettingInfo->setValue("NetworkStreamSettings/pullAddress4",
|
||||
ui->pullStreamAddrEdit4->text());
|
||||
int pushStreamIndex = ui->pushStreamIPCombox->currentIndex();
|
||||
g_networkSettingInfo->setValue("NetworkStreamSettings/pushStreamType",
|
||||
pushStreamIndex);
|
||||
if (2 == pushStreamIndex) {
|
||||
g_networkSettingInfo->setValue(
|
||||
"NetworkStreamSettings/pushStreamAddress",
|
||||
ui->pushStreamIPCombox->currentText());
|
||||
}
|
||||
|
||||
this->close();
|
||||
}
|
||||
|
||||
void StreamAddrSettingsDlg::initSavedParms() {
|
||||
if (!g_networkSettingInfo) return;
|
||||
ui->uavTypeCombox->setCurrentIndex(
|
||||
g_networkSettingInfo->value("NetworkStreamSettings/uavType").toInt());
|
||||
ui->uavIDspinBox->setValue(
|
||||
g_networkSettingInfo->value("NetworkStreamSettings/uavID").toInt());
|
||||
ui->podPullStreamIPEdit->setText(
|
||||
g_networkSettingInfo->value("NetworkStreamSettings/podPullAddress")
|
||||
.toString());
|
||||
ui->pullStreamAddrEdit1->setText(
|
||||
g_networkSettingInfo->value("NetworkStreamSettings/pullAddress1")
|
||||
.toString());
|
||||
ui->pullStreamAddrEdit2->setText(
|
||||
g_networkSettingInfo->value("NetworkStreamSettings/pullAddress2")
|
||||
.toString());
|
||||
ui->pullStreamAddrEdit3->setText(
|
||||
g_networkSettingInfo->value("NetworkStreamSettings/pullAddress3")
|
||||
.toString());
|
||||
ui->pullStreamAddrEdit4->setText(
|
||||
g_networkSettingInfo->value("NetworkStreamSettings/pullAddress4")
|
||||
.toString());
|
||||
int pushStreamIndex =
|
||||
g_networkSettingInfo->value("NetworkStreamSettings/pushStreamType")
|
||||
.toInt();
|
||||
ui->pushStreamIPCombox->setCurrentIndex(pushStreamIndex);
|
||||
if (2 == pushStreamIndex) {
|
||||
ui->pushStreamIPCombox->setItemText(
|
||||
pushStreamIndex,
|
||||
g_networkSettingInfo
|
||||
->value("NetworkStreamSettings/pushStreamAddress")
|
||||
.toString());
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
#ifndef STREAMADDRSETTINGSDLG_H
|
||||
#define STREAMADDRSETTINGSDLG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "global.h"
|
||||
|
||||
namespace Ui {
|
||||
class StreamAddrSettingsDlg;
|
||||
}
|
||||
|
||||
class StreamAddrSettingsDlg : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit StreamAddrSettingsDlg(QWidget *parent = nullptr);
|
||||
~StreamAddrSettingsDlg();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event);
|
||||
|
||||
private slots:
|
||||
void on_pushStreamIPCombox_currentIndexChanged(int index);
|
||||
|
||||
void on_saveBtn_clicked();
|
||||
|
||||
private:
|
||||
void initSavedParms();
|
||||
|
||||
private:
|
||||
Ui::StreamAddrSettingsDlg *ui;
|
||||
};
|
||||
|
||||
#endif // STREAMADDRSETTINGSDLG_H
|
@ -1,37 +1,33 @@
|
||||
#include "videoControl.h"
|
||||
|
||||
#include "ui_videoControl.h"
|
||||
|
||||
VideoControl::VideoControl(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::VideoControl)
|
||||
{
|
||||
VideoControl::VideoControl(QWidget *parent)
|
||||
: QWidget(parent), ui(new Ui::VideoControl) {
|
||||
ui->setupUi(this);
|
||||
// 其他初始化代码...
|
||||
|
||||
|
||||
ui->pbPlayer->setIcon(QIcon(":/images/playMedio.png"));
|
||||
ui->pbPlayer->setIconSize(QSize(56,56));
|
||||
ui->pbPlayer->setFixedSize(64, 64);
|
||||
ui->pbPlayer->setIconSize(QSize(32, 32));
|
||||
ui->pbPlayer->setFixedSize(40, 40);
|
||||
ui->pbPlayer->setObjectName("medio");
|
||||
// ui->pbPlayer->set
|
||||
// ui->pbPlayer->set
|
||||
ui->pbStop->setIcon(QIcon(":/images/stop.png"));
|
||||
ui->pbStop->setIconSize(QSize(42,42));
|
||||
ui->pbStop->setFixedSize(50, 50);
|
||||
ui->pbStop->setIconSize(QSize(24, 24));
|
||||
ui->pbStop->setFixedSize(32, 32);
|
||||
ui->pbStop->setObjectName("medio");
|
||||
|
||||
ui->pbFast->setIcon(QIcon(":/images/playFast.png"));
|
||||
ui->pbFast->setIconSize(QSize(42,42));
|
||||
ui->pbFast->setFixedSize(50, 50);
|
||||
ui->pbFast->setIconSize(QSize(24, 24));
|
||||
ui->pbFast->setFixedSize(32, 32);
|
||||
ui->pbFast->setObjectName("medio");
|
||||
|
||||
|
||||
ui->pbSlow->setIcon(QIcon(":/images/playSlow.png"));
|
||||
ui->pbSlow->setIconSize(QSize(42,42));
|
||||
ui->pbSlow->setFixedSize(50, 50);
|
||||
ui->pbSlow->setIconSize(QSize(24, 24));
|
||||
ui->pbSlow->setFixedSize(32, 32);
|
||||
ui->pbSlow->setObjectName("medio");
|
||||
}
|
||||
|
||||
VideoControl::~VideoControl()
|
||||
{
|
||||
VideoControl::~VideoControl() {
|
||||
delete ui;
|
||||
}
|
||||
|