新版布局
This commit is contained in:
+60
-18
@@ -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); }
|
||||
|
||||
Reference in New Issue
Block a user