新版布局

This commit is contained in:
xu
2026-10-16 09:06:03 +08:00
parent e5efdea06e
commit 1ec93e42cb
37 changed files with 1785 additions and 977 deletions
+60 -18
View File
@@ -8,33 +8,62 @@
#include <QMouseEvent>
#include <QGraphicsDropShadowEffect>
// 启动 500ms 闪烁:每 500ms 切换一次动态属性,QSS 据此在 40%↔100% 透明度间切换
QTimer* startBlink(QWidget* w, const char* prop)
// ===================== 全局闪烁节拍 =====================
BlinkBeat::BlinkBeat(QObject* parent)
: QObject(parent)
{
// 复用已存在的定时器
if (auto* t = w->findChild<QTimer*>(QString("blink_") + prop))
return t;
QTimer* t = new QTimer(w);
t->setObjectName(QString("blink_") + prop);
t->setInterval(500);
QObject::connect(t, &QTimer::timeout, w, [w, prop]() {
bool cur = w->property(prop).toBool();
w->setProperty(prop, !cur);
m_timer = new QTimer(this);
m_timer->setInterval(500); // 闪烁半周期:亮 500ms、灭 500ms
connect(m_timer, &QTimer::timeout, this, [this]() {
m_on = !m_on;
emit toggled(m_on);
});
m_timer->start();
}
BlinkBeat* BlinkBeat::instance()
{
// 首次用到才建(此时 QApplication 已就绪),进程结束自然销毁。
// 不挂在某个控件下:它要跨页面、跨控件地给所有闪烁源提供同一个相位。
static BlinkBeat beat;
return &beat;
}
namespace {
// 订阅句柄挂在被闪的控件下、名字固定,stopBlink 靠它找回并删掉。
// QObject 析构时会自动断开它身上所有的连接,所以“删除句柄”就等于“退订”。
QString blinkKey(const char* prop) { return QString("blinkconn_") + prop; }
}
// 挂到全局节拍上:每拍翻转一次动态属性,QSS 据此在 40%↔100% 透明度间切换。
// 用公共节拍而不是各起一个定时器——各自起的话相位各异,同屏几处会各闪各的。
void startBlink(QWidget* w, const char* prop)
{
if (w->findChild<QObject*>(blinkKey(prop)))
return; // 已订阅
QObject* holder = new QObject(w);
holder->setObjectName(blinkKey(prop));
// 以 holder 作接收者:它随控件一起销毁,连接也就一并断开
QObject::connect(BlinkBeat::instance(), &BlinkBeat::toggled, holder, [w, prop](bool on) {
w->setProperty(prop, on);
w->style()->unpolish(w);
w->style()->polish(w);
w->update();
});
w->setProperty(prop, true);
t->start();
return t;
// 立刻按当前相位显示,不然要等到下一拍
w->setProperty(prop, BlinkBeat::instance()->on());
w->style()->unpolish(w);
w->style()->polish(w);
w->update();
}
void stopBlink(QWidget* w, const char* prop)
{
if (auto* t = w->findChild<QTimer*>(QString("blink_") + prop)) {
t->stop();
delete t;
}
if (QObject* holder = w->findChild<QObject*>(blinkKey(prop)))
delete holder; // 删除句柄 = 退订
w->setProperty(prop, false);
w->style()->unpolish(w);
w->style()->polish(w);
@@ -101,6 +130,13 @@ bool ButtonFocusGlow::eventFilter(QObject* obj, QEvent* ev)
parent->update(parent->rect());
}
break;
case QEvent::EnabledChange:
// 光标一直停在按钮上时,禁用期间 Qt 仍给它留着 WA_UnderMouse;重新启用的一瞬间
// :hover 规则就生效了(:disabled 排在 :hover 之后才压得住它),看着像"被选中"。
// 这里把这层残影清掉,和 FocusIn 那条是同一件事:保证同时只有一个按钮呈选中样式。
if (w->isEnabled() && w->underMouse())
clearStaleHover(QApplication::focusWidget());
break;
case QEvent::MouseButtonPress:
// 触摸/点击与 Tab 归一到同一个焦点:谁最后操作,谁就是唯一的选中
if (static_cast<QMouseEvent*>(ev)->button() == Qt::LeftButton
@@ -264,6 +300,12 @@ QPushButton[numpad="true"]:focus {
border: 2px solid #8ABFFF;
}
/* ===== 数据卡片数值 ===== */
/* 数值标签的颜色必须由这里给:卡片的行内样式只写字号/字重,不写 color。
行内样式优先级高于应用级样式表,写上 color 就会把下面的告警红 / 暗红闪一起盖掉。
本规则与 [alert] 同为单属性选择器,靠位置分胜负,故必须排在告警规则之前。 */
QLabel[cardValue="true"] { color: TXT; }
/* ===== 告警文字(红色闪烁由 blink 属性驱动) ===== */
QLabel[alert="true"] { color: DAN; font-weight: 700; }
QLabel[alert="true"][blink="false"] { color: rgba(229,72,77,102); }