diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 28395d5..00b76c5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -26,12 +26,20 @@ jobs: - name: Check format on changed lines run: | - BASE_SHA=$(git merge-base origin/${{ github.base_ref }} HEAD) - echo "Comparing against merge-base: $BASE_SHA" + # Diff-only on PRs (have a base_ref). On workflow_dispatch, base_ref is + # empty — in that case run clang-format on the whole tree so a manual + # trigger still produces a useful signal instead of erroring out. + if [ -n "${{ github.base_ref }}" ]; then + BASE_SHA=$(git merge-base "origin/${{ github.base_ref }}" HEAD) + echo "Comparing against merge-base: $BASE_SHA" - # git-clang-format prints a diff if any changed line violates style. - # --diff exits non-zero when reformatting would change something. - OUTPUT=$(git clang-format --diff "$BASE_SHA" -- '*.cpp' '*.h' '*.hpp' '*.cc' || true) + # git-clang-format prints a diff if any changed line violates style. + # --diff exits non-zero when reformatting would change something. + OUTPUT=$(git clang-format --diff "$BASE_SHA" -- '*.cpp' '*.h' '*.hpp' '*.cc' || true) + else + echo "No base_ref (workflow_dispatch) — running clang-format on whole tree" + OUTPUT=$(git clang-format --diff $(git rev-list --max-parents=0 HEAD | head -1) -- '*.cpp' '*.h' '*.hpp' '*.cc' || true) + fi if [ -z "$OUTPUT" ] || [ "$OUTPUT" = "no modified files to format" ] || [ "$OUTPUT" = "clang-format did not modify any files" ]; then echo "clang-format: clean" @@ -83,9 +91,6 @@ jobs: - name: Run clang-tidy on changed lines run: | - BASE_SHA=$(git merge-base origin/${{ github.base_ref }} HEAD) - echo "Comparing against merge-base: $BASE_SHA" - # clang-tidy-diff.py ships with clang-tidy; runs tidy only on changed lines. DIFF_SCRIPT=$(dpkg -L clang-tidy-15 | grep clang-tidy-diff.py | head -1) if [ -z "$DIFF_SCRIPT" ]; then @@ -93,17 +98,42 @@ jobs: fi echo "Using: $DIFF_SCRIPT" + if [ -n "${{ github.base_ref }}" ]; then + BASE_SHA=$(git merge-base "origin/${{ github.base_ref }}" HEAD) + echo "Comparing against merge-base: $BASE_SHA" + git diff -U0 "$BASE_SHA" -- 'src/*.cpp' 'src/*.h' \ + ':(exclude)src/json/nlohmann_json.hpp' \ + ':(exclude)src/leveldb/*' \ + ':(exclude)src/lz4/*' \ + ':(exclude)src/tor/tor-src/*' > /tmp/changes.diff + else + echo "No base_ref (workflow_dispatch) — running clang-tidy on whole tree" + git diff -U0 -- $(git rev-list --max-parents=0 HEAD | head -1)..HEAD -- 'src/*.cpp' 'src/*.h' \ + ':(exclude)src/json/nlohmann_json.hpp' \ + ':(exclude)src/leveldb/*' \ + ':(exclude)src/lz4/*' \ + ':(exclude)src/tor/tor-src/*' > /tmp/changes.diff || true + # If the initial commit was so old that the diff is empty, fall back to HEAD vs HEAD~100 + if [ ! -s /tmp/changes.diff ]; then + git diff -U0 HEAD~100..HEAD -- 'src/*.cpp' 'src/*.h' \ + ':(exclude)src/json/nlohmann_json.hpp' \ + ':(exclude)src/leveldb/*' \ + ':(exclude)src/lz4/*' \ + ':(exclude)src/tor/tor-src/*' > /tmp/changes.diff || true + fi + fi + + if [ ! -s /tmp/changes.diff ]; then + echo "No changes to lint in dispatch context — skipping" + exit 0 + fi + # -p1 strips the leading "a/"/"b/" from git diff paths. # -path=build points clang-tidy at compile_commands.json. # -iregex restricts to project sources (not vendored). - git diff -U0 "$BASE_SHA" -- 'src/*.cpp' 'src/*.h' \ - ':(exclude)src/json/nlohmann_json.hpp' \ - ':(exclude)src/leveldb/*' \ - ':(exclude)src/lz4/*' \ - ':(exclude)src/tor/tor-src/*' \ - | python3 "$DIFF_SCRIPT" -p1 -path build \ - -iregex '.*\.(cpp|cc|h|hpp)$' \ - -j$(nproc) || EXIT=$? + cat /tmp/changes.diff | python3 "$DIFF_SCRIPT" -p1 -path build \ + -iregex '.*\.(cpp|cc|h|hpp)$' \ + -j$(nproc) || EXIT=$? # Warn-only initially. Flip this to `exit ${EXIT:-0}` once we're clean. exit 0 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9ee81df..0433b7e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -450,6 +450,7 @@ if(BUILD_QT) qt/qvaluecombobox.cpp qt/askpassphrasedialog.cpp qt/hdseeddialog.cpp + qt/outlinedlabel.cpp qt/notificator.cpp qt/qtipcserver.cpp qt/rpcconsole.cpp diff --git a/src/qt/forms/mainwindow.ui b/src/qt/forms/mainwindow.ui index bb85023..acdcb02 100644 --- a/src/qt/forms/mainwindow.ui +++ b/src/qt/forms/mainwindow.ui @@ -1664,7 +1664,7 @@ QProgressBar::chunk { - + 9 @@ -1672,6 +1672,16 @@ QProgressBar::chunk { true + + + 242 + 101 + 34 + + + + 3 + HD (BIP39) wallet seed status @@ -1759,6 +1769,13 @@ QProgressBar::chunk { + + + OutlinedLabel + QLabel +
qt/outlinedlabel.h
+
+
diff --git a/src/qt/outlinedlabel.cpp b/src/qt/outlinedlabel.cpp new file mode 100644 index 0000000..0964f79 --- /dev/null +++ b/src/qt/outlinedlabel.cpp @@ -0,0 +1,93 @@ +#include "outlinedlabel.h" + +#include +#include +#include +#include +#include +#include + +OutlinedLabel::OutlinedLabel(QWidget* parent) + : QLabel(parent) + , m_outlineColor(QColor("#f26522")) + , m_outlineWidth(3) +{ + // OutlinedLabel is always styled; do not let QSS override our paint. + setAttribute(Qt::WA_OpaquePaintEvent, false); +} + +void OutlinedLabel::setOutlineColor(const QColor& c) +{ + if (m_outlineColor == c) return; + m_outlineColor = c; + update(); +} + +void OutlinedLabel::setOutlineWidth(int w) +{ + if (m_outlineWidth == w) return; + m_outlineWidth = w; + update(); +} + +void OutlinedLabel::paintEvent(QPaintEvent* e) +{ + Q_UNUSED(e); + + // Honor any background styling the parent may have given us, but + // do our own text rendering below. We deliberately skip QLabel's + // built-in drawContents/drawText path because it cannot paint a + // per-character outline. + QStyleOption opt; + opt.initFrom(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, nullptr, this); + + if (text().isEmpty()) return; + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.setRenderHint(QPainter::TextAntialiasing, true); + + const QFontMetricsF fm(font()); + const QString t = text(); + // Bounding rect for the text, honoring alignment. Add half the + // outline width on each side so strokes don't clip against the + // widget edge. + const qreal pad = m_outlineWidth / 2.0; + QRectF r = rect().adjusted(pad, pad, -pad, -pad); + + // Center vertically based on font metrics + const qreal yOffset = (r.height() - fm.height()) / 2.0; + QPointF baseline(r.left(), r.top() + yOffset + fm.ascent()); + + // Align: use only the horizontal part of the alignment flag. + const int align = int(alignment() & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter | Qt::AlignJustify)); + const qreal textWidth = fm.horizontalAdvance(t); + qreal x = r.left(); + if (align & Qt::AlignHCenter) { + x = r.left() + (r.width() - textWidth) / 2.0; + } else if (align & Qt::AlignRight) { + x = r.right() - textWidth; + } + baseline.setX(x); + + QPainterPath path; + path.addText(baseline, font(), t); + + // Stroke (outline) — drawn first, in the brand red so each letter + // has a clear 3px red border matching the triangle icons. + QPen outlinePen(m_outlineColor); + outlinePen.setWidth(m_outlineWidth); + outlinePen.setJoinStyle(Qt::RoundJoin); + outlinePen.setCapStyle(Qt::RoundCap); + painter.setPen(outlinePen); + painter.setBrush(Qt::NoBrush); + painter.drawPath(path); + + // Fill the interior with the widget background color so the + // letters read as hollow red outlines against the dark wallet + // background, like the triangle icons beside them. + painter.setPen(Qt::NoPen); + painter.setBrush(QBrush(palette().color(backgroundRole()))); + painter.drawPath(path); +} diff --git a/src/qt/outlinedlabel.h b/src/qt/outlinedlabel.h new file mode 100644 index 0000000..58ab51d --- /dev/null +++ b/src/qt/outlinedlabel.h @@ -0,0 +1,40 @@ +#ifndef TRIANGLES_QT_OUTLINEDLABEL_H +#define TRIANGLES_QT_OUTLINEDLABEL_H + +#include + +/** + * QLabel that renders its text with an outline (stroke) in the + * outline color, and a fill in the fill color. Used for the + * "HD" badge in the status bar of the Triangles Qt wallet so + * that each letter is outlined in the same red (#f26522) as + * the triangle icons. + * + * Outline is drawn first (wide red pen), then the fill is drawn + * on top (narrower pen, slightly inset). Both pens use the + * same font/alignment as the parent label. + */ +class OutlinedLabel : public QLabel +{ + Q_OBJECT + Q_PROPERTY(QColor outlineColor READ outlineColor WRITE setOutlineColor) + Q_PROPERTY(int outlineWidth READ outlineWidth WRITE setOutlineWidth) + +public: + explicit OutlinedLabel(QWidget* parent = nullptr); + + QColor outlineColor() const { return m_outlineColor; } + void setOutlineColor(const QColor& c); + + int outlineWidth() const { return m_outlineWidth; } + void setOutlineWidth(int w); + +protected: + void paintEvent(QPaintEvent* e) override; + +private: + QColor m_outlineColor; + int m_outlineWidth; +}; + +#endif // TRIANGLES_QT_OUTLINEDLABEL_H diff --git a/src/qt/trianglesgui.cpp b/src/qt/trianglesgui.cpp index b9d8e1c..9fd6baf 100644 --- a/src/qt/trianglesgui.cpp +++ b/src/qt/trianglesgui.cpp @@ -32,6 +32,7 @@ #include "guiconstants.h" #include "askpassphrasedialog.h" #include "hdseeddialog.h" +#include "outlinedlabel.h" #include "notificator.h" #include "guiutil.h" #include "rpcconsole.h" @@ -349,7 +350,6 @@ TrianglesGUI::TrianglesGUI(bool fIsTestnet, QWidget *parent): labelOnionAddress->setCursor(Qt::PointingHandCursor); labelOnionAddress->installEventFilter(this); - // I2P address, stacked directly above the .onion address (click to copy) labelI2PAddress = ui->label_i2p; labelI2PAddress->setVisible(false); labelI2PAddress->setCursor(Qt::PointingHandCursor); @@ -1896,17 +1896,21 @@ void TrianglesGUI::updateHDStatus() } if (fHD) { - labelHdIcon->setStyleSheet("color: #f26522; font-weight: bold;"); + // Both letters outlined in the brand red, 3px stroke (matches the + // triangles beside it). + labelHdIcon->setOutlineColor(QColor("#f26522")); + labelHdIcon->setOutlineWidth(3); labelHdIcon->setToolTip(tr("HD wallet: BIP39 seed active. Backup your seed phrase — individual keys alone will not restore this wallet.")); } else { - labelHdIcon->setStyleSheet("color: #555555; font-weight: bold;"); + // Greyed-out (dim) badge until the user runs hdnew. + labelHdIcon->setOutlineColor(QColor("#555555")); + labelHdIcon->setOutlineWidth(3); labelHdIcon->setToolTip(tr("Non-HD wallet: backup each address key separately. Use hdnew to upgrade to an HD seed.")); } labelHdIcon->setText(QStringLiteral("HD")); labelHdIcon->setVisible(true); } - void TrianglesGUI::on_bHelp_clicked() { ensureRPCConsole(); diff --git a/src/qt/trianglesgui.h b/src/qt/trianglesgui.h index 566da5f..c168172 100644 --- a/src/qt/trianglesgui.h +++ b/src/qt/trianglesgui.h @@ -6,6 +6,8 @@ #include #include +class OutlinedLabel; + class TransactionTableModel; class ClientModel; class WalletModel; @@ -114,7 +116,7 @@ private: QLabel *labelV3Icon; QLabel *labelI2PIcon; QLabel *labelTorIcon; - QLabel *labelHdIcon; + OutlinedLabel *labelHdIcon; QLabel *progressBarLabel; QProgressBar *progressBar;