diff --git a/src/NotepadNext/NotepadNext.pro b/src/NotepadNext/NotepadNext.pro
index 74cd8dfea..598b9fd79 100644
--- a/src/NotepadNext/NotepadNext.pro
+++ b/src/NotepadNext/NotepadNext.pro
@@ -71,6 +71,7 @@ SOURCES += \
EditorHexViewerTableModel.cpp \
EditorManager.cpp \
EditorPrintPreviewRenderer.cpp \
+ SearchResultHighlighterDelegate.cpp \
widgets/FadingIndicator.cpp \
FileDialogHelpers.cpp \
Finder.cpp \
@@ -149,6 +150,8 @@ HEADERS += \
EditorHexViewerTableModel.h \
EditorManager.h \
EditorPrintPreviewRenderer.h \
+ SearchResultData.h \
+ SearchResultHighlighterDelegate.h \
widgets/FadingIndicator.h \
FileDialogHelpers.h \
Finder.h \
diff --git a/src/NotepadNext/SearchResultData.h b/src/NotepadNext/SearchResultData.h
new file mode 100644
index 000000000..23b2894ea
--- /dev/null
+++ b/src/NotepadNext/SearchResultData.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of Notepad Next.
+ * Copyright 2025 Justin Dailey
+ *
+ * Notepad Next is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Notepad Next is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Notepad Next. If not, see .
+ */
+
+#pragma once
+
+#include
+
+namespace SearchResultData {
+ enum Role
+ {
+ LineNumber = Qt::UserRole,
+ LinePosStart,
+ LinePosEnd
+ };
+}
diff --git a/src/NotepadNext/SearchResultHighlighterDelegate.cpp b/src/NotepadNext/SearchResultHighlighterDelegate.cpp
new file mode 100644
index 000000000..87cad5490
--- /dev/null
+++ b/src/NotepadNext/SearchResultHighlighterDelegate.cpp
@@ -0,0 +1,84 @@
+/*
+ * This file is part of Notepad Next.
+ * Copyright 2025 Justin Dailey
+ *
+ * Notepad Next is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Notepad Next is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Notepad Next. If not, see .
+ */
+
+#include "SearchResultHighlighterDelegate.h"
+#include "SearchResultData.h"
+
+#include
+
+void SearchResultHighlighterDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ // Only custom paint for column 1 with highlight flag set
+ if (index.column() != 1 || !index.data(SearchResultData::LineNumber).isValid()) {
+ QStyledItemDelegate::paint(painter, option, index);
+ return;
+ }
+
+ const QString text = index.data(Qt::DisplayRole).toString();
+ int start = index.data(SearchResultData::LinePosStart).toInt();
+ int end = index.data(SearchResultData::LinePosEnd).toInt();
+
+ QStyleOptionViewItem opt(option);
+ initStyleOption(&opt, index);
+
+ painter->save();
+ painter->setClipRect(opt.rect);
+
+ // Optional: draw background or selection as usual
+ if (opt.state & QStyle::State_Selected) {
+ painter->fillRect(opt.rect, opt.palette.highlight());
+ } else {
+ painter->fillRect(opt.rect, opt.backgroundBrush);
+ }
+
+ QRect textRect = opt.rect.adjusted(4, 0, -4, 0); // small padding
+ QFontMetrics fm(opt.font);
+ int y = textRect.top() + (textRect.height() + fm.ascent() - fm.descent()) / 2;
+ int x = textRect.left();
+
+ // Split the text
+ const QString before = text.left(start);
+ const QString match = text.mid(start, end - start);
+ const QString after = text.mid(end);
+
+ // Draw 'before' text (normal)
+ painter->setFont(opt.font);
+ painter->setPen(opt.palette.color(QPalette::Text));
+ painter->drawText(x, y, before);
+ x += fm.horizontalAdvance(before);
+
+ // Draw highlighted 'match' text (bold, red, yellow bg)
+ QFont boldFont = opt.font;
+ boldFont.setBold(true);
+ painter->setFont(boldFont);
+
+ int matchWidth = fm.horizontalAdvance(match);
+ QRect highlightRect(x, textRect.top(), matchWidth, textRect.height());
+ painter->fillRect(highlightRect, QColor(Qt::yellow));
+
+ painter->setPen(Qt::red);
+ painter->drawText(x, y, match);
+ x += matchWidth;
+
+ // Draw 'after' text (normal)
+ painter->setFont(opt.font);
+ painter->setPen(opt.palette.color(QPalette::Text));
+ painter->drawText(x, y, after);
+
+ painter->restore();
+}
diff --git a/src/NotepadNext/SearchResultHighlighterDelegate.h b/src/NotepadNext/SearchResultHighlighterDelegate.h
new file mode 100644
index 000000000..cbd8414e3
--- /dev/null
+++ b/src/NotepadNext/SearchResultHighlighterDelegate.h
@@ -0,0 +1,28 @@
+/*
+ * This file is part of Notepad Next.
+ * Copyright 2025 Justin Dailey
+ *
+ * Notepad Next is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Notepad Next is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Notepad Next. If not, see .
+ */
+
+#pragma once
+
+#include
+
+class SearchResultHighlighterDelegate : public QStyledItemDelegate {
+public:
+ SearchResultHighlighterDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}
+
+ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+};
diff --git a/src/NotepadNext/docks/SearchResultsDock.cpp b/src/NotepadNext/docks/SearchResultsDock.cpp
index 08bacd8a0..f686ccee5 100644
--- a/src/NotepadNext/docks/SearchResultsDock.cpp
+++ b/src/NotepadNext/docks/SearchResultsDock.cpp
@@ -17,6 +17,8 @@
*/
+#include "SearchResultHighlighterDelegate.h"
+#include "SearchResultData.h"
#include "SearchResultsDock.h"
#include "ScintillaNext.h"
#include "ui_SearchResultsDock.h"
@@ -27,12 +29,6 @@
#include
#include
-enum SearchResultData
-{
- LineNumber = Qt::UserRole,
- LinePosStart,
- LinePosEnd
-};
SearchResultsDock::SearchResultsDock(QWidget *parent) :
QDockWidget(parent),
@@ -71,6 +67,8 @@ SearchResultsDock::SearchResultsDock(QWidget *parent) :
menu.exec(QCursor::pos());
});
+
+ ui->treeWidget->setItemDelegate(new SearchResultHighlighterDelegate(ui->treeWidget));
}
SearchResultsDock::~SearchResultsDock()
@@ -125,12 +123,13 @@ void SearchResultsDock::newResultsEntry(const QString line, int lineNumber, int
// Scintilla internally references line numbers starting at 0, however it needs displayed starting at 1
item->setText(0, QString::number(lineNumber + 1));
- item->setData(0, SearchResultData::LineNumber, lineNumber);
- item->setData(0, SearchResultData::LinePosStart, startPositionFromBeginning);
- item->setData(0, SearchResultData::LinePosEnd, endPositionFromBeginning);
item->setBackground(0, QBrush(QColor(220, 220, 220)));
item->setTextAlignment(0, Qt::AlignRight);
+ item->setData(1, SearchResultData::LineNumber, lineNumber);
+ item->setData(1, SearchResultData::LinePosStart, startPositionFromBeginning);
+ item->setData(1, SearchResultData::LinePosEnd, endPositionFromBeginning);
+ item->setData(1, Qt::UserRole + 3, true); // <- Flag to enable highlight
item->setText(1, line);
totalFileHitCount += hitCount;
@@ -191,9 +190,9 @@ void SearchResultsDock::itemActivated(QTreeWidgetItem *item, int column)
// The editor may no longer exist
if (editor) {
- int lineNumber = item->data(0, SearchResultData::LineNumber).toInt();
- int startPositionFromBeginning = item->data(0, SearchResultData::LinePosStart).toInt();
- int endPositionFromBeginning = item->data(0, SearchResultData::LinePosEnd).toInt();
+ int lineNumber = item->data(1, SearchResultData::LineNumber).toInt();
+ int startPositionFromBeginning = item->data(1, SearchResultData::LinePosStart).toInt();
+ int endPositionFromBeginning = item->data(1, SearchResultData::LinePosEnd).toInt();
emit searchResultActivated(editor, lineNumber, startPositionFromBeginning, endPositionFromBeginning);
}