- Tags haben jetzt einen Verlauf
- Suchfunktion hat jetzt weiter und zurück - Tags mit Bindestrich werden erkannt
This commit is contained in:
@@ -9,9 +9,11 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
// RawView shows the raw UART output with timestamps, search, tag suppression,
|
// RawView shows the raw UART output with timestamps, search (with next/
|
||||||
// unlimited history, H+V scrolling and a copy-to-clipboard button.
|
// previous navigation and wrap-around), tag suppression, unlimited history,
|
||||||
|
// H+V scrolling and a copy-to-clipboard button.
|
||||||
// Auto-scroll is ON by default.
|
// Auto-scroll is ON by default.
|
||||||
class RawView : public QWidget
|
class RawView : public QWidget
|
||||||
{
|
{
|
||||||
@@ -29,13 +31,22 @@ private slots:
|
|||||||
void onScrollValueChanged(int value);
|
void onScrollValueChanged(int value);
|
||||||
void onAutoScrollToggled(bool enabled);
|
void onAutoScrollToggled(bool enabled);
|
||||||
void copyToClipboard();
|
void copyToClipboard();
|
||||||
|
void findNext();
|
||||||
|
void findPrevious();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUi();
|
void setupUi();
|
||||||
void applyColorScheme();
|
void applyColorScheme();
|
||||||
|
// Searches for `text` starting at the current cursor, honouring `flags`
|
||||||
|
// (e.g. QTextDocument::FindBackward). If nothing is found and
|
||||||
|
// `wrapAllowed` is true, retries once from the start/end of the
|
||||||
|
// document so search wraps around instead of dead-ending.
|
||||||
|
bool searchAndSelect(const QString &text, QTextDocument::FindFlags flags, bool wrapAllowed);
|
||||||
|
|
||||||
QPlainTextEdit *m_textEdit = nullptr;
|
QPlainTextEdit *m_textEdit = nullptr;
|
||||||
QLineEdit *m_searchEdit = nullptr;
|
QLineEdit *m_searchEdit = nullptr;
|
||||||
|
QPushButton *m_findPrevBtn = nullptr;
|
||||||
|
QPushButton *m_findNextBtn = nullptr;
|
||||||
QPushButton *m_clearBtn = nullptr;
|
QPushButton *m_clearBtn = nullptr;
|
||||||
QPushButton *m_copyBtn = nullptr;
|
QPushButton *m_copyBtn = nullptr;
|
||||||
QCheckBox *m_autoScrollCb = nullptr;
|
QCheckBox *m_autoScrollCb = nullptr;
|
||||||
|
|||||||
@@ -5,17 +5,24 @@
|
|||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QTableWidget>
|
#include <QTableWidget>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QTabWidget>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
|
||||||
// TagPanel displays the most recent data for a single tag (e.g. [WDG]).
|
// TagPanel displays the data received for a single tag (e.g. [WDG]) in two
|
||||||
// Key=value pairs are shown in a table that updates in-place.
|
// tabs:
|
||||||
// If a value contains ';', it is treated as a LIST: the key is shown once
|
// - "Aktuell": the most recent value(s), updating in-place (a table for
|
||||||
// and each list element gets its own row below it (key cell left empty
|
// key=value pairs, or a plain label for raw/free-form content). If a
|
||||||
// for the continuation rows, like a merged-cell look).
|
// value contains ';', it is treated as a LIST: the key is shown once
|
||||||
// Example: Devs=trackdisk.device;input.device
|
// and each list element gets its own row below it (key cell left empty
|
||||||
// Devs trackdisk.device
|
// for the continuation rows, like a merged-cell look).
|
||||||
// input.device
|
// Example: Devs=trackdisk.device;input.device
|
||||||
|
// Devs trackdisk.device
|
||||||
|
// input.device
|
||||||
|
// - "Verlauf": every value ever received for this tag, timestamped, in
|
||||||
|
// receive order - so you can see the full runtime history rather than
|
||||||
|
// just the latest snapshot.
|
||||||
class TagPanel : public QGroupBox
|
class TagPanel : public QGroupBox
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -30,16 +37,26 @@ public slots:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void copyToClipboard();
|
void copyToClipboard();
|
||||||
|
void clearHistory();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void parseKeyValue(const QString &value);
|
void parseKeyValue(const QString &value);
|
||||||
void showRaw(const QString &value);
|
void showRaw(const QString &value);
|
||||||
// Replaces all rows belonging to `key` with one row per value in `values`.
|
// Replaces all rows belonging to `key` with one row per value in `values`.
|
||||||
void setKeyValues(const QString &key, const QStringList &values);
|
void setKeyValues(const QString &key, const QStringList &values);
|
||||||
|
// Appends a timestamped entry for `value` to the "Verlauf" tab.
|
||||||
|
void appendHistory(const QString &value);
|
||||||
|
|
||||||
QString m_tag;
|
QString m_tag;
|
||||||
QTableWidget *m_table = nullptr;
|
QTabWidget *m_tabs = nullptr;
|
||||||
QLabel *m_rawLabel = nullptr;
|
QTableWidget *m_table = nullptr;
|
||||||
|
QLabel *m_rawLabel = nullptr;
|
||||||
|
QPlainTextEdit *m_historyEdit = nullptr;
|
||||||
|
int m_historyCount = 0;
|
||||||
|
|
||||||
|
// Cap history growth for very chatty/long-running tags so memory use
|
||||||
|
// stays bounded; oldest entries are dropped first (FIFO).
|
||||||
|
static constexpr int kMaxHistoryEntries = 5000;
|
||||||
|
|
||||||
// Track which table rows belong to which key, so repeated updates can
|
// Track which table rows belong to which key, so repeated updates can
|
||||||
// replace the previous rows instead of appending duplicates.
|
// replace the previous rows instead of appending duplicates.
|
||||||
|
|||||||
@@ -7,9 +7,17 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QShortcut>
|
||||||
|
#include <QKeySequence>
|
||||||
|
|
||||||
|
// Tag names may contain letters, digits, underscore AND hyphen (e.g.
|
||||||
|
// [I2C-BUS]) - keep this in sync with the tagRe in serialworker.cpp, which
|
||||||
|
// is what actually decides whether a tag is detected/emitted in the first
|
||||||
|
// place. If the two regexes disagree, hyphenated tags can end up detected
|
||||||
|
// by one but not recognised as "having a tag" by the other (breaking either
|
||||||
|
// cyan highlighting or the tag filter).
|
||||||
const QRegularExpression RawView::s_tagRe(
|
const QRegularExpression RawView::s_tagRe(
|
||||||
R"(\[([A-Z][A-Z0-9_]*)\])", QRegularExpression::CaseInsensitiveOption);
|
R"(\[([A-Z][A-Z0-9_-]*)\])", QRegularExpression::CaseInsensitiveOption);
|
||||||
|
|
||||||
RawView::RawView(QWidget *parent)
|
RawView::RawView(QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
@@ -30,6 +38,22 @@ void RawView::setupUi()
|
|||||||
m_searchEdit->setPlaceholderText(tr("Search…"));
|
m_searchEdit->setPlaceholderText(tr("Search…"));
|
||||||
m_searchEdit->setClearButtonEnabled(true);
|
m_searchEdit->setClearButtonEnabled(true);
|
||||||
connect(m_searchEdit, &QLineEdit::textChanged, this, &RawView::onSearch);
|
connect(m_searchEdit, &QLineEdit::textChanged, this, &RawView::onSearch);
|
||||||
|
connect(m_searchEdit, &QLineEdit::returnPressed, this, &RawView::findNext);
|
||||||
|
|
||||||
|
m_findPrevBtn = new QPushButton(tr("▲"), this);
|
||||||
|
m_findPrevBtn->setMaximumWidth(28);
|
||||||
|
m_findPrevBtn->setToolTip(tr("Previous match (Shift+F3)"));
|
||||||
|
connect(m_findPrevBtn, &QPushButton::clicked, this, &RawView::findPrevious);
|
||||||
|
|
||||||
|
m_findNextBtn = new QPushButton(tr("▼"), this);
|
||||||
|
m_findNextBtn->setMaximumWidth(28);
|
||||||
|
m_findNextBtn->setToolTip(tr("Next match (F3 / Enter)"));
|
||||||
|
connect(m_findNextBtn, &QPushButton::clicked, this, &RawView::findNext);
|
||||||
|
|
||||||
|
auto *nextShortcut = new QShortcut(QKeySequence(Qt::Key_F3), this);
|
||||||
|
connect(nextShortcut, &QShortcut::activated, this, &RawView::findNext);
|
||||||
|
auto *prevShortcut = new QShortcut(QKeySequence(Qt::SHIFT | Qt::Key_F3), this);
|
||||||
|
connect(prevShortcut, &QShortcut::activated, this, &RawView::findPrevious);
|
||||||
|
|
||||||
m_autoScrollCb = new QCheckBox(tr("Auto-scroll"), this);
|
m_autoScrollCb = new QCheckBox(tr("Auto-scroll"), this);
|
||||||
m_autoScrollCb->setChecked(true); // default ON
|
m_autoScrollCb->setChecked(true); // default ON
|
||||||
@@ -47,6 +71,8 @@ void RawView::setupUi()
|
|||||||
|
|
||||||
toolbar->addWidget(new QLabel(tr("Search:"), this));
|
toolbar->addWidget(new QLabel(tr("Search:"), this));
|
||||||
toolbar->addWidget(m_searchEdit, 1);
|
toolbar->addWidget(m_searchEdit, 1);
|
||||||
|
toolbar->addWidget(m_findPrevBtn);
|
||||||
|
toolbar->addWidget(m_findNextBtn);
|
||||||
toolbar->addWidget(m_autoScrollCb);
|
toolbar->addWidget(m_autoScrollCb);
|
||||||
toolbar->addWidget(m_lineCountLbl);
|
toolbar->addWidget(m_lineCountLbl);
|
||||||
toolbar->addWidget(m_copyBtn);
|
toolbar->addWidget(m_copyBtn);
|
||||||
@@ -122,18 +148,64 @@ void RawView::copyToClipboard()
|
|||||||
QApplication::clipboard()->setText(m_textEdit->toPlainText());
|
QApplication::clipboard()->setText(m_textEdit->toPlainText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RawView::searchAndSelect(const QString &text, QTextDocument::FindFlags flags, bool wrapAllowed)
|
||||||
|
{
|
||||||
|
if (text.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QTextDocument *doc = m_textEdit->document();
|
||||||
|
QTextCursor cursor = doc->find(text, m_textEdit->textCursor(), flags);
|
||||||
|
|
||||||
|
if (cursor.isNull() && wrapAllowed) {
|
||||||
|
// No further match in the search direction from here - wrap around
|
||||||
|
// to the start (or end, for backward search) and try once more so
|
||||||
|
// the user can keep pressing "next"/"previous" in a loop.
|
||||||
|
QTextCursor wrapCursor(doc);
|
||||||
|
if (flags.testFlag(QTextDocument::FindBackward))
|
||||||
|
wrapCursor.movePosition(QTextCursor::End);
|
||||||
|
cursor = doc->find(text, wrapCursor, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor.isNull())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_textEdit->setTextCursor(cursor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void RawView::onSearch(const QString &text)
|
void RawView::onSearch(const QString &text)
|
||||||
{
|
{
|
||||||
QTextDocument *doc = m_textEdit->document();
|
if (text.isEmpty()) {
|
||||||
QTextCursor cursor = doc->find(text);
|
|
||||||
if (!cursor.isNull()) {
|
|
||||||
m_textEdit->setTextCursor(cursor);
|
|
||||||
m_searchEdit->setStyleSheet(QString());
|
|
||||||
} else if (!text.isEmpty()) {
|
|
||||||
m_searchEdit->setStyleSheet("background: #5c2222;");
|
|
||||||
} else {
|
|
||||||
m_searchEdit->setStyleSheet(QString());
|
m_searchEdit->setStyleSheet(QString());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whenever the search text itself changes, restart from the top of the
|
||||||
|
// document so editing the query doesn't leave the view stranded at
|
||||||
|
// wherever the cursor happened to be from a previous search.
|
||||||
|
QTextCursor cursor(m_textEdit->document());
|
||||||
|
m_textEdit->setTextCursor(cursor);
|
||||||
|
|
||||||
|
const bool found = searchAndSelect(text, {}, true);
|
||||||
|
m_searchEdit->setStyleSheet(found ? QString() : "background: #5c2222;");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RawView::findNext()
|
||||||
|
{
|
||||||
|
const QString text = m_searchEdit->text();
|
||||||
|
if (text.isEmpty())
|
||||||
|
return;
|
||||||
|
const bool found = searchAndSelect(text, {}, true);
|
||||||
|
m_searchEdit->setStyleSheet(found ? QString() : "background: #5c2222;");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RawView::findPrevious()
|
||||||
|
{
|
||||||
|
const QString text = m_searchEdit->text();
|
||||||
|
if (text.isEmpty())
|
||||||
|
return;
|
||||||
|
const bool found = searchAndSelect(text, QTextDocument::FindBackward, true);
|
||||||
|
m_searchEdit->setStyleSheet(found ? QString() : "background: #5c2222;");
|
||||||
}
|
}
|
||||||
|
|
||||||
void RawView::onScrollValueChanged(int value)
|
void RawView::onScrollValueChanged(int value)
|
||||||
|
|||||||
@@ -269,8 +269,14 @@ void SerialWorker::processLine(const QString &line)
|
|||||||
m_logStream->flush();
|
m_logStream->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tag names may contain letters, digits, underscore AND hyphen (e.g.
|
||||||
|
// [I2C-BUS]). Keep this in sync with RawView::s_tagRe, which is used
|
||||||
|
// for cyan highlighting and re-checking suppressed tags client-side;
|
||||||
|
// if the two regexes disagree, a hyphenated tag can be detected here
|
||||||
|
// (and emitted/filterable) while RawView fails to recognise it as
|
||||||
|
// tagged, or vice versa.
|
||||||
static const QRegularExpression tagRe(
|
static const QRegularExpression tagRe(
|
||||||
R"(\[([A-Z][A-Z0-9_]*)\](.*))", QRegularExpression::CaseInsensitiveOption);
|
R"(\[([A-Z][A-Z0-9_-]*)\](.*))", QRegularExpression::CaseInsensitiveOption);
|
||||||
|
|
||||||
const auto match = tagRe.match(line);
|
const auto match = tagRe.match(line);
|
||||||
if (match.hasMatch()) {
|
if (match.hasMatch()) {
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QTextCursor>
|
||||||
|
|
||||||
TagPanel::TagPanel(const QString &tag, QWidget *parent)
|
TagPanel::TagPanel(const QString &tag, QWidget *parent)
|
||||||
: QGroupBox(QStringLiteral("[%1]").arg(tag), parent)
|
: QGroupBox(QStringLiteral("[%1]").arg(tag), parent)
|
||||||
@@ -12,7 +16,15 @@ TagPanel::TagPanel(const QString &tag, QWidget *parent)
|
|||||||
layout->setContentsMargins(6, 14, 6, 6);
|
layout->setContentsMargins(6, 14, 6, 6);
|
||||||
layout->setSpacing(4);
|
layout->setSpacing(4);
|
||||||
|
|
||||||
m_table = new QTableWidget(0, 2, this);
|
m_tabs = new QTabWidget(this);
|
||||||
|
|
||||||
|
// ── "Aktuell" tab: latest value(s), updates in-place ───────────────
|
||||||
|
auto *currentTab = new QWidget(m_tabs);
|
||||||
|
auto *currentLayout = new QVBoxLayout(currentTab);
|
||||||
|
currentLayout->setContentsMargins(0, 4, 0, 0);
|
||||||
|
currentLayout->setSpacing(4);
|
||||||
|
|
||||||
|
m_table = new QTableWidget(0, 2, currentTab);
|
||||||
m_table->setHorizontalHeaderLabels({tr("Key"), tr("Value")});
|
m_table->setHorizontalHeaderLabels({tr("Key"), tr("Value")});
|
||||||
m_table->horizontalHeader()->setStretchLastSection(true);
|
m_table->horizontalHeader()->setStretchLastSection(true);
|
||||||
m_table->verticalHeader()->hide();
|
m_table->verticalHeader()->hide();
|
||||||
@@ -20,18 +32,48 @@ TagPanel::TagPanel(const QString &tag, QWidget *parent)
|
|||||||
m_table->setAlternatingRowColors(true);
|
m_table->setAlternatingRowColors(true);
|
||||||
m_table->setMaximumHeight(220);
|
m_table->setMaximumHeight(220);
|
||||||
m_table->verticalHeader()->setDefaultSectionSize(22);
|
m_table->verticalHeader()->setDefaultSectionSize(22);
|
||||||
layout->addWidget(m_table);
|
currentLayout->addWidget(m_table);
|
||||||
|
|
||||||
m_rawLabel = new QLabel(this);
|
m_rawLabel = new QLabel(currentTab);
|
||||||
m_rawLabel->setWordWrap(true);
|
m_rawLabel->setWordWrap(true);
|
||||||
m_rawLabel->setStyleSheet("font-family: monospace;");
|
m_rawLabel->setStyleSheet("font-family: monospace;");
|
||||||
m_rawLabel->hide();
|
m_rawLabel->hide();
|
||||||
layout->addWidget(m_rawLabel);
|
currentLayout->addWidget(m_rawLabel);
|
||||||
|
|
||||||
|
m_tabs->addTab(currentTab, tr("Aktuell"));
|
||||||
|
|
||||||
|
// ── "Verlauf" tab: every value ever received, timestamped ──────────
|
||||||
|
auto *historyTab = new QWidget(m_tabs);
|
||||||
|
auto *historyLayout = new QVBoxLayout(historyTab);
|
||||||
|
historyLayout->setContentsMargins(0, 4, 0, 0);
|
||||||
|
historyLayout->setSpacing(4);
|
||||||
|
|
||||||
|
m_historyEdit = new QPlainTextEdit(historyTab);
|
||||||
|
m_historyEdit->setReadOnly(true);
|
||||||
|
m_historyEdit->setMaximumHeight(220);
|
||||||
|
m_historyEdit->setLineWrapMode(QPlainTextEdit::NoWrap);
|
||||||
|
QFont mono("Monospace");
|
||||||
|
mono.setStyleHint(QFont::Monospace);
|
||||||
|
mono.setPointSize(9);
|
||||||
|
m_historyEdit->setFont(mono);
|
||||||
|
historyLayout->addWidget(m_historyEdit);
|
||||||
|
|
||||||
|
auto *historyBtnRow = new QHBoxLayout();
|
||||||
|
auto *clearHistBtn = new QPushButton(tr("Verlauf leeren"), historyTab);
|
||||||
|
clearHistBtn->setToolTip(tr("Nur den Verlauf dieses Tags leeren (Panel bleibt bestehen)"));
|
||||||
|
connect(clearHistBtn, &QPushButton::clicked, this, &TagPanel::clearHistory);
|
||||||
|
historyBtnRow->addStretch();
|
||||||
|
historyBtnRow->addWidget(clearHistBtn);
|
||||||
|
historyLayout->addLayout(historyBtnRow);
|
||||||
|
|
||||||
|
m_tabs->addTab(historyTab, tr("Verlauf"));
|
||||||
|
|
||||||
|
layout->addWidget(m_tabs);
|
||||||
|
|
||||||
auto *btnRow = new QHBoxLayout();
|
auto *btnRow = new QHBoxLayout();
|
||||||
btnRow->addStretch();
|
btnRow->addStretch();
|
||||||
auto *copyBtn = new QPushButton(tr("📋 Copy"), this);
|
auto *copyBtn = new QPushButton(tr("📋 Copy"), this);
|
||||||
copyBtn->setToolTip(tr("Copy current tag values to clipboard"));
|
copyBtn->setToolTip(tr("Copy the active tab's content to clipboard"));
|
||||||
copyBtn->setMaximumWidth(90);
|
copyBtn->setMaximumWidth(90);
|
||||||
connect(copyBtn, &QPushButton::clicked, this, &TagPanel::copyToClipboard);
|
connect(copyBtn, &QPushButton::clicked, this, &TagPanel::copyToClipboard);
|
||||||
btnRow->addWidget(copyBtn);
|
btnRow->addWidget(copyBtn);
|
||||||
@@ -40,6 +82,8 @@ TagPanel::TagPanel(const QString &tag, QWidget *parent)
|
|||||||
|
|
||||||
void TagPanel::update(const QString &value)
|
void TagPanel::update(const QString &value)
|
||||||
{
|
{
|
||||||
|
appendHistory(value);
|
||||||
|
|
||||||
static const QRegularExpression kvRe(R"((\w+)=(\S+))");
|
static const QRegularExpression kvRe(R"((\w+)=(\S+))");
|
||||||
if (kvRe.match(value).hasMatch()) {
|
if (kvRe.match(value).hasMatch()) {
|
||||||
parseKeyValue(value);
|
parseKeyValue(value);
|
||||||
@@ -52,6 +96,31 @@ void TagPanel::update(const QString &value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TagPanel::appendHistory(const QString &value)
|
||||||
|
{
|
||||||
|
const QString ts = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
|
||||||
|
m_historyEdit->appendPlainText(QStringLiteral("[%1] %2").arg(ts, value));
|
||||||
|
m_historyEdit->verticalScrollBar()->setValue(
|
||||||
|
m_historyEdit->verticalScrollBar()->maximum());
|
||||||
|
|
||||||
|
++m_historyCount;
|
||||||
|
if (m_historyCount > kMaxHistoryEntries) {
|
||||||
|
// Drop the oldest entry (topmost line) to keep memory use bounded
|
||||||
|
// for very chatty or long-running tags.
|
||||||
|
QTextCursor cursor(m_historyEdit->document());
|
||||||
|
cursor.movePosition(QTextCursor::Start);
|
||||||
|
cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
|
||||||
|
cursor.removeSelectedText();
|
||||||
|
--m_historyCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TagPanel::clearHistory()
|
||||||
|
{
|
||||||
|
m_historyEdit->clear();
|
||||||
|
m_historyCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void TagPanel::parseKeyValue(const QString &value)
|
void TagPanel::parseKeyValue(const QString &value)
|
||||||
{
|
{
|
||||||
static const QRegularExpression kvRe(R"((\w+)=(\S+))");
|
static const QRegularExpression kvRe(R"((\w+)=(\S+))");
|
||||||
@@ -124,6 +193,13 @@ void TagPanel::copyToClipboard()
|
|||||||
QStringList lines;
|
QStringList lines;
|
||||||
lines << QStringLiteral("[%1]").arg(m_tag);
|
lines << QStringLiteral("[%1]").arg(m_tag);
|
||||||
|
|
||||||
|
if (m_tabs->currentIndex() == 1) {
|
||||||
|
// "Verlauf" tab active - copy the full timestamped history as-is.
|
||||||
|
QApplication::clipboard()->setText(
|
||||||
|
lines.join('\n') + '\n' + m_historyEdit->toPlainText());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_table->isVisible()) {
|
if (m_table->isVisible()) {
|
||||||
QString lastKey;
|
QString lastKey;
|
||||||
for (int r = 0; r < m_table->rowCount(); ++r) {
|
for (int r = 0; r < m_table->rowCount(); ++r) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Maintainer: diabolus <your@email.com>
|
# Maintainer: diabolus <your@email.com>
|
||||||
pkgname=uartscope
|
pkgname=uartscope
|
||||||
pkgver=1.0.0.r4.g72a2b7e
|
pkgver=1.0.0.r5.g1422efd
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Qt6-based UART serial monitor with tag monitoring, table view and auto-reconnect"
|
pkgdesc="Qt6-based UART serial monitor with tag monitoring, table view and auto-reconnect"
|
||||||
arch=('x86_64' 'aarch64')
|
arch=('x86_64' 'aarch64')
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -3,12 +3,12 @@
|
|||||||
pkgname = uartscope
|
pkgname = uartscope
|
||||||
pkgbase = uartscope
|
pkgbase = uartscope
|
||||||
xdata = pkgtype=pkg
|
xdata = pkgtype=pkg
|
||||||
pkgver = 1.0.0.r4.g72a2b7e-1
|
pkgver = 1.0.0.r5.g1422efd-1
|
||||||
pkgdesc = Qt6-based UART serial monitor with tag monitoring, table view and auto-reconnect
|
pkgdesc = Qt6-based UART serial monitor with tag monitoring, table view and auto-reconnect
|
||||||
url = https://git.projekt-hirnfrei.de/diabolus/uartscope
|
url = https://git.projekt-hirnfrei.de/diabolus/uartscope
|
||||||
builddate = 1785955532
|
builddate = 1786450603
|
||||||
packager = Unknown Packager
|
packager = Unknown Packager
|
||||||
size = 389745
|
size = 397937
|
||||||
arch = x86_64
|
arch = x86_64
|
||||||
license = MIT
|
license = MIT
|
||||||
conflict = uartscope
|
conflict = uartscope
|
||||||
|
|||||||
Binary file not shown.
Submodule uartscope-git/src/uartscope updated: 72a2b7edd0...1422efdc2f
@@ -1,2 +1,2 @@
|
|||||||
72a2b7edd0d87becbbbfcae8b1c2ecda630f43e2 not-for-merge branch 'main' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
1422efdc2f14d71edf3c7accec15966f5aa81c07 not-for-merge branch 'main' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
||||||
cc102c93eb17f7b910d8e74c3505f198bed77f10 not-for-merge tag 'v1.0.0' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
cc102c93eb17f7b910d8e74c3505f198bed77f10 not-for-merge tag 'v1.0.0' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
||||||
|
|||||||
Reference in New Issue
Block a user