[英]How to integrate the Qt frameless window in Windows composer? (system shortcut doesnt works)
我在 Windows 平台上工作。 如果我使用本機無框窗口標志,例如:
::SetWindowLongPtr((HWND)winId(), GWL_STYLE, WS_POPUP | WS_SYSMENU | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
我收到了可以與默認的 Windows 窗口編輯器一起正常工作的無框窗口 - 當我按“WIN”鍵 + 箭頭時,它會正確更改狀態。
當我嘗試使用帶有以下無框窗口標志的 Qt 庫時:
setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::CustomizeWindowHint);
我收到了對“WIN”鍵+箭頭沒有反應的窗口。 所以它不適用於默認的 Windows 窗口編輯器。
Qt 窗口標志的哪個組合將具有與上述本機標志類似的行為?
所以,這似乎確實與 Qt 關系不大。 在 Windows 窗口上除了全幀之外的任何東西似乎都禁用了“捕捉”快捷方式,即使窗口仍然可以調整大小或使用鍵盤箭頭移動(來自 Alt+Space 系統菜單)。
解決方法實際上非常簡單。 基本上實現QWidget::nativeEvent()
虛擬方法並忽略WM_NCCALCSIZE
消息。
從一個屏幕捕捉到另一個屏幕時,我遇到了一些繪畫問題,但使用遮罩(代碼注釋中的注釋)解決了這個問題。 找到一個更干凈的解決方案會很好(實際上它可能有資格作為 Qt 錯誤)。
作為獎勵,這也允許圓角。 繪畫/樣式基於我為另一個答案制作的圓形消息框,繪畫代碼在那里有更完整的記錄。
系統菜單和所有與鍵的交互(移動/調整大小/捕捉/等)都有效。 可以通過在nativeEvent()
實現WM_NCHITTEST
消息來添加鼠標處理(請參閱下面的參考資料)。
只在Win7上測試過,很好奇它在Win10上的表現如何。
無框小部件
#include <QPainter>
#include <QPalette>
#include <QStyle>
#include <QStyleOption>
#include <QWidget>
#include <qt_windows.h>
class FramelessWidget : public QWidget
{
Q_OBJECT
public:
explicit FramelessWidget(QWidget *p = nullptr, Qt::WindowFlags f = Qt::Window) :
// the flags set here should "match" the GWL_STYLE flags set below
QWidget(p, f | Qt::WindowMinMaxButtonsHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint | Qt::FramelessWindowHint)
{
setAttribute(Qt::WA_TranslucentBackground); // for rounded corners
// set flags which will override what Qt does, especially with the Qt::FramelessWindowHint which essentially disables WS_SIZEBOX/WS_THICKFRAME
SetWindowLongPtr(HWND(winId()), GWL_STYLE, WS_POPUPWINDOW | WS_CAPTION | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN);
}
// these settings are only used when no styleSheet is set
qreal radius = 0.0; // desired radius in absolute pixels
qreal borderWidth = -1.0; // -1: use style hint frame width; 0: no border; > 0: use this width.
protected:
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override
{
MSG *msg = static_cast<MSG*>(message);
if (msg && msg->message == WM_NCCALCSIZE) {
// Just return 0 and mark event as handled. This will draw the widget contents
// into the full size of the frame, instead of leaving room for it.
*result = 0;
return true;
}
return QWidget::nativeEvent(eventType, message, result);
}
// Override paint event because of transparent background.
// Can be styled using CSS or QPalette with backgroundRole()/foregroundRole().
void paintEvent(QPaintEvent *) override
{
QPainter p(this);
p.setRenderHints(QPainter::Antialiasing);
QStyleOption opt;
opt.initFrom(this);
// be sure to use the full frame size, not the default rect() which is inside frame.
opt.rect.setSize(frameSize());
if (testAttribute(Qt::WA_StyleSheetTarget)) {
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
setMask(QRegion(opt.rect)); // see notes below
return;
}
QRectF rect(opt.rect);
qreal penWidth = borderWidth;
if (penWidth < 0.0)
penWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this);
if (penWidth > 0.0) {
p.setPen(QPen(palette().brush(foregroundRole()), penWidth));
const qreal dlta = (penWidth * 0.5);
rect.adjust(dlta, dlta, -dlta, -dlta);
}
else {
p.setPen(Qt::NoPen);
}
p.setBrush(palette().brush(backgroundRole()));
if (radius > 0.0)
p.drawRoundedRect(rect, radius, radius, Qt::AbsoluteSize);
else
p.drawRect(rect);
// Setting a mask works around an issue with artifacts when switching screens with Win+arrow
// keys. I don't think it's the actual mask which does it, rather it triggers the region
// around the widget to be polished but I'm not sure. As support for my theory, the mask
// doesn't even have to follow the border radius.
setMask(QRegion(opt.rect));
}
}; // FramelessWidget
測試實施
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//QLoggingCategory::setFilterRules(QStringLiteral("qt.qpa.windows = true\n"));
FramelessWidget msgBox;
msgBox.setWindowTitle("Frameless window test");
msgBox.setLayout(new QVBoxLayout);
msgBox.layout()->addWidget(new QLabel(QStringLiteral("<h3>Frameless rounded widget.</h3>"), &msgBox));
QLabel *lbl = new QLabel(QStringLiteral(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean fermentum erat rhoncus, "
"scelerisque eros ac, hendrerit metus. Nunc ac lorem id tortor porttitor mollis. Nunc "
"tristique orci vel risus convallis, non hendrerit sapien condimentum. Phasellus lorem tortor, "
"mollis luctus efficitur id, consequat eget nulla. Nam ac magna quis elit tristique hendrerit id "
"at erat. Integer id tortor elementum, dictum urna sed, tincidunt metus. Proin ultrices tempus "
"lacinia. Integer sit amet fringilla nunc."
), &msgBox);
lbl->setWordWrap(true);
msgBox.layout()->addWidget(lbl);
QPushButton *pb = new QPushButton(QStringLiteral("Close"), &msgBox);
QObject::connect(pb, &QPushButton::clicked, qApp, &QApplication::quit);
msgBox.layout()->addItem(new QSpacerItem(1,1, QSizePolicy::Expanding, QSizePolicy::Expanding));
msgBox.layout()->addWidget(pb);
msgBox.setStyleSheet(QStringLiteral(
"FramelessWidget { "
"border-radius: 12px; "
"border: 3px solid palette(shadow); "
"background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #ffeb7f, stop: 1 #d09d1e); "
"}"
));
msgBox.show();
return a.exec();
}
參考:
WM_NCHITTEST
實現)WM_NCHITTEST
版本)qwindowsintegration
、 qwindowswindow
、 qwindowscontext
)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.