| 100 | } |
| 101 | |
| 102 | TitleBar::TitleBar(QWidget* parent) |
| 103 | : QWidget(parent) |
| 104 | { |
| 105 | AetherSDR::theme::setContainer(this, QStringLiteral("titlebar")); |
| 106 | setFixedHeight(32); |
| 107 | AetherSDR::ThemeManager::instance().applyStyleSheet(this, "TitleBar { background: {{color.background.0}}; border-bottom: 1px solid {{color.background.1}}; }"); |
| 108 | |
| 109 | m_hbox = new QHBoxLayout(this); |
| 110 | m_hbox->setContentsMargins(4, 2, 8, 2); |
| 111 | m_hbox->setSpacing(6); |
| 112 | |
| 113 | auto makeDragGutter = [this]() { |
| 114 | auto* gutter = new QWidget(this); |
| 115 | gutter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); |
| 116 | gutter->setMinimumWidth(0); |
| 117 | markDragHandle(gutter); |
| 118 | return gutter; |
| 119 | }; |
| 120 | |
| 121 | // Keep the identity/status cluster anchored on the left, immediately after |
| 122 | // the menu bar once it is inserted via setMenuBar(). |
| 123 | |
| 124 | // ── Heartbeat indicator ───────────────────────────────────────────────── |
| 125 | m_heartbeat = new QLabel; |
| 126 | m_heartbeat->setFixedSize(10, 10); |
| 127 | AetherSDR::ThemeManager::instance().applyStyleSheet(m_heartbeat, "QLabel { background: {{color.background.2}}; border-radius: 5px; }"); |
| 128 | m_heartbeat->setToolTip("Radio discovery heartbeat"); |
| 129 | m_heartbeat->setAccessibleName("Radio heartbeat"); |
| 130 | m_heartbeat->setAccessibleDescription("Flashes green when radio discovery packets are received"); |
| 131 | markDragHandle(m_heartbeat); |
| 132 | |
| 133 | // 100ms timer to return green flash back to grey |
| 134 | m_heartbeatOffTimer = new QTimer(this); |
| 135 | m_heartbeatOffTimer->setSingleShot(true); |
| 136 | m_heartbeatOffTimer->setInterval(100); |
| 137 | connect(m_heartbeatOffTimer, &QTimer::timeout, this, [this]() { |
| 138 | AetherSDR::ThemeManager::instance().applyStyleSheet(m_heartbeat, "QLabel { background: {{color.background.2}}; border-radius: 5px; }"); |
| 139 | }); |
| 140 | |
| 141 | // 500ms alarm blink timer (red/grey alternating) |
| 142 | m_heartbeatAlarmTimer = new QTimer(this); |
| 143 | m_heartbeatAlarmTimer->setInterval(500); |
| 144 | connect(m_heartbeatAlarmTimer, &QTimer::timeout, this, [this]() { |
| 145 | m_alarmRed = !m_alarmRed; |
| 146 | m_heartbeat->setStyleSheet(m_alarmRed |
| 147 | ? "QLabel { background: #cc2020; border-radius: 5px; }" |
| 148 | : "QLabel { background: #404858; border-radius: 5px; }"); |
| 149 | }); |
| 150 | |
| 151 | // Load persisted blink preference (default: enabled) |
| 152 | m_blinkEnabled = AppSettings::instance() |
| 153 | .value("HeartbeatBlinkEnabled", "True").toString() == "True"; |
| 154 | |
| 155 | // Right-click on the indicator to toggle blink on/off without opening a menu |
| 156 | m_heartbeat->setContextMenuPolicy(Qt::CustomContextMenu); |
| 157 | connect(m_heartbeat, &QWidget::customContextMenuRequested, |
| 158 | this, [this](const QPoint& pos) { |
| 159 | // Heap-allocate with WA_DeleteOnClose so the menu outlives this lambda. |
nothing calls this directly
no test coverage detected