geopro/src/app/ToastOverlay.cpp

108 lines
3.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ToastOverlay.hpp"
#include <QEvent>
#include <QHBoxLayout>
#include <QLabel>
#include <QMouseEvent>
#include <QTimer>
#include "Theme.hpp"
namespace geopro::app {
namespace {
// ── 自动消失时长(规范 §7.734s──
constexpr int kDismissMs = 3500;
// 距窗口内容区底部的留白(引用间距令牌 xxl
constexpr int kBottomMargin = space::kXxl;
// 卡片最大宽度,避免长文案铺满整窗。
constexpr int kMaxWidth = 480;
// 左侧状态色竖条宽度(规范 §7.7状态色左竖条3px
constexpr int kBarWidth = 3;
} // namespace
ToastOverlay::ToastOverlay(QWidget* parent) : QWidget(parent) {
setObjectName(QStringLiteral("toastCard"));
setAttribute(Qt::WA_StyledBackground, true); // 令 QSS 背景在 QWidget 上生效
setCursor(Qt::PointingHandCursor);
hide();
// 卡片样式bg/panel 底 + 1px border/default 边框 + radius/md 圆角;左侧 3px status/info 状态竖条
// 以「左 border-left」近似阴影无法用 QSS 绘制,故以边框 + 半透明底近似浮层高度。
applyTokenizedStyleSheet(
this, QStringLiteral(
"#toastCard { background:{{bg/panel}}; border:1px solid {{border/default}};"
" border-left:%1px solid {{status/info}}; border-radius:%2px; }"
"#toastLabel { color:{{text/primary}}; font-size:%3px; background:transparent;"
" border:none; }")
.arg(kBarWidth)
.arg(radius::kMd)
.arg(scaledPx(type::kBody)));
auto* lay = new QHBoxLayout(this);
// 左内边距补上状态竖条宽度,文案与竖条留出间隙。
lay->setContentsMargins(space::kLg + kBarWidth, space::kMd, space::kLg, space::kMd);
lay->setSpacing(space::kMd);
label_ = new QLabel(this);
label_->setObjectName(QStringLiteral("toastLabel"));
label_->setWordWrap(true);
label_->setMaximumWidth(kMaxWidth);
lay->addWidget(label_);
// 单次计时器:到时隐藏(复用实例不销毁,规避悬垂指针)。
timer_ = new QTimer(this);
timer_->setSingleShot(true);
QObject::connect(timer_, &QTimer::timeout, this, [this] { hide(); });
// 监听父窗口尺寸/移动,跟随重定位。
if (parent) parent->installEventFilter(this);
}
void ToastOverlay::showMessage(const QString& msg) {
if (!label_) return;
label_->setText(msg);
adjustSize();
reposition();
raise();
show();
timer_->start(kDismissMs); // 重置自动消失计时
}
void ToastOverlay::reposition() {
auto* p = parentWidget();
if (!p) return;
const int x = (p->width() - width()) / 2;
const int y = p->height() - height() - kBottomMargin;
move(qMax(0, x), qMax(0, y));
}
bool ToastOverlay::eventFilter(QObject* obj, QEvent* event) {
if (obj == parentWidget() &&
(event->type() == QEvent::Resize || event->type() == QEvent::Move)) {
if (isVisible()) reposition();
}
return QWidget::eventFilter(obj, event);
}
void ToastOverlay::mousePressEvent(QMouseEvent* event) {
hide(); // 点击立即关闭
timer_->stop();
QWidget::mousePressEvent(event);
}
void showToast(QWidget* anchorWindow, const QString& msg) {
if (!anchorWindow) return;
QWidget* win = anchorWindow->window(); // 取顶层窗口作为锚
if (!win) return;
// 同一窗口复用一个 overlay首次创建并以窗口为父后续按 objectName 找回直接替换文案。
auto* overlay = win->findChild<ToastOverlay*>(QStringLiteral("toastCard"));
if (!overlay) overlay = new ToastOverlay(win);
overlay->showMessage(msg);
}
} // namespace geopro::app