framelessbase.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. #include "framelessbase.h"
  2. #include <QCoreApplication>
  3. #include <QGuiApplication>
  4. #include <QTimer>
  5. #include <QVBoxLayout>
  6. #include <QtCore/QDebug>
  7. #include <QtCore/QFile>
  8. #include <QtCore/QTime>
  9. #include <QtCore/QTimer>
  10. #include <QtGui/QPainter>
  11. #include <QtGui/QWindow>
  12. #include <QtWidgets/QApplication>
  13. #include <QtWidgets/QPushButton>
  14. #include <QtWidgets/QStyle>
  15. #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
  16. #include <QtGui/QActionGroup>
  17. #else
  18. #include <QtWidgets/QActionGroup>
  19. #endif
  20. #include "widgetframe/windowbar.h"
  21. #include "widgetframe/windowbutton.h"
  22. #include "widgets/widgetwindowagent.h"
  23. static inline void emulateLeaveEvent(QWidget *widget)
  24. {
  25. Q_ASSERT(widget);
  26. if (!widget) {
  27. return;
  28. }
  29. QTimer::singleShot(0, widget, [widget]() {
  30. #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
  31. const QScreen *screen = widget->screen();
  32. #else
  33. const QScreen *screen = widget->windowHandle()->screen();
  34. #endif
  35. const QPoint globalPos = QCursor::pos(screen);
  36. if (!QRect(widget->mapToGlobal(QPoint{0, 0}), widget->size()).contains(globalPos)) {
  37. QCoreApplication::postEvent(widget, new QEvent(QEvent::Leave));
  38. if (widget->testAttribute(Qt::WA_Hover)) {
  39. const QPoint localPos = widget->mapFromGlobal(globalPos);
  40. const QPoint scenePos = widget->window()->mapFromGlobal(globalPos);
  41. static constexpr const auto oldPos = QPoint{};
  42. const Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
  43. #if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
  44. const auto event = new QHoverEvent(QEvent::HoverLeave,
  45. scenePos,
  46. globalPos,
  47. oldPos,
  48. modifiers);
  49. Q_UNUSED(localPos);
  50. #elif (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
  51. const auto event = new QHoverEvent(QEvent::HoverLeave, localPos, globalPos, oldPos, modifiers);
  52. Q_UNUSED(scenePos);
  53. #else
  54. const auto event = new QHoverEvent(QEvent::HoverLeave, localPos, oldPos, modifiers);
  55. Q_UNUSED(scenePos);
  56. #endif
  57. QCoreApplication::postEvent(widget, event);
  58. }
  59. }
  60. });
  61. }
  62. TMainWindow::TMainWindow(QWidget *parent)
  63. : QMainWindow(parent)
  64. {
  65. setAttribute(Qt::WA_DontCreateNativeAncestors);
  66. installWindowAgent();
  67. }
  68. bool TMainWindow::event(QEvent *event)
  69. {
  70. switch (event->type()) {
  71. case QEvent::WindowActivate: {
  72. auto menu = menuWidget();
  73. if (menu) {
  74. menu->setProperty("bar-active", true);
  75. style()->polish(menu);
  76. }
  77. break;
  78. }
  79. case QEvent::WindowDeactivate: {
  80. auto menu = menuWidget();
  81. if (menu) {
  82. menu->setProperty("bar-active", false);
  83. style()->polish(menu);
  84. }
  85. break;
  86. }
  87. default:
  88. break;
  89. }
  90. return QMainWindow::event(event);
  91. }
  92. void TMainWindow::installWindowAgent()
  93. {
  94. // 1. Setup window agent
  95. windowAgent = new QWK::WidgetWindowAgent(this);
  96. windowAgent->setup(this);
  97. auto titleLabel = new QLabel(windowTitle());
  98. titleLabel->setAlignment(Qt::AlignCenter);
  99. titleLabel->setObjectName(QStringLiteral("win-title-label"));
  100. #ifndef Q_OS_MAC
  101. auto iconButton = new QWK::WindowButton();
  102. iconButton->setObjectName(QStringLiteral("icon-button"));
  103. iconButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  104. auto pinButton = new QWK::WindowButton();
  105. pinButton->setCheckable(true);
  106. pinButton->setObjectName(QStringLiteral("pin-button"));
  107. pinButton->setProperty("system-button", true);
  108. pinButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  109. auto minButton = new QWK::WindowButton();
  110. minButton->setObjectName(QStringLiteral("min-button"));
  111. minButton->setProperty("system-button", true);
  112. minButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  113. auto maxButton = new QWK::WindowButton();
  114. maxButton->setCheckable(true);
  115. maxButton->setObjectName(QStringLiteral("max-button"));
  116. maxButton->setProperty("system-button", true);
  117. maxButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  118. auto closeButton = new QWK::WindowButton();
  119. closeButton->setObjectName(QStringLiteral("close-button"));
  120. closeButton->setProperty("system-button", true);
  121. closeButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  122. #endif
  123. auto windowBar = new QWK::WindowBar();
  124. #ifndef Q_OS_MAC
  125. windowBar->setIconButton(iconButton);
  126. windowBar->setPinButton(pinButton);
  127. windowBar->setMinButton(minButton);
  128. windowBar->setMaxButton(maxButton);
  129. windowBar->setCloseButton(closeButton);
  130. #endif
  131. windowBar->setTitleLabel(titleLabel);
  132. windowBar->setHostWidget(this);
  133. windowAgent->setTitleBar(windowBar);
  134. #ifndef Q_OS_MAC
  135. windowAgent->setHitTestVisible(pinButton, true);
  136. windowAgent->setSystemButton(QWK::WindowAgentBase::WindowIcon, iconButton);
  137. windowAgent->setSystemButton(QWK::WindowAgentBase::Minimize, minButton);
  138. windowAgent->setSystemButton(QWK::WindowAgentBase::Maximize, maxButton);
  139. windowAgent->setSystemButton(QWK::WindowAgentBase::Close, closeButton);
  140. #endif
  141. #ifdef Q_OS_MAC
  142. windowAgent->setSystemButtonAreaCallback([](const QSize &size) {
  143. static constexpr const int width = 75;
  144. return QRect(QPoint(size.width() - width, 0), QSize(width, size.height())); //
  145. });
  146. #endif
  147. setMenuWidget(windowBar);
  148. #ifndef Q_OS_MAC
  149. connect(this, &QMainWindow::windowTitleChanged, this, [titleLabel](const QString &title) {
  150. titleLabel->setText(title);
  151. });
  152. connect(windowBar, &QWK::WindowBar::pinRequested, this, [this, pinButton](bool pin) {
  153. if (isHidden() || isMinimized() || isMaximized() || isFullScreen()) {
  154. return;
  155. }
  156. setWindowFlag(Qt::WindowStaysOnTopHint, pin);
  157. show();
  158. pinButton->setChecked(pin);
  159. });
  160. connect(windowBar, &QWK::WindowBar::minimizeRequested, this, &QWidget::showMinimized);
  161. connect(windowBar, &QWK::WindowBar::maximizeRequested, this, [this, maxButton](bool max) {
  162. if (max) {
  163. showMaximized();
  164. } else {
  165. showNormal();
  166. }
  167. // It's a Qt issue that if a QAbstractButton::clicked triggers a window's maximization,
  168. // the button remains to be hovered until the mouse move. As a result, we need to
  169. // manually send leave events to the button.
  170. emulateLeaveEvent(maxButton);
  171. });
  172. connect(windowBar, &QWK::WindowBar::closeRequested, this, &QWidget::close);
  173. #endif
  174. }
  175. TWidget::TWidget(QWidget *parent)
  176. : QWidget(parent)
  177. , m_menuWidget(nullptr)
  178. , m_mainLayout(nullptr)
  179. {
  180. m_mainLayout = new QVBoxLayout(this);
  181. m_mainLayout->setContentsMargins(0, 0, 0, 0);
  182. m_mainLayout->setSpacing(0);
  183. setLayout(m_mainLayout);
  184. setAttribute(Qt::WA_DontCreateNativeAncestors);
  185. installWindowAgent();
  186. }
  187. QWidget *TWidget::menuWidget() const
  188. {
  189. return m_menuWidget;
  190. }
  191. void TWidget::setMenuWidget(QWidget *widget)
  192. {
  193. if (m_menuWidget) {
  194. m_mainLayout->removeWidget(m_menuWidget);
  195. delete m_menuWidget;
  196. }
  197. // 添加新标题栏到布局顶部
  198. m_menuWidget = widget;
  199. if (m_menuWidget) {
  200. m_mainLayout->insertWidget(0, m_menuWidget); // 插入到第一个位置
  201. }
  202. }
  203. bool TWidget::event(QEvent *event)
  204. {
  205. switch (event->type()) {
  206. case QEvent::WindowActivate: {
  207. auto menu = menuWidget();
  208. if (menu) {
  209. menu->setProperty("bar-active", true);
  210. style()->polish(menu);
  211. }
  212. break;
  213. }
  214. case QEvent::WindowDeactivate: {
  215. auto menu = menuWidget();
  216. if (menu) {
  217. menu->setProperty("bar-active", false);
  218. style()->polish(menu);
  219. }
  220. break;
  221. }
  222. default:
  223. break;
  224. }
  225. return QWidget::event(event);
  226. }
  227. void TWidget::installWindowAgent()
  228. {
  229. // 1. Setup window agent
  230. windowAgent = new QWK::WidgetWindowAgent(this);
  231. windowAgent->setup(this);
  232. auto titleLabel = new QLabel();
  233. titleLabel->setAlignment(Qt::AlignCenter);
  234. titleLabel->setObjectName(QStringLiteral("win-title-label"));
  235. #ifndef Q_OS_MAC
  236. auto iconButton = new QWK::WindowButton();
  237. iconButton->setObjectName(QStringLiteral("icon-button"));
  238. iconButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  239. auto pinButton = new QWK::WindowButton();
  240. pinButton->setCheckable(true);
  241. pinButton->setObjectName(QStringLiteral("pin-button"));
  242. pinButton->setProperty("system-button", true);
  243. pinButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  244. auto minButton = new QWK::WindowButton();
  245. minButton->setObjectName(QStringLiteral("min-button"));
  246. minButton->setProperty("system-button", true);
  247. minButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  248. auto maxButton = new QWK::WindowButton();
  249. maxButton->setCheckable(true);
  250. maxButton->setObjectName(QStringLiteral("max-button"));
  251. maxButton->setProperty("system-button", true);
  252. maxButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  253. auto closeButton = new QWK::WindowButton();
  254. closeButton->setObjectName(QStringLiteral("close-button"));
  255. closeButton->setProperty("system-button", true);
  256. closeButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  257. #endif
  258. auto windowBar = new QWK::WindowBar();
  259. #ifndef Q_OS_MAC
  260. windowBar->setIconButton(iconButton);
  261. windowBar->setPinButton(pinButton);
  262. windowBar->setMinButton(minButton);
  263. windowBar->setMaxButton(maxButton);
  264. windowBar->setCloseButton(closeButton);
  265. #endif
  266. windowBar->setTitleLabel(titleLabel);
  267. windowBar->setHostWidget(this);
  268. windowAgent->setTitleBar(windowBar);
  269. #ifndef Q_OS_MAC
  270. windowAgent->setHitTestVisible(pinButton, true);
  271. windowAgent->setSystemButton(QWK::WindowAgentBase::WindowIcon, iconButton);
  272. windowAgent->setSystemButton(QWK::WindowAgentBase::Minimize, minButton);
  273. windowAgent->setSystemButton(QWK::WindowAgentBase::Maximize, maxButton);
  274. windowAgent->setSystemButton(QWK::WindowAgentBase::Close, closeButton);
  275. #endif
  276. #ifdef Q_OS_MAC
  277. windowAgent->setSystemButtonAreaCallback([](const QSize &size) {
  278. static constexpr const int width = 75;
  279. return QRect(QPoint(size.width() - width, 0), QSize(width, size.height())); //
  280. });
  281. #endif
  282. setMenuWidget(windowBar);
  283. #ifndef Q_OS_MAC
  284. connect(windowBar, &QWK::WindowBar::pinRequested, this, [this, pinButton](bool pin) {
  285. if (isHidden() || isMinimized() || isMaximized() || isFullScreen()) {
  286. return;
  287. }
  288. setWindowFlag(Qt::WindowStaysOnTopHint, pin);
  289. show();
  290. pinButton->setChecked(pin);
  291. });
  292. connect(windowBar, &QWK::WindowBar::minimizeRequested, this, &QWidget::showMinimized);
  293. connect(windowBar, &QWK::WindowBar::maximizeRequested, this, [this, maxButton](bool max) {
  294. if (max) {
  295. showMaximized();
  296. } else {
  297. showNormal();
  298. }
  299. // It's a Qt issue that if a QAbstractButton::clicked triggers a window's maximization,
  300. // the button remains to be hovered until the mouse move. As a result, we need to
  301. // manually send leave events to the button.
  302. emulateLeaveEvent(maxButton);
  303. });
  304. connect(windowBar, &QWK::WindowBar::closeRequested, this, &QWidget::close);
  305. #endif
  306. }