147 lines
4.5 KiB
C++
147 lines
4.5 KiB
C++
#include "transition_overlay.h"
|
|
|
|
#include <QEvent>
|
|
#include <QFont>
|
|
#include <QGuiApplication>
|
|
#include <QLabel>
|
|
#include <QPalette>
|
|
#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
|
|
#ifdef Q_OS_UNIX
|
|
| Qt::X11BypassWindowManagerHint
|
|
#endif
|
|
);
|
|
setAttribute(Qt::WA_ShowWithoutActivating);
|
|
// A plain QWidget subclass needs an explicit opaque palette; a type-name
|
|
// stylesheet alone may leave the native window using its default white.
|
|
setAutoFillBackground(true);
|
|
QPalette background = palette();
|
|
background.setColor(QPalette::Window, QColor(QStringLiteral("#0f1420")));
|
|
setPalette(background);
|
|
|
|
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() {
|
|
// The exclusive X11 kiosk has no compositor to apply window opacity.
|
|
// Once the full-screen stream is ready, unmap the opaque cover directly.
|
|
#ifdef Q_OS_UNIX
|
|
if (QGuiApplication::platformName() == QStringLiteral("xcb")) {
|
|
finish();
|
|
return;
|
|
}
|
|
#endif
|
|
// 首帧已出画:停止动画并淡出;若尚未显示/已在淡出则忽略
|
|
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
|