4

我最近在我的 Windows 7 机器上安装了适用于 Windows 的 Github,并且喜欢它的自定义框架,它非常适合整个应用程序主题,并且有自己的标题栏按钮,布局非常好,非常流畅,而且看起来很自然和。

我进行了一些挖掘,发现 2 个标志可以完全清除边框,经过一些自定义后,我的应用程序也有一个很好的自定义外观,它直观但与所有具有旧 Windows 边框的应用程序不同。

问题是它不像其他窗口那样流畅和自然响应,它非常有故障,我很容易用鼠标移动窗口,但它经常出现故障并且能够在它不应该喜欢的区域移动单击并拖动禁用的按钮。

链接到 showMaximize 方法的最大化按钮只是扩大了整个窗口以占据整个桌面,您仍然可以移动它(并没有真正最大化)。

该窗口没有响应任何系统信号,例如单击任务栏以将其最小化等。

经过大量修复后,我终于放弃了,这很惭愧,因为我真的很喜欢它的外观,而且非常直观,就像 Windows 的 github 非常直观一样。

有什么办法可以做到这一点,我真的还没准备好放弃。

我知道在制作原始 Windows API 应用程序时,您必须将其链接到 XP 内置样式,因为它默认继承了 Windows 95 样式,可能存在 Qt 未连接到的 Windows 8 样式,我不知道没有在研究方面还没有走那么远。

4

2 回答 2

5

通过单击任务栏最小化窗口

似乎Qt::FramelessWindowHint' 的实现是有限的。当这个标志被设置时,Windows 认为这个窗口不能被最小化或最大化。我已经尝试过在纯 winapi 中实现的这个解决方案。通过单击任务栏最小化和恢复无框窗口工作正常。显然,Qt 设置了一些阻止此功能的错误标志。可能有一个很好的理由,我不知道。

我们可以同时使用winapi和Qt,但是很麻烦。首先,应该在设置窗口标志并使用 Qt 显示窗口后执行 winapi 代码。否则 Qt 将覆盖窗口标志。

另一个问题是,当我们使用 winapi 删除边框时,窗口几何形状突然改变,而 Qt 不知道这一点。渲染和事件映射(包括鼠标点击位置)变得无效。我没有找到任何记录在案的方法来更新映射。我发现我们可以告诉 Qt 屏幕方向已经改变,它会强制它重新计算窗口几何形状。但这看起来像一个肮脏的黑客。Qt 4 中也缺少该QWidget::windowHandle功能,Qt 5 中“可能会发生变化”。所以这种方法不可靠。但无论如何,它现在有效。这是应放置在顶部窗口类构造函数中的完整代码(在 Windows 8 中测试):

#include "windows.h"
#include <QWindow>
//...
show();
HWND hwnd = reinterpret_cast<HWND>(effectiveWinId());
LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU);
SetWindowLong(hwnd, GWL_STYLE, lStyle);
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
windowHandle()->reportContentOrientationChange(Qt::PrimaryOrientation);

真正解决这个问题的方法是修改Window Qt平台插件(见Qt源码中的QWindowsWindow类)。可能有一种方法可以从默认实现继承,修改它并在您的应用程序中使用。您也可以询问 Qt 开发人员这种行为是合理的还是一个错误。我认为这个问题可以通过补丁解决。

如果您仍打算使用此代码并且还应支持其他操作系统,请不要忘记将特定于 windows 的实现包装在#ifdef Q_OS_WIN.

仅在单击标题栏且未最大化窗口时启用窗口拖动

其他问题可以更容易地解决。当您处理鼠标事件以实现窗口拖动时,检查窗口状态和事件位置并在不需要时禁用移动。

void MainWindow::mousePressEvent(QMouseEvent *e) {
  if (!isMaximized() && 
      e->button() == Qt::LeftButton && 
      ui->title->geometry().contains(e->pos())) {
    window_drag_start_pos = e->pos();
  }
}

void MainWindow::mouseReleaseEvent(QMouseEvent *e) {
  window_drag_start_pos = QPoint(0, 0);
}

void MainWindow::mouseMoveEvent(QMouseEvent *e) {
  if (!window_drag_start_pos.isNull()) {
    move(pos() + e->pos() - window_drag_start_pos);
  }
}

void MainWindow::on_minimize_clicked() {
  showMinimized();
}

void MainWindow::on_maximize_clicked() {
  if (isMaximized()) {
    showNormal();
  } else {
    showMaximized();
  }
}

ui->title是一个用于显示假标题栏的标签,QPoint window_drag_start_pos是一个类变量。

于 2013-10-31T22:06:49.340 回答
1

如果你使用 Qt::FramelessWindowHint,你会失去所有与 Windows 框架相关的功能,例如停靠、快捷方式、最大化等。你可以自己实现其中的一些,在某些情况下会付出很大的努力,但其他的东西仍然不起作用,包括处理多个显示器和 Windows 键的所有功能。如果您需要您的应用程序像普通的 Windows 应用程序一样运行,Qt::FramelessWindowHint 几乎是一条死胡同。

唯一真正的解决方案是使用 Windows 为此目的提供的 DWM API,即使用 DWM 的自定义窗口框架。它们提供了如何在框架区域中执行您可能想要的任何操作的示例,同时保留了所有标准的窗口管理器行为。

Chrome 等应用程序确实为此使用了 DWM(请参阅Chromium 源代码中的 AeroGlassFrame 类)。诚然,Chrome 没有使用 Qt,因此它不必在QMainWindow周围偷偷摸摸地处理特定于 Windows 的消息,并且他们的代码有很多注释表明它是多么混乱,例如:

 // Hack necessary to stop black background flicker, we cut out
 // resizeborder here to save us from having to do too much
 // addition and subtraction in Layout() ...

同样,您提到的 Github-for-Windows 应用程序是建立在一个名为 Electron 的平台上的,而该平台又使用了,您猜对了,DWM。

我还没有找到任何围绕 Qt 应用程序的 DWM 框架的工作示例,但希望这提供了一个起点。

于 2017-08-03T20:27:59.983 回答