feat: 重绘窗口条
@ -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 |
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>
|
|