Qt工作Windows 8风格无框自定义窗口

时间:2013-10-31 20:39:21

标签: qt windows-8 github stylesheet

我最近在Windows 7机器上安装了Github for Windows,并且喜欢它拥有的自定义框架,它非常适合整个应用程序主题,并且拥有自己的标题栏按钮,这些按钮非常好,非常流畅,而且非常流畅很自然地工作。

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

事情是,它不像其他窗户那样流畅自然地响应,它很奇怪,我很容易让窗户用鼠标移动,但它经常出现故障,并且能够在它应该移动的区域上移动不喜欢点击并拖动已禁用的按钮。

与showMaximize方法相关联的最大化按钮只是将整个窗口放大以占据整个桌面,你仍然可以移动它(真的没有真正最大化)。

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

经过大量的修复之后,我终于放弃了,因为我真的很好看,看起来非常直观,就像github for Windows一样非常直观。

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

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

2 个答案:

答案 0 :(得分:5)

通过单击任务栏

来最小化窗口

Qt::FramelessWindowHint的实施似乎有限。设置此标志后,Windows认为此窗口无法最小化或最大化。我试过在纯winapi中实现this solution。通过单击任务栏最小化和恢复无框窗口工作正常。显然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开发人员这种行为是否合理,还是一个bug。我认为这个问题可以用补丁修复。

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

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

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

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是类变量。

答案 1 :(得分:0)

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

唯一真正的解决方案是使用Windows为此目的提供的DWM API Custom Window Frame Using DWM。它们提供了如何在框架区域中执行任何操作的示例,同时保留了所有标准的Windows管理器行为。

Chrome等应用程序确实使用DWM(请参阅Chromium source中的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框架的工作示例,但希望这提供了一个起点。