Advertisement
Guest User

BaseFramelessWindow implementation

a guest
Feb 21st, 2020
315
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.30 KB | None | 0 0
  1. #include "BaseFramelessWindow.hpp"
  2.  
  3. #include <QtWidgets/qapplication.h>
  4.  
  5. #ifdef Q_OS_WIN
  6. #include <windows.h>
  7. #include <windowsx.h>
  8.  
  9. #include <WinUser.h>
  10.  
  11. #include <gdiplus.h>
  12.  
  13. #include <GdiPlusColor.h>
  14.  
  15. #include <dwmapi.h>
  16. #include <objidl.h>
  17. #pragma comment(lib, "Dwmapi.lib")
  18. #pragma comment(lib, "user32.lib")
  19.  
  20. namespace Utils {
  21.     inline bool isMaximized(HWND hwnd)
  22.     {
  23.         WINDOWPLACEMENT placement;
  24.         if (!::GetWindowPlacement(hwnd, &placement)) {
  25.             return false;
  26.         }
  27.  
  28.         return placement.showCmd == SW_MAXIMIZE;
  29.     }
  30.  
  31.     inline void adjustMaximizedClientRect(HWND window, RECT& rect)
  32.     {
  33.         if (!isMaximized(window)) {
  34.             return;
  35.         }
  36.  
  37.         auto monitor = ::MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
  38.         if (!monitor) {
  39.             return;
  40.         }
  41.  
  42.         MONITORINFO monitor_info {};
  43.         monitor_info.cbSize = sizeof(monitor_info);
  44.         if (!::GetMonitorInfoW(monitor, &monitor_info)) {
  45.             return;
  46.         }
  47.  
  48.         // When maximized, make the client area fill just the monitor (without task bar) rect,
  49.         // not the whole window rect which extends beyond the monitor.
  50.         rect = monitor_info.rcWork;
  51.     }
  52.  
  53.     inline bool isCompositionEnabled()
  54.     {
  55.         auto compositionEnabled = FALSE;
  56.         auto success = ::DwmIsCompositionEnabled(&compositionEnabled) == S_OK;
  57.         return compositionEnabled && success;
  58.     }
  59.  
  60.     inline int selectBorderlessStyle()
  61.     {
  62.         constexpr auto AeroBorderless = WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX;
  63.         constexpr auto BasicBorderless = WS_POPUP | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX;
  64.         return isCompositionEnabled() ? AeroBorderless : BasicBorderless;
  65.     }
  66.  
  67.     inline void initializeBorderless(HWND window)
  68.     {
  69.         ::SetWindowLongPtr(window, GWL_STYLE, static_cast<LONG>(selectBorderlessStyle()));
  70.         ::SetWindowPos(window, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
  71.     }
  72. } /* namespace Utils */
  73.  
  74. BaseFramelessWindow::BaseFramelessWindow(QWidget* parent)
  75.     : QMainWindow(parent)
  76.     , m_titlebar(Q_NULLPTR)
  77. {
  78.     setWindowFlags(windowFlags() | Qt::WindowMinMaxButtonsHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint | Qt::FramelessWindowHint);
  79.     Utils::initializeBorderless((HWND)winId());
  80.     const MARGINS shadow = { 1, 1, 1, 1 };
  81.     DwmExtendFrameIntoClientArea(HWND(winId()), &shadow);
  82. }
  83.  
  84. bool BaseFramelessWindow::nativeEvent(const QByteArray& eventType, void* message, long* result)
  85. {
  86.     // Workaround for known bug -> check Qt forum : https://forum.qt.io/topic/93141/qtablewidget-itemselectionchanged/13
  87. #if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
  88.     MSG* msg = *reinterpret_cast<MSG**>(message);
  89. #else
  90.     MSG* msg = reinterpret_cast<MSG*>(message);
  91. #endif
  92.  
  93.     switch (msg->message) {
  94.     case WM_NCCALCSIZE: {
  95.         if (msg->wParam == TRUE) {
  96.             auto& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam);
  97.             Utils::adjustMaximizedClientRect(msg->hwnd, params.rgrc[0]);
  98.         }
  99.         *result = WVR_REDRAW;
  100.         return true;
  101.     }
  102.     case WM_NCHITTEST: {
  103.         *result = 0;
  104.  
  105.         const LONG borderWidth = 5; // in pixels
  106.         RECT winrect;
  107.         GetWindowRect(reinterpret_cast<HWND>(winId()), &winrect);
  108.  
  109.         long x = GET_X_LPARAM(msg->lParam);
  110.         long y = GET_Y_LPARAM(msg->lParam);
  111.  
  112.         auto resizeWidth = minimumWidth() != maximumWidth();
  113.         auto resizeHeight = minimumHeight() != maximumHeight();
  114.  
  115.         if (resizeWidth) {
  116.             // Left border
  117.             if (x >= winrect.left && x < winrect.left + borderWidth) {
  118.                 *result = HTLEFT;
  119.             }
  120.             // Right border
  121.             if (x < winrect.right && x >= winrect.right - borderWidth) {
  122.                 *result = HTRIGHT;
  123.             }
  124.         }
  125.         if (resizeHeight) {
  126.             // Bottom border
  127.             if (y < winrect.bottom && y >= winrect.bottom - borderWidth) {
  128.                 *result = HTBOTTOM;
  129.             }
  130.             // Top border
  131.             if (y >= winrect.top && y < winrect.top + borderWidth) {
  132.                 *result = HTTOP;
  133.             }
  134.         }
  135.         if (resizeWidth && resizeHeight) {
  136.             // Bottom left corner
  137.             if (x >= winrect.left && x < winrect.left + borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth) {
  138.                 *result = HTBOTTOMLEFT;
  139.             }
  140.             // Bottom right corner
  141.             if (x < winrect.right && x >= winrect.right - borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth) {
  142.                 *result = HTBOTTOMRIGHT;
  143.             }
  144.             // Top left corner
  145.             if (x >= winrect.left && x < winrect.left + borderWidth && y >= winrect.top && y < winrect.top + borderWidth) {
  146.                 *result = HTTOPLEFT;
  147.             }
  148.             // Top right corner
  149.             if (x < winrect.right && x >= winrect.right - borderWidth && y >= winrect.top && y < winrect.top + borderWidth) {
  150.                 *result = HTTOPRIGHT;
  151.             }
  152.         }
  153.  
  154.         if (*result != 0)
  155.             return true;
  156.  
  157.         // *result still equals 0, that means the cursor locate OUTSIDE the frame area
  158.         // but it may locate in titlebar area
  159.         if (!m_titlebar)
  160.             return false;
  161.  
  162.         // support highdpi
  163.         qreal dpr = this->devicePixelRatioF();
  164.         QPoint pos = m_titlebar->mapFromGlobal(QPoint(x / dpr, y / dpr));
  165.  
  166.         if (!m_titlebar->rect().contains(pos))
  167.             return false;
  168.  
  169.         QWidget* child = m_titlebar->childAt(pos);
  170.         if (!child) {
  171.             *result = HTCAPTION;
  172.             return true;
  173.         }
  174.  
  175.         return false;
  176.     }
  177.     case WM_NCRBUTTONUP: {
  178.         long x = GET_X_LPARAM(msg->lParam);
  179.         long y = GET_Y_LPARAM(msg->lParam);
  180.  
  181.         // Support highdpi
  182.         qreal dpr = this->devicePixelRatioF();
  183.         QPoint pos = m_titlebar->mapFromGlobal(QPoint(x / dpr, y / dpr));
  184.  
  185.         if (m_titlebar->rect().contains(pos)) {
  186.             HMENU menu = GetSystemMenu(msg->hwnd, FALSE);
  187.             TrackPopupMenu(menu, 0, x, y, 0, msg->hwnd, nullptr);
  188.         }
  189.  
  190.         return true;
  191.     }
  192.     case WM_GETMINMAXINFO: {
  193.         MINMAXINFO* mmi = reinterpret_cast<MINMAXINFO*>(msg->lParam);
  194.  
  195.         if (Utils::isMaximized(msg->hwnd)) {
  196.  
  197.             RECT window_rect;
  198.  
  199.             if (!GetWindowRect(msg->hwnd, &window_rect)) {
  200.                 return false;
  201.             }
  202.  
  203.             HMONITOR monitor = MonitorFromRect(&window_rect, MONITOR_DEFAULTTONULL);
  204.             if (!monitor) {
  205.                 return false;
  206.             }
  207.  
  208.             MONITORINFO monitor_info = { 0 };
  209.             monitor_info.cbSize = sizeof(monitor_info);
  210.             GetMonitorInfo(monitor, &monitor_info);
  211.  
  212.             RECT work_area = monitor_info.rcWork;
  213.             RECT monitor_rect = monitor_info.rcMonitor;
  214.  
  215.             mmi->ptMaxPosition.x = abs(work_area.left - monitor_rect.left);
  216.             mmi->ptMaxPosition.y = abs(work_area.top - monitor_rect.top);
  217.  
  218.             mmi->ptMaxSize.x = abs(work_area.right - work_area.left);
  219.             mmi->ptMaxSize.y = abs(work_area.bottom - work_area.top);
  220.             mmi->ptMaxTrackSize.x = mmi->ptMaxSize.x;
  221.             mmi->ptMaxTrackSize.y = mmi->ptMaxSize.y;
  222.  
  223.             *result = 1;
  224.         }
  225.  
  226.         return false;
  227.     }
  228.     case WM_NCACTIVATE: {
  229.         if (!Utils::isCompositionEnabled()) {
  230.             // Prevents window frame reappearing on window activation
  231.             // in "basic" theme, where no aero shadow is present.
  232.             *result = 1;
  233.             return true;
  234.         }
  235.     }
  236.     case WM_SIZE: {
  237.         RECT winrect;
  238.         GetClientRect(msg->hwnd, &winrect);
  239.  
  240.         WINDOWPLACEMENT wp;
  241.         wp.length = sizeof(WINDOWPLACEMENT);
  242.         GetWindowPlacement(msg->hwnd, &wp);
  243.         if (this) {
  244.             if (wp.showCmd == SW_MAXIMIZE) {
  245.                 ::SetWindowPos(reinterpret_cast<HWND>(winId()), Q_NULLPTR, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
  246.             }
  247.         }
  248.        
  249.         return false;
  250.     }
  251.     }
  252.  
  253.     return QWidget::nativeEvent(eventType, message, result);
  254. }
  255. #endif /* Q_OS_WIN */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement