maskoverlay.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #include "maskoverlay.h"
  2. #include <QApplication>
  3. #include <QGraphicsOpacityEffect>
  4. #include <QPainter>
  5. #include <QPropertyAnimation>
  6. #include <QResizeEvent>
  7. #include <QScreen>
  8. #include <QVBoxLayout>
  9. #include "qdebug.h"
  10. MaskOverlay* MaskOverlay::instance_ = nullptr;
  11. MaskOverlay* MaskOverlay::instance()
  12. {
  13. if (!instance_) {
  14. instance_ = new MaskOverlay(qApp);
  15. }
  16. return instance_;
  17. }
  18. MaskOverlay::MaskOverlay(QObject* parent)
  19. : QObject(parent)
  20. , globalStyle_("background-color: rgba(0, 0, 0, 128);")
  21. , progressStyle_("QProgressBar {"
  22. " border: 2px solid #555;"
  23. " border-radius: 5px;"
  24. " background: rgba(255, 255, 255, 150);"
  25. "}"
  26. "QProgressBar::chunk {"
  27. " background-color: #05B8CC;"
  28. " border-radius: 3px;"
  29. "}")
  30. {}
  31. QWidget* MaskOverlay::findBestTarget(QWidget* hintTarget, FindMode mode)
  32. {
  33. if (hintTarget)
  34. return hintTarget;
  35. switch (mode) {
  36. case ActiveWindow:
  37. return QApplication::activeWindow();
  38. case MainWindow: {
  39. foreach (QWidget* w, QApplication::topLevelWidgets()) {
  40. if (w->isWindow() && w->windowType() == Qt::Window) {
  41. return w;
  42. }
  43. }
  44. return nullptr;
  45. }
  46. case ParentWindow:
  47. return qobject_cast<QWidget*>(parent());
  48. default:
  49. return nullptr;
  50. }
  51. }
  52. void MaskOverlay::show(QWidget* target,
  53. int timeoutMs,
  54. FindMode findMode,
  55. std::function<void()> timeoutCallback)
  56. {
  57. QWidget* actualTarget = findBestTarget(target, findMode);
  58. if (!actualTarget) {
  59. qWarning() << "No suitable target window found";
  60. return;
  61. }
  62. if (overlays_.contains(actualTarget))
  63. return;
  64. setupOverlay(actualTarget);
  65. OverlayContext& ctx = overlays_[actualTarget];
  66. // 渐显动画
  67. QGraphicsOpacityEffect* effect = new QGraphicsOpacityEffect(ctx.maskWidget);
  68. ctx.maskWidget->setGraphicsEffect(effect);
  69. QPropertyAnimation* anim = new QPropertyAnimation(effect, "opacity");
  70. anim->setDuration(300);
  71. anim->setStartValue(0);
  72. anim->setEndValue(1);
  73. anim->start(QPropertyAnimation::DeleteWhenStopped);
  74. ctx.maskWidget->show();
  75. ctx.maskWidget->raise();
  76. lastShownTarget_ = actualTarget;
  77. if (timeoutMs > 0) {
  78. ctx.timeoutTimer->setInterval(timeoutMs);
  79. ctx.timeoutTimer->start();
  80. ctx.timeoutCallback = timeoutCallback;
  81. }
  82. }
  83. void MaskOverlay::hide(QWidget* target)
  84. {
  85. QWidget* actualTarget = target ? target : lastShownTarget_.data();
  86. if (!actualTarget)
  87. actualTarget = findBestTarget(nullptr, ActiveWindow);
  88. if (!actualTarget || !overlays_.contains(actualTarget))
  89. return;
  90. OverlayContext ctx = overlays_.take(actualTarget);
  91. ctx.timeoutTimer->stop();
  92. // 渐隐动画
  93. QGraphicsOpacityEffect* effect = new QGraphicsOpacityEffect(ctx.maskWidget);
  94. ctx.maskWidget->setGraphicsEffect(effect);
  95. QPropertyAnimation* anim = new QPropertyAnimation(effect, "opacity");
  96. anim->setDuration(300);
  97. anim->setStartValue(1);
  98. anim->setEndValue(0);
  99. connect(anim, &QPropertyAnimation::finished, [ctx]() {
  100. ctx.maskWidget->deleteLater();
  101. ctx.progressBar->deleteLater();
  102. ctx.timeoutTimer->deleteLater();
  103. });
  104. anim->start(QPropertyAnimation::DeleteWhenStopped);
  105. }
  106. void MaskOverlay::setupOverlay(QWidget* target)
  107. {
  108. if (!target || overlays_.contains(target))
  109. return;
  110. OverlayContext ctx;
  111. ctx.maskWidget = new QWidget(target);
  112. ctx.maskWidget->setStyleSheet(globalStyle_);
  113. ctx.maskWidget->setAttribute(Qt::WA_TransparentForMouseEvents, false);
  114. ctx.maskWidget->resize(target->size());
  115. ctx.progressBar = new QProgressBar(ctx.maskWidget);
  116. ctx.progressBar->setStyleSheet(progressStyle_);
  117. ctx.progressBar->setRange(0, 0);
  118. ctx.progressBar->setFixedSize(200, 20);
  119. QVBoxLayout* layout = new QVBoxLayout(ctx.maskWidget);
  120. layout->addWidget(ctx.progressBar, 0, Qt::AlignCenter);
  121. ctx.timeoutTimer = new QTimer(this);
  122. ctx.timeoutTimer->setSingleShot(true);
  123. connect(ctx.timeoutTimer, &QTimer::timeout, [this, target]() {
  124. if (overlays_.contains(target)) {
  125. if (overlays_[target].timeoutCallback) {
  126. overlays_[target].timeoutCallback();
  127. }
  128. this->hide(target);
  129. }
  130. });
  131. target->installEventFilter(this);
  132. overlays_.insert(target, ctx);
  133. }
  134. bool MaskOverlay::eventFilter(QObject* watched, QEvent* event)
  135. {
  136. QWidget* target = qobject_cast<QWidget*>(watched);
  137. if (!target)
  138. return false;
  139. if (event->type() == QEvent::Resize) {
  140. if (overlays_.contains(target)) {
  141. overlays_[target].maskWidget->resize(target->size());
  142. }
  143. } else if (event->type() == QEvent::Close) {
  144. hide(target);
  145. }
  146. return QObject::eventFilter(watched, event);
  147. }
  148. void MaskOverlay::setGlobalStyle(const QString& styleSheet)
  149. {
  150. globalStyle_ = styleSheet;
  151. for (auto& ctx : overlays_) {
  152. ctx.maskWidget->setStyleSheet(globalStyle_);
  153. }
  154. }
  155. void MaskOverlay::setProgressBarStyle(const QString& styleSheet)
  156. {
  157. progressStyle_ = styleSheet;
  158. for (auto& ctx : overlays_) {
  159. ctx.progressBar->setStyleSheet(progressStyle_);
  160. }
  161. }
  162. void MaskOverlay::setProgressValue(QWidget* target, int value)
  163. {
  164. QWidget* actualTarget = target ? target : lastShownTarget_.data();
  165. if (!actualTarget || !overlays_.contains(actualTarget))
  166. return;
  167. OverlayContext& ctx = overlays_[actualTarget];
  168. ctx.progressBar->setRange(0, 100); // 设置为确定模式
  169. ctx.progressBar->setValue(value);
  170. }
  171. void MaskOverlay::setTimeout(QWidget* target, int milliseconds)
  172. {
  173. if (target && overlays_.contains(target)) {
  174. overlays_[target].timeoutTimer->setInterval(milliseconds);
  175. }
  176. }