Compare commits

..

8 Commits

2
.gitignore vendored

@ -70,7 +70,7 @@ CMakeLists.txt.user*
# Binaries
# --------
*.dll
#*.dll
*.exe
# Directories with generated files

@ -5,17 +5,27 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
PRIVATE
Qt::Core
Qt::Widgets
${CMAKE_CURRENT_LIST_DIR}/QNotify/lib/QNotifyd.lib)
${CMAKE_CURRENT_LIST_DIR}/QNotify/lib/QNotifyd.lib
${CMAKE_CURRENT_LIST_DIR}/qwindowkit/lib/QWKCored.lib
${CMAKE_CURRENT_LIST_DIR}/qwindowkit/lib/QWKWidgetsd.lib
${CMAKE_CURRENT_LIST_DIR}/qwindowkit/lib/WidgetFramed.lib
)
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
# Release
target_link_libraries(VideoClient
PRIVATE
Qt::Core
Qt::Widgets
${CMAKE_CURRENT_LIST_DIR}/QNotify/lib/QNotify.lib)
${CMAKE_CURRENT_LIST_DIR}/QNotify/lib/QNotify.lib
${CMAKE_CURRENT_LIST_DIR}/qwindowkit/lib/QWKCore.lib
${CMAKE_CURRENT_LIST_DIR}/qwindowkit/lib/QWKWidgets.lib
${CMAKE_CURRENT_LIST_DIR}/qwindowkit/lib/WidgetFrame.lib
)
endif()
target_include_directories(VideoClient
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/QNotify/include
${CMAKE_CURRENT_LIST_DIR}/qwindowkit/include
)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -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,53 @@
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
// SPDX-License-Identifier: Apache-2.0
#ifndef WINIDCHANGEEVENTFILTER_P_H
#define WINIDCHANGEEVENTFILTER_P_H
//
// W A R N I N G !!!
// -----------------
//
// This file is not part of the QWindowKit API. It is used purely as an
// implementation detail. This header file may change from version to
// version without notice, or may even be removed.
//
#include <QtGui/QWindow>
#include <QWKCore/qwkglobal.h>
namespace QWK {
class AbstractWindowContext;
class WinIdChangeEventFilter : public QObject {
public:
WinIdChangeEventFilter(QObject *host, AbstractWindowContext *context)
: host(host), context(context) {
}
virtual WId winId() const = 0;
protected:
QObject *host;
AbstractWindowContext *context;
};
class QWK_CORE_EXPORT WindowWinIdChangeEventFilter : public WinIdChangeEventFilter {
public:
WindowWinIdChangeEventFilter(QWindow *host, AbstractWindowContext *context);
WId winId() const override;
protected:
QWindow *win;
bool isAboutToBeDestroyed;
bool eventFilter(QObject *obj, QEvent *event) override;
};
}
#endif // WINIDCHANGEEVENTFILTER_P_H

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -9,9 +9,9 @@ qt_add_executable(VideoClient
global.h global.cpp
communicationsettingdlg.h communicationsettingdlg.cpp communicationsettingdlg.ui
commandwidget.h commandwidget.cpp commandwidget.ui
streamaddrsettingsdlg.h streamaddrsettingsdlg.cpp streamaddrsettingsdlg.ui
videoControl.h videoControl.ui videoControl.cpp
wprogressbar.h wprogressbar.cpp
)
qt6_add_resources(VideoClient "resources"

@ -5,14 +5,11 @@
CommandWidget::CommandWidget(QWidget *parent)
: QWidget(parent), ui(new Ui::CommandWidget) {
ui->setupUi(this);
ui->stopConnectionToolBtn->setDisabled(true);
ui->settingToolBtn->setIcon(QIcon(":/images/settings.png"));
ui->settingToolBtn->setText("通信设置");
ui->settingToolBtn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
udpSocket = new QUdpSocket(this);
connect(&settingDlg, &CommunicationSettingDlg::sendErrorMessage, this,
&CommandWidget::receiveMessageSlots);
ui->videoLayout1TBtn->setIcon(QIcon(":/images/videolayout1.png"));
ui->videoLayout4TBtn->setIcon(QIcon(":/images/videolayout4.png"));
}
CommandWidget::~CommandWidget() {
@ -20,16 +17,18 @@ CommandWidget::~CommandWidget() {
if (udpSocket) udpSocket->deleteLater();
}
void CommandWidget::on_settingToolBtn_clicked() {
settingDlg.exec();
}
// 连接
void CommandWidget::on_startConnectionToolBtn_clicked() {
ui->startConnectionToolBtn->setDisabled(true);
if (!g_networkSettingInfo) return;
if (!g_networkSettingInfo) {
qWarning() << "Network settings are not initialized!";
return;
}
int dataSourceType = g_networkSettingInfo->value("DataSource").toInt();
QString groupName = "";
switch (dataSourceType) {
case 0:
groupName = "LLink";
@ -41,37 +40,220 @@ void CommandWidget::on_startConnectionToolBtn_clicked() {
groupName = "SATCOM2";
break;
default:
break;
qWarning() << "Unknown data source type:" << dataSourceType;
return;
}
m_remoteIP =
g_networkSettingInfo->value(groupName + "/remoteIP").toString();
m_remotePort =
g_networkSettingInfo->value(groupName + "/remotePort").toInt();
int localPort =
g_networkSettingInfo->value(groupName + "/localPort").toInt();
QString localIP =
g_networkSettingInfo->value(groupName + "/localIP").toString();
m_remoteIP = g_networkSettingInfo->value(groupName + "/remoteIP").toString();
m_remotePort = g_networkSettingInfo->value(groupName + "/remotePort").toInt();
int localPort = g_networkSettingInfo->value(groupName + "/localPort").toInt();
QString localIP = g_networkSettingInfo->value(groupName + "/localIP").toString();
// 校验 IP 和端口
if (m_remoteIP.isEmpty() || m_remotePort <= 0 || localIP.isEmpty() || localPort <= 0) {
qWarning() << "Invalid IP or port configuration!";
return;
}
if (dataSourceType == 0) { // 组播
qDebug() << "Starting multicast connection to" << m_remoteIP << ":" << m_remotePort;
emit startConnectionSignal(m_remoteIP, m_remotePort);
} else { // 单播
qDebug() << "Starting unicast connection on" << localIP << ":" << localPort;
emit startConnectionSignal(localIP, localPort);
}
ui->stopConnectionToolBtn->setDisabled(false);
}
void CommandWidget::receiveMessageSlots(QString message, int type) {
emit sendErrorMessage(message, type);
}
// 断开
void CommandWidget::on_stopConnectionToolBtn_clicked() {
ui->stopConnectionToolBtn->setDisabled(true);
emit stopConnectionSignal();
// ui->stopConnectionToolBtn->setDisabled(false);
ui->startConnectionToolBtn->setDisabled(false);
unsigned char CommandWidget::getCrc(quint8* data, quint8 length)
{
quint16 crc = 0;
int i=0;
while (length-- > 0)
{
crc = CRC8TAB[crc ^ (data[i]&0x00FF)];
i++;
}
void CommandWidget::receiveMessageSlots(QString message, int type) {
emit sendErrorMessage(message, type);
return crc&0x00ff;
}
ProtocalKB CommandWidget::EncodeCommandCmd(quint8 cmd)
{
std::unique_ptr<ProtocalKB> pBuff = std::make_unique<ProtocalKB>();
pBuff->head[0] = PROTOCOL_HEAD_0;
pBuff->head[1] = PROTOCOL_HEAD_1;
pBuff->reserved = PROTOCOL_RESERVED;
pBuff->data[0] = cmd;
pBuff->data[1] = cmd;
pBuff->data[2] = cmd;
pBuff->curPage = PROTOCOL_CUR_PAGE;
quint8 *ptr = reinterpret_cast<quint8*>(&pBuff);
pBuff->CHKSUM = getCrc(ptr, sizeof(pBuff) - sizeof(pBuff->CHKSUM));
return *pBuff;
}
bool CommandWidget::writeBufferToClient(const QByteArray &data)
{
// 目标地址和端口
QHostAddress destination("172.10.1.216"); // 目标 IP 地址
quint16 port = 9003; // 目标端口
// 发送数据
qint64 bytesSent = g_CommandudpSocket->writeDatagram(data, destination, port);
if (bytesSent == -1) {
qDebug() << "发送失败:" << g_CommandudpSocket->errorString();
return false;
} else {
qDebug() << "发送成功,字节数:" << bytesSent;
return true;
}
}
void CommandWidget::buttonResponse(quint8 cmd)
{
ProtocalKB packet = EncodeCommandCmd(cmd);
// 将 ProtocalKB 结构体转换为 QByteArray
QByteArray data;
data.append(reinterpret_cast<const char*>(&packet), sizeof(ProtocalKB));
for(int i=0;i<3;i++)
{
writeBufferToClient(data);
}
}
void CommandWidget::on_pushButton_clicked()
{
buttonResponse(0x75);
}
void CommandWidget::on_pushButton_2_clicked()
{
buttonResponse(0x76);
}
void CommandWidget::on_pushButton_5_clicked()
{
buttonResponse(0x7D);
}
void CommandWidget::on_pushButton_6_clicked()
{
buttonResponse(0x7C);
}
void CommandWidget::on_pushButton_10_clicked()
{
buttonResponse(0x69);
}
void CommandWidget::on_pushButton_9_clicked()
{
buttonResponse(0x6A);
}
void CommandWidget::on_pushButton_4_clicked()
{
buttonResponse(0x77);
}
void CommandWidget::on_pushButton_3_clicked()
{
buttonResponse(0x78);
}
void CommandWidget::on_pushButton_14_clicked()
{
buttonResponse(0x79);
}
void CommandWidget::on_pushButton_13_clicked()
{
buttonResponse(0xC6);
}
void CommandWidget::on_pushButton_17_clicked()
{
buttonResponse(0x66);
}
void CommandWidget::on_pushButton_8_clicked()
{
buttonResponse(0x67);
}
void CommandWidget::on_pushButton_7_clicked()
{
buttonResponse(0x68);
}
void CommandWidget::on_pushButton_12_clicked()
{
buttonResponse(0x7B);
}
void CommandWidget::on_pushButton_11_clicked()
{
buttonResponse(0x7A);
}
void CommandWidget::on_pushButton_16_clicked()
{
buttonResponse(0xC8);
}
void CommandWidget::on_pushButton_15_clicked()
{
buttonResponse(0xC9);
}
void CommandWidget::on_videoLayout1TBtn_clicked() {
ui->videoLayout1TBtn->setDisabled(true);
emit changeVideoLayout(0);
ui->videoLayout1TBtn->setDisabled(false);
}
void CommandWidget::on_videoLayout4TBtn_clicked() {
ui->videoLayout4TBtn->setDisabled(true);
emit changeVideoLayout(1);
ui->videoLayout4TBtn->setDisabled(false);
}

@ -3,9 +3,81 @@
#include <QUdpSocket>
#include <QWidget>
#include "global.h"
#include "communicationsettingdlg.h"
typedef struct struProtocalKB {
quint8 head[2]; //帧头0x
quint8 reserved; //预留
quint8 data[3]; //开关指令
quint8 curPage; //指令页
quint8 CHKSUM; //校验字节
}ProtocalKB;
// 定义常量
const unsigned char PROTOCOL_HEAD_0 = 0xEB;
const unsigned char PROTOCOL_HEAD_1 = 0x98;
const unsigned char PROTOCOL_RESERVED = 0x00;
const unsigned char PROTOCOL_CUR_PAGE = 0x01;
const quint16 CRC8TAB[256] =
{
//0
0x0000, 0x0031, 0x0062, 0x0053, 0x00C4, 0x00F5, 0x00A6, 0x0097,
0x00B9, 0x0088, 0x00DB, 0x00EA, 0x007D, 0x004C, 0x001F, 0x002E,
//1
0x0043, 0x0072, 0x0021, 0x0010, 0x0087, 0x00B6, 0x00E5, 0x00D4,
0x00FA, 0x00CB, 0x0098, 0x00A9, 0x003E, 0x000F, 0x005C, 0x006D,
//2
0x0086, 0x00B7, 0x00E4, 0x00D5, 0x0042, 0x0073, 0x0020, 0x0011,
0x003F, 0x000E, 0x005D, 0x006C, 0x00FB, 0x00CA, 0x0099, 0x00A8,
//3
0x00C5, 0x00F4, 0x00A7, 0x0096, 0x0001, 0x0030, 0x0063, 0x0052,
0x007C, 0x004D, 0x001E, 0x002F, 0x00B8, 0x0089, 0x00DA, 0x00EB,
//4
0x003D, 0x000C, 0x005F, 0x006E, 0x00F9, 0x00C8, 0x009B, 0x00AA,
0x0084, 0x00B5, 0x00E6, 0x00D7, 0x0040, 0x0071, 0x0022, 0x0013,
//5
0x007E, 0x004F, 0x001C, 0x002D, 0x00BA, 0x008B, 0x00D8, 0x00E9,
0x00C7, 0x00F6, 0x00A5, 0x0094, 0x0003, 0x0032, 0x0061, 0x0050,
//6
0x00BB, 0x008A, 0x00D9, 0x00E8, 0x007F, 0x004E, 0x001D, 0x002C,
0x0002, 0x0033, 0x0060, 0x0051, 0x00C6, 0x00F7, 0x00A4, 0x0095,
//7
0x00F8, 0x00C9, 0x009A, 0x00AB, 0x003C, 0x000D, 0x005E, 0x006F,
0x0041, 0x0070, 0x0023, 0x0012, 0x0085, 0x00B4, 0x00E7, 0x00D6,
//8
0x007A, 0x004B, 0x0018, 0x0029, 0x00BE, 0x008F, 0x00DC, 0x00ED,
0x00C3, 0x00F2, 0x00A1, 0x0090, 0x0007, 0x0036, 0x0065, 0x0054,
//9
0x0039, 0x0008, 0x005B, 0x006A, 0x00FD, 0x00CC, 0x009F, 0x00AE,
0x0080, 0x00B1, 0x00E2, 0x00D3, 0x0044, 0x0075, 0x0026, 0x0017,
//A
0x00FC, 0x00CD, 0x009E, 0x00AF, 0x0038, 0x0009, 0x005A, 0x006B,
0x0045, 0x0074, 0x0027, 0x0016, 0x0081, 0x00B0, 0x00E3, 0x00D2,
//B
0x00BF, 0x008E, 0x00DD, 0x00EC, 0x007B, 0x004A, 0x0019, 0x0028,
0x0006, 0x0037, 0x0064, 0x0055, 0x00C2, 0x00F3, 0x00A0, 0x0091,
//C
0x0047, 0x0076, 0x0025, 0x0014, 0x0083, 0x00B2, 0x00E1, 0x00D0,
0x00FE, 0x00CF, 0x009C, 0x00AD, 0x003A, 0x000B, 0x0058, 0x0069,
//D
0x0004, 0x0035, 0x0066, 0x0057, 0x00C0, 0x00F1, 0x00A2, 0x0093,
0x00BD, 0x008C, 0x00DF, 0x00FE, 0x0079, 0x0048, 0x001B, 0x002A,
//E
0x00C1, 0x00F0, 0x00A3, 0x0092, 0x0005, 0x0034, 0x0067, 0x0056,
0x0078, 0x0049, 0x001A, 0x002B, 0x00BC, 0x008D, 0x00DE, 0x00EF,
//F
0x0082, 0x00B3, 0x00E0, 0x00D1, 0x0046, 0x0077, 0x0024, 0x0015,
0x003B, 0x000A, 0x0059, 0x0068, 0x00FF, 0x00CE, 0x009D, 0x00AC
};
namespace Ui {
class CommandWidget;
}
@ -22,21 +94,67 @@ signals:
void startConnectionSignal(QString ip, int port);
void stopConnectionSignal();
void sendErrorMessage(QString message, int type);
void changeVideoLayout(int index);
private slots:
void on_settingToolBtn_clicked();
void on_startConnectionToolBtn_clicked();
void on_stopConnectionToolBtn_clicked();
void receiveMessageSlots(QString message, int type);
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_5_clicked();
void on_pushButton_6_clicked();
void on_pushButton_10_clicked();
void on_pushButton_9_clicked();
void on_pushButton_4_clicked();
void on_pushButton_3_clicked();
void on_pushButton_14_clicked();
void on_pushButton_13_clicked();
void on_pushButton_17_clicked();
void on_pushButton_8_clicked();
void on_pushButton_7_clicked();
void on_pushButton_12_clicked();
void on_pushButton_11_clicked();
void on_pushButton_16_clicked();
void on_pushButton_15_clicked();
void on_videoLayout1TBtn_clicked();
void on_videoLayout4TBtn_clicked();
private:
Ui::CommandWidget *ui;
CommunicationSettingDlg settingDlg;
QString m_remoteIP;
int m_remotePort;
unsigned char getCrc(quint8* data, quint8 length);
ProtocalKB EncodeCommandCmd(quint8 cmd);
bool writeBufferToClient(const QByteArray &data);
void buttonResponse(quint8 cmd);
};
#endif // COMMANDWIDGET_H

@ -6,54 +6,67 @@
<rect>
<x>0</x>
<y>0</y>
<width>358</width>
<width>295</width>
<height>541</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QWidget" name="">
<property name="geometry">
<rect>
<x>20</x>
<y>30</y>
<width>295</width>
<height>431</height>
</rect>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>1</number>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QToolButton" name="settingToolBtn">
<property name="text">
<string>通信设置</string>
<property name="leftMargin">
<number>4</number>
</property>
<property name="icon">
<iconset>
<normaloff>res/settings.png</normaloff>res/settings.png</iconset>
<property name="topMargin">
<number>4</number>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
<property name="rightMargin">
<number>4</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QToolButton" name="startConnectionToolBtn">
<property name="text">
<string>连接</string>
<property name="bottomMargin">
<number>4</number>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="stopConnectionToolBtn">
<property name="text">
<string>断开</string>
<item>
<widget class="QFrame" name="frame">
<property name="styleSheet">
<string notr="true">#frame{
border: 1px solid white;
border-radius:6px;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="spacing">
<number>14</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="pushButton">
<property name="text">
@ -173,8 +186,65 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QToolButton" name="startConnectionToolBtn">
<property name="text">
<string>连接</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QToolButton" name="videoLayout1TBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="videoLayout4TBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>219</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>

@ -92,7 +92,10 @@ void CommunicationSettingDlg::on_saveSettingBtn_clicked() {
}
// 保存配置参数
if (!g_networkSettingInfo) return;
if (!g_networkSettingInfo) {
this->close();
return;
};
g_networkSettingInfo->setValue("DataSource", dataType);
QString groupName = "";
switch (dataType) {
@ -119,4 +122,5 @@ void CommunicationSettingDlg::on_saveSettingBtn_clicked() {
ui->saveSettingBtn->setDisabled(false);
emit sendErrorMessage("保存成功!", 1);
this->close();
}

@ -1,6 +1,10 @@
#include "global.h"
QSettings* g_networkSettingInfo;
QUdpSocket* g_CommandudpSocket;
void getLocalIP(QStringList& localIPList) {
QHostInfo hostInfo = QHostInfo::fromName(QHostInfo::localHostName());
@ -42,4 +46,158 @@ bool isMulticastAddress(const QString& ip) {
return false;
}
/**
* @brief calCRC16
* @param cpu8Data
* @param u16Len
* @return
*/
uint16_t calCRC16(const uint8_t* cpu8Data, uint16_t u16Len) {
uint8_t u8X;
uint16_t u16CRC = 0X8848;
while (u16Len--) {
u8X = u16CRC >> 8 ^ *cpu8Data++;
u8X ^= u8X >> 4;
u16CRC = (u16CRC << 8) ^ ((uint16_t)(u8X << 12)) ^
((uint16_t)(u8X << 5)) ^ ((uint16_t)u8X);
}
return u16CRC;
}
/**
* @brief MD5
* @param str
* @return MD5
*/
QString calculateMD5(const QString& str) {
QByteArray hash =
QCryptographicHash::hash(str.toUtf8(), QCryptographicHash::Md5);
return hash.toHex();
}
/**
* @brief
* @param uavID: ID
* @param uavName: 981cs
* @param clientID: ID01
* @param pushDomain:
* @param appName: app
* @param expireTime: 1h
* @return
*/
QString generatePushURL(int uavID, QString appName, QString pushKey,
QString uavName, int clientID, QString pushDomain,
long expireTime) {
QString clientName = "";
if (0 == clientID) {
clientName = "gcs"; // 地面端
} else {
clientName = "uav"; // 载荷端
}
QString streamName =
uavName + "_" + QString::number(uavID) + "_" + clientName;
QString pushURL = "";
if (pushKey == "") {
pushURL = "rtmp://" + pushDomain + "/" + appName + "/" + streamName;
} else {
// 计算鉴权串
long timeStamp =
QDateTime::currentMSecsSinceEpoch() / 1000 + expireTime;
QString stringToMd5 = "/" + appName + "/" + streamName + "-" +
QString::number(timeStamp) + "-0-0-" + pushKey;
QString authKey = calculateMD5(stringToMd5);
pushURL = "rtmp://" + pushDomain + "/" + appName + "/" + streamName +
"?auth_key=" + QString::number(timeStamp) + "-0-0-" + authKey;
}
return pushURL;
}
std::map<int, std::string> g_mapAppName;
/**
* @brief
* @param uavID: ID
* @param appName: app
* @param uavName: 981cs
* @param clientID: ID01
* @param pullDomain:
* @param expireTime: 1h
* @param pullKey: Key
* @return
*/
QString generatePullURL(int uavID, QString appName, QString pullKey,
QString uavName, int clientID, QString pullDomain,
long expireTime) {
QString rtmpUrl = "";
QString clientName = "";
if (0 == clientID) {
clientName = "gcs"; // 地面端
} else {
clientName = "uav"; // 载荷端
}
QString streamName =
uavName + "_" + QString::number(uavID) + "_" + clientName;
if (pullKey == "") {
rtmpUrl = "rtmp://" + pullDomain + "/" + appName + "/" + streamName;
} else {
// 计算鉴权串
long timeStamp =
QDateTime::currentMSecsSinceEpoch() / 1000 + expireTime;
QString stringToMd5 = "/" + appName + "/" + streamName + "-" +
QString::number(timeStamp) + "-0-0-" + pullKey;
QString authKey = calculateMD5(stringToMd5);
rtmpUrl = "rtmp://" + pullDomain + "/" + appName + "/" + streamName +
"?auth_key=" + QString::number(timeStamp) + "-0-0-" + authKey;
}
return rtmpUrl;
}
/**
* @brief
* @param uavID:ID
* @param appName:
* @param uavName:981cs
* @return
*/
QString generatePushURL2(int uavID, QString appName, QString uavName,
int clientID, QString pushDomain) {
QString rtmpUrl = "";
QString clientName = "";
if (0 == clientID) {
clientName = "gcs"; // 地面端
} else {
clientName = "uav"; // 载荷端
}
QString streamName =
uavName + "_" + QString::number(uavID) + "_" + clientName;
rtmpUrl = "rtmp://" + pushDomain + "/" + appName + "/" + streamName;
return rtmpUrl;
}
/**
* @brief
* @param uavID: ID
* @param appName: app
* @param uavName: 981cs
* @param clientID: ID01
* @param pullDomain:
* @return
*/
QString generatePullURL2(int uavID, QString appName, QString uavName,
int clientID, QString pullDomain) {
QString clientName = "";
if (0 == clientID) {
clientName = "gcs"; // 地面端
} else {
clientName = "uav"; // 载荷端
}
QString streamName =
uavName + "_" + QString::number(uavID) + "_" + clientName;
QString pushURL = "";
pushURL = "rtsp://" + pullDomain + "/" + appName + "/" + streamName;
return pushURL;
}
Global::Global() {}

@ -1,6 +1,8 @@
#ifndef GLOBAL_H
#define GLOBAL_H
#include <QCryptographicHash>
#include <QDateTime>
#include <QHostAddress>
#include <QHostInfo>
#include <QList>
@ -8,8 +10,9 @@
#include <QSerialPortInfo>
#include <QSettings>
#include <QStringList>
#include <QUdpSocket>
extern QSettings* g_networkSettingInfo;
extern QUdpSocket* g_CommandudpSocket;
extern void getLocalIP(QStringList& localIPList);
@ -33,6 +36,81 @@ struct NetworkIPStruct {
// NOTIFICATION_WARNING = 3
// };
/**
* @brief calCRC16
* @param cpu8Data
* @param u16Len
* @return
*/
extern uint16_t calCRC16(const uint8_t* cpu8Data, uint16_t u16Len);
/**
* @brief MD5
* @param str
* @return MD5
*/
extern QString calculateMD5(const QString& str);
/**
* @brief
* @param uavID: ID
* @param uavName: 981cs
* @param clientID: ID01
* @param pushDomain:
* @param appName
* @param expireTime: 1h
* @param pushKey: Key
* @return
*/
extern QString generatePushURL(int uavID, QString appName = "nmyj",
QString pushKey = "ZRjGVcPYGhKib0rdgH",
QString uavName = "981cs", int clientID = 0,
QString pushDomain = "push.uavideo.cn",
long expireTime = 6 * 3600);
/**
* @brief
* @param uavID:ID
* @param clientID: ID01
* @param appName:
* @param uavName:981cs
* @param pushDomain:
* @return
*/
extern QString generatePushURL2(int uavID, QString appName = "nmyj",
QString uavName = "981cs", int clientID = 0,
QString pushDomain = "182.92.130.23");
/**
* @brief
* @param uavID: ID
* @param appName: app
* @param uavName: 981cs
* @param clientID: ID01
* @param pullDomain:
* @param expireTime: 1h
* @param pullKey: Key
* @return
*/
extern QString generatePullURL(int uavID, QString appName = "nmyj",
QString pullKey = "HDaMVkLnIcr0mGhV8d",
QString uavName = "981cs", int clientID = 0,
QString pullDomain = "play.uavideo.cn",
long expireTime = 6 * 3600);
/**
* @brief
* @param uavID: ID
* @param appName: app
* @param uavName: 981cs
* @param clientID: ID01
* @param pullDomain:
* @return
*/
extern QString generatePullURL2(int uavID, QString appName = "nmyj",
QString uavName = "981cs", int clientID = 0,
QString pullDomain = "182.92.130.23");
class Global {
public:
Global();

@ -1,14 +1,14 @@
#include "mainwindow.h"
// #include <QWKWidgets/qwkwidgetsglobal.h>
// #include <QWKWidgets/widgetwindowagent.h>
#include <QWKWidgets/widgetwindowagent.h>
#include <widgetframe/windowbar.h>
#include <widgetframe/windowbutton.h>
#include <QList>
#include <QtGui/QPainter>
#include "ui_mainwindow.h"
// #include "widgetframe/windowbar.h"
// #include "widgetframe/windowbutton.h"
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QtGui/QActionGroup>
#else
@ -19,47 +19,14 @@ MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
this->setWindowTitle("载荷视频播放软件");
/*
auto windowAgent = new QWK::WidgetWindowAgent(this);
windowAgent->setup(this);
auto windowBar = new QWK::WindowBar();
auto iconButton = new QWK::WindowButton();
iconButton->setObjectName(QStringLiteral("icon-button"));
iconButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
auto minButton = new QWK::WindowButton();
minButton->setObjectName(QStringLiteral("min-button"));
minButton->setProperty("system-button", true);
minButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
auto maxButton = new QWK::WindowButton();
maxButton->setCheckable(true);
maxButton->setObjectName(QStringLiteral("max-button"));
maxButton->setProperty("system-button", true);
maxButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
auto closeButton = new QWK::WindowButton();
closeButton->setObjectName(QStringLiteral("close-button"));
closeButton->setProperty("system-button", true);
closeButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
windowBar->setIconButton(iconButton);
windowBar->setMinButton(minButton);
windowBar->setMaxButton(maxButton);
windowBar->setCloseButton(closeButton);
windowAgent->setTitleBar(windowBar);
setMenuWidget(windowBar);
// windowAgent->setHitTestVisible(windowBar->menuBar(), true);
*/
// this->setWindowTitle("载荷视频播放软件");
installWindowAgent();
loadStyleSheet(Dark);
// apply the qss
QFile qssfile(":/Qss/qss.qss");
if (qssfile.open(QFile::ReadOnly)) {
// file.open(QFile::ReadOnly);
QString style = QLatin1String(qssfile.readAll());
qApp->setStyleSheet(style);
// qDebug()<<style;
qssfile.close();
} else {
qDebug() << "Open file fail " << Qt::endl;
@ -82,19 +49,59 @@ MainWindow::MainWindow(QWidget *parent)
file.open(QIODevice::WriteOnly);
file.close();
}
g_networkSettingInfo =
new QSettings("networkSettingInfo.ini", QSettings::IniFormat);
initSignalConnection();
initNotifyManager();
initNotifyMessageConnection();
initCommunitions();
initChangeVideoLayoutConnection();
initHideButton();
setSavedVideoDir();
// ui->stackedWidget->setCurrentIndex(1);
// ui->videoWidget1->play(list.at(0));
// ui->videoWidget2->play(list.at(0));
// ui->videoWidget3->play(list.at(0));
// ui->videoWidget4->play(list.at(0));
}
MainWindow::~MainWindow() {
delete ui;
}
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message,
qintptr *result) {
#ifdef Q_OS_WIN
MSG *msg = (MSG *)message;
if (msg->message == WM_SYSCOMMAND) {
if (61587 == msg->wParam) { // 单击事件
return true;
}
if (61539 == msg->wParam) { // 双击事件
return true;
}
}
#endif
return QMainWindow::nativeEvent(eventType, message, result);
}
void MainWindow::paintEvent(QPaintEvent *ev) {
int offset = 0;
if (ui->commandWidget->isHidden()) {
offset = hideCommandWidgetBtn->width() / 2;
}
hideCommandWidgetBtn->move(ui->stackedWidget->width() - offset,
ui->commandWidget->height() / 2);
int i = 0;
}
void MainWindow::initSignalConnection() {
connect(ui->commandWidget, &CommandWidget::startConnectionSignal,
ui->videoWidget, &VideoWidget::udpPlay, Qt::UniqueConnection);
@ -110,6 +117,15 @@ void MainWindow::initNotifyMessageConnection() {
&MainWindow::showMessageSlots);
}
void MainWindow::initChangeVideoLayoutConnection() {
connect(ui->commandWidget, &CommandWidget::changeVideoLayout, this,
[=](int index) {
ui->stackedWidget->setCurrentIndex(index);
});
}
void MainWindow::initNotifyManager() {
m_notifyManager = new NotifyManager(this, this);
m_notifyManager->setMaxCount(5);
@ -117,109 +133,57 @@ void MainWindow::initNotifyManager() {
m_notifyManager->setNotifyWndSize(400, 60);
}
void MainWindow::initCommunitions()
{
g_CommandudpSocket = new QUdpSocket(this);
// 将套接字绑定到指定端口
if (g_CommandudpSocket->bind(QHostAddress::Any, 1200)) {
qDebug() << "UDP 套接字已绑定到端口 1200";
} else {
qDebug() << "绑定 UDP 套接字失败";
}
}
void MainWindow::showMessageSlots(QString message, int type) {
if (m_notifyManager) {
m_notifyManager->notify(message, "", type, 3000);
}
}
/*
void MainWindow::installWindowAgent() {
// 1. Setup window agent
windowAgent = new QWK::WidgetWindowAgent(this);
windowAgent->setup(this);
// 2. Construct your title bar
auto menuBar = [this]() {
auto menuBar = new QMenuBar(this);
// QMenu *settingmenu =
// menuBar->addMenu(QIcon(":/images/settings1.png"), "设置");
QAction *settingAction = new QAction(this);
settingAction->setIcon(QIcon(":/images/settings1.png"));
settingAction->setIconText("设置");
menuBar->addAction(settingAction);
connect(settingAction, &QAction::triggered, this,
&MainWindow::showSettingDlgSlot);
QAction *playBackAction = new QAction(this);
playBackAction->setIcon(QIcon(":/images/playback.png"));
playBackAction->setIconText("回放");
menuBar->addAction(playBackAction);
connect(playBackAction, &QAction::triggered, this,
&MainWindow::openSavedVideoDirSlot);
QAction *pushStreamAction = new QAction(this);
pushStreamAction->setIcon(QIcon(":/images/pushstream.png"));
pushStreamAction->setIconText("推流");
menuBar->addAction(pushStreamAction);
connect(pushStreamAction, &QAction::triggered, this,
&MainWindow::showStreamSettingsDlgSlot);
// Virtual menu
auto file = new QMenu(tr("File(&F)"), menuBar);
file->addAction(new QAction(tr("New(&N)"), menuBar));
file->addAction(new QAction(tr("Open(&O)"), menuBar));
file->addSeparator();
auto edit = new QMenu(tr("Edit(&E)"), menuBar);
edit->addAction(new QAction(tr("Undo(&U)"), menuBar));
edit->addAction(new QAction(tr("Redo(&R)"), menuBar));
// Theme action
auto darkAction = new QAction(tr("Enable dark theme"), menuBar);
darkAction->setCheckable(true);
// connect(darkAction, &QAction::triggered, this, [this](bool checked) {
// loadStyleSheet(checked ? Dark : Light); //
// });
// connect(this, &MainWindow::themeChanged, darkAction,
// [this, darkAction]() {
// darkAction->setChecked(currentTheme == Dark); //
// });
auto noneAction = new QAction(tr("None"), menuBar);
noneAction->setData(QStringLiteral("none"));
noneAction->setCheckable(true);
noneAction->setChecked(true);
auto dwmBlurAction = new QAction(tr("Enable DWM blur"), menuBar);
dwmBlurAction->setData(QStringLiteral("dwm-blur"));
dwmBlurAction->setCheckable(true);
auto acrylicAction =
new QAction(tr("Enable acrylic material"), menuBar);
acrylicAction->setData(QStringLiteral("acrylic-material"));
acrylicAction->setCheckable(true);
auto micaAction = new QAction(tr("Enable mica"), menuBar);
micaAction->setData(QStringLiteral("mica"));
micaAction->setCheckable(true);
auto micaAltAction = new QAction(tr("Enable mica alt"), menuBar);
micaAltAction->setData(QStringLiteral("mica-alt"));
micaAltAction->setCheckable(true);
auto winStyleGroup = new QActionGroup(menuBar);
winStyleGroup->addAction(noneAction);
winStyleGroup->addAction(dwmBlurAction);
winStyleGroup->addAction(acrylicAction);
winStyleGroup->addAction(micaAction);
winStyleGroup->addAction(micaAltAction);
// connect(winStyleGroup, &QActionGroup::triggered, this,
// [this, winStyleGroup](QAction *action) {
// // Unset all custom style attributes first, otherwise the
// // style will not display correctly
// for (const QAction *_act : winStyleGroup->actions()) {
// const QString data = _act->data().toString();
// if (data.isEmpty() || data == QStringLiteral("none"))
// {
// continue;
// }
// windowAgent->setWindowAttribute(data, false);
// }
// const QString data = action->data().toString();
// if (data == QStringLiteral("none")) {
// setProperty("custom-style", false);
// } else if (!data.isEmpty()) {
// windowAgent->setWindowAttribute(data, true);
// setProperty("custom-style", true);
// }
// style()->polish(this);
// });
// Real menu
auto settings = new QMenu(tr("Settings(&S)"), menuBar);
settings->addAction(darkAction);
settings->addSeparator();
settings->addAction(noneAction);
settings->addAction(dwmBlurAction);
settings->addAction(acrylicAction);
settings->addAction(micaAction);
settings->addAction(micaAltAction);
menuBar->addMenu(file);
menuBar->addMenu(edit);
menuBar->addMenu(settings);
return menuBar;
}();
menuBar->setObjectName(QStringLiteral("win-menu-bar"));
auto titleLabel = new QLabel();
@ -228,6 +192,7 @@ void MainWindow::installWindowAgent() {
#ifndef Q_OS_MAC
auto iconButton = new QWK::WindowButton();
iconButton->setEnabled(false);
iconButton->setObjectName(QStringLiteral("icon-button"));
iconButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
@ -316,4 +281,56 @@ void MainWindow::installWindowAgent() {
connect(windowBar, &QWK::WindowBar::closeRequested, this, &QWidget::close);
#endif
}
*/
void MainWindow::loadStyleSheet(Theme theme) {
if (!styleSheet().isEmpty() && theme == currentTheme) return;
currentTheme = theme;
if (QFile qss(theme == Dark ? QStringLiteral(":/Qss/dark-style.qss")
: QStringLiteral(":/Qss/light-style.qss"));
qss.open(QIODevice::ReadOnly | QIODevice::Text)) {
setStyleSheet(QString::fromUtf8(qss.readAll()));
Q_EMIT themeChanged();
}
}
void MainWindow::setSavedVideoDir() {
ui->videoWidget->setVedioSaveFileDirPath("./video/main");
ui->videoWidget1->setVedioSaveFileDirPath("./video/video1");
ui->videoWidget2->setVedioSaveFileDirPath("./video/video2");
ui->videoWidget3->setVedioSaveFileDirPath("./video/video3");
ui->videoWidget4->setVedioSaveFileDirPath("./video/video4");
}
void MainWindow::initHideButton() {
hideCommandWidgetBtn = new QToolButton(this);
hideCommandWidgetBtn->setIcon(QIcon(":/images/right.png"));
hideCommandWidgetBtn->setIconSize(QSize(24, 24));
hideCommandWidgetBtn->setFixedSize(30, 30);
connect(hideCommandWidgetBtn, &QToolButton::clicked, this,
&MainWindow::hideCommandWidgetSlot);
}
void MainWindow::showSettingDlgSlot() {
settingDlg.exec();
}
void MainWindow::showStreamSettingsDlgSlot() {
streamAddrSettingsDlg.exec();
}
void MainWindow::hideCommandWidgetSlot() {
if (ui->commandWidget->isHidden()) {
ui->commandWidget->show();
hideCommandWidgetBtn->setIcon(QIcon(":/images/right.png"));
} else {
ui->commandWidget->hide();
hideCommandWidgetBtn->setIcon(QIcon(":/images/left.png"));
}
}
void MainWindow::openSavedVideoDirSlot() {
QDesktopServices::openUrl(
QUrl(QApplication::applicationDirPath() + "/video"));
}

@ -2,11 +2,23 @@
#define MAINWINDOW_H
#include <QDebug>
#include <QDesktopServices>
#include <QMainWindow>
#include <QToolButton>
#include <QWindow>
#include "NotifyManager.h"
#include "commandwidget.h"
#include "communicationsettingdlg.h"
#include "global.h"
#include "streamaddrsettingsdlg.h"
#include "videowidget.h"
#ifdef Q_OS_WIN
#include "windows.h"
#include "windowsx.h"
#pragma comment(lib, "user32.lib")
#endif
namespace QWK {
class WidgetWindowAgent;
@ -26,18 +38,46 @@ public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
enum Theme {
Dark,
Light,
};
Q_ENUM(Theme)
protected:
bool nativeEvent(const QByteArray &eventType, void *message,
qintptr *result) override;
void paintEvent(QPaintEvent *ev) override;
signals:
void themeChanged();
private:
void initSignalConnection();
void initNotifyMessageConnection();
void initChangeVideoLayoutConnection();
void initNotifyManager();
void showMessageSlots(QString message, int type);
void initCommunitions();
void installWindowAgent();
void loadStyleSheet(Theme theme);
void setSavedVideoDir();
// void installWindowAgent();
void initHideButton();
private slots:
void showSettingDlgSlot();
void showStreamSettingsDlgSlot();
void hideCommandWidgetSlot();
void openSavedVideoDirSlot();
private:
Ui::MainWindow *ui;
// QWK::WidgetWindowAgent *windowAgent;
VideoWidget *videoWidget1 = nullptr;
CommunicationSettingDlg settingDlg;
StreamAddrSettingsDlg streamAddrSettingsDlg;
QWK::WidgetWindowAgent *windowAgent;
Theme currentTheme{};
NotifyManager *m_notifyManager = nullptr;
QToolButton *hideCommandWidgetBtn = nullptr;
};
#endif // MAINWINDOW_H

@ -14,104 +14,154 @@
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="10,2">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="9,2">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>4</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout" stretch="12,1,1">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_3">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="VideoWidget" name="videoWidget"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_4">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1,27,1,0">
<layout class="QGridLayout" name="gridLayout">
<property name="spacing">
<number>14</number>
<number>2</number>
</property>
<item row="0" column="0">
<widget class="VideoWidget" name="videoWidget1"/>
</item>
<item row="0" column="1">
<widget class="VideoWidget" name="videoWidget2"/>
</item>
<item row="1" column="0">
<widget class="VideoWidget" name="videoWidget3"/>
</item>
<item row="1" column="1">
<widget class="VideoWidget" name="videoWidget4"/>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<widget class="QWidget" name="widget_3" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>4</number>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
</spacer>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,10,1">
<property name="verticalSpacing">
<number>4</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>00:00:00</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="minimumSize">
<size>
<width>854</width>
<height>0</height>
</size>
</property>
<property name="value">
<number>24</number>
</property>
<property name="textVisible">
<bool>false</bool>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<item row="0" column="1">
<widget class="WProgressBar" name="widget_2" native="true"/>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>00:00:00</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="1,10,1">
<item>
<item row="1" column="0">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
<width>37</width>
<height>17</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<item row="1" column="1">
<widget class="VideoControl" name="widget" native="true"/>
</item>
</layout>
</item>
<item>
<item row="1" column="2">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -122,6 +172,8 @@
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
@ -130,8 +182,6 @@
<widget class="CommandWidget" name="commandWidget" native="true"/>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
@ -139,7 +189,7 @@
<x>0</x>
<y>0</y>
<width>1388</width>
<height>25</height>
<height>24</height>
</rect>
</property>
</widget>
@ -162,6 +212,12 @@
<header>videoControl.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>WProgressBar</class>
<extends>QWidget</extends>
<header>wprogressbar.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>

@ -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;
}

@ -323,3 +323,4 @@ QDoubleSpinBox
color:white;
}

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

@ -9,9 +9,26 @@
<file>images/settings.png</file>
<file>images/up.png</file>
<file>images/wifi4G.png</file>
<file>Qss/dark-style.qss</file>
<file>Qss/light-style.qss</file>
<file>app/example.icns</file>
<file>app/example.ico</file>
<file>app/example.png</file>
<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/more-line.svg</file>
<file>window-bar/restore.svg</file>
<file>images/pushstream.png</file>
<file>images/settings1.png</file>
<file>images/playback.png</file>
<file>images/videolayout1.png</file>
<file>images/videolayout4.png</file>
<file>images/playMedio.png</file>
<file>images/stop.png</file>
<file>images/playFast.png</file>
<file>images/playSlow.png</file>
</qresource>
</RCC>

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>

@ -19,10 +19,10 @@ PUBLIC
windowbutton_p.h
)
qt6_add_resources(${PROJECT_NAME} "resources"
PREFIX "/"
FILES "../resources/shared.qrc"
)
# qt6_add_resources(${PROJECT_NAME} "resources"
# PREFIX "/"
# FILES "resources/shared.qrc"
# )
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt6::Core

@ -0,0 +1,107 @@
#include "streamaddrsettingsdlg.h"
#include "ui_streamaddrsettingsdlg.h"
StreamAddrSettingsDlg::StreamAddrSettingsDlg(QWidget *parent)
: QDialog(parent), ui(new Ui::StreamAddrSettingsDlg) {
ui->setupUi(this);
this->setWindowTitle(QStringLiteral("视频流设置"));
// 飞机类型
QStringList uavTypeList;
uavTypeList.append("FP98");
uavTypeList.append("FP985");
uavTypeList.append("FP981C");
uavTypeList.append("FP981CS");
uavTypeList.append("FP981A");
ui->uavTypeCombox->insertItems(0, uavTypeList);
//
ui->pushStreamIPCombox->insertItem(0, QStringLiteral("航天飞鹏服务器"));
ui->pushStreamIPCombox->insertItem(1,
QStringLiteral("航天飞鹏阿里云服务器"));
ui->pushStreamIPCombox->insertItem(2, QStringLiteral("自定义"));
}
StreamAddrSettingsDlg::~StreamAddrSettingsDlg() {
delete ui;
}
void StreamAddrSettingsDlg::showEvent(QShowEvent *event) {
initSavedParms();
}
void StreamAddrSettingsDlg::on_pushStreamIPCombox_currentIndexChanged(
int index) {
if (2 == index) {
ui->pushStreamIPCombox->setEditable(true);
} else {
ui->pushStreamIPCombox->setEditable(false);
}
}
// 保存设置
void StreamAddrSettingsDlg::on_saveBtn_clicked() {
if (!g_networkSettingInfo) {
this->close();
return;
}
g_networkSettingInfo->setValue("NetworkStreamSettings/uavType",
ui->uavTypeCombox->currentIndex());
g_networkSettingInfo->setValue("NetworkStreamSettings/uavID",
ui->uavIDspinBox->value());
g_networkSettingInfo->setValue("NetworkStreamSettings/podPullAddress",
ui->podPullStreamIPEdit->text());
g_networkSettingInfo->setValue("NetworkStreamSettings/pullAddress1",
ui->pullStreamAddrEdit1->text());
g_networkSettingInfo->setValue("NetworkStreamSettings/pullAddress2",
ui->pullStreamAddrEdit2->text());
g_networkSettingInfo->setValue("NetworkStreamSettings/pullAddress3",
ui->pullStreamAddrEdit3->text());
g_networkSettingInfo->setValue("NetworkStreamSettings/pullAddress4",
ui->pullStreamAddrEdit4->text());
int pushStreamIndex = ui->pushStreamIPCombox->currentIndex();
g_networkSettingInfo->setValue("NetworkStreamSettings/pushStreamType",
pushStreamIndex);
if (2 == pushStreamIndex) {
g_networkSettingInfo->setValue(
"NetworkStreamSettings/pushStreamAddress",
ui->pushStreamIPCombox->currentText());
}
this->close();
}
void StreamAddrSettingsDlg::initSavedParms() {
if (!g_networkSettingInfo) return;
ui->uavTypeCombox->setCurrentIndex(
g_networkSettingInfo->value("NetworkStreamSettings/uavType").toInt());
ui->uavIDspinBox->setValue(
g_networkSettingInfo->value("NetworkStreamSettings/uavID").toInt());
ui->podPullStreamIPEdit->setText(
g_networkSettingInfo->value("NetworkStreamSettings/podPullAddress")
.toString());
ui->pullStreamAddrEdit1->setText(
g_networkSettingInfo->value("NetworkStreamSettings/pullAddress1")
.toString());
ui->pullStreamAddrEdit2->setText(
g_networkSettingInfo->value("NetworkStreamSettings/pullAddress2")
.toString());
ui->pullStreamAddrEdit3->setText(
g_networkSettingInfo->value("NetworkStreamSettings/pullAddress3")
.toString());
ui->pullStreamAddrEdit4->setText(
g_networkSettingInfo->value("NetworkStreamSettings/pullAddress4")
.toString());
int pushStreamIndex =
g_networkSettingInfo->value("NetworkStreamSettings/pushStreamType")
.toInt();
ui->pushStreamIPCombox->setCurrentIndex(pushStreamIndex);
if (2 == pushStreamIndex) {
ui->pushStreamIPCombox->setItemText(
pushStreamIndex,
g_networkSettingInfo
->value("NetworkStreamSettings/pushStreamAddress")
.toString());
}
}

@ -0,0 +1,34 @@
#ifndef STREAMADDRSETTINGSDLG_H
#define STREAMADDRSETTINGSDLG_H
#include <QDialog>
#include "global.h"
namespace Ui {
class StreamAddrSettingsDlg;
}
class StreamAddrSettingsDlg : public QDialog {
Q_OBJECT
public:
explicit StreamAddrSettingsDlg(QWidget *parent = nullptr);
~StreamAddrSettingsDlg();
protected:
void showEvent(QShowEvent *event);
private slots:
void on_pushStreamIPCombox_currentIndexChanged(int index);
void on_saveBtn_clicked();
private:
void initSavedParms();
private:
Ui::StreamAddrSettingsDlg *ui;
};
#endif // STREAMADDRSETTINGSDLG_H

@ -0,0 +1,232 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StreamAddrSettingsDlg</class>
<widget class="QDialog" name="StreamAddrSettingsDlg">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>571</width>
<height>350</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,2,1,1">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>飞机信息</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QGridLayout" name="gridLayout_3" columnstretch="1,4,1,4">
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>飞机型号:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="uavTypeCombox"/>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_8">
<property name="text">
<string>飞机ID</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QSpinBox" name="uavIDspinBox">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>拉流地址</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="1,4,1,4">
<property name="topMargin">
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>7</number>
</property>
<property name="verticalSpacing">
<number>12</number>
</property>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>备用3</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>吊舱:</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>备用2</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>备用1</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>备用4</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="podPullStreamIPEdit"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="pullStreamAddrEdit1"/>
</item>
<item row="1" column="3">
<widget class="QLineEdit" name="pullStreamAddrEdit2"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="pullStreamAddrEdit3"/>
</item>
<item row="2" column="3">
<widget class="QLineEdit" name="pullStreamAddrEdit4"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>推流地址</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,4" columnminimumwidth="0,0">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>服务器地址:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="pushStreamIPCombox"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="saveBtn">
<property name="text">
<string>保存设置</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -1,37 +1,33 @@
#include "videoControl.h"
#include "ui_videoControl.h"
VideoControl::VideoControl(QWidget *parent) :
QWidget(parent),
ui(new Ui::VideoControl)
{
VideoControl::VideoControl(QWidget *parent)
: QWidget(parent), ui(new Ui::VideoControl) {
ui->setupUi(this);
// 其他初始化代码...
ui->pbPlayer->setIcon(QIcon(":/images/playMedio.png"));
ui->pbPlayer->setIconSize(QSize(56,56));
ui->pbPlayer->setFixedSize(64, 64);
ui->pbPlayer->setIconSize(QSize(32, 32));
ui->pbPlayer->setFixedSize(40, 40);
ui->pbPlayer->setObjectName("medio");
// ui->pbPlayer->set
ui->pbStop->setIcon(QIcon(":/images/stop.png"));
ui->pbStop->setIconSize(QSize(42,42));
ui->pbStop->setFixedSize(50, 50);
ui->pbStop->setIconSize(QSize(24, 24));
ui->pbStop->setFixedSize(32, 32);
ui->pbStop->setObjectName("medio");
ui->pbFast->setIcon(QIcon(":/images/playFast.png"));
ui->pbFast->setIconSize(QSize(42,42));
ui->pbFast->setFixedSize(50, 50);
ui->pbFast->setIconSize(QSize(24, 24));
ui->pbFast->setFixedSize(32, 32);
ui->pbFast->setObjectName("medio");
ui->pbSlow->setIcon(QIcon(":/images/playSlow.png"));
ui->pbSlow->setIconSize(QSize(42,42));
ui->pbSlow->setFixedSize(50, 50);
ui->pbSlow->setIconSize(QSize(24, 24));
ui->pbSlow->setFixedSize(32, 32);
ui->pbSlow->setObjectName("medio");
}
VideoControl::~VideoControl()
{
VideoControl::~VideoControl() {
delete ui;
}

@ -6,18 +6,30 @@
<rect>
<x>0</x>
<y>0</y>
<width>713</width>
<height>91</height>
<width>450</width>
<height>53</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,5,1">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -27,17 +39,6 @@
</property>
</spacer>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0">
<property name="spacing">
<number>6</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QPushButton" name="pbSlow">
<property name="text">
@ -78,15 +79,10 @@
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>

@ -0,0 +1,86 @@
#include "WProgressBar.h"
WProgressBar::WProgressBar(QWidget *parent) : QWidget(parent) {
this->setWindowFlags(Qt::FramelessWindowHint); // 隐藏窗口
this->setAttribute(Qt::WA_TranslucentBackground, true); // 窗口透明
}
WProgressBar::~WProgressBar() {}
double WProgressBar::getPos() {
return m_pos;
}
void WProgressBar::slotSetValue(double pos) {
m_pos = pos;
update();
}
void WProgressBar::mousePressEvent(QMouseEvent *ev) {
// double pos = (double)ev->pos().x() / (double)width();
// if (pos >= 1)
// pos = 1;
// if (pos <= 0)
// pos = 0;
// m_pos = pos;
// update();
// qDebug() << "seek pos = " << pos;
// emit sigCustomSliderValueChanged(pos);
}
void WProgressBar::mouseMoveEvent(QMouseEvent *ev) {
double pos = (double)ev->pos().x() / (double)width();
if (pos >= 1) pos = 1;
if (pos <= 0) pos = 0;
m_pos = pos;
update();
}
void WProgressBar::mouseReleaseEvent(QMouseEvent *ev) {
double pos = (double)ev->pos().x() / (double)width();
emit sigCustomSliderValueChanged(pos);
}
void WProgressBar::paintEvent(QPaintEvent *e) {
QWidget::paintEvent(e);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
// 绘制底图矩形
QBrush brush;
brush.setColor(QColor(233, 233, 233));
brush.setStyle(Qt::SolidPattern);
painter.setBrush(brush);
painter.drawRoundedRect(this->rect(), 8, 8);
// 绘制播放进度
QLinearGradient radial;
radial.setStart(0, 0);
radial.setFinalStop(0, 1);
// 设置起始点颜色0表示起始
radial.setColorAt(0, QColor("#87CEFA"));
// 设置终点颜色 1表示终点
radial.setColorAt(1, QColor("#1E90FF"));
// 设置延展方式
radial.setSpread(QGradient::PadSpread);
QPen pen(QBrush("#1E90FF"), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
painter.setPen(pen);
// 设置画刷
painter.setBrush(radial);
QRect rect = this->rect();
rect.setWidth(rect.width() * m_pos);
// 画矩形
painter.drawRoundedRect(rect, 8, 8);
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save