我正在努力解决以下问题。我的窗口左上角在最大化时具有负坐标(x 和 y 均为 -9),并且它的大小实际上大于可用区域。 我正在使用 WinAPI 来获取一些特定于平台的功能,例如 Aero snap,因此理解这个问题发生的确切原因变得很复杂。 我的窗口设置了以下样式和属性:
setFlags(Qt::Window | Qt::FramelessWindowHint);
SetWindowLongPtr((HWND)winId(), GWL_STYLE, WS_POPUPWINDOW | WS_CAPTION | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN);
WS_CAPTION
与WS_BORDER | WS_DLGFRAME
相同,所以如果我删除WS_DLGFRAME
,最大化尺寸就可以了,并且我看到原生最大化动画消失了——但是由于WS_SIZEBOX
,我在调整大小时得到闪烁的白色边框(相同)如WS_THICKFRAME
)。如果没有WS_SIZEBOX
,我就会松开空气动力按扣。
我想也许我可以在 nativeEvent
中做一些最大混合尺寸的事情,所以我尝试了以下方法:
bool FramelessWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
{
if (auto* msg = static_cast<MSG*>(message); msg->message == WM_NCCALCSIZE)
{
NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam);
if (params.rgrc[0].top != 0)
{
--params.rgrc[0].top;
}
*result = WVR_REDRAW;
return true; //this removes the native border and title
}
else if (msg->message == WM_GETMINMAXINFO)
{
if (IsMaximized((HWND)winId()))
{
MINMAXINFO* mmi = (MINMAXINFO*)msg->lParam;
auto g = screen()->availableGeometry();
mmi->ptMaxTrackSize.x = g.width() * devicePixelRatio();
mmi->ptMaxTrackSize.y = g.height() * devicePixelRatio();
*result = 0;
return true;
}
}
return QQuickWindow::nativeEvent(eventType, message, result);
}
出于某种原因,在我设置
MINMAXINFO
之前,mmi->ptMaxTrackSize.x = g.width() * devicePixelRatio() - 1;
重载不会执行任何操作。在这种情况下,尺寸几乎很好,只是屏幕右边框和窗口之间有 1 px 的空白间隙。所以我无法通过改变MINMAXINFO
来获得正确的尺寸,因为真正正确的尺寸实际上应该与上面的代码相同。最初的 ptMaxTrackSize 非常奇怪:mmi
中的 3462、1462 vs availableGeometry
中的 3440、1390)。
我可以做类似的事情,但我想保持 QWindow::Maximized 状态,当我调用
setGeometry
时会重置该状态。
bool FramelessWindow::eventFilter(QObject* watched, QEvent* event)
{
if (event->type() == QEvent::WindowStateChange && QWindow::visibility() == QWindow::Maximized)
{
setGeometry(screen()->availableGeometry()); //the window is no longer maximized
return true;
}
return QQuickWindow::eventFilter(watched, event);
}
由于我无法在
QWindow::Maximized
方法中使用setGeometry
,这种方法相当于实现我自己的最大化,归一化等,这是不可取的,也不会影响航空最大化。
我还尝试在最大化状态下添加边距,但是创建布局会在调整大小时导致难看的闪烁,因为我的窗口实例是在 QML 中创建的。我无法使用
setLayout
并尝试在 C++ 代码中添加边距,因为我的类继承了 QQuickWindow
。
我错过了什么吗?
更新: 现在我发现这个问题主要与WinAPI +无框有关,而不是与Qt有关,所以不仅仅是Qt用户有“窗口最大化时超出屏幕”的情况。 它有点帮助,至少我现在能够测试已经存在的 WinAPI 解决方案,但问题是它们不能按预期工作。
我尝试在处理
NCCALCSIZE_PARAMS
消息时修改 WM_NCCALCSIZE
:
这是我得到的
rgrc[0]
:
我根据melak47的代码尝试过的:
void adjustMaximized(HWND hwnd, RECT& rect)
{
auto monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
if (!monitor)
{
return;
}
MONITORINFO monitor_info{};
monitor_info.cbSize = sizeof(monitor_info);
if (!::GetMonitorInfoW(monitor, &monitor_info))
{
return;
}
rect = monitor_info.rcWork;
}
bool FramelessWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
{
if (auto* msg = static_cast<MSG*>(message); msg->message == WM_NCCALCSIZE)
{
NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam);
if (params.rgrc[0].top != 0)
{
--params.rgrc[0].top;
}
if (IsMaximized(hwnd_))
{
adjustMaximized(hwnd_, params.rgrc[0]);
}
return true;
}
...
}
遗憾的是,我未能找到任何可行的 WinAPI 解决方案,所以我选择了 Qt 方式。这种方法并不像我期望的 WinAPI 方法那样简洁,但它确实有效。 这是仅描述必要部分的代码片段。
.hpp
class FramelessWindow : public QQuickWindow
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(bool isMaximized READ isMaximized NOTIFY isMaximizedChanged)
signals:
void isMaximizedChanged();
public:
FramelessWindow() noexcept;
Q_INVOKABLE void showNormal() noexcept;
Q_INVOKABLE void showMaximized() noexcept;
bool isMaximized() const noexcept;
private:
bool eventFilter(QObject* watched, QEvent* event) override;
bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override;
QRect restoredGeometry_;
bool isMaximized_;
};
.cpp
FramelessWindow::FramelessWindow() noexcept :
isMaximized_ { false }
{
setFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMaximizeButtonHint);
installEventFilter(this);
SetWindowLongPtr((HWND)winId(), GWL_STYLE, WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
}
void FramelessWindow::showNormal() noexcept
{
setGeometry(restoredGeometry_);
isMaximized_ = false;
emit isMaximizedChanged();
}
void FramelessWindow::showMaximized() noexcept
{
restoredGeometry_ = geometry();
setGeometry(screen()->availableGeometry());
isMaximized_ = true;
emit isMaximizedChanged();
}
bool FramelessWindow::isMaximized() const noexcept
{
return isMaximized_;
}
bool FramelessWindow::eventFilter(QObject* watched, QEvent* event)
{
QPoint cursorPos = QCursor::pos();
qreal dpr = devicePixelRatio();
QRect draggingArea = geometry();
draggingArea.setHeight(32 * dpr);
draggingArea.setY(draggingArea.y() + dpr * ResizeBorderWidth);
if (draggingArea.contains(cursorPos))
{
if (event->type() == QEvent::MouseButtonPress)
{
if (isMaximized_)
{
restoredGeometry_.moveTo({ QCursor::pos().x() - restoredGeometry_.width() / 2, QCursor::pos().y() - 10 });
showNormal();
}
startSystemMove();
return true;
}
else if (isResizable_ && event->type() == QEvent::MouseButtonDblClick)
{
if (draggingArea.contains(cursorPos))
{
if (isMaximized_)
{
showNormal();
}
else
{
showMaximized();
}
return true;
}
}
else if (event->type() == QEvent::WindowStateChange && QWindow::visibility() == QWindow::Maximized)
{
setGeometry(screen()->availableGeometry());
isMaximized_ = true;
emit isMaximizedChanged();
return true;
}
}
return QQuickWindow::eventFilter(watched, event);
}
bool FramelessWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
{
if (auto* msg = static_cast<MSG*>(message); msg->message == WM_NCCALCSIZE)
{
NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam);
if (params.rgrc[0].top != 0)
{
--params.rgrc[0].top;
}
*result = 0;
return true;
}
return QQuickWindow::nativeEvent(eventType, message, result);
}