framelessmainwindowwin.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. #include "framelessmainwindowwin.h"
  2. #ifdef Q_OS_WIN
  3. #include <QTimer>
  4. #include <QDebug>
  5. #include <QEvent>
  6. #include <windows.h>
  7. #include <windowsx.h>
  8. #include <dwmapi.h>
  9. #pragma comment (lib,"dwmapi.lib")
  10. #pragma comment (lib, "user32.lib")
  11. using namespace vnotex;
  12. FramelessMainWindowWin::FramelessMainWindowWin(bool p_frameless, QWidget *p_parent)
  13. : FramelessMainWindow(p_frameless, p_parent)
  14. {
  15. if (m_frameless) {
  16. m_resizeAreaWidth *= devicePixelRatio();
  17. m_redrawTimer = new QTimer(this);
  18. m_redrawTimer->setSingleShot(true);
  19. m_redrawTimer->setInterval(500);
  20. connect(m_redrawTimer, &QTimer::timeout,
  21. this, &FramelessMainWindowWin::forceRedraw);
  22. connect(this, &FramelessMainWindow::windowStateChanged,
  23. this, &FramelessMainWindowWin::updateMargins);
  24. // Enable some window effects on Win, such as snap and maximizing.
  25. // It will activate the title bar again. Need to remove it in WM_NCCALCSIZE msg.
  26. HWND hwnd = reinterpret_cast<HWND>(winId());
  27. DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
  28. ::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION);
  29. // Leave 1 pixel width of border so OS will draw a window shadow.
  30. const MARGINS shadow = {1, 1, 1, 1};
  31. DwmExtendFrameIntoClientArea(hwnd, &shadow);
  32. }
  33. }
  34. #if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
  35. bool FramelessMainWindowWin::nativeEvent(const QByteArray &p_eventType, void *p_message, qintptr *p_result)
  36. #else
  37. bool FramelessMainWindowWin::nativeEvent(const QByteArray &p_eventType, void *p_message, long *p_result)
  38. #endif
  39. {
  40. if (!m_frameless) {
  41. return FramelessMainWindow::nativeEvent(p_eventType, p_message, p_result);
  42. }
  43. if (p_eventType == QStringLiteral("windows_generic_MSG")) {
  44. MSG *msg = static_cast<MSG *>(p_message);
  45. switch (msg->message) {
  46. case WM_NCCALCSIZE:
  47. *p_result = 0;
  48. return true;
  49. case WM_NCHITTEST:
  50. {
  51. if (m_windowStates & Qt::WindowFullScreen) {
  52. *p_result = HTCLIENT;
  53. return true;
  54. }
  55. RECT windowRect;
  56. ::GetWindowRect(msg->hwnd, &windowRect);
  57. // x and y could not be compared with width() and height() in hidpi case.
  58. const int x = static_cast<int>(GET_X_LPARAM(msg->lParam) - windowRect.left);
  59. const int y = static_cast<int>(GET_Y_LPARAM(msg->lParam) - windowRect.top);
  60. *p_result = 0;
  61. if (m_resizable) {
  62. if (x < m_resizeAreaWidth) {
  63. // Left.
  64. if (y < m_resizeAreaWidth) {
  65. // Top.
  66. *p_result = HTTOPLEFT;
  67. } else if (y > windowRect.bottom - windowRect.top - m_resizeAreaWidth) {
  68. // Bottom.
  69. *p_result = HTBOTTOMLEFT;
  70. } else {
  71. *p_result = HTLEFT;
  72. }
  73. } else if (x > windowRect.right - windowRect.left - m_resizeAreaWidth) {
  74. // Right.
  75. if (y < m_resizeAreaWidth) {
  76. // Top.
  77. *p_result = HTTOPRIGHT;
  78. } else if (y > windowRect.bottom - windowRect.top - m_resizeAreaWidth) {
  79. // Bottom.
  80. *p_result = HTBOTTOMRIGHT;
  81. } else {
  82. *p_result = HTRIGHT;
  83. }
  84. } else if (y < m_resizeAreaWidth) {
  85. *p_result = HTTOP;
  86. } else if (y > windowRect.bottom - windowRect.top - m_resizeAreaWidth) {
  87. *p_result = HTBOTTOM;
  88. }
  89. }
  90. if (0 != *p_result) {
  91. return true;
  92. }
  93. if (m_titleBar) {
  94. if (m_titleBarHeight == 0) {
  95. m_titleBarHeight = m_titleBar->height() * devicePixelRatio();
  96. }
  97. if (y < m_titleBarHeight) {
  98. QWidget *child = m_titleBar->childAt(m_titleBar->mapFromGlobal(QCursor::pos()));
  99. if (!child) {
  100. *p_result = HTCAPTION;
  101. if (::GetAsyncKeyState(VK_LBUTTON) < 0 || ::GetAsyncKeyState(VK_RBUTTON) < 0) {
  102. m_sizeBeforeMove = size();
  103. }
  104. return true;
  105. }
  106. }
  107. }
  108. break;
  109. }
  110. case WM_POWERBROADCAST:
  111. {
  112. if (msg->wParam == PBT_APMSUSPEND) {
  113. // Minimize when system is going to sleep to avoid bugs.
  114. showMinimized();
  115. }
  116. break;
  117. }
  118. case WM_GETMINMAXINFO:
  119. {
  120. // When maximized, OS will expand the content area. To avoid missing the real contents, set extra margins.
  121. if (::IsZoomed(msg->hwnd)) {
  122. RECT frame = {0, 0, 0, 0};
  123. ::AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, false, 0);
  124. const int dpiScale = devicePixelRatio();
  125. // Use bottom as top.
  126. QMargins newMargins(qAbs(frame.left) / dpiScale,
  127. qAbs(frame.bottom) / dpiScale,
  128. frame.right / dpiScale,
  129. frame.bottom / dpiScale);
  130. if (newMargins != m_maximizedMargins) {
  131. m_maximizedMargins = newMargins;
  132. updateMargins();
  133. }
  134. }
  135. break;
  136. }
  137. default:
  138. break;
  139. }
  140. }
  141. return FramelessMainWindow::nativeEvent(p_eventType, p_message, p_result);
  142. }
  143. void FramelessMainWindowWin::moveEvent(QMoveEvent *p_event)
  144. {
  145. FramelessMainWindow::moveEvent(p_event);
  146. if (m_frameless) {
  147. if (m_windowStates & Qt::WindowMaximized) {
  148. m_redrawTimer->stop();
  149. } else {
  150. m_redrawTimer->start();
  151. }
  152. }
  153. }
  154. void FramelessMainWindowWin::updateMargins()
  155. {
  156. if (!m_frameless) {
  157. return;
  158. }
  159. int topMargin = 0;
  160. if (isMaximized()) {
  161. setContentsMargins(m_maximizedMargins);
  162. topMargin = m_maximizedMargins.top();
  163. } else {
  164. setContentsMargins(0, 0, 0, 0);
  165. }
  166. if (m_titleBar) {
  167. m_titleBarHeight = (m_titleBar->height() + topMargin) * devicePixelRatio();
  168. }
  169. }
  170. void FramelessMainWindowWin::forceRedraw()
  171. {
  172. Q_ASSERT(m_frameless);
  173. if (m_windowStates & Qt::WindowMaximized) {
  174. return;
  175. }
  176. const QSize sz = size();
  177. RECT frame;
  178. ::GetWindowRect((HWND)winId(), &frame);
  179. const int clientWidth = (frame.right - frame.left) / devicePixelRatio();
  180. const int clientHeight = (frame.bottom - frame.top) / devicePixelRatio();
  181. if (clientWidth != sz.width() || clientHeight != sz.height()) {
  182. // resize() may result to "unable to set geometry" warning.
  183. // adjustsize() or resize() to another size before could solve this.
  184. resize(sz.width() + 1, sz.height() + 1);
  185. if (m_sizeBeforeMove.isEmpty()) {
  186. resize(clientWidth, clientHeight);
  187. } else {
  188. resize(m_sizeBeforeMove);
  189. }
  190. }
  191. }
  192. #endif