如何在 Windows composer 中集成 Qt 无框窗口?(系统快捷方式不起作用) [英] How to integrate the Qt frameless window in Windows composer? (system shortcut doesnt works)

查看:24
本文介绍了如何在 Windows composer 中集成 Qt 无框窗口?(系统快捷方式不起作用)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 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 错误).

作为奖励,这也允许圆角.绘画/样式基于我为

参考文献:

I worked on Windows platform. If I used native frameless window flags like:

::SetWindowLongPtr((HWND)winId(), GWL_STYLE, WS_POPUP | WS_SYSMENU | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);

I recieve frameless window that correctly works with default Windows windows composer - it correctly change the state when I press "WIN" key + arrows.

When I try to use Qt library with following frameless window flags:

setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::CustomizeWindowHint);

I recieve the window that doesnt responce on the "WIN" key + arrows. So it doesnt works with default Windows window composer.

Which compbination of Qt window flags would have similar behavior like native flags above?

解决方案

So, indeed this has little to do with Qt, it seems. Having anything but a full frame on a Windows window seems to disable the "snap" shortcuts, even if the window can still be resized or moved with keyboard arrows (from the Alt+Space system menu).

The workaround is actually pretty simple. Basically to implement the QWidget::nativeEvent() virtual method and ignore the WM_NCCALCSIZE message.

I ran into some painting issues when snapping from one screen to another, but worked around that with a mask (notes in code comments). Would be nice to find a cleaner solution (it may qualify as a Qt bug actually).

As a bonus this also allows for rounded corners. The painting/styling is based on a rounded message box I made for another answer, and the painting code is documented more fully over there.

The system menu and all interactions with keys (move/resize/snap/etc) work. Mouse handling could be added by implementing the WM_NCHITTEST message in nativeEvent() (see references below).

Only tested on Win7, would be curious how it acts on Win10.

FramelessWidget

#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

Test implementation


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();
}

References:

这篇关于如何在 Windows composer 中集成 Qt 无框窗口?(系统快捷方式不起作用)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆