chore: baseline usable version
This commit is contained in:
192
libs/qwindowkit/src/CMakeLists.txt
Normal file
192
libs/qwindowkit/src/CMakeLists.txt
Normal file
@@ -0,0 +1,192 @@
|
||||
qm_import(Preprocess)
|
||||
|
||||
set(QWINDOWKIT_GENERATED_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/../include)
|
||||
set(QWINDOWKIT_BUILD_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/../etc/include)
|
||||
|
||||
set(QWINDOWKIT_ENABLED_TARGETS)
|
||||
set(QWINDOWKIT_ENABLED_SUBDIRECTORIES)
|
||||
|
||||
# ----------------------------------
|
||||
# Configurations
|
||||
# ----------------------------------
|
||||
set(QMSETUP_DEFINITION_SCOPE DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(QMSETUP_DEFINITION_NUMERICAL on)
|
||||
|
||||
qm_add_definition(QWINDOWKIT_FORCE_QT_WINDOW_CONTEXT
|
||||
CONDITION QWINDOWKIT_FORCE_QT_WINDOW_CONTEXT
|
||||
)
|
||||
qm_add_definition(QWINDOWKIT_ENABLE_STYLE_AGENT
|
||||
CONDITION QWINDOWKIT_ENABLE_STYLE_AGENT
|
||||
)
|
||||
qm_add_definition(QWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS
|
||||
CONDITION QWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS
|
||||
)
|
||||
|
||||
qm_generate_config(${QWINDOWKIT_BUILD_INCLUDE_DIR}/QWKCore/qwkconfig.h)
|
||||
|
||||
if(QWINDOWKIT_INSTALL)
|
||||
install(FILES ${QWINDOWKIT_BUILD_INCLUDE_DIR}/QWKCore/qwkconfig.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${QWINDOWKIT_INSTALL_NAME}/QWKCore
|
||||
)
|
||||
endif()
|
||||
|
||||
# ----------------------------------
|
||||
# CMake API
|
||||
# ----------------------------------
|
||||
macro(qwk_add_library _target)
|
||||
set(options AUTOGEN NO_SYNC_INCLUDE NO_WIN_RC)
|
||||
set(oneValueArgs SYNC_INCLUDE_PREFIX PREFIX)
|
||||
set(multiValueArgs SYNC_INCLUDE_OPTIONS)
|
||||
cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(FUNC_AUTOGEN)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
endif()
|
||||
|
||||
if(QWINDOWKIT_BUILD_STATIC)
|
||||
set(_type STATIC)
|
||||
else()
|
||||
set(_type SHARED)
|
||||
endif()
|
||||
|
||||
add_library(${_target} ${_type})
|
||||
|
||||
if(WIN32 AND NOT FUNC_NO_WIN_RC AND(${_type} STREQUAL "SHARED"))
|
||||
qm_add_win_rc(${_target}
|
||||
NAME ${QWINDOWKIT_INSTALL_NAME}
|
||||
DESCRIPTION ${QWINDOWKIT_DESCRIPTION}
|
||||
COPYRIGHT ${QWINDOWKIT_COPYRIGHT}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(FUNC_PREFIX)
|
||||
set(_prefix_option PREFIX ${FUNC_PREFIX})
|
||||
else()
|
||||
set(_prefix_option)
|
||||
endif()
|
||||
|
||||
# Set global definitions
|
||||
qm_export_defines(${_target} ${_prefix_option})
|
||||
|
||||
# Configure target
|
||||
qm_configure_target(${_target} ${FUNC_UNPARSED_ARGUMENTS})
|
||||
|
||||
# Add include directories
|
||||
target_include_directories(${_target} PRIVATE ${QWINDOWKIT_BUILD_INCLUDE_DIR})
|
||||
target_include_directories(${_target} PRIVATE .)
|
||||
|
||||
# Library name
|
||||
if(${_target} MATCHES "^QWK(.+)")
|
||||
set(_name ${CMAKE_MATCH_1})
|
||||
set_target_properties(${_target} PROPERTIES EXPORT_NAME ${_name})
|
||||
else()
|
||||
set(_name ${_target})
|
||||
endif()
|
||||
|
||||
add_library(${QWINDOWKIT_INSTALL_NAME}::${_name} ALIAS ${_target})
|
||||
|
||||
if(FUNC_SYNC_INCLUDE_PREFIX)
|
||||
set(_inc_name ${FUNC_SYNC_INCLUDE_PREFIX})
|
||||
else()
|
||||
set(_inc_name ${_target})
|
||||
endif()
|
||||
|
||||
set(_install_options)
|
||||
|
||||
if(QWINDOWKIT_INSTALL)
|
||||
install(TARGETS ${_target}
|
||||
EXPORT ${QWINDOWKIT_INSTALL_NAME}Targets
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" OPTIONAL
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" OPTIONAL
|
||||
)
|
||||
|
||||
target_include_directories(${_target} PUBLIC
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${QWINDOWKIT_INSTALL_NAME}>"
|
||||
)
|
||||
|
||||
set(_install_options
|
||||
INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}/${QWINDOWKIT_INSTALL_NAME}/${_inc_name}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT FUNC_NO_SYNC_INCLUDE)
|
||||
# Generate a standard include directory in build directory
|
||||
qm_sync_include(. "${QWINDOWKIT_GENERATED_INCLUDE_DIR}/${_inc_name}" ${_install_options}
|
||||
${FUNC_SYNC_INCLUDE_OPTIONS} FORCE
|
||||
)
|
||||
target_include_directories(${_target} PUBLIC
|
||||
"$<BUILD_INTERFACE:${QWINDOWKIT_GENERATED_INCLUDE_DIR}>"
|
||||
)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# ----------------------------------
|
||||
# Main Project
|
||||
# ----------------------------------
|
||||
add_subdirectory(core)
|
||||
|
||||
if(QWINDOWKIT_BUILD_WIDGETS)
|
||||
add_subdirectory(widgets)
|
||||
endif()
|
||||
|
||||
if(QWINDOWKIT_BUILD_QUICK)
|
||||
add_subdirectory(quick)
|
||||
endif()
|
||||
|
||||
# ----------------------------------
|
||||
# Documentation
|
||||
# ----------------------------------
|
||||
if(QWINDOWKIT_BUILD_DOCUMENTATIONS)
|
||||
if(NOT DOXYGEN_EXECUTABLE)
|
||||
find_package(Doxygen REQUIRED)
|
||||
endif()
|
||||
|
||||
set(_install_options)
|
||||
|
||||
if(QWINDOWKIT_INSTALL)
|
||||
set(_install_options INSTALL_DIR share/doc/${QWINDOWKIT_INSTALL_NAME})
|
||||
endif()
|
||||
|
||||
set(_doc_targets ${QWINDOWKIT_ENABLED_TARGETS})
|
||||
|
||||
set(QWINDOWKIT_DOXYGEN_TAGFILE
|
||||
${CMAKE_BUILD_SHARE_DIR}/doc/${QWINDOWKIT_INSTALL_NAME}/${QWINDOWKIT_INSTALL_NAME}_tagfile.xml
|
||||
CACHE FILEPATH "QWindowKit doxygen tag file path" FORCE
|
||||
)
|
||||
|
||||
qm_import(Doxygen)
|
||||
qm_setup_doxygen(QWindowKit_RunDoxygen
|
||||
NAME "QWindowKit"
|
||||
DESCRIPTION "${QWINDOWKIT_DESCRIPTION}"
|
||||
MDFILE ../README.md
|
||||
OUTPUT_DIR ${CMAKE_BUILD_SHARE_DIR}/doc/${QWINDOWKIT_INSTALL_NAME}
|
||||
INPUT ${QWINDOWKIT_ENABLED_SUBDIRECTORIES}
|
||||
TARGETS ${_doc_targets}
|
||||
DEPENDS ${_doc_targets}
|
||||
NO_EXPAND_MACROS Q_OBJECT Q_GADGET Q_DECLARE_TR_FUNCTIONS
|
||||
COMPILE_DEFINITIONS Q_SIGNALS=Q_SIGNALS Q_SLOTS=Q_SLOTS
|
||||
GENERATE_TAGFILE "${QWINDOWKIT_DOXYGEN_TAGFILE}"
|
||||
${_install_options}
|
||||
)
|
||||
endif()
|
||||
|
||||
# ----------------------------------
|
||||
# Install
|
||||
# ----------------------------------
|
||||
if(QWINDOWKIT_INSTALL)
|
||||
qm_basic_install(
|
||||
NAME ${QWINDOWKIT_INSTALL_NAME}
|
||||
VERSION ${QWINDOWKIT_VERSION}
|
||||
INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${QWINDOWKIT_INSTALL_NAME}
|
||||
CONFIG_TEMPLATE "${QWINDOWKIT_INSTALL_NAME}Config.cmake.in"
|
||||
NAMESPACE ${QWINDOWKIT_INSTALL_NAME}::
|
||||
EXPORT ${QWINDOWKIT_INSTALL_NAME}Targets
|
||||
WRITE_CONFIG_OPTIONS NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||
)
|
||||
|
||||
# Install shared files
|
||||
include("../share/install.cmake")
|
||||
endif()
|
||||
18
libs/qwindowkit/src/QWindowKitConfig.cmake.in
Normal file
18
libs/qwindowkit/src/QWindowKitConfig.cmake.in
Normal file
@@ -0,0 +1,18 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
find_dependency(QT NAMES Qt6 Qt5 COMPONENTS Core Gui REQUIRED)
|
||||
find_dependency(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui REQUIRED)
|
||||
|
||||
if ("Widgets" IN_LIST QWindowKit_FIND_COMPONENTS)
|
||||
find_dependency(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
|
||||
find_dependency(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
|
||||
endif()
|
||||
|
||||
if ("Quick" IN_LIST QWindowKit_FIND_COMPONENTS)
|
||||
find_dependency(QT NAMES Qt6 Qt5 COMPONENTS Quick REQUIRED)
|
||||
find_dependency(Qt${QT_VERSION_MAJOR} COMPONENTS Quick REQUIRED)
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/QWindowKitTargets.cmake")
|
||||
102
libs/qwindowkit/src/core/CMakeLists.txt
Normal file
102
libs/qwindowkit/src/core/CMakeLists.txt
Normal file
@@ -0,0 +1,102 @@
|
||||
project(QWKCore
|
||||
VERSION ${QWINDOWKIT_VERSION}
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
set(_src
|
||||
qwkglobal.h
|
||||
qwkglobal_p.h
|
||||
qwkglobal.cpp
|
||||
windowagentbase.h
|
||||
windowagentbase_p.h
|
||||
windowagentbase.cpp
|
||||
windowitemdelegate_p.h
|
||||
windowitemdelegate.cpp
|
||||
kernel/nativeeventfilter_p.h
|
||||
kernel/nativeeventfilter.cpp
|
||||
kernel/sharedeventfilter_p.h
|
||||
kernel/sharedeventfilter.cpp
|
||||
kernel/winidchangeeventfilter_p.h
|
||||
kernel/winidchangeeventfilter.cpp
|
||||
shared/systemwindow_p.h
|
||||
contexts/abstractwindowcontext_p.h
|
||||
contexts/abstractwindowcontext.cpp
|
||||
contexts/qtwindowcontext_p.h
|
||||
contexts/qtwindowcontext.cpp
|
||||
)
|
||||
|
||||
set(_links_private)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND _src
|
||||
qwindowkit_windows.h
|
||||
qwindowkit_windows.cpp
|
||||
shared/qwkwindowsextra_p.h
|
||||
shared/windows10borderhandler_p.h
|
||||
)
|
||||
elseif(APPLE)
|
||||
else()
|
||||
list(APPEND _src
|
||||
qwindowkit_linux.h
|
||||
qwindowkit_linux.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND _src
|
||||
contexts/win32windowcontext_p.h
|
||||
contexts/win32windowcontext.cpp
|
||||
)
|
||||
list(APPEND _links_private uxtheme)
|
||||
elseif(APPLE)
|
||||
list(APPEND _src
|
||||
contexts/cocoawindowcontext_p.h
|
||||
contexts/cocoawindowcontext.mm
|
||||
)
|
||||
list(APPEND _links_private
|
||||
"-framework Foundation"
|
||||
"-framework Cocoa"
|
||||
"-framework AppKit"
|
||||
)
|
||||
else()
|
||||
list(APPEND _src
|
||||
contexts/linuxx11context_p.h
|
||||
contexts/linuxx11context.cpp
|
||||
contexts/linuxwaylandcontext_p.h
|
||||
contexts/linuxwaylandcontext.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(_sync_include_options)
|
||||
|
||||
if(QWINDOWKIT_ENABLE_STYLE_AGENT)
|
||||
list(APPEND _src
|
||||
style/styleagent.h
|
||||
style/styleagent_p.h
|
||||
style/styleagent.cpp
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND _src style/styleagent_win.cpp)
|
||||
elseif(APPLE)
|
||||
list(APPEND _src style/styleagent_mac.mm)
|
||||
else()
|
||||
list(APPEND _src style/styleagent_linux.cpp)
|
||||
endif()
|
||||
else()
|
||||
list(APPEND _sync_include_options EXCLUDE "src/core/style/\\.+")
|
||||
endif()
|
||||
|
||||
qwk_add_library(${PROJECT_NAME} AUTOGEN
|
||||
SOURCES ${_src}
|
||||
FEATURES cxx_std_17
|
||||
LINKS_PRIVATE ${_links_private}
|
||||
QT_LINKS Core Gui
|
||||
QT_INCLUDE_PRIVATE Core Gui
|
||||
INCLUDE_PRIVATE *
|
||||
PREFIX QWK_CORE
|
||||
SYNC_INCLUDE_OPTIONS ${_sync_include_options}
|
||||
)
|
||||
|
||||
set(QWINDOWKIT_ENABLED_TARGETS ${QWINDOWKIT_ENABLED_TARGETS} ${PROJECT_NAME} PARENT_SCOPE)
|
||||
set(QWINDOWKIT_ENABLED_SUBDIRECTORIES ${QWINDOWKIT_ENABLED_SUBDIRECTORIES} core PARENT_SCOPE)
|
||||
298
libs/qwindowkit/src/core/contexts/abstractwindowcontext.cpp
Normal file
298
libs/qwindowkit/src/core/contexts/abstractwindowcontext.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
|
||||
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "abstractwindowcontext_p.h"
|
||||
|
||||
#include <QtGui/QPen>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtGui/QScreen>
|
||||
|
||||
#include "qwkglobal_p.h"
|
||||
|
||||
namespace QWK {
|
||||
|
||||
AbstractWindowContext::AbstractWindowContext() = default;
|
||||
|
||||
AbstractWindowContext::~AbstractWindowContext() = default;
|
||||
|
||||
void AbstractWindowContext::setup(QObject *host, WindowItemDelegate *delegate) {
|
||||
if (m_host || !host || !delegate) {
|
||||
return;
|
||||
}
|
||||
m_host = host;
|
||||
m_delegate.reset(delegate);
|
||||
m_winIdChangeEventFilter.reset(delegate->createWinIdEventFilter(host, this));
|
||||
notifyWinIdChange();
|
||||
}
|
||||
|
||||
bool AbstractWindowContext::setHitTestVisible(QObject *obj, bool visible) {
|
||||
Q_ASSERT(obj);
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
m_hitTestVisibleItems.removeAll(nullptr);
|
||||
m_hitTestVisibleItems.removeAll(obj);
|
||||
m_hitTestVisibleItems.append(obj);
|
||||
} else {
|
||||
for (auto &item : m_hitTestVisibleItems) {
|
||||
if (item == obj) {
|
||||
item = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractWindowContext::setSystemButton(WindowAgentBase::SystemButton button,
|
||||
QObject *obj) {
|
||||
Q_ASSERT(button != WindowAgentBase::Unknown);
|
||||
if (button == WindowAgentBase::Unknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto org = m_systemButtons[button];
|
||||
if (org == obj) {
|
||||
return false;
|
||||
}
|
||||
m_systemButtons[button] = obj;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractWindowContext::setTitleBar(QObject *item) {
|
||||
Q_ASSERT(item);
|
||||
auto org = m_titleBar;
|
||||
if (org == item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (org) {
|
||||
// Since the title bar is changed, all items inside it should be dereferenced right away
|
||||
removeSystemButtonsAndHitTestItems();
|
||||
}
|
||||
m_titleBar = item;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
void AbstractWindowContext::setSystemButtonAreaCallback(const ScreenRectCallback &callback) {
|
||||
m_systemButtonAreaCallback = callback;
|
||||
virtual_hook(SystemButtonAreaChangedHook, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool AbstractWindowContext::isInSystemButtons(const QPoint &pos,
|
||||
WindowAgentBase::SystemButton *button) const {
|
||||
*button = WindowAgentBase::Unknown;
|
||||
for (int i = WindowAgentBase::WindowIcon; i <= WindowAgentBase::Close; ++i) {
|
||||
auto currentButton = m_systemButtons[i];
|
||||
if (!currentButton || !m_delegate->isVisible(currentButton) || !m_delegate->isEnabled(currentButton)) {
|
||||
continue;
|
||||
}
|
||||
if (m_delegate->mapGeometryToScene(currentButton).contains(pos)) {
|
||||
*button = static_cast<WindowAgentBase::SystemButton>(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AbstractWindowContext::isInTitleBarDraggableArea(const QPoint &pos) const {
|
||||
if (!m_titleBar) {
|
||||
// There's no title bar at all, the mouse will always be in the client area.
|
||||
return false;
|
||||
}
|
||||
if (!m_delegate->isVisible(m_titleBar) || !m_delegate->isEnabled(m_titleBar)) {
|
||||
// The title bar is hidden or disabled for some reason, treat it as there's
|
||||
// no title bar.
|
||||
return false;
|
||||
}
|
||||
QRect windowRect = {QPoint(0, 0), m_windowHandle->size()};
|
||||
QRect titleBarRect = m_delegate->mapGeometryToScene(m_titleBar);
|
||||
if (!titleBarRect.intersects(windowRect)) {
|
||||
// The title bar is totally outside the window for some reason,
|
||||
// also treat it as there's no title bar.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!titleBarRect.contains(pos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WindowAgentBase::SystemButton button;
|
||||
if (isInSystemButtons(pos, &button)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto &&item : std::as_const(m_hitTestVisibleItems)) {
|
||||
if (item && m_delegate->isVisible(item) &&
|
||||
m_delegate->mapGeometryToScene(item).contains(pos)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString AbstractWindowContext::key() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
QWK_USED static constexpr const struct {
|
||||
const quint32 activeLight = MAKE_RGBA_COLOR(210, 233, 189, 226);
|
||||
const quint32 activeDark = MAKE_RGBA_COLOR(177, 205, 190, 240);
|
||||
const quint32 inactiveLight = MAKE_RGBA_COLOR(193, 195, 211, 203);
|
||||
const quint32 inactiveDark = MAKE_RGBA_COLOR(240, 240, 250, 255);
|
||||
} kSampleColorSet;
|
||||
|
||||
void AbstractWindowContext::virtual_hook(int id, void *data) {
|
||||
switch (id) {
|
||||
case CentralizeHook: {
|
||||
if (!m_windowId)
|
||||
return;
|
||||
|
||||
QRect windowGeometry = m_delegate->getGeometry(m_host);
|
||||
QRect screenGeometry = m_windowHandle->screen()->geometry();
|
||||
int x = (screenGeometry.width() - windowGeometry.width()) / 2;
|
||||
int y = (screenGeometry.height() - windowGeometry.height()) / 2;
|
||||
QPoint pos(x, y);
|
||||
pos += screenGeometry.topLeft();
|
||||
m_delegate->setGeometry(m_host, QRect(pos, windowGeometry.size()));
|
||||
return;
|
||||
}
|
||||
|
||||
case RaiseWindowHook: {
|
||||
if (!m_windowId)
|
||||
return;
|
||||
|
||||
m_delegate->setWindowVisible(m_host, true);
|
||||
Qt::WindowStates state = m_delegate->getWindowState(m_host);
|
||||
if (state & Qt::WindowMinimized) {
|
||||
m_delegate->setWindowState(m_host, state & ~Qt::WindowMinimized);
|
||||
}
|
||||
m_delegate->bringWindowToTop(m_host);
|
||||
return;
|
||||
}
|
||||
|
||||
case DefaultColorsHook: {
|
||||
auto &map = *static_cast<QMap<QString, QColor> *>(data);
|
||||
map.clear();
|
||||
map.insert(QStringLiteral("activeLight"), kSampleColorSet.activeLight);
|
||||
map.insert(QStringLiteral("activeDark"), kSampleColorSet.activeDark);
|
||||
map.insert(QStringLiteral("inactiveLight"), kSampleColorSet.inactiveLight);
|
||||
map.insert(QStringLiteral("inactiveDark"), kSampleColorSet.inactiveDark);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractWindowContext::showSystemMenu(const QPoint &pos) {
|
||||
virtual_hook(ShowSystemMenuHook, &const_cast<QPoint &>(pos));
|
||||
}
|
||||
|
||||
void AbstractWindowContext::notifyWinIdChange() {
|
||||
auto oldWinId = m_windowId;
|
||||
m_windowId = m_winIdChangeEventFilter->winId();
|
||||
|
||||
// In Qt6, after QWidget::close() is called, the related QWindow's all surfaces and the
|
||||
// platform window will be removed, and the WinId will be set to 0. After that, when the
|
||||
// QWidget is shown again, the whole things will be recreated again.
|
||||
// As a result, we must update our WindowContext each time the WinId changes.
|
||||
if (m_windowHandle) {
|
||||
removeEventFilter(m_windowHandle);
|
||||
}
|
||||
m_windowHandle = m_delegate->hostWindow(m_host);
|
||||
if (m_windowHandle) {
|
||||
m_windowHandle->installEventFilter(this);
|
||||
}
|
||||
|
||||
if (oldWinId != m_windowId) {
|
||||
winIdChanged(m_windowId, oldWinId);
|
||||
|
||||
if (m_windowId) {
|
||||
// Refresh window attributes
|
||||
for (auto it = m_windowAttributesOrder.begin();
|
||||
it != m_windowAttributesOrder.end();) {
|
||||
if (!windowAttributeChanged(it->first, it->second, {})) {
|
||||
m_windowAttributes.remove(it->first);
|
||||
it = m_windowAttributesOrder.erase(it);
|
||||
continue;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Send to shared dispatchers
|
||||
QEvent e(QEvent::WinIdChange);
|
||||
sharedDispatch(m_host, &e);
|
||||
}
|
||||
}
|
||||
|
||||
QVariant AbstractWindowContext::windowAttribute(const QString &key) const {
|
||||
auto it = m_windowAttributes.find(key);
|
||||
if (it == m_windowAttributes.end()) {
|
||||
return {};
|
||||
}
|
||||
return it.value()->second;
|
||||
}
|
||||
|
||||
bool AbstractWindowContext::setWindowAttribute(const QString &key, const QVariant &attribute) {
|
||||
auto it = m_windowAttributes.find(key);
|
||||
if (it == m_windowAttributes.end()) {
|
||||
if (!attribute.isValid()) {
|
||||
return true;
|
||||
}
|
||||
if (m_windowId && !windowAttributeChanged(key, attribute, {})) {
|
||||
return false;
|
||||
}
|
||||
m_windowAttributes.insert(
|
||||
key, m_windowAttributesOrder.insert(m_windowAttributesOrder.end(),
|
||||
std::make_pair(key, attribute)));
|
||||
return true;
|
||||
}
|
||||
|
||||
auto &listIter = it.value();
|
||||
auto &oldAttr = listIter->second;
|
||||
if (m_windowId && !windowAttributeChanged(key, attribute, oldAttr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attribute.isValid()) {
|
||||
oldAttr = attribute;
|
||||
m_windowAttributesOrder.splice(m_windowAttributesOrder.end(), m_windowAttributesOrder,
|
||||
listIter);
|
||||
} else {
|
||||
m_windowAttributesOrder.erase(listIter);
|
||||
m_windowAttributes.erase(it);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractWindowContext::eventFilter(QObject *obj, QEvent *event) {
|
||||
if (obj == m_windowHandle && sharedDispatch(obj, event)) {
|
||||
return true;
|
||||
}
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
bool AbstractWindowContext::windowAttributeChanged(const QString &key,
|
||||
const QVariant &attribute,
|
||||
const QVariant &oldAttribute) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void AbstractWindowContext::removeSystemButtonsAndHitTestItems() {
|
||||
for (auto &button : m_systemButtons) {
|
||||
if (!button) {
|
||||
continue;
|
||||
}
|
||||
button = nullptr;
|
||||
}
|
||||
m_hitTestVisibleItems.clear();
|
||||
}
|
||||
|
||||
}
|
||||
172
libs/qwindowkit/src/core/contexts/abstractwindowcontext_p.h
Normal file
172
libs/qwindowkit/src/core/contexts/abstractwindowcontext_p.h
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
|
||||
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
|
||||
// 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{};
|
||||
|
||||
QVector<QPointer<QObject>> m_hitTestVisibleItems;
|
||||
#ifdef Q_OS_MAC
|
||||
ScreenRectCallback m_systemButtonAreaCallback;
|
||||
#endif
|
||||
|
||||
QPointer<QObject> m_titleBar{};
|
||||
std::array<QPointer<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();
|
||||
};
|
||||
|
||||
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(const_cast<QObject *>(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
|
||||
831
libs/qwindowkit/src/core/contexts/cocoawindowcontext.mm
Normal file
831
libs/qwindowkit/src/core/contexts/cocoawindowcontext.mm
Normal file
@@ -0,0 +1,831 @@
|
||||
// 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);
|
||||
}
|
||||
|
||||
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);
|
||||
auto nsview = [nswindow contentView];
|
||||
if (auto proxy = QWK::g_proxyList->value(reinterpret_cast<WId>(nsview))) {
|
||||
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
|
||||
QWK_NSWindowDelegate::WillEnterFullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidEnterFullScreen:(NSNotification *)notification {
|
||||
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
|
||||
auto nsview = [nswindow contentView];
|
||||
if (auto proxy = QWK::g_proxyList->value(reinterpret_cast<WId>(nsview))) {
|
||||
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
|
||||
QWK_NSWindowDelegate::DidEnterFullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowWillExitFullScreen:(NSNotification *)notification {
|
||||
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
|
||||
auto nsview = [nswindow contentView];
|
||||
if (auto proxy = QWK::g_proxyList->value(reinterpret_cast<WId>(nsview))) {
|
||||
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
|
||||
QWK_NSWindowDelegate::WillExitFullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidExitFullScreen:(NSNotification *)notification {
|
||||
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
|
||||
auto nsview = [nswindow contentView];
|
||||
if (auto proxy = QWK::g_proxyList->value(reinterpret_cast<WId>(nsview))) {
|
||||
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
|
||||
QWK_NSWindowDelegate::DidExitFullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidResize:(NSNotification *)notification {
|
||||
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
|
||||
auto nsview = [nswindow contentView];
|
||||
if (auto proxy = QWK::g_proxyList->value(reinterpret_cast<WId>(nsview))) {
|
||||
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
|
||||
QWK_NSWindowDelegate::DidResize);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface QWK_NSViewObserver : NSObject
|
||||
- (instancetype)initWithProxy:(QWK::NSWindowProxy*)proxy;
|
||||
@end
|
||||
|
||||
//
|
||||
// Objective C++ End
|
||||
//
|
||||
|
||||
namespace QWK {
|
||||
|
||||
struct NSWindowProxy : public QWK_NSWindowDelegate {
|
||||
enum class BlurMode {
|
||||
Dark,
|
||||
Light,
|
||||
None,
|
||||
};
|
||||
|
||||
NSWindowProxy(NSView *macView) {
|
||||
nsview = macView;
|
||||
|
||||
observer = [[QWK_NSViewObserver alloc] initWithProxy:this];
|
||||
[nsview addObserver:observer
|
||||
forKeyPath:@"window"
|
||||
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
|
||||
context:nil];
|
||||
}
|
||||
|
||||
~NSWindowProxy() override {
|
||||
[nsview removeObserver:observer forKeyPath:@"window"];
|
||||
[observer release];
|
||||
}
|
||||
|
||||
// Delegate
|
||||
void windowEvent(NSEventType eventType) override {
|
||||
switch (eventType) {
|
||||
case WillExitFullScreen: {
|
||||
auto nswindow = [nsview window];
|
||||
nswindow.titleVisibility = NSWindowTitleHidden;
|
||||
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;
|
||||
}
|
||||
|
||||
case DidEnterFullScreen: {
|
||||
auto nswindow = [nsview window];
|
||||
nswindow.titleVisibility = NSWindowTitleVisible;
|
||||
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() {
|
||||
if (!screenRectCallback || !systemButtonVisible) {
|
||||
return;
|
||||
}
|
||||
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 = nsview.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() {
|
||||
auto nswindow = [nsview window];
|
||||
if (!nswindow) {
|
||||
return {nullptr, nullptr, nullptr};
|
||||
}
|
||||
NSButton *closeBtn = [nswindow standardWindowButton:NSWindowCloseButton];
|
||||
NSButton *minimizeBtn = [nswindow standardWindowButton:NSWindowMiniaturizeButton];
|
||||
NSButton *zoomBtn = [nswindow standardWindowButton:NSWindowZoomButton];
|
||||
return {closeBtn, minimizeBtn, zoomBtn};
|
||||
}
|
||||
|
||||
inline int titleBarHeight() const {
|
||||
auto nswindow = [nsview window];
|
||||
if (!nswindow) {
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
for (NSView *subview in [[nsview 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) {
|
||||
auto nswindow = [nsview window];
|
||||
if (!nswindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsview.wantsLayer = YES;
|
||||
nswindow.styleMask |= NSWindowStyleMaskResizable;
|
||||
if (visible) {
|
||||
nswindow.styleMask &= ~NSWindowStyleMaskFullSizeContentView;
|
||||
} else {
|
||||
nswindow.styleMask |= NSWindowStyleMaskFullSizeContentView;
|
||||
}
|
||||
nswindow.titlebarAppearsTransparent = (visible ? NO : YES);
|
||||
nswindow.titleVisibility = (visible || (nswindow.styleMask & NSWindowStyleMaskFullScreen) ? 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) {
|
||||
auto nswindow = reinterpret_cast<NSWindow *>(obj);
|
||||
auto nsview = [nswindow contentView];
|
||||
if (g_proxyList->contains(reinterpret_cast<WId>(nsview))) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (oldCanBecomeKeyWindow) {
|
||||
return oldCanBecomeKeyWindow(obj, sel);
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
static BOOL canBecomeMainWindow(id obj, SEL sel) {
|
||||
auto nswindow = reinterpret_cast<NSWindow *>(obj);
|
||||
auto nsview = [nswindow contentView];
|
||||
if (g_proxyList->contains(reinterpret_cast<WId>(nsview))) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (oldCanBecomeMainWindow) {
|
||||
return oldCanBecomeMainWindow(obj, sel);
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
static void setStyleMask(id obj, SEL sel, NSWindowStyleMask styleMask) {
|
||||
auto nswindow = reinterpret_cast<NSWindow *>(obj);
|
||||
auto nsview = [nswindow contentView];
|
||||
if (g_proxyList->contains(reinterpret_cast<WId>(nsview))) {
|
||||
styleMask |= NSWindowStyleMaskFullSizeContentView;
|
||||
}
|
||||
|
||||
if (oldSetStyleMask) {
|
||||
oldSetStyleMask(obj, sel, styleMask);
|
||||
}
|
||||
}
|
||||
|
||||
static void setTitlebarAppearsTransparent(id obj, SEL sel, BOOL transparent) {
|
||||
auto nswindow = reinterpret_cast<NSWindow *>(obj);
|
||||
auto nsview = [nswindow contentView];
|
||||
if (g_proxyList->contains(reinterpret_cast<WId>(nsview))) {
|
||||
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)
|
||||
|
||||
NSView *nsview = nil;
|
||||
QWK_NSViewObserver* observer = 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()) {
|
||||
NSView *nsview = reinterpret_cast<NSView *>(windowId);
|
||||
const auto proxy = new NSWindowProxy(nsview);
|
||||
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 (!(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
|
||||
const auto proxy = ensureWindowProxy(winId);
|
||||
if (proxy) {
|
||||
proxy->setSystemButtonVisible(!windowAttribute(QStringLiteral("no-system-buttons")).toBool());
|
||||
proxy->setScreenRectCallback(m_systemButtonAreaCallback);
|
||||
proxy->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 (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
if (attribute.type() != QVariant::Bool)
|
||||
#else
|
||||
if (attribute.typeId() != QMetaType::Type::Bool)
|
||||
#endif
|
||||
return false;
|
||||
ensureWindowProxy(m_windowId)->setSystemButtonVisible(!attribute.toBool());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == QStringLiteral("blur-effect")) {
|
||||
auto mode = NSWindowProxy::BlurMode::None;
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
if (attribute.type() == QVariant::Bool) {
|
||||
#else
|
||||
if (attribute.typeId() == QMetaType::Type::Bool) {
|
||||
#endif
|
||||
if (attribute.toBool()) {
|
||||
NSString *osxMode =
|
||||
[[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
|
||||
mode = [osxMode isEqualToString:@"Dark"] ? NSWindowProxy::BlurMode::Dark
|
||||
: NSWindowProxy::BlurMode::Light;
|
||||
}
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
} else if (attribute.type() == QVariant::String) {
|
||||
#else
|
||||
} else if (attribute.typeId() == QMetaType::Type::QString) {
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@implementation QWK_NSViewObserver {
|
||||
QWK::NSWindowProxy* _proxy; // Weak reference
|
||||
}
|
||||
|
||||
- (instancetype)initWithProxy:(QWK::NSWindowProxy*)proxy {
|
||||
if (self = [super init]) {
|
||||
_proxy = proxy;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// Using QEvent::Show to call setSystemTitleBarVisible/updateSystemButtonRect could also work,
|
||||
// but observing the window property change via KVO provides more immediate notification when
|
||||
// the NSWindow becomes available, making this approach more natural and reliable.
|
||||
- (void)observeValueForKeyPath:(NSString*)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary*)change
|
||||
context:(void*)context {
|
||||
if ([keyPath isEqualToString:@"window"]) {
|
||||
NSWindow* newWindow = change[NSKeyValueChangeNewKey];
|
||||
// NSWindow* oldWindow = change[NSKeyValueChangeOldKey];
|
||||
if (newWindow) {
|
||||
_proxy->setSystemTitleBarVisible(false);
|
||||
_proxy->updateSystemButtonRect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
43
libs/qwindowkit/src/core/contexts/cocoawindowcontext_p.h
Normal file
43
libs/qwindowkit/src/core/contexts/cocoawindowcontext_p.h
Normal file
@@ -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
|
||||
67
libs/qwindowkit/src/core/contexts/linuxwaylandcontext.cpp
Normal file
67
libs/qwindowkit/src/core/contexts/linuxwaylandcontext.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
|
||||
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
|
||||
// Copyright (C) 2025-2027 Wing-summer (wingsummer)
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "linuxwaylandcontext_p.h"
|
||||
|
||||
#include "qwindowkit_linux.h"
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
#include <QtGui/qpa/qplatformnativeinterface.h>
|
||||
|
||||
namespace QWK {
|
||||
static inline void xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel,
|
||||
struct wl_seat *seat, uint32_t serial,
|
||||
int32_t x, int32_t y) {
|
||||
constexpr auto XDG_TOPLEVEL_SHOW_WINDOW_MENU = 4;
|
||||
const auto &api = QWK::Private::waylandAPI();
|
||||
Q_ASSERT(api.isValid());
|
||||
api.wl_proxy_marshal_flags(
|
||||
reinterpret_cast<struct wl_proxy *>(xdg_toplevel), XDG_TOPLEVEL_SHOW_WINDOW_MENU,
|
||||
nullptr, api.wl_proxy_get_version(reinterpret_cast<struct wl_proxy *>(xdg_toplevel)), 0,
|
||||
seat, serial, x, y);
|
||||
}
|
||||
|
||||
LinuxWaylandContext::LinuxWaylandContext() : QtWindowContext() {
|
||||
}
|
||||
|
||||
LinuxWaylandContext::~LinuxWaylandContext() = default;
|
||||
|
||||
QString LinuxWaylandContext::key() const {
|
||||
return QStringLiteral("wayland");
|
||||
}
|
||||
|
||||
void LinuxWaylandContext::virtual_hook(int id, void *data) {
|
||||
if (id == ShowSystemMenuHook) {
|
||||
auto *waylandApp = qApp->nativeInterface<QNativeInterface::QWaylandApplication>();
|
||||
if (!waylandApp) {
|
||||
return;
|
||||
}
|
||||
uint serial = waylandApp->lastInputSerial();
|
||||
wl_seat *seat = waylandApp->lastInputSeat();
|
||||
if (serial == 0 || !seat) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto toplevel = static_cast<xdg_toplevel *>(
|
||||
QGuiApplication::platformNativeInterface()->nativeResourceForWindow(
|
||||
"xdg_toplevel", m_windowHandle));
|
||||
if (!toplevel) {
|
||||
return;
|
||||
}
|
||||
auto pos = static_cast<const QPoint *>(data);
|
||||
xdg_toplevel_show_window_menu(toplevel, seat, serial, pos->x(), pos->y());
|
||||
|
||||
wl_display *d = waylandApp->display();
|
||||
if (d) {
|
||||
const auto &api = QWK::Private::waylandAPI();
|
||||
Q_ASSERT(api.isValid());
|
||||
api.wl_display_flush(d);
|
||||
}
|
||||
} else {
|
||||
QtWindowContext::virtual_hook(id, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
37
libs/qwindowkit/src/core/contexts/linuxwaylandcontext_p.h
Normal file
37
libs/qwindowkit/src/core/contexts/linuxwaylandcontext_p.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
|
||||
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
|
||||
// Copyright (C) 2025-2027 Wing-summer (wingsummer)
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
||||
#ifndef LINUXWAYLANDCONTEXT_P_H
|
||||
#define LINUXWAYLANDCONTEXT_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 "qtwindowcontext_p.h"
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
namespace QWK {
|
||||
|
||||
class LinuxWaylandContext : public QtWindowContext {
|
||||
Q_OBJECT
|
||||
public:
|
||||
LinuxWaylandContext();
|
||||
~LinuxWaylandContext() override;
|
||||
|
||||
QString key() const override;
|
||||
void virtual_hook(int id, void *data) override;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
#endif // LINUXWAYLANDCONTEXT_P_H
|
||||
194
libs/qwindowkit/src/core/contexts/linuxx11context.cpp
Normal file
194
libs/qwindowkit/src/core/contexts/linuxx11context.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
|
||||
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
|
||||
// Copyright (C) 2025-2027 Wing-summer (wingsummer)
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
||||
#include "linuxx11context_p.h"
|
||||
|
||||
#include "qwindowkit_linux.h"
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
// copy from X11 library and simplify it
|
||||
typedef struct _XExtData XExtData;
|
||||
struct ScreenFormat;
|
||||
struct Depth;
|
||||
struct Visual;
|
||||
typedef XID Colormap;
|
||||
typedef struct {
|
||||
XExtData *ext_data; /* hook for extension to hang data */
|
||||
struct _XDisplay *display; /* back pointer to display structure */
|
||||
Window root; /* Root window id. */
|
||||
int width, height; /* width and height of screen */
|
||||
int mwidth, mheight; /* width and height of in millimeters */
|
||||
int ndepths; /* number of depths possible */
|
||||
Depth *depths; /* list of allowable depths on the screen */
|
||||
int root_depth; /* bits per pixel */
|
||||
Visual *root_visual; /* root visual */
|
||||
|
||||
// typedef struct _XGC
|
||||
// #ifdef XLIB_ILLEGAL_ACCESS
|
||||
// {
|
||||
// XExtData *ext_data; /* hook for extension to hang data */
|
||||
// GContext gid; /* protocol ID for graphics context */
|
||||
// /* there is more to this structure, but it is private to Xlib */
|
||||
// }
|
||||
// #endif
|
||||
// *GC;
|
||||
void * /*GC*/ default_gc; /* GC for the root root visual */
|
||||
|
||||
Colormap cmap; /* default color map */
|
||||
unsigned long white_pixel;
|
||||
unsigned long black_pixel; /* White and Black pixel values */
|
||||
int max_maps, min_maps; /* max and min color maps */
|
||||
int backing_store; /* Never, WhenMapped, Always */
|
||||
Bool save_unders;
|
||||
long root_input_mask; /* initial root input mask */
|
||||
} Screen;
|
||||
typedef char *XPointer;
|
||||
typedef struct {
|
||||
XExtData *ext_data; /* hook for extension to hang data */
|
||||
struct _XPrivate *private1;
|
||||
int fd; /* Network socket. */
|
||||
int private2;
|
||||
int proto_major_version; /* major version of server's X protocol */
|
||||
int proto_minor_version; /* minor version of servers X protocol */
|
||||
char *vendor; /* vendor of the server hardware */
|
||||
XID private3;
|
||||
XID private4;
|
||||
XID private5;
|
||||
int private6;
|
||||
XID (*resource_alloc)(/* allocator function */
|
||||
struct _XDisplay *);
|
||||
int byte_order; /* screen byte order, LSBFirst, MSBFirst */
|
||||
int bitmap_unit; /* padding and data requirements */
|
||||
int bitmap_pad; /* padding requirements on bitmaps */
|
||||
int bitmap_bit_order; /* LeastSignificant or MostSignificant */
|
||||
int nformats; /* number of pixmap formats in list */
|
||||
ScreenFormat *pixmap_format; /* pixmap format list */
|
||||
int private8;
|
||||
int release; /* release of the server */
|
||||
struct _XPrivate *private9, *private10;
|
||||
int qlen; /* Length of input event queue */
|
||||
unsigned long last_request_read; /* seq number of last event read */
|
||||
unsigned long request; /* sequence number of last request. */
|
||||
XPointer private11;
|
||||
XPointer private12;
|
||||
XPointer private13;
|
||||
XPointer private14;
|
||||
unsigned max_request_size; /* maximum number 32 bit words in request*/
|
||||
struct _XrmHashBucketRec *db;
|
||||
int (*private15)(struct _XDisplay *);
|
||||
char *display_name; /* "host:display" string used on this connect*/
|
||||
int default_screen; /* default screen for operations */
|
||||
int nscreens; /* number of screens on this server*/
|
||||
Screen *screens; /* pointer to list of screens */
|
||||
unsigned long motion_buffer; /* size of motion buffer */
|
||||
unsigned long private16;
|
||||
int min_keycode; /* minimum defined keycode */
|
||||
int max_keycode; /* maximum defined keycode */
|
||||
XPointer private17;
|
||||
XPointer private18;
|
||||
int private19;
|
||||
char *xdefaults; /* contents of defaults from server */
|
||||
/* there is more to this structure, but it is private to Xlib */
|
||||
} *_XPrivDisplay;
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
unsigned long serial; /* # of last request processed by server */
|
||||
Bool send_event; /* true if this came from a SendEvent request */
|
||||
Display *display; /* Display the event was read from */
|
||||
Window window;
|
||||
Atom message_type;
|
||||
int format;
|
||||
union {
|
||||
char b[20];
|
||||
short s[10];
|
||||
long l[5];
|
||||
} data;
|
||||
} XClientMessageEvent;
|
||||
|
||||
/*
|
||||
* this union is defined so Xlib can always use the same sized
|
||||
* event structure internally, to avoid memory fragmentation.
|
||||
*/
|
||||
union _XEvent {
|
||||
// delete so many members but size unchanged because of pad
|
||||
XClientMessageEvent xclient;
|
||||
long pad[24];
|
||||
};
|
||||
|
||||
#define ScreenOfDisplay(dpy, scr) (&((_XPrivDisplay) (dpy))->screens[scr])
|
||||
#define DefaultScreen(dpy) (((_XPrivDisplay) (dpy))->default_screen)
|
||||
#define DefaultRootWindow(dpy) (ScreenOfDisplay(dpy, DefaultScreen(dpy))->root)
|
||||
|
||||
namespace QWK {
|
||||
|
||||
LinuxX11Context::LinuxX11Context() : QtWindowContext() {
|
||||
}
|
||||
|
||||
LinuxX11Context::~LinuxX11Context() = default;
|
||||
|
||||
QString LinuxX11Context::key() const {
|
||||
return QStringLiteral("xcb");
|
||||
}
|
||||
|
||||
void LinuxX11Context::virtual_hook(int id, void *data) {
|
||||
if (id == ShowSystemMenuHook) {
|
||||
auto *x11app = qApp->nativeInterface<QNativeInterface::QX11Application>();
|
||||
if (!x11app) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto display = x11app->display();
|
||||
if (!display) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &api = QWK::Private::x11API();
|
||||
Q_ASSERT(api.isValid());
|
||||
|
||||
// some marcos to constexpr in X11
|
||||
constexpr auto None = 0L;
|
||||
constexpr auto ClientMessage = 33;
|
||||
constexpr auto False = 0;
|
||||
constexpr auto Button3 = 3;
|
||||
constexpr auto SubstructureNotifyMask = 1L << 19;
|
||||
constexpr auto SubstructureRedirectMask = 1L << 20;
|
||||
|
||||
// use window id (XID)
|
||||
auto xwin = static_cast<Window>(m_windowId);
|
||||
Atom atom = api.XInternAtom(display, "_GTK_SHOW_WINDOW_MENU", False);
|
||||
if (atom == None)
|
||||
return; // WM might not support this atom
|
||||
auto pos = static_cast<const QPoint *>(data);
|
||||
XEvent ev{};
|
||||
ev.xclient.type = ClientMessage;
|
||||
ev.xclient.window = xwin;
|
||||
ev.xclient.message_type = atom;
|
||||
|
||||
// The format member is set to 8, 16, or 32
|
||||
// and specifies whether the data should be viewed as
|
||||
// a list of bytes, shorts, or longs - typeof(xclient.data).
|
||||
ev.xclient.format = 32;
|
||||
|
||||
qreal dpr = m_windowHandle->devicePixelRatio();
|
||||
int root_x = qRound(pos->x() * dpr);
|
||||
int root_y = qRound(pos->y() * dpr);
|
||||
|
||||
ev.xclient.data.l[0] = Button3; // right button
|
||||
ev.xclient.data.l[1] = root_x;
|
||||
ev.xclient.data.l[2] = root_y;
|
||||
|
||||
Window root = DefaultRootWindow(display);
|
||||
api.XUngrabPointer(display, 0L);
|
||||
api.XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask,
|
||||
&ev);
|
||||
api.XFlush(display);
|
||||
} else {
|
||||
QtWindowContext::virtual_hook(id, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
35
libs/qwindowkit/src/core/contexts/linuxx11context_p.h
Normal file
35
libs/qwindowkit/src/core/contexts/linuxx11context_p.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
|
||||
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
|
||||
// Copyright (C) 2025-2027 Wing-summer (wingsummer)
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef LINUXX11CONTEXT_P_H
|
||||
#define LINUXX11CONTEXT_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 "qtwindowcontext_p.h"
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
namespace QWK {
|
||||
|
||||
class LinuxX11Context : public QtWindowContext {
|
||||
Q_OBJECT
|
||||
public:
|
||||
LinuxX11Context();
|
||||
~LinuxX11Context() override;
|
||||
|
||||
QString key() const override;
|
||||
void virtual_hook(int id, void *data) override;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
#endif // LINUXX11CONTEXT_P_H
|
||||
284
libs/qwindowkit/src/core/contexts/qtwindowcontext.cpp
Normal file
284
libs/qwindowkit/src/core/contexts/qtwindowcontext.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
|
||||
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "qtwindowcontext_p.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "qwkglobal_p.h"
|
||||
#include "systemwindow_p.h"
|
||||
|
||||
namespace QWK {
|
||||
|
||||
static constexpr const quint8 kDefaultResizeBorderThickness = 8;
|
||||
|
||||
static Qt::CursorShape calculateCursorShape(const QWindow *window, const QPoint &pos) {
|
||||
#ifdef Q_OS_MACOS
|
||||
Q_UNUSED(window);
|
||||
Q_UNUSED(pos);
|
||||
return Qt::ArrowCursor;
|
||||
#else
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return Qt::ArrowCursor;
|
||||
}
|
||||
if (window->visibility() != QWindow::Windowed) {
|
||||
return Qt::ArrowCursor;
|
||||
}
|
||||
const int x = pos.x();
|
||||
const int y = pos.y();
|
||||
const int w = window->width();
|
||||
const int h = window->height();
|
||||
if (((x < kDefaultResizeBorderThickness) && (y < kDefaultResizeBorderThickness)) ||
|
||||
((x >= (w - kDefaultResizeBorderThickness)) &&
|
||||
(y >= (h - kDefaultResizeBorderThickness)))) {
|
||||
return Qt::SizeFDiagCursor;
|
||||
}
|
||||
if (((x >= (w - kDefaultResizeBorderThickness)) && (y < kDefaultResizeBorderThickness)) ||
|
||||
((x < kDefaultResizeBorderThickness) && (y >= (h - kDefaultResizeBorderThickness)))) {
|
||||
return Qt::SizeBDiagCursor;
|
||||
}
|
||||
if ((x < kDefaultResizeBorderThickness) || (x >= (w - kDefaultResizeBorderThickness))) {
|
||||
return Qt::SizeHorCursor;
|
||||
}
|
||||
if ((y < kDefaultResizeBorderThickness) || (y >= (h - kDefaultResizeBorderThickness))) {
|
||||
return Qt::SizeVerCursor;
|
||||
}
|
||||
return Qt::ArrowCursor;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline Qt::Edges calculateWindowEdges(const QWindow *window, const QPoint &pos) {
|
||||
#ifdef Q_OS_MACOS
|
||||
Q_UNUSED(window);
|
||||
Q_UNUSED(pos);
|
||||
return {};
|
||||
#else
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return {};
|
||||
}
|
||||
if (window->visibility() != QWindow::Windowed) {
|
||||
return {};
|
||||
}
|
||||
Qt::Edges edges = {};
|
||||
const int x = pos.x();
|
||||
const int y = pos.y();
|
||||
if (x < kDefaultResizeBorderThickness) {
|
||||
edges |= Qt::LeftEdge;
|
||||
}
|
||||
if (x >= (window->width() - kDefaultResizeBorderThickness)) {
|
||||
edges |= Qt::RightEdge;
|
||||
}
|
||||
if (y < kDefaultResizeBorderThickness) {
|
||||
edges |= Qt::TopEdge;
|
||||
}
|
||||
if (y >= (window->height() - kDefaultResizeBorderThickness)) {
|
||||
edges |= Qt::BottomEdge;
|
||||
}
|
||||
return edges;
|
||||
#endif
|
||||
}
|
||||
|
||||
class QtWindowEventFilter : public SharedEventFilter {
|
||||
public:
|
||||
explicit QtWindowEventFilter(AbstractWindowContext *context);
|
||||
~QtWindowEventFilter() override;
|
||||
|
||||
enum WindowStatus {
|
||||
Idle,
|
||||
WaitingRelease,
|
||||
PreparingMove,
|
||||
Moving,
|
||||
Resizing,
|
||||
};
|
||||
|
||||
protected:
|
||||
bool sharedEventFilter(QObject *object, QEvent *event) override;
|
||||
|
||||
private:
|
||||
AbstractWindowContext *m_context;
|
||||
bool m_cursorShapeChanged;
|
||||
WindowStatus m_windowStatus;
|
||||
};
|
||||
|
||||
QtWindowEventFilter::QtWindowEventFilter(AbstractWindowContext *context)
|
||||
: m_context(context), m_cursorShapeChanged(false), m_windowStatus(Idle) {
|
||||
m_context->installSharedEventFilter(this);
|
||||
}
|
||||
|
||||
QtWindowEventFilter::~QtWindowEventFilter() = default;
|
||||
|
||||
bool QtWindowEventFilter::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);
|
||||
bool fixedSize = m_context->isHostSizeFixed();
|
||||
|
||||
QPoint scenePos = getMouseEventScenePos(me);
|
||||
QPoint globalPos = getMouseEventGlobalPos(me);
|
||||
|
||||
bool inTitleBar = m_context->isInTitleBarDraggableArea(scenePos);
|
||||
|
||||
const auto& updateCursorShape{ [&](){
|
||||
if (fixedSize) {
|
||||
return;
|
||||
}
|
||||
const Qt::CursorShape shape = calculateCursorShape(window, scenePos);
|
||||
if (shape == Qt::ArrowCursor) {
|
||||
if (m_cursorShapeChanged) {
|
||||
delegate->restoreCursorShape(host);
|
||||
m_cursorShapeChanged = false;
|
||||
}
|
||||
} else {
|
||||
delegate->setCursorShape(host, shape);
|
||||
m_cursorShapeChanged = true;
|
||||
}
|
||||
} };
|
||||
|
||||
bool handled = false;
|
||||
|
||||
switch (type) {
|
||||
case QEvent::MouseButtonPress: {
|
||||
m_windowStatus = WaitingRelease;
|
||||
switch (me->button()) {
|
||||
case Qt::LeftButton: {
|
||||
if (!fixedSize) {
|
||||
Qt::Edges edges = calculateWindowEdges(window, scenePos);
|
||||
if (edges != Qt::Edges()) {
|
||||
startSystemResize(window, edges);
|
||||
m_windowStatus = Resizing;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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;
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Qt::RightButton: {
|
||||
if (inTitleBar) {
|
||||
m_context->showSystemMenu(globalPos);
|
||||
m_windowStatus = Idle;
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case QEvent::MouseButtonRelease: {
|
||||
switch (m_windowStatus) {
|
||||
case Idle: {
|
||||
if (inTitleBar) {
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WaitingRelease:
|
||||
break;
|
||||
case PreparingMove:
|
||||
case Moving:
|
||||
case Resizing: {
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_windowStatus = Idle;
|
||||
break;
|
||||
}
|
||||
|
||||
case QEvent::MouseMove: {
|
||||
switch (m_windowStatus) {
|
||||
case Idle:
|
||||
case WaitingRelease: {
|
||||
updateCursorShape();
|
||||
break;
|
||||
}
|
||||
case PreparingMove: {
|
||||
startSystemMove(window);
|
||||
m_windowStatus = Moving;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case Moving:
|
||||
case Resizing: {
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case QEvent::MouseButtonDblClick: {
|
||||
if (me->button() == Qt::LeftButton && inTitleBar && !fixedSize) {
|
||||
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);
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
event->accept();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QtWindowContext::QtWindowContext() : AbstractWindowContext() {
|
||||
qtWindowEventFilter = std::make_unique<QtWindowEventFilter>(this);
|
||||
}
|
||||
|
||||
QtWindowContext::~QtWindowContext() = default;
|
||||
|
||||
QString QtWindowContext::key() const {
|
||||
return QStringLiteral("qt");
|
||||
}
|
||||
|
||||
void QtWindowContext::virtual_hook(int id, void *data) {
|
||||
AbstractWindowContext::virtual_hook(id, data);
|
||||
}
|
||||
|
||||
void QtWindowContext::winIdChanged(WId winId, WId oldWinId) {
|
||||
if (!m_windowHandle) {
|
||||
m_delegate->setWindowFlags(m_host, m_delegate->getWindowFlags(m_host) &
|
||||
~Qt::FramelessWindowHint);
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate new resources
|
||||
m_delegate->setWindowFlags(m_host,
|
||||
m_delegate->getWindowFlags(m_host) | Qt::FramelessWindowHint);
|
||||
}
|
||||
|
||||
}
|
||||
39
libs/qwindowkit/src/core/contexts/qtwindowcontext_p.h
Normal file
39
libs/qwindowkit/src/core/contexts/qtwindowcontext_p.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
|
||||
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
|
||||
// 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
|
||||
2456
libs/qwindowkit/src/core/contexts/win32windowcontext.cpp
Normal file
2456
libs/qwindowkit/src/core/contexts/win32windowcontext.cpp
Normal file
File diff suppressed because it is too large
Load Diff
86
libs/qwindowkit/src/core/contexts/win32windowcontext_p.h
Normal file
86
libs/qwindowkit/src/core/contexts/win32windowcontext_p.h
Normal file
@@ -0,0 +1,86 @@
|
||||
// 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;
|
||||
|
||||
// Attributes
|
||||
bool noSystemMenu = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WIN32WINDOWCONTEXT_P_H
|
||||
90
libs/qwindowkit/src/core/kernel/nativeeventfilter.cpp
Normal file
90
libs/qwindowkit/src/core/kernel/nativeeventfilter.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
// 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 "nativeeventfilter_p.h"
|
||||
|
||||
#include <QtCore/QAbstractNativeEventFilter>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
namespace QWK {
|
||||
|
||||
NativeEventFilter::NativeEventFilter() : m_nativeDispatcher(nullptr) {
|
||||
}
|
||||
|
||||
NativeEventFilter::~NativeEventFilter() {
|
||||
if (m_nativeDispatcher)
|
||||
m_nativeDispatcher->removeNativeEventFilter(this);
|
||||
}
|
||||
|
||||
NativeEventDispatcher::NativeEventDispatcher() = default;
|
||||
|
||||
NativeEventDispatcher::~NativeEventDispatcher() {
|
||||
for (const auto &observer : std::as_const(m_nativeEventFilters)) {
|
||||
observer->m_nativeDispatcher = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool NativeEventDispatcher::nativeDispatch(const QByteArray &eventType, void *message,
|
||||
QT_NATIVE_EVENT_RESULT_TYPE *result) {
|
||||
for (const auto &ef : std::as_const(m_nativeEventFilters)) {
|
||||
if (ef->nativeEventFilter(eventType, message, result))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NativeEventDispatcher::installNativeEventFilter(NativeEventFilter *filter) {
|
||||
if (!filter || filter->m_nativeDispatcher)
|
||||
return;
|
||||
|
||||
m_nativeEventFilters.append(filter);
|
||||
filter->m_nativeDispatcher = this;
|
||||
}
|
||||
|
||||
void NativeEventDispatcher::removeNativeEventFilter(NativeEventFilter *filter) {
|
||||
if (!m_nativeEventFilters.removeOne(filter)) {
|
||||
return;
|
||||
}
|
||||
filter->m_nativeDispatcher = nullptr;
|
||||
}
|
||||
|
||||
|
||||
// Avoid adding multiple global native event filters to QGuiApplication
|
||||
// in this library.
|
||||
class AppMasterNativeEventFilter : public QAbstractNativeEventFilter,
|
||||
public NativeEventDispatcher {
|
||||
public:
|
||||
AppMasterNativeEventFilter() {
|
||||
qApp->installNativeEventFilter(this);
|
||||
}
|
||||
|
||||
// The base class removes automatically
|
||||
~AppMasterNativeEventFilter() override = default;
|
||||
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message,
|
||||
QT_NATIVE_EVENT_RESULT_TYPE *result) override {
|
||||
return nativeDispatch(eventType, message, result);
|
||||
}
|
||||
|
||||
static inline AppMasterNativeEventFilter *instance = nullptr;
|
||||
|
||||
friend class AppNativeEventFilter;
|
||||
};
|
||||
|
||||
AppNativeEventFilter::AppNativeEventFilter() {
|
||||
if (!AppMasterNativeEventFilter::instance) {
|
||||
AppMasterNativeEventFilter::instance = new AppMasterNativeEventFilter();
|
||||
}
|
||||
AppMasterNativeEventFilter::instance->installNativeEventFilter(this);
|
||||
}
|
||||
|
||||
AppNativeEventFilter::~AppNativeEventFilter() {
|
||||
AppMasterNativeEventFilter::instance->removeNativeEventFilter(this);
|
||||
if (AppMasterNativeEventFilter::instance->m_nativeEventFilters.isEmpty()) {
|
||||
delete AppMasterNativeEventFilter::instance;
|
||||
AppMasterNativeEventFilter::instance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
70
libs/qwindowkit/src/core/kernel/nativeeventfilter_p.h
Normal file
70
libs/qwindowkit/src/core/kernel/nativeeventfilter_p.h
Normal file
@@ -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
|
||||
48
libs/qwindowkit/src/core/kernel/sharedeventfilter.cpp
Normal file
48
libs/qwindowkit/src/core/kernel/sharedeventfilter.cpp
Normal file
@@ -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
|
||||
|
||||
#include "sharedeventfilter_p.h"
|
||||
|
||||
namespace QWK {
|
||||
|
||||
SharedEventFilter::SharedEventFilter() : m_sharedDispatcher(nullptr) {
|
||||
}
|
||||
|
||||
SharedEventFilter::~SharedEventFilter() {
|
||||
if (m_sharedDispatcher)
|
||||
m_sharedDispatcher->removeSharedEventFilter(this);
|
||||
}
|
||||
|
||||
SharedEventDispatcher::SharedEventDispatcher() = default;
|
||||
|
||||
SharedEventDispatcher::~SharedEventDispatcher() {
|
||||
for (const auto &observer : std::as_const(m_sharedEventFilters)) {
|
||||
observer->m_sharedDispatcher = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool SharedEventDispatcher::sharedDispatch(QObject *obj, QEvent *event) {
|
||||
for (const auto &ef : std::as_const(m_sharedEventFilters)) {
|
||||
if (ef->sharedEventFilter(obj, event))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SharedEventDispatcher::installSharedEventFilter(SharedEventFilter *filter) {
|
||||
if (!filter || filter->m_sharedDispatcher)
|
||||
return;
|
||||
|
||||
m_sharedEventFilters.append(filter);
|
||||
filter->m_sharedDispatcher = this;
|
||||
}
|
||||
|
||||
void SharedEventDispatcher::removeSharedEventFilter(SharedEventFilter *filter) {
|
||||
if (!m_sharedEventFilters.removeOne(filter)) {
|
||||
return;
|
||||
}
|
||||
filter->m_sharedDispatcher = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
61
libs/qwindowkit/src/core/kernel/sharedeventfilter_p.h
Normal file
61
libs/qwindowkit/src/core/kernel/sharedeventfilter_p.h
Normal file
@@ -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
|
||||
43
libs/qwindowkit/src/core/kernel/winidchangeeventfilter.cpp
Normal file
43
libs/qwindowkit/src/core/kernel/winidchangeeventfilter.cpp
Normal file
@@ -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
|
||||
|
||||
#include "winidchangeeventfilter_p.h"
|
||||
|
||||
#include <QtGui/qpa/qplatformwindow.h>
|
||||
|
||||
#include "abstractwindowcontext_p.h"
|
||||
|
||||
namespace QWK {
|
||||
|
||||
WindowWinIdChangeEventFilter::WindowWinIdChangeEventFilter(QWindow *host,
|
||||
AbstractWindowContext *context)
|
||||
: WinIdChangeEventFilter(host, context), win(host), isAboutToBeDestroyed(false) {
|
||||
host->installEventFilter(this);
|
||||
}
|
||||
|
||||
WId WindowWinIdChangeEventFilter::winId() const {
|
||||
auto win = static_cast<QWindow *>(host);
|
||||
if (isAboutToBeDestroyed)
|
||||
return 0;
|
||||
if (auto platformWindow = win->handle())
|
||||
return platformWindow->winId();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool WindowWinIdChangeEventFilter::eventFilter(QObject *obj, QEvent *event) {
|
||||
Q_UNUSED(obj)
|
||||
if (event->type() == QEvent::PlatformSurface) {
|
||||
auto e = static_cast<QPlatformSurfaceEvent *>(event);
|
||||
if (e->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
|
||||
isAboutToBeDestroyed = true;
|
||||
context->notifyWinIdChange();
|
||||
isAboutToBeDestroyed = false;
|
||||
} else {
|
||||
context->notifyWinIdChange();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
53
libs/qwindowkit/src/core/kernel/winidchangeeventfilter_p.h
Normal file
53
libs/qwindowkit/src/core/kernel/winidchangeeventfilter_p.h
Normal file
@@ -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
|
||||
87
libs/qwindowkit/src/core/qwindowkit_linux.cpp
Normal file
87
libs/qwindowkit/src/core/qwindowkit_linux.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
|
||||
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
|
||||
// Copyright (C) 2025-2027 Wing-summer (wingsummer)
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "qwindowkit_linux.h"
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
#include <QGuiApplication>
|
||||
#include <QLibrary>
|
||||
|
||||
namespace QWK {
|
||||
namespace Private {
|
||||
|
||||
bool isX11Platform() {
|
||||
static const bool isX11 = QGuiApplication::platformName().startsWith(
|
||||
QStringLiteral("xcb"), Qt::CaseInsensitive);
|
||||
return isX11;
|
||||
}
|
||||
|
||||
bool isWaylandPlatform() {
|
||||
static const bool isWayland = QGuiApplication::platformName().startsWith(
|
||||
QStringLiteral("wayland"), Qt::CaseInsensitive);
|
||||
return isWayland;
|
||||
}
|
||||
|
||||
const LinuxWaylandAPI &waylandAPI() {
|
||||
static LinuxWaylandAPI api;
|
||||
static bool guard = true;
|
||||
if (guard && isWaylandPlatform()) {
|
||||
QLibrary waylib(QStringLiteral("libwayland-client.so"));
|
||||
bool loaded = false;
|
||||
if (waylib.load()) {
|
||||
loaded = true;
|
||||
} else {
|
||||
waylib.setFileName(QStringLiteral("libwayland-client.so.0"));
|
||||
if (waylib.load()) {
|
||||
loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (loaded) {
|
||||
api.wl_display_flush = reinterpret_cast<LinuxWaylandAPI::wl_display_flush_fn>(
|
||||
waylib.resolve("wl_display_flush"));
|
||||
api.wl_proxy_marshal_flags =
|
||||
reinterpret_cast<LinuxWaylandAPI::wl_proxy_marshal_flags_fn>(
|
||||
waylib.resolve("wl_proxy_marshal_flags"));
|
||||
api.wl_proxy_get_version =
|
||||
reinterpret_cast<LinuxWaylandAPI::wl_proxy_get_version_fn>(
|
||||
waylib.resolve("wl_proxy_get_version"));
|
||||
}
|
||||
}
|
||||
guard = false;
|
||||
return api;
|
||||
}
|
||||
|
||||
const LinuxX11API &x11API() {
|
||||
static LinuxX11API api;
|
||||
static bool guard = true;
|
||||
if (guard && isX11Platform()) {
|
||||
QString libName = QStringLiteral(
|
||||
#if defined(__CYGWIN__)
|
||||
"libX11-6.so"
|
||||
#elif defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
"libX11.so"
|
||||
#else
|
||||
"libX11.so.6"
|
||||
#endif
|
||||
);
|
||||
QLibrary x11lib(libName);
|
||||
if (x11lib.load()) {
|
||||
api.XInternAtom =
|
||||
reinterpret_cast<LinuxX11API::XInternAtomFn>(x11lib.resolve("XInternAtom"));
|
||||
api.XSendEvent =
|
||||
reinterpret_cast<LinuxX11API::XSendEventFn>(x11lib.resolve("XSendEvent"));
|
||||
api.XFlush = reinterpret_cast<LinuxX11API::XFlushFn>(x11lib.resolve("XFlush"));
|
||||
api.XUngrabPointer = reinterpret_cast<LinuxX11API::XUngrabPointerFn>(
|
||||
x11lib.resolve("XUngrabPointer"));
|
||||
}
|
||||
}
|
||||
guard = false;
|
||||
return api;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif // QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
86
libs/qwindowkit/src/core/qwindowkit_linux.h
Normal file
86
libs/qwindowkit/src/core/qwindowkit_linux.h
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
|
||||
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
|
||||
// Copyright (C) 2025-2027 Wing-summer (wingsummer)
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef QWINDOWKIT_LINUX_H
|
||||
#define QWINDOWKIT_LINUX_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/qglobal.h>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
#include <qguiapplication_platform.h>
|
||||
|
||||
// some declarations about x11
|
||||
using Atom = unsigned long;
|
||||
using Bool = int;
|
||||
using XID = unsigned long;
|
||||
using Window = XID;
|
||||
|
||||
union _XEvent;
|
||||
using XEvent = union _XEvent;
|
||||
|
||||
// for wayland
|
||||
struct wl_proxy;
|
||||
|
||||
namespace QWK {
|
||||
namespace Private {
|
||||
struct LinuxX11API {
|
||||
LinuxX11API() = default;
|
||||
Q_DISABLE_COPY(LinuxX11API)
|
||||
|
||||
using XInternAtomFn = Atom (*)(Display *, const char *, Bool);
|
||||
using XSendEventFn = int (*)(Display *, Window, Bool, long, XEvent *);
|
||||
using XFlushFn = int (*)(Display *);
|
||||
using XUngrabPointerFn = int (*)(Display *, unsigned long);
|
||||
|
||||
XInternAtomFn XInternAtom = nullptr;
|
||||
XSendEventFn XSendEvent = nullptr;
|
||||
XFlushFn XFlush = nullptr;
|
||||
XUngrabPointerFn XUngrabPointer = nullptr;
|
||||
|
||||
inline bool isValid() const {
|
||||
return XInternAtom && XSendEvent && XFlush && XUngrabPointer;
|
||||
}
|
||||
};
|
||||
|
||||
struct LinuxWaylandAPI {
|
||||
LinuxWaylandAPI() = default;
|
||||
Q_DISABLE_COPY(LinuxWaylandAPI)
|
||||
|
||||
using wl_display_flush_fn = int (*)(struct wl_display *);
|
||||
using wl_proxy_marshal_flags_fn = void (*)(struct wl_proxy *, uint32_t,
|
||||
const struct wl_interface *, uint32_t,
|
||||
uint32_t, ...);
|
||||
using wl_proxy_get_version_fn = int (*)(struct wl_proxy *);
|
||||
|
||||
wl_display_flush_fn wl_display_flush = nullptr;
|
||||
wl_proxy_marshal_flags_fn wl_proxy_marshal_flags = nullptr;
|
||||
wl_proxy_get_version_fn wl_proxy_get_version = nullptr;
|
||||
|
||||
inline bool isValid() const {
|
||||
return wl_display_flush && wl_proxy_marshal_flags && wl_proxy_get_version;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bool isWaylandPlatform();
|
||||
|
||||
bool isX11Platform();
|
||||
|
||||
const LinuxX11API &x11API();
|
||||
|
||||
const LinuxWaylandAPI &waylandAPI();
|
||||
}
|
||||
}
|
||||
#endif // QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
#endif // QWINDOWKIT_LINUX_H
|
||||
93
libs/qwindowkit/src/core/qwindowkit_windows.cpp
Normal file
93
libs/qwindowkit/src/core/qwindowkit_windows.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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 "qwindowkit_windows.h"
|
||||
|
||||
using QWK_NTSTATUS = long;
|
||||
|
||||
namespace QWK {
|
||||
|
||||
static QWK_OSVERSIONINFOW GetRealOSVersionImpl() {
|
||||
HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
|
||||
Q_ASSERT(hMod);
|
||||
using RtlGetVersionPtr = QWK_NTSTATUS(WINAPI *)(QWK_OSVERSIONINFOW*);
|
||||
auto pRtlGetVersion =
|
||||
reinterpret_cast<RtlGetVersionPtr>(::GetProcAddress(hMod, "RtlGetVersion"));
|
||||
Q_ASSERT(pRtlGetVersion);
|
||||
QWK_OSVERSIONINFOW rovi{};
|
||||
rovi.dwOSVersionInfoSize = sizeof(rovi);
|
||||
pRtlGetVersion(&rovi);
|
||||
return rovi;
|
||||
}
|
||||
|
||||
namespace Private {
|
||||
|
||||
QWK_OSVERSIONINFOW GetRealOSVersion() {
|
||||
static const auto result = GetRealOSVersionImpl();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
WindowsRegistryKey::WindowsRegistryKey(HKEY parentHandle, QStringView subKey,
|
||||
REGSAM permissions, REGSAM access) {
|
||||
if (::RegOpenKeyExW(parentHandle, reinterpret_cast<const wchar_t *>(subKey.utf16()), 0,
|
||||
permissions | access, &m_key) != ERROR_SUCCESS) {
|
||||
m_key = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
WindowsRegistryKey::~WindowsRegistryKey() {
|
||||
close();
|
||||
}
|
||||
|
||||
void WindowsRegistryKey::close() {
|
||||
if (isValid()) {
|
||||
::RegCloseKey(m_key);
|
||||
m_key = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QString WindowsRegistryKey::stringValue(QStringView subKey) const {
|
||||
QString result;
|
||||
if (!isValid())
|
||||
return result;
|
||||
DWORD type;
|
||||
DWORD size;
|
||||
auto subKeyC = reinterpret_cast<const wchar_t *>(subKey.utf16());
|
||||
if (::RegQueryValueExW(m_key, subKeyC, nullptr, &type, nullptr, &size) != ERROR_SUCCESS ||
|
||||
(type != REG_SZ && type != REG_EXPAND_SZ) || size <= 2) {
|
||||
return result;
|
||||
}
|
||||
// Reserve more for rare cases where trailing '\0' are missing in registry.
|
||||
// Rely on 0-termination since strings of size 256 padded with 0 have been
|
||||
// observed (QTBUG-84455).
|
||||
size += 2;
|
||||
QVarLengthArray<unsigned char> buffer(static_cast<int>(size));
|
||||
std::fill(buffer.data(), buffer.data() + size, 0u);
|
||||
if (::RegQueryValueExW(m_key, subKeyC, nullptr, &type, buffer.data(), &size) ==
|
||||
ERROR_SUCCESS)
|
||||
result = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(buffer.constData()));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<DWORD, bool> WindowsRegistryKey::dwordValue(QStringView subKey) const {
|
||||
if (!isValid())
|
||||
return std::make_pair(0, false);
|
||||
DWORD type;
|
||||
auto subKeyC = reinterpret_cast<const wchar_t *>(subKey.utf16());
|
||||
if (::RegQueryValueExW(m_key, subKeyC, nullptr, &type, nullptr, nullptr) != ERROR_SUCCESS ||
|
||||
type != REG_DWORD) {
|
||||
return std::make_pair(0, false);
|
||||
}
|
||||
DWORD value = 0;
|
||||
DWORD size = sizeof(value);
|
||||
const bool ok =
|
||||
::RegQueryValueExW(m_key, subKeyC, nullptr, nullptr,
|
||||
reinterpret_cast<unsigned char *>(&value), &size) == ERROR_SUCCESS;
|
||||
return std::make_pair(value, ok);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
279
libs/qwindowkit/src/core/qwindowkit_windows.h
Normal file
279
libs/qwindowkit/src/core/qwindowkit_windows.h
Normal file
@@ -0,0 +1,279 @@
|
||||
// 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
|
||||
|
||||
#ifndef _USER32_
|
||||
# define _USER32_
|
||||
#endif
|
||||
|
||||
#ifndef _DWMAPI_
|
||||
# define _DWMAPI_
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
using QWK_OSVERSIONINFOW = struct _QWK_OSVERSIONINFOW {
|
||||
DWORD dwOSVersionInfoSize;
|
||||
DWORD dwMajorVersion;
|
||||
DWORD dwMinorVersion;
|
||||
DWORD dwBuildNumber;
|
||||
DWORD dwPlatformId;
|
||||
wchar_t szCSDVersion[128];
|
||||
};
|
||||
|
||||
namespace QWK {
|
||||
|
||||
namespace Private {
|
||||
|
||||
QWK_CORE_EXPORT QWK_OSVERSIONINFOW GetRealOSVersion();
|
||||
|
||||
inline bool IsWindows1122H2OrGreater_Real() {
|
||||
QWK_OSVERSIONINFOW rovi = GetRealOSVersion();
|
||||
return (rovi.dwMajorVersion > 10) ||
|
||||
(rovi.dwMajorVersion == 10 &&
|
||||
(rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 22621));
|
||||
}
|
||||
|
||||
inline bool IsWindows11OrGreater_Real() {
|
||||
QWK_OSVERSIONINFOW rovi = GetRealOSVersion();
|
||||
return (rovi.dwMajorVersion > 10) ||
|
||||
(rovi.dwMajorVersion == 10 &&
|
||||
(rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 22000));
|
||||
}
|
||||
|
||||
inline bool IsWindows1020H1OrGreater_Real() {
|
||||
QWK_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() {
|
||||
QWK_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() {
|
||||
QWK_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() {
|
||||
QWK_OSVERSIONINFOW rovi = GetRealOSVersion();
|
||||
return rovi.dwMajorVersion >= 10;
|
||||
}
|
||||
|
||||
inline bool IsWindows8Point1OrGreater_Real() {
|
||||
QWK_OSVERSIONINFOW rovi = GetRealOSVersion();
|
||||
return (rovi.dwMajorVersion > 6) ||
|
||||
(rovi.dwMajorVersion == 6 && rovi.dwMinorVersion >= 3);
|
||||
}
|
||||
|
||||
inline bool IsWindows8OrGreater_Real() {
|
||||
QWK_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 {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 1)
|
||||
using SubKeyType = const wchar_t*;
|
||||
#else
|
||||
using SubKeyType = QStringView;
|
||||
#endif
|
||||
public:
|
||||
WindowsRegistryKey(HKEY parentHandle, SubKeyType subKey,
|
||||
REGSAM permissions = KEY_READ, REGSAM access = 0)
|
||||
: QWinRegistryKey(parentHandle, subKey, permissions, access) {
|
||||
}
|
||||
|
||||
inline std::pair<DWORD, bool> dwordValue(SubKeyType subKey) const;
|
||||
};
|
||||
|
||||
inline std::pair<DWORD, bool> WindowsRegistryKey::dwordValue(SubKeyType 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
|
||||
48
libs/qwindowkit/src/core/qwkglobal.cpp
Normal file
48
libs/qwindowkit/src/core/qwkglobal.cpp
Normal file
@@ -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
|
||||
|
||||
#include "qwkglobal_p.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include <QtCore/private/qobject_p.h>
|
||||
|
||||
namespace QWK {
|
||||
|
||||
bool forwardObjectEventFilters(QObject *currentFilter, QObject *receiver, QEvent *event) {
|
||||
// https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/corelib/kernel/qcoreapplication.cpp#L1244
|
||||
// Send the event through the rest event filters
|
||||
auto d = QObjectPrivate::get(receiver);
|
||||
bool findCurrent = false;
|
||||
if (receiver != QCoreApplication::instance() && d->extraData) {
|
||||
for (qsizetype i = 0; i < d->extraData->eventFilters.size(); ++i) {
|
||||
QObject *obj = d->extraData->eventFilters.at(i);
|
||||
if (!findCurrent) {
|
||||
if (obj == currentFilter) {
|
||||
findCurrent = true; // Will start to filter from the next one
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!obj)
|
||||
continue;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
if (QObjectPrivate::get(obj)->threadData.loadRelaxed() !=
|
||||
d->threadData.loadRelaxed())
|
||||
#else
|
||||
if (QObjectPrivate::get(obj)->threadData != d->threadData)
|
||||
#endif
|
||||
{
|
||||
qWarning("QCoreApplication: Object event filter cannot be in a different "
|
||||
"thread.");
|
||||
continue;
|
||||
}
|
||||
if (obj->eventFilter(receiver, event))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
50
libs/qwindowkit/src/core/qwkglobal.h
Normal file
50
libs/qwindowkit/src/core/qwkglobal.h
Normal file
@@ -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 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
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define QWINDOWKIT_PRINTF_FORMAT(fmtpos, attrpos) \
|
||||
__attribute__((__format__(__printf__, fmtpos, attrpos)))
|
||||
#else
|
||||
# define QWINDOWKIT_PRINTF_FORMAT(fmtpos, attrpos)
|
||||
#endif
|
||||
|
||||
namespace QWK {
|
||||
|
||||
using ScreenRectCallback = std::function<QRect(const QSize &)>;
|
||||
|
||||
}
|
||||
|
||||
#endif // QWKGLOBAL_H
|
||||
71
libs/qwindowkit/src/core/qwkglobal_p.h
Normal file
71
libs/qwindowkit/src/core/qwkglobal_p.h
Normal file
@@ -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
|
||||
513
libs/qwindowkit/src/core/shared/qwkwindowsextra_p.h
Normal file
513
libs/qwindowkit/src/core/shared/qwkwindowsextra_p.h
Normal file
@@ -0,0 +1,513 @@
|
||||
// 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 <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.
|
||||
|
||||
typedef struct _MARGINS
|
||||
{
|
||||
int cxLeftWidth;
|
||||
int cxRightWidth;
|
||||
int cyTopHeight;
|
||||
int cyBottomHeight;
|
||||
} MARGINS, *PMARGINS;
|
||||
|
||||
typedef enum MONITOR_DPI_TYPE {
|
||||
MDT_EFFECTIVE_DPI = 0,
|
||||
MDT_ANGULAR_DPI = 1,
|
||||
MDT_RAW_DPI = 2,
|
||||
MDT_DEFAULT = MDT_EFFECTIVE_DPI
|
||||
} MONITOR_DPI_TYPE;
|
||||
|
||||
typedef struct _DWM_BLURBEHIND
|
||||
{
|
||||
DWORD dwFlags;
|
||||
BOOL fEnable;
|
||||
HRGN hRgnBlur;
|
||||
BOOL fTransitionOnMaximized;
|
||||
} DWM_BLURBEHIND, *PDWM_BLURBEHIND;
|
||||
|
||||
extern "C" {
|
||||
UINT WINAPI GetDpiForWindow(HWND);
|
||||
int WINAPI GetSystemMetricsForDpi(int, UINT);
|
||||
BOOL WINAPI AdjustWindowRectExForDpi(LPRECT, DWORD, BOOL, DWORD, UINT);
|
||||
HRESULT WINAPI GetDpiForMonitor(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *);
|
||||
HRESULT WINAPI DwmFlush();
|
||||
HRESULT WINAPI DwmIsCompositionEnabled(BOOL*);
|
||||
HRESULT WINAPI DwmGetWindowAttribute(HWND, DWORD, PVOID, DWORD);
|
||||
HRESULT WINAPI DwmSetWindowAttribute(HWND, DWORD, LPCVOID, DWORD);
|
||||
HRESULT WINAPI DwmExtendFrameIntoClientArea(HWND, const MARGINS*);
|
||||
HRESULT WINAPI DwmEnableBlurBehindWindow(HWND, const DWM_BLURBEHIND*);
|
||||
} // extern "C"
|
||||
|
||||
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 inline 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:
|
||||
inline 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
|
||||
}
|
||||
|
||||
inline ~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
|
||||
163
libs/qwindowkit/src/core/shared/systemwindow_p.h
Normal file
163
libs/qwindowkit/src/core/shared/systemwindow_p.h
Normal file
@@ -0,0 +1,163 @@
|
||||
// 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), operationComplete(false),
|
||||
initialMousePosition(QCursor::pos()),
|
||||
initialWindowPosition(targetWindow->position()) {
|
||||
target->installEventFilter(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override {
|
||||
if (operationComplete) {
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
operationComplete = true;
|
||||
deleteLater();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
QWindow *target;
|
||||
bool operationComplete;
|
||||
QPoint initialMousePosition;
|
||||
QPoint initialWindowPosition;
|
||||
};
|
||||
|
||||
class WindowResizeManipulator : public QObject {
|
||||
public:
|
||||
WindowResizeManipulator(QWindow *targetWindow, Qt::Edges edges)
|
||||
: QObject(targetWindow), target(targetWindow), operationComplete(false),
|
||||
initialMousePosition(QCursor::pos()), initialWindowRect(target->geometry()),
|
||||
resizeEdges(edges) {
|
||||
target->installEventFilter(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override {
|
||||
if (operationComplete) {
|
||||
return false;
|
||||
}
|
||||
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: {
|
||||
operationComplete = true;
|
||||
deleteLater();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
QWindow *target;
|
||||
bool operationComplete;
|
||||
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
|
||||
152
libs/qwindowkit/src/core/shared/windows10borderhandler_p.h
Normal file
152
libs/qwindowkit/src/core/shared/windows10borderhandler_p.h
Normal file
@@ -0,0 +1,152 @@
|
||||
// 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() {
|
||||
if (!isWin11OrGreater()) {
|
||||
// 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 drawBorderEmulated(QPainter *painter, const QRect &rect) {
|
||||
QRegion region(rect);
|
||||
void *args[] = {
|
||||
painter,
|
||||
const_cast<QRect *>(&rect),
|
||||
®ion,
|
||||
};
|
||||
ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook_Emulated, args);
|
||||
}
|
||||
|
||||
inline void drawBorderNative() {
|
||||
ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook_Native, nullptr);
|
||||
}
|
||||
|
||||
inline int borderThickness() const {
|
||||
return ctx->windowAttribute(QStringLiteral("border-thickness")).toInt();
|
||||
}
|
||||
|
||||
inline void updateExtraMargins(bool windowActive) {
|
||||
if (isWin11OrGreater()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ### FIXME: transparent seam
|
||||
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
|
||||
99
libs/qwindowkit/src/core/style/styleagent.cpp
Normal file
99
libs/qwindowkit/src/core/style/styleagent.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
// 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.h"
|
||||
#include "styleagent_p.h"
|
||||
|
||||
namespace QWK {
|
||||
|
||||
/*!
|
||||
\class StyleAgent
|
||||
\brief StyleAgent provides some features related to system theme.
|
||||
|
||||
Qt6.6 started to support system theme detection, this class is intended as an auxiliary
|
||||
support for lower versions of Qt. If your Qt already supports it, it is recommended that
|
||||
you don't include this class in your build system.
|
||||
*/
|
||||
|
||||
StyleAgentPrivate::StyleAgentPrivate() {
|
||||
}
|
||||
|
||||
StyleAgentPrivate::~StyleAgentPrivate() {
|
||||
removeSystemThemeHook();
|
||||
}
|
||||
|
||||
void StyleAgentPrivate::init() {
|
||||
setupSystemThemeHook();
|
||||
}
|
||||
|
||||
void StyleAgentPrivate::notifyThemeChanged(StyleAgent::SystemTheme theme) {
|
||||
if (theme == systemTheme)
|
||||
return;
|
||||
systemTheme = theme;
|
||||
|
||||
Q_Q(StyleAgent);
|
||||
Q_EMIT q->systemThemeChanged();
|
||||
}
|
||||
|
||||
void StyleAgentPrivate::notifyAccentColorChanged(const QColor &color) {
|
||||
Q_ASSERT(color.isValid());
|
||||
if (color == systemAccentColor)
|
||||
return;
|
||||
systemAccentColor = color;
|
||||
|
||||
Q_Q(StyleAgent);
|
||||
Q_EMIT q->systemAccentColorChanged();
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructor. Since it is not related to a concrete window instance, it is better to be used
|
||||
as a singleton.
|
||||
*/
|
||||
StyleAgent::StyleAgent(QObject *parent) : StyleAgent(*new StyleAgentPrivate(), parent) {
|
||||
}
|
||||
|
||||
/*!
|
||||
Destructor.
|
||||
*/
|
||||
StyleAgent::~StyleAgent() {
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the system theme.
|
||||
*/
|
||||
StyleAgent::SystemTheme StyleAgent::systemTheme() const {
|
||||
Q_D(const StyleAgent);
|
||||
return d->systemTheme;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the system accent color.
|
||||
*/
|
||||
QColor StyleAgent::systemAccentColor() const {
|
||||
Q_D(const StyleAgent);
|
||||
return d->systemAccentColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
StyleAgent::StyleAgent(StyleAgentPrivate &d, QObject *parent) : QObject(parent), d_ptr(&d) {
|
||||
d.q_ptr = this;
|
||||
|
||||
d.init();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn void StyleAgent::systemThemeChanged()
|
||||
|
||||
This signal is emitted when the system theme changes.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void StyleAgent::systemAccentColorChanged()
|
||||
|
||||
This signal is emitted when the system accent color changes.
|
||||
*/
|
||||
|
||||
}
|
||||
50
libs/qwindowkit/src/core/style/styleagent.h
Normal file
50
libs/qwindowkit/src/core/style/styleagent.h
Normal file
@@ -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 STYLEAGENT_H
|
||||
#define STYLEAGENT_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtGui/QColor>
|
||||
|
||||
#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;
|
||||
QColor systemAccentColor() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void systemThemeChanged();
|
||||
void systemAccentColorChanged();
|
||||
|
||||
protected:
|
||||
StyleAgent(StyleAgentPrivate &d, QObject *parent = nullptr);
|
||||
|
||||
const std::unique_ptr<StyleAgentPrivate> d_ptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // STYLEAGENT_H
|
||||
23
libs/qwindowkit/src/core/style/styleagent_linux.cpp
Normal file
23
libs/qwindowkit/src/core/style/styleagent_linux.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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 <QtGui/QPalette>
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace QWK {
|
||||
|
||||
void StyleAgentPrivate::setupSystemThemeHook() {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
|
||||
systemAccentColor = QGuiApplication::palette().color(QPalette::Accent);
|
||||
#else
|
||||
systemAccentColor = QGuiApplication::palette().color(QPalette::Highlight);
|
||||
#endif
|
||||
}
|
||||
|
||||
void StyleAgentPrivate::removeSystemThemeHook() {
|
||||
}
|
||||
|
||||
}
|
||||
114
libs/qwindowkit/src/core/style/styleagent_mac.mm
Normal file
114
libs/qwindowkit/src/core/style/styleagent_mac.mm
Normal file
@@ -0,0 +1,114 @@
|
||||
// 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>
|
||||
|
||||
namespace QWK {
|
||||
|
||||
static StyleAgent::SystemTheme getSystemTheme() {
|
||||
NSString *osxMode =
|
||||
[[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
|
||||
bool isDark = [osxMode isEqualToString:@"Dark"];
|
||||
return isDark ? StyleAgent::Dark : StyleAgent::Light;
|
||||
}
|
||||
|
||||
static QColor getAccentColor() {
|
||||
if (@available(macOS 10.14, *)) {
|
||||
NSColor *color = [NSColor controlAccentColor];
|
||||
NSColor *rgbColor = [color colorUsingColorSpace:[NSColorSpace sRGBColorSpace]];
|
||||
if (rgbColor) {
|
||||
return QColor::fromRgbF(rgbColor.redComponent, rgbColor.greenComponent, rgbColor.blueComponent, rgbColor.alphaComponent);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static void notifyAllStyleAgents();
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Objective C++ Begin
|
||||
//
|
||||
|
||||
@interface QWK_SystemThemeObserver : NSObject {
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation QWK_SystemThemeObserver
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
|
||||
[center addObserver:self
|
||||
selector:@selector(interfaceModeChanged:)
|
||||
name:@"AppleInterfaceThemeChangedNotification"
|
||||
object:nil];
|
||||
[center addObserver:self
|
||||
selector:@selector(interfaceModeChanged:)
|
||||
name:@"AppleColorPreferencesChangedNotification"
|
||||
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();
|
||||
auto color = getAccentColor();
|
||||
for (auto &&ap : std::as_const(*g_styleAgentSet())) {
|
||||
ap->notifyThemeChanged(theme);
|
||||
ap->notifyAccentColorChanged(color);
|
||||
}
|
||||
}
|
||||
|
||||
void StyleAgentPrivate::setupSystemThemeHook() {
|
||||
systemTheme = getSystemTheme();
|
||||
systemAccentColor = getAccentColor();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
43
libs/qwindowkit/src/core/style/styleagent_p.h
Normal file
43
libs/qwindowkit/src/core/style/styleagent_p.h
Normal file
@@ -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 <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;
|
||||
QColor systemAccentColor;
|
||||
|
||||
void setupSystemThemeHook();
|
||||
void removeSystemThemeHook();
|
||||
|
||||
void notifyThemeChanged(StyleAgent::SystemTheme theme);
|
||||
void notifyAccentColorChanged(const QColor &color);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // STYLEAGENTPRIVATE_H
|
||||
102
libs/qwindowkit/src/core/style/styleagent_win.cpp
Normal file
102
libs/qwindowkit/src/core/style/styleagent_win.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
// 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 <QtCore/QSet>
|
||||
|
||||
#include <QWKCore/private/qwkwindowsextra_p.h>
|
||||
#include <QWKCore/private/nativeeventfilter_p.h>
|
||||
|
||||
namespace QWK {
|
||||
|
||||
using StyleAgentSet = QSet<StyleAgentPrivate *>;
|
||||
Q_GLOBAL_STATIC(StyleAgentSet, g_styleAgentSet)
|
||||
|
||||
static StyleAgent::SystemTheme getSystemTheme() {
|
||||
if (isHighContrastModeEnabled()) {
|
||||
return StyleAgent::HighContrast;
|
||||
} else if (isDarkThemeActive()) {
|
||||
return StyleAgent::Dark;
|
||||
} else {
|
||||
return StyleAgent::Light;
|
||||
}
|
||||
}
|
||||
|
||||
static void notifyAllStyleAgents() {
|
||||
auto theme = getSystemTheme();
|
||||
auto color = getAccentColor();
|
||||
for (auto &&ap : std::as_const(*g_styleAgentSet())) {
|
||||
ap->notifyThemeChanged(theme);
|
||||
ap->notifyAccentColorChanged(color);
|
||||
}
|
||||
}
|
||||
|
||||
class SystemSettingEventFilter : public AppNativeEventFilter {
|
||||
public:
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message,
|
||||
QT_NATIVE_EVENT_RESULT_TYPE *result) override {
|
||||
Q_UNUSED(eventType)
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto msg = static_cast<const MSG *>(message);
|
||||
switch (msg->message) {
|
||||
case WM_THEMECHANGED:
|
||||
case WM_SYSCOLORCHANGE:
|
||||
case WM_DWMCOLORIZATIONCOLORCHANGED: {
|
||||
notifyAllStyleAgents();
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_SETTINGCHANGE: {
|
||||
if (isImmersiveColorSetChange(msg->wParam, msg->lParam)) {
|
||||
notifyAllStyleAgents();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline SystemSettingEventFilter *instance = nullptr;
|
||||
|
||||
static inline void install() {
|
||||
if (instance) {
|
||||
return;
|
||||
}
|
||||
instance = new SystemSettingEventFilter();
|
||||
}
|
||||
|
||||
static inline void uninstall() {
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
delete instance;
|
||||
instance = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
void StyleAgentPrivate::setupSystemThemeHook() {
|
||||
systemTheme = getSystemTheme();
|
||||
systemAccentColor = getAccentColor();
|
||||
|
||||
g_styleAgentSet->insert(this);
|
||||
SystemSettingEventFilter::install();
|
||||
}
|
||||
|
||||
void StyleAgentPrivate::removeSystemThemeHook() {
|
||||
if (!g_styleAgentSet->remove(this))
|
||||
return;
|
||||
|
||||
if (g_styleAgentSet->isEmpty()) {
|
||||
SystemSettingEventFilter::uninstall();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
169
libs/qwindowkit/src/core/windowagentbase.cpp
Normal file
169
libs/qwindowkit/src/core/windowagentbase.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
// 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 "windowagentbase.h"
|
||||
#include "windowagentbase_p.h"
|
||||
|
||||
#include <QWKCore/qwkconfig.h>
|
||||
|
||||
#include "qwkglobal_p.h"
|
||||
|
||||
#if defined(Q_OS_WINDOWS)
|
||||
# include "win32windowcontext_p.h"
|
||||
#elif defined(Q_OS_MAC)
|
||||
# include "cocoawindowcontext_p.h"
|
||||
#elif defined(Q_OS_LINUX) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
# include "qwindowkit_linux.h"
|
||||
# include "linuxwaylandcontext_p.h"
|
||||
# include "linuxx11context_p.h"
|
||||
#endif
|
||||
#include "qtwindowcontext_p.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(qWindowKitLog, "qwindowkit")
|
||||
|
||||
namespace QWK {
|
||||
|
||||
/*!
|
||||
\namespace QWK
|
||||
\brief QWindowKit namespace
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class WindowAgentBase
|
||||
\brief WindowAgentBase is the base class of the specifiy window agent for QtWidgets and
|
||||
QtQuick.
|
||||
|
||||
It processes some system events to remove the window's default title bar, and provides some
|
||||
shared methods for derived classes to call.
|
||||
*/
|
||||
|
||||
WindowAgentBasePrivate::WindowContextFactoryMethod
|
||||
WindowAgentBasePrivate::windowContextFactoryMethod = nullptr;
|
||||
|
||||
WindowAgentBasePrivate::WindowAgentBasePrivate() : q_ptr(nullptr), context(nullptr) {
|
||||
}
|
||||
|
||||
WindowAgentBasePrivate::~WindowAgentBasePrivate() = default;
|
||||
|
||||
void WindowAgentBasePrivate::init() {
|
||||
}
|
||||
|
||||
AbstractWindowContext *WindowAgentBasePrivate::createContext() const {
|
||||
if (windowContextFactoryMethod) {
|
||||
return windowContextFactoryMethod();
|
||||
}
|
||||
#if QWINDOWKIT_CONFIG(FORCE_QT_WINDOW_CONTEXT)
|
||||
return new QtWindowContext();
|
||||
#else
|
||||
# if defined(Q_OS_WINDOWS)
|
||||
return new Win32WindowContext();
|
||||
# elif defined(Q_OS_MAC)
|
||||
return new CocoaWindowContext();
|
||||
# elif defined(Q_OS_LINUX) && QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
if (Private::isWaylandPlatform() && Private::waylandAPI().isValid()) {
|
||||
return new LinuxWaylandContext();
|
||||
}
|
||||
if (Private::isX11Platform() && Private::x11API().isValid()) {
|
||||
return new LinuxX11Context();
|
||||
}
|
||||
# endif
|
||||
// Final fallback, no native features.
|
||||
return new QtWindowContext();
|
||||
#endif
|
||||
}
|
||||
|
||||
void WindowAgentBasePrivate::setup(QObject *host, WindowItemDelegate *delegate) {
|
||||
auto ctx = createContext();
|
||||
ctx->setup(host, delegate);
|
||||
context.reset(ctx);
|
||||
}
|
||||
|
||||
/*!
|
||||
Destructor.
|
||||
*/
|
||||
WindowAgentBase::~WindowAgentBase() = default;
|
||||
|
||||
/*!
|
||||
Returns the window attribute value.
|
||||
|
||||
\sa setWindowAttribute()
|
||||
*/
|
||||
QVariant WindowAgentBase::windowAttribute(const QString &key) const {
|
||||
Q_D(const WindowAgentBase);
|
||||
return d->context->windowAttribute(key);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the platform-related attribute for the window. Available attributes:
|
||||
|
||||
On Windows,
|
||||
\li \c no-system-menu: Specify a boolean value to disable the system menu.
|
||||
\li \c dwm-blur: Specify a boolean value to enable or disable dwm blur effect, this
|
||||
attribute is available on Windows 10 or later.
|
||||
\li \c dark-mode: Specify a boolean value to enable or disable the dark mode, it is
|
||||
enabled by default on Windows 10 if the system borders config is enabled. This
|
||||
attribute is available on Windows 10 or later.
|
||||
\li \c acrylic-material: Specify a boolean value to enable or disable acrylic material,
|
||||
this attribute is only available on Windows 11.
|
||||
\li \c mica: Specify a boolean value to enable or disable mica material,
|
||||
this attribute is only available on Windows 11.
|
||||
\li \c mica-alt: Specify a boolean value to enable or disable mica-alt material,
|
||||
this attribute is only available on Windows 11.
|
||||
\li \c extra-margins: Specify a margin value to change the \c dwm extended area
|
||||
geometry, you shouldn't change this attribute because it may break the
|
||||
internal state.
|
||||
\li \c border-thickness: Returns the system border thickness. (Readonly)
|
||||
\li \c title-bar-height: Returns the system title bar height, some system features may
|
||||
be related to this property so that it is recommended to set the custom title bar
|
||||
height to this value. (Readonly)
|
||||
|
||||
On macOS,
|
||||
\li \c no-system-buttons: Specify a boolean value to set the system buttons'
|
||||
visibility.
|
||||
\li \c blur-effect: You can specify a string value, "dark" to enable dark mode, "light"
|
||||
to set enable mode, "none" to disable. You can also specify a boolean value,
|
||||
\c true to enable current theme mode, \c false to disable.
|
||||
\li \c title-bar-height: Returns the system title bar height, the system button display
|
||||
area will be limited to this height. (Readonly)
|
||||
*/
|
||||
bool WindowAgentBase::setWindowAttribute(const QString &key, const QVariant &attribute) {
|
||||
Q_D(WindowAgentBase);
|
||||
return d->context->setWindowAttribute(key, attribute);
|
||||
}
|
||||
|
||||
/*!
|
||||
Shows the system menu, it's only implemented on Windows.
|
||||
*/
|
||||
void WindowAgentBase::showSystemMenu(const QPoint &pos) {
|
||||
Q_D(WindowAgentBase);
|
||||
d->context->showSystemMenu(pos);
|
||||
}
|
||||
|
||||
/*!
|
||||
Makes the window show in center of the current screen.
|
||||
*/
|
||||
void WindowAgentBase::centralize() {
|
||||
Q_D(WindowAgentBase);
|
||||
d->context->virtual_hook(AbstractWindowContext::CentralizeHook, nullptr);
|
||||
}
|
||||
|
||||
/*!
|
||||
Brings the window to top.
|
||||
*/
|
||||
void WindowAgentBase::raise() {
|
||||
Q_D(WindowAgentBase);
|
||||
d->context->virtual_hook(AbstractWindowContext::RaiseWindowHook, nullptr);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
WindowAgentBase::WindowAgentBase(WindowAgentBasePrivate &d, QObject *parent)
|
||||
: QObject(parent), d_ptr(&d) {
|
||||
d.q_ptr = this;
|
||||
|
||||
d.init();
|
||||
}
|
||||
|
||||
}
|
||||
50
libs/qwindowkit/src/core/windowagentbase.h
Normal file
50
libs/qwindowkit/src/core/windowagentbase.h
Normal file
@@ -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
|
||||
49
libs/qwindowkit/src/core/windowagentbase_p.h
Normal file
49
libs/qwindowkit/src/core/windowagentbase_p.h
Normal file
@@ -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
|
||||
23
libs/qwindowkit/src/core/windowitemdelegate.cpp
Normal file
23
libs/qwindowkit/src/core/windowitemdelegate.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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 "windowitemdelegate_p.h"
|
||||
|
||||
namespace QWK {
|
||||
|
||||
WindowItemDelegate::WindowItemDelegate() = default;
|
||||
|
||||
WindowItemDelegate::~WindowItemDelegate() = default;
|
||||
|
||||
void WindowItemDelegate::resetQtGrabbedControl(QObject *host) const {
|
||||
Q_UNUSED(host);
|
||||
}
|
||||
|
||||
WinIdChangeEventFilter *
|
||||
WindowItemDelegate::createWinIdEventFilter(QObject *host,
|
||||
AbstractWindowContext *context) const {
|
||||
return new WindowWinIdChangeEventFilter(static_cast<QWindow *>(host), context);
|
||||
}
|
||||
|
||||
}
|
||||
65
libs/qwindowkit/src/core/windowitemdelegate_p.h
Normal file
65
libs/qwindowkit/src/core/windowitemdelegate_p.h
Normal file
@@ -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
|
||||
33
libs/qwindowkit/src/quick/CMakeLists.txt
Normal file
33
libs/qwindowkit/src/quick/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
project(QWKQuick
|
||||
VERSION ${QWINDOWKIT_VERSION}
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
set(_src
|
||||
qwkquickglobal.h
|
||||
qwkquickglobal.cpp
|
||||
quickitemdelegate_p.h
|
||||
quickitemdelegate.cpp
|
||||
quickwindowagent.h
|
||||
quickwindowagent_p.h
|
||||
quickwindowagent.cpp
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND _src quickwindowagent_win.cpp)
|
||||
elseif(APPLE)
|
||||
list(APPEND _src quickwindowagent_mac.cpp)
|
||||
endif()
|
||||
|
||||
qwk_add_library(${PROJECT_NAME} AUTOGEN
|
||||
SOURCES ${_src}
|
||||
FEATURES cxx_std_17
|
||||
LINKS QWKCore
|
||||
QT_LINKS Core Gui Quick
|
||||
QT_INCLUDE_PRIVATE Core Gui Quick
|
||||
INCLUDE_PRIVATE *
|
||||
PREFIX QWK_QUICK
|
||||
)
|
||||
|
||||
set(QWINDOWKIT_ENABLED_TARGETS ${QWINDOWKIT_ENABLED_TARGETS} ${PROJECT_NAME} PARENT_SCOPE)
|
||||
set(QWINDOWKIT_ENABLED_SUBDIRECTORIES ${QWINDOWKIT_ENABLED_SUBDIRECTORIES} quick PARENT_SCOPE)
|
||||
84
libs/qwindowkit/src/quick/quickitemdelegate.cpp
Normal file
84
libs/qwindowkit/src/quick/quickitemdelegate.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
// 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 "quickitemdelegate_p.h"
|
||||
|
||||
#include <QtQuick/QQuickItem>
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
namespace QWK {
|
||||
|
||||
QuickItemDelegate::QuickItemDelegate() : WindowItemDelegate() {
|
||||
}
|
||||
|
||||
QuickItemDelegate::~QuickItemDelegate() = default;
|
||||
|
||||
QWindow *QuickItemDelegate::window(const QObject *obj) const {
|
||||
return static_cast<const QQuickItem *>(obj)->window();
|
||||
}
|
||||
|
||||
bool QuickItemDelegate::isEnabled(const QObject *obj) const {
|
||||
return static_cast<const QQuickItem *>(obj)->isEnabled();
|
||||
}
|
||||
|
||||
bool QuickItemDelegate::isVisible(const QObject *obj) const {
|
||||
return static_cast<const QQuickItem *>(obj)->isVisible();
|
||||
}
|
||||
|
||||
QRect QuickItemDelegate::mapGeometryToScene(const QObject *obj) const {
|
||||
auto item = static_cast<const QQuickItem *>(obj);
|
||||
const QPointF originPoint = item->mapToScene(QPointF(0.0, 0.0));
|
||||
const QSizeF size = item->size();
|
||||
return QRectF(originPoint, size).toRect();
|
||||
}
|
||||
|
||||
QWindow *QuickItemDelegate::hostWindow(const QObject *host) const {
|
||||
return static_cast<QQuickWindow *>(const_cast<QObject *>(host));
|
||||
}
|
||||
|
||||
bool QuickItemDelegate::isWindowActive(const QObject *host) const {
|
||||
return static_cast<const QQuickWindow *>(host)->isActive();
|
||||
}
|
||||
|
||||
Qt::WindowStates QuickItemDelegate::getWindowState(const QObject *host) const {
|
||||
return static_cast<const QQuickWindow *>(host)->windowStates();
|
||||
}
|
||||
|
||||
void QuickItemDelegate::setWindowState(QObject *host, Qt::WindowStates state) const {
|
||||
static_cast<QQuickWindow *>(host)->setWindowStates(state);
|
||||
}
|
||||
|
||||
void QuickItemDelegate::setCursorShape(QObject *host, const Qt::CursorShape shape) const {
|
||||
static_cast<QQuickWindow *>(host)->setCursor(QCursor(shape));
|
||||
}
|
||||
|
||||
void QuickItemDelegate::restoreCursorShape(QObject *host) const {
|
||||
static_cast<QQuickWindow *>(host)->unsetCursor();
|
||||
}
|
||||
|
||||
Qt::WindowFlags QuickItemDelegate::getWindowFlags(const QObject *host) const {
|
||||
return static_cast<const QQuickWindow *>(host)->flags();
|
||||
}
|
||||
|
||||
QRect QuickItemDelegate::getGeometry(const QObject *host) const {
|
||||
return static_cast<const QQuickWindow *>(host)->geometry();
|
||||
}
|
||||
|
||||
void QuickItemDelegate::setWindowFlags(QObject *host, Qt::WindowFlags flags) const {
|
||||
static_cast<QQuickWindow *>(host)->setFlags(flags);
|
||||
}
|
||||
|
||||
void QuickItemDelegate::setWindowVisible(QObject *host, bool visible) const {
|
||||
static_cast<QQuickWindow *>(host)->setVisible(visible);
|
||||
}
|
||||
|
||||
void QuickItemDelegate::setGeometry(QObject *host, const QRect &rect) {
|
||||
static_cast<QQuickWindow *>(host)->setGeometry(rect);
|
||||
}
|
||||
|
||||
void QuickItemDelegate::bringWindowToTop(QObject *host) const {
|
||||
static_cast<QQuickWindow *>(host)->raise();
|
||||
}
|
||||
|
||||
}
|
||||
53
libs/qwindowkit/src/quick/quickitemdelegate_p.h
Normal file
53
libs/qwindowkit/src/quick/quickitemdelegate_p.h
Normal file
@@ -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 QUICKITEMDELEGATE_P_H
|
||||
#define QUICKITEMDELEGATE_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 <QWKQuick/qwkquickglobal.h>
|
||||
|
||||
namespace QWK {
|
||||
|
||||
class QWK_QUICK_EXPORT QuickItemDelegate : public WindowItemDelegate {
|
||||
public:
|
||||
QuickItemDelegate();
|
||||
~QuickItemDelegate() 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 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // QUICKITEMDELEGATE_P_H
|
||||
107
libs/qwindowkit/src/quick/quickwindowagent.cpp
Normal file
107
libs/qwindowkit/src/quick/quickwindowagent.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
// 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 "quickwindowagent.h"
|
||||
#include "quickwindowagent_p.h"
|
||||
|
||||
#include <QtQuick/QQuickWindow>
|
||||
#include <QtQuick/private/qquickanchors_p.h>
|
||||
|
||||
#include "quickitemdelegate_p.h"
|
||||
|
||||
namespace QWK {
|
||||
|
||||
/*!
|
||||
\class QuickWindowAgent
|
||||
\brief QuickWindowAgent is the window agent for QtQuick.
|
||||
|
||||
It provides interfaces for QtQuick and processes some Qt events related to the QQuickItem
|
||||
instance. The usage of all APIs is consistent with the \a Widgets module.
|
||||
*/
|
||||
|
||||
QuickWindowAgentPrivate::QuickWindowAgentPrivate() = default;
|
||||
|
||||
QuickWindowAgentPrivate::~QuickWindowAgentPrivate() = default;
|
||||
|
||||
void QuickWindowAgentPrivate::init() {
|
||||
}
|
||||
|
||||
QuickWindowAgent::QuickWindowAgent(QObject *parent)
|
||||
: QuickWindowAgent(*new QuickWindowAgentPrivate(), parent) {
|
||||
}
|
||||
|
||||
QuickWindowAgent::~QuickWindowAgent() = default;
|
||||
|
||||
bool QuickWindowAgent::setup(QQuickWindow *window) {
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_D(QuickWindowAgent);
|
||||
if (d->hostWindow) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d->setup(window, new QuickItemDelegate());
|
||||
d->hostWindow = window;
|
||||
|
||||
#if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS)
|
||||
d->setupWindows10BorderWorkaround();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
QQuickItem *QuickWindowAgent::titleBar() const {
|
||||
Q_D(const QuickWindowAgent);
|
||||
return static_cast<QQuickItem *>(d->context->titleBar());
|
||||
}
|
||||
|
||||
void QuickWindowAgent::setTitleBar(QQuickItem *item) {
|
||||
Q_D(QuickWindowAgent);
|
||||
if (!d->context->setTitleBar(item)) {
|
||||
return;
|
||||
}
|
||||
#ifdef Q_OS_MAC
|
||||
setSystemButtonArea(nullptr);
|
||||
#endif
|
||||
Q_EMIT titleBarWidgetChanged(item);
|
||||
}
|
||||
|
||||
QQuickItem *QuickWindowAgent::systemButton(SystemButton button) const {
|
||||
Q_D(const QuickWindowAgent);
|
||||
return static_cast<QQuickItem *>(d->context->systemButton(button));
|
||||
}
|
||||
|
||||
void QuickWindowAgent::setSystemButton(SystemButton button, QQuickItem *item) {
|
||||
Q_D(QuickWindowAgent);
|
||||
if (!d->context->setSystemButton(button, item)) {
|
||||
return;
|
||||
}
|
||||
Q_EMIT systemButtonChanged(button, item);
|
||||
}
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
bool QuickWindowAgent::isHitTestVisible(QQuickItem *item) const {
|
||||
#else
|
||||
bool QuickWindowAgent::isHitTestVisible(const QQuickItem *item) const {
|
||||
#endif
|
||||
Q_D(const QuickWindowAgent);
|
||||
return d->context->isHitTestVisible(item);
|
||||
}
|
||||
|
||||
void QuickWindowAgent::setHitTestVisible(QQuickItem *item, bool visible) {
|
||||
Q_D(QuickWindowAgent);
|
||||
d->context->setHitTestVisible(item, visible);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QuickWindowAgent::QuickWindowAgent(QuickWindowAgentPrivate &d, QObject *parent)
|
||||
: WindowAgentBase(d, parent) {
|
||||
d.init();
|
||||
}
|
||||
|
||||
}
|
||||
60
libs/qwindowkit/src/quick/quickwindowagent.h
Normal file
60
libs/qwindowkit/src/quick/quickwindowagent.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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 QUICKWINDOWAGENT_H
|
||||
#define QUICKWINDOWAGENT_H
|
||||
|
||||
#include <QtQuick/QQuickItem>
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
#include <QWKCore/windowagentbase.h>
|
||||
#include <QWKQuick/qwkquickglobal.h>
|
||||
|
||||
namespace QWK {
|
||||
|
||||
class QuickWindowAgentPrivate;
|
||||
|
||||
class QWK_QUICK_EXPORT QuickWindowAgent : public WindowAgentBase {
|
||||
Q_OBJECT
|
||||
Q_DECLARE_PRIVATE(QuickWindowAgent)
|
||||
public:
|
||||
explicit QuickWindowAgent(QObject *parent = nullptr);
|
||||
~QuickWindowAgent() override;
|
||||
|
||||
public:
|
||||
Q_INVOKABLE bool setup(QQuickWindow *window);
|
||||
|
||||
Q_INVOKABLE QQuickItem *titleBar() const;
|
||||
Q_INVOKABLE void setTitleBar(QQuickItem *item);
|
||||
|
||||
Q_INVOKABLE QQuickItem *systemButton(SystemButton button) const;
|
||||
Q_INVOKABLE void setSystemButton(SystemButton button, QQuickItem *item);
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
Q_INVOKABLE bool isHitTestVisible(QQuickItem *item) const;
|
||||
#else
|
||||
Q_INVOKABLE bool isHitTestVisible(const QQuickItem *item) const;
|
||||
#endif
|
||||
Q_INVOKABLE void setHitTestVisible(QQuickItem *item, bool visible = true);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// The system button area APIs are experimental, very likely to change in the future.
|
||||
Q_INVOKABLE QQuickItem *systemButtonArea() const;
|
||||
Q_INVOKABLE void setSystemButtonArea(QQuickItem *item);
|
||||
|
||||
Q_INVOKABLE ScreenRectCallback systemButtonAreaCallback() const;
|
||||
Q_INVOKABLE void setSystemButtonAreaCallback(const ScreenRectCallback &callback);
|
||||
#endif
|
||||
|
||||
Q_SIGNALS:
|
||||
void titleBarWidgetChanged(QQuickItem *item);
|
||||
void systemButtonChanged(SystemButton button, QQuickItem *item);
|
||||
|
||||
protected:
|
||||
QuickWindowAgent(QuickWindowAgentPrivate &d, QObject *parent = nullptr);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // QUICKWINDOWAGENT_H
|
||||
75
libs/qwindowkit/src/quick/quickwindowagent_mac.cpp
Normal file
75
libs/qwindowkit/src/quick/quickwindowagent_mac.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// 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 "quickwindowagent_p.h"
|
||||
|
||||
namespace QWK {
|
||||
|
||||
class SystemButtonAreaItemHandler : public QObject {
|
||||
public:
|
||||
SystemButtonAreaItemHandler(QQuickItem *item, AbstractWindowContext *ctx,
|
||||
QObject *parent = nullptr);
|
||||
~SystemButtonAreaItemHandler() override = default;
|
||||
|
||||
void updateSystemButtonArea();
|
||||
|
||||
protected:
|
||||
QQuickItem *item;
|
||||
AbstractWindowContext *ctx;
|
||||
};
|
||||
|
||||
SystemButtonAreaItemHandler::SystemButtonAreaItemHandler(QQuickItem *item,
|
||||
AbstractWindowContext *ctx,
|
||||
QObject *parent)
|
||||
: QObject(parent), item(item), ctx(ctx) {
|
||||
connect(item, &QQuickItem::xChanged, this,
|
||||
&SystemButtonAreaItemHandler::updateSystemButtonArea);
|
||||
connect(item, &QQuickItem::yChanged, this,
|
||||
&SystemButtonAreaItemHandler::updateSystemButtonArea);
|
||||
connect(item, &QQuickItem::widthChanged, this,
|
||||
&SystemButtonAreaItemHandler::updateSystemButtonArea);
|
||||
connect(item, &QQuickItem::heightChanged, this,
|
||||
&SystemButtonAreaItemHandler::updateSystemButtonArea);
|
||||
|
||||
ctx->setSystemButtonAreaCallback([item](const QSize &) {
|
||||
return QRectF(item->mapToScene(QPointF(0, 0)), item->size()).toRect(); //
|
||||
});
|
||||
}
|
||||
|
||||
void SystemButtonAreaItemHandler::updateSystemButtonArea() {
|
||||
ctx->virtual_hook(AbstractWindowContext::SystemButtonAreaChangedHook, nullptr);
|
||||
}
|
||||
|
||||
QQuickItem *QuickWindowAgent::systemButtonArea() const {
|
||||
Q_D(const QuickWindowAgent);
|
||||
return d->systemButtonAreaItem;
|
||||
}
|
||||
|
||||
void QuickWindowAgent::setSystemButtonArea(QQuickItem *item) {
|
||||
Q_D(QuickWindowAgent);
|
||||
if (d->systemButtonAreaItem == item)
|
||||
return;
|
||||
|
||||
auto ctx = d->context.get();
|
||||
d->systemButtonAreaItem = item;
|
||||
if (!item) {
|
||||
d->systemButtonAreaItemHandler.reset();
|
||||
ctx->setSystemButtonAreaCallback({});
|
||||
return;
|
||||
}
|
||||
d->systemButtonAreaItemHandler = std::make_unique<SystemButtonAreaItemHandler>(item, ctx);
|
||||
}
|
||||
|
||||
ScreenRectCallback QuickWindowAgent::systemButtonAreaCallback() const {
|
||||
Q_D(const QuickWindowAgent);
|
||||
return d->systemButtonAreaItem ? nullptr : d->context->systemButtonAreaCallback();
|
||||
}
|
||||
|
||||
void QuickWindowAgent::setSystemButtonAreaCallback(const ScreenRectCallback &callback) {
|
||||
Q_D(QuickWindowAgent);
|
||||
setSystemButtonArea(nullptr);
|
||||
d->context->setSystemButtonAreaCallback(callback);
|
||||
}
|
||||
|
||||
}
|
||||
46
libs/qwindowkit/src/quick/quickwindowagent_p.h
Normal file
46
libs/qwindowkit/src/quick/quickwindowagent_p.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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 QUICKWINDOWAGENTPRIVATE_H
|
||||
#define QUICKWINDOWAGENTPRIVATE_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 <QWKQuick/quickwindowagent.h>
|
||||
|
||||
namespace QWK {
|
||||
|
||||
class QuickWindowAgentPrivate : public WindowAgentBasePrivate {
|
||||
Q_DECLARE_PUBLIC(QuickWindowAgent)
|
||||
public:
|
||||
QuickWindowAgentPrivate();
|
||||
~QuickWindowAgentPrivate() override;
|
||||
|
||||
void init();
|
||||
|
||||
// Host
|
||||
QQuickWindow *hostWindow{};
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QQuickItem *systemButtonAreaItem{};
|
||||
std::unique_ptr<QObject> systemButtonAreaItemHandler;
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS)
|
||||
void setupWindows10BorderWorkaround();
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // QUICKWINDOWAGENTPRIVATE_H
|
||||
203
libs/qwindowkit/src/quick/quickwindowagent_win.cpp
Normal file
203
libs/qwindowkit/src/quick/quickwindowagent_win.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
// 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 "quickwindowagent_p.h"
|
||||
|
||||
#include <QtQuick/QQuickPaintedItem>
|
||||
#include <QtQuick/private/qquickitem_p.h>
|
||||
|
||||
#include <QWKCore/qwindowkit_windows.h>
|
||||
#include <QWKCore/private/windows10borderhandler_p.h>
|
||||
|
||||
namespace QWK {
|
||||
|
||||
static inline bool isWindows1022H2OrGreater() {
|
||||
QWK_OSVERSIONINFOW rovi = Private::GetRealOSVersion();
|
||||
return (rovi.dwMajorVersion > 10) ||
|
||||
(rovi.dwMajorVersion == 10 &&
|
||||
(rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 19045));
|
||||
}
|
||||
|
||||
#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS)
|
||||
|
||||
class BorderItem : public QQuickPaintedItem, public Windows10BorderHandler {
|
||||
public:
|
||||
explicit BorderItem(QQuickItem *parent, AbstractWindowContext *context);
|
||||
~BorderItem() override;
|
||||
|
||||
bool shouldEnableEmulatedPainter() const;
|
||||
void updateGeometry() override;
|
||||
|
||||
public:
|
||||
void paint(QPainter *painter) override;
|
||||
void itemChange(ItemChange change, const ItemChangeData &data) override;
|
||||
|
||||
protected:
|
||||
bool sharedEventFilter(QObject *obj, QEvent *event) override;
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message,
|
||||
QT_NATIVE_EVENT_RESULT_TYPE *result) override;
|
||||
|
||||
private:
|
||||
bool needNativePaint = false;
|
||||
|
||||
void _q_afterSynchronizing();
|
||||
void _q_windowActivityChanged();
|
||||
};
|
||||
|
||||
bool BorderItem::shouldEnableEmulatedPainter() const {
|
||||
# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
const QQuickWindow *win = window();
|
||||
if (!win) {
|
||||
return true;
|
||||
}
|
||||
auto api = win->rendererInterface()->graphicsApi();
|
||||
switch (api) {
|
||||
case QSGRendererInterface::OpenGL:
|
||||
// FIXME: may be wrong in earlier Windows 10.
|
||||
return false;
|
||||
case QSGRendererInterface::Direct3D11:
|
||||
# if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
|
||||
case QSGRendererInterface::Direct3D12:
|
||||
# endif
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
# endif
|
||||
return true;
|
||||
}
|
||||
|
||||
BorderItem::BorderItem(QQuickItem *parent, AbstractWindowContext *context)
|
||||
: QQuickPaintedItem(parent), Windows10BorderHandler(context) {
|
||||
setAntialiasing(true); // We need anti-aliasing to give us better result.
|
||||
setFillColor(Qt::transparent); // Will improve the performance a little bit.
|
||||
setOpaquePainting(true); // Will also improve the performance, we don't draw
|
||||
// semi-transparent borders of course.
|
||||
|
||||
auto parentPri = QQuickItemPrivate::get(parent);
|
||||
auto anchors = QQuickItemPrivate::get(this)->anchors();
|
||||
|
||||
// Workaround for top border
|
||||
// anchors->setTop(parentPri->top());
|
||||
|
||||
anchors->setLeft(parentPri->left());
|
||||
anchors->setRight(parentPri->right());
|
||||
|
||||
setZ(std::numeric_limits<qreal>::max()); // Make sure our fake border always above
|
||||
// everything in the window.
|
||||
|
||||
# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
connect(window(), &QQuickWindow::afterSynchronizing, this,
|
||||
&BorderItem::_q_afterSynchronizing, Qt::DirectConnection);
|
||||
# endif
|
||||
connect(window(), &QQuickWindow::activeChanged, this,
|
||||
&BorderItem::_q_windowActivityChanged);
|
||||
|
||||
// First update
|
||||
if (context->windowId()) {
|
||||
setupNecessaryAttributes();
|
||||
}
|
||||
BorderItem::updateGeometry();
|
||||
}
|
||||
|
||||
BorderItem::~BorderItem() = default;
|
||||
|
||||
void BorderItem::updateGeometry() {
|
||||
const QQuickWindow *win = window();
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
# if QT_VERSION_MAJOR < 6
|
||||
setHeight(1);
|
||||
# else
|
||||
// Workaround for top border
|
||||
// When the height is less than 0.5, it will be regarded as invisible, we apply this
|
||||
// workaround to make it slightly exposed. When the height is too big, a transparent gap
|
||||
// will appear on the upper frame.
|
||||
setHeight(0.5);
|
||||
setY(-0.49);
|
||||
# endif
|
||||
setVisible(isNormalWindow());
|
||||
}
|
||||
|
||||
void BorderItem::paint(QPainter *painter) {
|
||||
Q_UNUSED(painter)
|
||||
if (shouldEnableEmulatedPainter()) {
|
||||
drawBorderEmulated(painter, QRect({0, 0}, size().toSize()));
|
||||
} else {
|
||||
needNativePaint = true;
|
||||
}
|
||||
}
|
||||
|
||||
void BorderItem::itemChange(ItemChange change, const ItemChangeData &data) {
|
||||
QQuickPaintedItem::itemChange(change, data);
|
||||
switch (change) {
|
||||
case ItemVisibleHasChanged:
|
||||
case ItemDevicePixelRatioHasChanged: {
|
||||
updateGeometry();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool BorderItem::sharedEventFilter(QObject *obj, QEvent *event) {
|
||||
Q_UNUSED(obj)
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::WindowStateChange: {
|
||||
updateGeometry();
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return Windows10BorderHandler::sharedEventFilter(obj, event);
|
||||
}
|
||||
|
||||
bool BorderItem::nativeEventFilter(const QByteArray &eventType, void *message,
|
||||
QT_NATIVE_EVENT_RESULT_TYPE *result) {
|
||||
const auto msg = static_cast<const MSG *>(message);
|
||||
switch (msg->message) {
|
||||
case WM_THEMECHANGED:
|
||||
case WM_SYSCOLORCHANGE:
|
||||
case WM_DWMCOLORIZATIONCOLORCHANGED: {
|
||||
update();
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_SETTINGCHANGE: {
|
||||
if (isImmersiveColorSetChange(msg->wParam, msg->lParam)) {
|
||||
update();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return Windows10BorderHandler::nativeEventFilter(eventType, message, result);
|
||||
}
|
||||
|
||||
void BorderItem::_q_afterSynchronizing() {
|
||||
if (needNativePaint) {
|
||||
needNativePaint = false;
|
||||
drawBorderNative();
|
||||
}
|
||||
}
|
||||
|
||||
void BorderItem::_q_windowActivityChanged() {
|
||||
update();
|
||||
}
|
||||
|
||||
void QuickWindowAgentPrivate::setupWindows10BorderWorkaround() {
|
||||
// Install painting hook
|
||||
auto ctx = context.get();
|
||||
if (ctx->windowAttribute(QStringLiteral("win10-border-needed")).toBool()) {
|
||||
std::ignore = new BorderItem(hostWindow->contentItem(), ctx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
29
libs/qwindowkit/src/quick/qwkquickglobal.cpp
Normal file
29
libs/qwindowkit/src/quick/qwkquickglobal.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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 "qwkquickglobal.h"
|
||||
|
||||
#include <QtQml/QQmlEngine>
|
||||
|
||||
#include "quickwindowagent.h"
|
||||
|
||||
namespace QWK {
|
||||
|
||||
static constexpr const char kModuleUri[] = "QWindowKit";
|
||||
|
||||
void registerTypes(QQmlEngine *engine) {
|
||||
Q_UNUSED(engine);
|
||||
|
||||
static bool once = false;
|
||||
if (once) {
|
||||
return;
|
||||
}
|
||||
once = true;
|
||||
|
||||
// @uri QWindowKit
|
||||
qmlRegisterType<QuickWindowAgent>(kModuleUri, 1, 0, "WindowAgent");
|
||||
qmlRegisterModule(kModuleUri, 1, 0);
|
||||
}
|
||||
|
||||
}
|
||||
32
libs/qwindowkit/src/quick/qwkquickglobal.h
Normal file
32
libs/qwindowkit/src/quick/qwkquickglobal.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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 QWKQUICKGLOBAL_H
|
||||
#define QWKQUICKGLOBAL_H
|
||||
|
||||
#include <QtCore/QtGlobal>
|
||||
|
||||
#ifndef QWK_QUICK_EXPORT
|
||||
# ifdef QWK_QUICK_STATIC
|
||||
# define QWK_QUICK_EXPORT
|
||||
# else
|
||||
# ifdef QWK_QUICK_LIBRARY
|
||||
# define QWK_QUICK_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define QWK_QUICK_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QQmlEngine;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace QWK {
|
||||
|
||||
QWK_QUICK_EXPORT void registerTypes(QQmlEngine *engine);
|
||||
|
||||
}
|
||||
|
||||
#endif // QWKQUICKGLOBAL_H
|
||||
32
libs/qwindowkit/src/widgets/CMakeLists.txt
Normal file
32
libs/qwindowkit/src/widgets/CMakeLists.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
project(QWKWidgets
|
||||
VERSION ${QWINDOWKIT_VERSION}
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
set(_src
|
||||
qwkwidgetsglobal.h
|
||||
widgetitemdelegate_p.h
|
||||
widgetitemdelegate.cpp
|
||||
widgetwindowagent.h
|
||||
widgetwindowagent_p.h
|
||||
widgetwindowagent.cpp
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND _src widgetwindowagent_win.cpp)
|
||||
elseif(APPLE)
|
||||
list(APPEND _src widgetwindowagent_mac.cpp)
|
||||
endif()
|
||||
|
||||
qwk_add_library(${PROJECT_NAME} AUTOGEN
|
||||
SOURCES ${_src}
|
||||
FEATURES cxx_std_17
|
||||
LINKS QWKCore
|
||||
QT_LINKS Core Gui Widgets
|
||||
QT_INCLUDE_PRIVATE Core Gui Widgets
|
||||
INCLUDE_PRIVATE *
|
||||
PREFIX QWK_WIDGETS
|
||||
)
|
||||
|
||||
set(QWINDOWKIT_ENABLED_TARGETS ${QWINDOWKIT_ENABLED_TARGETS} ${PROJECT_NAME} PARENT_SCOPE)
|
||||
set(QWINDOWKIT_ENABLED_SUBDIRECTORIES ${QWINDOWKIT_ENABLED_SUBDIRECTORIES} widgets PARENT_SCOPE)
|
||||
22
libs/qwindowkit/src/widgets/qwkwidgetsglobal.h
Normal file
22
libs/qwindowkit/src/widgets/qwkwidgetsglobal.h
Normal file
@@ -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
|
||||
131
libs/qwindowkit/src/widgets/widgetitemdelegate.cpp
Normal file
131
libs/qwindowkit/src/widgets/widgetitemdelegate.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
// 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 "widgetitemdelegate_p.h"
|
||||
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QWidget>
|
||||
|
||||
#include <QWKCore/private/abstractwindowcontext_p.h>
|
||||
|
||||
extern Q_DECL_IMPORT QWidget *qt_button_down;
|
||||
|
||||
namespace QWK {
|
||||
|
||||
class WidgetWinIdChangeEventFilter : public WinIdChangeEventFilter {
|
||||
public:
|
||||
explicit WidgetWinIdChangeEventFilter(QObject *host, AbstractWindowContext *ctx)
|
||||
: WinIdChangeEventFilter(host, ctx), widget(static_cast<QWidget *>(host)) {
|
||||
widget->installEventFilter(this);
|
||||
}
|
||||
|
||||
WId winId() const override {
|
||||
return widget->effectiveWinId();
|
||||
}
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override {
|
||||
Q_UNUSED(obj)
|
||||
if (event->type() == QEvent::WinIdChange) {
|
||||
context->notifyWinIdChange();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QWidget *widget;
|
||||
};
|
||||
|
||||
WidgetItemDelegate::WidgetItemDelegate() = default;
|
||||
|
||||
WidgetItemDelegate::~WidgetItemDelegate() = default;
|
||||
|
||||
QWindow *WidgetItemDelegate::window(const QObject *obj) const {
|
||||
return static_cast<const QWidget *>(obj)->windowHandle();
|
||||
}
|
||||
|
||||
bool WidgetItemDelegate::isEnabled(const QObject *obj) const {
|
||||
return static_cast<const QWidget *>(obj)->isEnabled();
|
||||
}
|
||||
|
||||
bool WidgetItemDelegate::isVisible(const QObject *obj) const {
|
||||
return static_cast<const QWidget *>(obj)->isVisible();
|
||||
}
|
||||
|
||||
QRect WidgetItemDelegate::mapGeometryToScene(const QObject *obj) const {
|
||||
auto widget = static_cast<const QWidget *>(obj);
|
||||
const QPoint originPoint = widget->mapTo(widget->window(), QPoint(0, 0));
|
||||
const QSize size = widget->size();
|
||||
return {originPoint, size};
|
||||
}
|
||||
|
||||
QWindow *WidgetItemDelegate::hostWindow(const QObject *host) const {
|
||||
return static_cast<const QWidget *>(host)->windowHandle();
|
||||
}
|
||||
|
||||
bool WidgetItemDelegate::isWindowActive(const QObject *host) const {
|
||||
return static_cast<const QWidget *>(host)->isActiveWindow();
|
||||
}
|
||||
|
||||
void WidgetItemDelegate::resetQtGrabbedControl(QObject *host) const {
|
||||
Q_UNUSED(host);
|
||||
if (!qt_button_down) {
|
||||
return;
|
||||
}
|
||||
static constexpr const auto invalidPos =
|
||||
QPoint{std::numeric_limits<int>::lowest(), std::numeric_limits<int>::lowest()};
|
||||
const auto event = new QMouseEvent(
|
||||
QEvent::MouseButtonRelease, invalidPos, invalidPos, invalidPos, Qt::LeftButton,
|
||||
QGuiApplication::mouseButtons() ^ Qt::LeftButton, QGuiApplication::keyboardModifiers());
|
||||
QCoreApplication::postEvent(qt_button_down, event);
|
||||
qt_button_down = nullptr;
|
||||
}
|
||||
|
||||
Qt::WindowStates WidgetItemDelegate::getWindowState(const QObject *host) const {
|
||||
return static_cast<const QWidget *>(host)->windowState();
|
||||
}
|
||||
|
||||
void WidgetItemDelegate::setWindowState(QObject *host, Qt::WindowStates state) const {
|
||||
static_cast<QWidget *>(host)->setWindowState(state);
|
||||
}
|
||||
|
||||
void WidgetItemDelegate::setCursorShape(QObject *host, Qt::CursorShape shape) const {
|
||||
static_cast<QWidget *>(host)->setCursor(QCursor(shape));
|
||||
}
|
||||
|
||||
void WidgetItemDelegate::restoreCursorShape(QObject *host) const {
|
||||
static_cast<QWidget *>(host)->unsetCursor();
|
||||
}
|
||||
|
||||
Qt::WindowFlags WidgetItemDelegate::getWindowFlags(const QObject *host) const {
|
||||
return static_cast<const QWidget *>(host)->windowFlags();
|
||||
}
|
||||
|
||||
QRect WidgetItemDelegate::getGeometry(const QObject *host) const {
|
||||
return static_cast<const QWidget *>(host)->geometry();
|
||||
}
|
||||
|
||||
void WidgetItemDelegate::setWindowFlags(QObject *host, Qt::WindowFlags flags) const {
|
||||
static_cast<QWidget *>(host)->setWindowFlags(flags);
|
||||
}
|
||||
|
||||
void WidgetItemDelegate::setWindowVisible(QObject *host, bool visible) const {
|
||||
static_cast<QWidget *>(host)->setVisible(visible);
|
||||
}
|
||||
|
||||
void WidgetItemDelegate::setGeometry(QObject *host, const QRect &rect) {
|
||||
static_cast<QWidget *>(host)->setGeometry(rect);
|
||||
}
|
||||
|
||||
void WidgetItemDelegate::bringWindowToTop(QObject *host) const {
|
||||
static_cast<QWidget *>(host)->raise();
|
||||
}
|
||||
|
||||
WinIdChangeEventFilter *
|
||||
WidgetItemDelegate::createWinIdEventFilter(QObject *host,
|
||||
AbstractWindowContext *context) const {
|
||||
return new WidgetWinIdChangeEventFilter(host, context);
|
||||
}
|
||||
|
||||
}
|
||||
57
libs/qwindowkit/src/widgets/widgetitemdelegate_p.h
Normal file
57
libs/qwindowkit/src/widgets/widgetitemdelegate_p.h
Normal file
@@ -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
|
||||
163
libs/qwindowkit/src/widgets/widgetwindowagent.cpp
Normal file
163
libs/qwindowkit/src/widgets/widgetwindowagent.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
// 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 "widgetwindowagent.h"
|
||||
#include "widgetwindowagent_p.h"
|
||||
|
||||
#include <QtGui/QtEvents>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "widgetitemdelegate_p.h"
|
||||
|
||||
namespace QWK {
|
||||
|
||||
/*!
|
||||
\class WidgetWindowAgent
|
||||
\brief WindowAgentBase is the window agent for QtWidgets.
|
||||
|
||||
It provides interfaces for QtWidgets and processes some Qt events related to the QWidget
|
||||
instance.
|
||||
*/
|
||||
|
||||
WidgetWindowAgentPrivate::WidgetWindowAgentPrivate() = default;
|
||||
|
||||
WidgetWindowAgentPrivate::~WidgetWindowAgentPrivate() = default;
|
||||
|
||||
void WidgetWindowAgentPrivate::init() {
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a widget agent, it's better to set the widget to setup as \a parent.
|
||||
*/
|
||||
WidgetWindowAgent::WidgetWindowAgent(QObject *parent)
|
||||
: WidgetWindowAgent(*new WidgetWindowAgentPrivate(), parent) {
|
||||
}
|
||||
|
||||
/*!
|
||||
Destructor.
|
||||
*/
|
||||
WidgetWindowAgent::~WidgetWindowAgent() = default;
|
||||
|
||||
/*!
|
||||
Installs the window agent on the widget. The window agent will take over some of the window
|
||||
events, making the window look frameless.
|
||||
*/
|
||||
bool WidgetWindowAgent::setup(QWidget *w) {
|
||||
Q_ASSERT(w);
|
||||
if (!w) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(_DEBUG) || !defined(NDEBUG)
|
||||
if (w->inherits("QDockWidget")) {
|
||||
qWarning()
|
||||
<< "QWK::WidgetWindowAgent: QWindowKit does not support QDockWidget as top window.";
|
||||
}
|
||||
#endif
|
||||
|
||||
Q_D(WidgetWindowAgent);
|
||||
if (d->hostWidget) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Qt will create invisible native window container for native QWidget
|
||||
// without this attribute, and this behavior will break QWK functionality.
|
||||
// So far enabling this attribute is a must for QWK users.
|
||||
w->setAttribute(Qt::WA_DontCreateNativeAncestors);
|
||||
// Make sure the native window handle is actually created before we apply
|
||||
// various hooks.
|
||||
// w->setAttribute(Qt::WA_NativeWindow); // ### FIXME: Check
|
||||
|
||||
d->setup(w, new WidgetItemDelegate());
|
||||
d->hostWidget = w;
|
||||
|
||||
#if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS)
|
||||
d->setupWindows10BorderWorkaround();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the title bar widget.
|
||||
*/
|
||||
QWidget *WidgetWindowAgent::titleBar() const {
|
||||
Q_D(const WidgetWindowAgent);
|
||||
return static_cast<QWidget *>(d->context->titleBar());
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the title bar widget, all system button and hit-test visible widget references that
|
||||
have been set will be removed.
|
||||
*/
|
||||
void WidgetWindowAgent::setTitleBar(QWidget *w) {
|
||||
Q_D(WidgetWindowAgent);
|
||||
if (!d->context->setTitleBar(w)) {
|
||||
return;
|
||||
}
|
||||
#ifdef Q_OS_MAC
|
||||
setSystemButtonArea(nullptr);
|
||||
#endif
|
||||
Q_EMIT titleBarChanged(w);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the system button of the given type.
|
||||
*/
|
||||
QWidget *WidgetWindowAgent::systemButton(SystemButton button) const {
|
||||
Q_D(const WidgetWindowAgent);
|
||||
return static_cast<QWidget *>(d->context->systemButton(button));
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the system button of the given type, the system buttons always receive mouse events so
|
||||
you don't need to call \c setHitTestVisible for them.
|
||||
*/
|
||||
void WidgetWindowAgent::setSystemButton(SystemButton button, QWidget *w) {
|
||||
Q_D(WidgetWindowAgent);
|
||||
if (!d->context->setSystemButton(button, w)) {
|
||||
return;
|
||||
}
|
||||
Q_EMIT systemButtonChanged(button, w);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \a true if the widget can receive mouse events on title bar.
|
||||
*/
|
||||
bool WidgetWindowAgent::isHitTestVisible(const QWidget *w) const {
|
||||
Q_D(const WidgetWindowAgent);
|
||||
return d->context->isHitTestVisible(w);
|
||||
}
|
||||
|
||||
/*!
|
||||
Makes the widget able to receive mouse events on title bar if \a visible is \c true.
|
||||
You're supposed to make sure that the specified widget \a w is a child or descendant
|
||||
of the title bar widget.
|
||||
*/
|
||||
void WidgetWindowAgent::setHitTestVisible(QWidget *w, bool visible) {
|
||||
Q_D(WidgetWindowAgent);
|
||||
d->context->setHitTestVisible(w, visible);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
WidgetWindowAgent::WidgetWindowAgent(WidgetWindowAgentPrivate &d, QObject *parent)
|
||||
: WindowAgentBase(d, parent) {
|
||||
d.init();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn void WidgetWindowAgent::titleBarChanged(const QWidget *w)
|
||||
|
||||
This signal is emitted when the title bar widget is replaced.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void WidgetWindowAgent::systemButtonChanged(SystemButton button, const QWidget *w)
|
||||
|
||||
This signal is emitted when a system button is replaced.
|
||||
*/
|
||||
|
||||
}
|
||||
55
libs/qwindowkit/src/widgets/widgetwindowagent.h
Normal file
55
libs/qwindowkit/src/widgets/widgetwindowagent.h
Normal file
@@ -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
|
||||
98
libs/qwindowkit/src/widgets/widgetwindowagent_mac.cpp
Normal file
98
libs/qwindowkit/src/widgets/widgetwindowagent_mac.cpp
Normal file
@@ -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 "widgetwindowagent_p.h"
|
||||
|
||||
#include <QtGui/QtEvents>
|
||||
|
||||
namespace QWK {
|
||||
|
||||
static inline QRect getWidgetSceneRect(QWidget *widget) {
|
||||
return {widget->mapTo(widget->window(), QPoint()), widget->size()};
|
||||
}
|
||||
|
||||
class SystemButtonAreaWidgetEventFilter : public QObject {
|
||||
public:
|
||||
SystemButtonAreaWidgetEventFilter(QWidget *widget, AbstractWindowContext *ctx,
|
||||
QObject *parent = nullptr)
|
||||
: QObject(parent), widget(widget), ctx(ctx) {
|
||||
widget->installEventFilter(this);
|
||||
ctx->setSystemButtonAreaCallback([widget](const QSize &) {
|
||||
return getWidgetSceneRect(widget); //
|
||||
});
|
||||
}
|
||||
~SystemButtonAreaWidgetEventFilter() override = default;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override {
|
||||
Q_UNUSED(obj)
|
||||
switch (event->type()) {
|
||||
case QEvent::Move:
|
||||
case QEvent::Resize: {
|
||||
ctx->virtual_hook(AbstractWindowContext::SystemButtonAreaChangedHook, nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
QWidget *widget;
|
||||
AbstractWindowContext *ctx;
|
||||
};
|
||||
|
||||
/*!
|
||||
Returns the widget that acts as the system button area.
|
||||
*/
|
||||
QWidget *WidgetWindowAgent::systemButtonArea() const {
|
||||
Q_D(const WidgetWindowAgent);
|
||||
return d->systemButtonAreaWidget;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the widget that acts as the system button area. The system button will be centered in
|
||||
its area, it is recommended to place the widget in a layout and set a fixed size policy.
|
||||
|
||||
The system button will be visible in the system title bar area.
|
||||
*/
|
||||
void WidgetWindowAgent::setSystemButtonArea(QWidget *widget) {
|
||||
Q_D(WidgetWindowAgent);
|
||||
if (d->systemButtonAreaWidget == widget)
|
||||
return;
|
||||
|
||||
auto ctx = d->context.get();
|
||||
d->systemButtonAreaWidget = widget;
|
||||
if (!widget) {
|
||||
d->context->setSystemButtonAreaCallback({});
|
||||
d->systemButtonAreaWidgetEventFilter.reset();
|
||||
return;
|
||||
}
|
||||
d->systemButtonAreaWidgetEventFilter =
|
||||
std::make_unique<SystemButtonAreaWidgetEventFilter>(widget, ctx);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the the system button area callback.
|
||||
*/
|
||||
ScreenRectCallback WidgetWindowAgent::systemButtonAreaCallback() const {
|
||||
Q_D(const WidgetWindowAgent);
|
||||
return d->systemButtonAreaWidget ? nullptr : d->context->systemButtonAreaCallback();
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the the system button area callback, the \c size of the callback is the native title
|
||||
bar size.
|
||||
|
||||
The system button position will be updated when the window resizes.
|
||||
*/
|
||||
void WidgetWindowAgent::setSystemButtonAreaCallback(const ScreenRectCallback &callback) {
|
||||
Q_D(WidgetWindowAgent);
|
||||
setSystemButtonArea(nullptr);
|
||||
d->context->setSystemButtonAreaCallback(callback);
|
||||
}
|
||||
|
||||
}
|
||||
47
libs/qwindowkit/src/widgets/widgetwindowagent_p.h
Normal file
47
libs/qwindowkit/src/widgets/widgetwindowagent_p.h
Normal file
@@ -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
|
||||
179
libs/qwindowkit/src/widgets/widgetwindowagent_win.cpp
Normal file
179
libs/qwindowkit/src/widgets/widgetwindowagent_win.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
// 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 "widgetwindowagent_p.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtGui/QPainter>
|
||||
|
||||
#include <QtCore/private/qcoreapplication_p.h>
|
||||
|
||||
#include <QWKCore/qwindowkit_windows.h>
|
||||
#include <QWKCore/private/qwkglobal_p.h>
|
||||
#include <QWKCore/private/windows10borderhandler_p.h>
|
||||
|
||||
namespace QWK {
|
||||
|
||||
#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS)
|
||||
// https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowsbackingstore.cpp#L42
|
||||
// In QtWidgets applications, when repainting happens, QPA at the last calls
|
||||
// QWindowsBackingStore::flush() to draw the contents of the buffer to the screen, we need to
|
||||
// call GDI drawing the top border after that.
|
||||
|
||||
// After debugging, we know that there are two situations that will lead to repaint.
|
||||
//
|
||||
// 1. Windows sends a WM_PAINT message, after which Qt immediately generates a QExposeEvent or
|
||||
// QResizeEvent and send it to the corresponding QWidgetWindow instance, calling "flush" at the
|
||||
// end of its handler.
|
||||
//
|
||||
// 2. When a timer or user input triggers Qt to repaint spontaneously, the corresponding
|
||||
// QWidget receives a QEvent::UpdateRequest event and also calls "flush" at the end of its
|
||||
// handler.
|
||||
//
|
||||
// The above two cases are mutually exclusive, so we just need to intercept the two events
|
||||
// separately and draw the border area after the "flush" is called.
|
||||
|
||||
// https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowswindow.cpp#L2440
|
||||
// Note that we can not draw the border right after WM_PAINT comes or right before the WndProc
|
||||
// returns, because Qt calls BeginPaint() and EndPaint() itself. We should make sure that we
|
||||
// draw the top border between these two calls, otherwise some display exceptions may arise.
|
||||
|
||||
class WidgetBorderHandler : public QObject, public Windows10BorderHandler {
|
||||
public:
|
||||
explicit WidgetBorderHandler(QWidget *widget, AbstractWindowContext *ctx,
|
||||
QObject *parent = nullptr)
|
||||
: QObject(parent), Windows10BorderHandler(ctx), widget(widget) {
|
||||
widget->installEventFilter(this);
|
||||
|
||||
// First update
|
||||
if (ctx->windowId()) {
|
||||
setupNecessaryAttributes();
|
||||
}
|
||||
WidgetBorderHandler::updateGeometry();
|
||||
}
|
||||
|
||||
void updateGeometry() override {
|
||||
// The window top border is manually painted by QWK so we want to give
|
||||
// some margins to avoid it covering real window contents, however, we
|
||||
// found that there are some rounding issues for the thin border and
|
||||
// thus this small trick doesn't work very well when the DPR is not
|
||||
// integer. So far we haven't found a perfect solution, so just don't
|
||||
// set any margins. In theory the window content will only be covered
|
||||
// by 1px or so, it should not be a serious issue in the real world.
|
||||
//
|
||||
// widget->setContentsMargins(isNormalWindow() ? QMargins(0, borderThickness(), 0, 0)
|
||||
// : QMargins());
|
||||
}
|
||||
|
||||
bool isWindowActive() const override {
|
||||
return widget->isActiveWindow();
|
||||
}
|
||||
|
||||
inline void forwardEventToWidgetAndDraw(QWidget *w, QEvent *event) {
|
||||
// https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/widgets/kernel/qapplication.cpp#L3286
|
||||
// Deliver the event
|
||||
if (!forwardObjectEventFilters(this, w, event)) {
|
||||
// Let the widget paint first
|
||||
std::ignore = static_cast<QObject *>(w)->event(event);
|
||||
QCoreApplicationPrivate::setEventSpontaneous(event, false);
|
||||
}
|
||||
|
||||
// Due to the timer or user action, Qt will repaint some regions spontaneously,
|
||||
// even if there is no WM_PAINT message, we must wait for it to finish painting
|
||||
// and then update the top border area.
|
||||
drawBorderNative();
|
||||
}
|
||||
|
||||
inline void forwardEventToWindowAndDraw(QWindow *window, QEvent *event) {
|
||||
// https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/widgets/kernel/qapplication.cpp#L3286
|
||||
// Deliver the event
|
||||
if (!forwardObjectEventFilters(ctx, window, event)) {
|
||||
// Let Qt paint first
|
||||
std::ignore = static_cast<QObject *>(window)->event(event);
|
||||
QCoreApplicationPrivate::setEventSpontaneous(event, false);
|
||||
}
|
||||
|
||||
// Upon receiving the WM_PAINT message, Qt will repaint the entire view, and we
|
||||
// must wait for it to finish painting before drawing this top border area.
|
||||
drawBorderNative();
|
||||
}
|
||||
|
||||
protected:
|
||||
bool sharedEventFilter(QObject *obj, QEvent *event) override {
|
||||
Q_UNUSED(obj)
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::Expose: {
|
||||
// Qt will absolutely send a QExposeEvent or QResizeEvent to the QWindow when it
|
||||
// receives a WM_PAINT message. When the control flow enters the expose handler,
|
||||
// Qt must have already called BeginPaint() and it's the best time for us to
|
||||
// draw the top border.
|
||||
|
||||
// Since a QExposeEvent will be sent immediately after the QResizeEvent, we can
|
||||
// simply ignore it.
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
struct ExposeEvent : public QExposeEvent {
|
||||
inline const QRegion &getRegion() const { return m_region; }
|
||||
};
|
||||
auto ee = static_cast<ExposeEvent *>(event);
|
||||
bool exposeRegionValid = !ee->getRegion().isNull();
|
||||
#else
|
||||
auto ee = static_cast<QExposeEvent *>(event);
|
||||
bool exposeRegionValid = !ee->region().isNull();
|
||||
#endif
|
||||
auto window = widget->windowHandle();
|
||||
if (window->isExposed() && isNormalWindow() && exposeRegionValid) {
|
||||
forwardEventToWindowAndDraw(window, event);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return Windows10BorderHandler::sharedEventFilter(obj, event);
|
||||
}
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *event) override {
|
||||
Q_UNUSED(obj)
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::UpdateRequest: {
|
||||
if (!isNormalWindow())
|
||||
break;
|
||||
forwardEventToWidgetAndDraw(widget, event);
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::WindowStateChange: {
|
||||
updateGeometry();
|
||||
break;
|
||||
}
|
||||
|
||||
case QEvent::WindowActivate:
|
||||
case QEvent::WindowDeactivate: {
|
||||
widget->update();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QWidget *widget;
|
||||
};
|
||||
|
||||
void WidgetWindowAgentPrivate::setupWindows10BorderWorkaround() {
|
||||
// Install painting hook
|
||||
auto ctx = context.get();
|
||||
if (ctx->windowAttribute(QStringLiteral("win10-border-needed")).toBool()) {
|
||||
borderHandler = std::make_unique<WidgetBorderHandler>(hostWidget, ctx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user