简体   繁体   English

如何在 Windows composer 中集成 Qt 无框窗口? (系统快捷方式不起作用)

[英]How to integrate the Qt frameless window in Windows composer? (system shortcut doesnt works)

I worked on Windows platform.我在 Windows 平台上工作。 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.我收到了可以与默认的 Windows 窗口编辑器一起正常工作的无框窗口 - 当我按“WIN”键 + 箭头时,它会正确更改状态。

When I try to use Qt library with following frameless window flags:当我尝试使用带有以下无框窗口标志的 Qt 库时:

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

I recieve the window that doesnt responce on the "WIN" key + arrows.我收到了对“WIN”键+箭头没有反应的窗口。 So it doesnt works with default Windows window composer.所以它不适用于默认的 Windows 窗口编辑器。

Which compbination of Qt window flags would have similar behavior like native flags above? Qt 窗口标志的哪个组合将具有与上述本机标志类似的行为?

So, indeed this has little to do with Qt, it seems.所以,这似乎确实与 Qt 关系不大。 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).在 Windows 窗口上除了全帧之外的任何东西似乎都禁用了“捕捉”快捷方式,即使窗口仍然可以调整大小或使用键盘箭头移动(来自 Alt+Space 系统菜单)。

The workaround is actually pretty simple.解决方法实际上非常简单。 Basically to implement the QWidget::nativeEvent() virtual method and ignore the WM_NCCALCSIZE message.基本上实现QWidget::nativeEvent()虚拟方法并忽略WM_NCCALCSIZE消息。

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).找到一个更干净的解决方案会很好(实际上它可能有资格作为 Qt 错误)。

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).可以通过在nativeEvent()实现WM_NCHITTEST消息来添加鼠标处理(请参阅下面的参考资料)。

Only tested on Win7, would be curious how it acts on Win10.只在Win7上测试过,很好奇它在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:参考:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM