Skip to content

Commit 4dbe6bc

Browse files
kixelatedclaude
andauthored
Simplify MoQ dock: full-width inputs and connection status (#42)
Make the URL/name inputs span the full dock width (set the form's field growth policy, which defaults to size-hint width on macOS). Rename the "Broadcast path" field to "Broadcast name". Drop the statistics box, which surfaced OBS-output counters rather than anything libmoq exposes. Replace it with a single connection indicator (Disconnected / Connecting / Connected) driven by the session-connect state libmoq actually reports. Use concrete stylesheet colors since Qt style sheets don't support the palette() function. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 47bead8 commit 4dbe6bc

2 files changed

Lines changed: 28 additions & 98 deletions

File tree

src/moq-dock.cpp

Lines changed: 26 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@
66
#include <util/config-file.h>
77

88
#include <QFormLayout>
9-
#include <QGridLayout>
109
#include <QVBoxLayout>
11-
#include <QGroupBox>
1210
#include <QLineEdit>
1311
#include <QPushButton>
1412
#include <QLabel>
13+
#include <QFont>
1514
#include <QTimer>
1615
#include <QDir>
1716
#include <QFileInfo>
@@ -72,27 +71,6 @@ std::string SettingsPath()
7271
return s;
7372
}
7473

75-
QString FormatDuration(int seconds)
76-
{
77-
int h = seconds / 3600;
78-
int m = (seconds % 3600) / 60;
79-
int s = seconds % 60;
80-
return QString::asprintf("%02d:%02d:%02d", h, m, s);
81-
}
82-
83-
// Add a "name: value" row to the stats grid and return the (right-aligned) value label.
84-
QLabel *AddStatRow(QGridLayout *grid, int row, const QString &name)
85-
{
86-
auto *nameLabel = new QLabel(name);
87-
nameLabel->setStyleSheet("color: palette(mid);");
88-
auto *valueLabel = new QLabel("");
89-
valueLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
90-
valueLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
91-
grid->addWidget(nameLabel, row, 0);
92-
grid->addWidget(valueLabel, row, 1);
93-
return valueLabel;
94-
}
95-
9674
} // namespace
9775

9876
MoQDock::MoQDock(QWidget *parent) : QWidget(parent)
@@ -105,48 +83,40 @@ MoQDock::MoQDock(QWidget *parent) : QWidget(parent)
10583
pathEdit->setText("obs");
10684
pathEdit->setPlaceholderText("(optional) broadcast name");
10785

108-
// Labels above the fields (WrapAllRows) so the inputs get the full width.
86+
// Labels above the fields (WrapAllRows), and let the fields grow to the full
87+
// dock width (the macOS default keeps them at their size hint otherwise).
10988
auto *form = new QFormLayout();
11089
form->setRowWrapPolicy(QFormLayout::WrapAllRows);
90+
form->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
11191
form->setContentsMargins(0, 0, 0, 0);
11292
form->addRow("Relay URL", urlEdit);
113-
form->addRow("Broadcast path", pathEdit);
93+
form->addRow("Broadcast name", pathEdit);
11494

11595
button = new QPushButton("Go Live", this);
11696
button->setCursor(Qt::PointingHandCursor);
11797
connect(button, &QPushButton::clicked, this, &MoQDock::ToggleStream);
11898

119-
status = new QLabel("Idle", this);
99+
status = new QLabel(this);
120100
status->setWordWrap(true);
121-
status->setStyleSheet("color: palette(mid);");
122-
123-
auto *statsBox = new QGroupBox("Statistics", this);
124-
auto *grid = new QGridLayout(statsBox);
125-
grid->setColumnStretch(1, 1);
126-
grid->setVerticalSpacing(4);
127-
statState = AddStatRow(grid, 0, "Status");
128-
statDuration = AddStatRow(grid, 1, "Duration");
129-
statBitrate = AddStatRow(grid, 2, "Bitrate");
130-
statSent = AddStatRow(grid, 3, "Data sent");
131-
statDropped = AddStatRow(grid, 4, "Dropped frames");
132-
statConnect = AddStatRow(grid, 5, "Connect time");
101+
QFont statusFont = status->font();
102+
statusFont.setBold(true);
103+
status->setFont(statusFont);
133104

134105
auto *versionLabel = new QLabel(QString("libmoq %1").arg(MOQ_VERSION_STRING), this);
135106
versionLabel->setAlignment(Qt::AlignRight | Qt::AlignBottom);
136-
versionLabel->setStyleSheet("color: palette(mid); font-size: 10px;");
107+
versionLabel->setStyleSheet("color: #888888; font-size: 10px;");
137108

138109
auto *layout = new QVBoxLayout(this);
139110
layout->setSpacing(10);
140111
layout->addLayout(form);
141112
layout->addWidget(button);
142113
layout->addWidget(status);
143-
layout->addWidget(statsBox);
144114
layout->addStretch();
145115
layout->addWidget(versionLabel);
146116

147-
statsTimer = new QTimer(this);
148-
statsTimer->setInterval(1000);
149-
connect(statsTimer, &QTimer::timeout, this, &MoQDock::UpdateStats);
117+
pollTimer = new QTimer(this);
118+
pollTimer->setInterval(1000);
119+
connect(pollTimer, &QTimer::timeout, this, &MoQDock::UpdateStatus);
150120

151121
connect(urlEdit, &QLineEdit::editingFinished, this, &MoQDock::SaveSettings);
152122
connect(pathEdit, &QLineEdit::editingFinished, this, &MoQDock::SaveSettings);
@@ -301,18 +271,16 @@ void MoQDock::StartStream()
301271
return;
302272
}
303273

304-
lastBytes = 0;
305-
lastSample = std::chrono::steady_clock::now();
306-
streamStart = lastSample;
307-
statsTimer->start();
274+
pollTimer->start();
308275

309276
SetRunning(true);
310-
status->setText("Connecting…");
277+
status->setText("● Connecting…");
278+
status->setStyleSheet("color: #d08b1d;");
311279
}
312280

313281
void MoQDock::StopStream()
314282
{
315-
statsTimer->stop();
283+
pollTimer->stop();
316284

317285
if (output) {
318286
signal_handler_disconnect(obs_output_get_signal_handler(output), "stop", OnOutputStopped, this);
@@ -342,48 +310,22 @@ void MoQDock::SetRunning(bool isRunning)
342310
pathEdit->setEnabled(!isRunning);
343311

344312
if (!isRunning) {
345-
status->setText("Idle");
346-
statState->setText("Offline");
347-
statState->setStyleSheet("color: palette(mid);");
348-
statDuration->setText("");
349-
statBitrate->setText("");
350-
statSent->setText("");
351-
statDropped->setText("");
352-
statConnect->setText("");
313+
status->setText("● Disconnected");
314+
status->setStyleSheet("color: #888888;");
353315
}
354316
}
355317

356-
void MoQDock::UpdateStats()
318+
void MoQDock::UpdateStatus()
357319
{
358320
if (!output || !running)
359321
return;
360322

361-
const auto now = std::chrono::steady_clock::now();
362-
const uint64_t bytes = obs_output_get_total_bytes(output);
363-
const double secs = std::chrono::duration<double>(now - lastSample).count();
364-
const double kbps = secs > 0.0 ? (double)(bytes - lastBytes) * 8.0 / 1000.0 / secs : 0.0;
365-
lastBytes = bytes;
366-
lastSample = now;
367-
368-
const bool connected = obs_output_active(output) && bytes > 0;
369-
statState->setText(connected ? "● Live" : "Connecting…");
370-
statState->setStyleSheet(connected ? "color: #36a45e; font-weight: bold;" : "color: palette(mid);");
371-
372-
const int liveSecs = (int)std::chrono::duration_cast<std::chrono::seconds>(now - streamStart).count();
373-
statDuration->setText(FormatDuration(liveSecs));
374-
statBitrate->setText(QString("%1 kb/s").arg((int)(kbps + 0.5)));
375-
statSent->setText(QString("%1 MB").arg((double)bytes / (1024.0 * 1024.0), 0, 'f', 1));
376-
377-
const int total = obs_output_get_total_frames(output);
378-
const int dropped = obs_output_get_frames_dropped(output);
379-
const double dropPct = total > 0 ? (double)dropped * 100.0 / (double)total : 0.0;
380-
statDropped->setText(QString("%1 (%2%)").arg(dropped).arg(dropPct, 0, 'f', 1));
381-
382-
const int connectMs = obs_output_get_connect_time_ms(output);
383-
statConnect->setText(connectMs > 0 ? QString("%1 ms").arg(connectMs) : "");
384-
385-
if (connected)
386-
status->setText("Streaming");
323+
// libmoq surfaces connection state via the session-connect callback, which
324+
// MoQOutput records as the output's connect time; until that fires we're
325+
// still connecting. There's no per-frame stats API to show beyond this.
326+
const bool connected = obs_output_get_connect_time_ms(output) > 0;
327+
status->setText(connected ? "● Connected" : "● Connecting…");
328+
status->setStyleSheet(connected ? "color: #36a45e;" : "color: #d08b1d;");
387329
}
388330

389331
void MoQDock::LoadSettings()

src/moq-dock.h

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
#include <QWidget>
44
#include <obs.hpp>
55

6-
#include <chrono>
7-
86
class QLineEdit;
97
class QPushButton;
108
class QLabel;
@@ -23,7 +21,7 @@ class MoQDock : public QWidget {
2321

2422
private slots:
2523
void ToggleStream();
26-
void UpdateStats();
24+
void UpdateStatus();
2725

2826
private:
2927
void StartStream();
@@ -43,24 +41,14 @@ private slots:
4341
QPushButton *button;
4442
QLabel *status;
4543

46-
QLabel *statState;
47-
QLabel *statDuration;
48-
QLabel *statBitrate;
49-
QLabel *statSent;
50-
QLabel *statDropped;
51-
QLabel *statConnect;
52-
53-
QTimer *statsTimer;
44+
QTimer *pollTimer;
5445

5546
OBSServiceAutoRelease service;
5647
OBSOutputAutoRelease output;
5748
OBSEncoderAutoRelease videoEncoder;
5849
OBSEncoderAutoRelease audioEncoder;
5950

6051
bool running = false;
61-
uint64_t lastBytes = 0;
62-
std::chrono::steady_clock::time_point lastSample;
63-
std::chrono::steady_clock::time_point streamStart;
6452
};
6553

6654
void register_moq_dock();

0 commit comments

Comments
 (0)