forked from github_mirror/framelesshelper
Revert back to the original solution.
We will use the original solution in this branch.
The new Windows Terminal solution will be in another branch.
Partially reverts commit 01707907cd
Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
parent
01707907cd
commit
f0ef569b08
14
main.cpp
14
main.cpp
|
@ -1,12 +1,7 @@
|
|||
#include <QApplication>
|
||||
#include <QWidget>
|
||||
#include <QWindow>
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
#include "winnativeeventfilter.h"
|
||||
#else
|
||||
#include "framelesshelper.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
|
@ -25,19 +20,10 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
QApplication application(argc, argv);
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
// Do this AFTER the Q(Gui)Application is constructed!
|
||||
WinNativeEventFilter::install();
|
||||
#else
|
||||
FramelessHelper helper;
|
||||
#endif
|
||||
|
||||
QWindow window;
|
||||
QWidget widget;
|
||||
#ifndef Q_OS_WINDOWS
|
||||
helper.setFramelessWindows({&widget});
|
||||
#endif
|
||||
window.show();
|
||||
widget.show();
|
||||
|
||||
return QApplication::exec();
|
||||
|
|
|
@ -481,7 +481,7 @@ UINT GetDotsPerInchForWindow(HWND handle) {
|
|||
// Return hard-coded DPI if DPI scaling is disabled.
|
||||
return m_defaultDotsPerInch;
|
||||
}
|
||||
if (!handle || !m_lpIsWindow(handle)) {
|
||||
if (!m_lpIsWindow(handle)) {
|
||||
if (m_lpGetSystemDpiForProcess) {
|
||||
return m_lpGetSystemDpiForProcess(m_lpGetCurrentProcess());
|
||||
} else if (m_lpGetDpiForSystem) {
|
||||
|
@ -597,23 +597,13 @@ RECT GetFrameSizeForWindow(HWND handle, bool includingTitleBar = false) {
|
|||
|
||||
void UpdateFrameMarginsForWindow(HWND handle) {
|
||||
if (handle && m_lpIsWindow(handle)) {
|
||||
// We removed the whole top part of the frame (see handling of
|
||||
// WM_NCCALCSIZE) so the top border is missing now. We add it back here.
|
||||
// Note #1: You might wonder why we don't remove just the title bar
|
||||
// instead of removing the whole top part of the frame and then adding
|
||||
// the little top border back. I tried to do this but it didn't work:
|
||||
// DWM drew the whole title bar anyways on top of the window. It seems
|
||||
// that DWM only wants to draw either nothing or the whole top part of
|
||||
// the frame.
|
||||
// Note #2: For some reason if you try to set the top margin to just
|
||||
// the top border height (what we want to do), then there is a
|
||||
// transparency bug when the window is inactive, so I've decided to add
|
||||
// the whole top part of the frame instead and then we will hide
|
||||
// everything that we don't need (that is, the whole thing but the
|
||||
// little 1 pixel wide border at the top) in the WM_PAINT handler.
|
||||
// This eliminates the transparency bug and it's what a lot of Win32
|
||||
// apps that customize the title bar do so it should work fine.
|
||||
const MARGINS margins = {0, 0, GetFrameSizeForWindow(handle).top, 0};
|
||||
MARGINS margins = {0, 0, 0, 0};
|
||||
if (IsDwmCompositionEnabled()) {
|
||||
const DWMNCRENDERINGPOLICY ncrp = DWMNCRP_ENABLED;
|
||||
m_lpDwmSetWindowAttribute(handle, DWMWA_NCRENDERING_POLICY, &ncrp,
|
||||
sizeof(ncrp));
|
||||
margins = {-1, -1, -1, -1};
|
||||
}
|
||||
m_lpDwmExtendFrameIntoClientArea(handle, &margins);
|
||||
}
|
||||
}
|
||||
|
@ -758,17 +748,27 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
if (!data->initialized) {
|
||||
// Avoid initializing a same window twice.
|
||||
data->initialized = TRUE;
|
||||
// Restore default window style.
|
||||
m_lpSetWindowLongPtrW(msg->hwnd, GWL_STYLE,
|
||||
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN |
|
||||
WS_CLIPSIBLINGS);
|
||||
// The following two functions can help us get rid of the three
|
||||
// system buttons (minimize, maximize and close). But they also
|
||||
// break the Arcylic effect (introduced in Win10 1709), don't know
|
||||
// why.
|
||||
m_lpSetWindowLongPtrW(msg->hwnd, GWL_EXSTYLE,
|
||||
WS_EX_APPWINDOW | WS_EX_LAYERED);
|
||||
m_lpSetLayeredWindowAttributes(msg->hwnd, RGB(255, 0, 255), 0,
|
||||
LWA_COLORKEY);
|
||||
// Trigger a frame change event to let us enter the WM_NCCALCSIZE
|
||||
// message to remove our title bar as early as possible.
|
||||
updateWindow(msg->hwnd, true, false);
|
||||
}
|
||||
LRESULT dwmHitResult = 0;
|
||||
if (!data->windowData.mouseTransparent && IsDwmCompositionEnabled() &&
|
||||
m_lpDwmDefWindowProc &&
|
||||
m_lpDwmDefWindowProc(msg->hwnd, msg->message, msg->wParam,
|
||||
msg->lParam, &dwmHitResult)) {
|
||||
*result = dwmHitResult;
|
||||
return true;
|
||||
// Make sure our window have it's frame shadow.
|
||||
// The frame shadow is drawn by Desktop Window Manager (DWM), don't
|
||||
// draw it yourself. The frame shadow will get lost if DWM
|
||||
// composition is disabled, it's designed to be, don't force the
|
||||
// window to draw a frame shadow in that case.
|
||||
UpdateFrameMarginsForWindow(msg->hwnd);
|
||||
}
|
||||
switch (msg->message) {
|
||||
case WM_NCCALCSIZE: {
|
||||
|
@ -817,6 +817,40 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
// structure. On entry, the structure contains the proposed window
|
||||
// rectangle for the window. On exit, the structure should contain
|
||||
// the screen coordinates of the corresponding window client area.
|
||||
const auto getClientAreaInsets = [](HWND _hWnd) -> RECT {
|
||||
// We don't need this correction when we're fullscreen. We will
|
||||
// have the WS_POPUP size, so we don't have to worry about
|
||||
// borders, and the default frame will be fine.
|
||||
if (IsMaximized(_hWnd) && !IsFullScreen(_hWnd)) {
|
||||
// Windows automatically adds a standard width border to all
|
||||
// sides when a window is maximized.
|
||||
int frameThickness_x =
|
||||
getSystemMetric(_hWnd, SystemMetric::BorderWidth);
|
||||
int frameThickness_y =
|
||||
getSystemMetric(_hWnd, SystemMetric::BorderHeight);
|
||||
// The following two lines are two seperate functions in
|
||||
// Chromium, it uses them to judge whether the window
|
||||
// should draw it's own frame or not. But here we will
|
||||
// always draw our own frame because our window is totally
|
||||
// frameless, so we can simply use constants here. I don't
|
||||
// remove them completely because I don't want to forget
|
||||
// what it's about to achieve.
|
||||
const bool removeStandardFrame = true;
|
||||
const bool hasFrame = !removeStandardFrame;
|
||||
if (!hasFrame) {
|
||||
frameThickness_x -= 1;
|
||||
frameThickness_y -= 1;
|
||||
}
|
||||
RECT rect;
|
||||
rect.top = frameThickness_y;
|
||||
rect.bottom = frameThickness_y;
|
||||
rect.left = frameThickness_x;
|
||||
rect.right = frameThickness_x;
|
||||
return rect;
|
||||
}
|
||||
return {0, 0, 0, 0};
|
||||
};
|
||||
const RECT insets = getClientAreaInsets(msg->hwnd);
|
||||
const auto mode = static_cast<BOOL>(msg->wParam);
|
||||
// If the window bounds change, we're going to relayout and repaint
|
||||
// anyway. Returning WVR_REDRAW avoids an extra paint before that of
|
||||
|
@ -827,32 +861,10 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
const auto clientRect = mode
|
||||
? &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam)->rgrc[0])
|
||||
: reinterpret_cast<LPRECT>(msg->lParam);
|
||||
// Store the original top before the default window proc applies the
|
||||
// default frame.
|
||||
const LONG originalTop = clientRect->top;
|
||||
// Apply the default frame
|
||||
const LRESULT ret = m_lpDefWindowProcW(msg->hwnd, WM_NCCALCSIZE,
|
||||
msg->wParam, msg->lParam);
|
||||
if (ret != 0) {
|
||||
*result = ret;
|
||||
return true;
|
||||
}
|
||||
// Re-apply the original top from before the size of the default
|
||||
// frame was applied.
|
||||
clientRect->top = originalTop;
|
||||
// We don't need this correction when we're fullscreen. We will have
|
||||
// the WS_POPUP size, so we don't have to worry about borders, and
|
||||
// the default frame will be fine.
|
||||
if (IsMaximized(msg->hwnd) && !IsFullScreen(msg->hwnd)) {
|
||||
// When a window is maximized, its size is actually a little bit
|
||||
// more than the monitor's work area. The window is positioned
|
||||
// and sized in such a way that the resize handles are outside
|
||||
// of the monitor and then the window is clipped to the monitor
|
||||
// so that the resize handle do not appear because you don't
|
||||
// need them (because you can't resize a window when it's
|
||||
// maximized unless you restore it).
|
||||
clientRect->top += GetFrameSizeForWindow(msg->hwnd).top;
|
||||
}
|
||||
clientRect->top += insets.top;
|
||||
clientRect->bottom -= insets.bottom;
|
||||
clientRect->left += insets.left;
|
||||
clientRect->right -= insets.right;
|
||||
// Attempt to detect if there's an autohide taskbar, and if
|
||||
// there is, reduce our size a bit on the side with the taskbar,
|
||||
// so the user can still mouse-over the taskbar to reveal it.
|
||||
|
@ -958,6 +970,19 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
*result = 0;
|
||||
return true;
|
||||
}
|
||||
case WM_NCPAINT: {
|
||||
// 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失
|
||||
|
||||
if (IsDwmCompositionEnabled()) {
|
||||
break;
|
||||
} else {
|
||||
// Only block WM_NCPAINT when DWM composition is disabled. If
|
||||
// it's blocked when DWM composition is enabled, the frame
|
||||
// shadow won't be drawn.
|
||||
*result = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case WM_NCACTIVATE: {
|
||||
// DefWindowProc won't repaint the window border if lParam (normally
|
||||
// a HRGN) is -1.
|
||||
|
@ -968,46 +993,183 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
return true;
|
||||
}
|
||||
case WM_NCHITTEST: {
|
||||
if (dwmHitResult != 0) {
|
||||
break;
|
||||
}
|
||||
// 原生Win32窗口只有顶边是在窗口内部resize的,其余三边都是在窗口
|
||||
// 外部进行resize的,其原理是,WS_THICKFRAME会在窗口的左、右和
|
||||
// 底边添加三个透明的resize区域,这三个区域在正常状态下是完全不可
|
||||
// 见的,它们由DWM负责绘制,消息也由DWM负责侦测和触发。这些区域的
|
||||
// 宽度等于(SM_CXSIZEFRAME + SM_CXPADDEDBORDER),高度等于
|
||||
// (SM_CYSIZEFRAME + SM_CXPADDEDBORDER),在100%缩放时,均等
|
||||
// 于8像素。它们属于窗口区域的一部分,但不属于客户区,而是属于非客
|
||||
// 户区,因此GetWindowRect获取的区域中是包含这三个resize区域的,
|
||||
// 而GetClientRect获取的区域是不包含它们的。当把
|
||||
// DWMWA_EXTENDED_FRAME_BOUNDS作为参数调用
|
||||
// DwmGetWindowAttribute时,也能获取到一个窗口大小,这个大小介
|
||||
// 于前面两者之间,暂时不知道这个数据的意义及其作用。我们在
|
||||
// WM_NCCALCSIZE消息的处理中,已经把整个窗口都设置为客户区了,也
|
||||
// 就是说,我们的窗口已经没有非客户区了,因此那三个透明的resize区
|
||||
// 域,此刻也已经成为窗口客户区的一部分了,从而变得不透明了。所以
|
||||
// 现在的resize,看起来像是在窗口内部resize,是因为原本透明的地方
|
||||
// 现在变得不透明了,实际上,单纯从范围上来看,现在我们resize的地方,
|
||||
// 就是普通窗口的边框外部,那三个透明区域的范围。
|
||||
// 因此,如果我们把边框完全去掉(就是我们正在做的事情),resize就
|
||||
// 会看起来是在内部进行,这个问题通过常规方法非常难以解决。我测试过
|
||||
// QQ和钉钉的窗口,它们的窗口就是在外部resize,但实际上它们是通过
|
||||
// 把窗口实际的内容,嵌入到一个完全透明的但尺寸要大一圈的窗口中实现
|
||||
// 的,虽然看起来效果还行,但在我看来不是正途。而且我之所以能发现,
|
||||
// 也是由于这种方法在很多情况下会露馅,比如窗口未响应卡住或贴边的时
|
||||
// 候,能明显看到窗口周围多出来一圈边界。我曾经尝试再把那三个区域弄
|
||||
// 透明,但无一例外都会破坏DWM绘制的边框阴影,因此只好作罢。
|
||||
|
||||
if (data->windowData.mouseTransparent) {
|
||||
// Mouse events will be passed to the parent window.
|
||||
*result = HTTRANSPARENT;
|
||||
return true;
|
||||
}
|
||||
// This will handle the left, right and bottom parts of the frame
|
||||
// because we didn't change them.
|
||||
const LRESULT originalRet = m_lpDefWindowProcW(
|
||||
msg->hwnd, WM_NCHITTEST, msg->wParam, msg->lParam);
|
||||
if (originalRet != HTCLIENT) {
|
||||
*result = originalRet;
|
||||
return true;
|
||||
const auto getHTResult = [](HWND _hWnd, LPARAM _lParam,
|
||||
const WINDOW *_data) -> LRESULT {
|
||||
const auto isInSpecificAreas = [](int x, int y,
|
||||
const QVector<QRect> &areas,
|
||||
qreal dpr) -> bool {
|
||||
for (auto &&area : std::as_const(areas)) {
|
||||
if (!area.isValid()) {
|
||||
continue;
|
||||
}
|
||||
if (QRect(std::round(area.x() * dpr),
|
||||
std::round(area.y() * dpr),
|
||||
std::round(area.width() * dpr),
|
||||
std::round(area.height() * dpr))
|
||||
.contains(x, y, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
RECT clientRect = {0, 0, 0, 0};
|
||||
m_lpGetClientRect(_hWnd, &clientRect);
|
||||
const LONG ww = clientRect.right;
|
||||
const LONG wh = clientRect.bottom;
|
||||
POINT mouse;
|
||||
// Don't use HIWORD(lParam) and LOWORD(lParam) to get cursor
|
||||
// coordinates because their results are unsigned numbers,
|
||||
// however the cursor position may be negative due to in a
|
||||
// different monitor.
|
||||
mouse.x = GET_X_LPARAM(_lParam);
|
||||
mouse.y = GET_Y_LPARAM(_lParam);
|
||||
m_lpScreenToClient(_hWnd, &mouse);
|
||||
// These values are DPI-aware.
|
||||
const LONG bw =
|
||||
getSystemMetric(_hWnd, SystemMetric::BorderWidth);
|
||||
const LONG bh =
|
||||
getSystemMetric(_hWnd, SystemMetric::BorderHeight);
|
||||
const LONG tbh =
|
||||
getSystemMetric(_hWnd, SystemMetric::TitleBarHeight);
|
||||
const qreal dpr = GetDevicePixelRatioForWindow(_hWnd);
|
||||
const bool isTitlebar = (mouse.y < tbh) &&
|
||||
!isInSpecificAreas(mouse.x, mouse.y,
|
||||
_data->windowData.ignoreAreas, dpr) &&
|
||||
(_data->windowData.draggableAreas.isEmpty()
|
||||
? true
|
||||
: isInSpecificAreas(mouse.x, mouse.y,
|
||||
_data->windowData.draggableAreas,
|
||||
dpr));
|
||||
if (IsMaximized(_hWnd)) {
|
||||
if (isTitlebar) {
|
||||
return HTCAPTION;
|
||||
}
|
||||
return HTCLIENT;
|
||||
}
|
||||
const bool isTop = mouse.y < bh;
|
||||
const bool isBottom = mouse.y > (wh - bh);
|
||||
// Make the border wider to let the user easy to resize on
|
||||
// corners.
|
||||
const int factor = (isTop || isBottom) ? 2 : 1;
|
||||
const bool isLeft = mouse.x < (bw * factor);
|
||||
const bool isRight = mouse.x > (ww - (bw * factor));
|
||||
const bool fixedSize = _data->windowData.fixedSize;
|
||||
const auto getBorderValue = [fixedSize](int value) -> int {
|
||||
// HTBORDER: non-resizeable window border.
|
||||
return fixedSize ? HTBORDER : value;
|
||||
};
|
||||
if (isTop) {
|
||||
if (isLeft) {
|
||||
return getBorderValue(HTTOPLEFT);
|
||||
}
|
||||
if (isRight) {
|
||||
return getBorderValue(HTTOPRIGHT);
|
||||
}
|
||||
return getBorderValue(HTTOP);
|
||||
}
|
||||
if (isBottom) {
|
||||
if (isLeft) {
|
||||
return getBorderValue(HTBOTTOMLEFT);
|
||||
}
|
||||
if (isRight) {
|
||||
return getBorderValue(HTBOTTOMRIGHT);
|
||||
}
|
||||
return getBorderValue(HTBOTTOM);
|
||||
}
|
||||
if (isLeft) {
|
||||
return getBorderValue(HTLEFT);
|
||||
}
|
||||
if (isRight) {
|
||||
return getBorderValue(HTRIGHT);
|
||||
}
|
||||
if (isTitlebar) {
|
||||
return HTCAPTION;
|
||||
}
|
||||
return HTCLIENT;
|
||||
};
|
||||
*result = getHTResult(msg->hwnd, msg->lParam, data);
|
||||
return true;
|
||||
}
|
||||
case WM_GETMINMAXINFO: {
|
||||
// Don't cover the taskbar when maximized.
|
||||
const HMONITOR monitor =
|
||||
m_lpMonitorFromWindow(msg->hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
MONITORINFO monitorInfo;
|
||||
SecureZeroMemory(&monitorInfo, sizeof(monitorInfo));
|
||||
monitorInfo.cbSize = sizeof(monitorInfo);
|
||||
m_lpGetMonitorInfoW(monitor, &monitorInfo);
|
||||
const RECT rcWorkArea = monitorInfo.rcWork;
|
||||
const RECT rcMonitorArea = monitorInfo.rcMonitor;
|
||||
auto &mmi = *reinterpret_cast<LPMINMAXINFO>(msg->lParam);
|
||||
if (QOperatingSystemVersion::current() <
|
||||
QOperatingSystemVersion::Windows8) {
|
||||
// FIXME: Buggy on Windows 7:
|
||||
// The origin of coordinates is the top left edge of the
|
||||
// monitor's work area. Why? It should be the top left edge of
|
||||
// the monitor's area.
|
||||
mmi.ptMaxPosition.x = rcMonitorArea.left;
|
||||
mmi.ptMaxPosition.y = rcMonitorArea.top;
|
||||
} else {
|
||||
// Works fine on Windows 8/8.1/10
|
||||
mmi.ptMaxPosition.x =
|
||||
std::abs(rcWorkArea.left - rcMonitorArea.left);
|
||||
mmi.ptMaxPosition.y =
|
||||
std::abs(rcWorkArea.top - rcMonitorArea.top);
|
||||
}
|
||||
// At this point, we know that the cursor is inside the client area
|
||||
// so it has to be either the little border at the top of our custom
|
||||
// title bar or the drag bar. Apparently, it must be the drag bar or
|
||||
// the little border at the top which the user can use to move or
|
||||
// resize the window.
|
||||
RECT rcWindow = {0, 0, 0, 0};
|
||||
// Only GetWindowRect can give us the most accurate size of our
|
||||
// window which includes the invisible resize area.
|
||||
m_lpGetWindowRect(msg->hwnd, &rcWindow);
|
||||
// Don't use HIWORD or LOWORD because they can only get positive
|
||||
// results, however, the cursor coordinates can be negative due to
|
||||
// in a different monitor.
|
||||
const LONG my = GET_Y_LPARAM(msg->lParam);
|
||||
// The top of the drag bar is used to resize the window
|
||||
if (!IsMaximized(msg->hwnd) &&
|
||||
(my < (rcWindow.top + GetFrameSizeForWindow(msg->hwnd).top))) {
|
||||
*result = HTTOP;
|
||||
return true;
|
||||
if (data->windowData.maximumSize.isEmpty()) {
|
||||
mmi.ptMaxSize.x = std::abs(rcWorkArea.right - rcWorkArea.left);
|
||||
mmi.ptMaxSize.y = std::abs(rcWorkArea.bottom - rcWorkArea.top);
|
||||
} else {
|
||||
mmi.ptMaxSize.x =
|
||||
std::round(GetDevicePixelRatioForWindow(msg->hwnd) *
|
||||
data->windowData.maximumSize.width());
|
||||
mmi.ptMaxSize.y =
|
||||
std::round(GetDevicePixelRatioForWindow(msg->hwnd) *
|
||||
data->windowData.maximumSize.height());
|
||||
}
|
||||
if (my <
|
||||
(rcWindow.top + GetFrameSizeForWindow(msg->hwnd, true).top)) {
|
||||
*result = HTCAPTION;
|
||||
return true;
|
||||
mmi.ptMaxTrackSize.x = mmi.ptMaxSize.x;
|
||||
mmi.ptMaxTrackSize.y = mmi.ptMaxSize.y;
|
||||
if (!data->windowData.minimumSize.isEmpty()) {
|
||||
mmi.ptMinTrackSize.x =
|
||||
std::round(GetDevicePixelRatioForWindow(msg->hwnd) *
|
||||
data->windowData.minimumSize.width());
|
||||
mmi.ptMinTrackSize.y =
|
||||
std::round(GetDevicePixelRatioForWindow(msg->hwnd) *
|
||||
data->windowData.minimumSize.height());
|
||||
}
|
||||
*result = HTCLIENT;
|
||||
*result = 0;
|
||||
return true;
|
||||
}
|
||||
case WM_SETICON:
|
||||
|
@ -1026,65 +1188,12 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
|||
*result = ret;
|
||||
return true;
|
||||
}
|
||||
case WM_ACTIVATE:
|
||||
case WM_DWMCOMPOSITIONCHANGED: {
|
||||
case WM_DWMCOMPOSITIONCHANGED:
|
||||
UpdateFrameMarginsForWindow(msg->hwnd);
|
||||
break;
|
||||
}
|
||||
case WM_NCPAINT: {
|
||||
if (IsDwmCompositionEnabled()) {
|
||||
break;
|
||||
} else {
|
||||
// Only block WM_NCPAINT when DWM composition is disabled. If
|
||||
// it's blocked when DWM composition is enabled, the frame
|
||||
// shadow won't be drawn.
|
||||
*result = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
case WM_PAINT: {
|
||||
PAINTSTRUCT ps;
|
||||
const HDC hdc = m_lpBeginPaint(msg->hwnd, &ps);
|
||||
const LONG topBorderHeight = 1;
|
||||
if (ps.rcPaint.top < topBorderHeight) {
|
||||
RECT rcTopBorder = ps.rcPaint;
|
||||
rcTopBorder.bottom = topBorderHeight;
|
||||
// To show the original top border, we have to paint on top
|
||||
// of it with the alpha component set to 0. This page
|
||||
// recommends to paint the area in black using the stock
|
||||
// BLACK_BRUSH to do this:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/dwm/customframe#extending-the-client-frame
|
||||
m_lpFillRect(hdc, &rcTopBorder, GetStockBrush(BLACK_BRUSH));
|
||||
}
|
||||
if (ps.rcPaint.bottom > topBorderHeight) {
|
||||
RECT rcRest = ps.rcPaint;
|
||||
rcRest.top = topBorderHeight;
|
||||
// To hide the original title bar, we have to paint on top
|
||||
// of it with the alpha component set to 255. This is a hack
|
||||
// to do it with GDI. See UpdateFrameMarginsForWindow for
|
||||
// more information.
|
||||
HDC opaqueDc;
|
||||
BP_PAINTPARAMS params;
|
||||
SecureZeroMemory(¶ms, sizeof(params));
|
||||
params.cbSize = sizeof(params);
|
||||
params.dwFlags = BPPF_NOCLIP | BPPF_ERASE;
|
||||
const HPAINTBUFFER buf = m_lpBeginBufferedPaint(
|
||||
hdc, &rcRest, BPBF_TOPDOWNDIB, ¶ms, &opaqueDc);
|
||||
m_lpFillRect(opaqueDc, &rcRest,
|
||||
reinterpret_cast<HBRUSH>(m_lpGetClassLongPtrW(
|
||||
msg->hwnd, GCLP_HBRBACKGROUND)));
|
||||
m_lpBufferedPaintSetAlpha(buf, nullptr, 255);
|
||||
m_lpEndBufferedPaint(buf, TRUE);
|
||||
}
|
||||
m_lpEndPaint(msg->hwnd, &ps);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ public:
|
|||
BOOL fixedSize = FALSE, mouseTransparent = FALSE;
|
||||
int borderWidth = -1, borderHeight = -1, titlebarHeight = -1;
|
||||
QVector<QRect> ignoreAreas, draggableAreas;
|
||||
QSize maximumSize = {-1, -1}, minimumSize = {-1, -1};
|
||||
};
|
||||
|
||||
using WINDOW = struct _WINDOW {
|
||||
|
|
Loading…
Reference in New Issue