vdi-cloud/terminal/kiosk-shell/src/transition_overlay.cpp

129 lines
3.9 KiB
C++

#include "transition_overlay.h"
#include <QEvent>
#include <QFont>
#include <QGuiApplication>
#include <QLabel>
#include <QScreen>
#include <QTimer>
#include <QVBoxLayout>
#include <QVariant>
namespace vdi {
namespace {
constexpr int kTickMs = 400; // 省略号/旋轮动画周期
constexpr int kFadeStepMs = 40; // 淡出步进周期
constexpr int kFadeSteps = 25; // 全程约 1 秒淡出
const char* kSpinnerFrames[] = {"◐", "◓", "◑", "◒"};
} // namespace
TransitionOverlay::TransitionOverlay(QWidget* parent) : QWidget(parent) {
// 独立置顶无边框窗口只在启动时映射一次,后续不再与 Moonlight 抢焦点。
setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint |
Qt::WindowDoesNotAcceptFocus);
setAttribute(Qt::WA_ShowWithoutActivating);
setStyleSheet(QStringLiteral("TransitionOverlay { background-color: #000000; }"));
auto* layout = new QVBoxLayout(this);
layout->setSpacing(28);
labelSpinner_ = new QLabel(this);
QFont spinFont = labelSpinner_->font();
spinFont.setPointSize(44);
labelSpinner_->setFont(spinFont);
labelSpinner_->setAlignment(Qt::AlignCenter);
labelSpinner_->setStyleSheet(QStringLiteral("color: #2f6feb; background: transparent;"));
layout->addStretch(1);
layout->addWidget(labelSpinner_);
labelTitle_ = new QLabel(this);
QFont titleFont = labelTitle_->font();
titleFont.setPointSize(20);
labelTitle_->setFont(titleFont);
labelTitle_->setAlignment(Qt::AlignCenter);
labelTitle_->setStyleSheet(QStringLiteral("color: #e6e9ef; background: transparent;"));
layout->addWidget(labelTitle_);
layout->addStretch(2);
tickTimer_ = new QTimer(this);
connect(tickTimer_, &QTimer::timeout, this, &TransitionOverlay::onTick);
fadeTimer_ = new QTimer(this);
connect(fadeTimer_, &QTimer::timeout, this, &TransitionOverlay::onFadeStep);
// 活动期间吞掉本窗口的键盘/鼠标事件,避免误触下层页面
installEventFilter(this);
}
void TransitionOverlay::start(const QString& title) {
tickTimer_->stop();
fadeTimer_->stop();
dots_ = 0;
setWindowOpacity(1.0);
setProperty("baseTitle", title);
labelTitle_->setText(title);
labelSpinner_->setText(QString::fromUtf8(kSpinnerFrames[0]));
if (QScreen* targetScreen = QGuiApplication::primaryScreen()) {
setGeometry(targetScreen->geometry());
}
show();
raise();
tickTimer_->start(kTickMs);
}
void TransitionOverlay::finish() {
tickTimer_->stop();
fadeTimer_->stop();
hide();
setWindowOpacity(1.0);
}
void TransitionOverlay::onFirstFrame() {
// 首帧已出画:停止动画并淡出;若尚未显示/已在淡出则忽略
if (!isVisible() || fadeTimer_->isActive()) {
return;
}
tickTimer_->stop();
fadeTimer_->start(kFadeStepMs);
}
void TransitionOverlay::onTick() {
dots_ = (dots_ + 1) % 4;
const QString base = property("baseTitle").toString();
if (!base.isEmpty()) {
labelTitle_->setText(base + QString(dots_, QLatin1Char('.')));
}
labelSpinner_->setText(QString::fromUtf8(kSpinnerFrames[dots_]));
}
void TransitionOverlay::onFadeStep() {
const double opacity = windowOpacity() - 1.0 / kFadeSteps;
if (opacity <= 0.0) {
finish();
return;
}
setWindowOpacity(opacity);
}
bool TransitionOverlay::eventFilter(QObject* watched, QEvent* event) {
if (watched == this && isVisible()) {
switch (event->type()) {
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove:
case QEvent::Wheel:
return true;
default:
break;
}
}
return QWidget::eventFilter(watched, event);
}
} // namespace vdi