forked from github_mirror/framelesshelper
Add initial snap layout implementation
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
parent
fbe241f29e
commit
ff8252aaf4
|
@ -27,7 +27,7 @@ cmake_minimum_required(VERSION 3.20)
|
|||
project(FramelessHelper
|
||||
VERSION 2.0.0.0
|
||||
DESCRIPTION "Window customization framework for Qt Widgets and Qt Quick."
|
||||
HOMEPAGE_URL "https://github.com/wangwenx190/framelesshelper"
|
||||
HOMEPAGE_URL "https://github.com/wangwenx190/framelesshelper/"
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
|
|
25
README.md
25
README.md
|
@ -1,4 +1,13 @@
|
|||
# FramelessHelper 2.0
|
||||
# FramelessHelper 2.x
|
||||
|
||||
## Highlights compared to 2.0
|
||||
|
||||
- Windows: Added support for the snap layout feature introduced in Windows 11.
|
||||
- Quick: Restored some 1.x interfaces which may be convenient for Qt Quick users.
|
||||
- Examples: Added QtWebEngine based demo projects for both Qt Widgets and Qt Quick.
|
||||
- Common: Added cross-platform customizable system menu for both Qt Widgets and Qt Quick. Also supports both light and dark theme.
|
||||
- Common: Removed bundled Qt internal classes that are licensed under Commercial/GPL/LGPL. This library is now pure MIT licensed.
|
||||
- Common: Bug fix and internal refactoring, improved stability on all supported platforms.
|
||||
|
||||
## Highlights compared to 1.x
|
||||
|
||||
|
@ -44,8 +53,16 @@
|
|||
- Future versions
|
||||
- [ ] Linux: Support runtime theme switching.
|
||||
- [ ] Linux: Move window resize area outside of the client area.
|
||||
- [ ] macOS: Move window resize area outside of the client area.
|
||||
- [ ] More feature requests are welcome!
|
||||
|
||||
## Requiredments
|
||||
|
||||
- Compiler: a modern compiler which supports C++17 at least.
|
||||
- Qt version: using the latest stable version of Qt is highly recommended, the minimum supported version is Qt 5.6.
|
||||
- Qt modules: QtCore and QtGui for the core module, QtWidgets for the widgets module, QtQml QtQuick QtQuickControls2 QtQuickTemplates2 for the quick module.
|
||||
- CMake & ninja: the newer, the better.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
|
@ -70,7 +87,7 @@ Please refer to the demo applications to see more detailed usages: [examples](./
|
|||
|
||||
### Windows
|
||||
|
||||
- If DWM composition is disabled in some very rare cases (only possible on Windows 7), the top-left corner and top-right corner will appear in round shape. The round corners can be restored to square again if you re-enable DWM composition.
|
||||
- If DWM composition is disabled in some very rare cases (only possible on Windows 7), the top-left corner and top-right corner will appear in round shape. The round corners can be restored to square if you re-enable DWM composition.
|
||||
- There's an OpenGL driver bug which will cause some frameless windows have a strange black bar right on top of your homemade title bar, and it also makes the controls in your windows shifted to the bottom-right corner for some pixels. It's a bug of your graphics card driver, specifically, your OpenGL driver, not FramelessHelper. There are some solutions provided by our users but some of them may not work in all conditions, you can pick one from them:
|
||||
- Upgrade your graphics card driver to the latest version.
|
||||
- Change your system theme to "Basic".
|
||||
|
@ -83,11 +100,13 @@ Please refer to the demo applications to see more detailed usages: [examples](./
|
|||
### Linux
|
||||
|
||||
- FramelessHelper will force your application to use the _XCB_ platform plugin when running on Wayland.
|
||||
- Currently lacks runtime theme switching support.
|
||||
- Currently lacks runtime theme switching support due to Qt is missing the ability to detect theme change event on Linux.
|
||||
- The resize area is inside of the window.
|
||||
|
||||
### macOS
|
||||
|
||||
- The frameless windows will appear in square corners instead of round corners.
|
||||
- The resize area is inside of the window.
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -72,10 +72,9 @@ void MainWindow::setupUi()
|
|||
setTitleBarWidget(titleBarWidget);
|
||||
|
||||
setHitTestVisible(mb); // IMPORTANT!
|
||||
setHitTestVisible(titleBar->iconButton);
|
||||
setHitTestVisible(titleBar->minimizeButton);
|
||||
setHitTestVisible(titleBar->maximizeButton);
|
||||
setHitTestVisible(titleBar->closeButton);
|
||||
setSystemButton(titleBar->minimizeButton, SystemButtonType::Minimize);
|
||||
setSystemButton(titleBar->maximizeButton, SystemButtonType::Maximize);
|
||||
setSystemButton(titleBar->closeButton, SystemButtonType::Close);
|
||||
|
||||
connect(titleBar->minimizeButton, &QPushButton::clicked, this, &MainWindow::showMinimized);
|
||||
connect(titleBar->maximizeButton, &QPushButton::clicked, this, &MainWindow::toggleMaximized);
|
||||
|
|
|
@ -84,4 +84,8 @@ void MainWindow::setupUi()
|
|||
setTitleBarWidget(m_titleBarWidget);
|
||||
resize(800, 600);
|
||||
setWindowTitle(tr("QOpenGLWidget demo"));
|
||||
|
||||
setSystemButton(m_minBtn, SystemButtonType::Minimize);
|
||||
setSystemButton(m_maxBtn, SystemButtonType::Maximize);
|
||||
setSystemButton(m_closeBtn, SystemButtonType::Close);
|
||||
}
|
||||
|
|
|
@ -76,14 +76,14 @@ FramelessWindow {
|
|||
Component.onCompleted: {
|
||||
// Make our homemade title bar snap to the window top frame border.
|
||||
window.snapToTopBorder(titleBar, FramelessHelper.Top, FramelessHelper.Bottom);
|
||||
// Make our homemade title bar draggable, and on Windows, open the system menu
|
||||
// Make our homemade title bar draggable, and open the system menu
|
||||
// when the user right clicks on the title bar area.
|
||||
window.titleBarItem = titleBar;
|
||||
// Make our homemade system buttons visible to hit test.
|
||||
// The call to "setHitTestVisible()" is necessary, don't forget to do it.
|
||||
window.setHitTestVisible(minimizeButton);
|
||||
window.setHitTestVisible(maximizeButton);
|
||||
window.setHitTestVisible(closeButton);
|
||||
// Make our own items visible to the hit test and on Windows, enable
|
||||
// the snap layout feature (available since Windows 11).
|
||||
window.setSystemButton(minimizeButton, FramelessHelper.Minimize);
|
||||
window.setSystemButton(maximizeButton, FramelessHelper.Maximize);
|
||||
window.setSystemButton(closeButton, FramelessHelper.Close);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -197,9 +197,9 @@ enum class Option
|
|||
ForceHideWindowFrameBorder = 0x00000001, // Windows only, force hide the window frame border even on Windows 10 and onwards.
|
||||
ForceShowWindowFrameBorder = 0x00000002, // Windows only, force show the window frame border even on Windows 7 (~ 8.1).
|
||||
DontDrawTopWindowFrameBorder = 0x00000004, // Windows only, don't draw the top window frame border even if the window frame border is visible.
|
||||
EnableRoundedWindowCorners = 0x00000008, // Not implemented yet.
|
||||
DontForceSquareWindowCorners = 0x00000008, // Windows only, don't force the window corners to be square if the default corner style is not square.
|
||||
TransparentWindowBackground = 0x00000010, // Make the window's background become transparent.
|
||||
MaximizeButtonDocking = 0x00000020, // Not implemented yet.
|
||||
DisableWindowsSnapLayout = 0x00000020, // Windows only, don't enable the snap layout feature (available since Windows 11) unconditionally.
|
||||
CreateStandardWindowLayout = 0x00000040, // Using this option will cause FramelessHelper create a homemade titlebar and a window layout to contain it. If your window has a layout already, the newly created layout will mess up your own layout.
|
||||
BeCompatibleWithQtFramelessWindowHint = 0x00000080, // Windows only, make the code compatible with Qt::FramelessWindowHint. Don't use this option unless you really need that flag.
|
||||
DontTouchQtInternals = 0x00000100, // Windows only, don't modify Qt's internal data.
|
||||
|
|
|
@ -95,7 +95,9 @@ FRAMELESSHELPER_CORE_API void installSystemMenuHook(
|
|||
const WId windowId,
|
||||
const Global::Options options,
|
||||
const QPoint &offset,
|
||||
const Global::IsWindowFixedSizeCallback &isWindowFixedSize);
|
||||
const Global::IsWindowFixedSizeCallback &isWindowFixedSize,
|
||||
const Global::IsInsideTitleBarDraggableAreaCallback &isInTitleBarArea,
|
||||
const Global::GetWindowDevicePixelRatioCallback &getDevicePixelRatio);
|
||||
FRAMELESSHELPER_CORE_API void uninstallSystemMenuHook(const WId windowId);
|
||||
FRAMELESSHELPER_CORE_API void tryToBeCompatibleWithQtFramelessWindowHint(
|
||||
const WId windowId,
|
||||
|
@ -106,6 +108,7 @@ FRAMELESSHELPER_CORE_API void setAeroSnappingEnabled(const WId windowId, const b
|
|||
FRAMELESSHELPER_CORE_API void tryToEnableHighestDpiAwarenessLevel();
|
||||
FRAMELESSHELPER_CORE_API void updateGlobalWin32ControlsTheme(const WId windowId, const bool dark);
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode_windows();
|
||||
FRAMELESSHELPER_CORE_API void forceSquareCornersForWindow(const WId windowId, const bool force);
|
||||
#endif // Q_OS_WINDOWS
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
|
|
|
@ -81,9 +81,9 @@ struct FRAMELESSHELPER_QUICK_API QuickGlobal
|
|||
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, ForceHideWindowFrameBorder)
|
||||
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, ForceShowWindowFrameBorder)
|
||||
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontDrawTopWindowFrameBorder)
|
||||
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, EnableRoundedWindowCorners)
|
||||
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontForceSquareWindowCorners)
|
||||
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, TransparentWindowBackground)
|
||||
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, MaximizeButtonDocking)
|
||||
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DisableWindowsSnapLayout)
|
||||
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, CreateStandardWindowLayout)
|
||||
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, BeCompatibleWithQtFramelessWindowHint)
|
||||
FRAMELESSHELPER_QUICK_ENUM_VALUE(Option, DontTouchQtInternals)
|
||||
|
|
|
@ -63,8 +63,6 @@ public:
|
|||
explicit FramelessQuickUtils(QObject *parent = nullptr);
|
||||
~FramelessQuickUtils() override;
|
||||
|
||||
Q_NODISCARD static FramelessQuickUtils *instance();
|
||||
|
||||
Q_NODISCARD static qreal titleBarHeight();
|
||||
Q_NODISCARD static bool frameBorderVisible();
|
||||
Q_NODISCARD static qreal frameBorderThickness();
|
||||
|
@ -81,19 +79,6 @@ public:
|
|||
Q_NODISCARD Q_INVOKABLE static QColor getSystemButtonBackgroundColor(
|
||||
const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state);
|
||||
|
||||
#if 0
|
||||
public Q_SLOTS:
|
||||
static void removeWindowFrame(QQuickWindow *window);
|
||||
static void setTitleBarItem(QQuickWindow *window, QQuickItem *item);
|
||||
static void setHitTestVisible(QQuickWindow *window, QQuickItem *item);
|
||||
static void setWindowFixedSize(QQuickWindow *window, const bool value);
|
||||
static void moveWindowToDesktopCenter(QQuickWindow *window);
|
||||
static void startSystemMove2(QQuickWindow *window, const QPoint &pos);
|
||||
static void startSystemResize2(QQuickWindow *window, const Qt::Edges edges, const QPoint &pos);
|
||||
static void bringWindowToFront(QQuickWindow *window);
|
||||
static void showSystemMenu(QQuickWindow *window, const QPoint &pos);
|
||||
#endif
|
||||
|
||||
Q_SIGNALS:
|
||||
void darkModeEnabledChanged();
|
||||
void systemAccentColorChanged();
|
||||
|
|
|
@ -82,6 +82,7 @@ public Q_SLOTS:
|
|||
void moveToDesktopCenter();
|
||||
void bringToFront();
|
||||
void snapToTopBorder(QQuickItem *item, const QuickGlobal::Anchor itemAnchor, const QuickGlobal::Anchor topBorderAnchor);
|
||||
void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType);
|
||||
|
||||
Q_SIGNALS:
|
||||
void hiddenChanged();
|
||||
|
|
|
@ -63,6 +63,7 @@ public Q_SLOTS:
|
|||
void showSystemMenu(const QPoint &pos);
|
||||
void startSystemMove2(const QPoint &pos);
|
||||
void startSystemResize2(const Qt::Edges edges, const QPoint &pos);
|
||||
void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType);
|
||||
|
||||
Q_SIGNALS:
|
||||
void hiddenChanged();
|
||||
|
|
|
@ -67,6 +67,7 @@ public Q_SLOTS:
|
|||
void showSystemMenu(const QPoint &pos);
|
||||
void startSystemMove2(const QPoint &pos);
|
||||
void startSystemResize2(const Qt::Edges edges, const QPoint &pos);
|
||||
void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType);
|
||||
|
||||
Q_SIGNALS:
|
||||
void hiddenChanged();
|
||||
|
|
|
@ -48,22 +48,18 @@ public:
|
|||
~StandardSystemButton() override;
|
||||
|
||||
Q_NODISCARD QSize sizeHint() const override;
|
||||
|
||||
void setIcon(const QIcon &icon);
|
||||
|
||||
Q_NODISCARD Global::SystemButtonType buttonType();
|
||||
void setButtonType(const Global::SystemButtonType value);
|
||||
|
||||
Q_NODISCARD bool isHovered() const;
|
||||
void setHovered(const bool value);
|
||||
|
||||
Q_NODISCARD bool isPressed() const;
|
||||
void setPressed(const bool value);
|
||||
|
||||
Q_NODISCARD QColor hoverColor() const;
|
||||
void setHoverColor(const QColor &value);
|
||||
|
||||
Q_NODISCARD QColor pressColor() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void setIcon(const QIcon &icon);
|
||||
void setButtonType(const Global::SystemButtonType value);
|
||||
void setHovered(const bool value);
|
||||
void setPressed(const bool value);
|
||||
void setHoverColor(const QColor &value);
|
||||
void setPressColor(const QColor &value);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -42,6 +42,8 @@ struct Win32HelperData
|
|||
{
|
||||
UserSettings settings = {};
|
||||
SystemParameters params = {};
|
||||
bool trackingMouse = false;
|
||||
WId dragBarWindowId = 0;
|
||||
};
|
||||
|
||||
struct Win32Helper
|
||||
|
@ -49,12 +51,14 @@ struct Win32Helper
|
|||
QMutex mutex;
|
||||
QScopedPointer<FramelessHelperWin> nativeEventFilter;
|
||||
QHash<WId, Win32HelperData> data = {};
|
||||
QHash<WId, WId> dragBarToParentWindowMapping = {};
|
||||
};
|
||||
|
||||
Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
|
||||
|
||||
FRAMELESSHELPER_BYTEARRAY_CONSTANT2(Win32MessageTypeName, "windows_generic_MSG")
|
||||
static const QString qThemeSettingChangeEventName = QString::fromWCharArray(kThemeSettingChangeEventName);
|
||||
static constexpr const wchar_t kDragBarWindowClassName[] = L"FRAMELESSHELPER@DRAG_BAR_WINDOW_CLASS";
|
||||
FRAMELESSHELPER_STRING_CONSTANT(MonitorFromWindow)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(GetMonitorInfoW)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(ScreenToClient)
|
||||
|
@ -63,14 +67,335 @@ FRAMELESSHELPER_STRING_CONSTANT(GetClientRect)
|
|||
#ifdef Q_PROCESSOR_X86_64
|
||||
FRAMELESSHELPER_STRING_CONSTANT(GetWindowLongPtrW)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(SetWindowLongPtrW)
|
||||
#else
|
||||
#else // Q_PROCESSOR_X86_64
|
||||
// WinUser.h defines G/SetClassLongPtr as G/SetClassLong due to the
|
||||
// "Ptr" suffixed APIs are not available on 32-bit platforms, so we
|
||||
// have to add the following workaround. Undefine the macros and then
|
||||
// redefine them is also an option but the following solution is more simple.
|
||||
FRAMELESSHELPER_STRING_CONSTANT2(GetWindowLongPtrW, "GetWindowLongW")
|
||||
FRAMELESSHELPER_STRING_CONSTANT2(SetWindowLongPtrW, "SetWindowLongW")
|
||||
#endif
|
||||
#endif // Q_PROCESSOR_X86_64
|
||||
FRAMELESSHELPER_STRING_CONSTANT(RegisterClassExW)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(GetModuleHandleW)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(CreateWindowExW)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(SetLayeredWindowAttributes)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(SetWindowPos)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
|
||||
|
||||
[[nodiscard]] static inline LRESULT CALLBACK DragBarWindowProc
|
||||
(const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam)
|
||||
{
|
||||
Q_ASSERT(hWnd);
|
||||
if (!hWnd) {
|
||||
return 0;
|
||||
}
|
||||
const auto windowId = reinterpret_cast<WId>(hWnd);
|
||||
g_win32Helper()->mutex.lock();
|
||||
if (!g_win32Helper()->dragBarToParentWindowMapping.contains(windowId)) {
|
||||
g_win32Helper()->mutex.unlock();
|
||||
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
const WId parentWindowId = g_win32Helper()->dragBarToParentWindowMapping.value(windowId);
|
||||
if (!g_win32Helper()->data.contains(parentWindowId)) {
|
||||
g_win32Helper()->mutex.unlock();
|
||||
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
const Win32HelperData data = g_win32Helper()->data.value(parentWindowId);
|
||||
g_win32Helper()->mutex.unlock();
|
||||
const auto parentWindowHandle = reinterpret_cast<HWND>(parentWindowId);
|
||||
const auto releaseButtons = [&data]() -> void {
|
||||
static constexpr const auto defaultButtonState = ButtonState::Unspecified;
|
||||
data.params.setSystemButtonState(SystemButtonType::WindowIcon, defaultButtonState);
|
||||
data.params.setSystemButtonState(SystemButtonType::Help, defaultButtonState);
|
||||
data.params.setSystemButtonState(SystemButtonType::Minimize, defaultButtonState);
|
||||
data.params.setSystemButtonState(SystemButtonType::Maximize, defaultButtonState);
|
||||
data.params.setSystemButtonState(SystemButtonType::Restore, defaultButtonState);
|
||||
data.params.setSystemButtonState(SystemButtonType::Close, defaultButtonState);
|
||||
};
|
||||
const auto hoverButton = [&releaseButtons, &data](const SystemButtonType button) -> void {
|
||||
releaseButtons();
|
||||
data.params.setSystemButtonState(button, ButtonState::Hovered);
|
||||
};
|
||||
const auto pressButton = [&releaseButtons, &data](const SystemButtonType button) -> void {
|
||||
releaseButtons();
|
||||
data.params.setSystemButtonState(button, ButtonState::Pressed);
|
||||
};
|
||||
const auto clickButton = [&releaseButtons, &data](const SystemButtonType button) -> void {
|
||||
releaseButtons();
|
||||
data.params.setSystemButtonState(button, ButtonState::Clicked);
|
||||
};
|
||||
switch (uMsg) {
|
||||
case WM_NCHITTEST: {
|
||||
// Try to determine what part of the window is being hovered here. This
|
||||
// is absolutely critical to making sure the snap layout works!
|
||||
const POINT nativeGlobalPos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
|
||||
POINT nativeLocalPos = nativeGlobalPos;
|
||||
if (ScreenToClient(hWnd, &nativeLocalPos) == FALSE) {
|
||||
qWarning() << Utils::getSystemErrorMessage(kScreenToClient);
|
||||
break;
|
||||
}
|
||||
const qreal devicePixelRatio = data.params.getWindowDevicePixelRatio();
|
||||
const QPoint qtScenePos = QPointF(QPointF(qreal(nativeLocalPos.x), qreal(nativeLocalPos.y)) / devicePixelRatio).toPoint();
|
||||
SystemButtonType buttonType = SystemButtonType::Unknown;
|
||||
if (data.params.isInsideSystemButtons(qtScenePos, &buttonType)) {
|
||||
switch (buttonType) {
|
||||
case SystemButtonType::Unknown:
|
||||
break;
|
||||
case SystemButtonType::WindowIcon:
|
||||
return HTSYSMENU;
|
||||
case SystemButtonType::Help:
|
||||
return HTHELP;
|
||||
case SystemButtonType::Minimize:
|
||||
return HTREDUCE;
|
||||
case SystemButtonType::Maximize:
|
||||
case SystemButtonType::Restore:
|
||||
return HTZOOM;
|
||||
case SystemButtonType::Close:
|
||||
return HTCLOSE;
|
||||
}
|
||||
}
|
||||
// The parent window has quite some logic in the hit test handler, we
|
||||
// should forward this message to the parent window and return what it
|
||||
// returns to make sure our homemade titlebar is still functional.
|
||||
return SendMessageW(parentWindowHandle, WM_NCHITTEST, 0, lParam);
|
||||
}
|
||||
case WM_NCMOUSEMOVE: {
|
||||
// When we get this message, it's because the mouse moved when it was
|
||||
// over somewhere we said was the non-client area.
|
||||
//
|
||||
// We'll use this to communicate state to the title bar control, so that
|
||||
// it can update its visuals.
|
||||
// - If we're over a button, hover it.
|
||||
// - If we're over _anything else_, stop hovering the buttons.
|
||||
switch (wParam) {
|
||||
case HTTOP:
|
||||
case HTCAPTION: {
|
||||
releaseButtons();
|
||||
// Pass caption-related nonclient messages to the parent window.
|
||||
// Make sure to do this for the HTTOP, which is the top resize
|
||||
// border, so we can resize the window on the top.
|
||||
return SendMessageW(parentWindowHandle, uMsg, wParam, lParam);
|
||||
}
|
||||
case HTSYSMENU:
|
||||
hoverButton(SystemButtonType::WindowIcon);
|
||||
break;
|
||||
case HTHELP:
|
||||
hoverButton(SystemButtonType::Help);
|
||||
break;
|
||||
case HTREDUCE:
|
||||
hoverButton(SystemButtonType::Minimize);
|
||||
break;
|
||||
case HTZOOM:
|
||||
hoverButton(SystemButtonType::Maximize);
|
||||
break;
|
||||
case HTCLOSE:
|
||||
hoverButton(SystemButtonType::Close);
|
||||
break;
|
||||
default:
|
||||
releaseButtons();
|
||||
break;
|
||||
}
|
||||
// If we haven't previously asked for mouse tracking, request mouse
|
||||
// tracking. We need to do this so we can get the WM_NCMOUSELEAVE
|
||||
// message when the mouse leave the titlebar. Otherwise, we won't always
|
||||
// get that message (especially if the user moves the mouse _real
|
||||
// fast_).
|
||||
if (!data.trackingMouse && ((wParam == HTSYSMENU) || (wParam == HTHELP)
|
||||
|| (wParam == HTREDUCE) || (wParam == HTZOOM) || (wParam == HTCLOSE))) {
|
||||
TRACKMOUSEEVENT tme;
|
||||
SecureZeroMemory(&tme, sizeof(tme));
|
||||
tme.cbSize = sizeof(tme);
|
||||
// TME_NONCLIENT is absolutely critical here. In my experimentation,
|
||||
// we'd get WM_MOUSELEAVE messages after just a HOVER_DEFAULT
|
||||
// timeout even though we're not requesting TME_HOVER, which kinda
|
||||
// ruined the whole point of this.
|
||||
tme.dwFlags = (TME_LEAVE | TME_NONCLIENT);
|
||||
tme.hwndTrack = hWnd;
|
||||
tme.dwHoverTime = HOVER_DEFAULT; // We don't _really_ care about this.
|
||||
if (TrackMouseEvent(&tme) == FALSE) {
|
||||
qWarning() << Utils::getSystemErrorMessage(kTrackMouseEvent);
|
||||
break;
|
||||
}
|
||||
QMutexLocker locker(&g_win32Helper()->mutex);
|
||||
g_win32Helper()->data[parentWindowId].trackingMouse = true;
|
||||
}
|
||||
} break;
|
||||
case WM_NCMOUSELEAVE:
|
||||
case WM_MOUSELEAVE: {
|
||||
// When the mouse leaves the drag rect, make sure to dismiss any hover.
|
||||
releaseButtons();
|
||||
QMutexLocker locker(&g_win32Helper()->mutex);
|
||||
g_win32Helper()->data[parentWindowId].trackingMouse = false;
|
||||
} break;
|
||||
// NB: *Shouldn't be forwarding these* when they're not over the caption
|
||||
// because they can inadvertently take action using the system's default
|
||||
// metrics instead of our own.
|
||||
case WM_NCLBUTTONDOWN:
|
||||
case WM_NCLBUTTONDBLCLK: {
|
||||
// Manual handling for mouse clicks in the drag bar. If it's in a
|
||||
// caption button, then tell the titlebar to "press" the button, which
|
||||
// should change its visual state.
|
||||
//
|
||||
// If it's not in a caption button, then just forward the message along
|
||||
// to the root HWND. Make sure to do this for the HTTOP, which is the
|
||||
// top resize border.
|
||||
switch (wParam) {
|
||||
case HTTOP:
|
||||
case HTCAPTION:
|
||||
// Pass caption-related nonclient messages to the parent window.
|
||||
return SendMessageW(parentWindowHandle, uMsg, wParam, lParam);
|
||||
// The buttons won't work as you'd expect; we need to handle those
|
||||
// ourselves.
|
||||
case HTSYSMENU:
|
||||
pressButton(SystemButtonType::WindowIcon);
|
||||
break;
|
||||
case HTHELP:
|
||||
pressButton(SystemButtonType::Help);
|
||||
break;
|
||||
case HTREDUCE:
|
||||
pressButton(SystemButtonType::Minimize);
|
||||
break;
|
||||
case HTZOOM:
|
||||
pressButton(SystemButtonType::Maximize);
|
||||
break;
|
||||
case HTCLOSE:
|
||||
pressButton(SystemButtonType::Close);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case WM_NCLBUTTONUP: {
|
||||
// Manual handling for mouse RELEASES in the drag bar. If it's in a
|
||||
// caption button, then manually handle what we'd expect for that button.
|
||||
//
|
||||
// If it's not in a caption button, then just forward the message along
|
||||
// to the root HWND.
|
||||
switch (wParam) {
|
||||
case HTTOP:
|
||||
case HTCAPTION:
|
||||
// Pass caption-related nonclient messages to the parent window.
|
||||
return SendMessageW(parentWindowHandle, uMsg, wParam, lParam);
|
||||
// The buttons won't work as you'd expect; we need to handle those ourselves.
|
||||
case HTSYSMENU:
|
||||
clickButton(SystemButtonType::WindowIcon);
|
||||
break;
|
||||
case HTHELP:
|
||||
clickButton(SystemButtonType::Help);
|
||||
break;
|
||||
case HTREDUCE:
|
||||
clickButton(SystemButtonType::Minimize);
|
||||
break;
|
||||
case HTZOOM:
|
||||
clickButton(SystemButtonType::Maximize);
|
||||
break;
|
||||
case HTCLOSE:
|
||||
clickButton(SystemButtonType::Close);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// Make sure to pass along right-clicks in this region to our parent window
|
||||
// - we don't need to handle these.
|
||||
case WM_NCRBUTTONDOWN:
|
||||
case WM_NCRBUTTONDBLCLK:
|
||||
case WM_NCRBUTTONUP:
|
||||
return SendMessageW(parentWindowHandle, uMsg, wParam, lParam);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Forward all the mouse events we don't handled here to the parent window,
|
||||
// this is a necessary step to make sure the child widgets/quick items can still
|
||||
// receive mouse events from our homemade titlebar.
|
||||
if (((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST)) ||
|
||||
((uMsg >= WM_NCMOUSEMOVE) && (uMsg <= WM_NCXBUTTONDBLCLK))) {
|
||||
SendMessageW(parentWindowHandle, uMsg, wParam, lParam);
|
||||
}
|
||||
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline bool resizeDragBarWindow(const WId parentWindowId, const WId dragBarWindowId)
|
||||
{
|
||||
Q_ASSERT(parentWindowId);
|
||||
Q_ASSERT(dragBarWindowId);
|
||||
if (!parentWindowId || !dragBarWindowId) {
|
||||
return false;
|
||||
}
|
||||
const auto parentWindowHandle = reinterpret_cast<HWND>(parentWindowId);
|
||||
RECT parentWindowClientRect = {};
|
||||
if (GetClientRect(parentWindowHandle, &parentWindowClientRect) == FALSE) {
|
||||
qWarning() << Utils::getSystemErrorMessage(kGetClientRect);
|
||||
return false;
|
||||
}
|
||||
const int titleBarHeight = Utils::getTitleBarHeight(parentWindowId, true);
|
||||
const auto dragBarWindowHandle = reinterpret_cast<HWND>(dragBarWindowId);
|
||||
if (SetWindowPos(dragBarWindowHandle, HWND_TOP, 0, 0, parentWindowClientRect.right,
|
||||
titleBarHeight, (SWP_NOACTIVATE | SWP_SHOWWINDOW)) == FALSE) {
|
||||
qWarning() << Utils::getSystemErrorMessage(kSetWindowPos);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline bool createDragBarWindow(const WId parentWindowId)
|
||||
{
|
||||
Q_ASSERT(parentWindowId);
|
||||
if (!parentWindowId) {
|
||||
return false;
|
||||
}
|
||||
if (!Utils::isWindowsVersionOrGreater(WindowsVersion::_8)) {
|
||||
qWarning() << "Our drag bar window needs the WS_EX_LAYERED style, however, "
|
||||
"it's not supported for child windows until Windows 8.";
|
||||
return false;
|
||||
}
|
||||
const auto parentWindowHandle = reinterpret_cast<HWND>(parentWindowId);
|
||||
const auto instance = static_cast<HINSTANCE>(GetModuleHandleW(nullptr));
|
||||
Q_ASSERT(instance);
|
||||
if (!instance) {
|
||||
qWarning() << Utils::getSystemErrorMessage(kGetModuleHandleW);
|
||||
return false;
|
||||
}
|
||||
static const ATOM dragBarWindowClass = [instance]() -> ATOM {
|
||||
WNDCLASSEXW wcex;
|
||||
SecureZeroMemory(&wcex, sizeof(wcex));
|
||||
wcex.cbSize = sizeof(wcex);
|
||||
wcex.style = (CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS);
|
||||
wcex.lpszClassName = kDragBarWindowClassName;
|
||||
wcex.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
|
||||
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
||||
wcex.lpfnWndProc = DragBarWindowProc;
|
||||
wcex.hInstance = instance;
|
||||
return RegisterClassExW(&wcex);
|
||||
}();
|
||||
Q_ASSERT(dragBarWindowClass);
|
||||
if (!dragBarWindowClass) {
|
||||
qWarning() << Utils::getSystemErrorMessage(kRegisterClassExW);
|
||||
return false;
|
||||
}
|
||||
const HWND dragBarWindowHandle = CreateWindowExW((WS_EX_LAYERED | WS_EX_NOREDIRECTIONBITMAP),
|
||||
kDragBarWindowClassName, nullptr, WS_CHILD, 0, 0, 0, 0, parentWindowHandle, nullptr, instance, nullptr);
|
||||
Q_ASSERT(dragBarWindowHandle);
|
||||
if (!dragBarWindowHandle) {
|
||||
qWarning() << Utils::getSystemErrorMessage(kCreateWindowExW);
|
||||
return false;
|
||||
}
|
||||
if (SetLayeredWindowAttributes(dragBarWindowHandle, 0, 255, LWA_ALPHA) == FALSE) {
|
||||
qWarning() << Utils::getSystemErrorMessage(kSetLayeredWindowAttributes);
|
||||
return false;
|
||||
}
|
||||
const auto dragBarWindowId = reinterpret_cast<WId>(dragBarWindowHandle);
|
||||
if (!resizeDragBarWindow(parentWindowId, dragBarWindowId)) {
|
||||
qWarning() << "Failed to re-position the drag bar window.";
|
||||
return false;
|
||||
}
|
||||
QMutexLocker locker(&g_win32Helper()->mutex);
|
||||
g_win32Helper()->data[parentWindowId].dragBarWindowId = dragBarWindowId;
|
||||
g_win32Helper()->dragBarToParentWindowMapping.insert(dragBarWindowId, parentWindowId);
|
||||
return true;
|
||||
}
|
||||
|
||||
FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {}
|
||||
|
||||
|
@ -108,6 +433,12 @@ void FramelessHelperWin::addWindow(const UserSettings &settings, const SystemPar
|
|||
}
|
||||
Utils::updateInternalWindowFrameMargins(params.getWindowHandle(), true);
|
||||
Utils::updateWindowFrameMargins(windowId, false);
|
||||
if (Utils::isWindowsVersionOrGreater(WindowsVersion::_8)) {
|
||||
if (!(settings.options & Option::DisableWindowsSnapLayout)) {
|
||||
if (!createDragBarWindow(windowId)) {
|
||||
qWarning() << "Failed to create the drag bar window.";
|
||||
}
|
||||
}
|
||||
if (Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1607)) {
|
||||
const bool dark = Utils::shouldAppsUseDarkMode();
|
||||
if (!(settings.options & Option::DontTouchWindowFrameBorderColor)) {
|
||||
|
@ -117,6 +448,12 @@ void FramelessHelperWin::addWindow(const UserSettings &settings, const SystemPar
|
|||
if (settings.options & Option::SyncNativeControlsThemeWithSystem) {
|
||||
Utils::updateGlobalWin32ControlsTheme(windowId, dark);
|
||||
}
|
||||
if (Utils::isWindowsVersionOrGreater(WindowsVersion::_11_21H2)) {
|
||||
if (!(settings.options & Option::DontForceSquareWindowCorners)) {
|
||||
Utils::forceSquareCornersForWindow(windowId, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -669,6 +1006,18 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (Utils::isWindowsVersionOrGreater(WindowsVersion::_8) && data.dragBarWindowId) {
|
||||
switch (uMsg) {
|
||||
case WM_SIZE:
|
||||
case WM_DISPLAYCHANGE: {
|
||||
if (!resizeDragBarWindow(windowId, data.dragBarWindowId)) {
|
||||
qWarning() << "Failed to re-position the drag bar window.";
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool systemThemeChanged = ((uMsg == WM_THEMECHANGED) || (uMsg == WM_SYSCOLORCHANGE)
|
||||
|| (uMsg == WM_DWMCOLORIZATIONCOLORCHANGED));
|
||||
if (Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1607)) {
|
||||
|
|
|
@ -158,7 +158,8 @@ void FramelessWindowsManagerPrivate::addWindow(const UserSettings &settings, con
|
|||
FramelessHelperWin::addWindow(settings, params);
|
||||
}
|
||||
if (!(settings.options & Option::DontInstallSystemMenuHook)) {
|
||||
Utils::installSystemMenuHook(windowId, settings.options, settings.systemMenuOffset, params.isWindowFixedSize);
|
||||
Utils::installSystemMenuHook(windowId, settings.options, settings.systemMenuOffset,
|
||||
params.isWindowFixedSize, params.isInsideTitleBarDraggableArea, params.getWindowDevicePixelRatio);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -53,6 +53,8 @@ struct Win32UtilsHelperData
|
|||
Options options = {};
|
||||
QPoint offset = {};
|
||||
IsWindowFixedSizeCallback isWindowFixedSize = nullptr;
|
||||
IsInsideTitleBarDraggableAreaCallback isInTitleBarArea = nullptr;
|
||||
GetWindowDevicePixelRatioCallback getDevicePixelRatio = nullptr;
|
||||
};
|
||||
|
||||
struct Win32UtilsHelper
|
||||
|
@ -97,7 +99,7 @@ FRAMELESSHELPER_STRING_CONSTANT(SystemParametersInfoW)
|
|||
FRAMELESSHELPER_STRING_CONSTANT(SetClassLongPtrW)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(GetWindowLongPtrW)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(SetWindowLongPtrW)
|
||||
#else
|
||||
#else // Q_PROCESSOR_X86_64
|
||||
// WinUser.h defines G/SetClassLongPtr as G/SetClassLong due to the
|
||||
// "Ptr" suffixed APIs are not available on 32-bit platforms, so we
|
||||
// have to add the following workaround. Undefine the macros and then
|
||||
|
@ -106,7 +108,7 @@ FRAMELESSHELPER_STRING_CONSTANT(SystemParametersInfoW)
|
|||
FRAMELESSHELPER_STRING_CONSTANT2(SetClassLongPtrW, "SetClassLongW")
|
||||
FRAMELESSHELPER_STRING_CONSTANT2(GetWindowLongPtrW, "GetWindowLongW")
|
||||
FRAMELESSHELPER_STRING_CONSTANT2(SetWindowLongPtrW, "SetWindowLongW")
|
||||
#endif
|
||||
#endif // Q_PROCESSOR_X86_64
|
||||
FRAMELESSHELPER_STRING_CONSTANT(ReleaseCapture)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(SetWindowTheme)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(SetProcessDpiAwarenessContext)
|
||||
|
@ -124,6 +126,7 @@ FRAMELESSHELPER_STRING_CONSTANT(EnableMenuItem)
|
|||
FRAMELESSHELPER_STRING_CONSTANT(SetMenuDefaultItem)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(HiliteMenuItem)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(TrackPopupMenu)
|
||||
FRAMELESSHELPER_STRING_CONSTANT(ClientToScreen)
|
||||
|
||||
[[nodiscard]] static inline bool
|
||||
doCompareWindowsVersion(const DWORD dwMajor, const DWORD dwMinor, const DWORD dwBuild)
|
||||
|
@ -238,10 +241,10 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackPopupMenu)
|
|||
}
|
||||
const Win32UtilsHelperData data = g_utilsHelper()->data.value(windowId);
|
||||
g_utilsHelper()->mutex.unlock();
|
||||
const auto getGlobalPosFromMouse = [lParam]() -> QPoint {
|
||||
const auto getNativePosFromMouse = [lParam]() -> QPoint {
|
||||
return {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
|
||||
};
|
||||
const auto getGlobalPosFromKeyboard = [hWnd, windowId, &data]() -> QPoint {
|
||||
const auto getNativeGlobalPosFromKeyboard = [hWnd, windowId, &data]() -> QPoint {
|
||||
RECT windowPos = {};
|
||||
if (GetWindowRect(hWnd, &windowPos) == FALSE) {
|
||||
qWarning() << Utils::getSystemErrorMessage(kGetWindowRect);
|
||||
|
@ -273,30 +276,51 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackPopupMenu)
|
|||
};
|
||||
bool shouldShowSystemMenu = false;
|
||||
bool broughtByKeyboard = false;
|
||||
QPoint globalPos = {};
|
||||
if (uMsg == WM_NCRBUTTONUP) {
|
||||
QPoint nativeGlobalPos = {};
|
||||
switch (uMsg) {
|
||||
case WM_RBUTTONUP: {
|
||||
const QPoint nativeLocalPos = getNativePosFromMouse();
|
||||
const qreal dpr = data.getDevicePixelRatio();
|
||||
const QPoint qtScenePos = QPointF(QPointF(nativeLocalPos) / dpr).toPoint();
|
||||
if (data.isInTitleBarArea(qtScenePos)) {
|
||||
POINT pos = {nativeLocalPos.x(), nativeLocalPos.y()};
|
||||
if (ClientToScreen(hWnd, &pos) == FALSE) {
|
||||
qWarning() << Utils::getSystemErrorMessage(kClientToScreen);
|
||||
break;
|
||||
}
|
||||
shouldShowSystemMenu = true;
|
||||
nativeGlobalPos = {pos.x, pos.y};
|
||||
}
|
||||
} break;
|
||||
case WM_NCRBUTTONUP: {
|
||||
if (wParam == HTCAPTION) {
|
||||
shouldShowSystemMenu = true;
|
||||
globalPos = getGlobalPosFromMouse();
|
||||
nativeGlobalPos = getNativePosFromMouse();
|
||||
}
|
||||
} else if (uMsg == WM_SYSCOMMAND) {
|
||||
} break;
|
||||
case WM_SYSCOMMAND: {
|
||||
const WPARAM filteredWParam = (wParam & 0xFFF0);
|
||||
if ((filteredWParam == SC_KEYMENU) && (lParam == VK_SPACE)) {
|
||||
shouldShowSystemMenu = true;
|
||||
broughtByKeyboard = true;
|
||||
globalPos = getGlobalPosFromKeyboard();
|
||||
nativeGlobalPos = getNativeGlobalPosFromKeyboard();
|
||||
}
|
||||
} else if ((uMsg == WM_KEYDOWN) || (uMsg == WM_SYSKEYDOWN)) {
|
||||
} break;
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN: {
|
||||
const bool altPressed = ((wParam == VK_MENU) || (GetKeyState(VK_MENU) < 0));
|
||||
const bool spacePressed = ((wParam == VK_SPACE) || (GetKeyState(VK_SPACE) < 0));
|
||||
if (altPressed && spacePressed) {
|
||||
shouldShowSystemMenu = true;
|
||||
broughtByKeyboard = true;
|
||||
globalPos = getGlobalPosFromKeyboard();
|
||||
nativeGlobalPos = getNativeGlobalPosFromKeyboard();
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (shouldShowSystemMenu) {
|
||||
Utils::showSystemMenu(windowId, globalPos, data.offset,
|
||||
Utils::showSystemMenu(windowId, nativeGlobalPos, data.offset,
|
||||
broughtByKeyboard, data.options, data.isWindowFixedSize);
|
||||
// QPA's internal code will handle system menu events separately, and its
|
||||
// behavior is not what we would want to see because it doesn't know our
|
||||
|
@ -1040,7 +1064,9 @@ bool Utils::isFrameBorderColorized()
|
|||
}
|
||||
|
||||
void Utils::installSystemMenuHook(const WId windowId, const Options options, const QPoint &offset,
|
||||
const IsWindowFixedSizeCallback &isWindowFixedSize)
|
||||
const IsWindowFixedSizeCallback &isWindowFixedSize,
|
||||
const IsInsideTitleBarDraggableAreaCallback &isInTitleBarArea,
|
||||
const GetWindowDevicePixelRatioCallback &getDevicePixelRatio)
|
||||
{
|
||||
Q_ASSERT(windowId);
|
||||
Q_ASSERT(isWindowFixedSize);
|
||||
|
@ -1070,6 +1096,8 @@ void Utils::installSystemMenuHook(const WId windowId, const Options options, con
|
|||
data.options = options;
|
||||
data.offset = offset;
|
||||
data.isWindowFixedSize = isWindowFixedSize;
|
||||
data.isInTitleBarArea = isInTitleBarArea;
|
||||
data.getDevicePixelRatio = getDevicePixelRatio;
|
||||
g_utilsHelper()->data.insert(windowId, data);
|
||||
}
|
||||
|
||||
|
@ -1295,4 +1323,28 @@ bool Utils::shouldAppsUseDarkMode_windows()
|
|||
return resultFromRegistry();
|
||||
}
|
||||
|
||||
void Utils::forceSquareCornersForWindow(const WId windowId, const bool force)
|
||||
{
|
||||
Q_ASSERT(windowId);
|
||||
if (!windowId) {
|
||||
return;
|
||||
}
|
||||
// We cannot change the window corner style until Windows 11.
|
||||
if (!isWindowsVersionOrGreater(WindowsVersion::_11_21H2)) {
|
||||
return;
|
||||
}
|
||||
static const auto pDwmSetWindowAttribute =
|
||||
reinterpret_cast<decltype(&DwmSetWindowAttribute)>(
|
||||
QSystemLibrary::resolve(kdwmapi, "DwmSetWindowAttribute"));
|
||||
if (!pDwmSetWindowAttribute) {
|
||||
return;
|
||||
}
|
||||
const auto hwnd = reinterpret_cast<HWND>(windowId);
|
||||
const DWM_WINDOW_CORNER_PREFERENCE wcp = (force ? DWMWCP_DONOTROUND : DWMWCP_ROUND);
|
||||
const HRESULT hr = pDwmSetWindowAttribute(hwnd, _DWMWA_WINDOW_CORNER_PREFERENCE, &wcp, sizeof(wcp));
|
||||
if (FAILED(hr)) {
|
||||
qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
|
||||
}
|
||||
}
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
|
@ -30,8 +30,6 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
|
|||
|
||||
using namespace Global;
|
||||
|
||||
Q_GLOBAL_STATIC(FramelessQuickUtils, g_quickUtils)
|
||||
|
||||
FramelessQuickUtils::FramelessQuickUtils(QObject *parent) : QObject(parent)
|
||||
{
|
||||
connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged, this, [this](){
|
||||
|
@ -43,11 +41,6 @@ FramelessQuickUtils::FramelessQuickUtils(QObject *parent) : QObject(parent)
|
|||
|
||||
FramelessQuickUtils::~FramelessQuickUtils() = default;
|
||||
|
||||
FramelessQuickUtils *FramelessQuickUtils::instance()
|
||||
{
|
||||
return g_quickUtils();
|
||||
}
|
||||
|
||||
qreal FramelessQuickUtils::titleBarHeight()
|
||||
{
|
||||
return kDefaultTitleBarHeight;
|
||||
|
@ -131,54 +124,4 @@ QColor FramelessQuickUtils::getSystemButtonBackgroundColor(const QuickGlobal::Sy
|
|||
FRAMELESSHELPER_ENUM_QUICK_TO_CORE(ButtonState, state));
|
||||
}
|
||||
|
||||
#if 0
|
||||
void FramelessQuickUtils::removeWindowFrame(QQuickWindow *window)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void FramelessQuickUtils::setTitleBarItem(QQuickWindow *window, QQuickItem *item)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FramelessQuickUtils::setHitTestVisible(QQuickWindow *window, QQuickItem *item)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FramelessQuickUtils::setWindowFixedSize(QQuickWindow *window, const bool value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FramelessQuickUtils::moveWindowToDesktopCenter(QQuickWindow *window)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FramelessQuickUtils::startSystemMove2(QQuickWindow *window, const QPoint &pos)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FramelessQuickUtils::startSystemResize2(QQuickWindow *window, const Qt::Edges edges, const QPoint &pos)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FramelessQuickUtils::bringWindowToFront(QQuickWindow *window)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FramelessQuickUtils::showSystemMenu(QQuickWindow *window, const QPoint &pos)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
|
@ -44,9 +44,9 @@ static constexpr const char QTQUICK_BUTTON_CLASS_NAME[] = "QQuickAbstractButton"
|
|||
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, ForceHideWindowFrameBorder, value, result)
|
||||
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, ForceShowWindowFrameBorder, value, result)
|
||||
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontDrawTopWindowFrameBorder, value, result)
|
||||
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, EnableRoundedWindowCorners, value, result)
|
||||
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontForceSquareWindowCorners, value, result)
|
||||
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, TransparentWindowBackground, value, result)
|
||||
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, MaximizeButtonDocking, value, result)
|
||||
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DisableWindowsSnapLayout, value, result)
|
||||
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, CreateStandardWindowLayout, value, result)
|
||||
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, BeCompatibleWithQtFramelessWindowHint, value, result)
|
||||
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchQtInternals, value, result)
|
||||
|
@ -73,9 +73,9 @@ static constexpr const char QTQUICK_BUTTON_CLASS_NAME[] = "QQuickAbstractButton"
|
|||
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, ForceHideWindowFrameBorder, value, result)
|
||||
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, ForceShowWindowFrameBorder, value, result)
|
||||
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontDrawTopWindowFrameBorder, value, result)
|
||||
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, EnableRoundedWindowCorners, value, result)
|
||||
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontForceSquareWindowCorners, value, result)
|
||||
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, TransparentWindowBackground, value, result)
|
||||
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, MaximizeButtonDocking, value, result)
|
||||
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DisableWindowsSnapLayout, value, result)
|
||||
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, CreateStandardWindowLayout, value, result)
|
||||
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, BeCompatibleWithQtFramelessWindowHint, value, result)
|
||||
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchQtInternals, value, result)
|
||||
|
@ -352,6 +352,36 @@ void FramelessQuickWindowPrivate::setOptions(const QuickGlobal::Options value)
|
|||
Q_EMIT q->optionsChanged();
|
||||
}
|
||||
|
||||
void FramelessQuickWindowPrivate::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType)
|
||||
{
|
||||
Q_ASSERT(item);
|
||||
Q_ASSERT(buttonType != QuickGlobal::SystemButtonType::Unknown);
|
||||
if (!item || (buttonType == QuickGlobal::SystemButtonType::Unknown)) {
|
||||
return;
|
||||
}
|
||||
switch (buttonType) {
|
||||
case QuickGlobal::SystemButtonType::Unknown:
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
case QuickGlobal::SystemButtonType::WindowIcon:
|
||||
m_settings.windowIconButton = item;
|
||||
break;
|
||||
case QuickGlobal::SystemButtonType::Help:
|
||||
m_settings.contextHelpButton = item;
|
||||
break;
|
||||
case QuickGlobal::SystemButtonType::Minimize:
|
||||
m_settings.minimizeButton = item;
|
||||
break;
|
||||
case QuickGlobal::SystemButtonType::Maximize:
|
||||
case QuickGlobal::SystemButtonType::Restore:
|
||||
m_settings.maximizeButton = item;
|
||||
break;
|
||||
case QuickGlobal::SystemButtonType::Close:
|
||||
m_settings.closeButton = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool FramelessQuickWindowPrivate::eventFilter(QObject *object, QEvent *event)
|
||||
{
|
||||
Q_ASSERT(object);
|
||||
|
@ -884,6 +914,17 @@ void FramelessQuickWindow::snapToTopBorder(QQuickItem *item, const QuickGlobal::
|
|||
d->snapToTopBorder(item, itemAnchor, topBorderAnchor);
|
||||
}
|
||||
|
||||
void FramelessQuickWindow::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType)
|
||||
{
|
||||
Q_ASSERT(item);
|
||||
Q_ASSERT(buttonType != QuickGlobal::SystemButtonType::Unknown);
|
||||
if (!item || (buttonType == QuickGlobal::SystemButtonType::Unknown)) {
|
||||
return;
|
||||
}
|
||||
Q_D(FramelessQuickWindow);
|
||||
d->setSystemButton(item, buttonType);
|
||||
}
|
||||
|
||||
void FramelessQuickWindow::showMinimized2()
|
||||
{
|
||||
Q_D(FramelessQuickWindow);
|
||||
|
|
|
@ -85,6 +85,7 @@ public Q_SLOTS:
|
|||
void bringToFront();
|
||||
void snapToTopBorder(QQuickItem *item, const QuickGlobal::Anchor itemAnchor, const QuickGlobal::Anchor topBorderAnchor);
|
||||
void setOptions(const QuickGlobal::Options value);
|
||||
void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType);
|
||||
|
||||
protected:
|
||||
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;
|
||||
|
|
|
@ -106,4 +106,9 @@ void FramelessMainWindow::startSystemResize2(const Qt::Edges edges, const QPoint
|
|||
d_ptr->startSystemResize2(edges, pos);
|
||||
}
|
||||
|
||||
void FramelessMainWindow::setSystemButton(QWidget *widget, const SystemButtonType buttonType)
|
||||
{
|
||||
d_ptr->setSystemButton(widget, buttonType);
|
||||
}
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
|
@ -116,4 +116,9 @@ void FramelessWidget::startSystemResize2(const Qt::Edges edges, const QPoint &po
|
|||
d_ptr->startSystemResize2(edges, pos);
|
||||
}
|
||||
|
||||
void FramelessWidget::setSystemButton(QWidget *widget, const SystemButtonType buttonType)
|
||||
{
|
||||
d_ptr->setSystemButton(widget, buttonType);
|
||||
}
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
|
@ -728,6 +728,36 @@ void FramelessWidgetsHelper::startSystemResize2(const Qt::Edges edges, const QPo
|
|||
Utils::startSystemResize(q->windowHandle(), edges, pos);
|
||||
}
|
||||
|
||||
void FramelessWidgetsHelper::setSystemButton(QWidget *widget, const SystemButtonType buttonType)
|
||||
{
|
||||
Q_ASSERT(widget);
|
||||
Q_ASSERT(buttonType != SystemButtonType::Unknown);
|
||||
if (!widget || (buttonType == SystemButtonType::Unknown)) {
|
||||
return;
|
||||
}
|
||||
switch (buttonType) {
|
||||
case SystemButtonType::Unknown:
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
case SystemButtonType::WindowIcon:
|
||||
m_settings.windowIconButton = widget;
|
||||
break;
|
||||
case SystemButtonType::Help:
|
||||
m_settings.contextHelpButton = widget;
|
||||
break;
|
||||
case SystemButtonType::Minimize:
|
||||
m_settings.minimizeButton = widget;
|
||||
break;
|
||||
case SystemButtonType::Maximize:
|
||||
case SystemButtonType::Restore:
|
||||
m_settings.maximizeButton = widget;
|
||||
break;
|
||||
case SystemButtonType::Close:
|
||||
m_settings.closeButton = widget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool FramelessWidgetsHelper::eventFilter(QObject *object, QEvent *event)
|
||||
{
|
||||
Q_ASSERT(object);
|
||||
|
|
|
@ -76,6 +76,7 @@ public Q_SLOTS:
|
|||
void showSystemMenu(const QPoint &pos);
|
||||
void startSystemMove2(const QPoint &pos);
|
||||
void startSystemResize2(const Qt::Edges edges, const QPoint &pos);
|
||||
void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType);
|
||||
|
||||
protected:
|
||||
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;
|
||||
|
|
Loading…
Reference in New Issue