Qt working Windows 8 style frameless custom window

前端 未结 2 650
梦谈多话
梦谈多话 2021-01-06 10:03

I recently installed Github for Windows on my Windows 7 machine and loved the custom frame it had, it fit really well with the overall application theme and had it\'s own ti

相关标签:
2条回答
  • 2021-01-06 10:48

    Minimize window by clicking on task bar

    It seems that Qt::FramelessWindowHint's implementation is limited. When this flag is set, Windows thinks that this window cannot be minimized or maximized. I've tried this solution implemented in pure winapi. Minimizing and restoring frameless window by clicking on taskbar works fine. Apparently Qt sets some bad flags that block this functionality. May be there is a good reason for that, I don't know.

    We can use winapi and Qt together but it is troublesome. Firstly, winapi code should be executed after you set window flags and show the window using Qt. Otherwise Qt will overwrite window flags.

    Another problem is when we remove border using winapi, window geometry suddently changes, and Qt doesn't know about that. Rendering and event mapping (including mouse click positions) become invalid. I didn't find any documented way to update mapping. I've found that we can tell Qt that screen orientation has changed, and it forces it to recalculate window geometry. But this looks like a dirty hack. Also the QWidget::windowHandle function is missing in Qt 4 and "is subject to change" in Qt 5. So this method is not reliable. But anyway, it works now. Here is complete code (tested in Windows 8) that should be placed in the top window class constructor:

    #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);
    

    The true way to solve this problem is to modify the Window Qt platform plugin (see QWindowsWindow class in Qt sources). May be there is a way to inherit from the default implementation, modify it and use in your app. Also you can ask Qt developers is this behavior reasonable or is it a bug. I think that this issue can be fixed with a patch.

    If you still intend to use this code and other OSs should be also supported, don't forget to wrap windows-specific implementation in #ifdef Q_OS_WIN.

    Enable window dragging only when title bar is clicked and window is not maximized

    Other problems can be fixed more easily. When you process mouse events to implement window dragging, check window state and event position and disable moving when it is unwanted.

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

    Here ui->title is a label used for displaying fake title bar, and QPoint window_drag_start_pos is a class variable.

    0 讨论(0)
  • 2021-01-06 10:55

    If you use Qt::FramelessWindowHint, you lose all Windows frame related features, such as docking, shortcuts, maximizing, etc. You can implement some of them yourself, with great effort in some cases, but other things will still just not work, including handling multiple monitors and all the capabilities of the Windows Key. If you need your app to behave like a regular Windows app, Qt::FramelessWindowHint is pretty much a dead end.

    The only real solution is to use the DWM API that Windows provides for this purpose, Custom Window Frame Using DWM. They provide examples of how to do anything you might want in the frame area, while preserving all the standard windows-manager behaviors.

    Applications such as Chrome do use DWM for this (see the AeroGlassFrame class in the Chromium source). Granted, Chrome isn't using Qt, so it doesn't have to do the work of sneaking around QMainWindow to handle the Windows-specific messages, and their code has plenty of comments that indicate how messy it is, like:

     // 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() ...
    

    Similarly, the Github-for-Windows app you mention is built on a platform called Electron, which in turn uses, you guessed it, DWM.

    I haven't found any working example of a DWM frame around a Qt application, but hopefully this provides a starting point.

    0 讨论(0)
提交回复
热议问题