diff --git a/tools/chatgui/src/mainwindow.cpp b/tools/chatgui/src/mainwindow.cpp index 5d2a49a9..8797692e 100644 --- a/tools/chatgui/src/mainwindow.cpp +++ b/tools/chatgui/src/mainwindow.cpp @@ -338,7 +338,7 @@ void MainWindow::showJoinDialog() { } void MainWindow::showSettings() { - SettingsDialog dlg(m_cfgPath, m_db, this); + SettingsDialog dlg(m_cfgPath, m_db, m_node, this); dlg.exec(); } diff --git a/tools/chatgui/src/settingsdialog.cpp b/tools/chatgui/src/settingsdialog.cpp index 8ec92d49..37f07da9 100644 --- a/tools/chatgui/src/settingsdialog.cpp +++ b/tools/chatgui/src/settingsdialog.cpp @@ -8,6 +8,7 @@ #include "../transport/gui_bridge.h" #include "../../lib/mem.h" #include "../db/db_manager.h" +#include "../transport/utun_node.h" extern "C" { #include "../transport/chat_core.h" } @@ -20,7 +21,7 @@ extern "C" { #include #include -SettingsDialog::SettingsDialog(const QString& configPath, DbManager* db, QWidget* parent) +SettingsDialog::SettingsDialog(const QString& configPath, DbManager* db, UtunNode* node, QWidget* parent) : QDialog(parent) , m_configPath(configPath) { @@ -79,7 +80,7 @@ SettingsDialog::SettingsDialog(const QString& configPath, DbManager* db, QWidget m_pages->addWidget(m_databasePage); // Status page - m_statusPage = new StatusPage(m_pages); + m_statusPage = new StatusPage(node, m_pages); m_pages->addWidget(m_statusPage); // Nodes page diff --git a/tools/chatgui/src/settingsdialog.h b/tools/chatgui/src/settingsdialog.h index af3caade..ead5c963 100644 --- a/tools/chatgui/src/settingsdialog.h +++ b/tools/chatgui/src/settingsdialog.h @@ -14,11 +14,12 @@ class StatusPage; class NodesPage; class SoundSettingsPage; class DbManager; +class UtunNode; class SettingsDialog : public QDialog { Q_OBJECT public: - explicit SettingsDialog(const QString& configPath, DbManager* db, QWidget* parent = nullptr); + explicit SettingsDialog(const QString& configPath, DbManager* db, UtunNode* node, QWidget* parent = nullptr); private slots: void onCategoryChanged(int row); diff --git a/tools/chatgui/src/statuspage.cpp b/tools/chatgui/src/statuspage.cpp index 19892fd3..c0449884 100644 --- a/tools/chatgui/src/statuspage.cpp +++ b/tools/chatgui/src/statuspage.cpp @@ -1,5 +1,6 @@ // statuspage.cpp — Status page (NTP + connections) in settings #include "statuspage.h" +#include "../transport/utun_node.h" extern "C" { #include "../transport/gui_bridge.h" @@ -10,6 +11,8 @@ extern "C" { #include #include #include +#include +#include static StatusPage* g_activeStatusPage = nullptr; @@ -18,13 +21,35 @@ static void statusRefreshCallback(const char* text, int len) { g_activeStatusPage->setText(QString::fromUtf8(text, len)); } -StatusPage::StatusPage(QWidget* parent) +StatusPage::StatusPage(UtunNode* node, QWidget* parent) : QWidget(parent) + , m_node(node) { auto* layout = new QVBoxLayout(this); layout->setSpacing(8); layout->setContentsMargins(0, 0, 0, 0); + /* ── clock block ── */ + m_clockFrame = new QFrame(this); + m_clockFrame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + m_clockFrame->setStyleSheet( + "QFrame { border: 1px solid palette(mid); border-radius: 6px;" + " padding: 10px; background: palette(alternate-base); }"); + + auto* clockLayout = new QVBoxLayout(m_clockFrame); + clockLayout->setContentsMargins(12, 8, 12, 8); + + m_clockLabel = new QLabel("--:--:--", m_clockFrame); + QFont clockFont("monospace", 22); + clockFont.setBold(true); + m_clockLabel->setFont(clockFont); + m_clockLabel->setAlignment(Qt::AlignCenter); + m_clockLabel->setStyleSheet("border: none; color: palette(text);"); + clockLayout->addWidget(m_clockLabel); + + layout->addWidget(m_clockFrame); + + /* ── System Status section ── */ auto* title = new QLabel("System Status", this); title->setStyleSheet("font-weight: bold; font-size: 13px;"); layout->addWidget(title); @@ -50,15 +75,43 @@ StatusPage::StatusPage(QWidget* parent) btnLayout->addWidget(m_refreshBtn); layout->addLayout(btnLayout); + m_clockTimer = new QTimer(this); + m_clockTimer->setSingleShot(true); + m_clockTimer->setTimerType(Qt::PreciseTimer); + connect(m_clockTimer, &QTimer::timeout, this, &StatusPage::updateClock); + g_activeStatusPage = this; gui_bridge_set_status_refresh_cb(statusRefreshCallback); } StatusPage::~StatusPage() { + m_clockTimer->stop(); if (g_activeStatusPage == this) g_activeStatusPage = nullptr; } +void StatusPage::updateClock() { + if (!m_node) return; + + qint64 nowMs = QDateTime::currentMSecsSinceEpoch(); + int64_t offsetUs = m_node->ntpOffsetUs(); + bool synced = m_node->ntpSynced(); + + qint64 correctedMs = nowMs - (offsetUs / 1000); + QDateTime corrected = QDateTime::fromMSecsSinceEpoch(correctedMs).toLocalTime(); + + QString t = corrected.toString("HH:mm:ss"); + + if (synced) + m_clockLabel->setText(QString("%1 [NTP ✓]").arg(t)); + else + m_clockLabel->setText(QString("%1 [LOCAL]").arg(t)); + + int msToNext = 1000 - (corrected.time().msec()); + m_clockTimer->start(msToNext); +} + void StatusPage::refreshStatus() { + updateClock(); m_textEdit->setPlainText("Loading..."); gui_bridge_post_uasync_fn(chat_core_collect_status_trampoline, NULL); } diff --git a/tools/chatgui/src/statuspage.h b/tools/chatgui/src/statuspage.h index 2572336c..be1c157b 100644 --- a/tools/chatgui/src/statuspage.h +++ b/tools/chatgui/src/statuspage.h @@ -1,14 +1,19 @@ -// statuspage.h — Status page (NTP + connections) in settings +// statuspage.h — Status page (NTP + connections + live clock) in settings #pragma once #include #include #include +#include +#include +#include + +class UtunNode; class StatusPage : public QWidget { Q_OBJECT public: - explicit StatusPage(QWidget* parent = nullptr); + explicit StatusPage(UtunNode* node, QWidget* parent = nullptr); ~StatusPage() override; void refreshStatus(); void setText(const QString& text) { m_textEdit->setPlainText(text); } @@ -17,6 +22,13 @@ private slots: void onRefreshClicked(); private: + void updateClock(); + QPlainTextEdit* m_textEdit; QPushButton* m_refreshBtn; + QLabel* m_clockLabel; + QFrame* m_clockFrame; + QTimer* m_clockTimer; + UtunNode* m_node; }; + diff --git a/tools/chatgui/transport/utun_node.cpp b/tools/chatgui/transport/utun_node.cpp index 9b4a7bae..1ce4fa95 100644 --- a/tools/chatgui/transport/utun_node.cpp +++ b/tools/chatgui/transport/utun_node.cpp @@ -103,6 +103,18 @@ QString UtunNode::pubKeyHex() const { return QString::fromLatin1(m_instance->config->global.my_public_key_hex, 64); } +int64_t UtunNode::ntpOffsetUs() const { + return m_instance ? m_instance->ntp.offset_us : 0; +} + +bool UtunNode::ntpSynced() const { + return m_instance ? m_instance->ntp.synced : false; +} + +bool UtunNode::ntpEnabled() const { + return m_instance ? m_instance->ntp.enabled : false; +} + QList UtunNode::getInviteAddresses(DbManager* db) { (void)db; QList addrs; diff --git a/tools/chatgui/transport/utun_node.h b/tools/chatgui/transport/utun_node.h index 5823a93a..28ab6b1a 100644 --- a/tools/chatgui/transport/utun_node.h +++ b/tools/chatgui/transport/utun_node.h @@ -7,6 +7,7 @@ #include #include #include +#include #include struct UTUN_INSTANCE; @@ -33,6 +34,9 @@ public: QString nodeIdHex() const; QString pubKeyHex() const; QList getInviteAddresses(DbManager* db); + int64_t ntpOffsetUs() const; + bool ntpSynced() const; + bool ntpEnabled() const; signals: void started();